mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
180 lines
5.6 KiB
Python
180 lines
5.6 KiB
Python
"""Support for WaterHeater entities of the Evohome integration."""
|
|
|
|
from datetime import timedelta
|
|
import logging
|
|
from typing import Any, override
|
|
|
|
import evohomeasync2 as evo
|
|
from evohomeasync2.const import (
|
|
SZ_STATE_STATUS,
|
|
SZ_TEMPERATURE_STATUS,
|
|
DhwState as EvoDhwState,
|
|
ZoneMode as EvoZoneMode,
|
|
)
|
|
|
|
from homeassistant.components.water_heater import (
|
|
WaterHeaterEntity,
|
|
WaterHeaterEntityFeature,
|
|
)
|
|
from homeassistant.const import (
|
|
PRECISION_TENTHS,
|
|
PRECISION_WHOLE,
|
|
STATE_OFF,
|
|
STATE_ON,
|
|
UnitOfTemperature,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
|
from homeassistant.util import dt as dt_util
|
|
|
|
from .const import EVOHOME_DATA
|
|
from .coordinator import EvoDataUpdateCoordinator
|
|
from .entity import EvoChild
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
STATE_AUTO = "auto"
|
|
|
|
HA_STATE_TO_EVO = {STATE_AUTO: "", STATE_ON: EvoDhwState.ON, STATE_OFF: EvoDhwState.OFF}
|
|
EVO_STATE_TO_HA = {v: k for k, v in HA_STATE_TO_EVO.items() if k != ""}
|
|
|
|
|
|
async def async_setup_platform(
|
|
hass: HomeAssistant,
|
|
_: ConfigType,
|
|
async_add_entities: AddEntitiesCallback,
|
|
discovery_info: DiscoveryInfoType | None = None,
|
|
) -> None:
|
|
"""Set up the water heater platform for Evohome."""
|
|
|
|
if discovery_info is None:
|
|
return
|
|
|
|
coordinator = hass.data[EVOHOME_DATA].coordinator
|
|
tcs = hass.data[EVOHOME_DATA].tcs
|
|
|
|
assert tcs.hotwater is not None # mypy check
|
|
|
|
_LOGGER.debug(
|
|
"Adding: DhwController (%s), id=%s",
|
|
tcs.hotwater.type,
|
|
tcs.hotwater.id,
|
|
)
|
|
|
|
entity = EvoDHW(coordinator, tcs.hotwater)
|
|
|
|
async_add_entities([entity])
|
|
|
|
await entity.update_attrs()
|
|
|
|
|
|
class EvoDHW(EvoChild, WaterHeaterEntity):
|
|
"""Base for any evohome-compatible DHW controller."""
|
|
|
|
_attr_operation_list = list(HA_STATE_TO_EVO)
|
|
_attr_supported_features = (
|
|
WaterHeaterEntityFeature.AWAY_MODE
|
|
| WaterHeaterEntityFeature.ON_OFF
|
|
| WaterHeaterEntityFeature.OPERATION_MODE
|
|
)
|
|
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
|
|
|
_evo_device: evo.HotWater
|
|
_evo_id_attr = "dhw_id"
|
|
_evo_state_attr_names = (SZ_STATE_STATUS, SZ_TEMPERATURE_STATUS)
|
|
|
|
def __init__(
|
|
self, coordinator: EvoDataUpdateCoordinator, evo_device: evo.HotWater
|
|
) -> None:
|
|
"""Initialize an evohome-compatible DHW controller."""
|
|
|
|
super().__init__(coordinator, evo_device)
|
|
|
|
self._attr_unique_id = evo_device.id
|
|
self._attr_name = evo_device.name # is static
|
|
|
|
self._attr_precision = (
|
|
PRECISION_TENTHS if coordinator.client_v1 else PRECISION_WHOLE
|
|
)
|
|
|
|
async def async_set_dhw_override(
|
|
self, state: bool, duration: timedelta | None = None
|
|
) -> None:
|
|
"""Override the DHW zone's on/off state permanently or for a duration."""
|
|
|
|
if duration is None:
|
|
until = None # indefinitely, aka permanent override
|
|
elif duration.total_seconds() == 0:
|
|
await self._update_schedule()
|
|
until = self.setpoints.get("next_sp_from")
|
|
else:
|
|
until = dt_util.now() + duration
|
|
|
|
until = dt_util.as_utc(until) if until else None
|
|
|
|
if state:
|
|
await self.coordinator.call_client_api(self._evo_device.set_on(until=until))
|
|
else:
|
|
await self.coordinator.call_client_api(
|
|
self._evo_device.set_off(until=until)
|
|
)
|
|
|
|
@property
|
|
@override
|
|
def current_operation(self) -> str | None:
|
|
"""Return the current operating mode (Auto, On, or Off)."""
|
|
if self._evo_device.mode == EvoZoneMode.FOLLOW_SCHEDULE:
|
|
return STATE_AUTO
|
|
return EVO_STATE_TO_HA[self._evo_device.state]
|
|
|
|
@property
|
|
@override
|
|
def is_away_mode_on(self) -> bool | None:
|
|
"""Return True if away mode is on."""
|
|
is_off = EVO_STATE_TO_HA[self._evo_device.state] == STATE_OFF
|
|
is_permanent = self._evo_device.mode == EvoZoneMode.PERMANENT_OVERRIDE
|
|
return is_off and is_permanent
|
|
|
|
@override
|
|
async def async_set_operation_mode(self, operation_mode: str) -> None:
|
|
"""Set new operation mode for a DHW controller.
|
|
|
|
Except for Auto, the mode is only until the next SetPoint.
|
|
"""
|
|
if operation_mode == STATE_AUTO:
|
|
await self.coordinator.call_client_api(self._evo_device.reset())
|
|
else:
|
|
await self._update_schedule()
|
|
until = self.setpoints.get("next_sp_from")
|
|
until = dt_util.as_utc(until) if until else None
|
|
|
|
if operation_mode == STATE_ON:
|
|
await self.coordinator.call_client_api(
|
|
self._evo_device.set_on(until=until)
|
|
)
|
|
else: # STATE_OFF
|
|
await self.coordinator.call_client_api(
|
|
self._evo_device.set_off(until=until)
|
|
)
|
|
|
|
@override
|
|
async def async_turn_away_mode_on(self) -> None:
|
|
"""Turn away mode on."""
|
|
await self.coordinator.call_client_api(self._evo_device.set_off())
|
|
|
|
@override
|
|
async def async_turn_away_mode_off(self) -> None:
|
|
"""Turn away mode off."""
|
|
await self.coordinator.call_client_api(self._evo_device.reset())
|
|
|
|
@override
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Turn on."""
|
|
await self.coordinator.call_client_api(self._evo_device.set_on())
|
|
|
|
@override
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Turn off."""
|
|
await self.coordinator.call_client_api(self._evo_device.set_off())
|