Skip to content
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

Matter TemperatureNumber feature from TemperatureControl cluster #128838

Open
wants to merge 56 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
5010f44
Create temperature_control.py
lboue Oct 20, 2024
ab9c7a0
Update temperature_control.py
lboue Oct 20, 2024
3dcfd71
Update temperature_control.py
lboue Oct 20, 2024
41615a0
Update temperature_control.py
lboue Oct 20, 2024
3713603
Update temperature_control.py
lboue Oct 20, 2024
be1cd73
Update temperature_control.py
lboue Oct 20, 2024
1bc8a01
Update temperature_control.py
lboue Oct 20, 2024
ae7d853
ruff-format
lboue Oct 20, 2024
008b5b9
Update temperature_control.py
lboue Oct 20, 2024
ded711e
Update temperature_control.py
lboue Oct 20, 2024
d7aee69
Update temperature_control.py
lboue Oct 20, 2024
70887f9
TemperatureControl SelectedTemperatureLevel
lboue Oct 20, 2024
b857efe
Update climate.py
lboue Oct 20, 2024
46c18ea
TemperatureControlFeature
lboue Oct 20, 2024
a9af663
Delete homeassistant/components/matter/temperature_control.py
lboue Oct 20, 2024
22a4c18
TemperatureControl mode
lboue Oct 20, 2024
2acd72e
StrEnum
lboue Oct 20, 2024
43ef56c
Update climate.py
lboue Oct 20, 2024
564821d
Update climate.py
lboue Oct 20, 2024
7a68c68
Update climate.py
lboue Oct 20, 2024
d5c4f1b
Use feature name instead of feature code
lboue Oct 20, 2024
68bf4df
Update climate.py
lboue Oct 20, 2024
ddca2d4
Update climate.py
lboue Oct 20, 2024
e1c332d
Create silabs_laundrywasher.json
lboue Oct 20, 2024
f0b6716
Update silabs_laundrywasher.json
lboue Oct 20, 2024
8297fd4
Merge branch 'dev' into TemperatureControlled
lboue Dec 13, 2024
35183bc
Update silabs_laundrywasher.json
lboue Jan 3, 2025
fefe07a
Remove the select part
lboue Jan 3, 2025
bbee0ed
Fixture
lboue Jan 3, 2025
d2d4f39
TemperatureControl / TemperatureSetpoint
lboue Jan 4, 2025
59d1391
Delete tests/components/matter/fixtures/nodes/silabs_laundrywasher.json
lboue Jan 4, 2025
ad5de14
Tests for TemperatureControl cluster
lboue Jan 4, 2025
08fa381
Update climate.py
lboue Jan 4, 2025
3e59c9b
Add fixture to conftest
lboue Jan 8, 2025
08c9b4d
Update tests
lboue Jan 8, 2025
b8432cb
Update tests
lboue Jan 8, 2025
72684c2
Update homeassistant/components/matter/climate.py
lboue Jan 25, 2025
f26de81
Update homeassistant/components/matter/climate.py
lboue Jan 25, 2025
b449484
Merge branch 'dev' into TemperatureControlled
lboue Jan 26, 2025
8d0441b
Set TemperatureSetpoint as primary attribute
lboue Jan 26, 2025
0876e57
Only update featuremap if it changes
lboue Jan 26, 2025
a75be25
Change platform_translation_key
lboue Jan 31, 2025
bb145af
Cleanup
lboue Jan 31, 2025
d234416
Mandatory attributes
lboue Jan 31, 2025
b48a8d5
Update tests
lboue Jan 31, 2025
b7e3032
Update from dev
lboue Jan 31, 2025
9e73d30
Merge branch 'dev' into TemperatureControlled
lboue Jan 31, 2025
86860bf
Update snapshots
lboue Jan 31, 2025
231b66f
Update snapshot
lboue Feb 1, 2025
1b8d7f8
Update homeassistant/components/matter/climate.py
lboue Feb 5, 2025
ac3b6bc
Update homeassistant/components/matter/climate.py
lboue Feb 5, 2025
39db882
Update homeassistant/components/matter/climate.py
lboue Feb 5, 2025
c9c24b4
Update homeassistant/components/matter/climate.py
lboue Feb 5, 2025
ae3eb5a
Add comment for hvac_mode
lboue Feb 5, 2025
49f9a75
Add TagList attribute
lboue Feb 7, 2025
495849d
Testing TagList to autodetect device type
lboue Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions homeassistant/components/matter/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,14 @@
(0x1209, 0x8029),
}

kNamespaceRefrigerator = 65
kTagRefrigerator = 0
kTagFreezer = 1

SystemModeEnum = clusters.Thermostat.Enums.SystemModeEnum
ControlSequenceEnum = clusters.Thermostat.Enums.ControlSequenceOfOperationEnum
ThermostatFeature = clusters.Thermostat.Bitmaps.Feature
TemperatureControlFeature = clusters.TemperatureControl.Bitmaps.Feature


class ThermostatRunningState(IntEnum):
Expand Down Expand Up @@ -408,6 +413,99 @@ def _get_temperature_in_degrees(
return None


class MatterTemperatureControlClimate(MatterEntity, ClimateEntity):
"""Representation of a climate entity from the Matter TemperatureControl cluster."""

_attr_temperature_unit: str = UnitOfTemperature.CELSIUS
# We can't set the mode for a TemperatureControl cluster but only set the target temperature.
_attr_hvac_mode: HVACMode = HVACMode.HEAT_COOL
_feature_map: int | None = None

_platform_translation_key = "temperature_control"

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:
if self.target_temperature != target_temperature:
await self.matter_client.send_device_command(
node_id=self._endpoint.node.node_id,
endpoint_id=self._endpoint.endpoint_id,
command=clusters.TemperatureControl.Commands.SetTemperature(
targetTemperature=int(
target_temperature * TEMPERATURE_SCALING_FACTOR
)
),
)
return

@callback
def _update_from_device(self) -> None:
"""Update from device."""
self._calculate_features()
# update TagList
self._attr_tag_list = list(
self.get_matter_attribute_value(clusters.Descriptor.Attributes.TagList)
)
tag_struct: clusters.Descriptor.Structs.SemanticTagStruct = self._attr_tag_list[
0
]
namespace_id = tag_struct.namespaceID
tag = tag_struct.tag
if namespace_id == kNamespaceRefrigerator:
if tag == kTagRefrigerator:
self._attr_name = "Refrigerator"
elif tag == kTagFreezer:
self._attr_name = "Freezer"

lboue marked this conversation as resolved.
Show resolved Hide resolved
# update target_temperature
self._attr_target_temperature = self._get_temperature_in_degrees(
clusters.TemperatureControl.Attributes.TemperatureSetpoint
)
# update min_temp
attribute = clusters.TemperatureControl.Attributes.MinTemperature
if (value := self._get_temperature_in_degrees(attribute)) is not None:
self._attr_min_temp = value
# update max_temp
attribute = clusters.TemperatureControl.Attributes.MaxTemperature
if (value := self._get_temperature_in_degrees(attribute)) is not None:
self._attr_max_temp = value

@callback
def _calculate_features(
self,
) -> None:
"""Calculate features for HA Thermostat platform from Matter FeatureMap."""
feature_map = int(
self.get_matter_attribute_value(
clusters.TemperatureControl.Attributes.FeatureMap
)
)
# NOTE: the featuremap can dynamically change, so we need to update the
# supported features if the featuremap changes.
# work out supported features and presets from matter featuremap
if self._feature_map == feature_map:
return
self._feature_map = feature_map
lboue marked this conversation as resolved.
Show resolved Hide resolved
self._attr_hvac_modes: list[HVACMode] = [HVACMode.HEAT_COOL]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment above this line that you can not set the mode for a TemperatureControl cluster, you can only set the target temperature.

Also I'm wondering if heat_cool is the right mode here. Can we somewhere determine if this device is either a cooling or heating device ? I think we can derive that from the device type maybe ?
So if we know from the devicetype that its a fridge, we should hardcode this mode to cooling

Copy link
Contributor Author

@lboue lboue Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I set it to HVACMode.HEAT_COOL because TemperatureControlledCabinet device type can have cooling/heating functionality:
https://github.com/project-chip/connectedhomeip/blob/master/data_model/1.4/device_types/TemperatureControlledCabinet.xml

Copy link
Contributor Author

@lboue lboue Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difficulty here is that it concerns a different EP. In the case of a refrigerator (fridge + freezer) there is 3 EP:

  1. Endpoint 1: Device Type(s): Refrigerator
  2. Endpoint 2: Device Type(s): Temperature Controlled Cabinet
  3. Endpoint 3: Device Type(s): Temperature Controlled Cabinet

image

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes you should look at root endpoint if its a refrigerator

Copy link
Contributor Author

@lboue lboue Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EP0 device type is 'Root Node' so we can't use that trick:

  "attributes": {
    "0/29/0": [
      {
        "0": 22,
        "1": 1
      }
    ],

Copy link
Contributor Author

@lboue lboue Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the specs :
24-27351-005_Matter-1.4-Device-Library-Specification.pdf (page 121)

image

So maybe with TagList attribute of the Descriptor cluster.

if feature_map & TemperatureControlFeature.kTemperatureNumber:
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
if feature_map & TemperatureControlFeature.kTemperatureStep:
self._attr_target_temperature_step = self._get_temperature_in_degrees(
clusters.TemperatureControl.Attributes.Step
)

@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(
Expand Down Expand Up @@ -435,4 +533,22 @@ def _get_temperature_in_degrees(
device_type=(device_types.Thermostat, device_types.RoomAirConditioner),
allow_multi=True, # also used for sensor entity
),
MatterDiscoverySchema(
platform=Platform.CLIMATE,
entity_description=ClimateEntityDescription(
key="MatterTemperatureControl",
name=None,
),
entity_class=MatterTemperatureControlClimate,
required_attributes=(
clusters.TemperatureControl.Attributes.TemperatureSetpoint,
clusters.TemperatureControl.Attributes.FeatureMap,
lboue marked this conversation as resolved.
Show resolved Hide resolved
clusters.TemperatureControl.Attributes.MinTemperature,
clusters.TemperatureControl.Attributes.MaxTemperature,
lboue marked this conversation as resolved.
Show resolved Hide resolved
),
optional_attributes=(
clusters.TemperatureControl.Attributes.Step,
clusters.Descriptor.Attributes.TagList,
),
),
]
3 changes: 3 additions & 0 deletions homeassistant/components/matter/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@
}
},
"climate": {
"temperature_control": {
"name": "Temperature control"
},
"thermostat": {
"name": "Thermostat"
}
Expand Down
1 change: 1 addition & 0 deletions tests/components/matter/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ async def integration_fixture(
"room_airconditioner",
"silabs_dishwasher",
"silabs_laundrywasher",
"silabs_refrigerator",
"smoke_detector",
"switch_unit",
"temperature_sensor",
Expand Down
Loading
Loading