Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The SwitchBee Smart Home integration."""
2 
3 from __future__ import annotations
4 
5 import logging
6 import re
7 
8 from aiohttp import ClientSession
9 from switchbee.api import CentralUnitPolling, CentralUnitWsRPC, is_wsrpc_api
10 from switchbee.api.central_unit import SwitchBeeError
11 
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
14 from homeassistant.core import HomeAssistant, callback
15 from homeassistant.exceptions import ConfigEntryNotReady
16 from homeassistant.helpers.aiohttp_client import async_get_clientsession
19 
20 from .const import DOMAIN
21 from .coordinator import SwitchBeeCoordinator
22 
23 _LOGGER = logging.getLogger(__name__)
24 
25 
26 PLATFORMS: list[Platform] = [
27  Platform.BUTTON,
28  Platform.CLIMATE,
29  Platform.COVER,
30  Platform.LIGHT,
31  Platform.SWITCH,
32 ]
33 
34 
35 async def get_api_object(
36  central_unit: str, user: str, password: str, websession: ClientSession
37 ) -> CentralUnitPolling | CentralUnitWsRPC:
38  """Return SwitchBee API object."""
39 
40  api: CentralUnitPolling | CentralUnitWsRPC = CentralUnitPolling(
41  central_unit, user, password, websession
42  )
43  # First try to connect and fetch the version
44  try:
45  await api.connect()
46  except SwitchBeeError as exp:
47  raise ConfigEntryNotReady("Failed to connect to the Central Unit") from exp
48 
49  # Check if websocket version
50  if is_wsrpc_api(api):
51  api = CentralUnitWsRPC(central_unit, user, password, websession)
52  await api.connect()
53 
54  return api
55 
56 
57 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
58  """Set up SwitchBee Smart Home from a config entry."""
59 
60  hass.data.setdefault(DOMAIN, {})
61  central_unit = entry.data[CONF_HOST]
62  user = entry.data[CONF_USERNAME]
63  password = entry.data[CONF_PASSWORD]
64  websession = async_get_clientsession(hass, verify_ssl=False)
65  api = await get_api_object(central_unit, user, password, websession)
66 
67  coordinator = SwitchBeeCoordinator(
68  hass,
69  api,
70  )
71 
72  await coordinator.async_config_entry_first_refresh()
73  entry.async_on_unload(entry.add_update_listener(update_listener))
74  hass.data[DOMAIN][entry.entry_id] = coordinator
75 
76  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
77 
78  return True
79 
80 
81 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
82  """Unload a config entry."""
83  if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
84  hass.data[DOMAIN].pop(entry.entry_id)
85 
86  return unload_ok
87 
88 
89 async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
90  """Update listener."""
91  await hass.config_entries.async_reload(config_entry.entry_id)
92 
93 
94 async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
95  """Migrate old entry."""
96  _LOGGER.debug("Migrating from version %s", config_entry.version)
97 
98  if config_entry.version == 1:
99  dev_reg = dr.async_get(hass)
100  websession = async_get_clientsession(hass, verify_ssl=False)
101  old_unique_id = config_entry.unique_id
102  assert isinstance(old_unique_id, str)
103  api = await get_api_object(
104  config_entry.data[CONF_HOST],
105  config_entry.data[CONF_USERNAME],
106  config_entry.data[CONF_PASSWORD],
107  websession,
108  )
109  new_unique_id = api.unique_id
110 
111  @callback
112  def update_unique_id(entity_entry: er.RegistryEntry) -> dict[str, str] | None:
113  """Update unique ID of entity entry."""
114  if match := re.match(
115  rf"(?:{old_unique_id})-(?P<id>\d+)", entity_entry.unique_id
116  ):
117  entity_new_unique_id = f'{new_unique_id}-{match.group("id")}'
118  _LOGGER.debug(
119  "Migrating entity %s from %s to new id %s",
120  entity_entry.entity_id,
121  entity_entry.unique_id,
122  entity_new_unique_id,
123  )
124  return {"new_unique_id": entity_new_unique_id}
125 
126  return None
127 
128  if new_unique_id:
129  # Migrate devices
130  for device_entry in dr.async_entries_for_config_entry(
131  dev_reg, config_entry.entry_id
132  ):
133  assert isinstance(device_entry, dr.DeviceEntry)
134  for identifier in device_entry.identifiers:
135  if match := re.match(
136  rf"(?P<id>.+)-{old_unique_id}$", identifier[1]
137  ):
138  new_identifiers = {
139  (
140  DOMAIN,
141  f"{match.group('id')}-{new_unique_id}",
142  )
143  }
144  _LOGGER.debug(
145  "Migrating device %s identifiers from %s to %s",
146  device_entry.name,
147  device_entry.identifiers,
148  new_identifiers,
149  )
150  dev_reg.async_update_device(
151  device_entry.id, new_identifiers=new_identifiers
152  )
153 
154  # Migrate entities
155  await er.async_migrate_entries(
156  hass, config_entry.entry_id, update_unique_id
157  )
158 
159  hass.config_entries.async_update_entry(config_entry, version=2)
160 
161  _LOGGER.debug("Migration to version %s successful", config_entry.version)
162 
163  return True
dict[str, str]|None update_unique_id(er.RegistryEntry entity_entry, str unique_id)
Definition: __init__.py:168
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:57
bool async_migrate_entry(HomeAssistant hass, ConfigEntry config_entry)
Definition: __init__.py:94
None update_listener(HomeAssistant hass, ConfigEntry config_entry)
Definition: __init__.py:89
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:81
CentralUnitPolling|CentralUnitWsRPC get_api_object(str central_unit, str user, str password, ClientSession websession)
Definition: __init__.py:37
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)