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_clip_sensor": "Allow deCONZ CLIP sensors",
|
||||||
"allow_deconz_groups": "Allow deCONZ light groups"
|
"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
|
gateway.option_allow_clip_sensor
|
||||||
or not sensor.type.startswith("CLIP")
|
or not sensor.type.startswith("CLIP")
|
||||||
)
|
)
|
||||||
|
and sensor.deconz_id not in gateway.deconz_ids.values()
|
||||||
):
|
):
|
||||||
entities.append(DeconzBinarySensor(sensor, gateway))
|
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
|
gateway.option_allow_clip_sensor
|
||||||
or not sensor.type.startswith("CLIP")
|
or not sensor.type.startswith("CLIP")
|
||||||
)
|
)
|
||||||
|
and sensor.deconz_id not in gateway.deconz_ids.values()
|
||||||
):
|
):
|
||||||
entities.append(DeconzThermostat(sensor, gateway))
|
entities.append(DeconzThermostat(sensor, gateway))
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,11 @@ class DeconzDevice(DeconzBase, Entity):
|
||||||
self.hass, self.gateway.signal_reachable, self.async_update_callback
|
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:
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
"""Disconnect device object when removed."""
|
"""Disconnect device object when removed."""
|
||||||
|
@ -85,6 +90,15 @@ class DeconzDevice(DeconzBase, Entity):
|
||||||
for unsub_dispatcher in self.listeners:
|
for unsub_dispatcher in self.listeners:
|
||||||
unsub_dispatcher()
|
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
|
@callback
|
||||||
def async_update_callback(self, force_update=False, ignore_update=False):
|
def async_update_callback(self, force_update=False, ignore_update=False):
|
||||||
"""Update the device's state."""
|
"""Update the device's state."""
|
||||||
|
|
|
@ -20,6 +20,8 @@ from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
LOGGER,
|
LOGGER,
|
||||||
NEW_DEVICE,
|
NEW_DEVICE,
|
||||||
|
NEW_GROUP,
|
||||||
|
NEW_SENSOR,
|
||||||
SUPPORTED_PLATFORMS,
|
SUPPORTED_PLATFORMS,
|
||||||
)
|
)
|
||||||
from .errors import AuthenticationRequired, CannotConnect
|
from .errors import AuthenticationRequired, CannotConnect
|
||||||
|
@ -45,6 +47,9 @@ class DeconzGateway:
|
||||||
self.events = []
|
self.events = []
|
||||||
self.listeners = []
|
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
|
@property
|
||||||
def bridgeid(self) -> str:
|
def bridgeid(self) -> str:
|
||||||
"""Return the unique identifier of the gateway."""
|
"""Return the unique identifier of the gateway."""
|
||||||
|
@ -108,22 +113,64 @@ class DeconzGateway:
|
||||||
|
|
||||||
self.api.start()
|
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
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def async_new_address(hass, entry) -> None:
|
async def async_config_entry_updated(hass, entry) -> None:
|
||||||
"""Handle signals of gateway getting new address.
|
"""Handle signals of config entry being updated.
|
||||||
|
|
||||||
This is a static method because a class method (bound method),
|
This is a static method because a class method (bound method), can not be used with weak references.
|
||||||
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)
|
gateway = get_gateway_from_config_entry(hass, entry)
|
||||||
if gateway.api.host != entry.data[CONF_HOST]:
|
if gateway.api.host != entry.data[CONF_HOST]:
|
||||||
gateway.api.close()
|
gateway.api.close()
|
||||||
gateway.api.host = entry.data[CONF_HOST]
|
gateway.api.host = entry.data[CONF_HOST]
|
||||||
gateway.api.start()
|
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
|
@property
|
||||||
def signal_reachable(self) -> str:
|
def signal_reachable(self) -> str:
|
||||||
|
@ -141,6 +188,11 @@ class DeconzGateway:
|
||||||
"""Gateway specific event to signal new device."""
|
"""Gateway specific event to signal new device."""
|
||||||
return NEW_DEVICE[device_type].format(self.bridgeid)
|
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
|
@callback
|
||||||
def async_add_device_callback(self, device_type, device) -> None:
|
def async_add_device_callback(self, device_type, device) -> None:
|
||||||
"""Handle event of new device creation in deCONZ."""
|
"""Handle event of new device creation in deCONZ."""
|
||||||
|
|
|
@ -67,7 +67,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
for group in groups:
|
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))
|
entities.append(DeconzGroup(group, gateway))
|
||||||
|
|
||||||
async_add_entities(entities, True)
|
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
|
gateway.option_allow_clip_sensor
|
||||||
or not sensor.type.startswith("CLIP")
|
or not sensor.type.startswith("CLIP")
|
||||||
)
|
)
|
||||||
|
and sensor.deconz_id not in gateway.deconz_ids.values()
|
||||||
):
|
):
|
||||||
entities.append(DeconzSensor(sensor, gateway))
|
entities.append(DeconzSensor(sensor, gateway))
|
||||||
|
|
||||||
|
|
|
@ -14,20 +14,9 @@
|
||||||
"title": "Link with deCONZ",
|
"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"
|
"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": {
|
"hassio_confirm": {
|
||||||
"title": "deCONZ Zigbee gateway via Hass.io add-on",
|
"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}?",
|
"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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
@ -45,11 +34,12 @@
|
||||||
"options": {
|
"options": {
|
||||||
"step": {
|
"step": {
|
||||||
"deconz_devices": {
|
"deconz_devices": {
|
||||||
"description": "Configure visibility of deCONZ device types",
|
|
||||||
"data": {
|
"data": {
|
||||||
"allow_clip_sensor": "Allow deCONZ CLIP sensors",
|
"allow_clip_sensor": "Allow deCONZ CLIP sensors",
|
||||||
"allow_deconz_groups": "Allow deCONZ light groups"
|
"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"
|
"side_6": "Side 6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -134,6 +134,28 @@ async def test_allow_clip_sensor(hass):
|
||||||
vibration_sensor = hass.states.get("binary_sensor.vibration_sensor")
|
vibration_sensor = hass.states.get("binary_sensor.vibration_sensor")
|
||||||
assert vibration_sensor.state == "on"
|
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):
|
async def test_add_new_binary_sensor(hass):
|
||||||
"""Test that adding a new binary sensor works."""
|
"""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")
|
clip_thermostat = hass.states.get("climate.clip_thermostat")
|
||||||
assert clip_thermostat.state == "heat"
|
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):
|
async def test_verify_state_update(hass):
|
||||||
"""Test that state update properly."""
|
"""Test that state update properly."""
|
||||||
|
|
|
@ -245,3 +245,29 @@ async def test_disable_light_groups(hass):
|
||||||
|
|
||||||
empty_group = hass.states.get("light.empty_group")
|
empty_group = hass.states.get("light.empty_group")
|
||||||
assert empty_group is None
|
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")
|
clip_light_level_sensor = hass.states.get("sensor.clip_light_level_sensor")
|
||||||
assert clip_light_level_sensor.state == "999.8"
|
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):
|
async def test_add_new_sensor(hass):
|
||||||
"""Test that adding a new sensor works."""
|
"""Test that adding a new sensor works."""
|
||||||
|
|
Loading…
Reference in New Issue