1 """Representation of a deCONZ gateway."""
3 from __future__
import annotations
5 from collections.abc
import Callable
6 from typing
import TYPE_CHECKING, cast
8 from pydeconz
import DeconzSession
9 from pydeconz.interfaces
import sensors
10 from pydeconz.interfaces.api_handlers
import APIHandler, GroupedAPIHandler
11 from pydeconz.interfaces.groups
import GroupHandler
12 from pydeconz.models.event
import EventType
22 DOMAIN
as DECONZ_DOMAIN,
23 HASSIO_CONFIGURATION_URL,
26 from .config
import DeconzConfig
29 from ..deconz_event
import (
33 DeconzRelativeRotaryEvent,
37 sensors.SensorResourceManager,
38 sensors.AirPurifierHandler,
39 sensors.AirQualityHandler,
41 sensors.AncillaryControlHandler,
42 sensors.BatteryHandler,
43 sensors.CarbonMonoxideHandler,
44 sensors.ConsumptionHandler,
45 sensors.DaylightHandler,
46 sensors.DoorLockHandler,
48 sensors.GenericFlagHandler,
49 sensors.GenericStatusHandler,
50 sensors.HumidityHandler,
51 sensors.LightLevelHandler,
52 sensors.OpenCloseHandler,
54 sensors.PresenceHandler,
55 sensors.PressureHandler,
56 sensors.RelativeRotaryHandler,
57 sensors.SwitchHandler,
58 sensors.TemperatureHandler,
59 sensors.ThermostatHandler,
61 sensors.VibrationHandler,
67 """Manages a single deCONZ gateway."""
70 self, hass: HomeAssistant, config_entry: ConfigEntry, api: DeconzSession
72 """Initialize the system."""
74 self.
configconfig = DeconzConfig.from_config_entry(config_entry)
86 self.entities: dict[str, set[str]] = {}
91 | DeconzRelativeRotaryEvent
93 self.clip_sensors: set[tuple[Callable[[EventType, str],
None], str]] = set()
94 self.deconz_groups: set[tuple[Callable[[EventType, str],
None], str]] = set()
95 self.ignored_devices: set[tuple[Callable[[EventType, str],
None], str]] = set()
99 def get_hub(hass: HomeAssistant, config_entry: ConfigEntry) -> DeconzHub:
100 """Return hub with a matching config entry ID."""
101 return cast(DeconzHub, hass.data[DECONZ_DOMAIN][config_entry.entry_id])
105 """Return the unique identifier of the gateway."""
106 return cast(str, self.
config_entryconfig_entry.unique_id)
110 """Gateway which is used with deCONZ services without defining id."""
111 return cast(bool, self.
config_entryconfig_entry.options[CONF_MASTER_GATEWAY])
116 add_device_callback: Callable[[EventType, str],
None],
117 deconz_device_interface: APIHandler | GroupedAPIHandler,
118 always_ignore_clip_sensors: bool =
False,
120 """Wrap add_device_callback to check allow_new_devices option."""
124 def async_add_device(_: EventType, device_id: str) ->
None:
125 """Add device or add it to ignored_devices set.
127 If ignore_state_updates is True means device_refresh service is used.
128 Device_refresh is expected to load new devices.
132 and not self.
configconfig.allow_new_devices
135 self.ignored_devices.
add((async_add_device, device_id))
138 if isinstance(deconz_device_interface, GroupHandler):
139 self.deconz_groups.
add((async_add_device, device_id))
140 if not self.
configconfig.allow_deconz_groups:
143 if isinstance(deconz_device_interface, SENSORS):
144 device = deconz_device_interface[device_id]
145 if device.type.startswith(
"CLIP")
and not always_ignore_clip_sensors:
146 self.clip_sensors.
add((async_add_device, device_id))
147 if not self.
configconfig.allow_clip_sensor:
150 add_device_callback(EventType.ADDED, device_id)
153 deconz_device_interface.subscribe(
159 for device_id
in sorted(deconz_device_interface, key=int):
160 async_add_device(EventType.ADDED, device_id)
166 """Load previously ignored devices."""
167 for add_entities, device_id
in self.ignored_devices:
169 self.ignored_devices.clear()
175 """Handle signals of gateway connection status."""
181 """Update device registry."""
182 if self.
apiapi.config.mac
is None:
185 device_registry = dr.async_get(self.
hasshass)
188 device_registry.async_get_or_create(
190 connections={(CONNECTION_NETWORK_MAC, self.
apiapi.config.mac)},
194 configuration_url = f
"http://{self.config.host}:{self.config.port}"
195 if self.
config_entryconfig_entry.source == SOURCE_HASSIO:
196 configuration_url = HASSIO_CONFIGURATION_URL
197 device_registry.async_get_or_create(
199 configuration_url=configuration_url,
200 entry_type=dr.DeviceEntryType.SERVICE,
201 identifiers={(DECONZ_DOMAIN, self.
apiapi.config.bridge_id)},
202 manufacturer=
"Dresden Elektronik",
203 model=self.
apiapi.config.model_id,
204 name=self.
apiapi.config.name,
205 sw_version=self.
apiapi.config.software_version,
206 via_device=(CONNECTION_NETWORK_MAC, self.
apiapi.config.mac),
211 hass: HomeAssistant, config_entry: ConfigEntry
213 """Handle signals of config entry being updated.
215 This is a static method because a class method (bound method),
216 cannot be used with weak references.
217 Causes for this is either discovery updating host address or
218 config entry options changing.
220 if config_entry.entry_id
not in hass.data[DECONZ_DOMAIN]:
224 hub = DeconzHub.get_hub(hass, config_entry)
225 previous_config = hub.config
226 hub.config = DeconzConfig.from_config_entry(config_entry)
227 if previous_config.host != hub.config.host:
229 hub.api.host = hub.config.host
233 await hub.options_updated(previous_config)
236 """Manage entities affected by config entry options."""
241 if self.
configconfig.allow_clip_sensor != previous_config.allow_clip_sensor:
242 if self.
configconfig.allow_clip_sensor:
243 for add_device, device_id
in self.clip_sensors:
244 add_device(EventType.ADDED, device_id)
248 for sensor
in self.
apiapi.sensors.values()
249 if sensor.type.startswith(
"CLIP")
254 if self.
configconfig.allow_deconz_groups != previous_config.allow_deconz_groups:
255 if self.
configconfig.allow_deconz_groups:
256 for add_device, device_id
in self.deconz_groups:
257 add_device(EventType.ADDED, device_id)
259 deconz_ids += [group.deconz_id
for group
in self.
apiapi.groups.values()]
263 if self.
configconfig.allow_new_devices != previous_config.allow_new_devices:
264 if self.
configconfig.allow_new_devices:
269 entity_registry = er.async_get(self.
hasshass)
274 for entity_id, deconz_id
in self.
deconz_idsdeconz_ids.copy().items():
275 if deconz_id
in deconz_ids
and entity_registry.async_is_registered(
280 entity_registry.async_remove(entity_id)
284 """Wrap the call to deconz.close.
286 Used as an argument to EventBus.async_listen_once.
291 """Reset this gateway to default state."""
292 self.
apiapi.connection_status_callback =
None
295 await self.
hasshass.config_entries.async_unload_platforms(
None __init__(self, HomeAssistant hass, ConfigEntry config_entry, DeconzSession api)
None async_update_device_registry(self)
None register_platform_add_device_callback(self, Callable[[EventType, str], None] add_device_callback, APIHandler|GroupedAPIHandler deconz_device_interface, bool always_ignore_clip_sensors=False)
None options_updated(self, DeconzConfig previous_config)
None async_connection_status_callback(self, bool available)
DeconzHub get_hub(HomeAssistant hass, ConfigEntry config_entry)
None load_ignored_devices(self)
None shutdown(self, Event event)
None async_config_entry_updated(HomeAssistant hass, ConfigEntry config_entry)
None add_entities(AsusWrtRouter router, AddEntitiesCallback async_add_entities, set[str] tracked)
bool add(self, _T matcher)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)