mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Allow LevelControl Cluster for Matter Pump devices (#145004)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
8ca1fe83b7
commit
a748525e03
@ -8,6 +8,7 @@ from typing import Any, cast
|
|||||||
|
|
||||||
from chip.clusters import Objects as clusters
|
from chip.clusters import Objects as clusters
|
||||||
from chip.clusters.ClusterObjects import ClusterAttributeDescriptor, ClusterCommand
|
from chip.clusters.ClusterObjects import ClusterAttributeDescriptor, ClusterCommand
|
||||||
|
from matter_server.client.models import device_types
|
||||||
from matter_server.common import custom_clusters
|
from matter_server.common import custom_clusters
|
||||||
|
|
||||||
from homeassistant.components.number import (
|
from homeassistant.components.number import (
|
||||||
@ -18,6 +19,7 @@ from homeassistant.components.number import (
|
|||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
PERCENTAGE,
|
||||||
EntityCategory,
|
EntityCategory,
|
||||||
Platform,
|
Platform,
|
||||||
UnitOfLength,
|
UnitOfLength,
|
||||||
@ -123,6 +125,31 @@ class MatterRangeNumber(MatterEntity, NumberEntity):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatterLevelControlNumber(MatterEntity, NumberEntity):
|
||||||
|
"""Representation of a Matter Attribute as a Number entity."""
|
||||||
|
|
||||||
|
entity_description: MatterNumberEntityDescription
|
||||||
|
|
||||||
|
async def async_set_native_value(self, value: float) -> None:
|
||||||
|
"""Set level value."""
|
||||||
|
send_value = int(value)
|
||||||
|
if value_convert := self.entity_description.ha_to_native_value:
|
||||||
|
send_value = value_convert(value)
|
||||||
|
await self.send_device_command(
|
||||||
|
clusters.LevelControl.Commands.MoveToLevel(
|
||||||
|
level=send_value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _update_from_device(self) -> None:
|
||||||
|
"""Update from device."""
|
||||||
|
value = self.get_matter_attribute_value(self._entity_info.primary_attribute)
|
||||||
|
if value_convert := self.entity_description.measurement_to_ha:
|
||||||
|
value = value_convert(value)
|
||||||
|
self._attr_native_value = value
|
||||||
|
|
||||||
|
|
||||||
# Discovery schema(s) to map Matter Attributes to HA entities
|
# Discovery schema(s) to map Matter Attributes to HA entities
|
||||||
DISCOVERY_SCHEMAS = [
|
DISCOVERY_SCHEMAS = [
|
||||||
MatterDiscoverySchema(
|
MatterDiscoverySchema(
|
||||||
@ -239,6 +266,26 @@ DISCOVERY_SCHEMAS = [
|
|||||||
),
|
),
|
||||||
vendor_id=(4874,),
|
vendor_id=(4874,),
|
||||||
),
|
),
|
||||||
|
MatterDiscoverySchema(
|
||||||
|
platform=Platform.NUMBER,
|
||||||
|
entity_description=MatterNumberEntityDescription(
|
||||||
|
key="pump_setpoint",
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
translation_key="pump_setpoint",
|
||||||
|
native_max_value=100,
|
||||||
|
native_min_value=0.5,
|
||||||
|
native_step=0.5,
|
||||||
|
measurement_to_ha=(
|
||||||
|
lambda x: None if x is None else x / 2 # Matter range (1-200)
|
||||||
|
),
|
||||||
|
ha_to_native_value=lambda x: round(x * 2), # HA range 0.5–100.0%
|
||||||
|
mode=NumberMode.SLIDER,
|
||||||
|
),
|
||||||
|
entity_class=MatterLevelControlNumber,
|
||||||
|
required_attributes=(clusters.LevelControl.Attributes.CurrentLevel,),
|
||||||
|
device_type=(device_types.Pump,),
|
||||||
|
allow_multi=True,
|
||||||
|
),
|
||||||
MatterDiscoverySchema(
|
MatterDiscoverySchema(
|
||||||
platform=Platform.NUMBER,
|
platform=Platform.NUMBER,
|
||||||
entity_description=MatterNumberEntityDescription(
|
entity_description=MatterNumberEntityDescription(
|
||||||
|
@ -180,6 +180,9 @@
|
|||||||
"altitude": {
|
"altitude": {
|
||||||
"name": "Altitude above sea level"
|
"name": "Altitude above sea level"
|
||||||
},
|
},
|
||||||
|
"pump_setpoint": {
|
||||||
|
"name": "Setpoint"
|
||||||
|
},
|
||||||
"temperature_offset": {
|
"temperature_offset": {
|
||||||
"name": "Temperature offset"
|
"name": "Temperature offset"
|
||||||
},
|
},
|
||||||
|
@ -1961,6 +1961,64 @@
|
|||||||
'state': '0',
|
'state': '0',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
# name: test_numbers[pump][number.mock_pump_setpoint-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'max': 100,
|
||||||
|
'min': 0.5,
|
||||||
|
'mode': <NumberMode.SLIDER: 'slider'>,
|
||||||
|
'step': 0.5,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'number',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'number.mock_pump_setpoint',
|
||||||
|
'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': 'Setpoint',
|
||||||
|
'platform': 'matter',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'pump_setpoint',
|
||||||
|
'unique_id': '00000000000004D2-0000000000000003-MatterNodeDevice-1-pump_setpoint-8-0',
|
||||||
|
'unit_of_measurement': '%',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_numbers[pump][number.mock_pump_setpoint-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mock Pump Setpoint',
|
||||||
|
'max': 100,
|
||||||
|
'min': 0.5,
|
||||||
|
'mode': <NumberMode.SLIDER: 'slider'>,
|
||||||
|
'step': 0.5,
|
||||||
|
'unit_of_measurement': '%',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'number.mock_pump_setpoint',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '127.0',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
# name: test_numbers[silabs_laundrywasher][number.laundrywasher_temperature_setpoint-entry]
|
# name: test_numbers[silabs_laundrywasher][number.laundrywasher_temperature_setpoint-entry]
|
||||||
EntityRegistryEntrySnapshot({
|
EntityRegistryEntrySnapshot({
|
||||||
'aliases': set({
|
'aliases': set({
|
||||||
|
@ -160,3 +160,44 @@ async def test_matter_exception_on_write_attribute(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("node_fixture", ["pump"])
|
||||||
|
async def test_pump_level(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
matter_client: MagicMock,
|
||||||
|
matter_node: MatterNode,
|
||||||
|
) -> None:
|
||||||
|
"""Test level control for pump."""
|
||||||
|
# CurrentLevel on LevelControl cluster
|
||||||
|
state = hass.states.get("number.mock_pump_setpoint")
|
||||||
|
assert state
|
||||||
|
assert state.state == "127.0"
|
||||||
|
|
||||||
|
set_node_attribute(matter_node, 1, 8, 0, 100)
|
||||||
|
await trigger_subscription_callback(hass, matter_client)
|
||||||
|
state = hass.states.get("number.mock_pump_setpoint")
|
||||||
|
assert state
|
||||||
|
assert state.state == "50.0"
|
||||||
|
|
||||||
|
# test set value
|
||||||
|
await hass.services.async_call(
|
||||||
|
"number",
|
||||||
|
"set_value",
|
||||||
|
{
|
||||||
|
"entity_id": "number.mock_pump_setpoint",
|
||||||
|
"value": 75,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
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=1,
|
||||||
|
command=clusters.LevelControl.Commands.MoveToLevel(
|
||||||
|
level=150
|
||||||
|
), # 75 * 2 = 150, as the value is multiplied by 2 in the HA to native value conversion
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user