diff --git a/homeassistant/components/mysensors/device.py b/homeassistant/components/mysensors/device.py index a7e0cd9b68b..bb5e0d72327 100644 --- a/homeassistant/components/mysensors/device.py +++ b/homeassistant/components/mysensors/device.py @@ -10,6 +10,7 @@ from mysensors.sensor import ChildSensor from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON, Platform from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, Entity @@ -55,7 +56,7 @@ class MySensorsDevice(ABC): self.value_type: int = value_type # value_type as int. string variant can be looked up in gateway consts self.child_type = self._child.type self._values: dict[int, Any] = {} - self._update_scheduled = False + self._debouncer: Debouncer | None = None @property def dev_id(self) -> DevId: @@ -184,24 +185,18 @@ class MySensorsDevice(ABC): def _async_update_callback(self) -> None: """Update the device.""" - @callback - def async_update_callback(self) -> None: + async def async_update_callback(self) -> None: """Update the device after delay.""" - if self._update_scheduled: - return + if not self._debouncer: + self._debouncer = Debouncer( + self.hass, + _LOGGER, + cooldown=UPDATE_DELAY, + immediate=False, + function=self._async_update_callback, + ) - @callback - def async_update() -> None: - """Perform update.""" - try: - self._async_update_callback() - except Exception: # pylint: disable=broad-except - _LOGGER.exception("Error updating %s", self.name) - finally: - self._update_scheduled = False - - self._update_scheduled = True - self.hass.loop.call_later(UPDATE_DELAY, async_update) + await self._debouncer.async_call() def get_mysensors_devices( diff --git a/tests/components/mysensors/conftest.py b/tests/components/mysensors/conftest.py index a71ccab6ce5..8068f6894bf 100644 --- a/tests/components/mysensors/conftest.py +++ b/tests/components/mysensors/conftest.py @@ -144,8 +144,26 @@ async def integration_fixture( """Set up the mysensors integration with a config entry.""" config: dict[str, Any] = {} config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.mysensors.device.Debouncer", autospec=True + ) as debouncer_class: + + def debouncer( + *args: Any, function: Callable | None = None, **kwargs: Any + ) -> MagicMock: + """Mock the debouncer.""" + + async def call_debouncer(): + """Mock call to debouncer.""" + if function is not None: + function() + + debounce_instance = MagicMock() + debounce_instance.async_call.side_effect = call_debouncer + return debounce_instance + + debouncer_class.side_effect = debouncer - with patch("homeassistant.components.mysensors.device.UPDATE_DELAY", new=0): await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() yield config_entry diff --git a/tests/components/mysensors/test_binary_sensor.py b/tests/components/mysensors/test_binary_sensor.py index 652a4a0bf66..7dfb188e842 100644 --- a/tests/components/mysensors/test_binary_sensor.py +++ b/tests/components/mysensors/test_binary_sensor.py @@ -27,9 +27,6 @@ async def test_door_sensor( assert state.attributes[ATTR_DEVICE_CLASS] == BinarySensorDeviceClass.DOOR receive_message("1;1;1;0;16;1\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -38,9 +35,6 @@ async def test_door_sensor( assert state.state == "on" receive_message("1;1;1;0;16;0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) diff --git a/tests/components/mysensors/test_climate.py b/tests/components/mysensors/test_climate.py index 437a3010ce6..730960f118d 100644 --- a/tests/components/mysensors/test_climate.py +++ b/tests/components/mysensors/test_climate.py @@ -49,9 +49,6 @@ async def test_hvac_node_auto( assert transport_write.call_args == call("1;1;1;1;21;AutoChangeOver\n") receive_message("1;1;1;0;21;AutoChangeOver\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -83,9 +80,6 @@ async def test_hvac_node_auto( receive_message("1;1;1;0;45;20.0\n") receive_message("1;1;1;0;44;22.0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -113,9 +107,6 @@ async def test_hvac_node_auto( assert transport_write.call_args == call("1;1;1;1;22;Max\n") receive_message("1;1;1;0;22;Max\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -138,9 +129,6 @@ async def test_hvac_node_auto( assert transport_write.call_args == call("1;1;1;1;21;Off\n") receive_message("1;1;1;0;21;Off\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -175,9 +163,6 @@ async def test_hvac_node_heat( assert transport_write.call_args == call("1;1;1;1;21;HeatOn\n") receive_message("1;1;1;0;21;HeatOn\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -205,9 +190,6 @@ async def test_hvac_node_heat( assert transport_write.call_args == call("1;1;1;1;45;20.0\n") receive_message("1;1;1;0;45;20.0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -234,9 +216,6 @@ async def test_hvac_node_heat( assert transport_write.call_args == call("1;1;1;1;22;Min\n") receive_message("1;1;1;0;22;Min\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -259,9 +238,6 @@ async def test_hvac_node_heat( assert transport_write.call_args == call("1;1;1;1;21;Off\n") receive_message("1;1;1;0;21;Off\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -296,9 +272,6 @@ async def test_hvac_node_cool( assert transport_write.call_args == call("1;1;1;1;21;CoolOn\n") receive_message("1;1;1;0;21;CoolOn\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -326,9 +299,6 @@ async def test_hvac_node_cool( assert transport_write.call_args == call("1;1;1;1;44;20.0\n") receive_message("1;1;1;0;44;20.0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -355,9 +325,6 @@ async def test_hvac_node_cool( assert transport_write.call_args == call("1;1;1;1;22;Auto\n") receive_message("1;1;1;0;22;Auto\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -380,9 +347,6 @@ async def test_hvac_node_cool( assert transport_write.call_args == call("1;1;1;1;21;Off\n") receive_message("1;1;1;0;21;Off\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) diff --git a/tests/components/mysensors/test_cover.py b/tests/components/mysensors/test_cover.py index c049c517d12..494800f388f 100644 --- a/tests/components/mysensors/test_cover.py +++ b/tests/components/mysensors/test_cover.py @@ -50,9 +50,6 @@ async def test_cover_node_percentage( receive_message("1;1;1;0;29;1\n") receive_message("1;1;1;0;3;50\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -75,9 +72,6 @@ async def test_cover_node_percentage( receive_message("1;1;1;0;31;1\n") receive_message("1;1;1;0;3;50\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -101,9 +95,6 @@ async def test_cover_node_percentage( receive_message("1;1;1;0;31;0\n") receive_message("1;1;1;0;29;1\n") receive_message("1;1;1;0;3;75\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -114,9 +105,6 @@ async def test_cover_node_percentage( receive_message("1;1;1;0;29;0\n") receive_message("1;1;1;0;3;100\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -139,9 +127,6 @@ async def test_cover_node_percentage( receive_message("1;1;1;0;30;1\n") receive_message("1;1;1;0;3;50\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -152,9 +137,6 @@ async def test_cover_node_percentage( receive_message("1;1;1;0;30;0\n") receive_message("1;1;1;0;3;0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -176,9 +158,6 @@ async def test_cover_node_percentage( assert transport_write.call_args == call("1;1;1;1;3;25\n") receive_message("1;1;1;0;3;25\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -214,9 +193,6 @@ async def test_cover_node_binary( receive_message("1;1;1;0;29;1\n") receive_message("1;1;1;0;2;1\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -237,9 +213,6 @@ async def test_cover_node_binary( assert transport_write.call_args == call("1;1;1;1;31;1\n") receive_message("1;1;1;0;31;1\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -261,9 +234,6 @@ async def test_cover_node_binary( receive_message("1;1;1;0;31;0\n") receive_message("1;1;1;0;29;1\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -273,9 +243,6 @@ async def test_cover_node_binary( receive_message("1;1;1;0;29;0\n") receive_message("1;1;1;0;2;1\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -296,9 +263,6 @@ async def test_cover_node_binary( assert transport_write.call_args == call("1;1;1;1;30;1\n") receive_message("1;1;1;0;30;1\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -308,9 +272,6 @@ async def test_cover_node_binary( receive_message("1;1;1;0;30;0\n") receive_message("1;1;1;0;2;0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) diff --git a/tests/components/mysensors/test_device_tracker.py b/tests/components/mysensors/test_device_tracker.py index b74d42cd0ce..63c9ed7b1da 100644 --- a/tests/components/mysensors/test_device_tracker.py +++ b/tests/components/mysensors/test_device_tracker.py @@ -23,9 +23,6 @@ async def test_gps_sensor( message_string = f"1;1;1;0;49;{latitude},{longitude},{altitude}\n" receive_message(message_string) - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -41,9 +38,6 @@ async def test_gps_sensor( message_string = f"1;1;1;0;49;{latitude},{longitude},{altitude}\n" receive_message(message_string) - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) diff --git a/tests/components/mysensors/test_light.py b/tests/components/mysensors/test_light.py index e4cf160684d..8d4ce445779 100644 --- a/tests/components/mysensors/test_light.py +++ b/tests/components/mysensors/test_light.py @@ -42,9 +42,6 @@ async def test_dimmer_node( receive_message("1;1;1;0;2;1\n") receive_message("1;1;1;0;3;100\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -68,9 +65,6 @@ async def test_dimmer_node( receive_message("1;1;1;0;2;1\n") receive_message("1;1;1;0;3;50\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -93,9 +87,6 @@ async def test_dimmer_node( assert transport_write.call_args == call("1;1;1;1;2;0\n") receive_message("1;1;1;0;2;0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -132,9 +123,6 @@ async def test_rgb_node( receive_message("1;1;1;0;2;1\n") receive_message("1;1;1;0;3;100\n") receive_message("1;1;1;0;40;ffffff\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -160,9 +148,6 @@ async def test_rgb_node( receive_message("1;1;1;0;2;1\n") receive_message("1;1;1;0;3;50\n") receive_message("1;1;1;0;40;ffffff\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -186,9 +171,6 @@ async def test_rgb_node( assert transport_write.call_args == call("1;1;1;1;2;0\n") receive_message("1;1;1;0;2;0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -213,9 +195,6 @@ async def test_rgb_node( receive_message("1;1;1;0;2;1\n") receive_message("1;1;1;0;3;50\n") receive_message("1;1;1;0;40;ff0000\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -254,9 +233,6 @@ async def test_rgbw_node( receive_message("1;1;1;0;2;1\n") receive_message("1;1;1;0;3;100\n") receive_message("1;1;1;0;41;ffffffff\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -282,9 +258,6 @@ async def test_rgbw_node( receive_message("1;1;1;0;2;1\n") receive_message("1;1;1;0;3;50\n") receive_message("1;1;1;0;41;ffffffff\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -308,9 +281,6 @@ async def test_rgbw_node( assert transport_write.call_args == call("1;1;1;1;2;0\n") receive_message("1;1;1;0;2;0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -335,9 +305,6 @@ async def test_rgbw_node( receive_message("1;1;1;0;2;1\n") receive_message("1;1;1;0;3;50\n") receive_message("1;1;1;0;41;ff000000\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) diff --git a/tests/components/mysensors/test_sensor.py b/tests/components/mysensors/test_sensor.py index e9c8aad148f..7b5854b9efe 100644 --- a/tests/components/mysensors/test_sensor.py +++ b/tests/components/mysensors/test_sensor.py @@ -48,9 +48,6 @@ async def test_gps_sensor( message_string = f"1;1;1;0;49;{new_coords},{altitude}\n" receive_message(message_string) - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -144,9 +141,6 @@ async def test_temperature_sensor( message_string = f"1;1;1;0;0;{temperature}\n" receive_message(message_string) - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) diff --git a/tests/components/mysensors/test_switch.py b/tests/components/mysensors/test_switch.py index a4ff3995c08..b77d540d543 100644 --- a/tests/components/mysensors/test_switch.py +++ b/tests/components/mysensors/test_switch.py @@ -36,9 +36,6 @@ async def test_relay_node( assert transport_write.call_args == call("1;1;1;1;2;1\n") receive_message("1;1;1;0;2;1\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -59,9 +56,6 @@ async def test_relay_node( assert transport_write.call_args == call("1;1;1;1;2;0\n") receive_message("1;1;1;0;2;0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -96,9 +90,6 @@ async def test_ir_transceiver( assert transport_write.call_args_list[1] == call("1;1;1;1;2;1\n") receive_message("1;1;1;0;2;1\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -120,9 +111,6 @@ async def test_ir_transceiver( assert transport_write.call_args == call("1;1;1;1;2;0\n") receive_message("1;1;1;0;2;0\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -145,9 +133,6 @@ async def test_ir_transceiver( receive_message("1;1;1;0;32;new_code\n") receive_message("1;1;1;0;2;1\n") - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id)