Files

116 lines
3.8 KiB
Python

"""Support for iZone sensors."""
from collections.abc import Callable
from dataclasses import dataclass
from operator import attrgetter
from typing import override
from pizone import Controller, Power, PowerChannel, PowerDevice, PowerGroup, Zone
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import EntityCategory, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from .const import DOMAIN
from .coordinator import IZoneConfigEntry, IZoneCoordinator
from .entity import IZoneCoordinatorEntity
PARALLEL_UPDATES = 0
type IZoneSensorSource = (
Controller | Zone | Power | PowerDevice | PowerChannel | PowerGroup
)
@dataclass(frozen=True, kw_only=True)
class IZoneSensorEntityDescription[SourceT: IZoneSensorSource](SensorEntityDescription):
"""Describes an iZone sensor; value_fn reads from a pizone source object."""
value_fn: Callable[[SourceT], StateType]
CONTROLLER_SENSOR_DESCRIPTIONS: tuple[IZoneSensorEntityDescription[Controller], ...] = (
IZoneSensorEntityDescription[Controller](
key="supply_temperature",
translation_key="supply_temperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=attrgetter("temp_supply"),
),
IZoneSensorEntityDescription[Controller](
key="return_temperature",
translation_key="return_temperature",
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=attrgetter("temp_return"),
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: IZoneConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up iZone sensor entities from the entry coordinator."""
coordinator = entry.runtime_data
controller = coordinator.controller
async_add_entities(
IZoneSensor(
coordinator,
description,
controller,
unique_id=f"{controller.device_uid}_{description.key}",
device_info=DeviceInfo(
identifiers={(DOMAIN, controller.device_uid)},
manufacturer="IZone",
model=controller.sys_type,
name=f"iZone Controller {controller.device_uid}",
),
)
for description in CONTROLLER_SENSOR_DESCRIPTIONS
)
class IZoneSensor[SourceT: IZoneSensorSource](IZoneCoordinatorEntity, SensorEntity):
"""Sensor backed by a pizone Controller, Zone, or Power* object."""
_attr_has_entity_name = True
entity_description: IZoneSensorEntityDescription[SourceT]
def __init__(
self,
coordinator: IZoneCoordinator,
description: IZoneSensorEntityDescription[SourceT],
source: SourceT,
*,
unique_id: str,
device_info: DeviceInfo,
) -> None:
"""Initialize the sensor for *source*."""
super().__init__(coordinator)
self.entity_description = description
self._source = source
self._attr_unique_id = unique_id
self._attr_device_info = device_info
@property
@override
def native_value(self) -> StateType:
"""Return the sensor value from the pizone source object."""
return self.entity_description.value_fn(self._source)