Repository: home-assistant/core
Issue category: Performance Issue

Fritz!box sockets switch very slowly
### The problem

If I want to switch on a Fritz!Box socket via Home Assistant, it takes a very long time until it is switched on and the entity is updated.

### What version of Home Assistant Core has the issue?

core-2024.2.5

### What was the last working version of Home Assistant Core?

_No response_

### What type of installation are you running?

Home Assistant OS

### Integration causing the issue

AVM FRITZ!SmartHome

### Link to integration documentation on our website

https://www.home-assistant.io/integrations/fritzbox/

### Diagnostics information

_No response_

### Example YAML snippet

_No response_

### Anything in the logs that might be useful for us?

_No response_

### Additional information

_No response_


Functions to edit:
homeassistant/components/fritzbox/climate.py:FritzboxThermostat.async_set_temperature
homeassistant/components/fritzbox/cover.py:FritzboxCover.async_open_cover
homeassistant/components/fritzbox/cover.py:FritzboxCover.async_close_cover
homeassistant/components/fritzbox/cover.py:FritzboxCover.async_set_cover_position
homeassistant/components/fritzbox/cover.py:FritzboxCover.async_stop_cover
homeassistant/components/fritzbox/light.py:FritzboxLight.async_turn_on
homeassistant/components/fritzbox/light.py:FritzboxLight.async_turn_off
homeassistant/components/fritzbox/switch.py:FritzboxSwitch.async_turn_on
homeassistant/components/fritzbox/switch.py:FritzboxSwitch.async_turn_off

Patch:
diff --git a/homeassistant/components/fritzbox/climate.py b/homeassistant/components/fritzbox/climate.py
index d5a81fdef1a3d3..87a87ac691f58b 100644
--- a/homeassistant/components/fritzbox/climate.py
+++ b/homeassistant/components/fritzbox/climate.py
@@ -141,7 +141,7 @@ async def async_set_temperature(self, **kwargs: Any) -> None:
             await self.async_set_hvac_mode(hvac_mode)
         elif target_temp is not None:
             await self.hass.async_add_executor_job(
-                self.data.set_target_temperature, target_temp
+                self.data.set_target_temperature, target_temp, True
             )
         else:
             return
diff --git a/homeassistant/components/fritzbox/cover.py b/homeassistant/components/fritzbox/cover.py
index de87d6f8852e43..070bb8682982b7 100644
--- a/homeassistant/components/fritzbox/cover.py
+++ b/homeassistant/components/fritzbox/cover.py
@@ -71,21 +71,21 @@ def is_closed(self) -> bool | None:
 
     async def async_open_cover(self, **kwargs: Any) -> None:
         """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()
 
     async def async_close_cover(self, **kwargs: Any) -> None:
         """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()
 
     async def async_set_cover_position(self, **kwargs: Any) -> None:
         """Move the cover to a specific position."""
         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:
         """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()
diff --git a/homeassistant/components/fritzbox/light.py b/homeassistant/components/fritzbox/light.py
index 36cb7dc8cff047..f6a1ba4cc944f4 100644
--- a/homeassistant/components/fritzbox/light.py
+++ b/homeassistant/components/fritzbox/light.py
@@ -122,7 +122,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
         """Turn the light on."""
         if kwargs.get(ATTR_BRIGHTNESS) is not None:
             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:
             # Try setunmappedcolor first. This allows free color selection,
             # but we don't know if its supported by all devices.
@@ -133,7 +133,10 @@ async def async_turn_on(self, **kwargs: Any) -> None:
                     cast(float, kwargs[ATTR_HS_COLOR][1]) * 255.0 / 100.0
                 )
                 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
             except HTTPError as err:
@@ -152,18 +155,18 @@ async def async_turn_on(self, **kwargs: Any) -> None:
                     key=lambda x: abs(x - unmapped_saturation),
                 )
                 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:
             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()
 
     async def async_turn_off(self, **kwargs: Any) -> None:
         """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()
diff --git a/homeassistant/components/fritzbox/switch.py b/homeassistant/components/fritzbox/switch.py
index 18b676d449ead9..d83793c77dc096 100644
--- a/homeassistant/components/fritzbox/switch.py
+++ b/homeassistant/components/fritzbox/switch.py
@@ -51,13 +51,13 @@ def is_on(self) -> bool:
     async def async_turn_on(self, **kwargs: Any) -> None:
         """Turn the switch on."""
         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()
 
     async def async_turn_off(self, **kwargs: Any) -> None:
         """Turn the switch off."""
         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()
 
     def check_lock_state(self) -> None:

Test patch: diff --git a/tests/components/fritzbox/test_climate.py b/tests/components/fritzbox/test_climate.py
index 29f5742216fb84..c7896920ce9364 100644
--- a/tests/components/fritzbox/test_climate.py
+++ b/tests/components/fritzbox/test_climate.py
@@ -273,20 +273,20 @@ async def test_update_error(hass: HomeAssistant, fritz: Mock) -> None:
 @pytest.mark.parametrize(
     ("service_data", "expected_call_args"),
     [
-        ({ATTR_TEMPERATURE: 23}, [call(23)]),
+        ({ATTR_TEMPERATURE: 23}, [call(23, True)]),
         (
             {
                 ATTR_HVAC_MODE: HVACMode.OFF,
                 ATTR_TEMPERATURE: 23,
             },
-            [call(0)],
+            [call(0, True)],
         ),
         (
             {
                 ATTR_HVAC_MODE: HVACMode.HEAT,
                 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"),
     [
         # mode off always sets target temperature to 0
-        ({ATTR_HVAC_MODE: HVACMode.OFF}, 22, PRESET_COMFORT, [call(0)]),
-        ({ATTR_HVAC_MODE: HVACMode.OFF}, 16, PRESET_ECO, [call(0)]),
-        ({ATTR_HVAC_MODE: HVACMode.OFF}, 16, None, [call(0)]),
+        ({ATTR_HVAC_MODE: HVACMode.OFF}, 22, PRESET_COMFORT, [call(0, True)]),
+        ({ATTR_HVAC_MODE: HVACMode.OFF}, 16, PRESET_ECO, [call(0, True)]),
+        ({ATTR_HVAC_MODE: HVACMode.OFF}, 16, None, [call(0, True)]),
         # mode heat sets target temperature based on current scheduled preset,
         # 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_ECO, [call(16)]),
-        ({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, None, [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, True)]),
+        ({ATTR_HVAC_MODE: HVACMode.HEAT}, 0.0, None, [call(22, True)]),
         # 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_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},
         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:
@@ -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},
         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:
diff --git a/tests/components/fritzbox/test_cover.py b/tests/components/fritzbox/test_cover.py
index f26e65fc28a33d..087206af5e6278 100644
--- a/tests/components/fritzbox/test_cover.py
+++ b/tests/components/fritzbox/test_cover.py
@@ -99,7 +99,7 @@ async def test_set_position_cover(hass: HomeAssistant, fritz: Mock) -> None:
         {ATTR_ENTITY_ID: ENTITY_ID, ATTR_POSITION: 50},
         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:
diff --git a/tests/components/fritzbox/test_light.py b/tests/components/fritzbox/test_light.py
index 84fafe25521072..474fa6897107ac 100644
--- a/tests/components/fritzbox/test_light.py
+++ b/tests/components/fritzbox/test_light.py
@@ -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_level.call_count == 1
     assert device.set_color_temp.call_count == 1
-    assert device.set_color_temp.call_args_list == [call(3000)]
-    assert device.set_level.call_args_list == [call(100)]
+    assert device.set_color_temp.call_args_list == [call(3000, 0, True)]
+    assert device.set_level.call_args_list == [call(100, True)]
 
 
 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_level.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 == [
-        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_level.call_count == 1
     assert device.set_color.call_count == 1
-    assert device.set_level.call_args_list == [call(100)]
-    assert device.set_color.call_args_list == [call((100, 70))]
+    assert device.set_level.call_args_list == [call(100, True)]
+    assert device.set_color.call_args_list == [call((100, 70), 0, True)]
 
     # test for unknown error
     error.response.status_code = 500