From 973fee9fe15f5e9b5b9e67912fe59758e9478f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Diego=20Rodr=C3=ADguez=20Royo?= Date: Tue, 4 Mar 2025 11:07:44 +0100 Subject: [PATCH] Delete refresh after a non-breaking error at event stream at Home Connect (#139740) * Delete refresh after non-breaking error And improve how many time does it take to retry to open stream * Update tests --- .../components/home_connect/coordinator.py | 14 +++++------ .../home_connect/test_coordinator.py | 24 ++++--------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/home_connect/coordinator.py b/homeassistant/components/home_connect/coordinator.py index d9200b282c9..4d275854e30 100644 --- a/homeassistant/components/home_connect/coordinator.py +++ b/homeassistant/components/home_connect/coordinator.py @@ -47,8 +47,6 @@ _LOGGER = logging.getLogger(__name__) type HomeConnectConfigEntry = ConfigEntry[HomeConnectCoordinator] -EVENT_STREAM_RECONNECT_DELAY = 30 - @dataclass(frozen=True, kw_only=True) class HomeConnectApplianceData: @@ -157,9 +155,11 @@ class HomeConnectCoordinator( async def _event_listener(self) -> None: """Match event with listener for event type.""" + retry_time = 10 while True: try: async for event_message in self.client.stream_all_events(): + retry_time = 10 event_message_ha_id = event_message.ha_id match event_message.type: case EventType.STATUS: @@ -256,20 +256,18 @@ class HomeConnectCoordinator( except (EventStreamInterruptedError, HomeConnectRequestError) as error: _LOGGER.debug( "Non-breaking error (%s) while listening for events," - " continuing in 30 seconds", + " continuing in %s seconds", type(error).__name__, + retry_time, ) - await asyncio.sleep(EVENT_STREAM_RECONNECT_DELAY) + await asyncio.sleep(retry_time) + retry_time = min(retry_time * 2, 3600) except HomeConnectApiError as error: _LOGGER.error("Error while listening for events: %s", error) self.hass.config_entries.async_schedule_reload( self.config_entry.entry_id ) break - # if there was a non-breaking error, we continue listening - # but we need to refresh the data to get the possible changes - # that happened while the event stream was interrupted - await self.async_refresh() @callback def _call_event_listener(self, event_message: EventMessage) -> None: diff --git a/tests/components/home_connect/test_coordinator.py b/tests/components/home_connect/test_coordinator.py index 3dd9ffbe7c1..ac27b848a36 100644 --- a/tests/components/home_connect/test_coordinator.py +++ b/tests/components/home_connect/test_coordinator.py @@ -1,6 +1,7 @@ """Test for Home Connect coordinator.""" from collections.abc import Awaitable, Callable +from datetime import timedelta from typing import Any from unittest.mock import AsyncMock, MagicMock, patch @@ -12,8 +13,6 @@ from aiohomeconnect.model import ( EventKey, EventMessage, EventType, - Status, - StatusKey, ) from aiohomeconnect.model.error import ( EventStreamInterruptedError, @@ -24,7 +23,6 @@ from aiohomeconnect.model.error import ( import pytest from homeassistant.components.home_connect.const import ( - BSH_DOOR_STATE_LOCKED, BSH_DOOR_STATE_OPEN, BSH_EVENT_PRESENT_STATE_PRESENT, BSH_POWER_OFF, @@ -38,8 +36,9 @@ from homeassistant.core import ( callback, ) from homeassistant.helpers import entity_registry as er +from homeassistant.util import dt as dt_util -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_fire_time_changed @pytest.fixture @@ -286,9 +285,6 @@ async def test_event_listener_error( ( "entity_id", "initial_state", - "status_key", - "status_value", - "after_refresh_expected_state", "event_key", "event_value", "after_event_expected_state", @@ -297,24 +293,15 @@ async def test_event_listener_error( ( "sensor.washer_door", "closed", - StatusKey.BSH_COMMON_DOOR_STATE, - BSH_DOOR_STATE_LOCKED, - "locked", EventKey.BSH_COMMON_STATUS_DOOR_STATE, BSH_DOOR_STATE_OPEN, "open", ), ], ) -@patch( - "homeassistant.components.home_connect.coordinator.EVENT_STREAM_RECONNECT_DELAY", 0 -) async def test_event_listener_resilience( entity_id: str, initial_state: str, - status_key: StatusKey, - status_value: Any, - after_refresh_expected_state: str, event_key: EventKey, event_value: Any, after_event_expected_state: str, @@ -345,16 +332,13 @@ async def test_event_listener_resilience( assert hass.states.is_state(entity_id, initial_state) - client.get_status.return_value = ArrayOfStatus( - [Status(key=status_key, raw_key=status_key.value, value=status_value)], - ) await hass.async_block_till_done() future.set_exception(exception) await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() assert client.stream_all_events.call_count == 2 - assert hass.states.is_state(entity_id, after_refresh_expected_state) await client.add_events( [