mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
294 lines
11 KiB
Python
294 lines
11 KiB
Python
"""Number platform for MELCloud Home."""
|
|
|
|
from collections.abc import Callable, Coroutine
|
|
from dataclasses import dataclass
|
|
from typing import Any, override
|
|
|
|
from aiomelcloudhome import ATAUnit, ATWUnit, MELCloudHome
|
|
|
|
from homeassistant.components.number import (
|
|
NumberDeviceClass,
|
|
NumberEntity,
|
|
NumberEntityDescription,
|
|
)
|
|
from homeassistant.const import EntityCategory, UnitOfTemperature
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
|
|
from .common import async_setup_unit_entities, perform_action, unit_ids
|
|
from .const import DOMAIN
|
|
from .coordinator import MelCloudHomeConfigEntry, MelCloudHomeCoordinator
|
|
from .entity import MelCloudHomeATAUnitEntity, MelCloudHomeATWUnitEntity
|
|
|
|
PARALLEL_UPDATES = 1
|
|
|
|
|
|
@dataclass(frozen=True, kw_only=True)
|
|
class MelCloudHomeNumberEntityDescription[_UnitT: ATAUnit | ATWUnit](
|
|
NumberEntityDescription
|
|
):
|
|
"""Class to hold MELCloud Home number description."""
|
|
|
|
available_fn: Callable[[_UnitT], bool]
|
|
value_fn: Callable[[_UnitT], float | None]
|
|
set_value_fn: Callable[[MELCloudHome, _UnitT, float], Coroutine[Any, Any, None]]
|
|
validate_fn: Callable[[_UnitT, float], str | None] | None = None
|
|
|
|
|
|
def _number_descriptions[_UnitT: ATAUnit | ATWUnit](
|
|
unit_type: type[_UnitT],
|
|
*,
|
|
overheat_min_temp: float,
|
|
overheat_max_temp: float,
|
|
) -> tuple[MelCloudHomeNumberEntityDescription[_UnitT], ...]:
|
|
"""Return the number descriptions for a unit type."""
|
|
return (
|
|
MelCloudHomeNumberEntityDescription(
|
|
key="frost_protection_min_temp",
|
|
translation_key="frost_protection_min_temp",
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
|
entity_category=EntityCategory.CONFIG,
|
|
native_min_value=0.0,
|
|
native_max_value=30.0,
|
|
native_step=0.5,
|
|
available_fn=lambda unit: (
|
|
unit.frost_protection is not None and unit.frost_protection.enabled
|
|
),
|
|
value_fn=lambda unit: (
|
|
unit.frost_protection.min if unit.frost_protection else None
|
|
),
|
|
set_value_fn=lambda client, unit, value: client.set_frost_protection(
|
|
enabled=unit.frost_protection.enabled
|
|
if unit.frost_protection
|
|
else False,
|
|
min_temp=value,
|
|
max_temp=unit.frost_protection.max if unit.frost_protection else 0.0,
|
|
**unit_ids(unit),
|
|
),
|
|
validate_fn=lambda unit, value: (
|
|
"temperature_min_exceeds_max"
|
|
if unit.frost_protection and value >= unit.frost_protection.max
|
|
else None
|
|
),
|
|
),
|
|
MelCloudHomeNumberEntityDescription(
|
|
key="frost_protection_max_temp",
|
|
translation_key="frost_protection_max_temp",
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
|
entity_category=EntityCategory.CONFIG,
|
|
native_min_value=0.0,
|
|
native_max_value=30.0,
|
|
native_step=0.5,
|
|
available_fn=lambda unit: (
|
|
unit.frost_protection is not None and unit.frost_protection.enabled
|
|
),
|
|
value_fn=lambda unit: (
|
|
unit.frost_protection.max if unit.frost_protection else None
|
|
),
|
|
set_value_fn=lambda client, unit, value: client.set_frost_protection(
|
|
enabled=unit.frost_protection.enabled
|
|
if unit.frost_protection
|
|
else False,
|
|
min_temp=unit.frost_protection.min if unit.frost_protection else 0.0,
|
|
max_temp=value,
|
|
**unit_ids(unit),
|
|
),
|
|
validate_fn=lambda unit, value: (
|
|
"temperature_max_below_min"
|
|
if unit.frost_protection and value <= unit.frost_protection.min
|
|
else None
|
|
),
|
|
),
|
|
MelCloudHomeNumberEntityDescription(
|
|
key="overheat_protection_min_temp",
|
|
translation_key="overheat_protection_min_temp",
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
|
entity_category=EntityCategory.CONFIG,
|
|
native_min_value=overheat_min_temp,
|
|
native_max_value=overheat_max_temp,
|
|
native_step=0.5,
|
|
available_fn=lambda unit: (
|
|
unit.overheat_protection is not None
|
|
and unit.overheat_protection.enabled
|
|
),
|
|
value_fn=lambda unit: (
|
|
unit.overheat_protection.min if unit.overheat_protection else None
|
|
),
|
|
set_value_fn=lambda client, unit, value: client.set_overheat_protection(
|
|
enabled=unit.overheat_protection.enabled
|
|
if unit.overheat_protection
|
|
else False,
|
|
min_temp=value,
|
|
max_temp=unit.overheat_protection.max
|
|
if unit.overheat_protection
|
|
else 0.0,
|
|
**unit_ids(unit),
|
|
),
|
|
validate_fn=lambda unit, value: (
|
|
"temperature_min_exceeds_max"
|
|
if unit.overheat_protection and value >= unit.overheat_protection.max
|
|
else None
|
|
),
|
|
),
|
|
MelCloudHomeNumberEntityDescription(
|
|
key="overheat_protection_max_temp",
|
|
translation_key="overheat_protection_max_temp",
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
|
entity_category=EntityCategory.CONFIG,
|
|
native_min_value=overheat_min_temp,
|
|
native_max_value=overheat_max_temp,
|
|
native_step=0.5,
|
|
available_fn=lambda unit: (
|
|
unit.overheat_protection is not None
|
|
and unit.overheat_protection.enabled
|
|
),
|
|
value_fn=lambda unit: (
|
|
unit.overheat_protection.max if unit.overheat_protection else None
|
|
),
|
|
set_value_fn=lambda client, unit, value: client.set_overheat_protection(
|
|
enabled=unit.overheat_protection.enabled
|
|
if unit.overheat_protection
|
|
else False,
|
|
min_temp=unit.overheat_protection.min
|
|
if unit.overheat_protection
|
|
else 0.0,
|
|
max_temp=value,
|
|
**unit_ids(unit),
|
|
),
|
|
validate_fn=lambda unit, value: (
|
|
"temperature_max_below_min"
|
|
if unit.overheat_protection and value <= unit.overheat_protection.min
|
|
else None
|
|
),
|
|
),
|
|
)
|
|
|
|
|
|
ATA_NUMBERS: tuple[MelCloudHomeNumberEntityDescription[ATAUnit], ...] = (
|
|
_number_descriptions(ATAUnit, overheat_min_temp=31.0, overheat_max_temp=40.0)
|
|
)
|
|
ATW_NUMBERS: tuple[MelCloudHomeNumberEntityDescription[ATWUnit], ...] = (
|
|
_number_descriptions(ATWUnit, overheat_min_temp=20.0, overheat_max_temp=60.0)
|
|
)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: MelCloudHomeConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
"""Set up MELCloud Home numbers."""
|
|
coordinator = entry.runtime_data.coordinator
|
|
|
|
async_setup_unit_entities(
|
|
coordinator,
|
|
async_add_entities,
|
|
lambda units: (
|
|
ATANumber(coordinator, entity_description, unit)
|
|
for entity_description in ATA_NUMBERS
|
|
for unit in units
|
|
),
|
|
lambda units: (
|
|
ATWNumber(coordinator, entity_description, unit)
|
|
for entity_description in ATW_NUMBERS
|
|
for unit in units
|
|
),
|
|
)
|
|
|
|
|
|
class ATANumber(MelCloudHomeATAUnitEntity, NumberEntity):
|
|
"""Representation of a MELCloud Home ATA number."""
|
|
|
|
entity_description: MelCloudHomeNumberEntityDescription[ATAUnit]
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: MelCloudHomeCoordinator,
|
|
entity_description: MelCloudHomeNumberEntityDescription[ATAUnit],
|
|
unit: ATAUnit,
|
|
) -> None:
|
|
"""Initialize the entity."""
|
|
super().__init__(coordinator, unit)
|
|
self.entity_description = entity_description
|
|
self._attr_unique_id = f"{unit.id}_{entity_description.key}"
|
|
|
|
@property
|
|
@override
|
|
def available(self) -> bool:
|
|
"""Return if the entity is available."""
|
|
return super().available and self.entity_description.available_fn(self.unit)
|
|
|
|
@property
|
|
@override
|
|
def native_value(self) -> float | None:
|
|
"""Return the current value."""
|
|
return self.entity_description.value_fn(self.unit)
|
|
|
|
@override
|
|
async def async_set_native_value(self, value: float) -> None:
|
|
"""Set the protection temperature threshold."""
|
|
if self.entity_description.validate_fn and (
|
|
error_key := self.entity_description.validate_fn(self.unit, value)
|
|
):
|
|
raise HomeAssistantError(
|
|
translation_domain=DOMAIN,
|
|
translation_key=error_key,
|
|
)
|
|
await perform_action(
|
|
self.coordinator,
|
|
self.entity_description.set_value_fn(
|
|
self.coordinator.client, self.unit, value
|
|
),
|
|
)
|
|
|
|
|
|
class ATWNumber(MelCloudHomeATWUnitEntity, NumberEntity):
|
|
"""Representation of a MELCloud Home ATW number."""
|
|
|
|
entity_description: MelCloudHomeNumberEntityDescription[ATWUnit]
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: MelCloudHomeCoordinator,
|
|
entity_description: MelCloudHomeNumberEntityDescription[ATWUnit],
|
|
unit: ATWUnit,
|
|
) -> None:
|
|
"""Initialize the entity."""
|
|
super().__init__(coordinator, unit)
|
|
self.entity_description = entity_description
|
|
self._attr_unique_id = f"{unit.id}_{entity_description.key}"
|
|
|
|
@property
|
|
@override
|
|
def available(self) -> bool:
|
|
"""Return if the entity is available."""
|
|
return super().available and self.entity_description.available_fn(self.unit)
|
|
|
|
@property
|
|
@override
|
|
def native_value(self) -> float | None:
|
|
"""Return the current value."""
|
|
return self.entity_description.value_fn(self.unit)
|
|
|
|
@override
|
|
async def async_set_native_value(self, value: float) -> None:
|
|
"""Set the protection temperature threshold."""
|
|
if self.entity_description.validate_fn and (
|
|
error_key := self.entity_description.validate_fn(self.unit, value)
|
|
):
|
|
raise HomeAssistantError(
|
|
translation_domain=DOMAIN,
|
|
translation_key=error_key,
|
|
)
|
|
await perform_action(
|
|
self.coordinator,
|
|
self.entity_description.set_value_fn(
|
|
self.coordinator.client, self.unit, value
|
|
),
|
|
)
|