Address review feedback on Matter fan PercentSetting fix (#180554)

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Ludovic BOUÉ
2026-08-29 08:20:32 +00:00
committed by Franck Nijhof
co-authored by Claude Sonnet 5
parent 2aa1b43811
commit fde7f6c93e
2 changed files with 42 additions and 8 deletions
+10 -4
View File
@@ -39,6 +39,12 @@ FAN_MODE_MAP_REVERSE = {v: k for k, v in FAN_MODE_MAP.items()}
PRESET_NATURAL_WIND = "natural_wind"
PRESET_SLEEP_WIND = "sleep_wind"
# Some devices report 255 for PercentCurrent instead of their actual speed
# while FanMode is Auto. This is not part of the Matter spec (which says the
# device should keep reporting its real current speed, see spec 4.4.6.1.2 and
# 4.4.6.4), but we need to work around it regardless.
PERCENT_CURRENT_AUTO_QUIRK = 255
async def async_setup_entry(
hass: HomeAssistant,
@@ -207,9 +213,9 @@ class MatterFan(MatterEntity, FanEntity):
current_percent = self.get_matter_attribute_value(
clusters.FanControl.Attributes.PercentCurrent
)
# NOTE that a device may give back 255 as a special value to indicate that
# the speed is under automatic control and not set to a specific value.
self._attr_percentage = None if current_percent == 255 else current_percent
self._attr_percentage = (
None if current_percent == PERCENT_CURRENT_AUTO_QUIRK else current_percent
)
# get preset mode from fan mode (and wind feature if available)
wind_setting = self.get_matter_attribute_value(
@@ -242,7 +248,7 @@ class MatterFan(MatterEntity, FanEntity):
# keep track of the last known mode for turn_on commands without preset
if self._attr_preset_mode is not None:
self._last_known_preset_mode = self._attr_preset_mode
if current_percent and current_percent != 255:
if current_percent and current_percent != PERCENT_CURRENT_AUTO_QUIRK:
self._last_known_percentage = current_percent
@callback
+32 -4
View File
@@ -134,10 +134,11 @@ async def test_fan_turn_on_with_percentage(
attribute_path="1/514/2",
value=50,
)
# test again where percentage is omitted in the service call
# PercentCurrent is 255 (auto) on this fixture, which is not a valid
# value to write back, so it should fall back to the last known preset
# mode instead of blindly replaying the sentinel value.
# test again where percentage is omitted in the service call.
# This fixture's PercentCurrent is 255, a device-specific quirk value
# (not part of the Matter spec) that is not valid to write back, so it
# should fall back to the last known preset mode instead of blindly
# replaying that sentinel value.
matter_client.write_attribute.reset_mock()
await hass.services.async_call(
FAN_DOMAIN,
@@ -153,6 +154,33 @@ async def test_fan_turn_on_with_percentage(
)
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("node_fixture", ["mock_air_purifier"])
async def test_fan_turn_on_replays_last_known_percentage(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test that a bare turn_on replays a real last known percentage."""
entity_id = "fan.mock_air_purifier"
# simulate the device confirming a real (non-quirk) speed
set_node_attribute(matter_node, 1, 514, 3, 70)
await trigger_subscription_callback(hass, matter_client)
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: entity_id},
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="1/514/2",
value=70,
)
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("node_fixture", ["mock_fan"])
async def test_fan_turn_on_with_preset_mode(