python-roborock 2.30.0__tar.gz → 2.32.0__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-2.30.0 → python_roborock-2.32.0}/PKG-INFO +1 -1
- {python_roborock-2.30.0 → python_roborock-2.32.0}/pyproject.toml +1 -1
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/cli.py +81 -63
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/containers.py +7 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/LICENSE +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/README.md +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/__init__.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/api.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/cloud_api.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/code_mappings.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/command_cache.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/const.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/device_features.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/README.md +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/__init__.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/a01_channel.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/channel.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/device.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/device_manager.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/local_channel.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/mqtt_channel.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/traits/dyad.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/traits/status.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/traits/trait.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/traits/zeo.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/v1_channel.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/devices/v1_rpc_channel.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/exceptions.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/local_api.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/mqtt/__init__.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/mqtt/roborock_session.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/mqtt/session.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/protocol.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/protocols/a01_protocol.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/protocols/v1_protocol.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/py.typed +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/roborock_future.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/roborock_message.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/roborock_typing.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/util.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/version_1_apis/__init__.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/version_1_apis/roborock_client_v1.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/version_1_apis/roborock_local_client_v1.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/version_a01_apis/__init__.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +0 -0
- {python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/web_api.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
import asyncio
|
|
4
2
|
import json
|
|
5
3
|
import logging
|
|
4
|
+
from dataclasses import dataclass
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
from typing import Any
|
|
8
7
|
|
|
@@ -12,7 +11,7 @@ from pyshark.capture.live_capture import LiveCapture, UnknownInterfaceException
|
|
|
12
11
|
from pyshark.packet.packet import Packet # type: ignore
|
|
13
12
|
|
|
14
13
|
from roborock import RoborockException
|
|
15
|
-
from roborock.containers import DeviceData, HomeData, HomeDataProduct, LoginData
|
|
14
|
+
from roborock.containers import DeviceData, HomeData, HomeDataProduct, LoginData, NetworkInfo, RoborockBase, UserData
|
|
16
15
|
from roborock.devices.device_manager import create_device_manager, create_home_data_api
|
|
17
16
|
from roborock.protocol import MessageParser
|
|
18
17
|
from roborock.util import run_sync
|
|
@@ -23,9 +22,26 @@ from roborock.web_api import RoborockApiClient
|
|
|
23
22
|
_LOGGER = logging.getLogger(__name__)
|
|
24
23
|
|
|
25
24
|
|
|
25
|
+
@dataclass
|
|
26
|
+
class ConnectionCache(RoborockBase):
|
|
27
|
+
"""Cache for Roborock data.
|
|
28
|
+
|
|
29
|
+
This is used to store data retrieved from the Roborock API, such as user
|
|
30
|
+
data and home data to avoid repeated API calls.
|
|
31
|
+
|
|
32
|
+
This cache is superset of `LoginData` since we used to directly store that
|
|
33
|
+
dataclass, but now we also store additional data.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
user_data: UserData
|
|
37
|
+
email: str
|
|
38
|
+
home_data: HomeData | None = None
|
|
39
|
+
network_info: dict[str, NetworkInfo] | None = None
|
|
40
|
+
|
|
41
|
+
|
|
26
42
|
class RoborockContext:
|
|
27
43
|
roborock_file = Path("~/.roborock").expanduser()
|
|
28
|
-
|
|
44
|
+
_cache_data: ConnectionCache | None = None
|
|
29
45
|
|
|
30
46
|
def __init__(self):
|
|
31
47
|
self.reload()
|
|
@@ -35,22 +51,22 @@ class RoborockContext:
|
|
|
35
51
|
with open(self.roborock_file) as f:
|
|
36
52
|
data = json.load(f)
|
|
37
53
|
if data:
|
|
38
|
-
self.
|
|
54
|
+
self._cache_data = ConnectionCache.from_dict(data)
|
|
39
55
|
|
|
40
|
-
def update(self,
|
|
41
|
-
data = json.dumps(
|
|
56
|
+
def update(self, cache_data: ConnectionCache):
|
|
57
|
+
data = json.dumps(cache_data.as_dict(), default=vars, indent=4)
|
|
42
58
|
with open(self.roborock_file, "w") as f:
|
|
43
59
|
f.write(data)
|
|
44
60
|
self.reload()
|
|
45
61
|
|
|
46
62
|
def validate(self):
|
|
47
|
-
if self.
|
|
63
|
+
if self._cache_data is None:
|
|
48
64
|
raise RoborockException("You must login first")
|
|
49
65
|
|
|
50
|
-
def
|
|
51
|
-
"""Get the
|
|
66
|
+
def cache_data(self) -> ConnectionCache:
|
|
67
|
+
"""Get the cache data."""
|
|
52
68
|
self.validate()
|
|
53
|
-
return self.
|
|
69
|
+
return self._cache_data
|
|
54
70
|
|
|
55
71
|
|
|
56
72
|
@click.option("-d", "--debug", default=False, count=True)
|
|
@@ -99,18 +115,18 @@ async def login(ctx, email, password):
|
|
|
99
115
|
@run_sync()
|
|
100
116
|
async def session(ctx, duration: int):
|
|
101
117
|
context: RoborockContext = ctx.obj
|
|
102
|
-
|
|
118
|
+
cache_data = context.cache_data()
|
|
103
119
|
|
|
104
|
-
home_data_api = create_home_data_api(
|
|
120
|
+
home_data_api = create_home_data_api(cache_data.email, cache_data.user_data)
|
|
105
121
|
|
|
106
122
|
async def home_data_cache() -> HomeData:
|
|
107
|
-
if
|
|
108
|
-
|
|
109
|
-
context.update(
|
|
110
|
-
return
|
|
123
|
+
if cache_data.home_data is None:
|
|
124
|
+
cache_data.home_data = await home_data_api()
|
|
125
|
+
context.update(cache_data)
|
|
126
|
+
return cache_data.home_data
|
|
111
127
|
|
|
112
128
|
# Create device manager
|
|
113
|
-
device_manager = await create_device_manager(
|
|
129
|
+
device_manager = await create_device_manager(cache_data.user_data, home_data_cache)
|
|
114
130
|
|
|
115
131
|
devices = await device_manager.get_devices()
|
|
116
132
|
click.echo(f"Discovered devices: {', '.join([device.name for device in devices])}")
|
|
@@ -136,16 +152,26 @@ async def session(ctx, duration: int):
|
|
|
136
152
|
|
|
137
153
|
async def _discover(ctx):
|
|
138
154
|
context: RoborockContext = ctx.obj
|
|
139
|
-
|
|
140
|
-
if not
|
|
155
|
+
cache_data = context.cache_data()
|
|
156
|
+
if not cache_data:
|
|
141
157
|
raise Exception("You need to login first")
|
|
142
|
-
client = RoborockApiClient(
|
|
143
|
-
home_data = await client.get_home_data(
|
|
144
|
-
|
|
145
|
-
context.update(
|
|
158
|
+
client = RoborockApiClient(cache_data.email)
|
|
159
|
+
home_data = await client.get_home_data(cache_data.user_data)
|
|
160
|
+
cache_data.home_data = home_data
|
|
161
|
+
context.update(cache_data)
|
|
146
162
|
click.echo(f"Discovered devices {', '.join([device.name for device in home_data.get_all_devices()])}")
|
|
147
163
|
|
|
148
164
|
|
|
165
|
+
async def _load_and_discover(ctx) -> RoborockContext:
|
|
166
|
+
"""Discover devices if home data is not available."""
|
|
167
|
+
context: RoborockContext = ctx.obj
|
|
168
|
+
cache_data = context.cache_data()
|
|
169
|
+
if not cache_data.home_data:
|
|
170
|
+
await _discover(ctx)
|
|
171
|
+
cache_data = context.cache_data()
|
|
172
|
+
return context
|
|
173
|
+
|
|
174
|
+
|
|
149
175
|
@click.command()
|
|
150
176
|
@click.pass_context
|
|
151
177
|
@run_sync()
|
|
@@ -157,16 +183,11 @@ async def discover(ctx):
|
|
|
157
183
|
@click.pass_context
|
|
158
184
|
@run_sync()
|
|
159
185
|
async def list_devices(ctx):
|
|
160
|
-
context: RoborockContext = ctx
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
home_data = login_data.home_data
|
|
166
|
-
device_name_id = ", ".join(
|
|
167
|
-
[f"{device.name}: {device.duid}" for device in home_data.devices + home_data.received_devices]
|
|
168
|
-
)
|
|
169
|
-
click.echo(f"Known devices {device_name_id}")
|
|
186
|
+
context: RoborockContext = await _load_and_discover(ctx)
|
|
187
|
+
cache_data = context.cache_data()
|
|
188
|
+
home_data = cache_data.home_data
|
|
189
|
+
device_name_id = {device.name: device.duid for device in home_data.devices + home_data.received_devices}
|
|
190
|
+
click.echo(json.dumps(device_name_id, indent=4))
|
|
170
191
|
|
|
171
192
|
|
|
172
193
|
@click.command()
|
|
@@ -174,13 +195,10 @@ async def list_devices(ctx):
|
|
|
174
195
|
@click.pass_context
|
|
175
196
|
@run_sync()
|
|
176
197
|
async def list_scenes(ctx, device_id):
|
|
177
|
-
context: RoborockContext = ctx
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
login_data = context.login_data()
|
|
182
|
-
client = RoborockApiClient(login_data.email)
|
|
183
|
-
scenes = await client.get_scenes(login_data.user_data, device_id)
|
|
198
|
+
context: RoborockContext = await _load_and_discover(ctx)
|
|
199
|
+
cache_data = context.cache_data()
|
|
200
|
+
client = RoborockApiClient(cache_data.email)
|
|
201
|
+
scenes = await client.get_scenes(cache_data.user_data, device_id)
|
|
184
202
|
output_list = []
|
|
185
203
|
for scene in scenes:
|
|
186
204
|
output_list.append(scene.as_dict())
|
|
@@ -192,13 +210,10 @@ async def list_scenes(ctx, device_id):
|
|
|
192
210
|
@click.pass_context
|
|
193
211
|
@run_sync()
|
|
194
212
|
async def execute_scene(ctx, scene_id):
|
|
195
|
-
context: RoborockContext = ctx
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
login_data = context.login_data()
|
|
200
|
-
client = RoborockApiClient(login_data.email)
|
|
201
|
-
await client.execute_scene(login_data.user_data, scene_id)
|
|
213
|
+
context: RoborockContext = await _load_and_discover(ctx)
|
|
214
|
+
cache_data = context.cache_data()
|
|
215
|
+
client = RoborockApiClient(cache_data.email)
|
|
216
|
+
await client.execute_scene(cache_data.user_data, scene_id)
|
|
202
217
|
|
|
203
218
|
|
|
204
219
|
@click.command()
|
|
@@ -206,18 +221,23 @@ async def execute_scene(ctx, scene_id):
|
|
|
206
221
|
@click.pass_context
|
|
207
222
|
@run_sync()
|
|
208
223
|
async def status(ctx, device_id):
|
|
209
|
-
context: RoborockContext = ctx
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
login_data = context.login_data()
|
|
214
|
-
home_data = login_data.home_data
|
|
224
|
+
context: RoborockContext = await _load_and_discover(ctx)
|
|
225
|
+
cache_data = context.cache_data()
|
|
226
|
+
|
|
227
|
+
home_data = cache_data.home_data
|
|
215
228
|
devices = home_data.devices + home_data.received_devices
|
|
216
229
|
device = next(device for device in devices if device.duid == device_id)
|
|
217
230
|
product_info: dict[str, HomeDataProduct] = {product.id: product for product in home_data.products}
|
|
218
231
|
device_data = DeviceData(device, product_info[device.product_id].model)
|
|
219
|
-
|
|
220
|
-
|
|
232
|
+
|
|
233
|
+
mqtt_client = RoborockMqttClientV1(cache_data.user_data, device_data)
|
|
234
|
+
if not (networking := cache_data.network_info.get(device.duid)):
|
|
235
|
+
networking = await mqtt_client.get_networking()
|
|
236
|
+
cache_data.network_info[device.duid] = networking
|
|
237
|
+
context.update(cache_data)
|
|
238
|
+
else:
|
|
239
|
+
_LOGGER.debug("Using cached networking info for device %s: %s", device.duid, networking)
|
|
240
|
+
|
|
221
241
|
local_device_data = DeviceData(device, product_info[device.product_id].model, networking.ip)
|
|
222
242
|
local_client = RoborockLocalClientV1(local_device_data)
|
|
223
243
|
status = await local_client.get_status()
|
|
@@ -231,12 +251,10 @@ async def status(ctx, device_id):
|
|
|
231
251
|
@click.pass_context
|
|
232
252
|
@run_sync()
|
|
233
253
|
async def command(ctx, cmd, device_id, params):
|
|
234
|
-
context: RoborockContext = ctx
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
login_data = context.login_data()
|
|
239
|
-
home_data = login_data.home_data
|
|
254
|
+
context: RoborockContext = await _load_and_discover(ctx)
|
|
255
|
+
cache_data = context.cache_data()
|
|
256
|
+
|
|
257
|
+
home_data = cache_data.home_data
|
|
240
258
|
devices = home_data.devices + home_data.received_devices
|
|
241
259
|
device = next(device for device in devices if device.duid == device_id)
|
|
242
260
|
model = next(
|
|
@@ -246,7 +264,7 @@ async def command(ctx, cmd, device_id, params):
|
|
|
246
264
|
if model is None:
|
|
247
265
|
raise RoborockException(f"Could not find model for device {device.name}")
|
|
248
266
|
device_info = DeviceData(device=device, model=model)
|
|
249
|
-
mqtt_client = RoborockMqttClientV1(
|
|
267
|
+
mqtt_client = RoborockMqttClientV1(cache_data.user_data, device_info)
|
|
250
268
|
await mqtt_client.send_command(cmd, json.loads(params) if params is not None else None)
|
|
251
269
|
await mqtt_client.async_release()
|
|
252
270
|
|
|
@@ -425,6 +425,13 @@ class Status(RoborockBase):
|
|
|
425
425
|
raise RoborockException("Attempted to get mop_mode before status has been updated.")
|
|
426
426
|
return self.mop_mode.as_dict().get(mop_mode)
|
|
427
427
|
|
|
428
|
+
@property
|
|
429
|
+
def current_map(self) -> int | None:
|
|
430
|
+
"""Returns the current map ID if the map is present."""
|
|
431
|
+
if self.map_status is not None:
|
|
432
|
+
return (self.map_status - 3) // 4
|
|
433
|
+
return None
|
|
434
|
+
|
|
428
435
|
|
|
429
436
|
@dataclass
|
|
430
437
|
class S4MaxStatus(Status):
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/version_1_apis/roborock_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-2.30.0 → python_roborock-2.32.0}/roborock/version_a01_apis/roborock_client_a01.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|