Fix command latency in AVM Fritz!SmartHome (#136739)

This commit is contained in:
Michael 2025-01-29 12:55:59 +01:00 committed by GitHub
parent bc2976904e
commit c974251faa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 34 additions and 31 deletions

View File

@ -141,7 +141,7 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity):
await self.async_set_hvac_mode(hvac_mode) await self.async_set_hvac_mode(hvac_mode)
elif target_temp is not None: elif target_temp is not None:
await self.hass.async_add_executor_job( await self.hass.async_add_executor_job(
self.data.set_target_temperature, target_temp self.data.set_target_temperature, target_temp, True
) )
else: else:
return return

View File

@ -71,21 +71,21 @@ class FritzboxCover(FritzBoxDeviceEntity, CoverEntity):
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover.""" """Open the cover."""
await self.hass.async_add_executor_job(self.data.set_blind_open) await self.hass.async_add_executor_job(self.data.set_blind_open, True)
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
async def async_close_cover(self, **kwargs: Any) -> None: async def async_close_cover(self, **kwargs: Any) -> None:
"""Close the cover.""" """Close the cover."""
await self.hass.async_add_executor_job(self.data.set_blind_close) await self.hass.async_add_executor_job(self.data.set_blind_close, True)
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
async def async_set_cover_position(self, **kwargs: Any) -> None: async def async_set_cover_position(self, **kwargs: Any) -> None:
"""Move the cover to a specific position.""" """Move the cover to a specific position."""
await self.hass.async_add_executor_job( await self.hass.async_add_executor_job(
self.data.set_level_percentage, 100 - kwargs[ATTR_POSITION] self.data.set_level_percentage, 100 - kwargs[ATTR_POSITION], True
) )
async def async_stop_cover(self, **kwargs: Any) -> None: async def async_stop_cover(self, **kwargs: Any) -> None:
"""Stop the cover.""" """Stop the cover."""
await self.hass.async_add_executor_job(self.data.set_blind_stop) await self.hass.async_add_executor_job(self.data.set_blind_stop, True)
await self.coordinator.async_refresh() await self.coordinator.async_refresh()

View File

@ -122,7 +122,7 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity):
"""Turn the light on.""" """Turn the light on."""
if kwargs.get(ATTR_BRIGHTNESS) is not None: if kwargs.get(ATTR_BRIGHTNESS) is not None:
level = kwargs[ATTR_BRIGHTNESS] level = kwargs[ATTR_BRIGHTNESS]
await self.hass.async_add_executor_job(self.data.set_level, level) await self.hass.async_add_executor_job(self.data.set_level, level, True)
if kwargs.get(ATTR_HS_COLOR) is not None: if kwargs.get(ATTR_HS_COLOR) is not None:
# Try setunmappedcolor first. This allows free color selection, # Try setunmappedcolor first. This allows free color selection,
# but we don't know if its supported by all devices. # but we don't know if its supported by all devices.
@ -133,7 +133,10 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity):
cast(float, kwargs[ATTR_HS_COLOR][1]) * 255.0 / 100.0 cast(float, kwargs[ATTR_HS_COLOR][1]) * 255.0 / 100.0
) )
await self.hass.async_add_executor_job( await self.hass.async_add_executor_job(
self.data.set_unmapped_color, (unmapped_hue, unmapped_saturation) self.data.set_unmapped_color,
(unmapped_hue, unmapped_saturation),
0,
True,
) )
# This will raise 400 BAD REQUEST if the setunmappedcolor is not available # This will raise 400 BAD REQUEST if the setunmappedcolor is not available
except HTTPError as err: except HTTPError as err:
@ -152,18 +155,18 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity):
key=lambda x: abs(x - unmapped_saturation), key=lambda x: abs(x - unmapped_saturation),
) )
await self.hass.async_add_executor_job( await self.hass.async_add_executor_job(
self.data.set_color, (hue, saturation) self.data.set_color, (hue, saturation), 0, True
) )
if kwargs.get(ATTR_COLOR_TEMP_KELVIN) is not None: if kwargs.get(ATTR_COLOR_TEMP_KELVIN) is not None:
await self.hass.async_add_executor_job( await self.hass.async_add_executor_job(
self.data.set_color_temp, kwargs[ATTR_COLOR_TEMP_KELVIN] self.data.set_color_temp, kwargs[ATTR_COLOR_TEMP_KELVIN], 0, True
) )
await self.hass.async_add_executor_job(self.data.set_state_on) await self.hass.async_add_executor_job(self.data.set_state_on, True)
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off.""" """Turn the light off."""
await self.hass.async_add_executor_job(self.data.set_state_off) await self.hass.async_add_executor_job(self.data.set_state_off, True)
await self.coordinator.async_refresh() await self.coordinator.async_refresh()

View File

@ -51,13 +51,13 @@ class FritzboxSwitch(FritzBoxDeviceEntity, SwitchEntity):
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on.""" """Turn the switch on."""
self.check_lock_state() self.check_lock_state()
await self.hass.async_add_executor_job(self.data.set_switch_state_on) await self.hass.async_add_executor_job(self.data.set_switch_state_on, True)
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off.""" """Turn the switch off."""
self.check_lock_state() self.check_lock_state()
await self.hass.async_add_executor_job(self.data.set_switch_state_off) await self.hass.async_add_executor_job(self.data.set_switch_state_off, True)
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
def check_lock_state(self) -> None: def check_lock_state(self) -> None:

View File

@ -273,20 +273,20 @@ async def test_update_error(hass: HomeAssistant, fritz: Mock) -> None:
@pytest.mark.parametrize( @pytest.mark.parametrize(
("service_data", "expected_call_args"), ("service_data", "expected_call_args"),
[ [
({ATTR_TEMPERATURE: 23}, [call(23)]), ({ATTR_TEMPERATURE: 23}, [call(23, True)]),
( (
{ {
ATTR_HVAC_MODE: HVACMode.OFF, ATTR_HVAC_MODE: HVACMode.OFF,
ATTR_TEMPERATURE: 23, ATTR_TEMPERATURE: 23,
}, },
[call(0)], [call(0, True)],
), ),
( (
{ {
ATTR_HVAC_MODE: HVACMode.HEAT, ATTR_HVAC_MODE: HVACMode.HEAT,
ATTR_TEMPERATURE: 23, ATTR_TEMPERATURE: 23,
}, },
[call(23)], [call(23, True)],
), ),
], ],
) )
@ -316,14 +316,14 @@ async def test_set_temperature(
("service_data", "target_temperature", "current_preset", "expected_call_args"), ("service_data", "target_temperature", "current_preset", "expected_call_args"),
[ [
# mode off always sets target temperature to 0 # mode off always sets target temperature to 0
({ATTR_HVAC_MODE: HVACMode.OFF}, 22, PRESET_COMFORT, [call(0)]), ({ATTR_HVAC_MODE: HVACMode.OFF}, 22, PRESET_COMFORT, [call(0, True)]),
({ATTR_HVAC_MODE: HVACMode.OFF}, 16, PRESET_ECO, [call(0)]), ({ATTR_HVAC_MODE: HVACMode.OFF}, 16, PRESET_ECO, [call(0, True)]),
({ATTR_HVAC_MODE: HVACMode.OFF}, 16, None, [call(0)]), ({ATTR_HVAC_MODE: HVACMode.OFF}, 16, None, [call(0, True)]),
# mode heat sets target temperature based on current scheduled preset, # mode heat sets target temperature based on current scheduled preset,
# when not already in mode heat # when not already in mode heat
({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, PRESET_COMFORT, [call(22)]), ({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, PRESET_COMFORT, [call(22, True)]),
({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, PRESET_ECO, [call(16)]), ({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, PRESET_ECO, [call(16, True)]),
({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, None, [call(22)]), ({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, None, [call(22, True)]),
# mode heat does not set target temperature, when already in mode heat # mode heat does not set target temperature, when already in mode heat
({ATTR_HVAC_MODE: HVACMode.HEAT}, 16, PRESET_COMFORT, []), ({ATTR_HVAC_MODE: HVACMode.HEAT}, 16, PRESET_COMFORT, []),
({ATTR_HVAC_MODE: HVACMode.HEAT}, 16, PRESET_ECO, []), ({ATTR_HVAC_MODE: HVACMode.HEAT}, 16, PRESET_ECO, []),
@ -380,7 +380,7 @@ async def test_set_preset_mode_comfort(hass: HomeAssistant, fritz: Mock) -> None
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_COMFORT}, {ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_COMFORT},
True, True,
) )
assert device.set_target_temperature.call_args_list == [call(22)] assert device.set_target_temperature.call_args_list == [call(22, True)]
async def test_set_preset_mode_eco(hass: HomeAssistant, fritz: Mock) -> None: async def test_set_preset_mode_eco(hass: HomeAssistant, fritz: Mock) -> None:
@ -396,7 +396,7 @@ async def test_set_preset_mode_eco(hass: HomeAssistant, fritz: Mock) -> None:
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_ECO}, {ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_ECO},
True, True,
) )
assert device.set_target_temperature.call_args_list == [call(16)] assert device.set_target_temperature.call_args_list == [call(16, True)]
async def test_preset_mode_update(hass: HomeAssistant, fritz: Mock) -> None: async def test_preset_mode_update(hass: HomeAssistant, fritz: Mock) -> None:

View File

@ -99,7 +99,7 @@ async def test_set_position_cover(hass: HomeAssistant, fritz: Mock) -> None:
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_POSITION: 50}, {ATTR_ENTITY_ID: ENTITY_ID, ATTR_POSITION: 50},
True, True,
) )
assert device.set_level_percentage.call_args_list == [call(50)] assert device.set_level_percentage.call_args_list == [call(50, True)]
async def test_stop_cover(hass: HomeAssistant, fritz: Mock) -> None: async def test_stop_cover(hass: HomeAssistant, fritz: Mock) -> None:

View File

@ -155,8 +155,8 @@ async def test_turn_on(hass: HomeAssistant, fritz: Mock) -> None:
assert device.set_state_on.call_count == 1 assert device.set_state_on.call_count == 1
assert device.set_level.call_count == 1 assert device.set_level.call_count == 1
assert device.set_color_temp.call_count == 1 assert device.set_color_temp.call_count == 1
assert device.set_color_temp.call_args_list == [call(3000)] assert device.set_color_temp.call_args_list == [call(3000, 0, True)]
assert device.set_level.call_args_list == [call(100)] assert device.set_level.call_args_list == [call(100, True)]
async def test_turn_on_color(hass: HomeAssistant, fritz: Mock) -> None: async def test_turn_on_color(hass: HomeAssistant, fritz: Mock) -> None:
@ -178,9 +178,9 @@ async def test_turn_on_color(hass: HomeAssistant, fritz: Mock) -> None:
assert device.set_state_on.call_count == 1 assert device.set_state_on.call_count == 1
assert device.set_level.call_count == 1 assert device.set_level.call_count == 1
assert device.set_unmapped_color.call_count == 1 assert device.set_unmapped_color.call_count == 1
assert device.set_level.call_args_list == [call(100)] assert device.set_level.call_args_list == [call(100, True)]
assert device.set_unmapped_color.call_args_list == [ assert device.set_unmapped_color.call_args_list == [
call((100, round(70 * 255.0 / 100.0))) call((100, round(70 * 255.0 / 100.0)), 0, True)
] ]
@ -212,8 +212,8 @@ async def test_turn_on_color_unsupported_api_method(
assert device.set_state_on.call_count == 1 assert device.set_state_on.call_count == 1
assert device.set_level.call_count == 1 assert device.set_level.call_count == 1
assert device.set_color.call_count == 1 assert device.set_color.call_count == 1
assert device.set_level.call_args_list == [call(100)] assert device.set_level.call_args_list == [call(100, True)]
assert device.set_color.call_args_list == [call((100, 70))] assert device.set_color.call_args_list == [call((100, 70), 0, True)]
# test for unknown error # test for unknown error
error.response.status_code = 500 error.response.status_code = 500