Streamline naming in deCONZ integration (#111977)

This commit is contained in:
Robert Svensson 2024-03-18 22:08:06 +01:00 committed by GitHub
parent e20cc4f8b9
commit 506240be10
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 325 additions and 342 deletions

View file

@ -7,7 +7,7 @@ from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from .config_flow import get_master_gateway from .config_flow import get_master_hub
from .const import CONF_MASTER_GATEWAY, DOMAIN, PLATFORMS from .const import CONF_MASTER_GATEWAY, DOMAIN, PLATFORMS
from .deconz_event import async_setup_events, async_unload_events from .deconz_event import async_setup_events, async_unload_events
from .errors import AuthenticationRequired, CannotConnect from .errors import AuthenticationRequired, CannotConnect
@ -24,7 +24,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
if not config_entry.options: if not config_entry.options:
await async_update_master_gateway(hass, config_entry) await async_update_master_hub(hass, config_entry)
try: try:
api = await get_deconz_api(hass, config_entry) api = await get_deconz_api(hass, config_entry)
@ -36,20 +36,18 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
if not hass.data[DOMAIN]: if not hass.data[DOMAIN]:
async_setup_services(hass) async_setup_services(hass)
gateway = hass.data[DOMAIN][config_entry.entry_id] = DeconzHub( hub = hass.data[DOMAIN][config_entry.entry_id] = DeconzHub(hass, config_entry, api)
hass, config_entry, api await hub.async_update_device_registry()
)
await gateway.async_update_device_registry()
config_entry.add_update_listener(gateway.async_config_entry_updated) config_entry.add_update_listener(hub.async_config_entry_updated)
await async_setup_events(gateway) await async_setup_events(hub)
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
api.start() api.start()
config_entry.async_on_unload( config_entry.async_on_unload(
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, gateway.shutdown) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, hub.shutdown)
) )
return True return True
@ -57,31 +55,31 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Unload deCONZ config entry.""" """Unload deCONZ config entry."""
gateway: DeconzHub = hass.data[DOMAIN].pop(config_entry.entry_id) hub: DeconzHub = hass.data[DOMAIN].pop(config_entry.entry_id)
async_unload_events(gateway) async_unload_events(hub)
if not hass.data[DOMAIN]: if not hass.data[DOMAIN]:
async_unload_services(hass) async_unload_services(hass)
elif gateway.master: elif hub.master:
await async_update_master_gateway(hass, config_entry) await async_update_master_hub(hass, config_entry)
new_master_gateway = next(iter(hass.data[DOMAIN].values())) new_master_hub = next(iter(hass.data[DOMAIN].values()))
await async_update_master_gateway(hass, new_master_gateway.config_entry) await async_update_master_hub(hass, new_master_hub.config_entry)
return await gateway.async_reset() return await hub.async_reset()
async def async_update_master_gateway( async def async_update_master_hub(
hass: HomeAssistant, config_entry: ConfigEntry hass: HomeAssistant, config_entry: ConfigEntry
) -> None: ) -> None:
"""Update master gateway boolean. """Update master hub boolean.
Called by setup_entry and unload_entry. Called by setup_entry and unload_entry.
Makes sure there is always one master available. Makes sure there is always one master available.
""" """
try: try:
master_gateway = get_master_gateway(hass) master_hub = get_master_hub(hass)
master = master_gateway.config_entry == config_entry master = master_hub.config_entry == config_entry
except ValueError: except ValueError:
master = True master = True

View file

@ -29,7 +29,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry from .hub import DeconzHub
DECONZ_TO_ALARM_STATE = { DECONZ_TO_ALARM_STATE = {
AncillaryControlPanel.ARMED_AWAY: STATE_ALARM_ARMED_AWAY, AncillaryControlPanel.ARMED_AWAY: STATE_ALARM_ARMED_AWAY,
@ -45,9 +45,9 @@ DECONZ_TO_ALARM_STATE = {
} }
def get_alarm_system_id_for_unique_id(gateway: DeconzHub, unique_id: str) -> str | None: def get_alarm_system_id_for_unique_id(hub: DeconzHub, unique_id: str) -> str | None:
"""Retrieve alarm system ID the unique ID is registered to.""" """Retrieve alarm system ID the unique ID is registered to."""
for alarm_system in gateway.api.alarm_systems.values(): for alarm_system in hub.api.alarm_systems.values():
if unique_id in alarm_system.devices: if unique_id in alarm_system.devices:
return alarm_system.resource_id return alarm_system.resource_id
return None return None
@ -59,23 +59,19 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the deCONZ alarm control panel devices.""" """Set up the deCONZ alarm control panel devices."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_sensor(_: EventType, sensor_id: str) -> None: def async_add_sensor(_: EventType, sensor_id: str) -> None:
"""Add alarm control panel devices from deCONZ.""" """Add alarm control panel devices from deCONZ."""
sensor = gateway.api.sensors.ancillary_control[sensor_id] sensor = hub.api.sensors.ancillary_control[sensor_id]
if alarm_system_id := get_alarm_system_id_for_unique_id( if alarm_system_id := get_alarm_system_id_for_unique_id(hub, sensor.unique_id):
gateway, sensor.unique_id async_add_entities([DeconzAlarmControlPanel(sensor, hub, alarm_system_id)])
):
async_add_entities(
[DeconzAlarmControlPanel(sensor, gateway, alarm_system_id)]
)
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_sensor, async_add_sensor,
gateway.api.sensors.ancillary_control, hub.api.sensors.ancillary_control,
) )
@ -95,11 +91,11 @@ class DeconzAlarmControlPanel(DeconzDevice[AncillaryControl], AlarmControlPanelE
def __init__( def __init__(
self, self,
device: AncillaryControl, device: AncillaryControl,
gateway: DeconzHub, hub: DeconzHub,
alarm_system_id: str, alarm_system_id: str,
) -> None: ) -> None:
"""Set up alarm control panel device.""" """Set up alarm control panel device."""
super().__init__(device, gateway) super().__init__(device, hub)
self.alarm_system_id = alarm_system_id self.alarm_system_id = alarm_system_id
@callback @callback
@ -118,27 +114,27 @@ class DeconzAlarmControlPanel(DeconzDevice[AncillaryControl], AlarmControlPanelE
async def async_alarm_arm_away(self, code: str | None = None) -> None: async def async_alarm_arm_away(self, code: str | None = None) -> None:
"""Send arm away command.""" """Send arm away command."""
if code: if code:
await self.gateway.api.alarm_systems.arm( await self.hub.api.alarm_systems.arm(
self.alarm_system_id, AlarmSystemArmAction.AWAY, code self.alarm_system_id, AlarmSystemArmAction.AWAY, code
) )
async def async_alarm_arm_home(self, code: str | None = None) -> None: async def async_alarm_arm_home(self, code: str | None = None) -> None:
"""Send arm home command.""" """Send arm home command."""
if code: if code:
await self.gateway.api.alarm_systems.arm( await self.hub.api.alarm_systems.arm(
self.alarm_system_id, AlarmSystemArmAction.STAY, code self.alarm_system_id, AlarmSystemArmAction.STAY, code
) )
async def async_alarm_arm_night(self, code: str | None = None) -> None: async def async_alarm_arm_night(self, code: str | None = None) -> None:
"""Send arm night command.""" """Send arm night command."""
if code: if code:
await self.gateway.api.alarm_systems.arm( await self.hub.api.alarm_systems.arm(
self.alarm_system_id, AlarmSystemArmAction.NIGHT, code self.alarm_system_id, AlarmSystemArmAction.NIGHT, code
) )
async def async_alarm_disarm(self, code: str | None = None) -> None: async def async_alarm_disarm(self, code: str | None = None) -> None:
"""Send disarm command.""" """Send disarm command."""
if code: if code:
await self.gateway.api.alarm_systems.arm( await self.hub.api.alarm_systems.arm(
self.alarm_system_id, AlarmSystemArmAction.DISARM, code self.alarm_system_id, AlarmSystemArmAction.DISARM, code
) )

View file

@ -31,7 +31,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ATTR_DARK, ATTR_ON from .const import ATTR_DARK, ATTR_ON
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry from .hub import DeconzHub
_SensorDeviceT = TypeVar("_SensorDeviceT", bound=PydeconzSensorBase) _SensorDeviceT = TypeVar("_SensorDeviceT", bound=PydeconzSensorBase)
@ -168,13 +168,13 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the deCONZ binary sensor.""" """Set up the deCONZ binary sensor."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_sensor(_: EventType, sensor_id: str) -> None: def async_add_sensor(_: EventType, sensor_id: str) -> None:
"""Add sensor from deCONZ.""" """Add sensor from deCONZ."""
sensor = gateway.api.sensors[sensor_id] sensor = hub.api.sensors[sensor_id]
for description in ENTITY_DESCRIPTIONS: for description in ENTITY_DESCRIPTIONS:
if ( if (
@ -182,11 +182,11 @@ async def async_setup_entry(
and not isinstance(sensor, description.instance_check) and not isinstance(sensor, description.instance_check)
) or description.value_fn(sensor) is None: ) or description.value_fn(sensor) is None:
continue continue
async_add_entities([DeconzBinarySensor(sensor, gateway, description)]) async_add_entities([DeconzBinarySensor(sensor, hub, description)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_sensor, async_add_sensor,
gateway.api.sensors, hub.api.sensors,
) )
@ -199,7 +199,7 @@ class DeconzBinarySensor(DeconzDevice[SensorResources], BinarySensorEntity):
def __init__( def __init__(
self, self,
device: SensorResources, device: SensorResources,
gateway: DeconzHub, hub: DeconzHub,
description: DeconzBinarySensorDescription, description: DeconzBinarySensorDescription,
) -> None: ) -> None:
"""Initialize deCONZ binary sensor.""" """Initialize deCONZ binary sensor."""
@ -208,7 +208,7 @@ class DeconzBinarySensor(DeconzDevice[SensorResources], BinarySensorEntity):
self._update_key = description.update_key self._update_key = description.update_key
if description.name_suffix: if description.name_suffix:
self._name_suffix = description.name_suffix self._name_suffix = description.name_suffix
super().__init__(device, gateway) super().__init__(device, hub)
if ( if (
self.entity_description.key in PROVIDES_EXTRA_ATTRIBUTES self.entity_description.key in PROVIDES_EXTRA_ATTRIBUTES

View file

@ -20,7 +20,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .deconz_device import DeconzDevice, DeconzSceneMixin from .deconz_device import DeconzDevice, DeconzSceneMixin
from .hub import DeconzHub, get_gateway_from_config_entry from .hub import DeconzHub
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -50,33 +50,33 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the deCONZ button entity.""" """Set up the deCONZ button entity."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_scene(_: EventType, scene_id: str) -> None: def async_add_scene(_: EventType, scene_id: str) -> None:
"""Add scene button from deCONZ.""" """Add scene button from deCONZ."""
scene = gateway.api.scenes[scene_id] scene = hub.api.scenes[scene_id]
async_add_entities( async_add_entities(
DeconzSceneButton(scene, gateway, description) DeconzSceneButton(scene, hub, description)
for description in ENTITY_DESCRIPTIONS.get(PydeconzScene, []) for description in ENTITY_DESCRIPTIONS.get(PydeconzScene, [])
) )
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_scene, async_add_scene,
gateway.api.scenes, hub.api.scenes,
) )
@callback @callback
def async_add_presence_sensor(_: EventType, sensor_id: str) -> None: def async_add_presence_sensor(_: EventType, sensor_id: str) -> None:
"""Add presence sensor reset button from deCONZ.""" """Add presence sensor reset button from deCONZ."""
sensor = gateway.api.sensors.presence[sensor_id] sensor = hub.api.sensors.presence[sensor_id]
if sensor.presence_event is not None: if sensor.presence_event is not None:
async_add_entities([DeconzPresenceResetButton(sensor, gateway)]) async_add_entities([DeconzPresenceResetButton(sensor, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_presence_sensor, async_add_presence_sensor,
gateway.api.sensors.presence, hub.api.sensors.presence,
) )
@ -88,19 +88,19 @@ class DeconzSceneButton(DeconzSceneMixin, ButtonEntity):
def __init__( def __init__(
self, self,
device: PydeconzScene, device: PydeconzScene,
gateway: DeconzHub, hub: DeconzHub,
description: DeconzButtonDescription, description: DeconzButtonDescription,
) -> None: ) -> None:
"""Initialize deCONZ number entity.""" """Initialize deCONZ number entity."""
self.entity_description: DeconzButtonDescription = description self.entity_description: DeconzButtonDescription = description
super().__init__(device, gateway) super().__init__(device, hub)
self._attr_name = f"{self._attr_name} {description.suffix}" self._attr_name = f"{self._attr_name} {description.suffix}"
async def async_press(self) -> None: async def async_press(self) -> None:
"""Store light states into scene.""" """Store light states into scene."""
async_button_fn = getattr( async_button_fn = getattr(
self.gateway.api.scenes, self.hub.api.scenes,
self.entity_description.button_fn, self.entity_description.button_fn,
) )
await async_button_fn(self._device.group_id, self._device.id) await async_button_fn(self._device.group_id, self._device.id)
@ -123,7 +123,7 @@ class DeconzPresenceResetButton(DeconzDevice[Presence], ButtonEntity):
async def async_press(self) -> None: async def async_press(self) -> None:
"""Store reset presence state.""" """Store reset presence state."""
await self.gateway.api.sensors.presence.set_config( await self.hub.api.sensors.presence.set_config(
id=self._device.resource_id, id=self._device.resource_id,
reset_presence=True, reset_presence=True,
) )

View file

@ -35,7 +35,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ATTR_LOCKED, ATTR_OFFSET, ATTR_VALVE from .const import ATTR_LOCKED, ATTR_OFFSET, ATTR_VALVE
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry from .hub import DeconzHub
DECONZ_FAN_SMART = "smart" DECONZ_FAN_SMART = "smart"
@ -80,18 +80,18 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the deCONZ climate devices.""" """Set up the deCONZ climate devices."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_climate(_: EventType, climate_id: str) -> None: def async_add_climate(_: EventType, climate_id: str) -> None:
"""Add climate from deCONZ.""" """Add climate from deCONZ."""
climate = gateway.api.sensors.thermostat[climate_id] climate = hub.api.sensors.thermostat[climate_id]
async_add_entities([DeconzThermostat(climate, gateway)]) async_add_entities([DeconzThermostat(climate, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_climate, async_add_climate,
gateway.api.sensors.thermostat, hub.api.sensors.thermostat,
) )
@ -103,9 +103,9 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
_attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False _enable_turn_on_off_backwards_compatibility = False
def __init__(self, device: Thermostat, gateway: DeconzHub) -> None: def __init__(self, device: Thermostat, hub: DeconzHub) -> None:
"""Set up thermostat device.""" """Set up thermostat device."""
super().__init__(device, gateway) super().__init__(device, hub)
self._attr_hvac_modes = [ self._attr_hvac_modes = [
HVACMode.HEAT, HVACMode.HEAT,
@ -149,7 +149,7 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
if fan_mode not in FAN_MODE_TO_DECONZ: if fan_mode not in FAN_MODE_TO_DECONZ:
raise ValueError(f"Unsupported fan mode {fan_mode}") raise ValueError(f"Unsupported fan mode {fan_mode}")
await self.gateway.api.sensors.thermostat.set_config( await self.hub.api.sensors.thermostat.set_config(
id=self._device.resource_id, id=self._device.resource_id,
fan_mode=FAN_MODE_TO_DECONZ[fan_mode], fan_mode=FAN_MODE_TO_DECONZ[fan_mode],
) )
@ -169,12 +169,12 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
raise ValueError(f"Unsupported HVAC mode {hvac_mode}") raise ValueError(f"Unsupported HVAC mode {hvac_mode}")
if len(self._attr_hvac_modes) == 2: # Only allow turn on and off thermostat if len(self._attr_hvac_modes) == 2: # Only allow turn on and off thermostat
await self.gateway.api.sensors.thermostat.set_config( await self.hub.api.sensors.thermostat.set_config(
id=self._device.resource_id, id=self._device.resource_id,
on=hvac_mode != HVACMode.OFF, on=hvac_mode != HVACMode.OFF,
) )
else: else:
await self.gateway.api.sensors.thermostat.set_config( await self.hub.api.sensors.thermostat.set_config(
id=self._device.resource_id, id=self._device.resource_id,
mode=HVAC_MODE_TO_DECONZ[hvac_mode], mode=HVAC_MODE_TO_DECONZ[hvac_mode],
) )
@ -208,7 +208,7 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
if preset_mode not in PRESET_MODE_TO_DECONZ: if preset_mode not in PRESET_MODE_TO_DECONZ:
raise ValueError(f"Unsupported preset mode {preset_mode}") raise ValueError(f"Unsupported preset mode {preset_mode}")
await self.gateway.api.sensors.thermostat.set_config( await self.hub.api.sensors.thermostat.set_config(
id=self._device.resource_id, id=self._device.resource_id,
preset=PRESET_MODE_TO_DECONZ[preset_mode], preset=PRESET_MODE_TO_DECONZ[preset_mode],
) )
@ -237,12 +237,12 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}") raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}")
if self._device.mode == ThermostatMode.COOL: if self._device.mode == ThermostatMode.COOL:
await self.gateway.api.sensors.thermostat.set_config( await self.hub.api.sensors.thermostat.set_config(
id=self._device.resource_id, id=self._device.resource_id,
cooling_setpoint=kwargs[ATTR_TEMPERATURE] * 100, cooling_setpoint=kwargs[ATTR_TEMPERATURE] * 100,
) )
else: else:
await self.gateway.api.sensors.thermostat.set_config( await self.hub.api.sensors.thermostat.set_config(
id=self._device.resource_id, id=self._device.resource_id,
heating_setpoint=kwargs[ATTR_TEMPERATURE] * 100, heating_setpoint=kwargs[ATTR_TEMPERATURE] * 100,
) )

View file

@ -52,11 +52,11 @@ CONF_MANUAL_INPUT = "Manually define gateway"
@callback @callback
def get_master_gateway(hass: HomeAssistant) -> DeconzHub: def get_master_hub(hass: HomeAssistant) -> DeconzHub:
"""Return the gateway which is marked as master.""" """Return the gateway which is marked as master."""
for gateway in hass.data[DOMAIN].values(): for hub in hass.data[DOMAIN].values():
if gateway.master: if hub.master:
return cast(DeconzHub, gateway) return cast(DeconzHub, hub)
raise ValueError raise ValueError

View file

@ -22,7 +22,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry from .hub import DeconzHub
DECONZ_TYPE_TO_DEVICE_CLASS = { DECONZ_TYPE_TO_DEVICE_CLASS = {
ResourceType.LEVEL_CONTROLLABLE_OUTPUT.value: CoverDeviceClass.DAMPER, ResourceType.LEVEL_CONTROLLABLE_OUTPUT.value: CoverDeviceClass.DAMPER,
@ -37,17 +37,17 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up covers for deCONZ component.""" """Set up covers for deCONZ component."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_cover(_: EventType, cover_id: str) -> None: def async_add_cover(_: EventType, cover_id: str) -> None:
"""Add cover from deCONZ.""" """Add cover from deCONZ."""
async_add_entities([DeconzCover(cover_id, gateway)]) async_add_entities([DeconzCover(cover_id, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_cover, async_add_cover,
gateway.api.lights.covers, hub.api.lights.covers,
) )
@ -56,9 +56,9 @@ class DeconzCover(DeconzDevice[Cover], CoverEntity):
TYPE = DOMAIN TYPE = DOMAIN
def __init__(self, cover_id: str, gateway: DeconzHub) -> None: def __init__(self, cover_id: str, hub: DeconzHub) -> None:
"""Set up cover device.""" """Set up cover device."""
super().__init__(cover := gateway.api.lights.covers[cover_id], gateway) super().__init__(cover := hub.api.lights.covers[cover_id], hub)
self._attr_supported_features = ( self._attr_supported_features = (
CoverEntityFeature.OPEN CoverEntityFeature.OPEN
@ -92,7 +92,7 @@ class DeconzCover(DeconzDevice[Cover], CoverEntity):
async def async_set_cover_position(self, **kwargs: Any) -> None: async def async_set_cover_position(self, **kwargs: Any) -> None:
"""Move the cover to a specific position.""" """Move the cover to a specific position."""
position = 100 - cast(int, kwargs[ATTR_POSITION]) position = 100 - cast(int, kwargs[ATTR_POSITION])
await self.gateway.api.lights.covers.set_state( await self.hub.api.lights.covers.set_state(
id=self._device.resource_id, id=self._device.resource_id,
lift=position, lift=position,
legacy_mode=self.legacy_mode, legacy_mode=self.legacy_mode,
@ -100,7 +100,7 @@ class DeconzCover(DeconzDevice[Cover], CoverEntity):
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Open cover.""" """Open cover."""
await self.gateway.api.lights.covers.set_state( await self.hub.api.lights.covers.set_state(
id=self._device.resource_id, id=self._device.resource_id,
action=CoverAction.OPEN, action=CoverAction.OPEN,
legacy_mode=self.legacy_mode, legacy_mode=self.legacy_mode,
@ -108,7 +108,7 @@ class DeconzCover(DeconzDevice[Cover], CoverEntity):
async def async_close_cover(self, **kwargs: Any) -> None: async def async_close_cover(self, **kwargs: Any) -> None:
"""Close cover.""" """Close cover."""
await self.gateway.api.lights.covers.set_state( await self.hub.api.lights.covers.set_state(
id=self._device.resource_id, id=self._device.resource_id,
action=CoverAction.CLOSE, action=CoverAction.CLOSE,
legacy_mode=self.legacy_mode, legacy_mode=self.legacy_mode,
@ -116,7 +116,7 @@ class DeconzCover(DeconzDevice[Cover], CoverEntity):
async def async_stop_cover(self, **kwargs: Any) -> None: async def async_stop_cover(self, **kwargs: Any) -> None:
"""Stop cover.""" """Stop cover."""
await self.gateway.api.lights.covers.set_state( await self.hub.api.lights.covers.set_state(
id=self._device.resource_id, id=self._device.resource_id,
action=CoverAction.STOP, action=CoverAction.STOP,
legacy_mode=self.legacy_mode, legacy_mode=self.legacy_mode,
@ -132,7 +132,7 @@ class DeconzCover(DeconzDevice[Cover], CoverEntity):
async def async_set_cover_tilt_position(self, **kwargs: Any) -> None: async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
"""Tilt the cover to a specific position.""" """Tilt the cover to a specific position."""
position = 100 - cast(int, kwargs[ATTR_TILT_POSITION]) position = 100 - cast(int, kwargs[ATTR_TILT_POSITION])
await self.gateway.api.lights.covers.set_state( await self.hub.api.lights.covers.set_state(
id=self._device.resource_id, id=self._device.resource_id,
tilt=position, tilt=position,
legacy_mode=self.legacy_mode, legacy_mode=self.legacy_mode,
@ -140,7 +140,7 @@ class DeconzCover(DeconzDevice[Cover], CoverEntity):
async def async_open_cover_tilt(self, **kwargs: Any) -> None: async def async_open_cover_tilt(self, **kwargs: Any) -> None:
"""Open cover tilt.""" """Open cover tilt."""
await self.gateway.api.lights.covers.set_state( await self.hub.api.lights.covers.set_state(
id=self._device.resource_id, id=self._device.resource_id,
tilt=0, tilt=0,
legacy_mode=self.legacy_mode, legacy_mode=self.legacy_mode,
@ -148,7 +148,7 @@ class DeconzCover(DeconzDevice[Cover], CoverEntity):
async def async_close_cover_tilt(self, **kwargs: Any) -> None: async def async_close_cover_tilt(self, **kwargs: Any) -> None:
"""Close cover tilt.""" """Close cover tilt."""
await self.gateway.api.lights.covers.set_state( await self.hub.api.lights.covers.set_state(
id=self._device.resource_id, id=self._device.resource_id,
tilt=100, tilt=100,
legacy_mode=self.legacy_mode, legacy_mode=self.legacy_mode,
@ -156,7 +156,7 @@ class DeconzCover(DeconzDevice[Cover], CoverEntity):
async def async_stop_cover_tilt(self, **kwargs: Any) -> None: async def async_stop_cover_tilt(self, **kwargs: Any) -> None:
"""Stop cover tilt.""" """Stop cover tilt."""
await self.gateway.api.lights.covers.set_state( await self.hub.api.lights.covers.set_state(
id=self._device.resource_id, id=self._device.resource_id,
action=CoverAction.STOP, action=CoverAction.STOP,
legacy_mode=self.legacy_mode, legacy_mode=self.legacy_mode,

View file

@ -33,11 +33,11 @@ class DeconzBase(Generic[_DeviceT]):
def __init__( def __init__(
self, self,
device: _DeviceT, device: _DeviceT,
gateway: DeconzHub, hub: DeconzHub,
) -> None: ) -> None:
"""Set up device and add update callback to get data from websocket.""" """Set up device and add update callback to get data from websocket."""
self._device: _DeviceT = device self._device: _DeviceT = device
self.gateway = gateway self.hub = hub
@property @property
def unique_id(self) -> str: def unique_id(self) -> str:
@ -67,7 +67,7 @@ class DeconzBase(Generic[_DeviceT]):
model=self._device.model_id, model=self._device.model_id,
name=self._device.name, name=self._device.name,
sw_version=self._device.software_version, sw_version=self._device.software_version,
via_device=(DECONZ_DOMAIN, self.gateway.api.config.bridge_id), via_device=(DECONZ_DOMAIN, self.hub.api.config.bridge_id),
) )
@ -85,11 +85,11 @@ class DeconzDevice(DeconzBase[_DeviceT], Entity):
def __init__( def __init__(
self, self,
device: _DeviceT, device: _DeviceT,
gateway: DeconzHub, hub: DeconzHub,
) -> None: ) -> None:
"""Set up device and add update callback to get data from websocket.""" """Set up device and add update callback to get data from websocket."""
super().__init__(device, gateway) super().__init__(device, hub)
self.gateway.entities[self.TYPE].add(self.unique_id) self.hub.entities[self.TYPE].add(self.unique_id)
self._attr_name = self._device.name self._attr_name = self._device.name
if self._name_suffix is not None: if self._name_suffix is not None:
@ -103,11 +103,11 @@ class DeconzDevice(DeconzBase[_DeviceT], Entity):
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
"""Subscribe to device events.""" """Subscribe to device events."""
self._device.register_callback(self.async_update_callback) self._device.register_callback(self.async_update_callback)
self.gateway.deconz_ids[self.entity_id] = self._device.deconz_id self.hub.deconz_ids[self.entity_id] = self._device.deconz_id
self.async_on_remove( self.async_on_remove(
async_dispatcher_connect( async_dispatcher_connect(
self.hass, self.hass,
self.gateway.signal_reachable, self.hub.signal_reachable,
self.async_update_connection_state, self.async_update_connection_state,
) )
) )
@ -115,8 +115,8 @@ class DeconzDevice(DeconzBase[_DeviceT], Entity):
async def async_will_remove_from_hass(self) -> None: async def async_will_remove_from_hass(self) -> None:
"""Disconnect device object when removed.""" """Disconnect device object when removed."""
self._device.remove_callback(self.async_update_callback) self._device.remove_callback(self.async_update_callback)
del self.gateway.deconz_ids[self.entity_id] del self.hub.deconz_ids[self.entity_id]
self.gateway.entities[self.TYPE].remove(self.unique_id) self.hub.entities[self.TYPE].remove(self.unique_id)
@callback @callback
def async_update_connection_state(self) -> None: def async_update_connection_state(self) -> None:
@ -126,7 +126,7 @@ class DeconzDevice(DeconzBase[_DeviceT], Entity):
@callback @callback
def async_update_callback(self) -> None: def async_update_callback(self) -> None:
"""Update the device's state.""" """Update the device's state."""
if self.gateway.ignore_state_updates: if self.hub.ignore_state_updates:
return return
if self._update_keys is not None and not self._device.changed_keys.intersection( if self._update_keys is not None and not self._device.changed_keys.intersection(
@ -140,8 +140,8 @@ class DeconzDevice(DeconzBase[_DeviceT], Entity):
def available(self) -> bool: def available(self) -> bool:
"""Return True if device is available.""" """Return True if device is available."""
if isinstance(self._device, PydeconzScene): if isinstance(self._device, PydeconzScene):
return self.gateway.available return self.hub.available
return self.gateway.available and self._device.reachable # type: ignore[union-attr] return self.hub.available and self._device.reachable # type: ignore[union-attr]
class DeconzSceneMixin(DeconzDevice[PydeconzScene]): class DeconzSceneMixin(DeconzDevice[PydeconzScene]):
@ -152,23 +152,23 @@ class DeconzSceneMixin(DeconzDevice[PydeconzScene]):
def __init__( def __init__(
self, self,
device: PydeconzScene, device: PydeconzScene,
gateway: DeconzHub, hub: DeconzHub,
) -> None: ) -> None:
"""Set up a scene.""" """Set up a scene."""
super().__init__(device, gateway) super().__init__(device, hub)
self.group = self.gateway.api.groups[device.group_id] self.group = self.hub.api.groups[device.group_id]
self._attr_name = device.name self._attr_name = device.name
self._group_identifier = self.get_parent_identifier() self._group_identifier = self.get_parent_identifier()
def get_device_identifier(self) -> str: def get_device_identifier(self) -> str:
"""Describe a unique identifier for this scene.""" """Describe a unique identifier for this scene."""
return f"{self.gateway.bridgeid}{self._device.deconz_id}" return f"{self.hub.bridgeid}{self._device.deconz_id}"
def get_parent_identifier(self) -> str: def get_parent_identifier(self) -> str:
"""Describe a unique identifier for group this scene belongs to.""" """Describe a unique identifier for group this scene belongs to."""
return f"{self.gateway.bridgeid}-{self.group.deconz_id}" return f"{self.hub.bridgeid}-{self.group.deconz_id}"
@property @property
def unique_id(self) -> str: def unique_id(self) -> str:
@ -183,5 +183,5 @@ class DeconzSceneMixin(DeconzDevice[PydeconzScene]):
manufacturer="Dresden Elektronik", manufacturer="Dresden Elektronik",
model="deCONZ group", model="deCONZ group",
name=self.group.name, name=self.group.name,
via_device=(DECONZ_DOMAIN, self.gateway.api.config.bridge_id), via_device=(DECONZ_DOMAIN, self.hub.api.config.bridge_id),
) )

View file

@ -55,57 +55,57 @@ RELATIVE_ROTARY_DECONZ_TO_EVENT = {
} }
async def async_setup_events(gateway: DeconzHub) -> None: async def async_setup_events(hub: DeconzHub) -> None:
"""Set up the deCONZ events.""" """Set up the deCONZ events."""
@callback @callback
def async_add_sensor(_: EventType, sensor_id: str) -> None: def async_add_sensor(_: EventType, sensor_id: str) -> None:
"""Create DeconzEvent.""" """Create DeconzEvent."""
new_event: DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent | DeconzRelativeRotaryEvent new_event: DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent | DeconzRelativeRotaryEvent
sensor = gateway.api.sensors[sensor_id] sensor = hub.api.sensors[sensor_id]
if isinstance(sensor, Switch): if isinstance(sensor, Switch):
new_event = DeconzEvent(sensor, gateway) new_event = DeconzEvent(sensor, hub)
elif isinstance(sensor, AncillaryControl): elif isinstance(sensor, AncillaryControl):
new_event = DeconzAlarmEvent(sensor, gateway) new_event = DeconzAlarmEvent(sensor, hub)
elif isinstance(sensor, Presence): elif isinstance(sensor, Presence):
if sensor.presence_event is None: if sensor.presence_event is None:
return return
new_event = DeconzPresenceEvent(sensor, gateway) new_event = DeconzPresenceEvent(sensor, hub)
elif isinstance(sensor, RelativeRotary): elif isinstance(sensor, RelativeRotary):
new_event = DeconzRelativeRotaryEvent(sensor, gateway) new_event = DeconzRelativeRotaryEvent(sensor, hub)
gateway.hass.async_create_task(new_event.async_update_device_registry()) hub.hass.async_create_task(new_event.async_update_device_registry())
gateway.events.append(new_event) hub.events.append(new_event)
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_sensor, async_add_sensor,
gateway.api.sensors.switch, hub.api.sensors.switch,
) )
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_sensor, async_add_sensor,
gateway.api.sensors.ancillary_control, hub.api.sensors.ancillary_control,
) )
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_sensor, async_add_sensor,
gateway.api.sensors.presence, hub.api.sensors.presence,
) )
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_sensor, async_add_sensor,
gateway.api.sensors.relative_rotary, hub.api.sensors.relative_rotary,
) )
@callback @callback
def async_unload_events(gateway: DeconzHub) -> None: def async_unload_events(hub: DeconzHub) -> None:
"""Unload all deCONZ events.""" """Unload all deCONZ events."""
for event in gateway.events: for event in hub.events:
event.async_will_remove_from_hass() event.async_will_remove_from_hass()
gateway.events.clear() hub.events.clear()
class DeconzEventBase(DeconzBase): class DeconzEventBase(DeconzBase):
@ -118,10 +118,10 @@ class DeconzEventBase(DeconzBase):
def __init__( def __init__(
self, self,
device: AncillaryControl | Presence | RelativeRotary | Switch, device: AncillaryControl | Presence | RelativeRotary | Switch,
gateway: DeconzHub, hub: DeconzHub,
) -> None: ) -> None:
"""Register callback that will be used for signals.""" """Register callback that will be used for signals."""
super().__init__(device, gateway) super().__init__(device, hub)
self._unsubscribe = device.subscribe(self.async_update_callback) self._unsubscribe = device.subscribe(self.async_update_callback)
@ -145,10 +145,10 @@ class DeconzEventBase(DeconzBase):
if not self.device_info: if not self.device_info:
return return
device_registry = dr.async_get(self.gateway.hass) device_registry = dr.async_get(self.hub.hass)
entry = device_registry.async_get_or_create( entry = device_registry.async_get_or_create(
config_entry_id=self.gateway.config_entry.entry_id, **self.device_info config_entry_id=self.hub.config_entry.entry_id, **self.device_info
) )
self.device_id = entry.id self.device_id = entry.id
@ -165,10 +165,7 @@ class DeconzEvent(DeconzEventBase):
@callback @callback
def async_update_callback(self) -> None: def async_update_callback(self) -> None:
"""Fire the event if reason is that state is updated.""" """Fire the event if reason is that state is updated."""
if ( if self.hub.ignore_state_updates or "state" not in self._device.changed_keys:
self.gateway.ignore_state_updates
or "state" not in self._device.changed_keys
):
return return
data: dict[str, Any] = { data: dict[str, Any] = {
@ -189,7 +186,7 @@ class DeconzEvent(DeconzEventBase):
if self._device.xy is not None: if self._device.xy is not None:
data[CONF_XY] = self._device.xy data[CONF_XY] = self._device.xy
self.gateway.hass.bus.async_fire(CONF_DECONZ_EVENT, data) self.hub.hass.bus.async_fire(CONF_DECONZ_EVENT, data)
class DeconzAlarmEvent(DeconzEventBase): class DeconzAlarmEvent(DeconzEventBase):
@ -201,7 +198,7 @@ class DeconzAlarmEvent(DeconzEventBase):
def async_update_callback(self) -> None: def async_update_callback(self) -> None:
"""Fire the event if reason is new action is updated.""" """Fire the event if reason is new action is updated."""
if ( if (
self.gateway.ignore_state_updates self.hub.ignore_state_updates
or "action" not in self._device.changed_keys or "action" not in self._device.changed_keys
or self._device.action not in SUPPORTED_DECONZ_ALARM_EVENTS or self._device.action not in SUPPORTED_DECONZ_ALARM_EVENTS
): ):
@ -214,7 +211,7 @@ class DeconzAlarmEvent(DeconzEventBase):
CONF_EVENT: self._device.action.value, CONF_EVENT: self._device.action.value,
} }
self.gateway.hass.bus.async_fire(CONF_DECONZ_ALARM_EVENT, data) self.hub.hass.bus.async_fire(CONF_DECONZ_ALARM_EVENT, data)
class DeconzPresenceEvent(DeconzEventBase): class DeconzPresenceEvent(DeconzEventBase):
@ -226,7 +223,7 @@ class DeconzPresenceEvent(DeconzEventBase):
def async_update_callback(self) -> None: def async_update_callback(self) -> None:
"""Fire the event if reason is new action is updated.""" """Fire the event if reason is new action is updated."""
if ( if (
self.gateway.ignore_state_updates self.hub.ignore_state_updates
or "presenceevent" not in self._device.changed_keys or "presenceevent" not in self._device.changed_keys
or self._device.presence_event not in SUPPORTED_DECONZ_PRESENCE_EVENTS or self._device.presence_event not in SUPPORTED_DECONZ_PRESENCE_EVENTS
): ):
@ -239,7 +236,7 @@ class DeconzPresenceEvent(DeconzEventBase):
CONF_EVENT: self._device.presence_event.value, CONF_EVENT: self._device.presence_event.value,
} }
self.gateway.hass.bus.async_fire(CONF_DECONZ_PRESENCE_EVENT, data) self.hub.hass.bus.async_fire(CONF_DECONZ_PRESENCE_EVENT, data)
class DeconzRelativeRotaryEvent(DeconzEventBase): class DeconzRelativeRotaryEvent(DeconzEventBase):
@ -251,7 +248,7 @@ class DeconzRelativeRotaryEvent(DeconzEventBase):
def async_update_callback(self) -> None: def async_update_callback(self) -> None:
"""Fire the event if reason is new action is updated.""" """Fire the event if reason is new action is updated."""
if ( if (
self.gateway.ignore_state_updates self.hub.ignore_state_updates
or "rotaryevent" not in self._device.changed_keys or "rotaryevent" not in self._device.changed_keys
): ):
return return
@ -265,4 +262,4 @@ class DeconzRelativeRotaryEvent(DeconzEventBase):
ATTR_DURATION: self._device.expected_event_duration, ATTR_DURATION: self._device.expected_event_duration,
} }
self.gateway.hass.bus.async_fire(CONF_DECONZ_RELATIVE_ROTARY_EVENT, data) self.hub.hass.bus.async_fire(CONF_DECONZ_RELATIVE_ROTARY_EVENT, data)

View file

@ -656,9 +656,9 @@ def _get_deconz_event_from_device(
device: dr.DeviceEntry, device: dr.DeviceEntry,
) -> DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent | DeconzRelativeRotaryEvent: ) -> DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent | DeconzRelativeRotaryEvent:
"""Resolve deconz event from device.""" """Resolve deconz event from device."""
gateways: dict[str, DeconzHub] = hass.data.get(DOMAIN, {}) hubs: dict[str, DeconzHub] = hass.data.get(DOMAIN, {})
for gateway in gateways.values(): for hub in hubs.values():
for deconz_event in gateway.events: for deconz_event in hub.events:
if device.id == deconz_event.device_id: if device.id == deconz_event.device_id:
return deconz_event return deconz_event

View file

@ -9,7 +9,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_UNIQUE_ID from homeassistant.const import CONF_API_KEY, CONF_UNIQUE_ID
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .hub import get_gateway_from_config_entry from .hub import DeconzHub
REDACT_CONFIG = {CONF_API_KEY, CONF_UNIQUE_ID} REDACT_CONFIG = {CONF_API_KEY, CONF_UNIQUE_ID}
REDACT_DECONZ_CONFIG = {"bridgeid", "mac", "panid"} REDACT_DECONZ_CONFIG = {"bridgeid", "mac", "panid"}
@ -19,29 +19,27 @@ async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry hass: HomeAssistant, config_entry: ConfigEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a config entry.""" """Return diagnostics for a config entry."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
diag: dict[str, Any] = {} diag: dict[str, Any] = {}
diag["config"] = async_redact_data(config_entry.as_dict(), REDACT_CONFIG) diag["config"] = async_redact_data(config_entry.as_dict(), REDACT_CONFIG)
diag["deconz_config"] = async_redact_data( diag["deconz_config"] = async_redact_data(hub.api.config.raw, REDACT_DECONZ_CONFIG)
gateway.api.config.raw, REDACT_DECONZ_CONFIG
)
diag["websocket_state"] = ( diag["websocket_state"] = (
gateway.api.websocket.state.value if gateway.api.websocket else "Unknown" hub.api.websocket.state.value if hub.api.websocket else "Unknown"
) )
diag["deconz_ids"] = gateway.deconz_ids diag["deconz_ids"] = hub.deconz_ids
diag["entities"] = gateway.entities diag["entities"] = hub.entities
diag["events"] = { diag["events"] = {
event.serial: { event.serial: {
"event_id": event.event_id, "event_id": event.event_id,
"event_type": type(event).__name__, "event_type": type(event).__name__,
} }
for event in gateway.events for event in hub.events
} }
diag["alarm_systems"] = {k: v.raw for k, v in gateway.api.alarm_systems.items()} diag["alarm_systems"] = {k: v.raw for k, v in hub.api.alarm_systems.items()}
diag["groups"] = {k: v.raw for k, v in gateway.api.groups.items()} diag["groups"] = {k: v.raw for k, v in hub.api.groups.items()}
diag["lights"] = {k: v.raw for k, v in gateway.api.lights.items()} diag["lights"] = {k: v.raw for k, v in hub.api.lights.items()}
diag["scenes"] = {k: v.raw for k, v in gateway.api.scenes.items()} diag["scenes"] = {k: v.raw for k, v in hub.api.scenes.items()}
diag["sensors"] = {k: v.raw for k, v in gateway.api.sensors.items()} diag["sensors"] = {k: v.raw for k, v in hub.api.sensors.items()}
return diag return diag

View file

@ -17,7 +17,7 @@ from homeassistant.util.percentage import (
) )
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry from .hub import DeconzHub
ORDERED_NAMED_FAN_SPEEDS: list[LightFanSpeed] = [ ORDERED_NAMED_FAN_SPEEDS: list[LightFanSpeed] = [
LightFanSpeed.PERCENT_25, LightFanSpeed.PERCENT_25,
@ -33,20 +33,20 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up fans for deCONZ component.""" """Set up fans for deCONZ component."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_fan(_: EventType, fan_id: str) -> None: def async_add_fan(_: EventType, fan_id: str) -> None:
"""Add fan from deCONZ.""" """Add fan from deCONZ."""
fan = gateway.api.lights.lights[fan_id] fan = hub.api.lights.lights[fan_id]
if not fan.supports_fan_speed: if not fan.supports_fan_speed:
return return
async_add_entities([DeconzFan(fan, gateway)]) async_add_entities([DeconzFan(fan, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_fan, async_add_fan,
gateway.api.lights.lights, hub.api.lights.lights,
) )
@ -58,9 +58,9 @@ class DeconzFan(DeconzDevice[Light], FanEntity):
_attr_supported_features = FanEntityFeature.SET_SPEED _attr_supported_features = FanEntityFeature.SET_SPEED
def __init__(self, device: Light, gateway: DeconzHub) -> None: def __init__(self, device: Light, hub: DeconzHub) -> None:
"""Set up fan.""" """Set up fan."""
super().__init__(device, gateway) super().__init__(device, hub)
_attr_speed_count = len(ORDERED_NAMED_FAN_SPEEDS) _attr_speed_count = len(ORDERED_NAMED_FAN_SPEEDS)
if device.fan_speed in ORDERED_NAMED_FAN_SPEEDS: if device.fan_speed in ORDERED_NAMED_FAN_SPEEDS:
self._default_on_speed = device.fan_speed self._default_on_speed = device.fan_speed
@ -92,7 +92,7 @@ class DeconzFan(DeconzDevice[Light], FanEntity):
"""Set the speed percentage of the fan.""" """Set the speed percentage of the fan."""
if percentage == 0: if percentage == 0:
return await self.async_turn_off() return await self.async_turn_off()
await self.gateway.api.lights.lights.set_state( await self.hub.api.lights.lights.set_state(
id=self._device.resource_id, id=self._device.resource_id,
fan_speed=percentage_to_ordered_list_item( fan_speed=percentage_to_ordered_list_item(
ORDERED_NAMED_FAN_SPEEDS, percentage ORDERED_NAMED_FAN_SPEEDS, percentage
@ -109,14 +109,14 @@ class DeconzFan(DeconzDevice[Light], FanEntity):
if percentage is not None: if percentage is not None:
await self.async_set_percentage(percentage) await self.async_set_percentage(percentage)
return return
await self.gateway.api.lights.lights.set_state( await self.hub.api.lights.lights.set_state(
id=self._device.resource_id, id=self._device.resource_id,
fan_speed=self._default_on_speed, fan_speed=self._default_on_speed,
) )
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off fan.""" """Turn off fan."""
await self.gateway.api.lights.lights.set_state( await self.hub.api.lights.lights.set_state(
id=self._device.resource_id, id=self._device.resource_id,
fan_speed=LightFanSpeed.OFF, fan_speed=LightFanSpeed.OFF,
) )

View file

@ -1,5 +1,4 @@
"""Internal functionality not part of HA infrastructure.""" """Internal functionality not part of HA infrastructure."""
from .api import get_deconz_api # noqa: F401 from .api import get_deconz_api # noqa: F401
from .config import DeconzConfig # noqa: F401 from .hub import DeconzHub # noqa: F401
from .hub import DeconzHub, get_gateway_from_config_entry # noqa: F401

View file

@ -94,6 +94,12 @@ class DeconzHub:
self.deconz_groups: set[tuple[Callable[[EventType, str], None], str]] = set() self.deconz_groups: set[tuple[Callable[[EventType, str], None], str]] = set()
self.ignored_devices: set[tuple[Callable[[EventType, str], None], str]] = set() self.ignored_devices: set[tuple[Callable[[EventType, str], None], str]] = set()
@callback
@staticmethod
def get_hub(hass: HomeAssistant, config_entry: ConfigEntry) -> DeconzHub:
"""Return hub with a matching config entry ID."""
return cast(DeconzHub, hass.data[DECONZ_DOMAIN][config_entry.entry_id])
@property @property
def bridgeid(self) -> str: def bridgeid(self) -> str:
"""Return the unique identifier of the gateway.""" """Return the unique identifier of the gateway."""
@ -215,16 +221,16 @@ class DeconzHub:
# A race condition can occur if multiple config entries are # A race condition can occur if multiple config entries are
# unloaded in parallel # unloaded in parallel
return return
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
previous_config = gateway.config previous_config = hub.config
gateway.config = DeconzConfig.from_config_entry(config_entry) hub.config = DeconzConfig.from_config_entry(config_entry)
if previous_config.host != gateway.config.host: if previous_config.host != hub.config.host:
gateway.api.close() hub.api.close()
gateway.api.host = gateway.config.host hub.api.host = hub.config.host
gateway.api.start() hub.api.start()
return return
await gateway.options_updated(previous_config) await hub.options_updated(previous_config)
async def options_updated(self, previous_config: DeconzConfig) -> None: async def options_updated(self, previous_config: DeconzConfig) -> None:
"""Manage entities affected by config entry options.""" """Manage entities affected by config entry options."""
@ -292,11 +298,3 @@ class DeconzHub:
self.deconz_ids = {} self.deconz_ids = {}
return True return True
@callback
def get_gateway_from_config_entry(
hass: HomeAssistant, config_entry: ConfigEntry
) -> DeconzHub:
"""Return gateway with a matching config entry ID."""
return cast(DeconzHub, hass.data[DECONZ_DOMAIN][config_entry.entry_id])

View file

@ -36,7 +36,7 @@ from homeassistant.util.color import color_hs_to_xy
from .const import DOMAIN as DECONZ_DOMAIN, POWER_PLUGS from .const import DOMAIN as DECONZ_DOMAIN, POWER_PLUGS
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry from .hub import DeconzHub
DECONZ_GROUP = "is_deconz_group" DECONZ_GROUP = "is_deconz_group"
EFFECT_TO_DECONZ = { EFFECT_TO_DECONZ = {
@ -111,13 +111,13 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the deCONZ lights and groups from a config entry.""" """Set up the deCONZ lights and groups from a config entry."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
entity_registry = er.async_get(hass) entity_registry = er.async_get(hass)
# On/Off Output should be switch not light 2022.5 # On/Off Output should be switch not light 2022.5
for light in gateway.api.lights.lights.values(): for light in hub.api.lights.lights.values():
if light.type == ResourceType.ON_OFF_OUTPUT.value and ( if light.type == ResourceType.ON_OFF_OUTPUT.value and (
entity_id := entity_registry.async_get_entity_id( entity_id := entity_registry.async_get_entity_id(
DOMAIN, DECONZ_DOMAIN, light.unique_id DOMAIN, DECONZ_DOMAIN, light.unique_id
@ -128,15 +128,15 @@ async def async_setup_entry(
@callback @callback
def async_add_light(_: EventType, light_id: str) -> None: def async_add_light(_: EventType, light_id: str) -> None:
"""Add light from deCONZ.""" """Add light from deCONZ."""
light = gateway.api.lights.lights[light_id] light = hub.api.lights.lights[light_id]
if light.type in POWER_PLUGS: if light.type in POWER_PLUGS:
return return
async_add_entities([DeconzLight(light, gateway)]) async_add_entities([DeconzLight(light, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_light, async_add_light,
gateway.api.lights.lights, hub.api.lights.lights,
) )
@callback @callback
@ -145,20 +145,20 @@ async def async_setup_entry(
Update group states based on its sum of related lights. Update group states based on its sum of related lights.
""" """
if (group := gateway.api.groups[group_id]) and not group.lights: if (group := hub.api.groups[group_id]) and not group.lights:
return return
first = True first = True
for light_id in group.lights: for light_id in group.lights:
if (light := gateway.api.lights.lights.get(light_id)) and light.reachable: if (light := hub.api.lights.lights.get(light_id)) and light.reachable:
group.update_color_state(light, update_all_attributes=first) group.update_color_state(light, update_all_attributes=first)
first = False first = False
async_add_entities([DeconzGroup(group, gateway)]) async_add_entities([DeconzGroup(group, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_group, async_add_group,
gateway.api.groups, hub.api.groups,
) )
@ -168,15 +168,15 @@ class DeconzBaseLight(DeconzDevice[_LightDeviceT], LightEntity):
TYPE = DOMAIN TYPE = DOMAIN
_attr_color_mode = ColorMode.UNKNOWN _attr_color_mode = ColorMode.UNKNOWN
def __init__(self, device: _LightDeviceT, gateway: DeconzHub) -> None: def __init__(self, device: _LightDeviceT, hub: DeconzHub) -> None:
"""Set up light.""" """Set up light."""
super().__init__(device, gateway) super().__init__(device, hub)
self.api: GroupHandler | LightHandler self.api: GroupHandler | LightHandler
if isinstance(self._device, Light): if isinstance(self._device, Light):
self.api = self.gateway.api.lights.lights self.api = self.hub.api.lights.lights
elif isinstance(self._device, Group): elif isinstance(self._device, Group):
self.api = self.gateway.api.groups self.api = self.hub.api.groups
self._attr_supported_color_modes: set[ColorMode] = set() self._attr_supported_color_modes: set[ColorMode] = set()
@ -324,7 +324,7 @@ class DeconzLight(DeconzBaseLight[Light]):
super().async_update_callback() super().async_update_callback()
if self._device.reachable and "attr" not in self._device.changed_keys: if self._device.reachable and "attr" not in self._device.changed_keys:
for group in self.gateway.api.groups.values(): for group in self.hub.api.groups.values():
if self._device.resource_id in group.lights: if self._device.resource_id in group.lights:
group.update_color_state(self._device) group.update_color_state(self._device)
@ -334,10 +334,10 @@ class DeconzGroup(DeconzBaseLight[Group]):
_attr_has_entity_name = True _attr_has_entity_name = True
def __init__(self, device: Group, gateway: DeconzHub) -> None: def __init__(self, device: Group, hub: DeconzHub) -> None:
"""Set up group and create an unique id.""" """Set up group and create an unique id."""
self._unique_id = f"{gateway.bridgeid}-{device.deconz_id}" self._unique_id = f"{hub.bridgeid}-{device.deconz_id}"
super().__init__(device, gateway) super().__init__(device, hub)
self._attr_name = None self._attr_name = None
@ -354,7 +354,7 @@ class DeconzGroup(DeconzBaseLight[Group]):
manufacturer="Dresden Elektronik", manufacturer="Dresden Elektronik",
model="deCONZ group", model="deCONZ group",
name=self._device.name, name=self._device.name,
via_device=(DECONZ_DOMAIN, self.gateway.api.config.bridge_id), via_device=(DECONZ_DOMAIN, self.hub.api.config.bridge_id),
) )
@property @property

View file

@ -14,7 +14,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import get_gateway_from_config_entry from .hub import DeconzHub
async def async_setup_entry( async def async_setup_entry(
@ -23,29 +23,29 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up locks for deCONZ component.""" """Set up locks for deCONZ component."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_lock_from_light(_: EventType, lock_id: str) -> None: def async_add_lock_from_light(_: EventType, lock_id: str) -> None:
"""Add lock from deCONZ.""" """Add lock from deCONZ."""
lock = gateway.api.lights.locks[lock_id] lock = hub.api.lights.locks[lock_id]
async_add_entities([DeconzLock(lock, gateway)]) async_add_entities([DeconzLock(lock, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_lock_from_light, async_add_lock_from_light,
gateway.api.lights.locks, hub.api.lights.locks,
) )
@callback @callback
def async_add_lock_from_sensor(_: EventType, lock_id: str) -> None: def async_add_lock_from_sensor(_: EventType, lock_id: str) -> None:
"""Add lock from deCONZ.""" """Add lock from deCONZ."""
lock = gateway.api.sensors.door_lock[lock_id] lock = hub.api.sensors.door_lock[lock_id]
async_add_entities([DeconzLock(lock, gateway)]) async_add_entities([DeconzLock(lock, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_lock_from_sensor, async_add_lock_from_sensor,
gateway.api.sensors.door_lock, hub.api.sensors.door_lock,
always_ignore_clip_sensors=True, always_ignore_clip_sensors=True,
) )
@ -63,12 +63,12 @@ class DeconzLock(DeconzDevice[DoorLock | Lock], LockEntity):
async def async_lock(self, **kwargs: Any) -> None: async def async_lock(self, **kwargs: Any) -> None:
"""Lock the lock.""" """Lock the lock."""
if isinstance(self._device, DoorLock): if isinstance(self._device, DoorLock):
await self.gateway.api.sensors.door_lock.set_config( await self.hub.api.sensors.door_lock.set_config(
id=self._device.resource_id, id=self._device.resource_id,
lock=True, lock=True,
) )
else: else:
await self.gateway.api.lights.locks.set_state( await self.hub.api.lights.locks.set_state(
id=self._device.resource_id, id=self._device.resource_id,
lock=True, lock=True,
) )
@ -76,12 +76,12 @@ class DeconzLock(DeconzDevice[DoorLock | Lock], LockEntity):
async def async_unlock(self, **kwargs: Any) -> None: async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock the lock.""" """Unlock the lock."""
if isinstance(self._device, DoorLock): if isinstance(self._device, DoorLock):
await self.gateway.api.sensors.door_lock.set_config( await self.hub.api.sensors.door_lock.set_config(
id=self._device.resource_id, id=self._device.resource_id,
lock=False, lock=False,
) )
else: else:
await self.gateway.api.lights.locks.set_state( await self.hub.api.lights.locks.set_state(
id=self._device.resource_id, id=self._device.resource_id,
lock=False, lock=False,
) )

View file

@ -23,7 +23,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry from .hub import DeconzHub
T = TypeVar("T", Presence, PydeconzSensorBase) T = TypeVar("T", Presence, PydeconzSensorBase)
@ -73,13 +73,13 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the deCONZ number entity.""" """Set up the deCONZ number entity."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_sensor(_: EventType, sensor_id: str) -> None: def async_add_sensor(_: EventType, sensor_id: str) -> None:
"""Add sensor from deCONZ.""" """Add sensor from deCONZ."""
sensor = gateway.api.sensors.presence[sensor_id] sensor = hub.api.sensors.presence[sensor_id]
for description in ENTITY_DESCRIPTIONS: for description in ENTITY_DESCRIPTIONS:
if ( if (
@ -87,11 +87,11 @@ async def async_setup_entry(
or description.value_fn(sensor) is None or description.value_fn(sensor) is None
): ):
continue continue
async_add_entities([DeconzNumber(sensor, gateway, description)]) async_add_entities([DeconzNumber(sensor, hub, description)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_sensor, async_add_sensor,
gateway.api.sensors.presence, hub.api.sensors.presence,
always_ignore_clip_sensors=True, always_ignore_clip_sensors=True,
) )
@ -105,7 +105,7 @@ class DeconzNumber(DeconzDevice[SensorResources], NumberEntity):
def __init__( def __init__(
self, self,
device: SensorResources, device: SensorResources,
gateway: DeconzHub, hub: DeconzHub,
description: DeconzNumberDescription, description: DeconzNumberDescription,
) -> None: ) -> None:
"""Initialize deCONZ number entity.""" """Initialize deCONZ number entity."""
@ -113,7 +113,7 @@ class DeconzNumber(DeconzDevice[SensorResources], NumberEntity):
self.unique_id_suffix = description.key self.unique_id_suffix = description.key
self._name_suffix = description.name_suffix self._name_suffix = description.name_suffix
self._update_key = description.update_key self._update_key = description.update_key
super().__init__(device, gateway) super().__init__(device, hub)
@property @property
def native_value(self) -> float | None: def native_value(self) -> float | None:
@ -123,7 +123,7 @@ class DeconzNumber(DeconzDevice[SensorResources], NumberEntity):
async def async_set_native_value(self, value: float) -> None: async def async_set_native_value(self, value: float) -> None:
"""Set sensor config.""" """Set sensor config."""
await self.entity_description.set_fn( await self.entity_description.set_fn(
self.gateway.api, self.hub.api,
self._device.resource_id, self._device.resource_id,
int(value), int(value),
) )

View file

@ -12,7 +12,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .deconz_device import DeconzSceneMixin from .deconz_device import DeconzSceneMixin
from .hub import get_gateway_from_config_entry from .hub import DeconzHub
async def async_setup_entry( async def async_setup_entry(
@ -21,18 +21,18 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up scenes for deCONZ integration.""" """Set up scenes for deCONZ integration."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_scene(_: EventType, scene_id: str) -> None: def async_add_scene(_: EventType, scene_id: str) -> None:
"""Add scene from deCONZ.""" """Add scene from deCONZ."""
scene = gateway.api.scenes[scene_id] scene = hub.api.scenes[scene_id]
async_add_entities([DeconzScene(scene, gateway)]) async_add_entities([DeconzScene(scene, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_scene, async_add_scene,
gateway.api.scenes, hub.api.scenes,
) )
@ -43,7 +43,7 @@ class DeconzScene(DeconzSceneMixin, Scene):
async def async_activate(self, **kwargs: Any) -> None: async def async_activate(self, **kwargs: Any) -> None:
"""Activate the scene.""" """Activate the scene."""
await self.gateway.api.scenes.recall( await self.hub.api.scenes.recall(
self._device.group_id, self._device.group_id,
self._device.id, self._device.id,
) )

View file

@ -17,7 +17,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import get_gateway_from_config_entry from .hub import DeconzHub
SENSITIVITY_TO_DECONZ = { SENSITIVITY_TO_DECONZ = {
"High": PresenceConfigSensitivity.HIGH.value, "High": PresenceConfigSensitivity.HIGH.value,
@ -33,25 +33,25 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the deCONZ button entity.""" """Set up the deCONZ button entity."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_presence_sensor(_: EventType, sensor_id: str) -> None: def async_add_presence_sensor(_: EventType, sensor_id: str) -> None:
"""Add presence select entity from deCONZ.""" """Add presence select entity from deCONZ."""
sensor = gateway.api.sensors.presence[sensor_id] sensor = hub.api.sensors.presence[sensor_id]
if sensor.presence_event is not None: if sensor.presence_event is not None:
async_add_entities( async_add_entities(
[ [
DeconzPresenceDeviceModeSelect(sensor, gateway), DeconzPresenceDeviceModeSelect(sensor, hub),
DeconzPresenceSensitivitySelect(sensor, gateway), DeconzPresenceSensitivitySelect(sensor, hub),
DeconzPresenceTriggerDistanceSelect(sensor, gateway), DeconzPresenceTriggerDistanceSelect(sensor, hub),
] ]
) )
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_presence_sensor, async_add_presence_sensor,
gateway.api.sensors.presence, hub.api.sensors.presence,
) )
@ -79,7 +79,7 @@ class DeconzPresenceDeviceModeSelect(DeconzDevice[Presence], SelectEntity):
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
"""Change the selected option.""" """Change the selected option."""
await self.gateway.api.sensors.presence.set_config( await self.hub.api.sensors.presence.set_config(
id=self._device.resource_id, id=self._device.resource_id,
device_mode=PresenceConfigDeviceMode(option), device_mode=PresenceConfigDeviceMode(option),
) )
@ -106,7 +106,7 @@ class DeconzPresenceSensitivitySelect(DeconzDevice[Presence], SelectEntity):
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
"""Change the selected option.""" """Change the selected option."""
await self.gateway.api.sensors.presence.set_config( await self.hub.api.sensors.presence.set_config(
id=self._device.resource_id, id=self._device.resource_id,
sensitivity=SENSITIVITY_TO_DECONZ[option], sensitivity=SENSITIVITY_TO_DECONZ[option],
) )
@ -137,7 +137,7 @@ class DeconzPresenceTriggerDistanceSelect(DeconzDevice[Presence], SelectEntity):
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
"""Change the selected option.""" """Change the selected option."""
await self.gateway.api.sensors.presence.set_config( await self.hub.api.sensors.presence.set_config(
id=self._device.resource_id, id=self._device.resource_id,
trigger_distance=PresenceConfigTriggerDistance(option), trigger_distance=PresenceConfigTriggerDistance(option),
) )

View file

@ -53,7 +53,7 @@ import homeassistant.util.dt as dt_util
from .const import ATTR_DARK, ATTR_ON from .const import ATTR_DARK, ATTR_ON
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry from .hub import DeconzHub
PROVIDES_EXTRA_ATTRIBUTES = ( PROVIDES_EXTRA_ATTRIBUTES = (
"battery", "battery",
@ -295,8 +295,8 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the deCONZ sensors.""" """Set up the deCONZ sensors."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
known_device_entities: dict[str, set[str]] = { known_device_entities: dict[str, set[str]] = {
description.key: set() description.key: set()
@ -307,7 +307,7 @@ async def async_setup_entry(
@callback @callback
def async_add_sensor(_: EventType, sensor_id: str) -> None: def async_add_sensor(_: EventType, sensor_id: str) -> None:
"""Add sensor from deCONZ.""" """Add sensor from deCONZ."""
sensor = gateway.api.sensors[sensor_id] sensor = hub.api.sensors[sensor_id]
entities: list[DeconzSensor] = [] entities: list[DeconzSensor] = []
for description in ENTITY_DESCRIPTIONS: for description in ENTITY_DESCRIPTIONS:
@ -333,20 +333,20 @@ async def async_setup_entry(
known_device_entities[description.key].add(unique_id) known_device_entities[description.key].add(unique_id)
if no_sensor_data and description.key == "battery": if no_sensor_data and description.key == "battery":
DeconzBatteryTracker( DeconzBatteryTracker(
sensor_id, gateway, description, async_add_entities sensor_id, hub, description, async_add_entities
) )
continue continue
if no_sensor_data: if no_sensor_data:
continue continue
entities.append(DeconzSensor(sensor, gateway, description)) entities.append(DeconzSensor(sensor, hub, description))
async_add_entities(entities) async_add_entities(entities)
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_sensor, async_add_sensor,
gateway.api.sensors, hub.api.sensors,
) )
@ -359,7 +359,7 @@ class DeconzSensor(DeconzDevice[SensorResources], SensorEntity):
def __init__( def __init__(
self, self,
device: SensorResources, device: SensorResources,
gateway: DeconzHub, hub: DeconzHub,
description: DeconzSensorDescription, description: DeconzSensorDescription,
) -> None: ) -> None:
"""Initialize deCONZ sensor.""" """Initialize deCONZ sensor."""
@ -368,7 +368,7 @@ class DeconzSensor(DeconzDevice[SensorResources], SensorEntity):
self._update_key = description.update_key self._update_key = description.update_key
if description.name_suffix: if description.name_suffix:
self._name_suffix = description.name_suffix self._name_suffix = description.name_suffix
super().__init__(device, gateway) super().__init__(device, hub)
if ( if (
self.entity_description.key in PROVIDES_EXTRA_ATTRIBUTES self.entity_description.key in PROVIDES_EXTRA_ATTRIBUTES
@ -413,7 +413,7 @@ class DeconzSensor(DeconzDevice[SensorResources], SensorEntity):
attr[ATTR_VOLTAGE] = self._device.voltage attr[ATTR_VOLTAGE] = self._device.voltage
elif isinstance(self._device, Switch): elif isinstance(self._device, Switch):
for event in self.gateway.events: for event in self.hub.events:
if self._device == event.device: if self._device == event.device:
attr[ATTR_EVENT_ID] = event.event_id attr[ATTR_EVENT_ID] = event.event_id
@ -426,13 +426,13 @@ class DeconzBatteryTracker:
def __init__( def __init__(
self, self,
sensor_id: str, sensor_id: str,
gateway: DeconzHub, hub: DeconzHub,
description: DeconzSensorDescription, description: DeconzSensorDescription,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up tracker.""" """Set up tracker."""
self.sensor = gateway.api.sensors[sensor_id] self.sensor = hub.api.sensors[sensor_id]
self.gateway = gateway self.hub = hub
self.description = description self.description = description
self.async_add_entities = async_add_entities self.async_add_entities = async_add_entities
self.unsubscribe = self.sensor.subscribe(self.async_update_callback) self.unsubscribe = self.sensor.subscribe(self.async_update_callback)
@ -443,5 +443,5 @@ class DeconzBatteryTracker:
if self.description.update_key in self.sensor.changed_keys: if self.description.update_key in self.sensor.changed_keys:
self.unsubscribe() self.unsubscribe()
self.async_add_entities( self.async_add_entities(
[DeconzSensor(self.sensor, self.gateway, self.description)] [DeconzSensor(self.sensor, self.hub, self.description)]
) )

View file

@ -16,7 +16,7 @@ from homeassistant.helpers.entity_registry import (
) )
from homeassistant.util.read_only_dict import ReadOnlyDict from homeassistant.util.read_only_dict import ReadOnlyDict
from .config_flow import get_master_gateway from .config_flow import get_master_hub
from .const import CONF_BRIDGE_ID, DOMAIN, LOGGER from .const import CONF_BRIDGE_ID, DOMAIN, LOGGER
from .hub import DeconzHub from .hub import DeconzHub
@ -66,33 +66,33 @@ def async_setup_services(hass: HomeAssistant) -> None:
service_data = service_call.data service_data = service_call.data
if CONF_BRIDGE_ID in service_data: if CONF_BRIDGE_ID in service_data:
found_gateway = False found_hub = False
bridge_id = normalize_bridge_id(service_data[CONF_BRIDGE_ID]) bridge_id = normalize_bridge_id(service_data[CONF_BRIDGE_ID])
for possible_gateway in hass.data[DOMAIN].values(): for possible_hub in hass.data[DOMAIN].values():
if possible_gateway.bridgeid == bridge_id: if possible_hub.bridgeid == bridge_id:
gateway = possible_gateway hub = possible_hub
found_gateway = True found_hub = True
break break
if not found_gateway: if not found_hub:
LOGGER.error("Could not find the gateway %s", bridge_id) LOGGER.error("Could not find the gateway %s", bridge_id)
return return
else: else:
try: try:
gateway = get_master_gateway(hass) hub = get_master_hub(hass)
except ValueError: except ValueError:
LOGGER.error("No master gateway available") LOGGER.error("No master gateway available")
return return
if service == SERVICE_CONFIGURE_DEVICE: if service == SERVICE_CONFIGURE_DEVICE:
await async_configure_service(gateway, service_data) await async_configure_service(hub, service_data)
elif service == SERVICE_DEVICE_REFRESH: elif service == SERVICE_DEVICE_REFRESH:
await async_refresh_devices_service(gateway) await async_refresh_devices_service(hub)
elif service == SERVICE_REMOVE_ORPHANED_ENTRIES: elif service == SERVICE_REMOVE_ORPHANED_ENTRIES:
await async_remove_orphaned_entries_service(gateway) await async_remove_orphaned_entries_service(hub)
for service in SUPPORTED_SERVICES: for service in SUPPORTED_SERVICES:
hass.services.async_register( hass.services.async_register(
@ -110,7 +110,7 @@ def async_unload_services(hass: HomeAssistant) -> None:
hass.services.async_remove(DOMAIN, service) hass.services.async_remove(DOMAIN, service)
async def async_configure_service(gateway: DeconzHub, data: ReadOnlyDict) -> None: async def async_configure_service(hub: DeconzHub, data: ReadOnlyDict) -> None:
"""Set attribute of device in deCONZ. """Set attribute of device in deCONZ.
Entity is used to resolve to a device path (e.g. '/lights/1'). Entity is used to resolve to a device path (e.g. '/lights/1').
@ -132,61 +132,61 @@ async def async_configure_service(gateway: DeconzHub, data: ReadOnlyDict) -> Non
if entity_id: if entity_id:
try: try:
field = gateway.deconz_ids[entity_id] + field field = hub.deconz_ids[entity_id] + field
except KeyError: except KeyError:
LOGGER.error("Could not find the entity %s", entity_id) LOGGER.error("Could not find the entity %s", entity_id)
return return
await gateway.api.request("put", field, json=data) await hub.api.request("put", field, json=data)
async def async_refresh_devices_service(gateway: DeconzHub) -> None: async def async_refresh_devices_service(hub: DeconzHub) -> None:
"""Refresh available devices from deCONZ.""" """Refresh available devices from deCONZ."""
gateway.ignore_state_updates = True hub.ignore_state_updates = True
await gateway.api.refresh_state() await hub.api.refresh_state()
gateway.load_ignored_devices() hub.load_ignored_devices()
gateway.ignore_state_updates = False hub.ignore_state_updates = False
async def async_remove_orphaned_entries_service(gateway: DeconzHub) -> None: async def async_remove_orphaned_entries_service(hub: DeconzHub) -> None:
"""Remove orphaned deCONZ entries from device and entity registries.""" """Remove orphaned deCONZ entries from device and entity registries."""
device_registry = dr.async_get(gateway.hass) device_registry = dr.async_get(hub.hass)
entity_registry = er.async_get(gateway.hass) entity_registry = er.async_get(hub.hass)
entity_entries = async_entries_for_config_entry( entity_entries = async_entries_for_config_entry(
entity_registry, gateway.config_entry.entry_id entity_registry, hub.config_entry.entry_id
) )
entities_to_be_removed = [] entities_to_be_removed = []
devices_to_be_removed = [ devices_to_be_removed = [
entry.id entry.id
for entry in device_registry.devices.values() for entry in device_registry.devices.values()
if gateway.config_entry.entry_id in entry.config_entries if hub.config_entry.entry_id in entry.config_entries
] ]
# Don't remove the Gateway host entry # Don't remove the Gateway host entry
if gateway.api.config.mac: if hub.api.config.mac:
gateway_host = device_registry.async_get_device( hub_host = device_registry.async_get_device(
connections={(CONNECTION_NETWORK_MAC, gateway.api.config.mac)}, connections={(CONNECTION_NETWORK_MAC, hub.api.config.mac)},
) )
if gateway_host and gateway_host.id in devices_to_be_removed: if hub_host and hub_host.id in devices_to_be_removed:
devices_to_be_removed.remove(gateway_host.id) devices_to_be_removed.remove(hub_host.id)
# Don't remove the Gateway service entry # Don't remove the Gateway service entry
gateway_service = device_registry.async_get_device( hub_service = device_registry.async_get_device(
identifiers={(DOMAIN, gateway.api.config.bridge_id)} identifiers={(DOMAIN, hub.api.config.bridge_id)}
) )
if gateway_service and gateway_service.id in devices_to_be_removed: if hub_service and hub_service.id in devices_to_be_removed:
devices_to_be_removed.remove(gateway_service.id) devices_to_be_removed.remove(hub_service.id)
# Don't remove devices belonging to available events # Don't remove devices belonging to available events
for event in gateway.events: for event in hub.events:
if event.device_id in devices_to_be_removed: if event.device_id in devices_to_be_removed:
devices_to_be_removed.remove(event.device_id) devices_to_be_removed.remove(event.device_id)
for entry in entity_entries: for entry in entity_entries:
# Don't remove available entities # Don't remove available entities
if entry.unique_id in gateway.entities[entry.domain]: if entry.unique_id in hub.entities[entry.domain]:
# Don't remove devices with available entities # Don't remove devices with available entities
if entry.device_id in devices_to_be_removed: if entry.device_id in devices_to_be_removed:
devices_to_be_removed.remove(entry.device_id) devices_to_be_removed.remove(entry.device_id)

View file

@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import get_gateway_from_config_entry from .hub import DeconzHub
async def async_setup_entry( async def async_setup_entry(
@ -27,18 +27,18 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up sirens for deCONZ component.""" """Set up sirens for deCONZ component."""
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_siren(_: EventType, siren_id: str) -> None: def async_add_siren(_: EventType, siren_id: str) -> None:
"""Add siren from deCONZ.""" """Add siren from deCONZ."""
siren = gateway.api.lights.sirens[siren_id] siren = hub.api.lights.sirens[siren_id]
async_add_entities([DeconzSiren(siren, gateway)]) async_add_entities([DeconzSiren(siren, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_siren, async_add_siren,
gateway.api.lights.sirens, hub.api.lights.sirens,
) )
@ -61,7 +61,7 @@ class DeconzSiren(DeconzDevice[Siren], SirenEntity):
"""Turn on siren.""" """Turn on siren."""
if (duration := kwargs.get(ATTR_DURATION)) is not None: if (duration := kwargs.get(ATTR_DURATION)) is not None:
duration *= 10 duration *= 10
await self.gateway.api.lights.sirens.set_state( await self.hub.api.lights.sirens.set_state(
id=self._device.resource_id, id=self._device.resource_id,
on=True, on=True,
duration=duration, duration=duration,
@ -69,7 +69,7 @@ class DeconzSiren(DeconzDevice[Siren], SirenEntity):
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off siren.""" """Turn off siren."""
await self.gateway.api.lights.sirens.set_state( await self.hub.api.lights.sirens.set_state(
id=self._device.resource_id, id=self._device.resource_id,
on=False, on=False,
) )

View file

@ -14,7 +14,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import POWER_PLUGS from .const import POWER_PLUGS
from .deconz_device import DeconzDevice from .deconz_device import DeconzDevice
from .hub import get_gateway_from_config_entry from .hub import DeconzHub
async def async_setup_entry( async def async_setup_entry(
@ -26,20 +26,20 @@ async def async_setup_entry(
Switches are based on the same device class as lights in deCONZ. Switches are based on the same device class as lights in deCONZ.
""" """
gateway = get_gateway_from_config_entry(hass, config_entry) hub = DeconzHub.get_hub(hass, config_entry)
gateway.entities[DOMAIN] = set() hub.entities[DOMAIN] = set()
@callback @callback
def async_add_switch(_: EventType, switch_id: str) -> None: def async_add_switch(_: EventType, switch_id: str) -> None:
"""Add switch from deCONZ.""" """Add switch from deCONZ."""
switch = gateway.api.lights.lights[switch_id] switch = hub.api.lights.lights[switch_id]
if switch.type not in POWER_PLUGS: if switch.type not in POWER_PLUGS:
return return
async_add_entities([DeconzPowerPlug(switch, gateway)]) async_add_entities([DeconzPowerPlug(switch, hub)])
gateway.register_platform_add_device_callback( hub.register_platform_add_device_callback(
async_add_switch, async_add_switch,
gateway.api.lights.lights, hub.api.lights.lights,
) )
@ -55,14 +55,14 @@ class DeconzPowerPlug(DeconzDevice[Light], SwitchEntity):
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on switch.""" """Turn on switch."""
await self.gateway.api.lights.lights.set_state( await self.hub.api.lights.lights.set_state(
id=self._device.resource_id, id=self._device.resource_id,
on=True, on=True,
) )
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off switch.""" """Turn off switch."""
await self.gateway.api.lights.lights.set_state( await self.hub.api.lights.lights.set_state(
id=self._device.resource_id, id=self._device.resource_id,
on=False, on=False,
) )

View file

@ -18,10 +18,7 @@ from homeassistant.components.cover import DOMAIN as COVER_DOMAIN
from homeassistant.components.deconz.config_flow import DECONZ_MANUFACTURERURL from homeassistant.components.deconz.config_flow import DECONZ_MANUFACTURERURL
from homeassistant.components.deconz.const import DOMAIN as DECONZ_DOMAIN from homeassistant.components.deconz.const import DOMAIN as DECONZ_DOMAIN
from homeassistant.components.deconz.errors import AuthenticationRequired, CannotConnect from homeassistant.components.deconz.errors import AuthenticationRequired, CannotConnect
from homeassistant.components.deconz.hub import ( from homeassistant.components.deconz.hub import DeconzHub, get_deconz_api
get_deconz_api,
get_gateway_from_config_entry,
)
from homeassistant.components.fan import DOMAIN as FAN_DOMAIN from homeassistant.components.fan import DOMAIN as FAN_DOMAIN
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
@ -149,7 +146,7 @@ async def test_gateway_setup(
return_value=True, return_value=True,
) as forward_entry_setup: ) as forward_entry_setup:
config_entry = await setup_deconz_integration(hass, aioclient_mock) config_entry = await setup_deconz_integration(hass, aioclient_mock)
gateway = get_gateway_from_config_entry(hass, config_entry) gateway = DeconzHub.get_hub(hass, config_entry)
assert gateway.bridgeid == BRIDGEID assert gateway.bridgeid == BRIDGEID
assert gateway.master is True assert gateway.master is True
assert gateway.config.allow_clip_sensor is False assert gateway.config.allow_clip_sensor is False
@ -201,7 +198,7 @@ async def test_gateway_device_configuration_url_when_addon(
config_entry = await setup_deconz_integration( config_entry = await setup_deconz_integration(
hass, aioclient_mock, source=SOURCE_HASSIO hass, aioclient_mock, source=SOURCE_HASSIO
) )
gateway = get_gateway_from_config_entry(hass, config_entry) gateway = DeconzHub.get_hub(hass, config_entry)
gateway_entry = device_registry.async_get_device( gateway_entry = device_registry.async_get_device(
identifiers={(DECONZ_DOMAIN, gateway.bridgeid)} identifiers={(DECONZ_DOMAIN, gateway.bridgeid)}
@ -248,7 +245,7 @@ async def test_update_address(
) -> None: ) -> None:
"""Make sure that connection status triggers a dispatcher send.""" """Make sure that connection status triggers a dispatcher send."""
config_entry = await setup_deconz_integration(hass, aioclient_mock) config_entry = await setup_deconz_integration(hass, aioclient_mock)
gateway = get_gateway_from_config_entry(hass, config_entry) gateway = DeconzHub.get_hub(hass, config_entry)
assert gateway.api.host == "1.2.3.4" assert gateway.api.host == "1.2.3.4"
with patch( with patch(
@ -280,7 +277,7 @@ async def test_reset_after_successful_setup(
) -> None: ) -> None:
"""Make sure that connection status triggers a dispatcher send.""" """Make sure that connection status triggers a dispatcher send."""
config_entry = await setup_deconz_integration(hass, aioclient_mock) config_entry = await setup_deconz_integration(hass, aioclient_mock)
gateway = get_gateway_from_config_entry(hass, config_entry) gateway = DeconzHub.get_hub(hass, config_entry)
result = await gateway.async_reset() result = await gateway.async_reset()
await hass.async_block_till_done() await hass.async_block_till_done()