core/homeassistant/components/homee/sensor.py

304 lines
10 KiB
Python

"""The homee sensor platform."""
from collections.abc import Callable
from dataclasses import dataclass
from pyHomee.const import AttributeType, NodeState
from pyHomee.model import HomeeAttribute, HomeeNode
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import HomeeConfigEntry
from .const import (
HOMEE_UNIT_TO_HA_UNIT,
OPEN_CLOSE_MAP,
OPEN_CLOSE_MAP_REVERSED,
WINDOW_MAP,
WINDOW_MAP_REVERSED,
)
from .entity import HomeeEntity, HomeeNodeEntity
from .helpers import get_name_for_enum
def get_open_close_value(attribute: HomeeAttribute) -> str | None:
"""Return the open/close value."""
vals = OPEN_CLOSE_MAP if not attribute.is_reversed else OPEN_CLOSE_MAP_REVERSED
return vals.get(attribute.current_value)
def get_window_value(attribute: HomeeAttribute) -> str | None:
"""Return the states of a window open sensor."""
vals = WINDOW_MAP if not attribute.is_reversed else WINDOW_MAP_REVERSED
return vals.get(attribute.current_value)
@dataclass(frozen=True, kw_only=True)
class HomeeSensorEntityDescription(SensorEntityDescription):
"""A class that describes Homee sensor entities."""
value_fn: Callable[[HomeeAttribute], str | float | None] = (
lambda value: value.current_value
)
native_unit_of_measurement_fn: Callable[[str], str | None] = (
lambda homee_unit: HOMEE_UNIT_TO_HA_UNIT[homee_unit]
)
SENSOR_DESCRIPTIONS: dict[AttributeType, HomeeSensorEntityDescription] = {
AttributeType.ACCUMULATED_ENERGY_USE: HomeeSensorEntityDescription(
key="energy",
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
AttributeType.BATTERY_LEVEL: HomeeSensorEntityDescription(
key="battery",
device_class=SensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.BRIGHTNESS: HomeeSensorEntityDescription(
key="brightness",
device_class=SensorDeviceClass.ILLUMINANCE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=(
lambda attribute: attribute.current_value * 1000
if attribute.unit == "klx"
else attribute.current_value
),
),
AttributeType.CURRENT: HomeeSensorEntityDescription(
key="current",
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.CURRENT_ENERGY_USE: HomeeSensorEntityDescription(
key="power",
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.CURRENT_VALVE_POSITION: HomeeSensorEntityDescription(
key="valve_position",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.DAWN: HomeeSensorEntityDescription(
key="dawn",
device_class=SensorDeviceClass.ILLUMINANCE,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.DEVICE_TEMPERATURE: HomeeSensorEntityDescription(
key="device_temperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.LEVEL: HomeeSensorEntityDescription(
key="level",
device_class=SensorDeviceClass.VOLUME,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.LINK_QUALITY: HomeeSensorEntityDescription(
key="link_quality",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.POSITION: HomeeSensorEntityDescription(
key="position",
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.RAIN_FALL_LAST_HOUR: HomeeSensorEntityDescription(
key="rainfall_hour",
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.RAIN_FALL_TODAY: HomeeSensorEntityDescription(
key="rainfall_day",
device_class=SensorDeviceClass.PRECIPITATION,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.RELATIVE_HUMIDITY: HomeeSensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.TEMPERATURE: HomeeSensorEntityDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.TOTAL_ACCUMULATED_ENERGY_USE: HomeeSensorEntityDescription(
key="total_energy",
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
AttributeType.TOTAL_CURRENT: HomeeSensorEntityDescription(
key="total_current",
device_class=SensorDeviceClass.CURRENT,
),
AttributeType.TOTAL_CURRENT_ENERGY_USE: HomeeSensorEntityDescription(
key="total_power",
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.TOTAL_VOLTAGE: HomeeSensorEntityDescription(
key="total_voltage",
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.UP_DOWN: HomeeSensorEntityDescription(
key="up_down",
device_class=SensorDeviceClass.ENUM,
options=[
"open",
"closed",
"partial",
"opening",
"closing",
],
value_fn=get_open_close_value,
),
AttributeType.UV: HomeeSensorEntityDescription(
key="uv",
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.VOLTAGE: HomeeSensorEntityDescription(
key="voltage",
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.WIND_SPEED: HomeeSensorEntityDescription(
key="wind_speed",
device_class=SensorDeviceClass.WIND_SPEED,
state_class=SensorStateClass.MEASUREMENT,
),
AttributeType.WINDOW_POSITION: HomeeSensorEntityDescription(
key="window_position",
device_class=SensorDeviceClass.ENUM,
options=["closed", "open", "tilted"],
value_fn=get_window_value,
),
}
@dataclass(frozen=True, kw_only=True)
class HomeeNodeSensorEntityDescription(SensorEntityDescription):
"""Describes Homee node sensor entities."""
value_fn: Callable[[HomeeNode], str | None]
NODE_SENSOR_DESCRIPTIONS: tuple[HomeeNodeSensorEntityDescription, ...] = (
HomeeNodeSensorEntityDescription(
key="state",
device_class=SensorDeviceClass.ENUM,
options=[
"available",
"unavailable",
"update_in_progress",
"waiting_for_attributes",
"initializing",
"user_interaction_required",
"password_required",
"host_unavailable",
"delete_in_progress",
"cosi_connected",
"blocked",
"waiting_for_wakeup",
"remote_node_deleted",
"firmware_update_in_progress",
],
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
value_fn=lambda node: get_name_for_enum(NodeState, node.state),
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: HomeeConfigEntry,
async_add_devices: AddEntitiesCallback,
) -> None:
"""Add the homee platform for the sensor components."""
devices: list[HomeeSensor | HomeeNodeSensor] = []
for node in config_entry.runtime_data.nodes:
# Node properties that are sensors.
devices.extend(
HomeeNodeSensor(node, config_entry, description)
for description in NODE_SENSOR_DESCRIPTIONS
)
# Node attributes that are sensors.
devices.extend(
HomeeSensor(attribute, config_entry, SENSOR_DESCRIPTIONS[attribute.type])
for attribute in node.attributes
if attribute.type in SENSOR_DESCRIPTIONS and not attribute.editable
)
if devices:
async_add_devices(devices)
class HomeeSensor(HomeeEntity, SensorEntity):
"""Representation of a homee sensor."""
entity_description: HomeeSensorEntityDescription
def __init__(
self,
attribute: HomeeAttribute,
entry: HomeeConfigEntry,
description: HomeeSensorEntityDescription,
) -> None:
"""Initialize a homee sensor entity."""
super().__init__(attribute, entry)
self.entity_description = description
self._attr_translation_key = description.key
if attribute.instance > 0:
self._attr_translation_key = f"{description.translation_key}_instance"
self._attr_translation_placeholders = {"instance": str(attribute.instance)}
@property
def native_value(self) -> float | str | None:
"""Return the native value of the sensor."""
return self.entity_description.value_fn(self._attribute)
@property
def native_unit_of_measurement(self) -> str | None:
"""Return the native unit of the sensor."""
return self.entity_description.native_unit_of_measurement_fn(
self._attribute.unit
)
class HomeeNodeSensor(HomeeNodeEntity, SensorEntity):
"""Represents a sensor based on a node's property."""
entity_description: HomeeNodeSensorEntityDescription
def __init__(
self,
node: HomeeNode,
entry: HomeeConfigEntry,
description: HomeeNodeSensorEntityDescription,
) -> None:
"""Initialize a homee node sensor entity."""
super().__init__(node, entry)
self.entity_description = description
self._attr_translation_key = f"node_{description.key}"
self._node = node
self._attr_unique_id = f"{self._attr_unique_id}-{description.key}"
@property
def native_value(self) -> str | None:
"""Return the sensors value."""
return self.entity_description.value_fn(self._node)