"""Support for MelCloud device sensors.""" from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass 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.config_entries import ConfigEntry from homeassistant.const import ENERGY_KILO_WATT_HOUR, TEMP_CELSIUS from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import MelCloudDevice from .const import DOMAIN @dataclass class MelcloudRequiredKeysMixin: """Mixin for required keys.""" value_fn: Callable[[Any], float] enabled: Callable[[Any], bool] @dataclass class MelcloudSensorEntityDescription( SensorEntityDescription, MelcloudRequiredKeysMixin ): """Describes Melcloud sensor entity.""" ATA_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( MelcloudSensorEntityDescription( key="room_temperature", name="Room Temperature", icon="mdi:thermometer", native_unit_of_measurement=TEMP_CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda x: x.device.room_temperature, enabled=lambda x: True, ), MelcloudSensorEntityDescription( key="energy", name="Energy", icon="mdi:factory", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value_fn=lambda x: x.device.total_energy_consumed, enabled=lambda x: x.device.has_energy_consumed_meter, ), MelcloudSensorEntityDescription( key="daily_energy", name="Daily Energy Consumed", icon="mdi:factory", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value_fn=lambda x: x.device.daily_energy_consumed, enabled=lambda x: True, ), ) ATW_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( MelcloudSensorEntityDescription( key="outside_temperature", name="Outside Temperature", icon="mdi:thermometer", native_unit_of_measurement=TEMP_CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda x: x.device.outside_temperature, enabled=lambda x: True, ), MelcloudSensorEntityDescription( key="tank_temperature", name="Tank Temperature", icon="mdi:thermometer", native_unit_of_measurement=TEMP_CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda x: x.device.tank_temperature, enabled=lambda x: True, ), MelcloudSensorEntityDescription( key="daily_energy", name="Daily Energy Consumed", icon="mdi:factory", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value_fn=lambda x: x.device.daily_energy_consumed, enabled=lambda x: True, ), ) ATW_ZONE_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( MelcloudSensorEntityDescription( key="room_temperature", name="Room Temperature", icon="mdi:thermometer", native_unit_of_measurement=TEMP_CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda zone: zone.room_temperature, enabled=lambda x: True, ), MelcloudSensorEntityDescription( key="flow_temperature", name="Flow Temperature", icon="mdi:thermometer", native_unit_of_measurement=TEMP_CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda zone: zone.flow_temperature, enabled=lambda x: True, ), MelcloudSensorEntityDescription( key="return_temperature", name="Flow Return Temperature", icon="mdi:thermometer", native_unit_of_measurement=TEMP_CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda zone: zone.return_temperature, enabled=lambda x: True, ), ) async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up MELCloud device sensors based on config_entry.""" mel_devices = hass.data[DOMAIN].get(entry.entry_id) entities: list[MelDeviceSensor] = [ MelDeviceSensor(mel_device, description) for description in ATA_SENSORS for mel_device in mel_devices[DEVICE_TYPE_ATA] if description.enabled(mel_device) ] + [ MelDeviceSensor(mel_device, description) for description in ATW_SENSORS for mel_device in mel_devices[DEVICE_TYPE_ATW] if description.enabled(mel_device) ] entities.extend( [ AtwZoneSensor(mel_device, zone, description) for mel_device in mel_devices[DEVICE_TYPE_ATW] for zone in mel_device.device.zones for description in ATW_ZONE_SENSORS if description.enabled(zone) ] ) async_add_entities(entities, True) class MelDeviceSensor(SensorEntity): """Representation of a Sensor.""" entity_description: MelcloudSensorEntityDescription def __init__( self, api: MelCloudDevice, description: MelcloudSensorEntityDescription, ) -> None: """Initialize the sensor.""" self._api = api self.entity_description = description self._attr_name = f"{api.name} {description.name}" self._attr_unique_id = f"{api.device.serial}-{api.device.mac}-{description.key}" if description.device_class == SensorDeviceClass.ENERGY: self._attr_state_class = SensorStateClass.TOTAL_INCREASING else: self._attr_state_class = SensorStateClass.MEASUREMENT @property def native_value(self): """Return the state of the sensor.""" return self.entity_description.value_fn(self._api) async def async_update(self) -> None: """Retrieve latest state.""" await self._api.async_update() @property def device_info(self): """Return a device description for device registry.""" return self._api.device_info class AtwZoneSensor(MelDeviceSensor): """Air-to-Air device sensor.""" def __init__( self, api: MelCloudDevice, zone: Zone, description: MelcloudSensorEntityDescription, ) -> None: """Initialize the sensor.""" if zone.zone_index != 1: description.key = f"{description.key}-zone-{zone.zone_index}" super().__init__(api, description) self._zone = zone self._attr_name = f"{api.name} {zone.name} {description.name}" @property def native_value(self): """Return zone based state.""" return self.entity_description.value_fn(self._zone)