From 6b37796ff57706f636565d3fc94230743afb68e7 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 24 Dec 2025 16:43:02 +0000 Subject: [PATCH] Fix Roborock repair issue behavior Clear repair issues related to connectivity if the device later becomes reachable locally. We only preserve the issue if the device has never ben reachable locally. --- .../components/roborock/coordinator.py | 5 +- tests/components/roborock/test_init.py | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/roborock/coordinator.py b/homeassistant/components/roborock/coordinator.py index fe070e10321eba..025a5c37186a40 100644 --- a/homeassistant/components/roborock/coordinator.py +++ b/homeassistant/components/roborock/coordinator.py @@ -122,6 +122,7 @@ def __init__( # Tracks the last successful update to control when we report failure # to the base class. This is reset on successful data update. self._last_update_success_time: datetime | None = None + self._has_connected_locally: bool = False @cached_property def dock_device_info(self) -> DeviceInfo: @@ -191,7 +192,8 @@ async def update_map(self) -> None: async def _verify_api(self) -> None: """Verify that the api is reachable.""" if self._device.is_connected: - if self._device.is_local_connected: + self._has_connected_locally |= self._device.is_local_connected + if self._has_connected_locally: async_delete_issue( self.hass, DOMAIN, f"cloud_api_used_{self.duid_slug}" ) @@ -234,6 +236,7 @@ async def _update_device_prop(self) -> None: async def _async_update_data(self) -> DeviceState: """Update data via library.""" + await self._verify_api() try: # Update device props and standard api information await self._update_device_prop() diff --git a/tests/components/roborock/test_init.py b/tests/components/roborock/test_init.py index 034d8b3c1f93f4..8aad5fde10e250 100644 --- a/tests/components/roborock/test_init.py +++ b/tests/components/roborock/test_init.py @@ -415,6 +415,63 @@ async def test_cloud_api_repair( assert len(issue_registry.issues) == 0 +@pytest.mark.parametrize("platforms", [[Platform.SENSOR]]) +async def test_cloud_api_repair_cleared_on_update( + hass: HomeAssistant, + mock_roborock_entry: MockConfigEntry, + fake_vacuum: FakeDevice, + freezer: FrozenDateTimeFactory, +) -> None: + """Test that a repair is created then cleared if the device is reachable locally again.""" + + # Fake that the device is only reachable via cloud + fake_vacuum.is_connected = True + fake_vacuum.is_local_connected = False + + # Load the integration and verify that a repair issue is created + await async_setup_component(hass, HA_DOMAIN, {}) + await hass.config_entries.async_setup(mock_roborock_entry.entry_id) + await hass.async_block_till_done() + assert mock_roborock_entry.state is ConfigEntryState.LOADED + + issue_registry = ir.async_get(hass) + assert len(issue_registry.issues) == 1 + + # Fake that the device is reachable locally again. + fake_vacuum.is_local_connected = True + + # Refresh the coordinator using an arbitrary sensor, which should + # clear the repair issue. + sensor_entity_id = "sensor.roborock_s7_maxv_battery" + await hass.services.async_call( + HA_DOMAIN, + SERVICE_UPDATE_ENTITY, + {ATTR_ENTITY_ID: sensor_entity_id}, + blocking=True, + ) + await hass.async_block_till_done() + + # Verify that the repair issue is cleared + issue_registry = ir.async_get(hass) + assert len(issue_registry.issues) == 0 + + # Fake the device is cloud only again. Refreshing the coordinator + # should not recreate the repair issue. + fake_vacuum.is_local_connected = False + + await hass.services.async_call( + HA_DOMAIN, + SERVICE_UPDATE_ENTITY, + {ATTR_ENTITY_ID: sensor_entity_id}, + blocking=True, + ) + await hass.async_block_till_done() + + # Verify that the repair issue still does not exist + issue_registry = ir.async_get(hass) + assert len(issue_registry.issues) == 0 + + @pytest.mark.parametrize("platforms", [[Platform.SENSOR]]) async def test_zeo_device_fails_setup( hass: HomeAssistant,