mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add Fallback to cloud api for Roborock (#96147)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
parent
6b980eb0a7
commit
2c42a319a2
@ -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}
|
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.
|
# 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 = [
|
mqtt_clients = {
|
||||||
RoborockMqttClient(
|
device.duid: RoborockMqttClient(
|
||||||
user_data, DeviceData(device, product_info[device.product_id].model)
|
user_data, DeviceData(device, product_info[device.product_id].model)
|
||||||
)
|
)
|
||||||
for device in device_map.values()
|
for device in device_map.values()
|
||||||
]
|
}
|
||||||
network_results = await asyncio.gather(
|
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 = {
|
network_info = {
|
||||||
device.duid: result
|
device.duid: result
|
||||||
for device, result in zip(device_map.values(), network_results)
|
for device, result in zip(device_map.values(), network_results)
|
||||||
if result is not None
|
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:
|
if not network_info:
|
||||||
raise ConfigEntryNotReady(
|
raise ConfigEntryNotReady(
|
||||||
"Could not get network information about your devices"
|
"Could not get network information about your devices"
|
||||||
@ -65,7 +61,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
device,
|
device,
|
||||||
network_info[device_id],
|
network_info[device_id],
|
||||||
product_info[device.product_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
|
# If one device update fails - we still want to set up other devices
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
*(
|
*(
|
||||||
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from roborock.cloud_api import RoborockMqttClient
|
||||||
from roborock.containers import DeviceData, HomeDataDevice, HomeDataProduct, NetworkInfo
|
from roborock.containers import DeviceData, HomeDataDevice, HomeDataProduct, NetworkInfo
|
||||||
from roborock.exceptions import RoborockException
|
from roborock.exceptions import RoborockException
|
||||||
from roborock.local_api import RoborockLocalClient
|
from roborock.local_api import RoborockLocalClient
|
||||||
@ -30,6 +31,7 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
|
|||||||
device: HomeDataDevice,
|
device: HomeDataDevice,
|
||||||
device_networking: NetworkInfo,
|
device_networking: NetworkInfo,
|
||||||
product_info: HomeDataProduct,
|
product_info: HomeDataProduct,
|
||||||
|
cloud_api: RoborockMqttClient | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL)
|
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)
|
device_data = DeviceData(device, product_info.model, device_networking.ip)
|
||||||
self.api = RoborockLocalClient(device_data)
|
self.api = RoborockLocalClient(device_data)
|
||||||
|
self.cloud_api = cloud_api
|
||||||
self.device_info = DeviceInfo(
|
self.device_info = DeviceInfo(
|
||||||
name=self.roborock_device_info.device.name,
|
name=self.roborock_device_info.device.name,
|
||||||
identifiers={(DOMAIN, self.roborock_device_info.device.duid)},
|
identifiers={(DOMAIN, self.roborock_device_info.device.duid)},
|
||||||
@ -49,6 +52,21 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
|
|||||||
sw_version=self.roborock_device_info.device.fv,
|
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:
|
async def release(self) -> None:
|
||||||
"""Disconnect from API."""
|
"""Disconnect from API."""
|
||||||
await self.api.async_disconnect()
|
await self.api.async_disconnect()
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from roborock.api import AttributeCache
|
from roborock.api import AttributeCache, RoborockClient
|
||||||
from roborock.command_cache import CacheableAttribute
|
from roborock.command_cache import CacheableAttribute
|
||||||
from roborock.containers import Status
|
from roborock.containers import Status
|
||||||
from roborock.exceptions import RoborockException
|
from roborock.exceptions import RoborockException
|
||||||
from roborock.local_api import RoborockLocalClient
|
|
||||||
from roborock.roborock_typing import RoborockCommand
|
from roborock.roborock_typing import RoborockCommand
|
||||||
|
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
@ -22,7 +21,7 @@ class RoborockEntity(Entity):
|
|||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, unique_id: str, device_info: DeviceInfo, api: RoborockLocalClient
|
self, unique_id: str, device_info: DeviceInfo, api: RoborockClient
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the coordinated Roborock Device."""
|
"""Initialize the coordinated Roborock Device."""
|
||||||
self._attr_unique_id = unique_id
|
self._attr_unique_id = unique_id
|
||||||
@ -30,8 +29,8 @@ class RoborockEntity(Entity):
|
|||||||
self._api = api
|
self._api = api
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api(self) -> RoborockLocalClient:
|
def api(self) -> RoborockClient:
|
||||||
"""Return the Api."""
|
"""Returns the api."""
|
||||||
return self._api
|
return self._api
|
||||||
|
|
||||||
def get_cache(self, attribute: CacheableAttribute) -> AttributeCache:
|
def get_cache(self, attribute: CacheableAttribute) -> AttributeCache:
|
||||||
|
@ -9,13 +9,11 @@ from typing import Any
|
|||||||
|
|
||||||
from roborock.api import AttributeCache
|
from roborock.api import AttributeCache
|
||||||
from roborock.command_cache import CacheableAttribute
|
from roborock.command_cache import CacheableAttribute
|
||||||
from roborock.local_api import RoborockLocalClient
|
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import EntityCategory
|
from homeassistant.const import EntityCategory
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
@ -121,9 +119,8 @@ async def async_setup_entry(
|
|||||||
valid_entities.append(
|
valid_entities.append(
|
||||||
RoborockSwitch(
|
RoborockSwitch(
|
||||||
f"{description.key}_{slugify(coordinator.roborock_device_info.device.duid)}",
|
f"{description.key}_{slugify(coordinator.roborock_device_info.device.duid)}",
|
||||||
coordinator.device_info,
|
coordinator,
|
||||||
description,
|
description,
|
||||||
coordinator.api,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
async_add_entities(valid_entities)
|
async_add_entities(valid_entities)
|
||||||
@ -137,13 +134,12 @@ class RoborockSwitch(RoborockEntity, SwitchEntity):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
unique_id: str,
|
unique_id: str,
|
||||||
device_info: DeviceInfo,
|
coordinator: RoborockDataUpdateCoordinator,
|
||||||
description: RoborockSwitchDescription,
|
entity_description: RoborockSwitchDescription,
|
||||||
api: RoborockLocalClient,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the entity."""
|
"""Initialize the entity."""
|
||||||
super().__init__(unique_id, device_info, api)
|
self.entity_description = entity_description
|
||||||
self.entity_description = description
|
super().__init__(unique_id, coordinator.device_info, coordinator.api)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn off the switch."""
|
"""Turn off the switch."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user