Files
core/homeassistant/components/melcloud/sensor.py
divers33 58ef925a07 Refactor MELCloud integration to use DataUpdateCoordinator (#160131)
Co-authored-by: divers33 <divers33@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-01-13 18:52:37 +01:00

194 lines
6.6 KiB
Python

"""Support for MelCloud device sensors."""
from __future__ import annotations
from collections.abc import Callable
import dataclasses
from typing import Any
from pymelcloud import DEVICE_TYPE_ATA, DEVICE_TYPE_ATW
from pymelcloud.atw_device import Zone
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import UnitOfEnergy, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import MelCloudConfigEntry, MelCloudDeviceUpdateCoordinator
from .entity import MelCloudEntity
@dataclasses.dataclass(frozen=True, kw_only=True)
class MelcloudSensorEntityDescription(SensorEntityDescription):
"""Describes Melcloud sensor entity."""
value_fn: Callable[[Any], float]
enabled: Callable[[Any], bool]
ATA_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = (
MelcloudSensorEntityDescription(
key="room_temperature",
translation_key="room_temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda x: x.device.room_temperature,
enabled=lambda x: True,
),
MelcloudSensorEntityDescription(
key="energy",
translation_key="energy_consumed",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda x: x.device.total_energy_consumed,
enabled=lambda x: x.device.has_energy_consumed_meter,
),
MelcloudSensorEntityDescription(
key="outside_temperature",
translation_key="outside_temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda x: x.device.outdoor_temperature,
enabled=lambda x: x.device.has_outdoor_temperature,
),
)
ATW_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = (
MelcloudSensorEntityDescription(
key="outside_temperature",
translation_key="outside_temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda x: x.device.outside_temperature,
enabled=lambda x: True,
),
MelcloudSensorEntityDescription(
key="tank_temperature",
translation_key="tank_temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda x: x.device.tank_temperature,
enabled=lambda x: True,
),
)
ATW_ZONE_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = (
MelcloudSensorEntityDescription(
key="room_temperature",
translation_key="room_temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda zone: zone.room_temperature,
enabled=lambda x: True,
),
MelcloudSensorEntityDescription(
key="flow_temperature",
translation_key="flow_temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda zone: zone.flow_temperature,
enabled=lambda x: True,
),
MelcloudSensorEntityDescription(
key="return_temperature",
translation_key="return_temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda zone: zone.return_temperature,
enabled=lambda x: True,
),
)
async def async_setup_entry(
_hass: HomeAssistant,
entry: MelCloudConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up MELCloud device sensors based on config_entry."""
coordinators = entry.runtime_data
entities: list[MelDeviceSensor] = [
MelDeviceSensor(coordinator, description)
for description in ATA_SENSORS
for coordinator in coordinators.get(DEVICE_TYPE_ATA, [])
if description.enabled(coordinator)
] + [
MelDeviceSensor(coordinator, description)
for description in ATW_SENSORS
for coordinator in coordinators.get(DEVICE_TYPE_ATW, [])
if description.enabled(coordinator)
]
entities.extend(
[
AtwZoneSensor(coordinator, zone, description)
for coordinator in coordinators.get(DEVICE_TYPE_ATW, [])
for zone in coordinator.device.zones
for description in ATW_ZONE_SENSORS
if description.enabled(zone)
]
)
async_add_entities(entities)
class MelDeviceSensor(MelCloudEntity, SensorEntity):
"""Representation of a Sensor."""
entity_description: MelcloudSensorEntityDescription
def __init__(
self,
coordinator: MelCloudDeviceUpdateCoordinator,
description: MelcloudSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = (
f"{coordinator.device.serial}-{coordinator.device.mac}-{description.key}"
)
self._attr_device_info = coordinator.device_info
@property
def native_value(self) -> float | None:
"""Return the state of the sensor."""
return self.entity_description.value_fn(self.coordinator)
class AtwZoneSensor(MelDeviceSensor):
"""Air-to-Water zone sensor."""
def __init__(
self,
coordinator: MelCloudDeviceUpdateCoordinator,
zone: Zone,
description: MelcloudSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
if zone.zone_index != 1:
description = dataclasses.replace(
description,
key=f"{description.key}-zone-{zone.zone_index}",
)
super().__init__(coordinator, description)
self._attr_device_info = coordinator.zone_device_info(zone)
self._zone = zone
@property
def native_value(self) -> float | None:
"""Return zone based state."""
return self.entity_description.value_fn(self._zone)