195 lines
7.0 KiB
Python
195 lines
7.0 KiB
Python
"""Matter water heater platform."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, cast
|
|
|
|
from chip.clusters import Objects as clusters
|
|
from matter_server.client.models import device_types
|
|
from matter_server.common.helpers.util import create_attribute_path_from_attribute
|
|
|
|
from homeassistant.components.water_heater import (
|
|
STATE_ECO,
|
|
STATE_HIGH_DEMAND,
|
|
STATE_OFF,
|
|
WaterHeaterEntity,
|
|
WaterHeaterEntityDescription,
|
|
WaterHeaterEntityFeature,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.const import (
|
|
ATTR_TEMPERATURE,
|
|
PRECISION_WHOLE,
|
|
Platform,
|
|
UnitOfTemperature,
|
|
)
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
|
|
from .entity import MatterEntity
|
|
from .helpers import get_matter
|
|
from .models import MatterDiscoverySchema
|
|
|
|
TEMPERATURE_SCALING_FACTOR = 100
|
|
|
|
# Map HA WH system mode to Matter ThermostatRunningMode attribute of the Thermostat cluster (Heat = 4)
|
|
WATER_HEATER_SYSTEM_MODE_MAP = {
|
|
STATE_ECO: 4,
|
|
STATE_HIGH_DEMAND: 4,
|
|
STATE_OFF: 0,
|
|
}
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
config_entry: ConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
"""Set up Matter WaterHeater platform from Config Entry."""
|
|
matter = get_matter(hass)
|
|
matter.register_platform_handler(Platform.WATER_HEATER, async_add_entities)
|
|
|
|
|
|
class MatterWaterHeater(MatterEntity, WaterHeaterEntity):
|
|
"""Representation of a Matter WaterHeater entity."""
|
|
|
|
_attr_current_temperature: float | None = None
|
|
_attr_current_operation: str
|
|
_attr_operation_list = [
|
|
STATE_ECO,
|
|
STATE_HIGH_DEMAND,
|
|
STATE_OFF,
|
|
]
|
|
_attr_precision = PRECISION_WHOLE
|
|
_attr_supported_features = (
|
|
WaterHeaterEntityFeature.TARGET_TEMPERATURE
|
|
| WaterHeaterEntityFeature.ON_OFF
|
|
| WaterHeaterEntityFeature.OPERATION_MODE
|
|
)
|
|
_attr_target_temperature: float | None = None
|
|
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
|
_platform_translation_key = "water_heater"
|
|
|
|
async def async_set_temperature(self, **kwargs: Any) -> None:
|
|
"""Set new target temperature."""
|
|
target_temperature: float | None = kwargs.get(ATTR_TEMPERATURE)
|
|
if (
|
|
target_temperature is not None
|
|
and self.target_temperature != target_temperature
|
|
):
|
|
matter_attribute = clusters.Thermostat.Attributes.OccupiedHeatingSetpoint
|
|
await self.write_attribute(
|
|
value=round(target_temperature * TEMPERATURE_SCALING_FACTOR),
|
|
matter_attribute=matter_attribute,
|
|
)
|
|
|
|
async def async_set_operation_mode(self, operation_mode: str) -> None:
|
|
"""Set new operation mode."""
|
|
self._attr_current_operation = operation_mode
|
|
# Boost 1h (3600s)
|
|
boost_info: type[
|
|
clusters.WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct
|
|
] = clusters.WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct(
|
|
duration=3600
|
|
)
|
|
system_mode_value = WATER_HEATER_SYSTEM_MODE_MAP[operation_mode]
|
|
await self.write_attribute(
|
|
value=system_mode_value,
|
|
matter_attribute=clusters.Thermostat.Attributes.SystemMode,
|
|
)
|
|
system_mode_path = create_attribute_path_from_attribute(
|
|
endpoint_id=self._endpoint.endpoint_id,
|
|
attribute=clusters.Thermostat.Attributes.SystemMode,
|
|
)
|
|
self._endpoint.set_attribute_value(system_mode_path, system_mode_value)
|
|
self._update_from_device()
|
|
# Trigger Boost command
|
|
if operation_mode == STATE_HIGH_DEMAND:
|
|
await self.send_device_command(
|
|
clusters.WaterHeaterManagement.Commands.Boost(boostInfo=boost_info)
|
|
)
|
|
# Trigger CancelBoost command for other modes
|
|
else:
|
|
await self.send_device_command(
|
|
clusters.WaterHeaterManagement.Commands.CancelBoost()
|
|
)
|
|
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Turn on water heater."""
|
|
await self.async_set_operation_mode("eco")
|
|
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Turn off water heater."""
|
|
await self.async_set_operation_mode("off")
|
|
|
|
@callback
|
|
def _update_from_device(self) -> None:
|
|
"""Update from device."""
|
|
self._attr_current_temperature = self._get_temperature_in_degrees(
|
|
clusters.Thermostat.Attributes.LocalTemperature
|
|
)
|
|
self._attr_target_temperature = self._get_temperature_in_degrees(
|
|
clusters.Thermostat.Attributes.OccupiedHeatingSetpoint
|
|
)
|
|
boost_state = self.get_matter_attribute_value(
|
|
clusters.WaterHeaterManagement.Attributes.BoostState
|
|
)
|
|
if boost_state == clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive:
|
|
self._attr_current_operation = STATE_HIGH_DEMAND
|
|
else:
|
|
self._attr_current_operation = STATE_ECO
|
|
self._attr_temperature = cast(
|
|
float,
|
|
self._get_temperature_in_degrees(
|
|
clusters.Thermostat.Attributes.OccupiedHeatingSetpoint
|
|
),
|
|
)
|
|
self._attr_min_temp = cast(
|
|
float,
|
|
self._get_temperature_in_degrees(
|
|
clusters.Thermostat.Attributes.AbsMinHeatSetpointLimit
|
|
),
|
|
)
|
|
self._attr_max_temp = cast(
|
|
float,
|
|
self._get_temperature_in_degrees(
|
|
clusters.Thermostat.Attributes.AbsMaxHeatSetpointLimit
|
|
),
|
|
)
|
|
|
|
@callback
|
|
def _get_temperature_in_degrees(
|
|
self, attribute: type[clusters.ClusterAttributeDescriptor]
|
|
) -> float | None:
|
|
"""Return the scaled temperature value for the given attribute."""
|
|
if (value := self.get_matter_attribute_value(attribute)) is not None:
|
|
return float(value) / TEMPERATURE_SCALING_FACTOR
|
|
return None
|
|
|
|
|
|
# Discovery schema(s) to map Matter Attributes to HA entities
|
|
DISCOVERY_SCHEMAS = [
|
|
MatterDiscoverySchema(
|
|
platform=Platform.WATER_HEATER,
|
|
entity_description=WaterHeaterEntityDescription(
|
|
key="MatterWaterHeater",
|
|
name=None,
|
|
),
|
|
entity_class=MatterWaterHeater,
|
|
required_attributes=(
|
|
clusters.Thermostat.Attributes.OccupiedHeatingSetpoint,
|
|
clusters.Thermostat.Attributes.AbsMinHeatSetpointLimit,
|
|
clusters.Thermostat.Attributes.AbsMaxHeatSetpointLimit,
|
|
clusters.Thermostat.Attributes.LocalTemperature,
|
|
clusters.WaterHeaterManagement.Attributes.FeatureMap,
|
|
),
|
|
optional_attributes=(
|
|
clusters.WaterHeaterManagement.Attributes.HeaterTypes,
|
|
clusters.WaterHeaterManagement.Attributes.BoostState,
|
|
clusters.WaterHeaterManagement.Attributes.HeatDemand,
|
|
),
|
|
device_type=(device_types.WaterHeater,),
|
|
allow_multi=True, # also used for sensor entity
|
|
),
|
|
]
|