From bf5cc22bef3a65e278056dd94b51c78e5089272c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 30 Apr 2020 13:34:25 -0500 Subject: [PATCH] Fix preservation of homekit fan speed on toggle (#34971) --- homeassistant/components/homekit/type_fans.py | 4 ++- homeassistant/components/homekit/util.py | 2 +- tests/components/homekit/test_type_fans.py | 26 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/homekit/type_fans.py b/homeassistant/components/homekit/type_fans.py index b7208b1746c..291b3ffed90 100644 --- a/homeassistant/components/homekit/type_fans.py +++ b/homeassistant/components/homekit/type_fans.py @@ -165,7 +165,9 @@ class Fan(HomeAccessory): self.char_direction.set_value(hk_direction) # Handle Speed - if self.char_speed is not None: + if self.char_speed is not None and state != STATE_OFF: + # We do not change the homekit speed when turning off + # as it will clear the restore state speed = new_state.attributes.get(ATTR_SPEED) hk_speed_value = self.speed_mapping.speed_to_homekit(speed) if hk_speed_value is not None and self.char_speed.value != hk_speed_value: diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index 0295440df49..b8d98ad2304 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -200,7 +200,7 @@ class HomeKitSpeedMapping: if speed is None: return None speed_range = self.speed_ranges[speed] - return speed_range.target + return round(speed_range.target) def speed_to_states(self, speed): """Map HomeKit speed to Home Assistant speed state.""" diff --git a/tests/components/homekit/test_type_fans.py b/tests/components/homekit/test_type_fans.py index ca6e03217f3..4d2ace24eab 100644 --- a/tests/components/homekit/test_type_fans.py +++ b/tests/components/homekit/test_type_fans.py @@ -304,6 +304,7 @@ async def test_fan_speed(hass, hk_driver, cls, events): call_set_speed = async_mock_service(hass, DOMAIN, "set_speed") char_speed_iid = acc.char_speed.to_HAP()[HAP_REPR_IID] + char_active_iid = acc.char_active.to_HAP()[HAP_REPR_IID] hk_driver.set_characteristics( { @@ -320,12 +321,37 @@ async def test_fan_speed(hass, hk_driver, cls, events): await hass.async_add_executor_job(acc.char_speed.client_update_value, 42) await hass.async_block_till_done() acc.speed_mapping.speed_to_states.assert_called_with(42) + assert acc.char_speed.value == 42 + assert acc.char_active.value == 1 + assert call_set_speed[0] assert call_set_speed[0].data[ATTR_ENTITY_ID] == entity_id assert call_set_speed[0].data[ATTR_SPEED] == "ludicrous" assert len(events) == 1 assert events[-1].data[ATTR_VALUE] == "ludicrous" + # Verify speed is preserved from off to on + hass.states.async_set(entity_id, STATE_OFF, {ATTR_SPEED: SPEED_OFF}) + await hass.async_block_till_done() + assert acc.char_speed.value == 42 + assert acc.char_active.value == 0 + + hk_driver.set_characteristics( + { + HAP_REPR_CHARS: [ + { + HAP_REPR_AID: acc.aid, + HAP_REPR_IID: char_active_iid, + HAP_REPR_VALUE: 1, + }, + ] + }, + "mock_addr", + ) + await hass.async_block_till_done() + assert acc.char_speed.value == 42 + assert acc.char_active.value == 1 + async def test_fan_set_all_one_shot(hass, hk_driver, cls, events): """Test fan with speed."""