mirror of https://github.com/home-assistant/core
304 lines
10 KiB
Python
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)
|