diff --git a/homeassistant/components/deconz/alarm_control_panel.py b/homeassistant/components/deconz/alarm_control_panel.py index a29c439123e..2e825dafd65 100644 --- a/homeassistant/components/deconz/alarm_control_panel.py +++ b/homeassistant/components/deconz/alarm_control_panel.py @@ -81,7 +81,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.sensors.ancillary_control.subscribe( - async_add_sensor, + gateway.evaluate_add_device(async_add_sensor), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/binary_sensor.py b/homeassistant/components/deconz/binary_sensor.py index c3b16e509cb..e57f0bde3a6 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -5,6 +5,7 @@ from collections.abc import Callable from dataclasses import dataclass from pydeconz.interfaces.sensors import SensorResources +from pydeconz.models.event import EventType from pydeconz.models.sensor.alarm import Alarm from pydeconz.models.sensor.carbon_monoxide import CarbonMonoxide from pydeconz.models.sensor.fire import Fire @@ -188,48 +189,48 @@ async def async_setup_entry( gateway.entities[DOMAIN] = set() @callback - def async_add_sensor(sensors: list[SensorResources] | None = None) -> None: - """Add binary sensor from deCONZ.""" - entities: list[DeconzBinarySensor] = [] + def async_add_sensor(_: EventType, sensor_id: str) -> None: + """Add sensor from deCONZ.""" + sensor = gateway.api.sensors[sensor_id] - if sensors is None: - sensors = gateway.api.sensors.values() + if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"): + return - for sensor in sensors: - - if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"): + for description in ( + ENTITY_DESCRIPTIONS.get(type(sensor), []) + BINARY_SENSOR_DESCRIPTIONS + ): + if ( + not hasattr(sensor, description.key) + or description.value_fn(sensor) is None + ): continue - known_entities = set(gateway.entities[DOMAIN]) - for description in ( - ENTITY_DESCRIPTIONS.get(type(sensor), []) + BINARY_SENSOR_DESCRIPTIONS - ): + async_add_entities([DeconzBinarySensor(sensor, gateway, description)]) - if ( - not hasattr(sensor, description.key) - or description.value_fn(sensor) is None - ): - continue + config_entry.async_on_unload( + gateway.api.sensors.subscribe( + gateway.evaluate_add_device(async_add_sensor), + EventType.ADDED, + ) + ) + for sensor_id in gateway.api.sensors: + async_add_sensor(EventType.ADDED, sensor_id) - new_sensor = DeconzBinarySensor(sensor, gateway, description) - if new_sensor.unique_id not in known_entities: - entities.append(new_sensor) - - if entities: - async_add_entities(entities) + @callback + def async_reload_clip_sensors() -> None: + """Load clip sensor sensors from deCONZ.""" + for sensor_id, sensor in gateway.api.sensors.items(): + if sensor.type.startswith("CLIP"): + async_add_sensor(EventType.ADDED, sensor_id) config_entry.async_on_unload( async_dispatcher_connect( hass, - gateway.signal_new_sensor, - async_add_sensor, + gateway.signal_reload_clip_sensors, + async_reload_clip_sensors, ) ) - async_add_sensor( - [gateway.api.sensors[key] for key in sorted(gateway.api.sensors, key=int)] - ) - class DeconzBinarySensor(DeconzDevice, BinarySensorEntity): """Representation of a deCONZ binary sensor.""" diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py index bdc366ff7db..1d57288d275 100644 --- a/homeassistant/components/deconz/climate.py +++ b/homeassistant/components/deconz/climate.py @@ -106,7 +106,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.sensors.thermostat.subscribe( - async_add_climate, + gateway.evaluate_add_device(async_add_climate), EventType.ADDED, ) ) @@ -115,7 +115,7 @@ async def async_setup_entry( @callback def async_reload_clip_sensors() -> None: - """Load clip climate sensors from deCONZ.""" + """Load clip sensors from deCONZ.""" for climate_id, climate in gateway.api.sensors.thermostat.items(): if climate.type.startswith("CLIP"): async_add_climate(EventType.ADDED, climate_id) diff --git a/homeassistant/components/deconz/cover.py b/homeassistant/components/deconz/cover.py index 3d5dabb73c1..1931ac78389 100644 --- a/homeassistant/components/deconz/cover.py +++ b/homeassistant/components/deconz/cover.py @@ -45,7 +45,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.lights.covers.subscribe( - async_add_cover, + gateway.evaluate_add_device(async_add_cover), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/deconz_event.py b/homeassistant/components/deconz/deconz_event.py index 4f04aa34fe2..cfd60a63ec9 100644 --- a/homeassistant/components/deconz/deconz_event.py +++ b/homeassistant/components/deconz/deconz_event.py @@ -66,14 +66,14 @@ async def async_setup_events(gateway: DeconzGateway) -> None: gateway.config_entry.async_on_unload( gateway.api.sensors.ancillary_control.subscribe( - async_add_sensor, + gateway.evaluate_add_device(async_add_sensor), EventType.ADDED, ) ) gateway.config_entry.async_on_unload( gateway.api.sensors.switch.subscribe( - async_add_sensor, + gateway.evaluate_add_device(async_add_sensor), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/fan.py b/homeassistant/components/deconz/fan.py index 93daae55ccf..b0d6d7755a2 100644 --- a/homeassistant/components/deconz/fan.py +++ b/homeassistant/components/deconz/fan.py @@ -50,7 +50,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.lights.fans.subscribe( - async_add_fan, + gateway.evaluate_add_device(async_add_fan), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index 0e880fa22c1..d59c5e2d160 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio +from collections.abc import Callable from types import MappingProxyType from typing import TYPE_CHECKING, Any, cast @@ -10,6 +11,7 @@ import async_timeout from pydeconz import DeconzSession, errors from pydeconz.models import ResourceGroup from pydeconz.models.alarm_system import AlarmSystem as DeconzAlarmSystem +from pydeconz.models.event import EventType from pydeconz.models.group import Group as DeconzGroup from pydeconz.models.light import LightBase as DeconzLight from pydeconz.models.sensor import SensorBase as DeconzSensor @@ -76,10 +78,14 @@ class DeconzGateway: self.deconz_ids: dict[str, str] = {} self.entities: dict[str, set[str]] = {} self.events: list[DeconzAlarmEvent | DeconzEvent] = [] + self.ignored_devices: set[tuple[Callable[[EventType, str], None], str]] = set() self._option_allow_deconz_groups = self.config_entry.options.get( CONF_ALLOW_DECONZ_GROUPS, DEFAULT_ALLOW_DECONZ_GROUPS ) + self.option_allow_new_devices = self.config_entry.options.get( + CONF_ALLOW_NEW_DEVICES, DEFAULT_ALLOW_NEW_DEVICES + ) @property def bridgeid(self) -> str: @@ -112,12 +118,31 @@ class DeconzGateway: CONF_ALLOW_DECONZ_GROUPS, DEFAULT_ALLOW_DECONZ_GROUPS ) - @property - def option_allow_new_devices(self) -> bool: - """Allow automatic adding of new devices.""" - return self.config_entry.options.get( - CONF_ALLOW_NEW_DEVICES, DEFAULT_ALLOW_NEW_DEVICES - ) + @callback + def evaluate_add_device( + self, add_device_callback: Callable[[EventType, str], None] + ) -> Callable[[EventType, str], None]: + """Wrap add_device_callback to check allow_new_devices option.""" + + def async_add_device(event: EventType, device_id: str) -> None: + """Add device or add it to ignored_devices set. + + If ignore_state_updates is True means device_refresh service is used. + Device_refresh is expected to load new devices. + """ + if not self.option_allow_new_devices and not self.ignore_state_updates: + self.ignored_devices.add((add_device_callback, device_id)) + return + add_device_callback(event, device_id) + + return async_add_device + + @callback + def load_ignored_devices(self) -> None: + """Load previously ignored devices.""" + for add_entities, device_id in self.ignored_devices: + add_entities(EventType.ADDED, device_id) + self.ignored_devices.clear() # Callbacks @@ -230,6 +255,14 @@ class DeconzGateway: self._option_allow_deconz_groups = self.option_allow_deconz_groups + option_allow_new_devices = self.config_entry.options.get( + CONF_ALLOW_NEW_DEVICES, DEFAULT_ALLOW_NEW_DEVICES + ) + if option_allow_new_devices and not self.option_allow_new_devices: + self.load_ignored_devices() + + self.option_allow_new_devices = option_allow_new_devices + entity_registry = er.async_get(self.hass) for entity_id, deconz_id in self.deconz_ids.items(): diff --git a/homeassistant/components/deconz/light.py b/homeassistant/components/deconz/light.py index 2b03bb0ddb4..0f23ff02831 100644 --- a/homeassistant/components/deconz/light.py +++ b/homeassistant/components/deconz/light.py @@ -95,7 +95,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.lights.lights.subscribe( - async_add_light, + gateway.evaluate_add_device(async_add_light), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/lock.py b/homeassistant/components/deconz/lock.py index 5b8c9d87e67..dc3da3cbe8c 100644 --- a/homeassistant/components/deconz/lock.py +++ b/homeassistant/components/deconz/lock.py @@ -34,7 +34,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.lights.locks.subscribe( - async_add_lock_from_light, + gateway.evaluate_add_device(async_add_lock_from_light), EventType.ADDED, ) ) @@ -49,7 +49,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.sensors.door_lock.subscribe( - async_add_lock_from_sensor, + gateway.evaluate_add_device(async_add_lock_from_sensor), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/number.py b/homeassistant/components/deconz/number.py index 81886fd3c7c..5d555797372 100644 --- a/homeassistant/components/deconz/number.py +++ b/homeassistant/components/deconz/number.py @@ -77,7 +77,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.sensors.presence.subscribe( - async_add_sensor, + gateway.evaluate_add_device(async_add_sensor), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/services.py b/homeassistant/components/deconz/services.py index 3d12a293c39..a9a5172d72e 100644 --- a/homeassistant/components/deconz/services.py +++ b/homeassistant/components/deconz/services.py @@ -144,6 +144,7 @@ async def async_refresh_devices_service(gateway: DeconzGateway) -> 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 for resource_type in gateway.deconz_resource_type_to_signal_new_device: diff --git a/homeassistant/components/deconz/siren.py b/homeassistant/components/deconz/siren.py index 2a11cd5346a..0c5b3010626 100644 --- a/homeassistant/components/deconz/siren.py +++ b/homeassistant/components/deconz/siren.py @@ -37,7 +37,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.lights.sirens.subscribe( - async_add_siren, + gateway.evaluate_add_device(async_add_siren), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/switch.py b/homeassistant/components/deconz/switch.py index 6789b20b3c7..d6e12365407 100644 --- a/homeassistant/components/deconz/switch.py +++ b/homeassistant/components/deconz/switch.py @@ -30,21 +30,21 @@ async def async_setup_entry( gateway.entities[DOMAIN] = set() @callback - def async_add_switch(_: EventType, light_id: str) -> None: + def async_add_switch(_: EventType, switch_id: str) -> None: """Add switch from deCONZ.""" - light = gateway.api.lights.lights[light_id] - if light.type not in POWER_PLUGS: + switch = gateway.api.lights.lights[switch_id] + if switch.type not in POWER_PLUGS: return - async_add_entities([DeconzPowerPlug(light, gateway)]) + async_add_entities([DeconzPowerPlug(switch, gateway)]) config_entry.async_on_unload( gateway.api.lights.lights.subscribe( - async_add_switch, + gateway.evaluate_add_device(async_add_switch), EventType.ADDED, ) ) - for light_id in gateway.api.lights.lights: - async_add_switch(EventType.ADDED, light_id) + for switch_id in gateway.api.lights.lights: + async_add_switch(EventType.ADDED, switch_id) class DeconzPowerPlug(DeconzDevice, SwitchEntity):