Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The Bond integration."""
2 
3 from http import HTTPStatus
4 import logging
5 from typing import Any
6 
7 from aiohttp import ClientError, ClientResponseError, ClientTimeout
8 from bond_async import Bond, BPUPSubscriptions, start_bpup
9 
10 from homeassistant.config_entries import ConfigEntry
11 from homeassistant.const import (
12  CONF_ACCESS_TOKEN,
13  CONF_HOST,
14  EVENT_HOMEASSISTANT_STOP,
15  Platform,
16 )
17 from homeassistant.core import HomeAssistant, callback
18 from homeassistant.exceptions import ConfigEntryNotReady
19 from homeassistant.helpers import device_registry as dr
20 from homeassistant.helpers.aiohttp_client import async_get_clientsession
21 from homeassistant.helpers.entity import SLOW_UPDATE_WARNING
22 
23 from .const import BRIDGE_MAKE, DOMAIN
24 from .models import BondData
25 from .utils import BondHub
26 
27 PLATFORMS = [
28  Platform.BUTTON,
29  Platform.COVER,
30  Platform.FAN,
31  Platform.LIGHT,
32  Platform.SWITCH,
33 ]
34 _API_TIMEOUT = SLOW_UPDATE_WARNING - 1
35 
36 _LOGGER = logging.getLogger(__name__)
37 
38 type BondConfigEntry = ConfigEntry[BondData]
39 
40 
41 async def async_setup_entry(hass: HomeAssistant, entry: BondConfigEntry) -> bool:
42  """Set up Bond from a config entry."""
43  host = entry.data[CONF_HOST]
44  token = entry.data[CONF_ACCESS_TOKEN]
45  config_entry_id = entry.entry_id
46 
47  bond = Bond(
48  host=host,
49  token=token,
50  timeout=ClientTimeout(total=_API_TIMEOUT),
51  session=async_get_clientsession(hass),
52  )
53  hub = BondHub(bond, host)
54  try:
55  await hub.setup()
56  except ClientResponseError as ex:
57  if ex.status == HTTPStatus.UNAUTHORIZED:
58  _LOGGER.error("Bond token no longer valid: %s", ex)
59  return False
60  raise ConfigEntryNotReady from ex
61  except (ClientError, TimeoutError, OSError) as error:
62  raise ConfigEntryNotReady from error
63 
64  bpup_subs = BPUPSubscriptions()
65  stop_bpup = await start_bpup(host, bpup_subs)
66 
67  @callback
68  def _async_stop_event(*_: Any) -> None:
69  stop_bpup()
70 
71  entry.async_on_unload(_async_stop_event)
72  entry.async_on_unload(
73  hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, _async_stop_event)
74  )
75  entry.runtime_data = BondData(hub, bpup_subs)
76 
77  if not entry.unique_id:
78  hass.config_entries.async_update_entry(entry, unique_id=hub.bond_id)
79 
80  assert hub.bond_id is not None
81  hub_name = hub.name or hub.bond_id
82  device_registry = dr.async_get(hass)
83  device_registry.async_get_or_create(
84  config_entry_id=config_entry_id,
85  identifiers={(DOMAIN, hub.bond_id)},
86  manufacturer=BRIDGE_MAKE,
87  name=hub_name,
88  model=hub.target,
89  sw_version=hub.fw_ver,
90  hw_version=hub.mcu_ver,
91  suggested_area=hub.location,
92  configuration_url=f"http://{host}",
93  )
94 
95  _async_remove_old_device_identifiers(config_entry_id, device_registry, hub)
96 
97  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
98 
99  return True
100 
101 
102 async def async_unload_entry(hass: HomeAssistant, entry: BondConfigEntry) -> bool:
103  """Unload a config entry."""
104  return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
105 
106 
107 @callback
109  config_entry_id: str, device_registry: dr.DeviceRegistry, hub: BondHub
110 ) -> None:
111  """Remove the non-unique device registry entries."""
112  for device in hub.devices:
113  dev = device_registry.async_get_device(identifiers={(DOMAIN, device.device_id)})
114  if dev is None:
115  continue
116  if config_entry_id in dev.config_entries:
117  device_registry.async_remove_device(dev.id)
118 
119 
121  hass: HomeAssistant, config_entry: BondConfigEntry, device_entry: dr.DeviceEntry
122 ) -> bool:
123  """Remove bond config entry from a device."""
124  data = config_entry.runtime_data
125  hub = data.hub
126  for identifier in device_entry.identifiers:
127  if identifier[0] != DOMAIN or len(identifier) != 3:
128  continue
129  bond_id: str = identifier[1] # type: ignore[unreachable]
130  # Bond still uses the 3 arg tuple before
131  # the identifiers were typed
132  device_id: str = identifier[2]
133  # If device_id is no longer present on
134  # the hub, we allow removal.
135  if hub.bond_id != bond_id or not any(
136  device_id == device.device_id for device in hub.devices
137  ):
138  return True
139  return False
bool async_unload_entry(HomeAssistant hass, BondConfigEntry entry)
Definition: __init__.py:102
bool async_setup_entry(HomeAssistant hass, BondConfigEntry entry)
Definition: __init__.py:41
bool async_remove_config_entry_device(HomeAssistant hass, BondConfigEntry config_entry, dr.DeviceEntry device_entry)
Definition: __init__.py:122
None _async_remove_old_device_identifiers(str config_entry_id, dr.DeviceRegistry device_registry, BondHub hub)
Definition: __init__.py:110
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)