deCONZ - Directly reflect changes to config entry options (#31661)
* Directly reflect changes to config entry options * Remove print * Add tests * Add title to config optionspull/31963/head
parent
17f3332c89
commit
b5df2ba853
|
@ -108,7 +108,8 @@
|
|||
"allow_clip_sensor": "Allow deCONZ CLIP sensors",
|
||||
"allow_deconz_groups": "Allow deCONZ light groups"
|
||||
},
|
||||
"description": "Configure visibility of deCONZ device types"
|
||||
"description": "Configure visibility of deCONZ device types",
|
||||
"title": "deCONZ options"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
gateway.option_allow_clip_sensor
|
||||
or not sensor.type.startswith("CLIP")
|
||||
)
|
||||
and sensor.deconz_id not in gateway.deconz_ids.values()
|
||||
):
|
||||
entities.append(DeconzBinarySensor(sensor, gateway))
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
gateway.option_allow_clip_sensor
|
||||
or not sensor.type.startswith("CLIP")
|
||||
)
|
||||
and sensor.deconz_id not in gateway.deconz_ids.values()
|
||||
):
|
||||
entities.append(DeconzThermostat(sensor, gateway))
|
||||
|
||||
|
|
|
@ -77,6 +77,11 @@ class DeconzDevice(DeconzBase, Entity):
|
|||
self.hass, self.gateway.signal_reachable, self.async_update_callback
|
||||
)
|
||||
)
|
||||
self.listeners.append(
|
||||
async_dispatcher_connect(
|
||||
self.hass, self.gateway.signal_remove_entity, self.async_remove_self
|
||||
)
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Disconnect device object when removed."""
|
||||
|
@ -85,6 +90,15 @@ class DeconzDevice(DeconzBase, Entity):
|
|||
for unsub_dispatcher in self.listeners:
|
||||
unsub_dispatcher()
|
||||
|
||||
async def async_remove_self(self, deconz_ids: list) -> None:
|
||||
"""Schedule removal of this entity.
|
||||
|
||||
Called by signal_remove_entity scheduled by async_added_to_hass.
|
||||
"""
|
||||
if self._device.deconz_id not in deconz_ids:
|
||||
return
|
||||
await self.async_remove()
|
||||
|
||||
@callback
|
||||
def async_update_callback(self, force_update=False, ignore_update=False):
|
||||
"""Update the device's state."""
|
||||
|
|
|
@ -20,6 +20,8 @@ from .const import (
|
|||
DOMAIN,
|
||||
LOGGER,
|
||||
NEW_DEVICE,
|
||||
NEW_GROUP,
|
||||
NEW_SENSOR,
|
||||
SUPPORTED_PLATFORMS,
|
||||
)
|
||||
from .errors import AuthenticationRequired, CannotConnect
|
||||
|
@ -45,6 +47,9 @@ class DeconzGateway:
|
|||
self.events = []
|
||||
self.listeners = []
|
||||
|
||||
self._current_option_allow_clip_sensor = self.option_allow_clip_sensor
|
||||
self._current_option_allow_deconz_groups = self.option_allow_deconz_groups
|
||||
|
||||
@property
|
||||
def bridgeid(self) -> str:
|
||||
"""Return the unique identifier of the gateway."""
|
||||
|
@ -108,22 +113,64 @@ class DeconzGateway:
|
|||
|
||||
self.api.start()
|
||||
|
||||
self.config_entry.add_update_listener(self.async_new_address)
|
||||
self.config_entry.add_update_listener(self.async_config_entry_updated)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
async def async_new_address(hass, entry) -> None:
|
||||
"""Handle signals of gateway getting new address.
|
||||
async def async_config_entry_updated(hass, entry) -> None:
|
||||
"""Handle signals of config entry being updated.
|
||||
|
||||
This is a static method because a class method (bound method),
|
||||
can not be used with weak references.
|
||||
This is a static method because a class method (bound method), can not be used with weak references.
|
||||
Causes for this is either discovery updating host address or config entry options changing.
|
||||
"""
|
||||
gateway = get_gateway_from_config_entry(hass, entry)
|
||||
if gateway.api.host != entry.data[CONF_HOST]:
|
||||
gateway.api.close()
|
||||
gateway.api.host = entry.data[CONF_HOST]
|
||||
gateway.api.start()
|
||||
return
|
||||
|
||||
await gateway.options_updated()
|
||||
|
||||
async def options_updated(self):
|
||||
"""Manage entities affected by config entry options."""
|
||||
deconz_ids = []
|
||||
|
||||
if self._current_option_allow_clip_sensor != self.option_allow_clip_sensor:
|
||||
self._current_option_allow_clip_sensor = self.option_allow_clip_sensor
|
||||
|
||||
sensors = [
|
||||
sensor
|
||||
for sensor in self.api.sensors.values()
|
||||
if sensor.type.startswith("CLIP")
|
||||
]
|
||||
|
||||
if self.option_allow_clip_sensor:
|
||||
self.async_add_device_callback(NEW_SENSOR, sensors)
|
||||
else:
|
||||
deconz_ids += [sensor.deconz_id for sensor in sensors]
|
||||
|
||||
if self._current_option_allow_deconz_groups != self.option_allow_deconz_groups:
|
||||
self._current_option_allow_deconz_groups = self.option_allow_deconz_groups
|
||||
|
||||
groups = list(self.api.groups.values())
|
||||
|
||||
if self.option_allow_deconz_groups:
|
||||
self.async_add_device_callback(NEW_GROUP, groups)
|
||||
else:
|
||||
deconz_ids += [group.deconz_id for group in groups]
|
||||
|
||||
if deconz_ids:
|
||||
async_dispatcher_send(self.hass, self.signal_remove_entity, deconz_ids)
|
||||
|
||||
entity_registry = await self.hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
for entity_id, deconz_id in self.deconz_ids.items():
|
||||
if deconz_id in deconz_ids and entity_registry.async_is_registered(
|
||||
entity_id
|
||||
):
|
||||
entity_registry.async_remove(entity_id)
|
||||
|
||||
@property
|
||||
def signal_reachable(self) -> str:
|
||||
|
@ -141,6 +188,11 @@ class DeconzGateway:
|
|||
"""Gateway specific event to signal new device."""
|
||||
return NEW_DEVICE[device_type].format(self.bridgeid)
|
||||
|
||||
@property
|
||||
def signal_remove_entity(self) -> str:
|
||||
"""Gateway specific event to signal removal of entity."""
|
||||
return f"deconz-remove-{self.bridgeid}"
|
||||
|
||||
@callback
|
||||
def async_add_device_callback(self, device_type, device) -> None:
|
||||
"""Handle event of new device creation in deCONZ."""
|
||||
|
|
|
@ -67,7 +67,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
entities = []
|
||||
|
||||
for group in groups:
|
||||
if group.lights:
|
||||
if group.lights and group.deconz_id not in gateway.deconz_ids.values():
|
||||
entities.append(DeconzGroup(group, gateway))
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
|
|
@ -68,6 +68,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
gateway.option_allow_clip_sensor
|
||||
or not sensor.type.startswith("CLIP")
|
||||
)
|
||||
and sensor.deconz_id not in gateway.deconz_ids.values()
|
||||
):
|
||||
entities.append(DeconzSensor(sensor, gateway))
|
||||
|
||||
|
|
|
@ -14,20 +14,9 @@
|
|||
"title": "Link with deCONZ",
|
||||
"description": "Unlock your deCONZ gateway to register with Home Assistant.\n\n1. Go to deCONZ Settings -> Gateway -> Advanced\n2. Press \"Authenticate app\" button"
|
||||
},
|
||||
"options": {
|
||||
"title": "Extra configuration options for deCONZ",
|
||||
"data": {
|
||||
"allow_clip_sensor": "Allow importing virtual sensors",
|
||||
"allow_deconz_groups": "Allow importing deCONZ groups"
|
||||
}
|
||||
},
|
||||
"hassio_confirm": {
|
||||
"title": "deCONZ Zigbee gateway via Hass.io add-on",
|
||||
"description": "Do you want to configure Home Assistant to connect to the deCONZ gateway provided by the Hass.io add-on {addon}?",
|
||||
"data": {
|
||||
"allow_clip_sensor": "Allow importing virtual sensors",
|
||||
"allow_deconz_groups": "Allow importing deCONZ groups"
|
||||
}
|
||||
"description": "Do you want to configure Home Assistant to connect to the deCONZ gateway provided by the Hass.io add-on {addon}?"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
|
@ -45,11 +34,12 @@
|
|||
"options": {
|
||||
"step": {
|
||||
"deconz_devices": {
|
||||
"description": "Configure visibility of deCONZ device types",
|
||||
"data": {
|
||||
"allow_clip_sensor": "Allow deCONZ CLIP sensors",
|
||||
"allow_deconz_groups": "Allow deCONZ light groups"
|
||||
}
|
||||
},
|
||||
"description": "Configure visibility of deCONZ device types",
|
||||
"title": "deCONZ options"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -105,4 +95,4 @@
|
|||
"side_6": "Side 6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -134,6 +134,28 @@ async def test_allow_clip_sensor(hass):
|
|||
vibration_sensor = hass.states.get("binary_sensor.vibration_sensor")
|
||||
assert vibration_sensor.state == "on"
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: False}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "binary_sensor.presence_sensor" in gateway.deconz_ids
|
||||
assert "binary_sensor.temperature_sensor" not in gateway.deconz_ids
|
||||
assert "binary_sensor.clip_presence_sensor" not in gateway.deconz_ids
|
||||
assert "binary_sensor.vibration_sensor" in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 3
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: True}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "binary_sensor.presence_sensor" in gateway.deconz_ids
|
||||
assert "binary_sensor.temperature_sensor" not in gateway.deconz_ids
|
||||
assert "binary_sensor.clip_presence_sensor" in gateway.deconz_ids
|
||||
assert "binary_sensor.vibration_sensor" in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 4
|
||||
|
||||
|
||||
async def test_add_new_binary_sensor(hass):
|
||||
"""Test that adding a new binary sensor works."""
|
||||
|
|
|
@ -214,6 +214,30 @@ async def test_clip_climate_device(hass):
|
|||
clip_thermostat = hass.states.get("climate.clip_thermostat")
|
||||
assert clip_thermostat.state == "heat"
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: False}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "climate.thermostat" in gateway.deconz_ids
|
||||
assert "sensor.thermostat" not in gateway.deconz_ids
|
||||
assert "sensor.thermostat_battery_level" in gateway.deconz_ids
|
||||
assert "climate.presence_sensor" not in gateway.deconz_ids
|
||||
assert "climate.clip_thermostat" not in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 3
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: True}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "climate.thermostat" in gateway.deconz_ids
|
||||
assert "sensor.thermostat" not in gateway.deconz_ids
|
||||
assert "sensor.thermostat_battery_level" in gateway.deconz_ids
|
||||
assert "climate.presence_sensor" not in gateway.deconz_ids
|
||||
assert "climate.clip_thermostat" in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 4
|
||||
|
||||
|
||||
async def test_verify_state_update(hass):
|
||||
"""Test that state update properly."""
|
||||
|
|
|
@ -245,3 +245,29 @@ async def test_disable_light_groups(hass):
|
|||
|
||||
empty_group = hass.states.get("light.empty_group")
|
||||
assert empty_group is None
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_DECONZ_GROUPS: True}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "light.rgb_light" in gateway.deconz_ids
|
||||
assert "light.tunable_white_light" in gateway.deconz_ids
|
||||
assert "light.light_group" in gateway.deconz_ids
|
||||
assert "light.empty_group" not in gateway.deconz_ids
|
||||
assert "light.on_off_switch" not in gateway.deconz_ids
|
||||
# 3 entities
|
||||
assert len(hass.states.async_all()) == 5
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_DECONZ_GROUPS: False}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "light.rgb_light" in gateway.deconz_ids
|
||||
assert "light.tunable_white_light" in gateway.deconz_ids
|
||||
assert "light.light_group" not in gateway.deconz_ids
|
||||
assert "light.empty_group" not in gateway.deconz_ids
|
||||
assert "light.on_off_switch" not in gateway.deconz_ids
|
||||
# 3 entities
|
||||
assert len(hass.states.async_all()) == 4
|
||||
|
|
|
@ -218,6 +218,40 @@ async def test_allow_clip_sensors(hass):
|
|||
clip_light_level_sensor = hass.states.get("sensor.clip_light_level_sensor")
|
||||
assert clip_light_level_sensor.state == "999.8"
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: False}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "sensor.light_level_sensor" in gateway.deconz_ids
|
||||
assert "sensor.presence_sensor" not in gateway.deconz_ids
|
||||
assert "sensor.switch_1" not in gateway.deconz_ids
|
||||
assert "sensor.switch_1_battery_level" not in gateway.deconz_ids
|
||||
assert "sensor.switch_2" not in gateway.deconz_ids
|
||||
assert "sensor.switch_2_battery_level" in gateway.deconz_ids
|
||||
assert "sensor.daylight_sensor" not in gateway.deconz_ids
|
||||
assert "sensor.power_sensor" in gateway.deconz_ids
|
||||
assert "sensor.consumption_sensor" in gateway.deconz_ids
|
||||
assert "sensor.clip_light_level_sensor" not in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 5
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: True}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "sensor.light_level_sensor" in gateway.deconz_ids
|
||||
assert "sensor.presence_sensor" not in gateway.deconz_ids
|
||||
assert "sensor.switch_1" not in gateway.deconz_ids
|
||||
assert "sensor.switch_1_battery_level" not in gateway.deconz_ids
|
||||
assert "sensor.switch_2" not in gateway.deconz_ids
|
||||
assert "sensor.switch_2_battery_level" in gateway.deconz_ids
|
||||
assert "sensor.daylight_sensor" not in gateway.deconz_ids
|
||||
assert "sensor.power_sensor" in gateway.deconz_ids
|
||||
assert "sensor.consumption_sensor" in gateway.deconz_ids
|
||||
assert "sensor.clip_light_level_sensor" in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 6
|
||||
|
||||
|
||||
async def test_add_new_sensor(hass):
|
||||
"""Test that adding a new sensor works."""
|
||||
|
|
Loading…
Reference in New Issue