Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The MELCloud Climate integration."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from datetime import timedelta
7 import logging
8 from typing import Any
9 
10 from aiohttp import ClientConnectionError, ClientResponseError
11 from pymelcloud import Device, get_devices
12 from pymelcloud.atw_device import Zone
13 
14 from homeassistant.config_entries import ConfigEntry
15 from homeassistant.const import CONF_TOKEN, Platform
16 from homeassistant.core import HomeAssistant
17 from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
18 from homeassistant.helpers.aiohttp_client import async_get_clientsession
19 from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
20 from homeassistant.util import Throttle
21 
22 from .const import DOMAIN
23 
24 _LOGGER = logging.getLogger(__name__)
25 
26 MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
27 
28 PLATFORMS = [Platform.CLIMATE, Platform.SENSOR, Platform.WATER_HEATER]
29 
30 
31 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
32  """Establish connection with MELClooud."""
33  conf = entry.data
34  try:
35  mel_devices = await mel_devices_setup(hass, conf[CONF_TOKEN])
36  except ClientResponseError as ex:
37  if isinstance(ex, ClientResponseError) and ex.code == 401:
38  raise ConfigEntryAuthFailed from ex
39  raise ConfigEntryNotReady from ex
40  except (TimeoutError, ClientConnectionError) as ex:
41  raise ConfigEntryNotReady from ex
42 
43  hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: mel_devices})
44  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
45  return True
46 
47 
48 async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
49  """Unload a config entry."""
50  unload_ok = await hass.config_entries.async_unload_platforms(
51  config_entry, PLATFORMS
52  )
53  hass.data[DOMAIN].pop(config_entry.entry_id)
54  if not hass.data[DOMAIN]:
55  hass.data.pop(DOMAIN)
56  return unload_ok
57 
58 
60  """MELCloud Device instance."""
61 
62  def __init__(self, device: Device) -> None:
63  """Construct a device wrapper."""
64  self.devicedevice = device
65  self.namename = device.name
66  self._available_available = True
67 
68  @Throttle(MIN_TIME_BETWEEN_UPDATES)
69  async def async_update(self, **kwargs):
70  """Pull the latest data from MELCloud."""
71  try:
72  await self.devicedevice.update()
73  self._available_available = True
74  except ClientConnectionError:
75  _LOGGER.warning("Connection failed for %s", self.namename)
76  self._available_available = False
77 
78  async def async_set(self, properties: dict[str, Any]):
79  """Write state changes to the MELCloud API."""
80  try:
81  await self.devicedevice.set(properties)
82  self._available_available = True
83  except ClientConnectionError:
84  _LOGGER.warning("Connection failed for %s", self.namename)
85  self._available_available = False
86 
87  @property
88  def available(self) -> bool:
89  """Return True if entity is available."""
90  return self._available_available
91 
92  @property
93  def device_id(self):
94  """Return device ID."""
95  return self.devicedevice.device_id
96 
97  @property
98  def building_id(self):
99  """Return building ID of the device."""
100  return self.devicedevice.building_id
101 
102  @property
103  def device_info(self) -> DeviceInfo:
104  """Return a device description for device registry."""
105  model = None
106  if (unit_infos := self.devicedevice.units) is not None:
107  model = ", ".join([x["model"] for x in unit_infos if x["model"]])
108  return DeviceInfo(
109  connections={(CONNECTION_NETWORK_MAC, self.devicedevice.mac)},
110  identifiers={(DOMAIN, f"{self.device.mac}-{self.device.serial}")},
111  manufacturer="Mitsubishi Electric",
112  model=model,
113  name=self.namename,
114  )
115 
116  def zone_device_info(self, zone: Zone) -> DeviceInfo:
117  """Return a zone device description for device registry."""
118  dev = self.devicedevice
119  return DeviceInfo(
120  identifiers={(DOMAIN, f"{dev.mac}-{dev.serial}-{zone.zone_index}")},
121  manufacturer="Mitsubishi Electric",
122  model="ATW zone device",
123  name=f"{self.name} {zone.name}",
124  via_device=(DOMAIN, f"{dev.mac}-{dev.serial}"),
125  )
126 
127 
129  hass: HomeAssistant, token: str
130 ) -> dict[str, list[MelCloudDevice]]:
131  """Query connected devices from MELCloud."""
132  session = async_get_clientsession(hass)
133  async with asyncio.timeout(10):
134  all_devices = await get_devices(
135  token,
136  session,
137  conf_update_interval=timedelta(minutes=30),
138  device_set_debounce=timedelta(seconds=2),
139  )
140  wrapped_devices: dict[str, list[MelCloudDevice]] = {}
141  for device_type, devices in all_devices.items():
142  wrapped_devices[device_type] = [MelCloudDevice(device) for device in devices]
143  return wrapped_devices
def async_set(self, dict[str, Any] properties)
Definition: __init__.py:78
DeviceInfo zone_device_info(self, Zone zone)
Definition: __init__.py:116
IssData update(pyiss.ISS iss)
Definition: __init__.py:33
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:31
bool async_unload_entry(HomeAssistant hass, ConfigEntry config_entry)
Definition: __init__.py:48
dict[str, list[MelCloudDevice]] mel_devices_setup(HomeAssistant hass, str token)
Definition: __init__.py:130
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)