diff --git a/homeassistant/components/deconz/alarm_control_panel.py b/homeassistant/components/deconz/alarm_control_panel.py index e635b11972e..90c34da0f12 100644 --- a/homeassistant/components/deconz/alarm_control_panel.py +++ b/homeassistant/components/deconz/alarm_control_panel.py @@ -83,16 +83,11 @@ async def async_setup_entry( [DeconzAlarmControlPanel(sensor, gateway, alarm_system_id)] ) - config_entry.async_on_unload( - gateway.api.sensors.ancillary_control.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors.ancillary_control, ) - for sensor_id in gateway.api.sensors.ancillary_control: - async_add_sensor(EventType.ADDED, sensor_id) - class DeconzAlarmControlPanel(DeconzDevice, AlarmControlPanelEntity): """Representation of a deCONZ alarm control panel.""" diff --git a/homeassistant/components/deconz/binary_sensor.py b/homeassistant/components/deconz/binary_sensor.py index e57f0bde3a6..d109fb8b34d 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -207,14 +207,10 @@ async def async_setup_entry( async_add_entities([DeconzBinarySensor(sensor, gateway, description)]) - config_entry.async_on_unload( - gateway.api.sensors.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors, ) - for sensor_id in gateway.api.sensors: - async_add_sensor(EventType.ADDED, sensor_id) @callback def async_reload_clip_sensors() -> None: diff --git a/homeassistant/components/deconz/button.py b/homeassistant/components/deconz/button.py index ed61750af6f..498c88a2351 100644 --- a/homeassistant/components/deconz/button.py +++ b/homeassistant/components/deconz/button.py @@ -65,16 +65,11 @@ async def async_setup_entry( for description in ENTITY_DESCRIPTIONS.get(PydeconzScene, []) ) - config_entry.async_on_unload( - gateway.api.scenes.subscribe( - async_add_scene, - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_scene, + gateway.api.scenes, ) - for scene_id in gateway.api.scenes: - async_add_scene(EventType.ADDED, scene_id) - class DeconzButton(DeconzSceneMixin, ButtonEntity): """Representation of a deCONZ button entity.""" diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py index 1d57288d275..6887b4238d2 100644 --- a/homeassistant/components/deconz/climate.py +++ b/homeassistant/components/deconz/climate.py @@ -104,14 +104,10 @@ async def async_setup_entry( return async_add_entities([DeconzThermostat(climate, gateway)]) - config_entry.async_on_unload( - gateway.api.sensors.thermostat.subscribe( - gateway.evaluate_add_device(async_add_climate), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_climate, + gateway.api.sensors.thermostat, ) - for climate_id in gateway.api.sensors.thermostat: - async_add_climate(EventType.ADDED, climate_id) @callback def async_reload_clip_sensors() -> None: diff --git a/homeassistant/components/deconz/cover.py b/homeassistant/components/deconz/cover.py index 1931ac78389..9efbeac366f 100644 --- a/homeassistant/components/deconz/cover.py +++ b/homeassistant/components/deconz/cover.py @@ -43,14 +43,10 @@ async def async_setup_entry( cover = gateway.api.lights.covers[cover_id] async_add_entities([DeconzCover(cover, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.covers.subscribe( - gateway.evaluate_add_device(async_add_cover), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_cover, + gateway.api.lights.covers, ) - for cover_id in gateway.api.lights.covers: - async_add_cover(EventType.ADDED, cover_id) class DeconzCover(DeconzDevice, CoverEntity): diff --git a/homeassistant/components/deconz/deconz_event.py b/homeassistant/components/deconz/deconz_event.py index cfd60a63ec9..fa53ef1b5bc 100644 --- a/homeassistant/components/deconz/deconz_event.py +++ b/homeassistant/components/deconz/deconz_event.py @@ -58,29 +58,18 @@ async def async_setup_events(gateway: DeconzGateway) -> None: elif isinstance(sensor, AncillaryControl): new_event = DeconzAlarmEvent(sensor, gateway) - else: - return None - gateway.hass.async_create_task(new_event.async_update_device_registry()) gateway.events.append(new_event) - gateway.config_entry.async_on_unload( - gateway.api.sensors.ancillary_control.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors.switch, ) - - gateway.config_entry.async_on_unload( - gateway.api.sensors.switch.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors.ancillary_control, ) - for sensor_id in gateway.api.sensors: - async_add_sensor(EventType.ADDED, sensor_id) - @callback def async_unload_events(gateway: DeconzGateway) -> None: diff --git a/homeassistant/components/deconz/fan.py b/homeassistant/components/deconz/fan.py index b0d6d7755a2..ab9a1ba6f4a 100644 --- a/homeassistant/components/deconz/fan.py +++ b/homeassistant/components/deconz/fan.py @@ -48,14 +48,10 @@ async def async_setup_entry( fan = gateway.api.lights.fans[fan_id] async_add_entities([DeconzFan(fan, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.fans.subscribe( - gateway.evaluate_add_device(async_add_fan), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_fan, + gateway.api.lights.fans, ) - for fan_id in gateway.api.lights.fans: - async_add_fan(EventType.ADDED, fan_id) class DeconzFan(DeconzDevice, FanEntity): diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index 1f6a45f3ad6..5890e372e66 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, cast import async_timeout from pydeconz import DeconzSession, errors +from pydeconz.interfaces.api import APIItems, GroupedAPIItems from pydeconz.models.event import EventType from homeassistant.config_entries import SOURCE_HASSIO, ConfigEntry @@ -105,9 +106,11 @@ class DeconzGateway: ) @callback - def evaluate_add_device( - self, add_device_callback: Callable[[EventType, str], None] - ) -> Callable[[EventType, str], None]: + def register_platform_add_device_callback( + self, + add_device_callback: Callable[[EventType, str], None], + deconz_device_interface: APIItems | GroupedAPIItems, + ) -> None: """Wrap add_device_callback to check allow_new_devices option.""" def async_add_device(event: EventType, device_id: str) -> None: @@ -117,11 +120,19 @@ class DeconzGateway: 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)) + self.ignored_devices.add((async_add_device, device_id)) return add_device_callback(event, device_id) - return async_add_device + self.config_entry.async_on_unload( + deconz_device_interface.subscribe( + async_add_device, + EventType.ADDED, + ) + ) + + for device_id in deconz_device_interface: + add_device_callback(EventType.ADDED, device_id) @callback def load_ignored_devices(self) -> None: @@ -191,6 +202,8 @@ class DeconzGateway: """Manage entities affected by config entry options.""" deconz_ids = [] + # Allow CLIP sensors + if self.option_allow_clip_sensor: async_dispatcher_send(self.hass, self.signal_reload_clip_sensors) @@ -201,6 +214,8 @@ class DeconzGateway: if sensor.type.startswith("CLIP") ] + # Allow Groups + if self.option_allow_deconz_groups: if not self._option_allow_deconz_groups: async_dispatcher_send(self.hass, self.signal_reload_groups) @@ -209,13 +224,17 @@ class DeconzGateway: self._option_allow_deconz_groups = self.option_allow_deconz_groups + # Allow adding new devices + 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() + if option_allow_new_devices != self.option_allow_new_devices: + self.option_allow_new_devices = option_allow_new_devices + if option_allow_new_devices: + self.load_ignored_devices() - self.option_allow_new_devices = option_allow_new_devices + # Remove entities based on above categories entity_registry = er.async_get(self.hass) diff --git a/homeassistant/components/deconz/light.py b/homeassistant/components/deconz/light.py index 0f23ff02831..53773369176 100644 --- a/homeassistant/components/deconz/light.py +++ b/homeassistant/components/deconz/light.py @@ -93,23 +93,15 @@ async def async_setup_entry( async_add_entities([DeconzLight(light, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.lights.subscribe( - gateway.evaluate_add_device(async_add_light), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_light, + gateway.api.lights.lights, ) - for light_id in gateway.api.lights.lights: - async_add_light(EventType.ADDED, light_id) - config_entry.async_on_unload( - gateway.api.lights.fans.subscribe( - async_add_light, - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_light, + gateway.api.lights.fans, ) - for light_id in gateway.api.lights.fans: - async_add_light(EventType.ADDED, light_id) @callback def async_add_group(_: EventType, group_id: str) -> None: diff --git a/homeassistant/components/deconz/lock.py b/homeassistant/components/deconz/lock.py index dc3da3cbe8c..78ccae30441 100644 --- a/homeassistant/components/deconz/lock.py +++ b/homeassistant/components/deconz/lock.py @@ -32,14 +32,10 @@ async def async_setup_entry( lock = gateway.api.lights.locks[lock_id] async_add_entities([DeconzLock(lock, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.locks.subscribe( - gateway.evaluate_add_device(async_add_lock_from_light), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_lock_from_light, + gateway.api.lights.locks, ) - for lock_id in gateway.api.lights.locks: - async_add_lock_from_light(EventType.ADDED, lock_id) @callback def async_add_lock_from_sensor(_: EventType, lock_id: str) -> None: @@ -47,14 +43,10 @@ async def async_setup_entry( lock = gateway.api.sensors.door_lock[lock_id] async_add_entities([DeconzLock(lock, gateway)]) - config_entry.async_on_unload( - gateway.api.sensors.door_lock.subscribe( - gateway.evaluate_add_device(async_add_lock_from_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_lock_from_sensor, + gateway.api.sensors.door_lock, ) - for lock_id in gateway.api.sensors.door_lock: - async_add_lock_from_sensor(EventType.ADDED, lock_id) class DeconzLock(DeconzDevice, LockEntity): diff --git a/homeassistant/components/deconz/number.py b/homeassistant/components/deconz/number.py index 5d555797372..a7bb014d76a 100644 --- a/homeassistant/components/deconz/number.py +++ b/homeassistant/components/deconz/number.py @@ -75,14 +75,10 @@ async def async_setup_entry( continue async_add_entities([DeconzNumber(sensor, gateway, description)]) - config_entry.async_on_unload( - gateway.api.sensors.presence.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors.presence, ) - for sensor_id in gateway.api.sensors.presence: - async_add_sensor(EventType.ADDED, sensor_id) class DeconzNumber(DeconzDevice, NumberEntity): diff --git a/homeassistant/components/deconz/scene.py b/homeassistant/components/deconz/scene.py index 28448da4f75..dfbb6ae828b 100644 --- a/homeassistant/components/deconz/scene.py +++ b/homeassistant/components/deconz/scene.py @@ -30,16 +30,11 @@ async def async_setup_entry( scene = gateway.api.scenes[scene_id] async_add_entities([DeconzScene(scene, gateway)]) - config_entry.async_on_unload( - gateway.api.scenes.subscribe( - async_add_scene, - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_scene, + gateway.api.scenes, ) - for scene_id in gateway.api.scenes: - async_add_scene(EventType.ADDED, scene_id) - class DeconzScene(DeconzSceneMixin, Scene): """Representation of a deCONZ scene.""" diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index 021dcf7168a..663aadde73f 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -264,14 +264,10 @@ async def async_setup_entry( async_add_entities([DeconzSensor(sensor, gateway, description)]) - config_entry.async_on_unload( - gateway.api.sensors.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors, ) - for sensor_id in gateway.api.sensors: - async_add_sensor(EventType.ADDED, sensor_id) @callback def async_reload_clip_sensors() -> None: diff --git a/homeassistant/components/deconz/siren.py b/homeassistant/components/deconz/siren.py index 0c5b3010626..8427b6ce75d 100644 --- a/homeassistant/components/deconz/siren.py +++ b/homeassistant/components/deconz/siren.py @@ -35,14 +35,10 @@ async def async_setup_entry( siren = gateway.api.lights.sirens[siren_id] async_add_entities([DeconzSiren(siren, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.sirens.subscribe( - gateway.evaluate_add_device(async_add_siren), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_siren, + gateway.api.lights.sirens, ) - for siren_id in gateway.api.lights.sirens: - async_add_siren(EventType.ADDED, siren_id) class DeconzSiren(DeconzDevice, SirenEntity): diff --git a/homeassistant/components/deconz/switch.py b/homeassistant/components/deconz/switch.py index d6e12365407..d54ff1f36ba 100644 --- a/homeassistant/components/deconz/switch.py +++ b/homeassistant/components/deconz/switch.py @@ -37,14 +37,10 @@ async def async_setup_entry( return async_add_entities([DeconzPowerPlug(switch, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.lights.subscribe( - gateway.evaluate_add_device(async_add_switch), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_switch, + gateway.api.lights.lights, ) - for switch_id in gateway.api.lights.lights: - async_add_switch(EventType.ADDED, switch_id) class DeconzPowerPlug(DeconzDevice, SwitchEntity): diff --git a/tests/components/deconz/test_binary_sensor.py b/tests/components/deconz/test_binary_sensor.py index 8d3eb105896..b6a21fb9fa6 100644 --- a/tests/components/deconz/test_binary_sensor.py +++ b/tests/components/deconz/test_binary_sensor.py @@ -636,7 +636,7 @@ async def test_add_new_binary_sensor(hass, aioclient_mock, mock_deconz_websocket assert hass.states.get("binary_sensor.presence_sensor").state == STATE_OFF -async def test_add_new_binary_sensor_ignored( +async def test_add_new_binary_sensor_ignored_load_entities_on_service_call( hass, aioclient_mock, mock_deconz_websocket ): """Test that adding a new binary sensor is not allowed.""" @@ -683,3 +683,54 @@ async def test_add_new_binary_sensor_ignored( assert len(hass.states.async_all()) == 1 assert hass.states.get("binary_sensor.presence_sensor") + + +async def test_add_new_binary_sensor_ignored_load_entities_on_options_change( + hass, aioclient_mock, mock_deconz_websocket +): + """Test that adding a new binary sensor is not allowed.""" + sensor = { + "name": "Presence sensor", + "type": "ZHAPresence", + "state": {"presence": False}, + "config": {"on": True, "reachable": True}, + "uniqueid": "00:00:00:00:00:00:00:00-00", + } + event_added_sensor = { + "t": "event", + "e": "added", + "r": "sensors", + "id": "1", + "sensor": sensor, + } + + config_entry = await setup_deconz_integration( + hass, + aioclient_mock, + options={CONF_MASTER_GATEWAY: True, CONF_ALLOW_NEW_DEVICES: False}, + ) + + assert len(hass.states.async_all()) == 0 + + await mock_deconz_websocket(data=event_added_sensor) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + assert not hass.states.get("binary_sensor.presence_sensor") + + entity_registry = er.async_get(hass) + assert ( + len(async_entries_for_config_entry(entity_registry, config_entry.entry_id)) == 0 + ) + + aioclient_mock.clear_requests() + data = {"config": {}, "groups": {}, "lights": {}, "sensors": {"1": sensor}} + mock_deconz_request(aioclient_mock, config_entry.data, data) + + hass.config_entries.async_update_entry( + config_entry, options={CONF_ALLOW_NEW_DEVICES: True} + ) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 1 + assert hass.states.get("binary_sensor.presence_sensor")