diff --git a/homeassistant/components/roborock/coordinator.py b/homeassistant/components/roborock/coordinator.py index 192a52f925d4a0..d93cf779ad9392 100644 --- a/homeassistant/components/roborock/coordinator.py +++ b/homeassistant/components/roborock/coordinator.py @@ -38,6 +38,11 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import DeviceInfo +from homeassistant.helpers.issue_registry import ( + IssueSeverity, + async_create_issue, + async_delete_issue, +) from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import dt as dt_util, slugify @@ -274,6 +279,9 @@ async def _verify_api(self) -> None: try: await self.api.async_connect() await self.api.ping() + async_delete_issue( + self.hass, DOMAIN, f"cloud_api_used_{self.duid_slug}" + ) except RoborockException: _LOGGER.warning( "Using the cloud API for device %s. This is not recommended as it can lead to rate limiting. We recommend making your vacuum accessible by your Home Assistant instance", @@ -284,6 +292,19 @@ async def _verify_api(self) -> None: self.api = self.cloud_api self.update_interval = V1_CLOUD_NOT_CLEANING_INTERVAL self._is_cloud_api = True + async_create_issue( + self.hass, + DOMAIN, + f"cloud_api_used_{self.duid_slug}", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="cloud_api_used", + translation_placeholders={ + "device_name": self.roborock_device_info.device.name + }, + learn_more_url="https://www.home-assistant.io/integrations/roborock/#the-integration-tells-me-it-cannot-reach-my-vacuum-and-is-using-the-cloud-api-and-that-this-is-not-supported-or-i-am-having-any-networking-issues", + ) + # Right now this should never be called if the cloud api is the primary api, # but in the future if it is, a new else should be added. diff --git a/homeassistant/components/roborock/strings.json b/homeassistant/components/roborock/strings.json index a8f58cf24923da..8a00fc9445c9c8 100644 --- a/homeassistant/components/roborock/strings.json +++ b/homeassistant/components/roborock/strings.json @@ -39,6 +39,13 @@ "wrong_account": "Wrong account: Please authenticate with the right account." } }, + "issues": { + "cloud_api_used": { + "title": "Cloud API used", + "description": "The Roborock integration is unable to connect directly to {device_name} and falling back to the cloud API. This is not recommended as it can lead to rate limiting. Please make your vacuum accessible on the local network by your Home Assistant instance." + } + }, + "options": { "step": { "drawables": { diff --git a/tests/components/roborock/test_coordinator.py b/tests/components/roborock/test_coordinator.py index cf2ead85cb2f25..77b87752dc6b71 100644 --- a/tests/components/roborock/test_coordinator.py +++ b/tests/components/roborock/test_coordinator.py @@ -22,6 +22,7 @@ from homeassistant.components.roborock.coordinator import RoborockDataUpdateCoordinator from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant +from homeassistant.helpers import issue_registry as ir from homeassistant.util import dt as dt_util from .mock_data import PROP @@ -171,6 +172,44 @@ async def test_no_maps( assert load_map.call_count == 0 +async def test_cloud_api_repair( + hass: HomeAssistant, + mock_roborock_entry: MockConfigEntry, + bypass_api_fixture_v1_only, +) -> None: + """Test that a repair is created when we use the cloud api.""" + # Force the system to use the cloud api. + with patch( + "homeassistant.components.roborock.coordinator.RoborockLocalClientV1.ping", + side_effect=RoborockException(), + ): + await hass.config_entries.async_setup(mock_roborock_entry.entry_id) + await hass.async_block_till_done() + + issue_registry = ir.async_get(hass) + assert len(issue_registry.issues) == 2 + # Check that both expected device names are present, regardless of order + assert all( + issue.translation_key == "cloud_api_used" + for issue in issue_registry.issues.values() + ) + names = { + issue.translation_placeholders["device_name"] + for issue in issue_registry.issues.values() + } + assert names == {"Roborock S7 MaxV", "Roborock S7 2"} + await hass.config_entries.async_unload(mock_roborock_entry.entry_id) + # Now change to using the local api + with patch( + "homeassistant.components.roborock.coordinator.RoborockLocalClientV1.ping" + ): + # Set it back up + await hass.config_entries.async_setup(mock_roborock_entry.entry_id) + await hass.async_block_till_done() + + assert len(issue_registry.issues) == 0 + + async def test_two_maps_in_cleaning( hass: HomeAssistant, mock_roborock_entry: MockConfigEntry,