Improve tracking of existing entities in deconz (#40265)

* Store all entities in dict

* Use stored unique id to select if to create entity or not

* Remove unnecessary init

* Change so same physical sensor doesnt try to create multiple battery sensors
Change so groups get created properly

* Add controls in tests that entities are logged correctly
This commit is contained in:
Robert Svensson 2020-09-25 22:49:28 +02:00 committed by GitHub
parent e30acfbfee
commit 203c556ba3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 147 additions and 58 deletions

View file

@ -8,6 +8,7 @@ from homeassistant.components.binary_sensor import (
DEVICE_CLASS_OPENING, DEVICE_CLASS_OPENING,
DEVICE_CLASS_SMOKE, DEVICE_CLASS_SMOKE,
DEVICE_CLASS_VIBRATION, DEVICE_CLASS_VIBRATION,
DOMAIN,
BinarySensorEntity, BinarySensorEntity,
) )
from homeassistant.const import ATTR_TEMPERATURE from homeassistant.const import ATTR_TEMPERATURE
@ -39,17 +40,18 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the deCONZ binary sensor.""" """Set up the deCONZ binary sensor."""
gateway = get_gateway_from_config_entry(hass, config_entry) gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
@callback @callback
def async_add_sensor(sensors, new=True): def async_add_sensor(sensors):
"""Add binary sensor from deCONZ.""" """Add binary sensor from deCONZ."""
entities = [] entities = []
for sensor in sensors: for sensor in sensors:
if ( if (
new sensor.BINARY
and sensor.BINARY and sensor.uniqueid not in gateway.entities[DOMAIN]
and ( and (
gateway.option_allow_clip_sensor gateway.option_allow_clip_sensor
or not sensor.type.startswith("CLIP") or not sensor.type.startswith("CLIP")
@ -73,6 +75,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class DeconzBinarySensor(DeconzDevice, BinarySensorEntity): class DeconzBinarySensor(DeconzDevice, BinarySensorEntity):
"""Representation of a deCONZ binary sensor.""" """Representation of a deCONZ binary sensor."""
TYPE = DOMAIN
@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 sensor's state.""" """Update the sensor's state."""

View file

@ -1,7 +1,7 @@
"""Support for deCONZ climate devices.""" """Support for deCONZ climate devices."""
from pydeconz.sensor import Thermostat from pydeconz.sensor import Thermostat
from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate import DOMAIN, ClimateEntity
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
HVAC_MODE_AUTO, HVAC_MODE_AUTO,
HVAC_MODE_HEAT, HVAC_MODE_HEAT,
@ -29,17 +29,18 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
Thermostats are based on the same device class as sensors in deCONZ. Thermostats are based on the same device class as sensors in deCONZ.
""" """
gateway = get_gateway_from_config_entry(hass, config_entry) gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
@callback @callback
def async_add_climate(sensors, new=True): def async_add_climate(sensors):
"""Add climate devices from deCONZ.""" """Add climate devices from deCONZ."""
entities = [] entities = []
for sensor in sensors: for sensor in sensors:
if ( if (
new sensor.type in Thermostat.ZHATYPE
and sensor.type in Thermostat.ZHATYPE and sensor.uniqueid not in gateway.entities[DOMAIN]
and ( and (
gateway.option_allow_clip_sensor gateway.option_allow_clip_sensor
or not sensor.type.startswith("CLIP") or not sensor.type.startswith("CLIP")
@ -61,6 +62,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class DeconzThermostat(DeconzDevice, ClimateEntity): class DeconzThermostat(DeconzDevice, ClimateEntity):
"""Representation of a deCONZ thermostat.""" """Representation of a deCONZ thermostat."""
TYPE = DOMAIN
@property @property
def supported_features(self): def supported_features(self):
"""Return the list of supported features.""" """Return the list of supported features."""

View file

@ -2,6 +2,7 @@
from homeassistant.components.cover import ( from homeassistant.components.cover import (
ATTR_POSITION, ATTR_POSITION,
DEVICE_CLASS_WINDOW, DEVICE_CLASS_WINDOW,
DOMAIN,
SUPPORT_CLOSE, SUPPORT_CLOSE,
SUPPORT_OPEN, SUPPORT_OPEN,
SUPPORT_SET_POSITION, SUPPORT_SET_POSITION,
@ -23,9 +24,10 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up covers for deCONZ component. """Set up covers for deCONZ component.
Covers are based on same device class as lights in deCONZ. Covers are based on the same device class as lights in deCONZ.
""" """
gateway = get_gateway_from_config_entry(hass, config_entry) gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
@callback @callback
def async_add_cover(lights): def async_add_cover(lights):
@ -33,7 +35,10 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
entities = [] entities = []
for light in lights: for light in lights:
if light.type in COVER_TYPES: if (
light.type in COVER_TYPES
and light.uniqueid not in gateway.entities[DOMAIN]
):
entities.append(DeconzCover(light, gateway)) entities.append(DeconzCover(light, gateway))
async_add_entities(entities, True) async_add_entities(entities, True)
@ -50,6 +55,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class DeconzCover(DeconzDevice, CoverEntity): class DeconzCover(DeconzDevice, CoverEntity):
"""Representation of a deCONZ cover.""" """Representation of a deCONZ cover."""
TYPE = DOMAIN
def __init__(self, device, gateway): def __init__(self, device, gateway):
"""Set up cover device.""" """Set up cover device."""
super().__init__(device, gateway) super().__init__(device, gateway)

View file

@ -10,11 +10,17 @@ from .const import DOMAIN as DECONZ_DOMAIN
class DeconzBase: class DeconzBase:
"""Common base for deconz entities and events.""" """Common base for deconz entities and events."""
TYPE = ""
def __init__(self, device, gateway): def __init__(self, device, gateway):
"""Set up device and add update callback to get data from websocket.""" """Set up device and add update callback to get data from websocket."""
self._device = device self._device = device
self.gateway = gateway self.gateway = gateway
self.listeners = [] self.gateway.entities[self.TYPE].add(self.unique_id)
async def async_will_remove_from_hass(self) -> None:
"""Remove unique id."""
self.gateway.entities[self.TYPE].remove(self.unique_id)
@property @property
def unique_id(self): def unique_id(self):
@ -51,12 +57,6 @@ class DeconzBase:
class DeconzDevice(DeconzBase, Entity): class DeconzDevice(DeconzBase, Entity):
"""Representation of a deCONZ device.""" """Representation of a deCONZ device."""
def __init__(self, device, gateway):
"""Set up device and add update callback to get data from websocket."""
super().__init__(device, gateway)
self.unsub_dispatcher = None
@property @property
def entity_registry_enabled_default(self): def entity_registry_enabled_default(self):
"""Return if the entity should be enabled when first added to the entity registry. """Return if the entity should be enabled when first added to the entity registry.
@ -72,7 +72,7 @@ class DeconzDevice(DeconzBase, Entity):
"""Subscribe to device events.""" """Subscribe to device events."""
self._device.register_callback(self.async_update_callback) self._device.register_callback(self.async_update_callback)
self.gateway.deconz_ids[self.entity_id] = self._device.deconz_id self.gateway.deconz_ids[self.entity_id] = self._device.deconz_id
self.listeners.append( self.async_on_remove(
async_dispatcher_connect( async_dispatcher_connect(
self.hass, self.gateway.signal_reachable, self.async_update_callback self.hass, self.gateway.signal_reachable, self.async_update_callback
) )
@ -83,8 +83,7 @@ class DeconzDevice(DeconzBase, Entity):
self._device.remove_callback(self.async_update_callback) self._device.remove_callback(self.async_update_callback)
if self.entity_id in self.gateway.deconz_ids: if self.entity_id in self.gateway.deconz_ids:
del self.gateway.deconz_ids[self.entity_id] del self.gateway.deconz_ids[self.entity_id]
for unsub_dispatcher in self.listeners: await super().async_will_remove_from_hass()
unsub_dispatcher()
@callback @callback
def async_update_callback(self, force_update=False, ignore_update=False): def async_update_callback(self, force_update=False, ignore_update=False):

View file

@ -11,19 +11,25 @@ from .deconz_device import DeconzBase
CONF_DECONZ_EVENT = "deconz_event" CONF_DECONZ_EVENT = "deconz_event"
EVENT = "Event"
async def async_setup_events(gateway) -> None: async def async_setup_events(gateway) -> None:
"""Set up the deCONZ events.""" """Set up the deCONZ events."""
gateway.entities[EVENT] = set()
@callback @callback
def async_add_sensor(sensors, new=True): def async_add_sensor(sensors):
"""Create DeconzEvent.""" """Create DeconzEvent."""
for sensor in sensors: for sensor in sensors:
if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"): if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"):
continue continue
if not new or sensor.type not in Switch.ZHATYPE: if (
sensor.type not in Switch.ZHATYPE
or sensor.uniqueid in gateway.entities[EVENT]
):
continue continue
new_event = DeconzEvent(sensor, gateway) new_event = DeconzEvent(sensor, gateway)
@ -44,7 +50,7 @@ async def async_setup_events(gateway) -> None:
async def async_unload_events(gateway) -> None: async def async_unload_events(gateway) -> None:
"""Unload all deCONZ events.""" """Unload all deCONZ events."""
for event in gateway.events: for event in gateway.events:
event.async_will_remove_from_hass() await event.async_will_remove_from_hass()
gateway.events.clear() gateway.events.clear()
@ -56,6 +62,8 @@ class DeconzEvent(DeconzBase):
instead of a sensor entity in hass. instead of a sensor entity in hass.
""" """
TYPE = EVENT
def __init__(self, device, gateway): def __init__(self, device, gateway):
"""Register callback that will be used for signals.""" """Register callback that will be used for signals."""
super().__init__(device, gateway) super().__init__(device, gateway)
@ -71,11 +79,10 @@ class DeconzEvent(DeconzBase):
"""Return Event device.""" """Return Event device."""
return self._device return self._device
@callback async def async_will_remove_from_hass(self) -> None:
def async_will_remove_from_hass(self) -> None:
"""Disconnect event object when removed.""" """Disconnect event object when removed."""
self._device.remove_callback(self.async_update_callback) self._device.remove_callback(self.async_update_callback)
self._device = None await super().async_will_remove_from_hass()
@callback @callback
def async_update_callback(self, force_update=False, ignore_update=False): def async_update_callback(self, force_update=False, ignore_update=False):

View file

@ -49,6 +49,8 @@ class DeconzGateway:
self.events = [] self.events = []
self.listeners = [] self.listeners = []
self.entities = {}
self._current_option_allow_clip_sensor = self.option_allow_clip_sensor self._current_option_allow_clip_sensor = self.option_allow_clip_sensor
self._current_option_allow_deconz_groups = self.option_allow_deconz_groups self._current_option_allow_deconz_groups = self.option_allow_deconz_groups

View file

@ -6,6 +6,7 @@ from homeassistant.components.light import (
ATTR_FLASH, ATTR_FLASH,
ATTR_HS_COLOR, ATTR_HS_COLOR,
ATTR_TRANSITION, ATTR_TRANSITION,
DOMAIN,
EFFECT_COLORLOOP, EFFECT_COLORLOOP,
FLASH_LONG, FLASH_LONG,
FLASH_SHORT, FLASH_SHORT,
@ -40,6 +41,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the deCONZ lights and groups from a config entry.""" """Set up the deCONZ lights and groups from a config entry."""
gateway = get_gateway_from_config_entry(hass, config_entry) gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
@callback @callback
def async_add_light(lights): def async_add_light(lights):
@ -47,7 +49,10 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
entities = [] entities = []
for light in lights: for light in lights:
if light.type not in COVER_TYPES + SWITCH_TYPES: if (
light.type not in COVER_TYPES + SWITCH_TYPES
and light.uniqueid not in gateway.entities[DOMAIN]
):
entities.append(DeconzLight(light, gateway)) entities.append(DeconzLight(light, gateway))
async_add_entities(entities, True) async_add_entities(entities, True)
@ -67,8 +72,13 @@ 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 not group.lights:
entities.append(DeconzGroup(group, gateway)) continue
known_groups = list(gateway.entities[DOMAIN])
new_group = DeconzGroup(group, gateway)
if new_group.unique_id not in known_groups:
entities.append(new_group)
async_add_entities(entities, True) async_add_entities(entities, True)
@ -85,6 +95,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class DeconzBaseLight(DeconzDevice, LightEntity): class DeconzBaseLight(DeconzDevice, LightEntity):
"""Representation of a deCONZ light.""" """Representation of a deCONZ light."""
TYPE = DOMAIN
def __init__(self, device, gateway): def __init__(self, device, gateway):
"""Set up light.""" """Set up light."""
super().__init__(device, gateway) super().__init__(device, gateway)
@ -223,14 +235,13 @@ class DeconzGroup(DeconzBaseLight):
def __init__(self, device, gateway): def __init__(self, device, gateway):
"""Set up group and create an unique id.""" """Set up group and create an unique id."""
group_id_base = gateway.config_entry.unique_id
if CONF_GROUP_ID_BASE in gateway.config_entry.data:
group_id_base = gateway.config_entry.data[CONF_GROUP_ID_BASE]
self._unique_id = f"{group_id_base}-{device.deconz_id}"
super().__init__(device, gateway) super().__init__(device, gateway)
group_id_base = self.gateway.config_entry.unique_id
if CONF_GROUP_ID_BASE in self.gateway.config_entry.data:
group_id_base = self.gateway.config_entry.data[CONF_GROUP_ID_BASE]
self._unique_id = f"{group_id_base}-{self._device.deconz_id}"
@property @property
def unique_id(self): def unique_id(self):
"""Return a unique identifier for this device.""" """Return a unique identifier for this device."""

View file

@ -12,6 +12,7 @@ from pydeconz.sensor import (
Thermostat, Thermostat,
) )
from homeassistant.components.sensor import DOMAIN
from homeassistant.const import ( from homeassistant.const import (
ATTR_TEMPERATURE, ATTR_TEMPERATURE,
ATTR_VOLTAGE, ATTR_VOLTAGE,
@ -74,43 +75,43 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the deCONZ sensors.""" """Set up the deCONZ sensors."""
gateway = get_gateway_from_config_entry(hass, config_entry) gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
batteries = set()
battery_handler = DeconzBatteryHandler(gateway) battery_handler = DeconzBatteryHandler(gateway)
@callback @callback
def async_add_sensor(sensors, new=True): def async_add_sensor(sensors):
"""Add sensors from deCONZ. """Add sensors from deCONZ.
Create DeconzSensor if not a ZHAType and not a binary sensor.
Create DeconzBattery if sensor has a battery attribute. Create DeconzBattery if sensor has a battery attribute.
If new is false it means an existing sensor has got a battery state reported. Create DeconzSensor if not a battery, switch or thermostat and not a binary sensor.
""" """
entities = [] entities = []
for sensor in sensors: for sensor in sensors:
if ( if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"):
new continue
and sensor.BINARY is False
and sensor.type
not in Battery.ZHATYPE + Switch.ZHATYPE + Thermostat.ZHATYPE
and (
gateway.option_allow_clip_sensor
or not sensor.type.startswith("CLIP")
)
):
entities.append(DeconzSensor(sensor, gateway))
if sensor.battery is not None: if sensor.battery is not None:
battery_handler.remove_tracker(sensor)
known_batteries = list(gateway.entities[DOMAIN])
new_battery = DeconzBattery(sensor, gateway) new_battery = DeconzBattery(sensor, gateway)
if new_battery.unique_id not in batteries: if new_battery.unique_id not in known_batteries:
batteries.add(new_battery.unique_id)
entities.append(new_battery) entities.append(new_battery)
battery_handler.remove_tracker(sensor)
else: else:
battery_handler.create_tracker(sensor) battery_handler.create_tracker(sensor)
if (
not sensor.BINARY
and sensor.type
not in Battery.ZHATYPE + Switch.ZHATYPE + Thermostat.ZHATYPE
and sensor.uniqueid not in gateway.entities[DOMAIN]
):
entities.append(DeconzSensor(sensor, gateway))
async_add_entities(entities, True) async_add_entities(entities, True)
gateway.listeners.append( gateway.listeners.append(
@ -127,6 +128,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class DeconzSensor(DeconzDevice): class DeconzSensor(DeconzDevice):
"""Representation of a deCONZ sensor.""" """Representation of a deCONZ sensor."""
TYPE = DOMAIN
@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 sensor's state.""" """Update the sensor's state."""
@ -192,6 +195,8 @@ class DeconzSensor(DeconzDevice):
class DeconzBattery(DeconzDevice): class DeconzBattery(DeconzDevice):
"""Battery class for when a device is only represented as an event.""" """Battery class for when a device is only represented as an event."""
TYPE = DOMAIN
@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 battery's state, if needed.""" """Update the battery's state, if needed."""
@ -264,7 +269,6 @@ class DeconzSensorStateTracker:
self.gateway.hass, self.gateway.hass,
self.gateway.async_signal_new_device(NEW_SENSOR), self.gateway.async_signal_new_device(NEW_SENSOR),
[self.sensor], [self.sensor],
False,
) )

View file

@ -1,5 +1,5 @@
"""Support for deCONZ switches.""" """Support for deCONZ switches."""
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import DOMAIN, SwitchEntity
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -15,9 +15,10 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up switches for deCONZ component. """Set up switches for deCONZ component.
Switches are based same device class as lights in deCONZ. Switches are based on the same device class as lights in deCONZ.
""" """
gateway = get_gateway_from_config_entry(hass, config_entry) gateway = get_gateway_from_config_entry(hass, config_entry)
gateway.entities[DOMAIN] = set()
@callback @callback
def async_add_switch(lights): def async_add_switch(lights):
@ -26,10 +27,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
for light in lights: for light in lights:
if light.type in POWER_PLUGS: if (
light.type in POWER_PLUGS
and light.uniqueid not in gateway.entities[DOMAIN]
):
entities.append(DeconzPowerPlug(light, gateway)) entities.append(DeconzPowerPlug(light, gateway))
elif light.type in SIRENS: elif (
light.type in SIRENS and light.uniqueid not in gateway.entities[DOMAIN]
):
entities.append(DeconzSiren(light, gateway)) entities.append(DeconzSiren(light, gateway))
async_add_entities(entities, True) async_add_entities(entities, True)
@ -46,6 +52,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class DeconzPowerPlug(DeconzDevice, SwitchEntity): class DeconzPowerPlug(DeconzDevice, SwitchEntity):
"""Representation of a deCONZ power plug.""" """Representation of a deCONZ power plug."""
TYPE = DOMAIN
@property @property
def is_on(self): def is_on(self):
"""Return true if switch is on.""" """Return true if switch is on."""
@ -65,6 +73,8 @@ class DeconzPowerPlug(DeconzDevice, SwitchEntity):
class DeconzSiren(DeconzDevice, SwitchEntity): class DeconzSiren(DeconzDevice, SwitchEntity):
"""Representation of a deCONZ siren.""" """Representation of a deCONZ siren."""
TYPE = DOMAIN
@property @property
def is_on(self): def is_on(self):
"""Return true if switch is on.""" """Return true if switch is on."""

View file

@ -67,6 +67,7 @@ async def test_no_binary_sensors(hass):
"""Test that no sensors in deconz results in no sensor entities.""" """Test that no sensors in deconz results in no sensor entities."""
gateway = await setup_deconz_integration(hass) gateway = await setup_deconz_integration(hass)
assert len(gateway.deconz_ids) == 0 assert len(gateway.deconz_ids) == 0
assert len(gateway.entities[binary_sensor.DOMAIN]) == 0
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
@ -80,6 +81,7 @@ async def test_binary_sensors(hass):
assert "binary_sensor.clip_presence_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 "binary_sensor.vibration_sensor" in gateway.deconz_ids
assert len(hass.states.async_all()) == 3 assert len(hass.states.async_all()) == 3
assert len(gateway.entities[binary_sensor.DOMAIN]) == 2
presence_sensor = hass.states.get("binary_sensor.presence_sensor") presence_sensor = hass.states.get("binary_sensor.presence_sensor")
assert presence_sensor.state == "off" assert presence_sensor.state == "off"
@ -111,6 +113,7 @@ async def test_binary_sensors(hass):
await gateway.async_reset() await gateway.async_reset()
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
assert len(gateway.entities[binary_sensor.DOMAIN]) == 0
async def test_allow_clip_sensor(hass): async def test_allow_clip_sensor(hass):
@ -127,6 +130,7 @@ async def test_allow_clip_sensor(hass):
assert "binary_sensor.clip_presence_sensor" in gateway.deconz_ids assert "binary_sensor.clip_presence_sensor" in gateway.deconz_ids
assert "binary_sensor.vibration_sensor" in gateway.deconz_ids assert "binary_sensor.vibration_sensor" in gateway.deconz_ids
assert len(hass.states.async_all()) == 4 assert len(hass.states.async_all()) == 4
assert len(gateway.entities[binary_sensor.DOMAIN]) == 3
presence_sensor = hass.states.get("binary_sensor.presence_sensor") presence_sensor = hass.states.get("binary_sensor.presence_sensor")
assert presence_sensor.state == "off" assert presence_sensor.state == "off"
@ -150,6 +154,7 @@ async def test_allow_clip_sensor(hass):
assert "binary_sensor.clip_presence_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 "binary_sensor.vibration_sensor" in gateway.deconz_ids
assert len(hass.states.async_all()) == 3 assert len(hass.states.async_all()) == 3
assert len(gateway.entities[binary_sensor.DOMAIN]) == 2
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: True} gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: True}
@ -161,12 +166,14 @@ async def test_allow_clip_sensor(hass):
assert "binary_sensor.clip_presence_sensor" in gateway.deconz_ids assert "binary_sensor.clip_presence_sensor" in gateway.deconz_ids
assert "binary_sensor.vibration_sensor" in gateway.deconz_ids assert "binary_sensor.vibration_sensor" in gateway.deconz_ids
assert len(hass.states.async_all()) == 4 assert len(hass.states.async_all()) == 4
assert len(gateway.entities[binary_sensor.DOMAIN]) == 3
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."""
gateway = await setup_deconz_integration(hass) gateway = await setup_deconz_integration(hass)
assert len(gateway.deconz_ids) == 0 assert len(gateway.deconz_ids) == 0
assert len(gateway.entities[binary_sensor.DOMAIN]) == 0
state_added_event = { state_added_event = {
"t": "event", "t": "event",
@ -182,3 +189,4 @@ async def test_add_new_binary_sensor(hass):
presence_sensor = hass.states.get("binary_sensor.presence_sensor") presence_sensor = hass.states.get("binary_sensor.presence_sensor")
assert presence_sensor.state == "off" assert presence_sensor.state == "off"
assert len(gateway.entities[binary_sensor.DOMAIN]) == 1

View file

@ -59,6 +59,7 @@ async def test_no_sensors(hass):
gateway = await setup_deconz_integration(hass) gateway = await setup_deconz_integration(hass)
assert len(gateway.deconz_ids) == 0 assert len(gateway.deconz_ids) == 0
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
assert len(gateway.entities[climate.DOMAIN]) == 0
async def test_climate_devices(hass): async def test_climate_devices(hass):
@ -72,6 +73,7 @@ async def test_climate_devices(hass):
assert "climate.presence_sensor" not in gateway.deconz_ids assert "climate.presence_sensor" not in gateway.deconz_ids
assert "climate.clip_thermostat" not in gateway.deconz_ids assert "climate.clip_thermostat" not in gateway.deconz_ids
assert len(hass.states.async_all()) == 3 assert len(hass.states.async_all()) == 3
assert len(gateway.entities[climate.DOMAIN]) == 1
thermostat = hass.states.get("climate.thermostat") thermostat = hass.states.get("climate.thermostat")
assert thermostat.state == "auto" assert thermostat.state == "auto"
@ -181,6 +183,7 @@ async def test_climate_devices(hass):
await gateway.async_reset() await gateway.async_reset()
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
assert len(gateway.entities[climate.DOMAIN]) == 0
async def test_clip_climate_device(hass): async def test_clip_climate_device(hass):
@ -198,6 +201,7 @@ async def test_clip_climate_device(hass):
assert "climate.presence_sensor" not in gateway.deconz_ids assert "climate.presence_sensor" not in gateway.deconz_ids
assert "climate.clip_thermostat" in gateway.deconz_ids assert "climate.clip_thermostat" in gateway.deconz_ids
assert len(hass.states.async_all()) == 4 assert len(hass.states.async_all()) == 4
assert len(gateway.entities[climate.DOMAIN]) == 2
thermostat = hass.states.get("climate.thermostat") thermostat = hass.states.get("climate.thermostat")
assert thermostat.state == "auto" assert thermostat.state == "auto"
@ -225,6 +229,7 @@ async def test_clip_climate_device(hass):
assert "climate.presence_sensor" not in gateway.deconz_ids assert "climate.presence_sensor" not in gateway.deconz_ids
assert "climate.clip_thermostat" not in gateway.deconz_ids assert "climate.clip_thermostat" not in gateway.deconz_ids
assert len(hass.states.async_all()) == 3 assert len(hass.states.async_all()) == 3
assert len(gateway.entities[climate.DOMAIN]) == 1
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: True} gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: True}
@ -237,6 +242,7 @@ async def test_clip_climate_device(hass):
assert "climate.presence_sensor" not in gateway.deconz_ids assert "climate.presence_sensor" not in gateway.deconz_ids
assert "climate.clip_thermostat" in gateway.deconz_ids assert "climate.clip_thermostat" in gateway.deconz_ids
assert len(hass.states.async_all()) == 4 assert len(hass.states.async_all()) == 4
assert len(gateway.entities[climate.DOMAIN]) == 2
async def test_verify_state_update(hass): async def test_verify_state_update(hass):
@ -268,6 +274,7 @@ async def test_add_new_climate_device(hass):
"""Test that adding a new climate device works.""" """Test that adding a new climate device works."""
gateway = await setup_deconz_integration(hass) gateway = await setup_deconz_integration(hass)
assert len(gateway.deconz_ids) == 0 assert len(gateway.deconz_ids) == 0
assert len(gateway.entities[climate.DOMAIN]) == 0
state_added_event = { state_added_event = {
"t": "event", "t": "event",
@ -283,3 +290,4 @@ async def test_add_new_climate_device(hass):
thermostat = hass.states.get("climate.thermostat") thermostat = hass.states.get("climate.thermostat")
assert thermostat.state == "auto" assert thermostat.state == "auto"
assert len(gateway.entities[climate.DOMAIN]) == 1

View file

@ -67,6 +67,7 @@ async def test_no_covers(hass):
"""Test that no cover entities are created.""" """Test that no cover entities are created."""
gateway = await setup_deconz_integration(hass) gateway = await setup_deconz_integration(hass)
assert len(gateway.deconz_ids) == 0 assert len(gateway.deconz_ids) == 0
assert len(gateway.entities[cover.DOMAIN]) == 0
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
@ -81,6 +82,7 @@ async def test_cover(hass):
assert "cover.deconz_old_brightness_cover" in gateway.deconz_ids assert "cover.deconz_old_brightness_cover" in gateway.deconz_ids
assert "cover.window_covering_controller" in gateway.deconz_ids assert "cover.window_covering_controller" in gateway.deconz_ids
assert len(hass.states.async_all()) == 5 assert len(hass.states.async_all()) == 5
assert len(gateway.entities[cover.DOMAIN]) == 4
level_controllable_cover = hass.states.get("cover.level_controllable_cover") level_controllable_cover = hass.states.get("cover.level_controllable_cover")
assert level_controllable_cover.state == "open" assert level_controllable_cover.state == "open"
@ -158,3 +160,4 @@ async def test_cover(hass):
await gateway.async_reset() await gateway.async_reset()
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
assert len(gateway.entities[cover.DOMAIN]) == 0

View file

@ -1,7 +1,7 @@
"""Test deCONZ remote events.""" """Test deCONZ remote events."""
from copy import deepcopy from copy import deepcopy
from homeassistant.components.deconz.deconz_event import CONF_DECONZ_EVENT from homeassistant.components.deconz.deconz_event import CONF_DECONZ_EVENT, EVENT
from .test_gateway import DECONZ_WEB_REQUEST, setup_deconz_integration from .test_gateway import DECONZ_WEB_REQUEST, setup_deconz_integration
@ -62,6 +62,7 @@ async def test_deconz_events(hass):
assert "sensor.switch_2_battery_level" in gateway.deconz_ids assert "sensor.switch_2_battery_level" in gateway.deconz_ids
assert len(hass.states.async_all()) == 3 assert len(hass.states.async_all()) == 3
assert len(gateway.events) == 5 assert len(gateway.events) == 5
assert len(gateway.entities[EVENT]) == 5
switch_1 = hass.states.get("sensor.switch_1") switch_1 = hass.states.get("sensor.switch_1")
assert switch_1 is None assert switch_1 is None
@ -127,3 +128,4 @@ async def test_deconz_events(hass):
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
assert len(gateway.events) == 0 assert len(gateway.events) == 0
assert len(gateway.entities[EVENT]) == 0

View file

@ -95,6 +95,7 @@ async def test_no_lights_or_groups(hass):
gateway = await setup_deconz_integration(hass) gateway = await setup_deconz_integration(hass)
assert len(gateway.deconz_ids) == 0 assert len(gateway.deconz_ids) == 0
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
assert len(gateway.entities[light.DOMAIN]) == 0
async def test_lights_and_groups(hass): async def test_lights_and_groups(hass):
@ -111,6 +112,7 @@ async def test_lights_and_groups(hass):
assert "light.on_off_light" in gateway.deconz_ids assert "light.on_off_light" in gateway.deconz_ids
assert len(hass.states.async_all()) == 6 assert len(hass.states.async_all()) == 6
assert len(gateway.entities[light.DOMAIN]) == 5
rgb_light = hass.states.get("light.rgb_light") rgb_light = hass.states.get("light.rgb_light")
assert rgb_light.state == "on" assert rgb_light.state == "on"
@ -256,6 +258,7 @@ async def test_lights_and_groups(hass):
await gateway.async_reset() await gateway.async_reset()
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
assert len(gateway.entities[light.DOMAIN]) == 0
async def test_disable_light_groups(hass): async def test_disable_light_groups(hass):
@ -275,6 +278,7 @@ async def test_disable_light_groups(hass):
assert "light.on_off_switch" not in gateway.deconz_ids assert "light.on_off_switch" not in gateway.deconz_ids
# 3 entities # 3 entities
assert len(hass.states.async_all()) == 5 assert len(hass.states.async_all()) == 5
assert len(gateway.entities[light.DOMAIN]) == 4
rgb_light = hass.states.get("light.rgb_light") rgb_light = hass.states.get("light.rgb_light")
assert rgb_light is not None assert rgb_light is not None
@ -300,6 +304,7 @@ async def test_disable_light_groups(hass):
assert "light.on_off_switch" not in gateway.deconz_ids assert "light.on_off_switch" not in gateway.deconz_ids
# 3 entities # 3 entities
assert len(hass.states.async_all()) == 6 assert len(hass.states.async_all()) == 6
assert len(gateway.entities[light.DOMAIN]) == 5
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_DECONZ_GROUPS: False} gateway.config_entry, options={deconz.gateway.CONF_ALLOW_DECONZ_GROUPS: False}
@ -313,3 +318,4 @@ async def test_disable_light_groups(hass):
assert "light.on_off_switch" not in gateway.deconz_ids assert "light.on_off_switch" not in gateway.deconz_ids
# 3 entities # 3 entities
assert len(hass.states.async_all()) == 5 assert len(hass.states.async_all()) == 5
assert len(gateway.entities[light.DOMAIN]) == 4

View file

@ -2,6 +2,7 @@
from copy import deepcopy from copy import deepcopy
from homeassistant.components import deconz from homeassistant.components import deconz
from homeassistant.components.deconz.deconz_event import EVENT
import homeassistant.components.sensor as sensor import homeassistant.components.sensor as sensor
from homeassistant.const import ( from homeassistant.const import (
DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY,
@ -96,6 +97,7 @@ async def test_no_sensors(hass):
gateway = await setup_deconz_integration(hass) gateway = await setup_deconz_integration(hass)
assert len(gateway.deconz_ids) == 0 assert len(gateway.deconz_ids) == 0
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
assert len(gateway.entities[sensor.DOMAIN]) == 0
async def test_sensors(hass): async def test_sensors(hass):
@ -114,6 +116,7 @@ async def test_sensors(hass):
assert "sensor.consumption_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 "sensor.clip_light_level_sensor" not in gateway.deconz_ids
assert len(hass.states.async_all()) == 5 assert len(hass.states.async_all()) == 5
assert len(gateway.entities[sensor.DOMAIN]) == 5
light_level_sensor = hass.states.get("sensor.light_level_sensor") light_level_sensor = hass.states.get("sensor.light_level_sensor")
assert light_level_sensor.state == "999.8" assert light_level_sensor.state == "999.8"
@ -174,6 +177,8 @@ async def test_sensors(hass):
await gateway.async_reset() await gateway.async_reset()
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
# Daylight sensor from deCONZ is added to set but is disabled by default
assert len(gateway.entities[sensor.DOMAIN]) == 1
async def test_allow_clip_sensors(hass): async def test_allow_clip_sensors(hass):
@ -196,6 +201,7 @@ async def test_allow_clip_sensors(hass):
assert "sensor.consumption_sensor" in gateway.deconz_ids assert "sensor.consumption_sensor" in gateway.deconz_ids
assert "sensor.clip_light_level_sensor" in gateway.deconz_ids assert "sensor.clip_light_level_sensor" in gateway.deconz_ids
assert len(hass.states.async_all()) == 6 assert len(hass.states.async_all()) == 6
assert len(gateway.entities[sensor.DOMAIN]) == 6
light_level_sensor = hass.states.get("sensor.light_level_sensor") light_level_sensor = hass.states.get("sensor.light_level_sensor")
assert light_level_sensor.state == "999.8" assert light_level_sensor.state == "999.8"
@ -243,6 +249,7 @@ async def test_allow_clip_sensors(hass):
assert "sensor.consumption_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 "sensor.clip_light_level_sensor" not in gateway.deconz_ids
assert len(hass.states.async_all()) == 5 assert len(hass.states.async_all()) == 5
assert len(gateway.entities[sensor.DOMAIN]) == 5
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: True} gateway.config_entry, options={deconz.gateway.CONF_ALLOW_CLIP_SENSOR: True}
@ -260,6 +267,7 @@ async def test_allow_clip_sensors(hass):
assert "sensor.consumption_sensor" in gateway.deconz_ids assert "sensor.consumption_sensor" in gateway.deconz_ids
assert "sensor.clip_light_level_sensor" in gateway.deconz_ids assert "sensor.clip_light_level_sensor" in gateway.deconz_ids
assert len(hass.states.async_all()) == 6 assert len(hass.states.async_all()) == 6
assert len(gateway.entities[sensor.DOMAIN]) == 6
async def test_add_new_sensor(hass): async def test_add_new_sensor(hass):
@ -292,6 +300,8 @@ async def test_add_battery_later(hass):
assert len(gateway.deconz_ids) == 0 assert len(gateway.deconz_ids) == 0
assert len(gateway.events) == 1 assert len(gateway.events) == 1
assert len(remote._callbacks) == 2 assert len(remote._callbacks) == 2
assert len(gateway.entities[sensor.DOMAIN]) == 0
assert len(gateway.entities[EVENT]) == 1
remote.update({"config": {"battery": 50}}) remote.update({"config": {"battery": 50}})
await hass.async_block_till_done() await hass.async_block_till_done()
@ -302,3 +312,5 @@ async def test_add_battery_later(hass):
battery_sensor = hass.states.get("sensor.switch_1_battery_level") battery_sensor = hass.states.get("sensor.switch_1_battery_level")
assert battery_sensor is not None assert battery_sensor is not None
assert len(gateway.entities[sensor.DOMAIN]) == 1
assert len(gateway.entities[EVENT]) == 1

View file

@ -64,6 +64,7 @@ async def test_no_switches(hass):
gateway = await setup_deconz_integration(hass) gateway = await setup_deconz_integration(hass)
assert len(gateway.deconz_ids) == 0 assert len(gateway.deconz_ids) == 0
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
assert len(gateway.entities[switch.DOMAIN]) == 0
async def test_switches(hass): async def test_switches(hass):
@ -77,6 +78,7 @@ async def test_switches(hass):
assert "switch.unsupported_switch" not in gateway.deconz_ids assert "switch.unsupported_switch" not in gateway.deconz_ids
assert "switch.on_off_relay" in gateway.deconz_ids assert "switch.on_off_relay" in gateway.deconz_ids
assert len(hass.states.async_all()) == 5 assert len(hass.states.async_all()) == 5
assert len(gateway.entities[switch.DOMAIN]) == 4
on_off_switch = hass.states.get("switch.on_off_switch") on_off_switch = hass.states.get("switch.on_off_switch")
assert on_off_switch.state == "on" assert on_off_switch.state == "on"
@ -173,3 +175,4 @@ async def test_switches(hass):
await gateway.async_reset() await gateway.async_reset()
assert len(hass.states.async_all()) == 0 assert len(hass.states.async_all()) == 0
assert len(gateway.entities[switch.DOMAIN]) == 0