diff --git a/homeassistant/components/homematicip_cloud/hap.py b/homeassistant/components/homematicip_cloud/hap.py index f3681a89110..c42ebff200d 100644 --- a/homeassistant/components/homematicip_cloud/hap.py +++ b/homeassistant/components/homematicip_cloud/hap.py @@ -128,6 +128,7 @@ class HomematicipHAP: self.config_entry.data.get(HMIPC_AUTHTOKEN), self.config_entry.data.get(HMIPC_NAME), ) + except HmipcConnectionError as err: raise ConfigEntryNotReady from err except Exception as err: # noqa: BLE001 @@ -210,41 +211,13 @@ class HomematicipHAP: for device in self.home.devices: device.fire_update_event() - async def async_connect(self) -> None: - """Start WebSocket connection.""" - tries = 0 - while True: - retry_delay = 2 ** min(tries, 8) + async def async_connect(self, home: AsyncHome) -> None: + """Connect to HomematicIP Cloud Websocket.""" + await home.enable_events() - try: - await self.home.get_current_state_async() - hmip_events = self.home.enable_events() - self.home.set_on_connected_handler(self.ws_connected_handler) - self.home.set_on_disconnected_handler(self.ws_disconnected_handler) - tries = 0 - await hmip_events - except HmipConnectionError: - _LOGGER.error( - ( - "Error connecting to HomematicIP with HAP %s. " - "Retrying in %d seconds" - ), - self.config_entry.unique_id, - retry_delay, - ) - - if self._ws_close_requested: - break - self._ws_close_requested = False - tries += 1 - - try: - self._retry_task = self.hass.async_create_task( - asyncio.sleep(retry_delay) - ) - await self._retry_task - except asyncio.CancelledError: - break + home.set_on_connected_handler(self.ws_connected_handler) + home.set_on_disconnected_handler(self.ws_disconnected_handler) + home.set_on_reconnect_handler(self.ws_reconnected_handler) async def async_reset(self) -> bool: """Close the websocket connection.""" @@ -272,14 +245,22 @@ class HomematicipHAP: async def ws_connected_handler(self) -> None: """Handle websocket connected.""" - _LOGGER.debug("WebSocket connection to HomematicIP established") + _LOGGER.info("Websocket connection to HomematicIP Cloud established") if self._ws_connection_closed.is_set(): await self.get_state() self._ws_connection_closed.clear() async def ws_disconnected_handler(self) -> None: """Handle websocket disconnection.""" - _LOGGER.warning("WebSocket connection to HomematicIP closed") + _LOGGER.warning("Websocket connection to HomematicIP Cloud closed") + self._ws_connection_closed.set() + + async def ws_reconnected_handler(self, reason: str) -> None: + """Handle websocket reconnection.""" + _LOGGER.info( + "Websocket connection to HomematicIP Cloud re-established due to reason: %s", + reason, + ) self._ws_connection_closed.set() async def get_hap( @@ -306,6 +287,6 @@ class HomematicipHAP: home.on_update(self.async_update) home.on_create(self.async_create_entity) - hass.loop.create_task(self.async_connect()) + await self.async_connect(home) return home diff --git a/homeassistant/components/homematicip_cloud/manifest.json b/homeassistant/components/homematicip_cloud/manifest.json index fc4a1cb831f..163f3c402dc 100644 --- a/homeassistant/components/homematicip_cloud/manifest.json +++ b/homeassistant/components/homematicip_cloud/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/homematicip_cloud", "iot_class": "cloud_push", "loggers": ["homematicip"], - "requirements": ["homematicip==2.0.4"] + "requirements": ["homematicip==2.0.5"] } diff --git a/requirements_all.txt b/requirements_all.txt index 40f0e21ef80..7ec909b653f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1170,7 +1170,7 @@ home-assistant-frontend==20250531.3 home-assistant-intents==2025.6.10 # homeassistant.components.homematicip_cloud -homematicip==2.0.4 +homematicip==2.0.5 # homeassistant.components.horizon horimote==0.4.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4b4f177b3e0..dd69b70b90d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1016,7 +1016,7 @@ home-assistant-frontend==20250531.3 home-assistant-intents==2025.6.10 # homeassistant.components.homematicip_cloud -homematicip==2.0.4 +homematicip==2.0.5 # homeassistant.components.remember_the_milk httplib2==0.20.4 diff --git a/tests/components/homematicip_cloud/test_hap.py b/tests/components/homematicip_cloud/test_hap.py index a8aab422eb9..ae094f7dded 100644 --- a/tests/components/homematicip_cloud/test_hap.py +++ b/tests/components/homematicip_cloud/test_hap.py @@ -1,6 +1,6 @@ """Test HomematicIP Cloud accesspoint.""" -from unittest.mock import Mock, patch +from unittest.mock import AsyncMock, Mock, patch from homematicip.auth import Auth from homematicip.connection.connection_context import ConnectionContext @@ -16,6 +16,7 @@ from homeassistant.components.homematicip_cloud.const import ( ) from homeassistant.components.homematicip_cloud.errors import HmipcConnectionError from homeassistant.components.homematicip_cloud.hap import ( + AsyncHome, HomematicipAuth, HomematicipHAP, ) @@ -251,3 +252,21 @@ async def test_get_state_after_disconnect( assert hap._ws_connection_closed.is_set() await hap.ws_connected_handler() mock_get_state.assert_called_once() + + +async def test_async_connect( + hass: HomeAssistant, hmip_config_entry: MockConfigEntry, simple_mock_home +) -> None: + """Test async_connect.""" + hass.config.components.add(DOMAIN) + hap = HomematicipHAP(hass, hmip_config_entry) + assert hap + + simple_mock_home = AsyncMock(spec=AsyncHome, autospec=True) + + await hap.async_connect(simple_mock_home) + + simple_mock_home.set_on_connected_handler.assert_called_once() + simple_mock_home.set_on_disconnected_handler.assert_called_once() + simple_mock_home.set_on_reconnect_handler.assert_called_once() + simple_mock_home.enable_events.assert_called_once()