-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for Matter 1.4 Water Heater #131505
Open
lboue
wants to merge
70
commits into
home-assistant:dev
Choose a base branch
from
lboue:WaterHeater
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,527
−1
Open
Changes from 65 commits
Commits
Show all changes
70 commits
Select commit
Hold shift + click to select a range
04fdb50
Create water_heater.json
lboue bf1de2c
Update water_heater.json
lboue c84ea01
Update water_heater.json
lboue 6bbef11
TankVolume
lboue 9e053dc
TankPercentage
lboue 41128e6
WaterHeaterMode
lboue 091e867
Update sensor.py
lboue 52769b9
ruff-format
lboue aa14636
Update water_heater.json
lboue 7d3d677
Update test_sensor.py
lboue 7fbb10d
Update test_sensor.py
lboue 1e95f44
SensorDeviceClass=VOLUME_STORAGE for `TankVolume`
lboue 1beb6bd
`BoostStateEnum` map
lboue 395233a
WaterHeaterManagementBoostState
lboue 72b8399
Update sensor.py
lboue 6e7cf99
WaterHeaterManagementEstimatedHeatRequired
lboue b0c9f20
Fix UnitOfEnergy
lboue 152f2f6
Format
lboue 1df29b0
Add `device_types.WaterHeater` to Climate
lboue 2b3ebdc
Strings for Tank sensors
lboue 3881f08
WaterHeater icons
lboue b281f97
Update icons.json
lboue 6a2f4ce
Update strings.json
lboue bca6658
Update water_heater.json
lboue 54c6ff7
ruff-format
lboue e50399e
Merge branch 'dev' into WaterHeater
lboue 6eb3ad7
Fix tests
lboue d5fe4e1
Fix sensor.py
lboue 67e9d18
Fix icons
lboue b7c1d81
WaterHeaterManagementEstimatedHeatRequired
lboue 3173f4f
WaterHeaterManagementBoostState
lboue 01a0f80
BoostState as a binary sensor
lboue 7736015
ElectricalPowerMeasurement values
lboue 60aff94
Fix tests
lboue 3c50c53
Create water_heater.py
lboue b234d9c
Update climate.py from dev branch
lboue a065876
Resolve conflicts
lboue 77607c0
Merge branch 'dev' into WaterHeater
lboue bdfb9fc
ruff-format
lboue f4b6358
Add Platform.WATER_HEATER
lboue cf98fb5
Merge branch 'dev' into WaterHeater
lboue fa2ef33
Update water_heater.py
lboue 49e27c1
Update water_heater.py
lboue eb1aaa3
Update water_heater.py
lboue ce9b646
Update water_heater.py
lboue 6639cfe
Merge branch 'dev' into WaterHeater
lboue a754995
Add WaterHeaterManagement sensors
lboue 792d51f
Update tests
lboue b573d9e
Add select test
lboue a6f5b24
Add strings
lboue a342d05
First try with water_heater
lboue 93a2f14
Testing current_operation
lboue 9eb0f73
BoostState attribute
lboue ed3150f
target_temperature attributes
lboue bbb6e97
target_temperature attribute
lboue 79b01c4
set_temperature and set_operation_mode
lboue 7de0c5d
turn_on / turn_off
lboue 5e75311
Trigger Boost command
lboue f7ee6ea
Fix WaterHeaterBoostInfoStruct
lboue 7e722e2
Add test file
lboue 8111064
Add climate cluster to fixture
lboue 7a49667
Add climate cluster to fixture
lboue 5126b37
Add tests
lboue 1fd713b
Add ON_OFF feature
lboue 97883b2
Update tests
lboue 344978b
Update tests
lboue 26b8c1d
Translate WaterHeaterMode
lboue 8996a94
Change description
lboue e4fa5b2
Update test and snapshots
lboue 79a359c
Update snapshots
lboue File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
"""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 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) | ||
boostInfo: 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 ValueError(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=boostInfo) | ||
) | ||
|
||
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 | ||
) | ||
BoostState = self.get_matter_attribute_value( | ||
clusters.WaterHeaterManagement.Attributes.BoostState | ||
) | ||
if (BoostState) == 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", | ||
), | ||
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 | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct me if I'm wrong:
If that is the estimated energy needed to heat the water that should be "Required heating energy"
"heat energy" sounds more like the energy contained in hot water.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you. I will fix it.