Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Platform for the Daikin AC."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 import logging
7 
8 from aiohttp import ClientConnectionError
9 from pydaikin.daikin_base import Appliance
10 from pydaikin.factory import DaikinFactory
11 
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import (
14  CONF_API_KEY,
15  CONF_HOST,
16  CONF_PASSWORD,
17  CONF_UUID,
18  Platform,
19 )
20 from homeassistant.core import HomeAssistant, callback
21 from homeassistant.exceptions import ConfigEntryNotReady
22 from homeassistant.helpers import device_registry as dr, entity_registry as er
23 from homeassistant.helpers.aiohttp_client import async_get_clientsession
24 from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
25 
26 from .const import DOMAIN, KEY_MAC, TIMEOUT
27 from .coordinator import DaikinCoordinator
28 
29 _LOGGER = logging.getLogger(__name__)
30 
31 
32 PLATFORMS = [Platform.CLIMATE, Platform.SENSOR, Platform.SWITCH]
33 
34 
35 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
36  """Establish connection with Daikin."""
37  conf = entry.data
38  # For backwards compat, set unique ID
39  if entry.unique_id is None or ".local" in entry.unique_id:
40  hass.config_entries.async_update_entry(entry, unique_id=conf[KEY_MAC])
41 
42  session = async_get_clientsession(hass)
43  host = conf[CONF_HOST]
44  try:
45  async with asyncio.timeout(TIMEOUT):
46  device: Appliance = await DaikinFactory(
47  host,
48  session,
49  key=entry.data.get(CONF_API_KEY),
50  uuid=entry.data.get(CONF_UUID),
51  password=entry.data.get(CONF_PASSWORD),
52  )
53  _LOGGER.debug("Connection to %s successful", host)
54  except TimeoutError as err:
55  _LOGGER.debug("Connection to %s timed out in 60 seconds", host)
56  raise ConfigEntryNotReady from err
57  except ClientConnectionError as err:
58  _LOGGER.debug("ClientConnectionError to %s", host)
59  raise ConfigEntryNotReady from err
60 
61  coordinator = DaikinCoordinator(hass, device)
62 
63  await coordinator.async_config_entry_first_refresh()
64 
65  await async_migrate_unique_id(hass, entry, device)
66 
67  hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
68  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
69  return True
70 
71 
72 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
73  """Unload a config entry."""
74  unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
75  if unload_ok:
76  hass.data[DOMAIN].pop(entry.entry_id)
77  if not hass.data[DOMAIN]:
78  hass.data.pop(DOMAIN)
79  return unload_ok
80 
81 
83  hass: HomeAssistant, config_entry: ConfigEntry, device: Appliance
84 ) -> None:
85  """Migrate old entry."""
86  dev_reg = dr.async_get(hass)
87  ent_reg = er.async_get(hass)
88  old_unique_id = config_entry.unique_id
89  new_unique_id = device.mac
90  new_mac = dr.format_mac(new_unique_id)
91  new_name = device.values.get("name", "Daikin AC")
92 
93  @callback
94  def _update_unique_id(entity_entry: er.RegistryEntry) -> dict[str, str] | None:
95  """Update unique ID of entity entry."""
96  return update_unique_id(entity_entry, new_unique_id)
97 
98  if new_unique_id == old_unique_id:
99  return
100 
101  duplicate = dev_reg.async_get_device(
102  connections={(CONNECTION_NETWORK_MAC, new_mac)}, identifiers=None
103  )
104 
105  # Remove duplicated device
106  if duplicate is not None:
107  if config_entry.entry_id in duplicate.config_entries:
108  _LOGGER.debug(
109  "Removing duplicated device %s",
110  duplicate.name,
111  )
112 
113  # The automatic cleanup in entity registry is scheduled as a task, remove
114  # the entities manually to avoid unique_id collision when the entities
115  # are migrated.
116  duplicate_entities = er.async_entries_for_device(
117  ent_reg, duplicate.id, True
118  )
119  for entity in duplicate_entities:
120  if entity.config_entry_id == config_entry.entry_id:
121  ent_reg.async_remove(entity.entity_id)
122 
123  dev_reg.async_update_device(
124  duplicate.id, remove_config_entry_id=config_entry.entry_id
125  )
126 
127  # Migrate devices
128  for device_entry in dr.async_entries_for_config_entry(
129  dev_reg, config_entry.entry_id
130  ):
131  for connection in device_entry.connections:
132  if connection[1] == old_unique_id:
133  new_connections = {(CONNECTION_NETWORK_MAC, new_mac)}
134 
135  _LOGGER.debug(
136  "Migrating device %s connections to %s",
137  device_entry.name,
138  new_connections,
139  )
140  dev_reg.async_update_device(
141  device_entry.id,
142  merge_connections=new_connections,
143  )
144 
145  if device_entry.name is None:
146  _LOGGER.debug(
147  "Migrating device name to %s",
148  new_name,
149  )
150  dev_reg.async_update_device(
151  device_entry.id,
152  name=new_name,
153  )
154 
155  # Migrate entities
156  await er.async_migrate_entries(hass, config_entry.entry_id, _update_unique_id)
157 
158  new_data = {**config_entry.data, KEY_MAC: dr.format_mac(new_unique_id)}
159 
160  hass.config_entries.async_update_entry(
161  config_entry, unique_id=new_unique_id, data=new_data
162  )
163 
164 
165 @callback
167  entity_entry: er.RegistryEntry, unique_id: str
168 ) -> dict[str, str] | None:
169  """Update unique ID of entity entry."""
170  if entity_entry.unique_id.startswith(unique_id):
171  # Already correct, nothing to do
172  return None
173 
174  unique_id_parts = entity_entry.unique_id.split("-")
175  unique_id_parts[0] = unique_id
176  entity_new_unique_id = "-".join(unique_id_parts)
177 
178  _LOGGER.debug(
179  "Migrating entity %s from %s to new id %s",
180  entity_entry.entity_id,
181  entity_entry.unique_id,
182  entity_new_unique_id,
183  )
184  return {"new_unique_id": entity_new_unique_id}
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:35
None async_migrate_unique_id(HomeAssistant hass, ConfigEntry config_entry, Appliance device)
Definition: __init__.py:84
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:72
dict[str, str]|None update_unique_id(er.RegistryEntry entity_entry, str unique_id)
Definition: __init__.py:168
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)