deCONZ - Support creating battery sensor when reported (#27538)

pull/28450/head
Robert Svensson 2019-11-01 22:31:22 +01:00 committed by GitHub
parent 62b09580c4
commit 557e585e56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 8 deletions

View File

@ -26,13 +26,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
entity_handler = DeconzEntityHandler(gateway)
@callback
def async_add_sensor(sensors):
def async_add_sensor(sensors, new=True):
"""Add binary sensor from deCONZ."""
entities = []
for sensor in sensors:
if sensor.BINARY:
if new and sensor.BINARY:
new_sensor = DeconzBinarySensor(sensor, gateway)
entity_handler.add_entity(new_sensor)
entities.append(new_sensor)

View File

@ -31,13 +31,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
gateway = get_gateway_from_config_entry(hass, config_entry)
@callback
def async_add_climate(sensors):
def async_add_climate(sensors, new=True):
"""Add climate devices from deCONZ."""
entities = []
for sensor in sensors:
if sensor.type in Thermostat.ZHATYPE:
if new and sensor.type in Thermostat.ZHATYPE:
entities.append(DeconzThermostat(sensor, gateway))
async_add_entities(entities, True)

View File

@ -3,7 +3,10 @@ from pydeconz.sensor import Consumption, Daylight, LightLevel, Power, Switch, Th
from homeassistant.const import ATTR_TEMPERATURE, ATTR_VOLTAGE, DEVICE_CLASS_BATTERY
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR
from .deconz_device import DeconzDevice
@ -25,21 +28,23 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
gateway = get_gateway_from_config_entry(hass, config_entry)
batteries = set()
battery_handler = DeconzBatteryHandler(gateway)
entity_handler = DeconzEntityHandler(gateway)
@callback
def async_add_sensor(sensors):
def async_add_sensor(sensors, new=True):
"""Add sensors from deCONZ.
Create DeconzEvent if part of ZHAType list.
Create DeconzSensor if not a ZHAType and not a binary sensor.
Create DeconzBattery if sensor has a battery attribute.
If new is false it means an existing sensor has got a battery state reported.
"""
entities = []
for sensor in sensors:
if sensor.type in Switch.ZHATYPE:
if new and sensor.type in Switch.ZHATYPE:
if gateway.option_allow_clip_sensor or not sensor.type.startswith(
"CLIP"
@ -48,7 +53,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
hass.async_create_task(new_event.async_update_device_registry())
gateway.events.append(new_event)
elif not sensor.BINARY and sensor.type not in Thermostat.ZHATYPE:
elif new and not sensor.BINARY and sensor.type not in Thermostat.ZHATYPE:
new_sensor = DeconzSensor(sensor, gateway)
entity_handler.add_entity(new_sensor)
@ -59,6 +64,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
if new_battery.unique_id not in batteries:
batteries.add(new_battery.unique_id)
entities.append(new_battery)
battery_handler.remove_tracker(sensor)
else:
battery_handler.create_tracker(sensor)
async_add_entities(entities, True)
@ -176,3 +184,54 @@ class DeconzBattery(DeconzDevice):
attr[ATTR_EVENT_ID] = event.event_id
return attr
class DeconzSensorStateTracker:
"""Track sensors without a battery state and signal when battery state exist."""
def __init__(self, sensor, gateway):
"""Set up tracker."""
self.sensor = sensor
self.gateway = gateway
sensor.register_async_callback(self.async_update_callback)
@callback
def close(self):
"""Clean up tracker."""
self.sensor.remove_callback(self.async_update_callback)
self.gateway = None
self.sensor = None
@callback
def async_update_callback(self):
"""Sensor state updated."""
if "battery" in self.sensor.changed_keys:
async_dispatcher_send(
self.gateway.hass,
self.gateway.async_signal_new_device(NEW_SENSOR),
[self.sensor],
False,
)
class DeconzBatteryHandler:
"""Creates and stores trackers for sensors without a battery state."""
def __init__(self, gateway):
"""Set up battery handler."""
self.gateway = gateway
self._trackers = set()
@callback
def create_tracker(self, sensor):
"""Create new tracker for battery state."""
self._trackers.add(DeconzSensorStateTracker(sensor, self.gateway))
@callback
def remove_tracker(self, sensor):
"""Remove tracker of battery state."""
for tracker in self._trackers:
if sensor == tracker.sensor:
tracker.close()
self._trackers.remove(tracker)
break

View File

@ -233,3 +233,26 @@ async def test_add_new_sensor(hass):
light_level_sensor = hass.states.get("sensor.light_level_sensor")
assert light_level_sensor.state == "999.8"
async def test_add_battery_later(hass):
"""Test that a sensor without an initial battery state creates a battery sensor once state exist."""
data = deepcopy(DECONZ_WEB_REQUEST)
data["sensors"] = {"1": deepcopy(SENSORS["3"])}
gateway = await setup_deconz_integration(
hass, ENTRY_CONFIG, options={}, get_state_response=data
)
remote = gateway.api.sensors["1"]
assert len(gateway.deconz_ids) == 0
assert len(gateway.events) == 1
assert len(remote._async_callbacks) == 2
remote.async_update({"config": {"battery": 50}})
await hass.async_block_till_done()
assert len(gateway.deconz_ids) == 1
assert len(gateway.events) == 1
assert len(remote._async_callbacks) == 2
battery_sensor = hass.states.get("sensor.switch_1_battery_level")
assert battery_sensor is not None