diff --git a/homeassistant/components/matter/binary_sensor.py b/homeassistant/components/matter/binary_sensor.py index 6882078a71219..4078db940c0c2 100644 --- a/homeassistant/components/matter/binary_sensor.py +++ b/homeassistant/components/matter/binary_sensor.py @@ -262,4 +262,16 @@ def _update_from_device(self) -> None: entity_class=MatterBinarySensor, required_attributes=(clusters.SmokeCoAlarm.Attributes.InterconnectCOAlarm,), ), + 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,), + ), ] diff --git a/homeassistant/components/matter/discovery.py b/homeassistant/components/matter/discovery.py index 7102b693e45e5..8042b7505f44c 100644 --- a/homeassistant/components/matter/discovery.py +++ b/homeassistant/components/matter/discovery.py @@ -27,6 +27,7 @@ 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 @@ Platform.UPDATE: UPDATE_SCHEMAS, Platform.VACUUM: VACUUM_SCHEMAS, Platform.VALVE: VALVE_SCHEMAS, + Platform.WATER_HEATER: WATER_HEATER_SCHEMAS, } SUPPORTED_PLATFORMS = tuple(DISCOVERY_SCHEMAS) diff --git a/homeassistant/components/matter/icons.json b/homeassistant/components/matter/icons.json index f9217cabcc4a1..5faf41a840bdb 100644 --- a/homeassistant/components/matter/icons.json +++ b/homeassistant/components/matter/icons.json @@ -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" }, diff --git a/homeassistant/components/matter/select.py b/homeassistant/components/matter/select.py index b2d1c7f8ddb44..bf4527c239a81 100644 --- a/homeassistant/components/matter/select.py +++ b/homeassistant/components/matter/select.py @@ -41,6 +41,7 @@ | clusters.DishwasherMode | clusters.EnergyEvseMode | clusters.DeviceEnergyManagementMode + | clusters.WaterHeaterMode ) @@ -318,6 +319,18 @@ def _update_from_device(self) -> None: # don't discover this entry if the supported modes list is empty secondary_value_is_not=[], ), + MatterDiscoverySchema( + platform=Platform.SELECT, + entity_description=MatterSelectEntityDescription( + key="MatterWaterHeaterMode", + translation_key="mode", + ), + entity_class=MatterModeSelectEntity, + required_attributes=( + clusters.WaterHeaterMode.Attributes.CurrentMode, + clusters.WaterHeaterMode.Attributes.SupportedModes, + ), + ), MatterDiscoverySchema( platform=Platform.SELECT, entity_description=MatterSelectEntityDescription( diff --git a/homeassistant/components/matter/sensor.py b/homeassistant/components/matter/sensor.py index 3503e112db57e..dda05312888da 100644 --- a/homeassistant/components/matter/sensor.py +++ b/homeassistant/components/matter/sensor.py @@ -37,6 +37,7 @@ UnitOfPower, UnitOfPressure, UnitOfTemperature, + UnitOfVolume, UnitOfVolumeFlowRate, ) from homeassistant.core import HomeAssistant, callback @@ -65,7 +66,6 @@ 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 @@ 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, +} + async def async_setup_entry( hass: HomeAssistant, @@ -904,4 +910,44 @@ def _update_from_device(self) -> None: # don't discover this entry if the supported state list is empty secondary_value_is_not=[], ), + 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, + ), + ), ] diff --git a/homeassistant/components/matter/strings.json b/homeassistant/components/matter/strings.json index f299b5cb628e6..558fd656e2b15 100644 --- a/homeassistant/components/matter/strings.json +++ b/homeassistant/components/matter/strings.json @@ -76,6 +76,9 @@ }, "muted": { "name": "Muted" + }, + "boost_state": { + "name": "Boost state" } }, "button": { @@ -220,6 +223,9 @@ }, "laundry_washer_spin_speed": { "name": "Spin speed" + }, + "water_heater_mode": { + "name": "Water heater mode" } }, "sensor": { @@ -270,6 +276,15 @@ "switch_current_position": { "name": "Current switch position" }, + "tank_volume": { + "name": "Tank volume" + }, + "tank_percentage": { + "name": "Tank percentage" + }, + "estimated_heat_required": { + "name": "Required heating energy" + }, "valve_position": { "name": "Valve position" }, @@ -300,6 +315,11 @@ "valve": { "name": "[%key:component::valve::title%]" } + }, + "water_heater": { + "water_heater": { + "name": "Water heater" + } } }, "issues": { diff --git a/homeassistant/components/matter/water_heater.py b/homeassistant/components/matter/water_heater.py new file mode 100644 index 0000000000000..ce0a7bbb9c3bf --- /dev/null +++ b/homeassistant/components/matter/water_heater.py @@ -0,0 +1,209 @@ +"""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.exceptions import ServiceValidationError +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .entity import MatterEntity +from .helpers import get_matter +from .models import MatterDiscoverySchema + +SUPPORT_FLAGS_HEATER = ( + WaterHeaterEntityFeature.TARGET_TEMPERATURE + | WaterHeaterEntityFeature.ON_OFF + | WaterHeaterEntityFeature.OPERATION_MODE +) +TEMPERATURE_SCALING_FACTOR = 100 + +WATER_HEATER_SYSTEM_MODE_MAP = { + STATE_ECO: 4, + STATE_HIGH_DEMAND: 4, + STATE_OFF: 0, +} + +BOOST_STATE_MAP = { + clusters.WaterHeaterManagement.Enums.BoostStateEnum.kInactive: "inactive", + clusters.WaterHeaterManagement.Enums.BoostStateEnum.kActive: "active", + clusters.WaterHeaterManagement.Enums.BoostStateEnum.kUnknownEnumValue: None, +} + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> 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 temperatures.""" + target_temperature: float | None = kwargs.get(ATTR_TEMPERATURE) + self._attr_target_temperature = target_temperature + if target_temperature is not None: + if self.target_temperature != target_temperature: + matter_attribute = ( + clusters.Thermostat.Attributes.OccupiedHeatingSetpoint + ) + await self.write_attribute( + value=int(target_temperature * TEMPERATURE_SCALING_FACTOR), + matter_attribute=matter_attribute, + ) + return + + 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.get(operation_mode) + if system_mode_value is None: + raise ServiceValidationError( + f"Unsupported hvac mode {operation_mode} in Matter" + ) + 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._calculate_features() + 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 + ), + ) + self._attr_target_temperature_low = self._attr_min_temp + self._attr_target_temperature_high = self._attr_max_temp + + @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): + 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 + ), +] diff --git a/tests/components/matter/conftest.py b/tests/components/matter/conftest.py index d7429f6087d33..5b7d7a3f7c8a8 100644 --- a/tests/components/matter/conftest.py +++ b/tests/components/matter/conftest.py @@ -105,6 +105,7 @@ async def integration_fixture( "room_airconditioner", "silabs_dishwasher", "silabs_laundrywasher", + "silabs_water_heater", "smoke_detector", "switch_unit", "temperature_sensor", diff --git a/tests/components/matter/fixtures/nodes/silabs_water_heater.json b/tests/components/matter/fixtures/nodes/silabs_water_heater.json new file mode 100644 index 0000000000000..b0cdf8542c2fe --- /dev/null +++ b/tests/components/matter/fixtures/nodes/silabs_water_heater.json @@ -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": 0, + "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": [] +} diff --git a/tests/components/matter/snapshots/test_binary_sensor.ambr b/tests/components/matter/snapshots/test_binary_sensor.ambr index 82dcc166f13d6..6b781cc1f53a3 100644 --- a/tests/components/matter/snapshots/test_binary_sensor.ambr +++ b/tests/components/matter/snapshots/test_binary_sensor.ambr @@ -375,6 +375,52 @@ 'state': 'off', }) # --- +# 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': , + 'device_class': None, + 'device_id': , + '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': , + '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': , + 'entity_id': 'binary_sensor.water_heater_boost_state', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- # name: test_binary_sensors[smoke_detector][binary_sensor.smoke_sensor_battery_alert-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/matter/snapshots/test_select.ambr b/tests/components/matter/snapshots/test_select.ambr index d7ddf636ff961..3b371eba342eb 100644 --- a/tests/components/matter/snapshots/test_select.ambr +++ b/tests/components/matter/snapshots/test_select.ambr @@ -1689,6 +1689,124 @@ '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': , + 'device_class': None, + 'device_id': , + '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': , + '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': , + 'entity_id': 'select.water_heater_energy_management_mode', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'No energy management (forecast only)', + }) +# --- +# name: test_selects[silabs_water_heater][select.water_heater_mode-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'Off', + 'Manual', + 'Timed', + ]), + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.water_heater_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Mode', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'mode', + 'unique_id': '00000000000004D2-0000000000000019-MatterNodeDevice-2-MatterWaterHeaterMode-158-1', + 'unit_of_measurement': None, + }) +# --- +# name: test_selects[silabs_water_heater][select.water_heater_mode-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Water Heater Mode', + 'options': list([ + 'Off', + 'Manual', + 'Timed', + ]), + }), + 'context': , + 'entity_id': 'select.water_heater_mode', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'Off', + }) +# --- # name: test_selects[switch_unit][select.mock_switchunit_power_on_behavior_on_startup-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/matter/snapshots/test_sensor.ambr b/tests/components/matter/snapshots/test_sensor.ambr index 541f1bc178f27..50c5922e82084 100644 --- a/tests/components/matter/snapshots/test_sensor.ambr +++ b/tests/components/matter/snapshots/test_sensor.ambr @@ -3159,6 +3159,335 @@ 'state': '120.0', }) # --- +# name: test_sensors[silabs_water_heater][sensor.water_heater_current-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.water_heater_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + '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': , + }) +# --- +# name: test_sensors[silabs_water_heater][sensor.water_heater_current-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'current', + 'friendly_name': 'Water Heater Current', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.water_heater_current', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.1', + }) +# --- +# name: test_sensors[silabs_water_heater][sensor.water_heater_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.water_heater_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + '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': , + }) +# --- +# name: test_sensors[silabs_water_heater][sensor.water_heater_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'Water Heater Power', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.water_heater_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + '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': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.water_heater_required_heating_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + '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': , + }) +# --- +# 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': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.water_heater_required_heating_energy', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '4.0', + }) +# --- +# name: test_sensors[silabs_water_heater][sensor.water_heater_tank_percentage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.water_heater_tank_percentage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Tank percentage', + '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_tank_percentage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Water Heater Tank percentage', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.water_heater_tank_percentage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '40', + }) +# --- +# name: test_sensors[silabs_water_heater][sensor.water_heater_tank_volume-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + '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': , + }) +# --- +# 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': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.water_heater_tank_volume', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '200', + }) +# --- +# name: test_sensors[silabs_water_heater][sensor.water_heater_voltage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.water_heater_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + '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': , + }) +# --- +# name: test_sensors[silabs_water_heater][sensor.water_heater_voltage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'voltage', + 'friendly_name': 'Water Heater Voltage', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.water_heater_voltage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '230.0', + }) +# --- # name: test_sensors[smoke_detector][sensor.smoke_sensor_battery-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/matter/snapshots/test_water_heater.ambr b/tests/components/matter/snapshots/test_water_heater.ambr new file mode 100644 index 0000000000000..3d01012e287fd --- /dev/null +++ b/tests/components/matter/snapshots/test_water_heater.ambr @@ -0,0 +1,68 @@ +# 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': , + 'device_class': None, + 'device_id': , + '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': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': , + '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': , + 'target_temp_high': 65, + 'target_temp_low': 40, + 'temperature': 65, + }), + 'context': , + 'entity_id': 'water_heater.water_heater', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'eco', + }) +# --- diff --git a/tests/components/matter/test_binary_sensor.py b/tests/components/matter/test_binary_sensor.py index cddee975ac8d6..a4d221de1fdd7 100644 --- a/tests/components/matter/test_binary_sensor.py +++ b/tests/components/matter/test_binary_sensor.py @@ -147,3 +147,23 @@ async def test_optional_sensor_from_featuremap( ) state = hass.states.get(entity_id) assert state is None + + +@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" diff --git a/tests/components/matter/test_sensor.py b/tests/components/matter/test_sensor.py index 251aab73e3bce..105decc3c3db0 100644 --- a/tests/components/matter/test_sensor.py +++ b/tests/components/matter/test_sensor.py @@ -399,3 +399,47 @@ async def test_list_sensor( state = hass.states.get("sensor.laundrywasher_current_phase") assert state assert state.state == "rinse" + + +@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_tank_percentage") + 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_tank_percentage") + assert state + assert state.state == "50" diff --git a/tests/components/matter/test_water_heater.py b/tests/components/matter/test_water_heater.py new file mode 100644 index 0000000000000..5312385d90734 --- /dev/null +++ b/tests/components/matter/test_water_heater.py @@ -0,0 +1,62 @@ +"""Test Matter sensors.""" + +from unittest.mock import MagicMock + +from matter_server.client.models.node import MatterNode +import pytest +from syrupy import SnapshotAssertion + +from homeassistant.components.water_heater import ( + STATE_ECO, + STATE_HIGH_DEMAND, + 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 sensor.""" + 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 + + # test BoostState update from device + set_node_attribute(matter_node, 2, 148, 5, 1) + await trigger_subscription_callback(hass, matter_client) + state = hass.states.get("water_heater.water_heater") + assert state.state == STATE_HIGH_DEMAND