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.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 .deconz_event import async_setup_events, async_unload_events
from .errors import AuthenticationRequired, CannotConnect
@ -24,7 +24,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
hass.data.setdefault(DOMAIN, {})
if not config_entry.options:
await async_update_master_gateway(hass, config_entry)
await async_update_master_hub(hass, config_entry)
try:
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]:
async_setup_services(hass)
gateway = hass.data[DOMAIN][config_entry.entry_id] = DeconzHub(
hass, config_entry, api
)
await gateway.async_update_device_registry()
hub = hass.data[DOMAIN][config_entry.entry_id] = DeconzHub(hass, config_entry, api)
await hub.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)
api.start()
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
@ -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:
"""Unload deCONZ config entry."""
gateway: DeconzHub = hass.data[DOMAIN].pop(config_entry.entry_id)
async_unload_events(gateway)
hub: DeconzHub = hass.data[DOMAIN].pop(config_entry.entry_id)
async_unload_events(hub)
if not hass.data[DOMAIN]:
async_unload_services(hass)
elif gateway.master:
await async_update_master_gateway(hass, config_entry)
new_master_gateway = next(iter(hass.data[DOMAIN].values()))
await async_update_master_gateway(hass, new_master_gateway.config_entry)
elif hub.master:
await async_update_master_hub(hass, config_entry)
new_master_hub = next(iter(hass.data[DOMAIN].values()))
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
) -> None:
"""Update master gateway boolean.
"""Update master hub boolean.
Called by setup_entry and unload_entry.
Makes sure there is always one master available.
"""
try:
master_gateway = get_master_gateway(hass)
master = master_gateway.config_entry == config_entry
master_hub = get_master_hub(hass)
master = master_hub.config_entry == config_entry
except ValueError:
master = True

View file

@ -29,7 +29,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry
from .hub import DeconzHub
DECONZ_TO_ALARM_STATE = {
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."""
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:
return alarm_system.resource_id
return None
@ -59,23 +59,19 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the deCONZ alarm control panel devices."""
gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
hub = DeconzHub.get_hub(hass, config_entry)
hub.entities[DOMAIN] = set()
@callback
def async_add_sensor(_: EventType, sensor_id: str) -> None:
"""Add alarm control panel devices from deCONZ."""
sensor = gateway.api.sensors.ancillary_control[sensor_id]
if alarm_system_id := get_alarm_system_id_for_unique_id(
gateway, sensor.unique_id
):
async_add_entities(
[DeconzAlarmControlPanel(sensor, gateway, alarm_system_id)]
)
sensor = hub.api.sensors.ancillary_control[sensor_id]
if alarm_system_id := get_alarm_system_id_for_unique_id(hub, sensor.unique_id):
async_add_entities([DeconzAlarmControlPanel(sensor, hub, alarm_system_id)])
gateway.register_platform_add_device_callback(
hub.register_platform_add_device_callback(
async_add_sensor,
gateway.api.sensors.ancillary_control,
hub.api.sensors.ancillary_control,
)
@ -95,11 +91,11 @@ class DeconzAlarmControlPanel(DeconzDevice[AncillaryControl], AlarmControlPanelE
def __init__(
self,
device: AncillaryControl,
gateway: DeconzHub,
hub: DeconzHub,
alarm_system_id: str,
) -> None:
"""Set up alarm control panel device."""
super().__init__(device, gateway)
super().__init__(device, hub)
self.alarm_system_id = alarm_system_id
@callback
@ -118,27 +114,27 @@ class DeconzAlarmControlPanel(DeconzDevice[AncillaryControl], AlarmControlPanelE
async def async_alarm_arm_away(self, code: str | None = None) -> None:
"""Send arm away command."""
if code:
await self.gateway.api.alarm_systems.arm(
await self.hub.api.alarm_systems.arm(
self.alarm_system_id, AlarmSystemArmAction.AWAY, code
)
async def async_alarm_arm_home(self, code: str | None = None) -> None:
"""Send arm home command."""
if code:
await self.gateway.api.alarm_systems.arm(
await self.hub.api.alarm_systems.arm(
self.alarm_system_id, AlarmSystemArmAction.STAY, code
)
async def async_alarm_arm_night(self, code: str | None = None) -> None:
"""Send arm night command."""
if code:
await self.gateway.api.alarm_systems.arm(
await self.hub.api.alarm_systems.arm(
self.alarm_system_id, AlarmSystemArmAction.NIGHT, code
)
async def async_alarm_disarm(self, code: str | None = None) -> None:
"""Send disarm command."""
if code:
await self.gateway.api.alarm_systems.arm(
await self.hub.api.alarm_systems.arm(
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 .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry
from .hub import DeconzHub
_SensorDeviceT = TypeVar("_SensorDeviceT", bound=PydeconzSensorBase)
@ -168,13 +168,13 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the deCONZ binary sensor."""
gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
hub = DeconzHub.get_hub(hass, config_entry)
hub.entities[DOMAIN] = set()
@callback
def async_add_sensor(_: EventType, sensor_id: str) -> None:
"""Add sensor from deCONZ."""
sensor = gateway.api.sensors[sensor_id]
sensor = hub.api.sensors[sensor_id]
for description in ENTITY_DESCRIPTIONS:
if (
@ -182,11 +182,11 @@ async def async_setup_entry(
and not isinstance(sensor, description.instance_check)
) or description.value_fn(sensor) is None:
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,
gateway.api.sensors,
hub.api.sensors,
)
@ -199,7 +199,7 @@ class DeconzBinarySensor(DeconzDevice[SensorResources], BinarySensorEntity):
def __init__(
self,
device: SensorResources,
gateway: DeconzHub,
hub: DeconzHub,
description: DeconzBinarySensorDescription,
) -> None:
"""Initialize deCONZ binary sensor."""
@ -208,7 +208,7 @@ class DeconzBinarySensor(DeconzDevice[SensorResources], BinarySensorEntity):
self._update_key = description.update_key
if description.name_suffix:
self._name_suffix = description.name_suffix
super().__init__(device, gateway)
super().__init__(device, hub)
if (
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 .deconz_device import DeconzDevice, DeconzSceneMixin
from .hub import DeconzHub, get_gateway_from_config_entry
from .hub import DeconzHub
@dataclass(frozen=True, kw_only=True)
@ -50,33 +50,33 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the deCONZ button entity."""
gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
hub = DeconzHub.get_hub(hass, config_entry)
hub.entities[DOMAIN] = set()
@callback
def async_add_scene(_: EventType, scene_id: str) -> None:
"""Add scene button from deCONZ."""
scene = gateway.api.scenes[scene_id]
scene = hub.api.scenes[scene_id]
async_add_entities(
DeconzSceneButton(scene, gateway, description)
DeconzSceneButton(scene, hub, description)
for description in ENTITY_DESCRIPTIONS.get(PydeconzScene, [])
)
gateway.register_platform_add_device_callback(
hub.register_platform_add_device_callback(
async_add_scene,
gateway.api.scenes,
hub.api.scenes,
)
@callback
def async_add_presence_sensor(_: EventType, sensor_id: str) -> None:
"""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:
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,
gateway.api.sensors.presence,
hub.api.sensors.presence,
)
@ -88,19 +88,19 @@ class DeconzSceneButton(DeconzSceneMixin, ButtonEntity):
def __init__(
self,
device: PydeconzScene,
gateway: DeconzHub,
hub: DeconzHub,
description: DeconzButtonDescription,
) -> None:
"""Initialize deCONZ number entity."""
self.entity_description: DeconzButtonDescription = description
super().__init__(device, gateway)
super().__init__(device, hub)
self._attr_name = f"{self._attr_name} {description.suffix}"
async def async_press(self) -> None:
"""Store light states into scene."""
async_button_fn = getattr(
self.gateway.api.scenes,
self.hub.api.scenes,
self.entity_description.button_fn,
)
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:
"""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,
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 .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry
from .hub import DeconzHub
DECONZ_FAN_SMART = "smart"
@ -80,18 +80,18 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the deCONZ climate devices."""
gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
hub = DeconzHub.get_hub(hass, config_entry)
hub.entities[DOMAIN] = set()
@callback
def async_add_climate(_: EventType, climate_id: str) -> None:
"""Add climate from deCONZ."""
climate = gateway.api.sensors.thermostat[climate_id]
async_add_entities([DeconzThermostat(climate, gateway)])
climate = hub.api.sensors.thermostat[climate_id]
async_add_entities([DeconzThermostat(climate, hub)])
gateway.register_platform_add_device_callback(
hub.register_platform_add_device_callback(
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
_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."""
super().__init__(device, gateway)
super().__init__(device, hub)
self._attr_hvac_modes = [
HVACMode.HEAT,
@ -149,7 +149,7 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
if fan_mode not in FAN_MODE_TO_DECONZ:
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,
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}")
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,
on=hvac_mode != HVACMode.OFF,
)
else:
await self.gateway.api.sensors.thermostat.set_config(
await self.hub.api.sensors.thermostat.set_config(
id=self._device.resource_id,
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:
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,
preset=PRESET_MODE_TO_DECONZ[preset_mode],
)
@ -237,12 +237,12 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}")
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,
cooling_setpoint=kwargs[ATTR_TEMPERATURE] * 100,
)
else:
await self.gateway.api.sensors.thermostat.set_config(
await self.hub.api.sensors.thermostat.set_config(
id=self._device.resource_id,
heating_setpoint=kwargs[ATTR_TEMPERATURE] * 100,
)

View file

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

View file

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

View file

@ -33,11 +33,11 @@ class DeconzBase(Generic[_DeviceT]):
def __init__(
self,
device: _DeviceT,
gateway: DeconzHub,
hub: DeconzHub,
) -> None:
"""Set up device and add update callback to get data from websocket."""
self._device: _DeviceT = device
self.gateway = gateway
self.hub = hub
@property
def unique_id(self) -> str:
@ -67,7 +67,7 @@ class DeconzBase(Generic[_DeviceT]):
model=self._device.model_id,
name=self._device.name,
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__(
self,
device: _DeviceT,
gateway: DeconzHub,
hub: DeconzHub,
) -> None:
"""Set up device and add update callback to get data from websocket."""
super().__init__(device, gateway)
self.gateway.entities[self.TYPE].add(self.unique_id)
super().__init__(device, hub)
self.hub.entities[self.TYPE].add(self.unique_id)
self._attr_name = self._device.name
if self._name_suffix is not None:
@ -103,11 +103,11 @@ class DeconzDevice(DeconzBase[_DeviceT], Entity):
async def async_added_to_hass(self) -> None:
"""Subscribe to device events."""
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(
async_dispatcher_connect(
self.hass,
self.gateway.signal_reachable,
self.hub.signal_reachable,
self.async_update_connection_state,
)
)
@ -115,8 +115,8 @@ class DeconzDevice(DeconzBase[_DeviceT], Entity):
async def async_will_remove_from_hass(self) -> None:
"""Disconnect device object when removed."""
self._device.remove_callback(self.async_update_callback)
del self.gateway.deconz_ids[self.entity_id]
self.gateway.entities[self.TYPE].remove(self.unique_id)
del self.hub.deconz_ids[self.entity_id]
self.hub.entities[self.TYPE].remove(self.unique_id)
@callback
def async_update_connection_state(self) -> None:
@ -126,7 +126,7 @@ class DeconzDevice(DeconzBase[_DeviceT], Entity):
@callback
def async_update_callback(self) -> None:
"""Update the device's state."""
if self.gateway.ignore_state_updates:
if self.hub.ignore_state_updates:
return
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:
"""Return True if device is available."""
if isinstance(self._device, PydeconzScene):
return self.gateway.available
return self.gateway.available and self._device.reachable # type: ignore[union-attr]
return self.hub.available
return self.hub.available and self._device.reachable # type: ignore[union-attr]
class DeconzSceneMixin(DeconzDevice[PydeconzScene]):
@ -152,23 +152,23 @@ class DeconzSceneMixin(DeconzDevice[PydeconzScene]):
def __init__(
self,
device: PydeconzScene,
gateway: DeconzHub,
hub: DeconzHub,
) -> None:
"""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._group_identifier = self.get_parent_identifier()
def get_device_identifier(self) -> str:
"""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:
"""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
def unique_id(self) -> str:
@ -183,5 +183,5 @@ class DeconzSceneMixin(DeconzDevice[PydeconzScene]):
manufacturer="Dresden Elektronik",
model="deCONZ group",
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."""
@callback
def async_add_sensor(_: EventType, sensor_id: str) -> None:
"""Create DeconzEvent."""
new_event: DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent | DeconzRelativeRotaryEvent
sensor = gateway.api.sensors[sensor_id]
sensor = hub.api.sensors[sensor_id]
if isinstance(sensor, Switch):
new_event = DeconzEvent(sensor, gateway)
new_event = DeconzEvent(sensor, hub)
elif isinstance(sensor, AncillaryControl):
new_event = DeconzAlarmEvent(sensor, gateway)
new_event = DeconzAlarmEvent(sensor, hub)
elif isinstance(sensor, Presence):
if sensor.presence_event is None:
return
new_event = DeconzPresenceEvent(sensor, gateway)
new_event = DeconzPresenceEvent(sensor, hub)
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())
gateway.events.append(new_event)
hub.hass.async_create_task(new_event.async_update_device_registry())
hub.events.append(new_event)
gateway.register_platform_add_device_callback(
hub.register_platform_add_device_callback(
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,
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,
gateway.api.sensors.presence,
hub.api.sensors.presence,
)
gateway.register_platform_add_device_callback(
hub.register_platform_add_device_callback(
async_add_sensor,
gateway.api.sensors.relative_rotary,
hub.api.sensors.relative_rotary,
)
@callback
def async_unload_events(gateway: DeconzHub) -> None:
def async_unload_events(hub: DeconzHub) -> None:
"""Unload all deCONZ events."""
for event in gateway.events:
for event in hub.events:
event.async_will_remove_from_hass()
gateway.events.clear()
hub.events.clear()
class DeconzEventBase(DeconzBase):
@ -118,10 +118,10 @@ class DeconzEventBase(DeconzBase):
def __init__(
self,
device: AncillaryControl | Presence | RelativeRotary | Switch,
gateway: DeconzHub,
hub: DeconzHub,
) -> None:
"""Register callback that will be used for signals."""
super().__init__(device, gateway)
super().__init__(device, hub)
self._unsubscribe = device.subscribe(self.async_update_callback)
@ -145,10 +145,10 @@ class DeconzEventBase(DeconzBase):
if not self.device_info:
return
device_registry = dr.async_get(self.gateway.hass)
device_registry = dr.async_get(self.hub.hass)
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
@ -165,10 +165,7 @@ class DeconzEvent(DeconzEventBase):
@callback
def async_update_callback(self) -> None:
"""Fire the event if reason is that state is updated."""
if (
self.gateway.ignore_state_updates
or "state" not in self._device.changed_keys
):
if self.hub.ignore_state_updates or "state" not in self._device.changed_keys:
return
data: dict[str, Any] = {
@ -189,7 +186,7 @@ class DeconzEvent(DeconzEventBase):
if self._device.xy is not None:
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):
@ -201,7 +198,7 @@ class DeconzAlarmEvent(DeconzEventBase):
def async_update_callback(self) -> None:
"""Fire the event if reason is new action is updated."""
if (
self.gateway.ignore_state_updates
self.hub.ignore_state_updates
or "action" not in self._device.changed_keys
or self._device.action not in SUPPORTED_DECONZ_ALARM_EVENTS
):
@ -214,7 +211,7 @@ class DeconzAlarmEvent(DeconzEventBase):
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):
@ -226,7 +223,7 @@ class DeconzPresenceEvent(DeconzEventBase):
def async_update_callback(self) -> None:
"""Fire the event if reason is new action is updated."""
if (
self.gateway.ignore_state_updates
self.hub.ignore_state_updates
or "presenceevent" not in self._device.changed_keys
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,
}
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):
@ -251,7 +248,7 @@ class DeconzRelativeRotaryEvent(DeconzEventBase):
def async_update_callback(self) -> None:
"""Fire the event if reason is new action is updated."""
if (
self.gateway.ignore_state_updates
self.hub.ignore_state_updates
or "rotaryevent" not in self._device.changed_keys
):
return
@ -265,4 +262,4 @@ class DeconzRelativeRotaryEvent(DeconzEventBase):
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,
) -> DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent | DeconzRelativeRotaryEvent:
"""Resolve deconz event from device."""
gateways: dict[str, DeconzHub] = hass.data.get(DOMAIN, {})
for gateway in gateways.values():
for deconz_event in gateway.events:
hubs: dict[str, DeconzHub] = hass.data.get(DOMAIN, {})
for hub in hubs.values():
for deconz_event in hub.events:
if device.id == deconz_event.device_id:
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.core import HomeAssistant
from .hub import get_gateway_from_config_entry
from .hub import DeconzHub
REDACT_CONFIG = {CONF_API_KEY, CONF_UNIQUE_ID}
REDACT_DECONZ_CONFIG = {"bridgeid", "mac", "panid"}
@ -19,29 +19,27 @@ async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
) -> dict[str, Any]:
"""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["config"] = async_redact_data(config_entry.as_dict(), REDACT_CONFIG)
diag["deconz_config"] = async_redact_data(
gateway.api.config.raw, REDACT_DECONZ_CONFIG
)
diag["deconz_config"] = async_redact_data(hub.api.config.raw, REDACT_DECONZ_CONFIG)
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["entities"] = gateway.entities
diag["deconz_ids"] = hub.deconz_ids
diag["entities"] = hub.entities
diag["events"] = {
event.serial: {
"event_id": event.event_id,
"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["groups"] = {k: v.raw for k, v in gateway.api.groups.items()}
diag["lights"] = {k: v.raw for k, v in gateway.api.lights.items()}
diag["scenes"] = {k: v.raw for k, v in gateway.api.scenes.items()}
diag["sensors"] = {k: v.raw for k, v in gateway.api.sensors.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 hub.api.groups.items()}
diag["lights"] = {k: v.raw for k, v in hub.api.lights.items()}
diag["scenes"] = {k: v.raw for k, v in hub.api.scenes.items()}
diag["sensors"] = {k: v.raw for k, v in hub.api.sensors.items()}
return diag

View file

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

View file

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

View file

@ -94,6 +94,12 @@ class DeconzHub:
self.deconz_groups: 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
def bridgeid(self) -> str:
"""Return the unique identifier of the gateway."""
@ -215,16 +221,16 @@ class DeconzHub:
# A race condition can occur if multiple config entries are
# unloaded in parallel
return
gateway = get_gateway_from_config_entry(hass, config_entry)
previous_config = gateway.config
gateway.config = DeconzConfig.from_config_entry(config_entry)
if previous_config.host != gateway.config.host:
gateway.api.close()
gateway.api.host = gateway.config.host
gateway.api.start()
hub = DeconzHub.get_hub(hass, config_entry)
previous_config = hub.config
hub.config = DeconzConfig.from_config_entry(config_entry)
if previous_config.host != hub.config.host:
hub.api.close()
hub.api.host = hub.config.host
hub.api.start()
return
await gateway.options_updated(previous_config)
await hub.options_updated(previous_config)
async def options_updated(self, previous_config: DeconzConfig) -> None:
"""Manage entities affected by config entry options."""
@ -292,11 +298,3 @@ class DeconzHub:
self.deconz_ids = {}
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 .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry
from .hub import DeconzHub
DECONZ_GROUP = "is_deconz_group"
EFFECT_TO_DECONZ = {
@ -111,13 +111,13 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the deCONZ lights and groups from a config entry."""
gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
hub = DeconzHub.get_hub(hass, config_entry)
hub.entities[DOMAIN] = set()
entity_registry = er.async_get(hass)
# 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 (
entity_id := entity_registry.async_get_entity_id(
DOMAIN, DECONZ_DOMAIN, light.unique_id
@ -128,15 +128,15 @@ async def async_setup_entry(
@callback
def async_add_light(_: EventType, light_id: str) -> None:
"""Add light from deCONZ."""
light = gateway.api.lights.lights[light_id]
light = hub.api.lights.lights[light_id]
if light.type in POWER_PLUGS:
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,
gateway.api.lights.lights,
hub.api.lights.lights,
)
@callback
@ -145,20 +145,20 @@ async def async_setup_entry(
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
first = True
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)
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,
gateway.api.groups,
hub.api.groups,
)
@ -168,15 +168,15 @@ class DeconzBaseLight(DeconzDevice[_LightDeviceT], LightEntity):
TYPE = DOMAIN
_attr_color_mode = ColorMode.UNKNOWN
def __init__(self, device: _LightDeviceT, gateway: DeconzHub) -> None:
def __init__(self, device: _LightDeviceT, hub: DeconzHub) -> None:
"""Set up light."""
super().__init__(device, gateway)
super().__init__(device, hub)
self.api: GroupHandler | LightHandler
if isinstance(self._device, Light):
self.api = self.gateway.api.lights.lights
self.api = self.hub.api.lights.lights
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()
@ -324,7 +324,7 @@ class DeconzLight(DeconzBaseLight[Light]):
super().async_update_callback()
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:
group.update_color_state(self._device)
@ -334,10 +334,10 @@ class DeconzGroup(DeconzBaseLight[Group]):
_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."""
self._unique_id = f"{gateway.bridgeid}-{device.deconz_id}"
super().__init__(device, gateway)
self._unique_id = f"{hub.bridgeid}-{device.deconz_id}"
super().__init__(device, hub)
self._attr_name = None
@ -354,7 +354,7 @@ class DeconzGroup(DeconzBaseLight[Group]):
manufacturer="Dresden Elektronik",
model="deCONZ group",
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

View file

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

View file

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

View file

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

View file

@ -17,7 +17,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .deconz_device import DeconzDevice
from .hub import get_gateway_from_config_entry
from .hub import DeconzHub
SENSITIVITY_TO_DECONZ = {
"High": PresenceConfigSensitivity.HIGH.value,
@ -33,25 +33,25 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the deCONZ button entity."""
gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
hub = DeconzHub.get_hub(hass, config_entry)
hub.entities[DOMAIN] = set()
@callback
def async_add_presence_sensor(_: EventType, sensor_id: str) -> None:
"""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:
async_add_entities(
[
DeconzPresenceDeviceModeSelect(sensor, gateway),
DeconzPresenceSensitivitySelect(sensor, gateway),
DeconzPresenceTriggerDistanceSelect(sensor, gateway),
DeconzPresenceDeviceModeSelect(sensor, hub),
DeconzPresenceSensitivitySelect(sensor, hub),
DeconzPresenceTriggerDistanceSelect(sensor, hub),
]
)
gateway.register_platform_add_device_callback(
hub.register_platform_add_device_callback(
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:
"""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,
device_mode=PresenceConfigDeviceMode(option),
)
@ -106,7 +106,7 @@ class DeconzPresenceSensitivitySelect(DeconzDevice[Presence], SelectEntity):
async def async_select_option(self, option: str) -> None:
"""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,
sensitivity=SENSITIVITY_TO_DECONZ[option],
)
@ -137,7 +137,7 @@ class DeconzPresenceTriggerDistanceSelect(DeconzDevice[Presence], SelectEntity):
async def async_select_option(self, option: str) -> None:
"""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,
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 .deconz_device import DeconzDevice
from .hub import DeconzHub, get_gateway_from_config_entry
from .hub import DeconzHub
PROVIDES_EXTRA_ATTRIBUTES = (
"battery",
@ -295,8 +295,8 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the deCONZ sensors."""
gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
hub = DeconzHub.get_hub(hass, config_entry)
hub.entities[DOMAIN] = set()
known_device_entities: dict[str, set[str]] = {
description.key: set()
@ -307,7 +307,7 @@ async def async_setup_entry(
@callback
def async_add_sensor(_: EventType, sensor_id: str) -> None:
"""Add sensor from deCONZ."""
sensor = gateway.api.sensors[sensor_id]
sensor = hub.api.sensors[sensor_id]
entities: list[DeconzSensor] = []
for description in ENTITY_DESCRIPTIONS:
@ -333,20 +333,20 @@ async def async_setup_entry(
known_device_entities[description.key].add(unique_id)
if no_sensor_data and description.key == "battery":
DeconzBatteryTracker(
sensor_id, gateway, description, async_add_entities
sensor_id, hub, description, async_add_entities
)
continue
if no_sensor_data:
continue
entities.append(DeconzSensor(sensor, gateway, description))
entities.append(DeconzSensor(sensor, hub, description))
async_add_entities(entities)
gateway.register_platform_add_device_callback(
hub.register_platform_add_device_callback(
async_add_sensor,
gateway.api.sensors,
hub.api.sensors,
)
@ -359,7 +359,7 @@ class DeconzSensor(DeconzDevice[SensorResources], SensorEntity):
def __init__(
self,
device: SensorResources,
gateway: DeconzHub,
hub: DeconzHub,
description: DeconzSensorDescription,
) -> None:
"""Initialize deCONZ sensor."""
@ -368,7 +368,7 @@ class DeconzSensor(DeconzDevice[SensorResources], SensorEntity):
self._update_key = description.update_key
if description.name_suffix:
self._name_suffix = description.name_suffix
super().__init__(device, gateway)
super().__init__(device, hub)
if (
self.entity_description.key in PROVIDES_EXTRA_ATTRIBUTES
@ -413,7 +413,7 @@ class DeconzSensor(DeconzDevice[SensorResources], SensorEntity):
attr[ATTR_VOLTAGE] = self._device.voltage
elif isinstance(self._device, Switch):
for event in self.gateway.events:
for event in self.hub.events:
if self._device == event.device:
attr[ATTR_EVENT_ID] = event.event_id
@ -426,13 +426,13 @@ class DeconzBatteryTracker:
def __init__(
self,
sensor_id: str,
gateway: DeconzHub,
hub: DeconzHub,
description: DeconzSensorDescription,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up tracker."""
self.sensor = gateway.api.sensors[sensor_id]
self.gateway = gateway
self.sensor = hub.api.sensors[sensor_id]
self.hub = hub
self.description = description
self.async_add_entities = async_add_entities
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:
self.unsubscribe()
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 .config_flow import get_master_gateway
from .config_flow import get_master_hub
from .const import CONF_BRIDGE_ID, DOMAIN, LOGGER
from .hub import DeconzHub
@ -66,33 +66,33 @@ def async_setup_services(hass: HomeAssistant) -> None:
service_data = service_call.data
if CONF_BRIDGE_ID in service_data:
found_gateway = False
found_hub = False
bridge_id = normalize_bridge_id(service_data[CONF_BRIDGE_ID])
for possible_gateway in hass.data[DOMAIN].values():
if possible_gateway.bridgeid == bridge_id:
gateway = possible_gateway
found_gateway = True
for possible_hub in hass.data[DOMAIN].values():
if possible_hub.bridgeid == bridge_id:
hub = possible_hub
found_hub = True
break
if not found_gateway:
if not found_hub:
LOGGER.error("Could not find the gateway %s", bridge_id)
return
else:
try:
gateway = get_master_gateway(hass)
hub = get_master_hub(hass)
except ValueError:
LOGGER.error("No master gateway available")
return
if service == SERVICE_CONFIGURE_DEVICE:
await async_configure_service(gateway, service_data)
await async_configure_service(hub, service_data)
elif service == SERVICE_DEVICE_REFRESH:
await async_refresh_devices_service(gateway)
await async_refresh_devices_service(hub)
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:
hass.services.async_register(
@ -110,7 +110,7 @@ def async_unload_services(hass: HomeAssistant) -> None:
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.
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:
try:
field = gateway.deconz_ids[entity_id] + field
field = hub.deconz_ids[entity_id] + field
except KeyError:
LOGGER.error("Could not find the entity %s", entity_id)
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."""
gateway.ignore_state_updates = True
await gateway.api.refresh_state()
gateway.load_ignored_devices()
gateway.ignore_state_updates = False
hub.ignore_state_updates = True
await hub.api.refresh_state()
hub.load_ignored_devices()
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."""
device_registry = dr.async_get(gateway.hass)
entity_registry = er.async_get(gateway.hass)
device_registry = dr.async_get(hub.hass)
entity_registry = er.async_get(hub.hass)
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 = []
devices_to_be_removed = [
entry.id
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
if gateway.api.config.mac:
gateway_host = device_registry.async_get_device(
connections={(CONNECTION_NETWORK_MAC, gateway.api.config.mac)},
if hub.api.config.mac:
hub_host = device_registry.async_get_device(
connections={(CONNECTION_NETWORK_MAC, hub.api.config.mac)},
)
if gateway_host and gateway_host.id in devices_to_be_removed:
devices_to_be_removed.remove(gateway_host.id)
if hub_host and hub_host.id in devices_to_be_removed:
devices_to_be_removed.remove(hub_host.id)
# Don't remove the Gateway service entry
gateway_service = device_registry.async_get_device(
identifiers={(DOMAIN, gateway.api.config.bridge_id)}
hub_service = device_registry.async_get_device(
identifiers={(DOMAIN, hub.api.config.bridge_id)}
)
if gateway_service and gateway_service.id in devices_to_be_removed:
devices_to_be_removed.remove(gateway_service.id)
if hub_service and hub_service.id in devices_to_be_removed:
devices_to_be_removed.remove(hub_service.id)
# 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:
devices_to_be_removed.remove(event.device_id)
for entry in entity_entries:
# 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
if entry.device_id in devices_to_be_removed:
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 .deconz_device import DeconzDevice
from .hub import get_gateway_from_config_entry
from .hub import DeconzHub
async def async_setup_entry(
@ -27,18 +27,18 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up sirens for deCONZ component."""
gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
hub = DeconzHub.get_hub(hass, config_entry)
hub.entities[DOMAIN] = set()
@callback
def async_add_siren(_: EventType, siren_id: str) -> None:
"""Add siren from deCONZ."""
siren = gateway.api.lights.sirens[siren_id]
async_add_entities([DeconzSiren(siren, gateway)])
siren = hub.api.lights.sirens[siren_id]
async_add_entities([DeconzSiren(siren, hub)])
gateway.register_platform_add_device_callback(
hub.register_platform_add_device_callback(
async_add_siren,
gateway.api.lights.sirens,
hub.api.lights.sirens,
)
@ -61,7 +61,7 @@ class DeconzSiren(DeconzDevice[Siren], SirenEntity):
"""Turn on siren."""
if (duration := kwargs.get(ATTR_DURATION)) is not None:
duration *= 10
await self.gateway.api.lights.sirens.set_state(
await self.hub.api.lights.sirens.set_state(
id=self._device.resource_id,
on=True,
duration=duration,
@ -69,7 +69,7 @@ class DeconzSiren(DeconzDevice[Siren], SirenEntity):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off siren."""
await self.gateway.api.lights.sirens.set_state(
await self.hub.api.lights.sirens.set_state(
id=self._device.resource_id,
on=False,
)

View file

@ -14,7 +14,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import POWER_PLUGS
from .deconz_device import DeconzDevice
from .hub import get_gateway_from_config_entry
from .hub import DeconzHub
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.
"""
gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
hub = DeconzHub.get_hub(hass, config_entry)
hub.entities[DOMAIN] = set()
@callback
def async_add_switch(_: EventType, switch_id: str) -> None:
"""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:
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,
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:
"""Turn on switch."""
await self.gateway.api.lights.lights.set_state(
await self.hub.api.lights.lights.set_state(
id=self._device.resource_id,
on=True,
)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off switch."""
await self.gateway.api.lights.lights.set_state(
await self.hub.api.lights.lights.set_state(
id=self._device.resource_id,
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.const import DOMAIN as DECONZ_DOMAIN
from homeassistant.components.deconz.errors import AuthenticationRequired, CannotConnect
from homeassistant.components.deconz.hub import (
get_deconz_api,
get_gateway_from_config_entry,
)
from homeassistant.components.deconz.hub import DeconzHub, get_deconz_api
from homeassistant.components.fan import DOMAIN as FAN_DOMAIN
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
@ -149,7 +146,7 @@ async def test_gateway_setup(
return_value=True,
) as forward_entry_setup:
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.master is True
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(
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(
identifiers={(DECONZ_DOMAIN, gateway.bridgeid)}
@ -248,7 +245,7 @@ async def test_update_address(
) -> None:
"""Make sure that connection status triggers a dispatcher send."""
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"
with patch(
@ -280,7 +277,7 @@ async def test_reset_after_successful_setup(
) -> None:
"""Make sure that connection status triggers a dispatcher send."""
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()
await hass.async_block_till_done()