python-roborock 3.3.2__tar.gz → 3.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_roborock-3.3.2 → python_roborock-3.7.1}/PKG-INFO +1 -1
- {python_roborock-3.3.2 → python_roborock-3.7.1}/pyproject.toml +1 -1
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/cli.py +11 -8
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/cache.py +5 -2
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/device_manager.py +52 -27
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/__init__.py +10 -1
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/clean_summary.py +2 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/command.py +2 -1
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/home.py +76 -29
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/map_content.py +24 -0
- python_roborock-3.7.1/roborock/devices/traits/v1/routines.py +26 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/valley_electricity_timer.py +1 -1
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/web_api.py +26 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/.gitignore +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/LICENSE +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/README.md +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/api.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/broadcast_protocol.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/callbacks.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/cloud_api.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/command_cache.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/const.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/b01_q10/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/b01_q7/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/code_mappings.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/containers.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/dyad/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/dyad/dyad_code_mappings.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/dyad/dyad_containers.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/v1/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/v1/v1_clean_modes.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/v1/v1_code_mappings.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/v1/v1_containers.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/zeo/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/zeo/zeo_code_mappings.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/zeo/zeo_containers.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/device_features.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/README.md +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/a01_channel.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/b01_channel.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/channel.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/device.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/local_channel.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/mqtt_channel.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/a01/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/b01/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/traits_mixin.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/child_lock.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/common.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/consumeable.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/device_features.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/flow_led_status.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/led_status.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/maps.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/network_info.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/rooms.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/status.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/volume.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/v1_channel.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/v1_rpc_channel.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/exceptions.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/map/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/map/map_parser.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/mqtt/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/mqtt/roborock_session.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/mqtt/session.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/protocol.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/protocols/a01_protocol.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/protocols/b01_protocol.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/protocols/v1_protocol.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/py.typed +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/roborock_future.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/roborock_message.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/roborock_typing.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/util.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_1_apis/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_1_apis/roborock_client_v1.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_1_apis/roborock_local_client_v1.py +1 -1
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_1_apis/roborock_mqtt_client_v1.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_a01_apis/__init__.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
- {python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-roborock
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.7.1
|
|
4
4
|
Summary: A package to control Roborock vacuums.
|
|
5
5
|
Project-URL: Repository, https://github.com/humbertogontijo/python-roborock
|
|
6
6
|
Project-URL: Documentation, https://python-roborock.readthedocs.io/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "python-roborock"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.7.1"
|
|
4
4
|
description = "A package to control Roborock vacuums."
|
|
5
5
|
authors = [{ name = "humbertogontijo", email = "humbertogontijo@users.noreply.github.com" }, {name="Lash-L"}, {name="allenporter"}]
|
|
6
6
|
requires-python = ">=3.11, <4"
|
|
@@ -46,7 +46,7 @@ from roborock.data import CombinedMapInfo, DeviceData, HomeData, NetworkInfo, Ro
|
|
|
46
46
|
from roborock.device_features import DeviceFeatures
|
|
47
47
|
from roborock.devices.cache import Cache, CacheData
|
|
48
48
|
from roborock.devices.device import RoborockDevice
|
|
49
|
-
from roborock.devices.device_manager import DeviceManager,
|
|
49
|
+
from roborock.devices.device_manager import DeviceManager, UserParams, create_device_manager
|
|
50
50
|
from roborock.devices.traits import Trait
|
|
51
51
|
from roborock.devices.traits.v1 import V1TraitMixin
|
|
52
52
|
from roborock.devices.traits.v1.consumeable import ConsumableAttribute
|
|
@@ -118,7 +118,7 @@ class ConnectionCache(RoborockBase):
|
|
|
118
118
|
email: str
|
|
119
119
|
home_data: HomeData | None = None
|
|
120
120
|
network_info: dict[str, NetworkInfo] | None = None
|
|
121
|
-
|
|
121
|
+
home_map_info: dict[int, CombinedMapInfo] | None = None
|
|
122
122
|
trait_data: dict[str, Any] | None = None
|
|
123
123
|
|
|
124
124
|
|
|
@@ -135,8 +135,11 @@ class DeviceConnectionManager:
|
|
|
135
135
|
"""Ensure device manager is initialized."""
|
|
136
136
|
if self.device_manager is None:
|
|
137
137
|
cache_data = self.context.cache_data()
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
user_params = UserParams(
|
|
139
|
+
username=cache_data.email,
|
|
140
|
+
user_data=cache_data.user_data,
|
|
141
|
+
)
|
|
142
|
+
self.device_manager = await create_device_manager(user_params, cache=self.context)
|
|
140
143
|
# Cache devices for quick lookup
|
|
141
144
|
devices = await self.device_manager.get_devices()
|
|
142
145
|
self._devices = {device.duid: device for device in devices}
|
|
@@ -267,7 +270,7 @@ class RoborockContext(Cache):
|
|
|
267
270
|
return CacheData(
|
|
268
271
|
home_data=connection_cache.home_data,
|
|
269
272
|
network_info=connection_cache.network_info or {},
|
|
270
|
-
|
|
273
|
+
home_map_info=connection_cache.home_map_info,
|
|
271
274
|
trait_data=connection_cache.trait_data or {},
|
|
272
275
|
)
|
|
273
276
|
|
|
@@ -277,7 +280,7 @@ class RoborockContext(Cache):
|
|
|
277
280
|
connection_cache = self.cache_data()
|
|
278
281
|
connection_cache.home_data = value.home_data
|
|
279
282
|
connection_cache.network_info = value.network_info
|
|
280
|
-
connection_cache.
|
|
283
|
+
connection_cache.home_map_info = value.home_map_info
|
|
281
284
|
connection_cache.trait_data = value.trait_data
|
|
282
285
|
self.update(connection_cache)
|
|
283
286
|
|
|
@@ -717,14 +720,14 @@ async def home(ctx, device_id: str, refresh: bool):
|
|
|
717
720
|
await home_trait.refresh()
|
|
718
721
|
|
|
719
722
|
# Display the discovered home cache
|
|
720
|
-
if home_trait.
|
|
723
|
+
if home_trait.home_map_info:
|
|
721
724
|
cache_summary = {
|
|
722
725
|
map_flag: {
|
|
723
726
|
"name": map_data.name,
|
|
724
727
|
"room_count": len(map_data.rooms),
|
|
725
728
|
"rooms": [{"segment_id": room.segment_id, "name": room.name} for room in map_data.rooms],
|
|
726
729
|
}
|
|
727
|
-
for map_flag, map_data in home_trait.
|
|
730
|
+
for map_flag, map_data in home_trait.home_map_info.items()
|
|
728
731
|
}
|
|
729
732
|
click.echo(dump_json(cache_summary))
|
|
730
733
|
else:
|
|
@@ -22,8 +22,11 @@ class CacheData:
|
|
|
22
22
|
network_info: dict[str, NetworkInfo] = field(default_factory=dict)
|
|
23
23
|
"""Network information indexed by device DUID."""
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
"""Home
|
|
25
|
+
home_map_info: dict[int, CombinedMapInfo] = field(default_factory=dict)
|
|
26
|
+
"""Home map information indexed by map_flag."""
|
|
27
|
+
|
|
28
|
+
home_map_content: dict[int, bytes] = field(default_factory=dict)
|
|
29
|
+
"""Home cache content for each map data indexed by map_flag."""
|
|
27
30
|
|
|
28
31
|
device_features: DeviceFeatures | None = None
|
|
29
32
|
"""Device features information."""
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import enum
|
|
5
5
|
import logging
|
|
6
|
-
from collections.abc import
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from dataclasses import dataclass
|
|
7
8
|
|
|
8
9
|
import aiohttp
|
|
9
10
|
|
|
@@ -18,7 +19,7 @@ from roborock.map.map_parser import MapParserConfig
|
|
|
18
19
|
from roborock.mqtt.roborock_session import create_lazy_mqtt_session
|
|
19
20
|
from roborock.mqtt.session import MqttSession
|
|
20
21
|
from roborock.protocol import create_mqtt_params
|
|
21
|
-
from roborock.web_api import RoborockApiClient
|
|
22
|
+
from roborock.web_api import RoborockApiClient, UserWebApiClient
|
|
22
23
|
|
|
23
24
|
from .cache import Cache, NoCache
|
|
24
25
|
from .channel import Channel
|
|
@@ -30,12 +31,11 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
30
31
|
|
|
31
32
|
__all__ = [
|
|
32
33
|
"create_device_manager",
|
|
33
|
-
"
|
|
34
|
+
"UserParams",
|
|
34
35
|
"DeviceManager",
|
|
35
36
|
]
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
HomeDataApi = Callable[[], Awaitable[HomeData]]
|
|
39
39
|
DeviceCreator = Callable[[HomeData, HomeDataDevice, HomeDataProduct], RoborockDevice]
|
|
40
40
|
|
|
41
41
|
|
|
@@ -53,7 +53,7 @@ class DeviceManager:
|
|
|
53
53
|
|
|
54
54
|
def __init__(
|
|
55
55
|
self,
|
|
56
|
-
|
|
56
|
+
web_api: UserWebApiClient,
|
|
57
57
|
device_creator: DeviceCreator,
|
|
58
58
|
mqtt_session: MqttSession,
|
|
59
59
|
cache: Cache,
|
|
@@ -62,7 +62,7 @@ class DeviceManager:
|
|
|
62
62
|
|
|
63
63
|
This takes ownership of the MQTT session and will close it when the manager is closed.
|
|
64
64
|
"""
|
|
65
|
-
self.
|
|
65
|
+
self._web_api = web_api
|
|
66
66
|
self._cache = cache
|
|
67
67
|
self._device_creator = device_creator
|
|
68
68
|
self._devices: dict[str, RoborockDevice] = {}
|
|
@@ -73,7 +73,7 @@ class DeviceManager:
|
|
|
73
73
|
cache_data = await self._cache.get()
|
|
74
74
|
if not cache_data.home_data:
|
|
75
75
|
_LOGGER.debug("No cached home data found, fetching from API")
|
|
76
|
-
cache_data.home_data = await self.
|
|
76
|
+
cache_data.home_data = await self._web_api.get_home_data()
|
|
77
77
|
await self._cache.set(cache_data)
|
|
78
78
|
home_data = cache_data.home_data
|
|
79
79
|
|
|
@@ -108,45 +108,69 @@ class DeviceManager:
|
|
|
108
108
|
await asyncio.gather(*tasks)
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
"""Create a home data API wrapper.
|
|
111
|
+
@dataclass
|
|
112
|
+
class UserParams:
|
|
113
|
+
"""Parameters for creating a new session with Roborock devices.
|
|
115
114
|
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
These parameters include the username, user data for authentication,
|
|
116
|
+
and an optional base URL for the Roborock API. The `user_data` and `base_url`
|
|
117
|
+
parameters are obtained from `RoborockApiClient` during the login process.
|
|
118
118
|
"""
|
|
119
|
-
# Note: This will auto discover the API base URL. This can be improved
|
|
120
|
-
# by caching this next to `UserData` if needed to avoid unnecessary API calls.
|
|
121
|
-
client = RoborockApiClient(username=email, base_url=base_url, session=session)
|
|
122
119
|
|
|
123
|
-
|
|
120
|
+
username: str
|
|
121
|
+
"""The username (email) used for logging in."""
|
|
122
|
+
|
|
123
|
+
user_data: UserData
|
|
124
|
+
"""This is the user data containing authentication information."""
|
|
125
|
+
|
|
126
|
+
base_url: str | None = None
|
|
127
|
+
"""Optional base URL for the Roborock API.
|
|
128
|
+
|
|
129
|
+
This is used to speed up connection times by avoiding the need to
|
|
130
|
+
discover the API base URL each time. If not provided, the API client
|
|
131
|
+
will attempt to discover it automatically which may take multiple requests.
|
|
132
|
+
"""
|
|
124
133
|
|
|
125
134
|
|
|
126
|
-
def
|
|
135
|
+
def create_web_api_wrapper(
|
|
136
|
+
user_params: UserParams,
|
|
137
|
+
*,
|
|
138
|
+
cache: Cache | None = None,
|
|
139
|
+
session: aiohttp.ClientSession | None = None,
|
|
140
|
+
) -> UserWebApiClient:
|
|
127
141
|
"""Create a home data API wrapper from an existing API client."""
|
|
128
142
|
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
# Note: This will auto discover the API base URL. This can be improved
|
|
144
|
+
# by caching this next to `UserData` if needed to avoid unnecessary API calls.
|
|
145
|
+
client = RoborockApiClient(username=user_params.username, base_url=user_params.base_url, session=session)
|
|
131
146
|
|
|
132
|
-
return
|
|
147
|
+
return UserWebApiClient(client, user_params.user_data)
|
|
133
148
|
|
|
134
149
|
|
|
135
150
|
async def create_device_manager(
|
|
136
|
-
|
|
137
|
-
|
|
151
|
+
user_params: UserParams,
|
|
152
|
+
*,
|
|
138
153
|
cache: Cache | None = None,
|
|
139
154
|
map_parser_config: MapParserConfig | None = None,
|
|
155
|
+
session: aiohttp.ClientSession | None = None,
|
|
140
156
|
) -> DeviceManager:
|
|
141
157
|
"""Convenience function to create and initialize a DeviceManager.
|
|
142
158
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
159
|
+
Args:
|
|
160
|
+
user_params: Parameters for creating the user session.
|
|
161
|
+
cache: Optional cache implementation to use for caching device data.
|
|
162
|
+
map_parser_config: Optional configuration for parsing maps.
|
|
163
|
+
session: Optional aiohttp ClientSession to use for HTTP requests.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
An initialized DeviceManager with discovered devices.
|
|
146
167
|
"""
|
|
147
168
|
if cache is None:
|
|
148
169
|
cache = NoCache()
|
|
149
170
|
|
|
171
|
+
web_api = create_web_api_wrapper(user_params, session=session, cache=cache)
|
|
172
|
+
user_data = user_params.user_data
|
|
173
|
+
|
|
150
174
|
mqtt_params = create_mqtt_params(user_data.rriot)
|
|
151
175
|
mqtt_session = await create_lazy_mqtt_session(mqtt_params)
|
|
152
176
|
|
|
@@ -163,6 +187,7 @@ async def create_device_manager(
|
|
|
163
187
|
channel.rpc_channel,
|
|
164
188
|
channel.mqtt_rpc_channel,
|
|
165
189
|
channel.map_rpc_channel,
|
|
190
|
+
web_api,
|
|
166
191
|
cache,
|
|
167
192
|
map_parser_config=map_parser_config,
|
|
168
193
|
)
|
|
@@ -176,6 +201,6 @@ async def create_device_manager(
|
|
|
176
201
|
raise NotImplementedError(f"Device {device.name} has unsupported version {device.pv}")
|
|
177
202
|
return RoborockDevice(device, product, channel, trait)
|
|
178
203
|
|
|
179
|
-
manager = DeviceManager(
|
|
204
|
+
manager = DeviceManager(web_api, device_creator, mqtt_session=mqtt_session, cache=cache)
|
|
180
205
|
await manager.discover_devices()
|
|
181
206
|
return manager
|
|
@@ -40,6 +40,7 @@ from roborock.devices.cache import Cache
|
|
|
40
40
|
from roborock.devices.traits import Trait
|
|
41
41
|
from roborock.devices.v1_rpc_channel import V1RpcChannel
|
|
42
42
|
from roborock.map.map_parser import MapParserConfig
|
|
43
|
+
from roborock.web_api import UserWebApiClient
|
|
43
44
|
|
|
44
45
|
from .child_lock import ChildLockTrait
|
|
45
46
|
from .clean_summary import CleanSummaryTrait
|
|
@@ -56,6 +57,7 @@ from .map_content import MapContentTrait
|
|
|
56
57
|
from .maps import MapsTrait
|
|
57
58
|
from .network_info import NetworkInfoTrait
|
|
58
59
|
from .rooms import RoomsTrait
|
|
60
|
+
from .routines import RoutinesTrait
|
|
59
61
|
from .smart_wash_params import SmartWashParamsTrait
|
|
60
62
|
from .status import StatusTrait
|
|
61
63
|
from .valley_electricity_timer import ValleyElectricityTimerTrait
|
|
@@ -85,6 +87,7 @@ __all__ = [
|
|
|
85
87
|
"WashTowelModeTrait",
|
|
86
88
|
"SmartWashParamsTrait",
|
|
87
89
|
"NetworkInfoTrait",
|
|
90
|
+
"RoutinesTrait",
|
|
88
91
|
]
|
|
89
92
|
|
|
90
93
|
|
|
@@ -108,6 +111,7 @@ class PropertiesApi(Trait):
|
|
|
108
111
|
home: HomeTrait
|
|
109
112
|
device_features: DeviceFeaturesTrait
|
|
110
113
|
network_info: NetworkInfoTrait
|
|
114
|
+
routines: RoutinesTrait
|
|
111
115
|
|
|
112
116
|
# Optional features that may not be supported on all devices
|
|
113
117
|
child_lock: ChildLockTrait | None = None
|
|
@@ -126,6 +130,7 @@ class PropertiesApi(Trait):
|
|
|
126
130
|
rpc_channel: V1RpcChannel,
|
|
127
131
|
mqtt_rpc_channel: V1RpcChannel,
|
|
128
132
|
map_rpc_channel: V1RpcChannel,
|
|
133
|
+
web_api: UserWebApiClient,
|
|
129
134
|
cache: Cache,
|
|
130
135
|
map_parser_config: MapParserConfig | None = None,
|
|
131
136
|
) -> None:
|
|
@@ -134,6 +139,7 @@ class PropertiesApi(Trait):
|
|
|
134
139
|
self._rpc_channel = rpc_channel
|
|
135
140
|
self._mqtt_rpc_channel = mqtt_rpc_channel
|
|
136
141
|
self._map_rpc_channel = map_rpc_channel
|
|
142
|
+
self._web_api = web_api
|
|
137
143
|
self._cache = cache
|
|
138
144
|
|
|
139
145
|
self.status = StatusTrait(product)
|
|
@@ -141,9 +147,10 @@ class PropertiesApi(Trait):
|
|
|
141
147
|
self.rooms = RoomsTrait(home_data)
|
|
142
148
|
self.maps = MapsTrait(self.status)
|
|
143
149
|
self.map_content = MapContentTrait(map_parser_config)
|
|
144
|
-
self.home = HomeTrait(self.status, self.maps, self.rooms, cache)
|
|
150
|
+
self.home = HomeTrait(self.status, self.maps, self.map_content, self.rooms, cache)
|
|
145
151
|
self.device_features = DeviceFeaturesTrait(product.product_nickname, cache)
|
|
146
152
|
self.network_info = NetworkInfoTrait(device_uid, cache)
|
|
153
|
+
self.routines = RoutinesTrait(device_uid, web_api)
|
|
147
154
|
|
|
148
155
|
# Dynamically create any traits that need to be populated
|
|
149
156
|
for item in fields(self):
|
|
@@ -267,6 +274,7 @@ def create(
|
|
|
267
274
|
rpc_channel: V1RpcChannel,
|
|
268
275
|
mqtt_rpc_channel: V1RpcChannel,
|
|
269
276
|
map_rpc_channel: V1RpcChannel,
|
|
277
|
+
web_api: UserWebApiClient,
|
|
270
278
|
cache: Cache,
|
|
271
279
|
map_parser_config: MapParserConfig | None = None,
|
|
272
280
|
) -> PropertiesApi:
|
|
@@ -278,6 +286,7 @@ def create(
|
|
|
278
286
|
rpc_channel,
|
|
279
287
|
mqtt_rpc_channel,
|
|
280
288
|
map_rpc_channel,
|
|
289
|
+
web_api,
|
|
281
290
|
cache,
|
|
282
291
|
map_parser_config,
|
|
283
292
|
)
|
|
@@ -53,6 +53,8 @@ class CleanSummaryTrait(CleanSummaryWithDetail, common.V1TraitMixin):
|
|
|
53
53
|
@classmethod
|
|
54
54
|
def _parse_clean_record_response(cls, response: common.V1ResponseData) -> CleanRecord:
|
|
55
55
|
"""Parse the response from the device into a CleanRecord."""
|
|
56
|
+
if isinstance(response, list) and len(response) == 1:
|
|
57
|
+
response = response[0]
|
|
56
58
|
if isinstance(response, dict):
|
|
57
59
|
return CleanRecord.from_dict(response)
|
|
58
60
|
if isinstance(response, list):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
3
|
from roborock import RoborockCommand
|
|
4
|
+
from roborock.protocols.v1_protocol import ParamsType
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class CommandTrait:
|
|
@@ -14,7 +15,7 @@ class CommandTrait:
|
|
|
14
15
|
"""
|
|
15
16
|
self._rpc_channel = None
|
|
16
17
|
|
|
17
|
-
async def send(self, command: RoborockCommand | str, params:
|
|
18
|
+
async def send(self, command: RoborockCommand | str, params: ParamsType = None) -> Any:
|
|
18
19
|
"""Send a command to the device."""
|
|
19
20
|
if not self._rpc_channel:
|
|
20
21
|
raise ValueError("Device trait in invalid state")
|
|
@@ -20,6 +20,7 @@ from roborock.devices.traits.v1 import common
|
|
|
20
20
|
from roborock.exceptions import RoborockDeviceBusy, RoborockException
|
|
21
21
|
from roborock.roborock_typing import RoborockCommand
|
|
22
22
|
|
|
23
|
+
from .map_content import MapContent, MapContentTrait
|
|
23
24
|
from .maps import MapsTrait
|
|
24
25
|
from .rooms import RoomsTrait
|
|
25
26
|
from .status import StatusTrait
|
|
@@ -38,6 +39,7 @@ class HomeTrait(RoborockBase, common.V1TraitMixin):
|
|
|
38
39
|
self,
|
|
39
40
|
status_trait: StatusTrait,
|
|
40
41
|
maps_trait: MapsTrait,
|
|
42
|
+
map_content: MapContentTrait,
|
|
41
43
|
rooms_trait: RoomsTrait,
|
|
42
44
|
cache: Cache,
|
|
43
45
|
) -> None:
|
|
@@ -59,9 +61,11 @@ class HomeTrait(RoborockBase, common.V1TraitMixin):
|
|
|
59
61
|
super().__init__()
|
|
60
62
|
self._status_trait = status_trait
|
|
61
63
|
self._maps_trait = maps_trait
|
|
64
|
+
self._map_content = map_content
|
|
62
65
|
self._rooms_trait = rooms_trait
|
|
63
66
|
self._cache = cache
|
|
64
|
-
self.
|
|
67
|
+
self._home_map_info: dict[int, CombinedMapInfo] | None = None
|
|
68
|
+
self._home_map_content: dict[int, MapContent] | None = None
|
|
65
69
|
|
|
66
70
|
async def discover_home(self) -> None:
|
|
67
71
|
"""Iterate through all maps to discover rooms and cache them.
|
|
@@ -72,13 +76,21 @@ class HomeTrait(RoborockBase, common.V1TraitMixin):
|
|
|
72
76
|
cleaning process. This will raise `RoborockDeviceBusy` if the device is
|
|
73
77
|
currently cleaning.
|
|
74
78
|
|
|
75
|
-
After discovery, the home cache will be populated and can be accessed via the `
|
|
79
|
+
After discovery, the home cache will be populated and can be accessed via the `home_map_info` property.
|
|
76
80
|
"""
|
|
77
81
|
cache_data = await self._cache.get()
|
|
78
|
-
if cache_data.
|
|
82
|
+
if cache_data.home_map_info and cache_data.home_map_content:
|
|
79
83
|
_LOGGER.debug("Home cache already populated, skipping discovery")
|
|
80
|
-
self.
|
|
81
|
-
|
|
84
|
+
self._home_map_info = cache_data.home_map_info
|
|
85
|
+
try:
|
|
86
|
+
self._home_map_content = {
|
|
87
|
+
k: self._map_content.parse_map_content(v) for k, v in cache_data.home_map_content.items()
|
|
88
|
+
}
|
|
89
|
+
except (ValueError, RoborockException) as ex:
|
|
90
|
+
_LOGGER.warning("Failed to parse cached home map content, will re-discover: %s", ex)
|
|
91
|
+
self._home_map_content = {}
|
|
92
|
+
else:
|
|
93
|
+
return
|
|
82
94
|
|
|
83
95
|
if self._status_trait.state == RoborockStateCode.cleaning:
|
|
84
96
|
raise RoborockDeviceBusy("Cannot perform home discovery while the device is cleaning")
|
|
@@ -87,11 +99,11 @@ class HomeTrait(RoborockBase, common.V1TraitMixin):
|
|
|
87
99
|
if self._maps_trait.current_map_info is None:
|
|
88
100
|
raise RoborockException("Cannot perform home discovery without current map info")
|
|
89
101
|
|
|
90
|
-
|
|
91
|
-
_LOGGER.debug("Home discovery complete, caching data for %d maps", len(
|
|
92
|
-
await self._update_home_cache(
|
|
102
|
+
home_map_info, home_map_content = await self._build_home_map_info()
|
|
103
|
+
_LOGGER.debug("Home discovery complete, caching data for %d maps", len(home_map_info))
|
|
104
|
+
await self._update_home_cache(home_map_info, home_map_content)
|
|
93
105
|
|
|
94
|
-
async def
|
|
106
|
+
async def _refresh_map_info(self, map_info) -> CombinedMapInfo:
|
|
95
107
|
"""Collect room data for a specific map and return CombinedMapInfo."""
|
|
96
108
|
await self._rooms_trait.refresh()
|
|
97
109
|
return CombinedMapInfo(
|
|
@@ -100,9 +112,19 @@ class HomeTrait(RoborockBase, common.V1TraitMixin):
|
|
|
100
112
|
rooms=self._rooms_trait.rooms or [],
|
|
101
113
|
)
|
|
102
114
|
|
|
103
|
-
async def
|
|
104
|
-
"""
|
|
105
|
-
|
|
115
|
+
async def _refresh_map_content(self) -> MapContent:
|
|
116
|
+
"""Refresh the map content trait to get the latest map data."""
|
|
117
|
+
await self._map_content.refresh()
|
|
118
|
+
return MapContent(
|
|
119
|
+
image_content=self._map_content.image_content,
|
|
120
|
+
map_data=self._map_content.map_data,
|
|
121
|
+
raw_api_response=self._map_content.raw_api_response,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
async def _build_home_map_info(self) -> tuple[dict[int, CombinedMapInfo], dict[int, MapContent]]:
|
|
125
|
+
"""Perform the actual discovery and caching of home map info and content."""
|
|
126
|
+
home_map_info: dict[int, CombinedMapInfo] = {}
|
|
127
|
+
home_map_content: dict[int, MapContent] = {}
|
|
106
128
|
|
|
107
129
|
# Sort map_info to process the current map last, reducing map switching.
|
|
108
130
|
# False (non-original maps) sorts before True (original map). We ensure
|
|
@@ -120,9 +142,12 @@ class HomeTrait(RoborockBase, common.V1TraitMixin):
|
|
|
120
142
|
await self._maps_trait.set_current_map(map_info.map_flag)
|
|
121
143
|
await asyncio.sleep(MAP_SLEEP)
|
|
122
144
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
145
|
+
map_content = await self._refresh_map_content()
|
|
146
|
+
home_map_content[map_info.map_flag] = map_content
|
|
147
|
+
|
|
148
|
+
combined_map_info = await self._refresh_map_info(map_info)
|
|
149
|
+
home_map_info[map_info.map_flag] = combined_map_info
|
|
150
|
+
return home_map_info, home_map_content
|
|
126
151
|
|
|
127
152
|
async def refresh(self) -> Self:
|
|
128
153
|
"""Refresh current map's underlying map and room data, updating cache as needed.
|
|
@@ -131,7 +156,7 @@ class HomeTrait(RoborockBase, common.V1TraitMixin):
|
|
|
131
156
|
active maps or re-discover the home. It is expected that this will keep
|
|
132
157
|
information up to date for the current map as users switch to that map.
|
|
133
158
|
"""
|
|
134
|
-
if self.
|
|
159
|
+
if self._home_map_info is None:
|
|
135
160
|
raise RoborockException("Cannot refresh home data without home cache, did you call discover_home()?")
|
|
136
161
|
|
|
137
162
|
# Refresh the list of map names/info
|
|
@@ -141,34 +166,56 @@ class HomeTrait(RoborockBase, common.V1TraitMixin):
|
|
|
141
166
|
) is None:
|
|
142
167
|
raise RoborockException("Cannot refresh home data without current map info")
|
|
143
168
|
|
|
169
|
+
# Refresh the map content to ensure we have the latest image and object positions
|
|
170
|
+
new_map_content = await self._refresh_map_content()
|
|
144
171
|
# Refresh the current map's room data
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
map_data = await self._refresh_map_data(current_map_info)
|
|
148
|
-
if map_data != current_map_data:
|
|
149
|
-
await self._update_home_cache({**self._home_cache, map_flag: map_data})
|
|
150
|
-
|
|
172
|
+
combined_map_info = await self._refresh_map_info(current_map_info)
|
|
173
|
+
await self._update_current_map_cache(map_flag, combined_map_info, new_map_content)
|
|
151
174
|
return self
|
|
152
175
|
|
|
153
176
|
@property
|
|
154
|
-
def
|
|
177
|
+
def home_map_info(self) -> dict[int, CombinedMapInfo] | None:
|
|
155
178
|
"""Returns the map information for all cached maps."""
|
|
156
|
-
return self.
|
|
179
|
+
return self._home_map_info
|
|
157
180
|
|
|
158
181
|
@property
|
|
159
182
|
def current_map_data(self) -> CombinedMapInfo | None:
|
|
160
183
|
"""Returns the map data for the current map."""
|
|
161
184
|
current_map_flag = self._maps_trait.current_map
|
|
162
|
-
if current_map_flag is None or self.
|
|
185
|
+
if current_map_flag is None or self._home_map_info is None:
|
|
163
186
|
return None
|
|
164
|
-
return self.
|
|
187
|
+
return self._home_map_info.get(current_map_flag)
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def home_map_content(self) -> dict[int, MapContent] | None:
|
|
191
|
+
"""Returns the map content for all cached maps."""
|
|
192
|
+
return self._home_map_content
|
|
165
193
|
|
|
166
194
|
def _parse_response(self, response: common.V1ResponseData) -> Self:
|
|
167
195
|
"""This trait does not parse responses directly."""
|
|
168
196
|
raise NotImplementedError("HomeTrait does not support direct command responses")
|
|
169
197
|
|
|
170
|
-
async def _update_home_cache(
|
|
198
|
+
async def _update_home_cache(
|
|
199
|
+
self, home_map_info: dict[int, CombinedMapInfo], home_map_content: dict[int, MapContent]
|
|
200
|
+
) -> None:
|
|
201
|
+
"""Update the entire home cache with new map info and content."""
|
|
202
|
+
cache_data = await self._cache.get()
|
|
203
|
+
cache_data.home_map_info = home_map_info
|
|
204
|
+
cache_data.home_map_content = {k: v.raw_api_response for k, v in home_map_content.items() if v.raw_api_response}
|
|
205
|
+
await self._cache.set(cache_data)
|
|
206
|
+
self._home_map_info = home_map_info
|
|
207
|
+
self._home_map_content = home_map_content
|
|
208
|
+
|
|
209
|
+
async def _update_current_map_cache(
|
|
210
|
+
self, map_flag: int, map_info: CombinedMapInfo, map_content: MapContent
|
|
211
|
+
) -> None:
|
|
212
|
+
"""Update the cache for the current map only."""
|
|
171
213
|
cache_data = await self._cache.get()
|
|
172
|
-
cache_data.
|
|
214
|
+
cache_data.home_map_info[map_flag] = map_info
|
|
215
|
+
if map_content.raw_api_response:
|
|
216
|
+
cache_data.home_map_content[map_flag] = map_content.raw_api_response
|
|
173
217
|
await self._cache.set(cache_data)
|
|
174
|
-
self.
|
|
218
|
+
if self._home_map_info is None or self._home_map_content is None:
|
|
219
|
+
raise RoborockException("Home cache is not initialized, cannot update current map cache")
|
|
220
|
+
self._home_map_info[map_flag] = map_info
|
|
221
|
+
self._home_map_content[map_flag] = map_content
|
|
@@ -25,6 +25,13 @@ class MapContent(RoborockBase):
|
|
|
25
25
|
map_data: MapData | None = None
|
|
26
26
|
"""The parsed map data which contains metadata for points on the map."""
|
|
27
27
|
|
|
28
|
+
raw_api_response: bytes | None = None
|
|
29
|
+
"""The raw bytes of the map data from the API for caching for future use.
|
|
30
|
+
|
|
31
|
+
This should be treated as an opaque blob used only internally by this library
|
|
32
|
+
to re-parse the map data when needed.
|
|
33
|
+
"""
|
|
34
|
+
|
|
28
35
|
def __repr__(self) -> str:
|
|
29
36
|
"""Return a string representation of the MapContent."""
|
|
30
37
|
img = self.image_content
|
|
@@ -48,7 +55,23 @@ class MapContentTrait(MapContent, common.V1TraitMixin):
|
|
|
48
55
|
"""Parse the response from the device into a MapContentTrait instance."""
|
|
49
56
|
if not isinstance(response, bytes):
|
|
50
57
|
raise ValueError(f"Unexpected MapContentTrait response format: {type(response)}")
|
|
58
|
+
return self.parse_map_content(response)
|
|
59
|
+
|
|
60
|
+
def parse_map_content(self, response: bytes) -> MapContent:
|
|
61
|
+
"""Parse the map content from raw bytes.
|
|
62
|
+
|
|
63
|
+
This method is exposed so that cached map data can be parsed without
|
|
64
|
+
needing to go through the RPC channel.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
response: The raw bytes of the map data from the API.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
MapContent: The parsed map content.
|
|
51
71
|
|
|
72
|
+
Raises:
|
|
73
|
+
RoborockException: If the map data cannot be parsed.
|
|
74
|
+
"""
|
|
52
75
|
parsed_data = self._map_parser.parse(response)
|
|
53
76
|
if parsed_data is None:
|
|
54
77
|
raise ValueError("Failed to parse map data")
|
|
@@ -56,4 +79,5 @@ class MapContentTrait(MapContent, common.V1TraitMixin):
|
|
|
56
79
|
return MapContent(
|
|
57
80
|
image_content=parsed_data.image_content,
|
|
58
81
|
map_data=parsed_data.map_data,
|
|
82
|
+
raw_api_response=response,
|
|
59
83
|
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Routines trait for V1 devices."""
|
|
2
|
+
|
|
3
|
+
from roborock.data.containers import HomeDataScene
|
|
4
|
+
from roborock.web_api import UserWebApiClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RoutinesTrait:
|
|
8
|
+
"""Trait for interacting with routines."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, device_id: str, web_api: UserWebApiClient) -> None:
|
|
11
|
+
"""Initialize the routines trait."""
|
|
12
|
+
self._device_id = device_id
|
|
13
|
+
self._web_api = web_api
|
|
14
|
+
|
|
15
|
+
async def get_routines(self) -> list[HomeDataScene]:
|
|
16
|
+
"""Get available routines."""
|
|
17
|
+
return await self._web_api.get_routines(self._device_id)
|
|
18
|
+
|
|
19
|
+
async def execute_routine(self, routine_id: int) -> None:
|
|
20
|
+
"""Execute a routine by its ID.
|
|
21
|
+
|
|
22
|
+
Technically, routines are per-device, but the API does not
|
|
23
|
+
require the device ID to execute them. This can execute a
|
|
24
|
+
routine for any device but it is exposed here for convenience.
|
|
25
|
+
"""
|
|
26
|
+
await self._web_api.execute_routine(routine_id)
|
|
@@ -5,7 +5,7 @@ from roborock.roborock_typing import RoborockCommand
|
|
|
5
5
|
_ENABLED_PARAM = "enabled"
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class ValleyElectricityTimerTrait(ValleyElectricityTimer, common.V1TraitMixin):
|
|
8
|
+
class ValleyElectricityTimerTrait(ValleyElectricityTimer, common.V1TraitMixin, common.RoborockSwitchBase):
|
|
9
9
|
"""Trait for managing Valley Electricity Timer settings on Roborock devices."""
|
|
10
10
|
|
|
11
11
|
command = RoborockCommand.GET_VALLEY_ELECTRICITY_TIMER
|
|
@@ -707,3 +707,29 @@ def _get_hawk_authentication(rriot: RRiot, url: str, formdata: dict | None = Non
|
|
|
707
707
|
)
|
|
708
708
|
mac = base64.b64encode(hmac.new(rriot.h.encode(), prestr.encode(), hashlib.sha256).digest()).decode()
|
|
709
709
|
return f'Hawk id="{rriot.u}",s="{rriot.s}",ts="{timestamp}",nonce="{nonce}",mac="{mac}"'
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
class UserWebApiClient:
|
|
713
|
+
"""Wrapper around RoborockApiClient to provide information for a specific user.
|
|
714
|
+
|
|
715
|
+
This binds a RoborockApiClient to a specific user context with the
|
|
716
|
+
provided UserData. This allows for easier access to user-specific data,
|
|
717
|
+
to avoid needing to pass UserData around and mock out the web API.
|
|
718
|
+
"""
|
|
719
|
+
|
|
720
|
+
def __init__(self, web_api: RoborockApiClient, user_data: UserData) -> None:
|
|
721
|
+
"""Initialize the wrapper with the API client and user data."""
|
|
722
|
+
self._web_api = web_api
|
|
723
|
+
self._user_data = user_data
|
|
724
|
+
|
|
725
|
+
async def get_home_data(self) -> HomeData:
|
|
726
|
+
"""Fetch home data using the API client."""
|
|
727
|
+
return await self._web_api.get_home_data_v3(self._user_data)
|
|
728
|
+
|
|
729
|
+
async def get_routines(self, device_id: str) -> list[HomeDataScene]:
|
|
730
|
+
"""Fetch routines (scenes) for a specific device."""
|
|
731
|
+
return await self._web_api.get_scenes(self._user_data, device_id)
|
|
732
|
+
|
|
733
|
+
async def execute_routine(self, scene_id: int) -> None:
|
|
734
|
+
"""Execute a specific routine (scene) by its ID."""
|
|
735
|
+
await self._web_api.execute_scene(self._user_data, scene_id)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/b01_q10/b01_q10_code_mappings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/data/b01_q7/b01_q7_code_mappings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/device_features.py
RENAMED
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/do_not_disturb.py
RENAMED
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/dust_collection_mode.py
RENAMED
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/flow_led_status.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/smart_wash_params.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/devices/traits/v1/wash_towel_mode.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_1_apis/roborock_client_v1.py
RENAMED
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_1_apis/roborock_local_client_v1.py
RENAMED
|
@@ -210,6 +210,7 @@ class RoborockLocalClientV1(RoborockClientV1, RoborockClient):
|
|
|
210
210
|
version=self.local_protocol_version,
|
|
211
211
|
)
|
|
212
212
|
self._logger.debug("Building message id %s for method %s", request_message.request_id, method)
|
|
213
|
+
await self._validate_connection()
|
|
213
214
|
return await self._send_message(
|
|
214
215
|
roborock_message,
|
|
215
216
|
request_id=request_message.request_id,
|
|
@@ -226,7 +227,6 @@ class RoborockLocalClientV1(RoborockClientV1, RoborockClient):
|
|
|
226
227
|
method: str | None = None,
|
|
227
228
|
params: list | dict | int | None = None,
|
|
228
229
|
) -> RoborockMessage:
|
|
229
|
-
await self._validate_connection()
|
|
230
230
|
msg = self._encoder(roborock_message)
|
|
231
231
|
if method:
|
|
232
232
|
self._logger.debug(f"id={request_id} Requesting method {method} with {params}")
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_1_apis/roborock_mqtt_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-3.3.2 → python_roborock-3.7.1}/roborock/version_a01_apis/roborock_client_a01.py
RENAMED
|
File without changes
|
|
File without changes
|