Streamline setup of deCONZ sensor platform (#71905)
This commit is contained in:
parent
82d4d96672
commit
cc7a0e3c24
3 changed files with 49 additions and 134 deletions
|
@ -9,12 +9,7 @@ from typing import TYPE_CHECKING, Any, cast
|
|||
|
||||
import async_timeout
|
||||
from pydeconz import DeconzSession, errors
|
||||
from pydeconz.models import ResourceGroup
|
||||
from pydeconz.models.alarm_system import AlarmSystem as DeconzAlarmSystem
|
||||
from pydeconz.models.event import EventType
|
||||
from pydeconz.models.group import Group as DeconzGroup
|
||||
from pydeconz.models.light import LightBase as DeconzLight
|
||||
from pydeconz.models.sensor import SensorBase as DeconzSensor
|
||||
|
||||
from homeassistant.config_entries import SOURCE_HASSIO, ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
|
||||
|
@ -57,7 +52,6 @@ class DeconzGateway:
|
|||
self.config_entry = config_entry
|
||||
self.api = api
|
||||
|
||||
api.add_device_callback = self.async_add_device_callback
|
||||
api.connection_status_callback = self.async_connection_status_callback
|
||||
|
||||
self.available = True
|
||||
|
@ -67,14 +61,6 @@ class DeconzGateway:
|
|||
self.signal_reload_groups = f"deconz_reload_group_{config_entry.entry_id}"
|
||||
self.signal_reload_clip_sensors = f"deconz_reload_clip_{config_entry.entry_id}"
|
||||
|
||||
self.signal_new_light = f"deconz_new_light_{config_entry.entry_id}"
|
||||
self.signal_new_sensor = f"deconz_new_sensor_{config_entry.entry_id}"
|
||||
|
||||
self.deconz_resource_type_to_signal_new_device = {
|
||||
ResourceGroup.LIGHT.value: self.signal_new_light,
|
||||
ResourceGroup.SENSOR.value: self.signal_new_sensor,
|
||||
}
|
||||
|
||||
self.deconz_ids: dict[str, str] = {}
|
||||
self.entities: dict[str, set[str]] = {}
|
||||
self.events: list[DeconzAlarmEvent | DeconzEvent] = []
|
||||
|
@ -153,37 +139,6 @@ class DeconzGateway:
|
|||
self.ignore_state_updates = False
|
||||
async_dispatcher_send(self.hass, self.signal_reachable)
|
||||
|
||||
@callback
|
||||
def async_add_device_callback(
|
||||
self,
|
||||
resource_type: str,
|
||||
device: DeconzAlarmSystem
|
||||
| DeconzGroup
|
||||
| DeconzLight
|
||||
| DeconzSensor
|
||||
| list[DeconzAlarmSystem | DeconzGroup | DeconzLight | DeconzSensor]
|
||||
| None = None,
|
||||
force: bool = False,
|
||||
) -> None:
|
||||
"""Handle event of new device creation in deCONZ."""
|
||||
if (
|
||||
not force
|
||||
and not self.option_allow_new_devices
|
||||
or resource_type not in self.deconz_resource_type_to_signal_new_device
|
||||
):
|
||||
return
|
||||
|
||||
args = []
|
||||
|
||||
if device is not None and not isinstance(device, list):
|
||||
args.append([device])
|
||||
|
||||
async_dispatcher_send(
|
||||
self.hass,
|
||||
self.deconz_resource_type_to_signal_new_device[resource_type],
|
||||
*args, # Don't send device if None, it would override default value in listeners
|
||||
)
|
||||
|
||||
async def async_update_device_registry(self) -> None:
|
||||
"""Update device registry."""
|
||||
if self.api.config.mac is None:
|
||||
|
@ -237,7 +192,6 @@ class DeconzGateway:
|
|||
deconz_ids = []
|
||||
|
||||
if self.option_allow_clip_sensor:
|
||||
self.async_add_device_callback(ResourceGroup.SENSOR.value)
|
||||
async_dispatcher_send(self.hass, self.signal_reload_clip_sensors)
|
||||
|
||||
else:
|
||||
|
@ -314,6 +268,7 @@ async def get_deconz_session(
|
|||
config[CONF_HOST],
|
||||
config[CONF_PORT],
|
||||
config[CONF_API_KEY],
|
||||
legacy_add_device=False,
|
||||
)
|
||||
try:
|
||||
async with async_timeout.timeout(10):
|
||||
|
|
|
@ -6,6 +6,7 @@ from dataclasses import dataclass
|
|||
from datetime import datetime
|
||||
|
||||
from pydeconz.interfaces.sensors import SensorResources
|
||||
from pydeconz.models.event import EventType
|
||||
from pydeconz.models.sensor.air_quality import AirQuality
|
||||
from pydeconz.models.sensor.consumption import Consumption
|
||||
from pydeconz.models.sensor.daylight import Daylight
|
||||
|
@ -38,10 +39,7 @@ from homeassistant.const import (
|
|||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
@ -244,61 +242,52 @@ async def async_setup_entry(
|
|||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||
gateway.entities[DOMAIN] = set()
|
||||
|
||||
battery_handler = DeconzBatteryHandler(gateway)
|
||||
|
||||
@callback
|
||||
def async_add_sensor(sensors: list[SensorResources] | None = None) -> None:
|
||||
"""Add sensors from deCONZ.
|
||||
def async_add_sensor(_: EventType, sensor_id: str) -> None:
|
||||
"""Add sensor from deCONZ."""
|
||||
sensor = gateway.api.sensors[sensor_id]
|
||||
|
||||
Create DeconzBattery if sensor has a battery attribute.
|
||||
Create DeconzSensor if not a battery, switch or thermostat and not a binary sensor.
|
||||
"""
|
||||
entities: list[DeconzSensor] = []
|
||||
if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"):
|
||||
return
|
||||
|
||||
if sensors is None:
|
||||
sensors = gateway.api.sensors.values()
|
||||
if sensor.battery is None:
|
||||
DeconzBatteryTracker(sensor_id, gateway, async_add_entities)
|
||||
|
||||
for sensor in sensors:
|
||||
|
||||
if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"):
|
||||
for description in (
|
||||
ENTITY_DESCRIPTIONS.get(type(sensor), []) + SENSOR_DESCRIPTIONS
|
||||
):
|
||||
if (
|
||||
not hasattr(sensor, description.key)
|
||||
or description.value_fn(sensor) is None
|
||||
):
|
||||
continue
|
||||
|
||||
if sensor.battery is None:
|
||||
battery_handler.create_tracker(sensor)
|
||||
async_add_entities([DeconzSensor(sensor, gateway, description)])
|
||||
|
||||
known_entities = set(gateway.entities[DOMAIN])
|
||||
for description in (
|
||||
ENTITY_DESCRIPTIONS.get(type(sensor), []) + SENSOR_DESCRIPTIONS
|
||||
):
|
||||
config_entry.async_on_unload(
|
||||
gateway.api.sensors.subscribe(
|
||||
gateway.evaluate_add_device(async_add_sensor),
|
||||
EventType.ADDED,
|
||||
)
|
||||
)
|
||||
for sensor_id in gateway.api.sensors:
|
||||
async_add_sensor(EventType.ADDED, sensor_id)
|
||||
|
||||
if (
|
||||
not hasattr(sensor, description.key)
|
||||
or description.value_fn(sensor) is None
|
||||
):
|
||||
continue
|
||||
|
||||
new_entity = DeconzSensor(sensor, gateway, description)
|
||||
if new_entity.unique_id not in known_entities:
|
||||
entities.append(new_entity)
|
||||
|
||||
if description.key == "battery":
|
||||
battery_handler.remove_tracker(sensor)
|
||||
|
||||
if entities:
|
||||
async_add_entities(entities)
|
||||
@callback
|
||||
def async_reload_clip_sensors() -> None:
|
||||
"""Load clip sensor sensors from deCONZ."""
|
||||
for sensor_id, sensor in gateway.api.sensors.items():
|
||||
if sensor.type.startswith("CLIP"):
|
||||
async_add_sensor(EventType.ADDED, sensor_id)
|
||||
|
||||
config_entry.async_on_unload(
|
||||
async_dispatcher_connect(
|
||||
hass,
|
||||
gateway.signal_new_sensor,
|
||||
async_add_sensor,
|
||||
gateway.signal_reload_clip_sensors,
|
||||
async_reload_clip_sensors,
|
||||
)
|
||||
)
|
||||
|
||||
async_add_sensor(
|
||||
[gateway.api.sensors[key] for key in sorted(gateway.api.sensors, key=int)]
|
||||
)
|
||||
|
||||
|
||||
class DeconzSensor(DeconzDevice, SensorEntity):
|
||||
"""Representation of a deCONZ sensor."""
|
||||
|
@ -398,52 +387,26 @@ class DeconzSensor(DeconzDevice, SensorEntity):
|
|||
return attr
|
||||
|
||||
|
||||
class DeconzSensorStateTracker:
|
||||
"""Track sensors without a battery state and signal when battery state exist."""
|
||||
class DeconzBatteryTracker:
|
||||
"""Track sensors without a battery state and add entity when battery state exist."""
|
||||
|
||||
def __init__(self, sensor: SensorResources, gateway: DeconzGateway) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
sensor_id: str,
|
||||
gateway: DeconzGateway,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up tracker."""
|
||||
self.sensor = sensor
|
||||
self.sensor = gateway.api.sensors[sensor_id]
|
||||
self.gateway = gateway
|
||||
sensor.register_callback(self.async_update_callback)
|
||||
|
||||
@callback
|
||||
def close(self) -> None:
|
||||
"""Clean up tracker."""
|
||||
self.sensor.remove_callback(self.async_update_callback)
|
||||
self.async_add_entities = async_add_entities
|
||||
self.unsub = self.sensor.subscribe(self.async_update_callback)
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Sensor state updated."""
|
||||
"""Update the device's state."""
|
||||
if "battery" in self.sensor.changed_keys:
|
||||
async_dispatcher_send(
|
||||
self.gateway.hass,
|
||||
self.gateway.signal_new_sensor,
|
||||
[self.sensor],
|
||||
self.unsub()
|
||||
self.async_add_entities(
|
||||
[DeconzSensor(self.sensor, self.gateway, SENSOR_DESCRIPTIONS[0])]
|
||||
)
|
||||
|
||||
|
||||
class DeconzBatteryHandler:
|
||||
"""Creates and stores trackers for sensors without a battery state."""
|
||||
|
||||
def __init__(self, gateway: DeconzGateway) -> None:
|
||||
"""Set up battery handler."""
|
||||
self.gateway = gateway
|
||||
self._trackers: set[DeconzSensorStateTracker] = set()
|
||||
|
||||
@callback
|
||||
def create_tracker(self, sensor: SensorResources) -> None:
|
||||
"""Create new tracker for battery state."""
|
||||
for tracker in self._trackers:
|
||||
if sensor == tracker.sensor:
|
||||
return
|
||||
self._trackers.add(DeconzSensorStateTracker(sensor, self.gateway))
|
||||
|
||||
@callback
|
||||
def remove_tracker(self, sensor: SensorResources) -> None:
|
||||
"""Remove tracker of battery state."""
|
||||
for tracker in self._trackers:
|
||||
if sensor == tracker.sensor:
|
||||
tracker.close()
|
||||
self._trackers.remove(tracker)
|
||||
break
|
||||
|
|
|
@ -147,9 +147,6 @@ async def async_refresh_devices_service(gateway: DeconzGateway) -> None:
|
|||
gateway.load_ignored_devices()
|
||||
gateway.ignore_state_updates = False
|
||||
|
||||
for resource_type in gateway.deconz_resource_type_to_signal_new_device:
|
||||
gateway.async_add_device_callback(resource_type, force=True)
|
||||
|
||||
|
||||
async def async_remove_orphaned_entries_service(gateway: DeconzGateway) -> None:
|
||||
"""Remove orphaned deCONZ entries from device and entity registries."""
|
||||
|
|
Loading…
Add table
Reference in a new issue