Support for Matter 1.4 Water Heater device type (#131505)

* Create water_heater.json

* Update water_heater.json

* Update water_heater.json

* TankVolume

* TankPercentage

* WaterHeaterMode

WaterHeaterMode

* Update sensor.py

* ruff-format

* Update water_heater.json

 Attributes of WaterHeaterManagement Cluster on Endpoint 2
ClusterId 148 (0x0094)

* Update test_sensor.py

water_heater fixture

* Update test_sensor.py

* SensorDeviceClass=VOLUME_STORAGE for `TankVolume`

* `BoostStateEnum` map

* WaterHeaterManagementBoostState

* Update sensor.py

* WaterHeaterManagementEstimatedHeatRequired

* Fix UnitOfEnergy

* Format

* Add `device_types.WaterHeater` to Climate

* Strings for Tank sensors

* WaterHeater icons

* Update icons.json

* Update strings.json

* Update water_heater.json

* ruff-format

* Fix tests

* Fix sensor.py

* Fix icons

* WaterHeaterManagementEstimatedHeatRequired

* WaterHeaterManagementBoostState

* BoostState as a binary sensor

* ElectricalPowerMeasurement values

* Fix tests

* Create water_heater.py

* Update climate.py from dev branch

* Resolve conflicts

* ruff-format

* Add Platform.WATER_HEATER

* Update water_heater.py

* Update water_heater.py

* Update water_heater.py

* Update water_heater.py

* Add WaterHeaterManagement sensors

* Update tests

* Add select test

* Add strings

* First try with water_heater

* Testing current_operation

* BoostState attribute

* target_temperature attributes

* target_temperature attribute

* set_temperature and set_operation_mode

* turn_on / turn_off

* Trigger Boost command

* Fix WaterHeaterBoostInfoStruct

* Add test file

* Add climate cluster to fixture

* Add climate cluster to fixture

* Add tests

* Add ON_OFF feature

* Update tests

* Update tests

* Translate WaterHeaterMode

* Change description

* Update test and snapshots

* Update snapshots

* Set entity name to None to make the device name be the name of the entity

* Format

* Update water_heater.py

* Fix format

* ruff-format

* Import ServiceValidationError

* Update homeassistant/components/matter/water_heater.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* Update water_heater.py

* Update test_water_heater.py

* Update test_water_heater.ambr

* Update test_water_heater.py

* Update select.py

* Update snapshots

* Rename to boost_info

* Set WaterHeaterMode

* Update snapshots

* Update snapshots

* Fix for warning
W7431: Argument 3 should be of type AddConfigEntryEntitiesCallback in async_setup_entry (hass-argument-type)

* Update strings.json

* Update strings and tests

* Fix missing brace

* Update tests

* fix test

* Updates strings

* Fix async_set_temperature

* Update tests

* Update tests

* Update homeassistant/components/matter/water_heater.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Sort strings in strings.json

* Update homeassistant/components/matter/water_heater.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Remove unused line

* Remove min/max target temperatures

* Remove BOOST_STATE_MAP

* Add comment

* Remove SUPPORT_FLAGS_HEATER

* Remove system_mode_value check

* Update homeassistant/components/matter/water_heater.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Reformat async_set_temperature()

* Update snapshots

* Remove MatterWaterHeaterMode selector

* Update snapshots

* Rename test to test_water_heater_set_temperature

* Add test_water_heater_set_operation_mode

* Remove reset_mock

* Update tests/components/matter/test_water_heater.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Add test_update_from_water_heater

* Add test_water_heater_turn_on_off

* Add test_water_heater_boostmode

* Fix SystemMode value for STATE_HIGH_DEMAND

* Add disable boost from water heater device side test

* Remove unused lines

* Remove unused lines

* Fix test indentation

* Fix water heater tests

* Check for None

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Ludovic BOUÉ 2025-04-25 15:28:28 +02:00 committed by GitHub
parent 4a1905a2a2
commit 4adf5ce826
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 1635 additions and 1 deletions

View File

@ -322,4 +322,16 @@ DISCOVERY_SCHEMAS = [
required_attributes=(clusters.EnergyEvse.Attributes.SupplyState,),
allow_multi=True, # also used for sensor entity
),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="WaterHeaterManagementBoostStateSensor",
translation_key="boost_state",
measurement_to_ha=lambda x: (
x == clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive
),
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.WaterHeaterManagement.Attributes.BoostState,),
),
]

View File

@ -27,6 +27,7 @@ from .switch import DISCOVERY_SCHEMAS as SWITCH_SCHEMAS
from .update import DISCOVERY_SCHEMAS as UPDATE_SCHEMAS
from .vacuum import DISCOVERY_SCHEMAS as VACUUM_SCHEMAS
from .valve import DISCOVERY_SCHEMAS as VALVE_SCHEMAS
from .water_heater import DISCOVERY_SCHEMAS as WATER_HEATER_SCHEMAS
DISCOVERY_SCHEMAS: dict[Platform, list[MatterDiscoverySchema]] = {
Platform.BINARY_SENSOR: BINARY_SENSOR_SCHEMAS,
@ -44,6 +45,7 @@ DISCOVERY_SCHEMAS: dict[Platform, list[MatterDiscoverySchema]] = {
Platform.UPDATE: UPDATE_SCHEMAS,
Platform.VACUUM: VACUUM_SCHEMAS,
Platform.VALVE: VALVE_SCHEMAS,
Platform.WATER_HEATER: WATER_HEATER_SCHEMAS,
}
SUPPORTED_PLATFORMS = tuple(DISCOVERY_SCHEMAS)

View File

@ -66,6 +66,12 @@
"operational_state": {
"default": "mdi:play-pause"
},
"tank_volume": {
"default": "mdi:water-boiler"
},
"tank_percentage": {
"default": "mdi:water-boiler"
},
"valve_position": {
"default": "mdi:valve"
},

View File

@ -41,6 +41,7 @@ type SelectCluster = (
| clusters.DishwasherMode
| clusters.EnergyEvseMode
| clusters.DeviceEnergyManagementMode
| clusters.WaterHeaterMode
)

View File

@ -37,6 +37,7 @@ from homeassistant.const import (
UnitOfPower,
UnitOfPressure,
UnitOfTemperature,
UnitOfVolume,
UnitOfVolumeFlowRate,
)
from homeassistant.core import HomeAssistant, callback
@ -65,7 +66,6 @@ CONTAMINATION_STATE_MAP = {
clusters.SmokeCoAlarm.Enums.ContaminationStateEnum.kCritical: "critical",
}
OPERATIONAL_STATE_MAP = {
# enum with known Operation state values which we can translate
clusters.OperationalState.Enums.OperationalStateEnum.kStopped: "stopped",
@ -77,6 +77,12 @@ OPERATIONAL_STATE_MAP = {
clusters.RvcOperationalState.Enums.OperationalStateEnum.kDocked: "docked",
}
BOOST_STATE_MAP = {
clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive: "inactive",
clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive: "active",
clusters.WaterHeaterManagement.Enums.BoostStateEnum.kUnknownEnumValue: None,
}
EVSE_FAULT_STATE_MAP = {
clusters.EnergyEvse.Enums.FaultStateEnum.kNoError: "no_error",
clusters.EnergyEvse.Enums.FaultStateEnum.kMeterFailure: "meter_failure",
@ -996,4 +1002,44 @@ DISCOVERY_SCHEMAS = [
entity_class=MatterSensor,
required_attributes=(clusters.EnergyEvse.Attributes.UserMaximumChargeCurrent,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="WaterHeaterManagementTankVolume",
translation_key="tank_volume",
device_class=SensorDeviceClass.VOLUME_STORAGE,
native_unit_of_measurement=UnitOfVolume.LITERS,
state_class=SensorStateClass.MEASUREMENT,
),
entity_class=MatterSensor,
required_attributes=(clusters.WaterHeaterManagement.Attributes.TankVolume,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="WaterHeaterManagementTankPercentage",
translation_key="tank_percentage",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
entity_class=MatterSensor,
required_attributes=(clusters.WaterHeaterManagement.Attributes.TankPercentage,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="WaterHeaterManagementEstimatedHeatRequired",
translation_key="estimated_heat_required",
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfEnergy.MILLIWATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=3,
state_class=SensorStateClass.TOTAL,
),
entity_class=MatterSensor,
required_attributes=(
clusters.WaterHeaterManagement.Attributes.EstimatedHeatRequired,
),
),
]

View File

@ -85,6 +85,9 @@
},
"evse_supply_charging_state": {
"name": "Supply charging state"
},
"boost_state": {
"name": "Boost state"
}
},
"button": {
@ -229,6 +232,9 @@
},
"laundry_washer_spin_speed": {
"name": "Spin speed"
},
"water_heater_mode": {
"name": "Water heater mode"
}
},
"sensor": {
@ -279,6 +285,15 @@
"switch_current_position": {
"name": "Current switch position"
},
"estimated_heat_required": {
"name": "Required heating energy"
},
"tank_volume": {
"name": "Tank volume"
},
"tank_percentage": {
"name": "Hot water level"
},
"valve_position": {
"name": "Valve position"
},
@ -348,6 +363,11 @@
"valve": {
"name": "[%key:component::valve::title%]"
}
},
"water_heater": {
"water_heater": {
"name": "[%key:component::water_heater::title%]"
}
}
},
"issues": {

View File

@ -0,0 +1,189 @@
"""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)
)
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
),
]

View File

@ -106,6 +106,7 @@ async def integration_fixture(
"silabs_dishwasher",
"silabs_evse_charging",
"silabs_laundrywasher",
"silabs_water_heater",
"smoke_detector",
"switch_unit",
"temperature_sensor",

View File

@ -0,0 +1,534 @@
{
"node_id": 25,
"date_commissioned": "2024-11-21T20:21:44.371473",
"last_interview": "2024-11-21T20:21:44.371503",
"interview_version": 6,
"available": true,
"is_bridge": false,
"attributes": {
"0/29/0": [
{
"0": 22,
"1": 1
}
],
"0/29/1": [29, 31, 40, 43, 44, 45, 48, 49, 51, 60, 62, 63],
"0/29/2": [],
"0/29/3": [2],
"0/29/65532": 0,
"0/29/65533": 2,
"0/29/65528": [],
"0/29/65529": [],
"0/29/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"0/31/0": [
{
"1": 5,
"2": 2,
"3": [112233],
"4": null,
"254": 3
}
],
"0/31/2": 4,
"0/31/3": 3,
"0/31/4": 4,
"0/31/65532": 0,
"0/31/65533": 2,
"0/31/65528": [],
"0/31/65529": [],
"0/31/65531": [0, 2, 3, 4, 65528, 65529, 65531, 65532, 65533],
"0/40/0": 18,
"0/40/1": "Silabs",
"0/40/2": 65521,
"0/40/3": "Water Heater",
"0/40/4": 32773,
"0/40/5": "",
"0/40/6": "**REDACTED**",
"0/40/7": 0,
"0/40/8": "TEST_VERSION",
"0/40/9": 1,
"0/40/10": "v1.3-fix-energy-man-app-comp-2d92654525-dirty",
"0/40/15": "",
"0/40/18": "1868F000380F300B",
"0/40/19": {
"0": 3,
"1": 3
},
"0/40/21": 17039360,
"0/40/22": 1,
"0/40/65532": 0,
"0/40/65533": 4,
"0/40/65528": [],
"0/40/65529": [],
"0/40/65531": [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 18, 19, 21, 22, 65528, 65529, 65531,
65532, 65533
],
"0/43/0": "",
"0/43/1": [
"en-US",
"de-DE",
"fr-FR",
"en-GB",
"es-ES",
"zh-CN",
"it-IT",
"ja-JP"
],
"0/43/65532": 0,
"0/43/65533": 1,
"0/43/65528": [],
"0/43/65529": [],
"0/43/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
"0/44/0": 0,
"0/44/65532": 0,
"0/44/65533": 1,
"0/44/65528": [],
"0/44/65529": [],
"0/44/65531": [0, 65528, 65529, 65531, 65532, 65533],
"0/45/65532": 0,
"0/45/65533": 1,
"0/45/65528": [],
"0/45/65529": [],
"0/45/65531": [65528, 65529, 65531, 65532, 65533],
"0/48/0": 0,
"0/48/1": {
"0": 60,
"1": 900
},
"0/48/2": 0,
"0/48/3": 0,
"0/48/4": true,
"0/48/65532": 0,
"0/48/65533": 2,
"0/48/65528": [1, 3, 5],
"0/48/65529": [0, 2, 4],
"0/48/65531": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533],
"0/49/0": 1,
"0/49/1": [
{
"0": "p0jbsOzJRNw=",
"1": true
}
],
"0/49/4": true,
"0/49/5": 0,
"0/49/6": "p0jbsOzJRNw=",
"0/49/7": null,
"0/49/8": [0],
"0/49/65532": 2,
"0/49/65533": 2,
"0/49/65528": [1, 5, 7],
"0/49/65529": [0, 3, 4, 6, 8],
"0/49/65531": [0, 1, 4, 5, 6, 7, 8, 65528, 65529, 65531, 65532, 65533],
"0/51/0": [
{
"0": "MyHome",
"1": true,
"2": null,
"3": null,
"4": "0ln4A+M/qdU=",
"5": [],
"6": [
"/QANuACgAAAAAAD//gCEAA==",
"/akBUIsgAADu+RflBK+awg==",
"/QANuACgAACOGElK6AMfiw==",
"/oAAAAAAAADQWfgD4z+p1Q=="
],
"7": 4
}
],
"0/51/1": 2,
"0/51/2": 970,
"0/51/8": true,
"0/51/65532": 0,
"0/51/65533": 2,
"0/51/65528": [2],
"0/51/65529": [0, 1],
"0/51/65531": [0, 1, 2, 8, 65528, 65529, 65531, 65532, 65533],
"0/60/0": 0,
"0/60/1": null,
"0/60/2": null,
"0/60/65532": 0,
"0/60/65533": 1,
"0/60/65528": [],
"0/60/65529": [0, 2],
"0/60/65531": [0, 1, 2, 65528, 65529, 65531, 65532, 65533],
"0/62/0": [
{
"1": "FTABAQEkAgE3AyQTAhgmBIAigScmBYAlTTo3BiQVAiQRLBgkBwEkCAEwCUEET/Kg7i1M+NQnTtjldQKCfg81STfZkuBWKlnUUolYjkKNUkOEGf/CAMckg3BH/vbbS8wbC17pWG8EvB7D6RSUfDcKNQEoARgkAgE2AwQCBAEYMAQUBAW4lb/V1fEJebN5Z4UTmE5XrEowBRRv4WHQKIysaFy3b/zkFJmrjWlt7hgwC0Cl0ZjooRQMxjnO0liVKSiIwY+sl0S34aMXNR/PAU89ZqTlHJocegee54S4ajdVZsj1LMV6YWQA3GNw61sC79aFGA==",
"2": "FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQTAhgkBwEkCAEwCUEERIK+dKrh7jNjamMZKV9Ir5gyKBMyce881JnXvjjdrJI3B3OjB6DbhqXvpgk96gZam85WxwGWrRlJEjVl2YQu6DcKNQEpARgkAmAwBBRv4WHQKIysaFy3b/zkFJmrjWlt7jAFFLsR9bzzqpjG9Z5aOkD8b8KMO7AQGDALQAK1q01Umn5ER39/84eai6HfZDKTNsGsuLyhIfpQa6XZQXenGbFDeenDLy8zv5NOLtwu8b44Zv0IrqONItfZqOMY",
"254": 3
}
],
"0/62/1": [
{
"1": "BNI+NL43G+mbJrQUfyNKwd2SHwAPJT3lgk8Ru5z0mzaXqXtfF8C4nYRSBypr7WVg2dx5dzDPTQQfiwGZQhav3nY=",
"2": 4939,
"3": 2,
"4": 44,
"5": "HA_test",
"254": 3
}
],
"0/62/2": 5,
"0/62/3": 3,
"0/62/4": [
"FTABAQAkAgE3AyYUyakYCSYVj6gLsxgmBP2G+CskBQA3BiYUyakYCSYVj6gLsxgkBwEkCAEwCUEEgYwxrTB+tyiEGfrRwjlXTG34MiQtJXbg5Qqd0ohdRW7MfwYY7vZiX/0h9hI8MqUralFaVPcnghAP0MSJm1YrqTcKNQEpARgkAmAwBBS3BS9aJzt+p6i28Nj+trB2Uu+vdzAFFLcFL1onO36nqLbw2P62sHZS7693GDALQIrLt7Uq3S9HEe7apdzYSR+j3BLWNXSTLWD4YbrdyYLpm6xqHDV/NPARcIp4skZdtz91WwFBDfuS4jO5aVoER1sY",
"FTABAQAkAgE3AycUQhmZbaIbYjokFQIYJgRWZLcqJAUANwYnFEIZmW2iG2I6JBUCGCQHASQIATAJQQT2AlKGW/kOMjqayzeO0md523/fuhrhGEUU91uQpTiKo0I7wcPpKnmrwfQNPX6g0kEQl+VGaXa3e22lzfu5Tzp0Nwo1ASkBGCQCYDAEFOOMk13ScMKuT2hlaydi1yEJnhTqMAUU44yTXdJwwq5PaGVrJ2LXIQmeFOoYMAtAv2jJd1qd5miXbYesH1XrJ+vgyY0hzGuZ78N6Jw4Cb1oN1sLSpA+PNM0u7+hsEqcSvvn2eSV8EaRR+hg5YQjHDxg=",
"FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQUARgkBwEkCAEwCUEE0j40vjcb6ZsmtBR/I0rB3ZIfAA8lPeWCTxG7nPSbNpepe18XwLidhFIHKmvtZWDZ3Hl3MM9NBB+LAZlCFq/edjcKNQEpARgkAmAwBBS7EfW886qYxvWeWjpA/G/CjDuwEDAFFLsR9bzzqpjG9Z5aOkD8b8KMO7AQGDALQIgQgt5asUGXO0ZyTWWKdjAmBSoJAzRMuD4Z+tQYZanQ3s0OItL07MU2In6uyXhjNBfjJlRqon780lhjTsm2Y+8Y"
],
"0/62/5": 3,
"0/62/65532": 0,
"0/62/65533": 1,
"0/62/65528": [1, 3, 5, 8],
"0/62/65529": [0, 2, 4, 6, 7, 9, 10, 11],
"0/62/65531": [0, 1, 2, 3, 4, 5, 65528, 65529, 65531, 65532, 65533],
"0/63/0": [],
"0/63/1": [],
"0/63/2": 4,
"0/63/3": 3,
"0/63/65532": 0,
"0/63/65533": 2,
"0/63/65528": [2, 5],
"0/63/65529": [0, 1, 3, 4],
"0/63/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"2/3/0": 0,
"2/3/1": 0,
"2/3/65532": 0,
"2/3/65533": 5,
"2/3/65528": [],
"2/3/65529": [0],
"2/3/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
"2/29/0": [
{
"0": 1293,
"1": 1
},
{
"0": 1296,
"1": 1
},
{
"0": 1295,
"1": 1
}
],
"2/29/1": [3, 29, 144, 145, 148, 152, 156, 158, 159],
"2/29/2": [],
"2/29/3": [],
"2/29/65532": 0,
"2/29/65533": 2,
"2/29/65528": [],
"2/29/65529": [],
"2/29/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"2/144/0": 0,
"2/144/1": 3,
"2/144/2": [
{
"0": 5,
"1": true,
"2": -50000000,
"3": 50000000,
"4": [
{
"0": -50000000,
"1": -10000000,
"2": 5000,
"3": 2000,
"4": 3000
},
{
"0": -9999999,
"1": 9999999,
"2": 1000,
"3": 100,
"4": 500
},
{
"0": 10000000,
"1": 50000000,
"2": 5000,
"3": 2000,
"4": 3000
}
]
},
{
"0": 2,
"1": true,
"2": -100000,
"3": 100000,
"4": [
{
"0": -100000,
"1": -5000,
"2": 5000,
"3": 2000,
"4": 3000
},
{
"0": -4999,
"1": 4999,
"2": 1000,
"3": 100,
"4": 500
},
{
"0": 5000,
"1": 100000,
"2": 5000,
"3": 2000,
"4": 3000
}
]
},
{
"0": 1,
"1": true,
"2": -500000,
"3": 500000,
"4": [
{
"0": -500000,
"1": -100000,
"2": 5000,
"3": 2000,
"4": 3000
},
{
"0": -99999,
"1": 99999,
"2": 1000,
"3": 100,
"4": 500
},
{
"0": 100000,
"1": 500000,
"2": 5000,
"3": 2000,
"4": 3000
}
]
}
],
"2/144/3": [],
"2/144/4": 230000,
"2/144/5": 100,
"2/144/6": null,
"2/144/7": null,
"2/144/8": 23000,
"2/144/9": null,
"2/144/10": null,
"2/144/11": null,
"2/144/12": null,
"2/144/13": null,
"2/144/14": 50,
"2/144/15": [
{
"0": 1,
"1": 100000
}
],
"2/144/16": [
{
"0": 1,
"1": 100000
}
],
"2/144/17": null,
"2/144/18": null,
"2/144/65532": 31,
"2/144/65533": 1,
"2/144/65528": [],
"2/144/65529": [],
"2/144/65531": [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 65528,
65529, 65531, 65532, 65533
],
"2/145/0": {
"0": 14,
"1": true,
"2": 0,
"3": 1000000000000000,
"4": [
{
"0": 0,
"1": 0
}
]
},
"2/145/1": null,
"2/145/2": null,
"2/145/3": null,
"2/145/4": null,
"2/145/5": {
"0": 0,
"1": 0,
"2": 0,
"3": 0
},
"2/145/65532": 15,
"2/145/65533": 1,
"2/145/65528": [],
"2/145/65529": [],
"2/145/65531": [0, 1, 2, 3, 4, 5, 65528, 65529, 65531, 65532, 65533],
"2/148/0": 1,
"2/148/1": 0,
"2/148/2": 200,
"2/148/3": 4000000,
"2/148/4": 40,
"2/148/5": 0,
"2/148/65532": 3,
"2/148/65533": 2,
"2/148/65528": [],
"2/148/65529": [0, 1],
"2/148/65531": [0, 1, 2, 3, 4, 5, 65528, 65529, 65531, 65532, 65533],
"2/152/0": 2,
"2/152/1": false,
"2/152/2": 1,
"2/152/3": 1200000,
"2/152/4": 7600000,
"2/152/5": null,
"2/152/6": null,
"2/152/7": 0,
"2/152/65532": 123,
"2/152/65533": 4,
"2/152/65528": [],
"2/152/65529": [0, 1, 2, 3, 4, 5, 6, 7],
"2/152/65531": [0, 1, 2, 3, 4, 5, 6, 7, 65528, 65529, 65531, 65532, 65533],
"2/156/65532": 1,
"2/156/65533": 1,
"2/156/65528": [],
"2/156/65529": [],
"2/156/65531": [65528, 65529, 65531, 65532, 65533],
"2/158/0": [
{
"0": "Off",
"1": 0,
"2": [
{
"1": 16384
}
]
},
{
"0": "Manual",
"1": 1,
"2": [
{
"1": 16385
}
]
},
{
"0": "Timed",
"1": 2,
"2": [
{
"1": 16386
}
]
}
],
"2/158/1": 1,
"2/158/65532": 0,
"2/158/65533": 1,
"2/158/65528": [1],
"2/158/65529": [0],
"2/158/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
"2/159/0": [
{
"0": "No energy management (forecast only)",
"1": 0,
"2": [
{
"1": 16384
}
]
},
{
"0": "Device optimizes (no local or grid control)",
"1": 1,
"2": [
{
"1": 16385
}
]
},
{
"0": "Optimized within building",
"1": 2,
"2": [
{
"1": 16386
},
{
"1": 16385
}
]
},
{
"0": "Optimized for grid",
"1": 3,
"2": [
{
"1": 16385
},
{
"1": 16387
}
]
},
{
"0": "Optimized for grid and building",
"1": 4,
"2": [
{
"1": 16386
},
{
"1": 16385
},
{
"1": 16387
}
]
}
],
"2/159/1": 0,
"2/159/65532": 0,
"2/159/65533": 2,
"2/159/65528": [1],
"2/159/65529": [0],
"2/159/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
"2/513/0": 5000,
"2/513/3": 4000,
"2/513/4": 6500,
"2/513/18": 6500,
"2/513/21": 4000,
"2/513/22": 6500,
"2/513/27": 2,
"2/513/28": 4,
"2/513/65532": 1,
"2/513/65533": 7,
"2/513/65528": [],
"2/513/65529": [0],
"2/513/65531": [0, 27, 28, 65528, 65529, 65531, 65532, 65533]
},
"2/513/0": 5000,
"2/513/3": 4000,
"2/513/4": 6500,
"2/513/18": 6500,
"2/513/21": 4000,
"2/513/22": 6500,
"2/513/27": 2,
"2/513/28": 4,
"2/513/65532": 1,
"2/513/65533": 7,
"2/513/65528": [],
"2/513/65529": [0],
"2/513/65531": [0, 27, 28, 65528, 65529, 65531, 65532, 65533],
"attribute_subscriptions": []
}

View File

@ -527,6 +527,53 @@
'state': 'on',
})
# ---
# name: test_binary_sensors[silabs_water_heater][binary_sensor.water_heater_boost_state-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.water_heater_boost_state',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Boost state',
'platform': 'matter',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'boost_state',
'unique_id': '00000000000004D2-0000000000000019-MatterNodeDevice-2-WaterHeaterManagementBoostStateSensor-148-5',
'unit_of_measurement': None,
})
# ---
# name: test_binary_sensors[silabs_water_heater][binary_sensor.water_heater_boost_state-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Water Heater Boost state',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.water_heater_boost_state',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_binary_sensors[smoke_detector][binary_sensor.smoke_sensor_battery_alert-entry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@ -1839,6 +1839,68 @@
'state': 'Colors',
})
# ---
# name: test_selects[silabs_water_heater][select.water_heater_energy_management_mode-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'No energy management (forecast only)',
'Device optimizes (no local or grid control)',
'Optimized within building',
'Optimized for grid',
'Optimized for grid and building',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': None,
'entity_id': 'select.water_heater_energy_management_mode',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Energy management mode',
'platform': 'matter',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'device_energy_management_mode',
'unique_id': '00000000000004D2-0000000000000019-MatterNodeDevice-2-MatterDeviceEnergyManagementMode-159-1',
'unit_of_measurement': None,
})
# ---
# name: test_selects[silabs_water_heater][select.water_heater_energy_management_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Water Heater Energy management mode',
'options': list([
'No energy management (forecast only)',
'Device optimizes (no local or grid control)',
'Optimized within building',
'Optimized for grid',
'Optimized for grid and building',
]),
}),
'context': <ANY>,
'entity_id': 'select.water_heater_energy_management_mode',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'No energy management (forecast only)',
})
# ---
# name: test_selects[switch_unit][select.mock_switchunit_power_on_behavior_on_startup-entry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@ -3535,6 +3535,341 @@
'state': '120.0',
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_current-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.water_heater_current',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfElectricCurrent.AMPERE: 'A'>,
}),
}),
'original_device_class': <SensorDeviceClass.CURRENT: 'current'>,
'original_icon': None,
'original_name': 'Current',
'platform': 'matter',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '00000000000004D2-0000000000000019-MatterNodeDevice-2-ElectricalPowerMeasurementActiveCurrent-144-5',
'unit_of_measurement': <UnitOfElectricCurrent.AMPERE: 'A'>,
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_current-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'current',
'friendly_name': 'Water Heater Current',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricCurrent.AMPERE: 'A'>,
}),
'context': <ANY>,
'entity_id': 'sensor.water_heater_current',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0.1',
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_hot_water_level-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.water_heater_hot_water_level',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Hot water level',
'platform': 'matter',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'tank_percentage',
'unique_id': '00000000000004D2-0000000000000019-MatterNodeDevice-2-WaterHeaterManagementTankPercentage-148-4',
'unit_of_measurement': '%',
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_hot_water_level-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Water Heater Hot water level',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.water_heater_hot_water_level',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '40',
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_power-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.water_heater_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Power',
'platform': 'matter',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '00000000000004D2-0000000000000019-MatterNodeDevice-2-ElectricalPowerMeasurementWatt-144-8',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Water Heater Power',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.water_heater_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '23.0',
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_required_heating_energy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.water_heater_required_heating_energy',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 3,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Required heating energy',
'platform': 'matter',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'estimated_heat_required',
'unique_id': '00000000000004D2-0000000000000019-MatterNodeDevice-2-WaterHeaterManagementEstimatedHeatRequired-148-3',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_required_heating_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Water Heater Required heating energy',
'state_class': <SensorStateClass.TOTAL: 'total'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.water_heater_required_heating_energy',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '4.0',
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_tank_volume-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.water_heater_tank_volume',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.VOLUME_STORAGE: 'volume_storage'>,
'original_icon': None,
'original_name': 'Tank volume',
'platform': 'matter',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'tank_volume',
'unique_id': '00000000000004D2-0000000000000019-MatterNodeDevice-2-WaterHeaterManagementTankVolume-148-2',
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_tank_volume-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'volume_storage',
'friendly_name': 'Water Heater Tank volume',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
'context': <ANY>,
'entity_id': 'sensor.water_heater_tank_volume',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '200',
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_voltage-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.water_heater_voltage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
'original_icon': None,
'original_name': 'Voltage',
'platform': 'matter',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '00000000000004D2-0000000000000019-MatterNodeDevice-2-ElectricalPowerMeasurementVoltage-144-4',
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
})
# ---
# name: test_sensors[silabs_water_heater][sensor.water_heater_voltage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'voltage',
'friendly_name': 'Water Heater Voltage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
'context': <ANY>,
'entity_id': 'sensor.water_heater_voltage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '230.0',
})
# ---
# name: test_sensors[smoke_detector][sensor.smoke_sensor_battery-entry]
EntityRegistryEntrySnapshot({
'aliases': set({

View File

@ -0,0 +1,69 @@
# serializer version: 1
# name: test_water_heaters[silabs_water_heater][water_heater.water_heater-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max_temp': 65,
'min_temp': 40,
'operation_list': list([
'eco',
'high_demand',
'off',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'water_heater',
'entity_category': None,
'entity_id': 'water_heater.water_heater',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'matter',
'previous_unique_id': None,
'supported_features': <WaterHeaterEntityFeature: 11>,
'translation_key': None,
'unique_id': '00000000000004D2-0000000000000019-MatterNodeDevice-2-MatterWaterHeater-513-18',
'unit_of_measurement': None,
})
# ---
# name: test_water_heaters[silabs_water_heater][water_heater.water_heater-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 50,
'friendly_name': 'Water Heater',
'max_temp': 65,
'min_temp': 40,
'operation_list': list([
'eco',
'high_demand',
'off',
]),
'operation_mode': 'eco',
'supported_features': <WaterHeaterEntityFeature: 11>,
'target_temp_high': None,
'target_temp_low': None,
'temperature': 65,
}),
'context': <ANY>,
'entity_id': 'water_heater.water_heater',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'eco',
})
# ---

View File

@ -197,3 +197,23 @@ async def test_evse_sensor(
state = hass.states.get(entity_id)
assert state
assert state.state == "off"
@pytest.mark.parametrize("node_fixture", ["silabs_water_heater"])
async def test_water_heater(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test water heater sensor."""
# BoostState
state = hass.states.get("binary_sensor.water_heater_boost_state")
assert state
assert state.state == "off"
set_node_attribute(matter_node, 2, 148, 5, 1)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("binary_sensor.water_heater_boost_state")
assert state
assert state.state == "on"

View File

@ -467,3 +467,47 @@ async def test_evse_sensor(
state = hass.states.get("sensor.evse_user_max_charge_current")
assert state
assert state.state == "63.0"
@pytest.mark.parametrize("node_fixture", ["silabs_water_heater"])
async def test_water_heater(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test water heater sensor."""
# TankVolume
state = hass.states.get("sensor.water_heater_tank_volume")
assert state
assert state.state == "200"
set_node_attribute(matter_node, 2, 148, 2, 100)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("sensor.water_heater_tank_volume")
assert state
assert state.state == "100"
# EstimatedHeatRequired
state = hass.states.get("sensor.water_heater_required_heating_energy")
assert state
assert state.state == "4.0"
set_node_attribute(matter_node, 2, 148, 3, 1000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("sensor.water_heater_required_heating_energy")
assert state
assert state.state == "1.0"
# TankPercentage
state = hass.states.get("sensor.water_heater_hot_water_level")
assert state
assert state.state == "40"
set_node_attribute(matter_node, 2, 148, 4, 50)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("sensor.water_heater_hot_water_level")
assert state
assert state.state == "50"

View File

@ -0,0 +1,246 @@
"""Test Matter sensors."""
from unittest.mock import MagicMock, call
from chip.clusters import Objects as clusters
from matter_server.client.models.node import MatterNode
from matter_server.common.helpers.util import create_attribute_path_from_attribute
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.water_heater import (
STATE_ECO,
STATE_HIGH_DEMAND,
STATE_OFF,
WaterHeaterEntityFeature,
)
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from .common import (
set_node_attribute,
snapshot_matter_entities,
trigger_subscription_callback,
)
@pytest.mark.usefixtures("matter_devices")
async def test_water_heaters(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test water heaters."""
snapshot_matter_entities(hass, entity_registry, snapshot, Platform.WATER_HEATER)
@pytest.mark.parametrize("node_fixture", ["silabs_water_heater"])
async def test_water_heater(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test water heater entity."""
state = hass.states.get("water_heater.water_heater")
assert state
assert state.attributes["min_temp"] == 40
assert state.attributes["max_temp"] == 65
assert state.attributes["temperature"] == 65
assert state.attributes["operation_list"] == ["eco", "high_demand", "off"]
assert state.state == STATE_ECO
# test supported features correctly parsed
mask = (
WaterHeaterEntityFeature.TARGET_TEMPERATURE
| WaterHeaterEntityFeature.ON_OFF
| WaterHeaterEntityFeature.OPERATION_MODE
)
assert state.attributes["supported_features"] & mask == mask
@pytest.mark.parametrize("node_fixture", ["silabs_water_heater"])
async def test_water_heater_set_temperature(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test water_heater set temperature service."""
# test single-setpoint temperature adjustment when eco mode is active
state = hass.states.get("water_heater.water_heater")
assert state
assert state.state == STATE_ECO
await hass.services.async_call(
"water_heater",
"set_temperature",
{
"entity_id": "water_heater.water_heater",
"temperature": 52,
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 1
assert matter_client.write_attribute.call_args == call(
node_id=matter_node.node_id,
attribute_path="2/513/18",
value=5200,
)
matter_client.write_attribute.reset_mock()
@pytest.mark.parametrize("node_fixture", ["silabs_water_heater"])
@pytest.mark.parametrize(
("operation_mode", "matter_attribute_value"),
[(STATE_OFF, 0), (STATE_ECO, 4), (STATE_HIGH_DEMAND, 4)],
)
async def test_water_heater_set_operation_mode(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
operation_mode: str,
matter_attribute_value: int,
) -> None:
"""Test water_heater set operation mode service."""
state = hass.states.get("water_heater.water_heater")
assert state
# test change mode to each operation_mode
await hass.services.async_call(
"water_heater",
"set_operation_mode",
{
"entity_id": "water_heater.water_heater",
"operation_mode": operation_mode,
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 1
assert matter_client.write_attribute.call_args == call(
node_id=matter_node.node_id,
attribute_path=create_attribute_path_from_attribute(
endpoint_id=2,
attribute=clusters.Thermostat.Attributes.SystemMode,
),
value=matter_attribute_value,
)
@pytest.mark.parametrize("node_fixture", ["silabs_water_heater"])
async def test_water_heater_boostmode(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test water_heater set operation mode service."""
# Boost 1h (3600s)
boost_info: type[
clusters.WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct
] = clusters.WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct(duration=3600)
state = hass.states.get("water_heater.water_heater")
assert state
# enable water_heater boostmode
await hass.services.async_call(
"water_heater",
"set_operation_mode",
{
"entity_id": "water_heater.water_heater",
"operation_mode": STATE_HIGH_DEMAND,
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 1
assert matter_client.write_attribute.call_args == call(
node_id=matter_node.node_id,
attribute_path=create_attribute_path_from_attribute(
endpoint_id=2,
attribute=clusters.Thermostat.Attributes.SystemMode,
),
value=4,
)
assert matter_client.send_device_command.call_count == 1
assert matter_client.send_device_command.call_args == call(
node_id=matter_node.node_id,
endpoint_id=2,
command=clusters.WaterHeaterManagement.Commands.Boost(boostInfo=boost_info),
)
@pytest.mark.parametrize("node_fixture", ["silabs_water_heater"])
async def test_update_from_water_heater(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test enable boost from water heater device side."""
entity_id = "water_heater.water_heater"
# confirm initial BoostState (as stored in the fixture)
state = hass.states.get(entity_id)
assert state
# confirm thermostat state is 'high_demand' by setting the BoostState to 1
set_node_attribute(matter_node, 2, 148, 5, 1)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_HIGH_DEMAND
# confirm thermostat state is 'eco' by setting the BoostState to 0
set_node_attribute(matter_node, 2, 148, 5, 0)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_ECO
@pytest.mark.parametrize("node_fixture", ["silabs_water_heater"])
async def test_water_heater_turn_on_off(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test water_heater set turn_off/turn_on."""
state = hass.states.get("water_heater.water_heater")
assert state
# turn_off water_heater
await hass.services.async_call(
"water_heater",
"turn_off",
{
"entity_id": "water_heater.water_heater",
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 1
assert matter_client.write_attribute.call_args == call(
node_id=matter_node.node_id,
attribute_path=create_attribute_path_from_attribute(
endpoint_id=2,
attribute=clusters.Thermostat.Attributes.SystemMode,
),
value=0,
)
matter_client.write_attribute.reset_mock()
# turn_on water_heater
await hass.services.async_call(
"water_heater",
"turn_on",
{
"entity_id": "water_heater.water_heater",
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 1
assert matter_client.write_attribute.call_args == call(
node_id=matter_node.node_id,
attribute_path=create_attribute_path_from_attribute(
endpoint_id=2,
attribute=clusters.Thermostat.Attributes.SystemMode,
),
value=4,
)