Add Fallback to cloud api for Roborock (#96147)

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
pull/97152/head^2
Luke 2023-07-24 10:37:37 -06:00 committed by GitHub
parent 6b980eb0a7
commit 2c42a319a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 22 deletions

View File

@ -36,24 +36,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
}
product_info = {product.id: product for product in home_data.products}
# Create a mqtt_client, which is needed to get the networking information of the device for local connection and in the future, get the map.
mqtt_clients = [
RoborockMqttClient(
mqtt_clients = {
device.duid: RoborockMqttClient(
user_data, DeviceData(device, product_info[device.product_id].model)
)
for device in device_map.values()
]
}
network_results = await asyncio.gather(
*(mqtt_client.get_networking() for mqtt_client in mqtt_clients)
*(mqtt_client.get_networking() for mqtt_client in mqtt_clients.values())
)
network_info = {
device.duid: result
for device, result in zip(device_map.values(), network_results)
if result is not None
}
await asyncio.gather(
*(mqtt_client.async_disconnect() for mqtt_client in mqtt_clients),
return_exceptions=True,
)
if not network_info:
raise ConfigEntryNotReady(
"Could not get network information about your devices"
@ -65,7 +61,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
device,
network_info[device_id],
product_info[device.product_id],
mqtt_clients[device.duid],
)
await asyncio.gather(
*(coordinator.verify_api() for coordinator in coordinator_map.values())
)
# If one device update fails - we still want to set up other devices
await asyncio.gather(
*(

View File

@ -4,6 +4,7 @@ from __future__ import annotations
from datetime import timedelta
import logging
from roborock.cloud_api import RoborockMqttClient
from roborock.containers import DeviceData, HomeDataDevice, HomeDataProduct, NetworkInfo
from roborock.exceptions import RoborockException
from roborock.local_api import RoborockLocalClient
@ -30,6 +31,7 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
device: HomeDataDevice,
device_networking: NetworkInfo,
product_info: HomeDataProduct,
cloud_api: RoborockMqttClient | None = None,
) -> None:
"""Initialize."""
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL)
@ -41,6 +43,7 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
)
device_data = DeviceData(device, product_info.model, device_networking.ip)
self.api = RoborockLocalClient(device_data)
self.cloud_api = cloud_api
self.device_info = DeviceInfo(
name=self.roborock_device_info.device.name,
identifiers={(DOMAIN, self.roborock_device_info.device.duid)},
@ -49,6 +52,21 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
sw_version=self.roborock_device_info.device.fv,
)
async def verify_api(self) -> None:
"""Verify that the api is reachable. If it is not, switch clients."""
try:
await self.api.ping()
except RoborockException:
if isinstance(self.api, RoborockLocalClient):
_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",
self.roborock_device_info.device.duid,
)
# We use the cloud api if the local api fails to connect.
self.api = self.cloud_api
# 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.
async def release(self) -> None:
"""Disconnect from API."""
await self.api.async_disconnect()

View File

@ -2,11 +2,10 @@
from typing import Any
from roborock.api import AttributeCache
from roborock.api import AttributeCache, RoborockClient
from roborock.command_cache import CacheableAttribute
from roborock.containers import Status
from roborock.exceptions import RoborockException
from roborock.local_api import RoborockLocalClient
from roborock.roborock_typing import RoborockCommand
from homeassistant.exceptions import HomeAssistantError
@ -22,7 +21,7 @@ class RoborockEntity(Entity):
_attr_has_entity_name = True
def __init__(
self, unique_id: str, device_info: DeviceInfo, api: RoborockLocalClient
self, unique_id: str, device_info: DeviceInfo, api: RoborockClient
) -> None:
"""Initialize the coordinated Roborock Device."""
self._attr_unique_id = unique_id
@ -30,8 +29,8 @@ class RoborockEntity(Entity):
self._api = api
@property
def api(self) -> RoborockLocalClient:
"""Return the Api."""
def api(self) -> RoborockClient:
"""Returns the api."""
return self._api
def get_cache(self, attribute: CacheableAttribute) -> AttributeCache:

View File

@ -9,13 +9,11 @@ from typing import Any
from roborock.api import AttributeCache
from roborock.command_cache import CacheableAttribute
from roborock.local_api import RoborockLocalClient
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import slugify
@ -121,9 +119,8 @@ async def async_setup_entry(
valid_entities.append(
RoborockSwitch(
f"{description.key}_{slugify(coordinator.roborock_device_info.device.duid)}",
coordinator.device_info,
coordinator,
description,
coordinator.api,
)
)
async_add_entities(valid_entities)
@ -137,13 +134,12 @@ class RoborockSwitch(RoborockEntity, SwitchEntity):
def __init__(
self,
unique_id: str,
device_info: DeviceInfo,
description: RoborockSwitchDescription,
api: RoborockLocalClient,
coordinator: RoborockDataUpdateCoordinator,
entity_description: RoborockSwitchDescription,
) -> None:
"""Initialize the entity."""
super().__init__(unique_id, device_info, api)
self.entity_description = description
self.entity_description = entity_description
super().__init__(unique_id, coordinator.device_info, coordinator.api)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the switch."""