Add typing to deCONZ Number and Sensor platforms (#59604)
This commit is contained in:
parent
29e0ef604e
commit
0339761e72
2 changed files with 84 additions and 44 deletions
|
@ -2,6 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import ValuesView
|
||||
from dataclasses import dataclass
|
||||
|
||||
from pydeconz.sensor import PRESENCE_DELAY, Presence
|
||||
|
@ -11,25 +12,35 @@ from homeassistant.components.number import (
|
|||
NumberEntity,
|
||||
NumberEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ENTITY_CATEGORY_CONFIG
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .deconz_device import DeconzDevice
|
||||
from .gateway import get_gateway_from_config_entry
|
||||
from .gateway import DeconzGateway, get_gateway_from_config_entry
|
||||
|
||||
|
||||
@dataclass
|
||||
class DeconzNumberEntityDescription(NumberEntityDescription):
|
||||
class DeconzNumberEntityDescriptionBase:
|
||||
"""Required values when describing deCONZ number entities."""
|
||||
|
||||
device_property: str
|
||||
suffix: str
|
||||
update_key: str
|
||||
max_value: int
|
||||
min_value: int
|
||||
step: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class DeconzNumberEntityDescription(
|
||||
NumberEntityDescription, DeconzNumberEntityDescriptionBase
|
||||
):
|
||||
"""Class describing deCONZ number entities."""
|
||||
|
||||
entity_category = ENTITY_CATEGORY_CONFIG
|
||||
device_property: str | None = None
|
||||
suffix: str | None = None
|
||||
update_key: str | None = None
|
||||
max_value: int | None = None
|
||||
min_value: int | None = None
|
||||
step: int | None = None
|
||||
|
||||
|
||||
ENTITY_DESCRIPTIONS = {
|
||||
|
@ -47,13 +58,19 @@ ENTITY_DESCRIPTIONS = {
|
|||
}
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the deCONZ number entity."""
|
||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||
gateway.entities[DOMAIN] = set()
|
||||
|
||||
@callback
|
||||
def async_add_sensor(sensors=gateway.api.sensors.values()):
|
||||
def async_add_sensor(
|
||||
sensors: list[Presence] | ValuesView[Presence] = gateway.api.sensors.values(),
|
||||
) -> None:
|
||||
"""Add number config sensor from deCONZ."""
|
||||
entities = []
|
||||
|
||||
|
@ -92,13 +109,19 @@ class DeconzNumber(DeconzDevice, NumberEntity):
|
|||
"""Representation of a deCONZ number entity."""
|
||||
|
||||
TYPE = DOMAIN
|
||||
_device: Presence
|
||||
|
||||
def __init__(self, device, gateway, description):
|
||||
def __init__(
|
||||
self,
|
||||
device: Presence,
|
||||
gateway: DeconzGateway,
|
||||
description: DeconzNumberEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize deCONZ number entity."""
|
||||
self.entity_description = description
|
||||
self.entity_description: DeconzNumberEntityDescription = description
|
||||
super().__init__(device, gateway)
|
||||
|
||||
self._attr_name = f"{self._device.name} {description.suffix}"
|
||||
self._attr_name = f"{device.name} {description.suffix}"
|
||||
self._attr_max_value = description.max_value
|
||||
self._attr_min_value = description.min_value
|
||||
self._attr_step = description.step
|
||||
|
@ -113,7 +136,7 @@ class DeconzNumber(DeconzDevice, NumberEntity):
|
|||
@property
|
||||
def value(self) -> float:
|
||||
"""Return the value of the sensor property."""
|
||||
return getattr(self._device, self.entity_description.device_property)
|
||||
return getattr(self._device, self.entity_description.device_property) # type: ignore[no-any-return]
|
||||
|
||||
async def async_set_value(self, value: float) -> None:
|
||||
"""Set sensor config."""
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
"""Support for deCONZ sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import ValuesView
|
||||
|
||||
from pydeconz.sensor import (
|
||||
AirQuality,
|
||||
Battery,
|
||||
Consumption,
|
||||
Daylight,
|
||||
DeconzSensor as PydeconzSensor,
|
||||
GenericStatus,
|
||||
Humidity,
|
||||
LightLevel,
|
||||
|
@ -22,6 +27,7 @@ from homeassistant.components.sensor import (
|
|||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_TEMPERATURE,
|
||||
ATTR_VOLTAGE,
|
||||
|
@ -40,15 +46,17 @@ from homeassistant.const import (
|
|||
PRESSURE_HPA,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from .const import ATTR_DARK, ATTR_ON
|
||||
from .deconz_device import DeconzDevice
|
||||
from .gateway import get_gateway_from_config_entry
|
||||
from .gateway import DeconzGateway, get_gateway_from_config_entry
|
||||
|
||||
DECONZ_SENSORS = (
|
||||
AirQuality,
|
||||
|
@ -119,7 +127,11 @@ ENTITY_DESCRIPTIONS = {
|
|||
}
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the deCONZ sensors."""
|
||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||
gateway.entities[DOMAIN] = set()
|
||||
|
@ -127,13 +139,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
battery_handler = DeconzBatteryHandler(gateway)
|
||||
|
||||
@callback
|
||||
def async_add_sensor(sensors=gateway.api.sensors.values()):
|
||||
def async_add_sensor(
|
||||
sensors: list[PydeconzSensor]
|
||||
| ValuesView[PydeconzSensor] = gateway.api.sensors.values(),
|
||||
) -> None:
|
||||
"""Add sensors from deCONZ.
|
||||
|
||||
Create DeconzBattery if sensor has a battery attribute.
|
||||
Create DeconzSensor if not a battery, switch or thermostat and not a binary sensor.
|
||||
"""
|
||||
entities = []
|
||||
entities: list[DeconzBattery | DeconzSensor | DeconzTemperature] = []
|
||||
|
||||
for sensor in sensors:
|
||||
|
||||
|
@ -184,8 +199,9 @@ class DeconzSensor(DeconzDevice, SensorEntity):
|
|||
"""Representation of a deCONZ sensor."""
|
||||
|
||||
TYPE = DOMAIN
|
||||
_device: PydeconzSensor
|
||||
|
||||
def __init__(self, device, gateway):
|
||||
def __init__(self, device: PydeconzSensor, gateway: DeconzGateway) -> None:
|
||||
"""Initialize deCONZ binary sensor."""
|
||||
super().__init__(device, gateway)
|
||||
|
||||
|
@ -193,19 +209,19 @@ class DeconzSensor(DeconzDevice, SensorEntity):
|
|||
self.entity_description = entity_description
|
||||
|
||||
@callback
|
||||
def async_update_callback(self):
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the sensor's state."""
|
||||
keys = {"on", "reachable", "state"}
|
||||
if self._device.changed_keys.intersection(keys):
|
||||
super().async_update_callback()
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state of the sensor."""
|
||||
return self._device.state
|
||||
return self._device.state # type: ignore[no-any-return]
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
def extra_state_attributes(self) -> dict[str, bool | float | int | None]:
|
||||
"""Return the state attributes of the sensor."""
|
||||
attr = {}
|
||||
|
||||
|
@ -243,8 +259,9 @@ class DeconzTemperature(DeconzDevice, SensorEntity):
|
|||
"""
|
||||
|
||||
TYPE = DOMAIN
|
||||
_device: PydeconzSensor
|
||||
|
||||
def __init__(self, device, gateway):
|
||||
def __init__(self, device: PydeconzSensor, gateway: DeconzGateway) -> None:
|
||||
"""Initialize deCONZ temperature sensor."""
|
||||
super().__init__(device, gateway)
|
||||
|
||||
|
@ -252,29 +269,30 @@ class DeconzTemperature(DeconzDevice, SensorEntity):
|
|||
self._attr_name = f"{self._device.name} Temperature"
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique identifier for this device."""
|
||||
return f"{self.serial}-temperature"
|
||||
|
||||
@callback
|
||||
def async_update_callback(self):
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the sensor's state."""
|
||||
keys = {"temperature", "reachable"}
|
||||
if self._device.changed_keys.intersection(keys):
|
||||
super().async_update_callback()
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state of the sensor."""
|
||||
return self._device.secondary_temperature
|
||||
return self._device.secondary_temperature # type: ignore[no-any-return]
|
||||
|
||||
|
||||
class DeconzBattery(DeconzDevice, SensorEntity):
|
||||
"""Battery class for when a device is only represented as an event."""
|
||||
|
||||
TYPE = DOMAIN
|
||||
_device: PydeconzSensor
|
||||
|
||||
def __init__(self, device, gateway):
|
||||
def __init__(self, device: PydeconzSensor, gateway: DeconzGateway) -> None:
|
||||
"""Initialize deCONZ battery level sensor."""
|
||||
super().__init__(device, gateway)
|
||||
|
||||
|
@ -282,14 +300,14 @@ class DeconzBattery(DeconzDevice, SensorEntity):
|
|||
self._attr_name = f"{self._device.name} Battery Level"
|
||||
|
||||
@callback
|
||||
def async_update_callback(self):
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the battery's state, if needed."""
|
||||
keys = {"battery", "reachable"}
|
||||
if self._device.changed_keys.intersection(keys):
|
||||
super().async_update_callback()
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique identifier for this device.
|
||||
|
||||
Normally there should only be one battery sensor per device from deCONZ.
|
||||
|
@ -305,12 +323,12 @@ class DeconzBattery(DeconzDevice, SensorEntity):
|
|||
return f"{self.serial}-battery"
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state of the battery."""
|
||||
return self._device.battery
|
||||
return self._device.battery # type: ignore[no-any-return]
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
def extra_state_attributes(self) -> dict[str, str]:
|
||||
"""Return the state attributes of the battery."""
|
||||
attr = {}
|
||||
|
||||
|
@ -325,21 +343,20 @@ class DeconzBattery(DeconzDevice, SensorEntity):
|
|||
class DeconzSensorStateTracker:
|
||||
"""Track sensors without a battery state and signal when battery state exist."""
|
||||
|
||||
def __init__(self, sensor, gateway):
|
||||
def __init__(self, sensor: PydeconzSensor, gateway: DeconzGateway) -> None:
|
||||
"""Set up tracker."""
|
||||
self.sensor = sensor
|
||||
self.gateway = gateway
|
||||
sensor.register_callback(self.async_update_callback)
|
||||
|
||||
@callback
|
||||
def close(self):
|
||||
def close(self) -> None:
|
||||
"""Clean up tracker."""
|
||||
self.sensor.remove_callback(self.async_update_callback)
|
||||
self.gateway = None
|
||||
self.sensor = None
|
||||
|
||||
@callback
|
||||
def async_update_callback(self):
|
||||
def async_update_callback(self) -> None:
|
||||
"""Sensor state updated."""
|
||||
if "battery" in self.sensor.changed_keys:
|
||||
async_dispatcher_send(
|
||||
|
@ -352,13 +369,13 @@ class DeconzSensorStateTracker:
|
|||
class DeconzBatteryHandler:
|
||||
"""Creates and stores trackers for sensors without a battery state."""
|
||||
|
||||
def __init__(self, gateway):
|
||||
def __init__(self, gateway: DeconzGateway) -> None:
|
||||
"""Set up battery handler."""
|
||||
self.gateway = gateway
|
||||
self._trackers = set()
|
||||
self._trackers: set[DeconzSensorStateTracker] = set()
|
||||
|
||||
@callback
|
||||
def create_tracker(self, sensor):
|
||||
def create_tracker(self, sensor: PydeconzSensor) -> None:
|
||||
"""Create new tracker for battery state."""
|
||||
for tracker in self._trackers:
|
||||
if sensor == tracker.sensor:
|
||||
|
@ -366,7 +383,7 @@ class DeconzBatteryHandler:
|
|||
self._trackers.add(DeconzSensorStateTracker(sensor, self.gateway))
|
||||
|
||||
@callback
|
||||
def remove_tracker(self, sensor):
|
||||
def remove_tracker(self, sensor: PydeconzSensor) -> None:
|
||||
"""Remove tracker of battery state."""
|
||||
for tracker in self._trackers:
|
||||
if sensor == tracker.sensor:
|
||||
|
|
Loading…
Add table
Reference in a new issue