mirror of
https://github.com/home-assistant/core.git
synced 2026-09-05 13:02:34 +01:00
Fix TemperatureOffset native min/max unit conversion for Matter thermostats (#180172)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
456582ed2d
commit
3cf056ddb2
@@ -308,7 +308,8 @@ DISCOVERY_SCHEMAS = [
|
||||
),
|
||||
featuremap_contains=(clusters.Thermostat.Bitmaps.Feature.kSetback),
|
||||
),
|
||||
# Eve temperature offset with higher min/max
|
||||
# Eve temperature offset; shares the bounds of the generic schema below
|
||||
# and will be merged into it under that schema's unique ID (follow-up PR)
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.NUMBER,
|
||||
entity_description=MatterNumberEntityDescription(
|
||||
@@ -316,9 +317,13 @@ DISCOVERY_SCHEMAS = [
|
||||
device_class=NumberDeviceClass.TEMPERATURE,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
translation_key="temperature_offset",
|
||||
native_max_value=50,
|
||||
native_min_value=-50,
|
||||
native_step=0.5,
|
||||
# Matter 1.4 raises this to the SignedTemperature type's usable
|
||||
# range (±127 in 0.1°C units; -128 is reserved). Matter 1.3 is
|
||||
# limited to ±2.5°C; that will be enforced via cluster_revision
|
||||
# filtering in a follow-up PR.
|
||||
native_max_value=12.7,
|
||||
native_min_value=-12.7,
|
||||
native_step=0.1,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_to_ha=lambda x: None if x is None else x / 10,
|
||||
ha_to_device=lambda x: round(x * 10),
|
||||
@@ -337,9 +342,13 @@ DISCOVERY_SCHEMAS = [
|
||||
device_class=NumberDeviceClass.TEMPERATURE,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
translation_key="temperature_offset",
|
||||
native_max_value=25, # Matter 1.3 limit
|
||||
native_min_value=-25, # Matter 1.3 limit
|
||||
native_step=0.5,
|
||||
# Matter 1.4 raises this to the SignedTemperature type's usable
|
||||
# range (±127 in 0.1°C units; -128 is reserved). Matter 1.3 is
|
||||
# limited to ±2.5°C; that will be enforced via cluster_revision
|
||||
# filtering in a follow-up PR.
|
||||
native_max_value=12.7,
|
||||
native_min_value=-12.7,
|
||||
native_step=0.1,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_to_ha=lambda x: None if x is None else x / 10,
|
||||
ha_to_device=lambda x: round(x * 10),
|
||||
|
||||
@@ -422,10 +422,10 @@
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 25,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -25,
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 12.7,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -12.7,
|
||||
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.BOX: 'box'>,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.5,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
@@ -462,10 +462,10 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Floor Heating Thermostat Temperature offset',
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 25,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -25,
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 12.7,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -12.7,
|
||||
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.BOX: 'box'>,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.5,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.1,
|
||||
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
@@ -720,10 +720,10 @@
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 50,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -50,
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 12.7,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -12.7,
|
||||
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.BOX: 'box'>,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.5,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
@@ -760,10 +760,10 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Eve Thermo 20EBP1701 Temperature offset',
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 50,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -50,
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 12.7,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -12.7,
|
||||
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.BOX: 'box'>,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.5,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.1,
|
||||
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
@@ -781,10 +781,10 @@
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 50,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -50,
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 12.7,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -12.7,
|
||||
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.BOX: 'box'>,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.5,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
@@ -821,10 +821,10 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Eve Thermo 20ECD1701 Temperature offset',
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 50,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -50,
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 12.7,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -12.7,
|
||||
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.BOX: 'box'>,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.5,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.1,
|
||||
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
@@ -4654,10 +4654,10 @@
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 25,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -25,
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 12.7,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -12.7,
|
||||
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.BOX: 'box'>,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.5,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
@@ -4694,10 +4694,10 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'temperature',
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Mock Thermostat Temperature offset',
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 25,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -25,
|
||||
<NumberEntityCapabilityAttribute.MAX: 'max'>: 12.7,
|
||||
<NumberEntityCapabilityAttribute.MIN: 'min'>: -12.7,
|
||||
<NumberEntityCapabilityAttribute.MODE: 'mode'>: <NumberMode.BOX: 'box'>,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.5,
|
||||
<NumberEntityCapabilityAttribute.STEP: 'step'>: 0.1,
|
||||
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
|
||||
@@ -339,6 +339,43 @@ async def test_thermostat_occupied_setback(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["aqara_thermostat_w500"])
|
||||
async def test_thermostat_temperature_offset(
|
||||
hass: HomeAssistant,
|
||||
matter_client: MagicMock,
|
||||
matter_node: MatterNode,
|
||||
) -> None:
|
||||
"""Test thermostat temperature offset number entity."""
|
||||
|
||||
entity_id = "number.floor_heating_thermostat_temperature_offset"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == "0.0"
|
||||
|
||||
# Setting value to 0.3 °C writes 3 to LocalTemperatureCalibration (scale x10),
|
||||
# a value only reachable with the 0.1 °C step
|
||||
await hass.services.async_call(
|
||||
"number",
|
||||
"set_value",
|
||||
{
|
||||
"entity_id": entity_id,
|
||||
"value": 0.3,
|
||||
},
|
||||
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=1,
|
||||
attribute=clusters.Thermostat.Attributes.LocalTemperatureCalibration,
|
||||
),
|
||||
value=3,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["aqara_multi_state_p100"])
|
||||
async def test_boolean_state_configuration_current_sensitivity_level(
|
||||
hass: HomeAssistant,
|
||||
|
||||
Reference in New Issue
Block a user