python-roborock 2.6.1__tar.gz → 2.8.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-2.6.1 → python_roborock-2.8.1}/PKG-INFO +3 -3
- {python_roborock-2.6.1 → python_roborock-2.8.1}/pyproject.toml +4 -3
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/api.py +8 -1
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/code_mappings.py +31 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/const.py +1 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/containers.py +76 -11
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/roborock_message.py +3 -3
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/roborock_typing.py +1 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/util.py +12 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/version_1_apis/roborock_client_v1.py +8 -5
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/version_1_apis/roborock_mqtt_client_v1.py +16 -1
- {python_roborock-2.6.1 → python_roborock-2.8.1}/LICENSE +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/README.md +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/__init__.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/cli.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/cloud_api.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/command_cache.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/exceptions.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/local_api.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/protocol.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/py.typed +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/roborock_future.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/version_1_apis/__init__.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/version_1_apis/roborock_local_client_v1.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/version_a01_apis/__init__.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +0 -0
- {python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/web_api.py +0 -0
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-roborock
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.1
|
|
4
4
|
Summary: A package to control Roborock vacuums.
|
|
5
5
|
Home-page: https://github.com/humbertogontijo/python-roborock
|
|
6
6
|
License: GPL-3.0-only
|
|
7
7
|
Keywords: roborock,vacuum,homeassistant
|
|
8
8
|
Author: humbertogontijo
|
|
9
9
|
Author-email: humbertogontijo@users.noreply.github.com
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.11,<4.0
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
14
14
|
Classifier: Natural Language :: English
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
@@ -27,6 +26,7 @@ Requires-Dist: dacite (>=1.8.0,<2.0.0)
|
|
|
27
26
|
Requires-Dist: paho-mqtt (>=1.6.1,<2.0.0)
|
|
28
27
|
Requires-Dist: pycryptodome (>=3.18,<4.0)
|
|
29
28
|
Requires-Dist: pycryptodomex (>=3.18,<4.0) ; sys_platform == "darwin"
|
|
29
|
+
Requires-Dist: vacuum-map-parser-roborock
|
|
30
30
|
Project-URL: Documentation, https://python-roborock.readthedocs.io/
|
|
31
31
|
Project-URL: Repository, https://github.com/humbertogontijo/python-roborock
|
|
32
32
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-roborock"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.8.1"
|
|
4
4
|
description = "A package to control Roborock vacuums."
|
|
5
5
|
authors = ["humbertogontijo <humbertogontijo@users.noreply.github.com>"]
|
|
6
6
|
license = "GPL-3.0-only"
|
|
@@ -21,7 +21,7 @@ keywords = ["roborock", "vacuum", "homeassistant"]
|
|
|
21
21
|
roborock = "roborock.cli:main"
|
|
22
22
|
|
|
23
23
|
[tool.poetry.dependencies]
|
|
24
|
-
python = "^3.
|
|
24
|
+
python = "^3.11"
|
|
25
25
|
click = ">=8"
|
|
26
26
|
aiohttp = "^3.8.2"
|
|
27
27
|
async-timeout = "*"
|
|
@@ -30,6 +30,7 @@ pycryptodomex = {version = "^3.18", markers = "sys_platform == 'darwin'"}
|
|
|
30
30
|
paho-mqtt = "^1.6.1"
|
|
31
31
|
dacite = "^1.8.0"
|
|
32
32
|
construct = "^2.10.57"
|
|
33
|
+
vacuum-map-parser-roborock = "*"
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
[build-system]
|
|
@@ -47,7 +48,7 @@ pyshark = "^0.6"
|
|
|
47
48
|
|
|
48
49
|
[tool.semantic_release]
|
|
49
50
|
branch = "main"
|
|
50
|
-
version_toml = "pyproject.toml:tool.poetry.version"
|
|
51
|
+
version_toml = ["pyproject.toml:tool.poetry.version"]
|
|
51
52
|
build_command = "pip install poetry && poetry build"
|
|
52
53
|
[tool.semantic_release.commit_parser_options]
|
|
53
54
|
allowed_tags = [
|
|
@@ -23,7 +23,7 @@ from .roborock_message import (
|
|
|
23
23
|
RoborockMessage,
|
|
24
24
|
)
|
|
25
25
|
from .roborock_typing import RoborockCommand
|
|
26
|
-
from .util import RoborockLoggerAdapter, get_running_loop_or_create_one
|
|
26
|
+
from .util import RoborockLoggerAdapter, get_next_int, get_running_loop_or_create_one
|
|
27
27
|
|
|
28
28
|
_LOGGER = logging.getLogger(__name__)
|
|
29
29
|
KEEPALIVE = 60
|
|
@@ -113,6 +113,13 @@ class RoborockClient:
|
|
|
113
113
|
self, request_id: int, protocol_id: int = 0
|
|
114
114
|
) -> Coroutine[Any, Any, tuple[Any, VacuumError | None]]:
|
|
115
115
|
queue = RoborockFuture(protocol_id)
|
|
116
|
+
if request_id in self._waiting_queue:
|
|
117
|
+
new_id = get_next_int(10000, 32767)
|
|
118
|
+
_LOGGER.warning(
|
|
119
|
+
f"Attempting to create a future with an existing request_id... New id is {new_id}. "
|
|
120
|
+
f"Code may not function properly."
|
|
121
|
+
)
|
|
122
|
+
request_id = new_id
|
|
116
123
|
self._waiting_queue[request_id] = queue
|
|
117
124
|
return self._wait_response(request_id, queue)
|
|
118
125
|
|
|
@@ -243,6 +243,15 @@ class RoborockFanSpeedQ7Max(RoborockFanPowerCode):
|
|
|
243
243
|
max = 104
|
|
244
244
|
|
|
245
245
|
|
|
246
|
+
class RoborockFanSpeedQRevoMaster(RoborockFanPowerCode):
|
|
247
|
+
quiet = 101
|
|
248
|
+
balanced = 102
|
|
249
|
+
turbo = 103
|
|
250
|
+
max = 104
|
|
251
|
+
max_plus = 105
|
|
252
|
+
custom = 110 # Smartplan
|
|
253
|
+
|
|
254
|
+
|
|
246
255
|
class RoborockFanSpeedP10(RoborockFanPowerCode):
|
|
247
256
|
off = 105
|
|
248
257
|
quiet = 101
|
|
@@ -288,6 +297,7 @@ class RoborockMopModeS8ProUltra(RoborockMopModeCode):
|
|
|
288
297
|
class RoborockMopModeS8MaxVUltra(RoborockMopModeCode):
|
|
289
298
|
standard = 300
|
|
290
299
|
deep = 301
|
|
300
|
+
custom = 302
|
|
291
301
|
deep_plus = 303
|
|
292
302
|
fast = 304
|
|
293
303
|
deep_plus_pearl = 305
|
|
@@ -318,6 +328,17 @@ class RoborockMopIntensityV2(RoborockMopIntensityCode):
|
|
|
318
328
|
custom = 207
|
|
319
329
|
|
|
320
330
|
|
|
331
|
+
class RoborockMopIntensityQRevoMaster(RoborockMopIntensityCode):
|
|
332
|
+
"""Describes the mop intensity of the vacuum cleaner."""
|
|
333
|
+
|
|
334
|
+
off = 200
|
|
335
|
+
low = 201
|
|
336
|
+
medium = 202
|
|
337
|
+
high = 203
|
|
338
|
+
custom_water_flow = 207
|
|
339
|
+
custom = 209 # SmartPlan
|
|
340
|
+
|
|
341
|
+
|
|
321
342
|
class RoborockMopIntensityP10(RoborockMopIntensityCode):
|
|
322
343
|
"""Describes the mop intensity of the vacuum cleaner."""
|
|
323
344
|
|
|
@@ -362,6 +383,16 @@ class RoborockMopIntensityS6MaxV(RoborockMopIntensityCode):
|
|
|
362
383
|
custom_water_flow = 207
|
|
363
384
|
|
|
364
385
|
|
|
386
|
+
class RoborockMopIntensityQ7Max(RoborockMopIntensityCode):
|
|
387
|
+
"""Describes the mop intensity of the vacuum cleaner."""
|
|
388
|
+
|
|
389
|
+
off = 200
|
|
390
|
+
low = 201
|
|
391
|
+
medium = 202
|
|
392
|
+
high = 203
|
|
393
|
+
custom_water_flow = 207
|
|
394
|
+
|
|
395
|
+
|
|
365
396
|
class RoborockDockErrorCode(RoborockEnum):
|
|
366
397
|
"""Describes the error code of the dock."""
|
|
367
398
|
|
|
@@ -30,6 +30,7 @@ ROBOROCK_Q5_PRO = "roborock.vacuum.a72"
|
|
|
30
30
|
ROBOROCK_Q7 = "roborock.vacuum.a40"
|
|
31
31
|
ROBOROCK_Q7_MAX = "roborock.vacuum.a38"
|
|
32
32
|
ROBOROCK_Q7PLUS = "roborock.vacuum.a40"
|
|
33
|
+
ROBOROCK_QREVO_MASTER = "roborock.vacuum.a117"
|
|
33
34
|
ROBOROCK_Q8_MAX = "roborock.vacuum.a73"
|
|
34
35
|
ROBOROCK_G10S_PRO = "roborock.vacuum.a26"
|
|
35
36
|
ROBOROCK_G10S = "roborock.vacuum.a46"
|
|
@@ -7,9 +7,7 @@ import re
|
|
|
7
7
|
from dataclasses import asdict, dataclass, field
|
|
8
8
|
from datetime import timezone
|
|
9
9
|
from enum import Enum
|
|
10
|
-
from typing import Any, NamedTuple
|
|
11
|
-
|
|
12
|
-
from dacite import Config, from_dict
|
|
10
|
+
from typing import Any, NamedTuple, get_args, get_origin
|
|
13
11
|
|
|
14
12
|
from .code_mappings import (
|
|
15
13
|
RoborockCategory,
|
|
@@ -22,6 +20,7 @@ from .code_mappings import (
|
|
|
22
20
|
RoborockFanPowerCode,
|
|
23
21
|
RoborockFanSpeedP10,
|
|
24
22
|
RoborockFanSpeedQ7Max,
|
|
23
|
+
RoborockFanSpeedQRevoMaster,
|
|
25
24
|
RoborockFanSpeedS6Pure,
|
|
26
25
|
RoborockFanSpeedS7,
|
|
27
26
|
RoborockFanSpeedS7MaxV,
|
|
@@ -30,11 +29,12 @@ from .code_mappings import (
|
|
|
30
29
|
RoborockInCleaning,
|
|
31
30
|
RoborockMopIntensityCode,
|
|
32
31
|
RoborockMopIntensityP10,
|
|
32
|
+
RoborockMopIntensityQ7Max,
|
|
33
|
+
RoborockMopIntensityQRevoMaster,
|
|
33
34
|
RoborockMopIntensityS5Max,
|
|
34
35
|
RoborockMopIntensityS6MaxV,
|
|
35
36
|
RoborockMopIntensityS7,
|
|
36
37
|
RoborockMopIntensityS8MaxVUltra,
|
|
37
|
-
RoborockMopIntensityV2,
|
|
38
38
|
RoborockMopModeCode,
|
|
39
39
|
RoborockMopModeS7,
|
|
40
40
|
RoborockMopModeS8MaxVUltra,
|
|
@@ -51,6 +51,7 @@ from .const import (
|
|
|
51
51
|
ROBOROCK_G10S_PRO,
|
|
52
52
|
ROBOROCK_P10,
|
|
53
53
|
ROBOROCK_Q7_MAX,
|
|
54
|
+
ROBOROCK_QREVO_MASTER,
|
|
54
55
|
ROBOROCK_QREVO_MAXV,
|
|
55
56
|
ROBOROCK_QREVO_PRO,
|
|
56
57
|
ROBOROCK_QREVO_S,
|
|
@@ -102,14 +103,70 @@ class RoborockBase:
|
|
|
102
103
|
_ignore_keys = [] # type: ignore
|
|
103
104
|
is_cached = False
|
|
104
105
|
|
|
106
|
+
@staticmethod
|
|
107
|
+
def convert_to_class_obj(type, value):
|
|
108
|
+
try:
|
|
109
|
+
class_type = eval(type)
|
|
110
|
+
if get_origin(class_type) is list:
|
|
111
|
+
return_list = []
|
|
112
|
+
cls_type = get_args(class_type)[0]
|
|
113
|
+
for obj in value:
|
|
114
|
+
if issubclass(cls_type, RoborockBase):
|
|
115
|
+
return_list.append(cls_type.from_dict(obj))
|
|
116
|
+
elif cls_type in {str, int, float}:
|
|
117
|
+
return_list.append(cls_type(obj))
|
|
118
|
+
else:
|
|
119
|
+
return_list.append(cls_type(**obj))
|
|
120
|
+
return return_list
|
|
121
|
+
if issubclass(class_type, RoborockBase):
|
|
122
|
+
converted_value = class_type.from_dict(value)
|
|
123
|
+
else:
|
|
124
|
+
converted_value = class_type(value)
|
|
125
|
+
return converted_value
|
|
126
|
+
except NameError as err:
|
|
127
|
+
_LOGGER.exception(err)
|
|
128
|
+
except ValueError as err:
|
|
129
|
+
_LOGGER.exception(err)
|
|
130
|
+
except Exception as err:
|
|
131
|
+
_LOGGER.exception(err)
|
|
132
|
+
raise Exception("Fail")
|
|
133
|
+
|
|
105
134
|
@classmethod
|
|
106
135
|
def from_dict(cls, data: dict[str, Any]):
|
|
107
136
|
if isinstance(data, dict):
|
|
108
137
|
ignore_keys = cls._ignore_keys
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
138
|
+
data = decamelize_obj(data, ignore_keys)
|
|
139
|
+
cls_annotations: dict[str, str] = {}
|
|
140
|
+
for base in reversed(cls.__mro__):
|
|
141
|
+
cls_annotations.update(getattr(base, "__annotations__", {}))
|
|
142
|
+
remove_keys = []
|
|
143
|
+
for key, value in data.items():
|
|
144
|
+
if key not in cls_annotations:
|
|
145
|
+
remove_keys.append(key)
|
|
146
|
+
continue
|
|
147
|
+
if value == "None" or value is None:
|
|
148
|
+
data[key] = None
|
|
149
|
+
continue
|
|
150
|
+
field_type: str = cls_annotations[key]
|
|
151
|
+
if "|" in field_type:
|
|
152
|
+
# It's a union
|
|
153
|
+
types = field_type.split("|")
|
|
154
|
+
for type in types:
|
|
155
|
+
if "None" in type or "Any" in type:
|
|
156
|
+
continue
|
|
157
|
+
try:
|
|
158
|
+
data[key] = RoborockBase.convert_to_class_obj(type, value)
|
|
159
|
+
break
|
|
160
|
+
except Exception:
|
|
161
|
+
...
|
|
162
|
+
else:
|
|
163
|
+
try:
|
|
164
|
+
data[key] = RoborockBase.convert_to_class_obj(field_type, value)
|
|
165
|
+
except Exception:
|
|
166
|
+
...
|
|
167
|
+
for key in remove_keys:
|
|
168
|
+
del data[key]
|
|
169
|
+
return cls(**data)
|
|
113
170
|
|
|
114
171
|
def as_dict(self) -> dict:
|
|
115
172
|
return asdict(
|
|
@@ -185,6 +242,7 @@ class HomeDataProductSchema(RoborockBase):
|
|
|
185
242
|
mode: Any | None = None
|
|
186
243
|
type: Any | None = None
|
|
187
244
|
product_property: Any | None = None
|
|
245
|
+
property: Any | None = None
|
|
188
246
|
desc: Any | None = None
|
|
189
247
|
|
|
190
248
|
|
|
@@ -195,7 +253,7 @@ class HomeDataProduct(RoborockBase):
|
|
|
195
253
|
model: str
|
|
196
254
|
category: RoborockCategory
|
|
197
255
|
code: str | None = None
|
|
198
|
-
|
|
256
|
+
icon_url: str | None = None
|
|
199
257
|
attribute: Any | None = None
|
|
200
258
|
capability: int | None = None
|
|
201
259
|
schema: list[HomeDataProductSchema] | None = None
|
|
@@ -512,7 +570,13 @@ class S5MaxStatus(Status):
|
|
|
512
570
|
@dataclass
|
|
513
571
|
class Q7MaxStatus(Status):
|
|
514
572
|
fan_power: RoborockFanSpeedQ7Max | None = None
|
|
515
|
-
water_box_mode:
|
|
573
|
+
water_box_mode: RoborockMopIntensityQ7Max | None = None
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
@dataclass
|
|
577
|
+
class QRevoMasterStatus(Status):
|
|
578
|
+
fan_power: RoborockFanSpeedQRevoMaster | None = None
|
|
579
|
+
water_box_mode: RoborockMopIntensityQRevoMaster | None = None
|
|
516
580
|
|
|
517
581
|
|
|
518
582
|
@dataclass
|
|
@@ -572,6 +636,7 @@ ModelStatus: dict[str, type[Status]] = {
|
|
|
572
636
|
ROBOROCK_S4_MAX: S4MaxStatus,
|
|
573
637
|
ROBOROCK_S5_MAX: S5MaxStatus,
|
|
574
638
|
ROBOROCK_Q7_MAX: Q7MaxStatus,
|
|
639
|
+
ROBOROCK_QREVO_MASTER: QRevoMasterStatus,
|
|
575
640
|
ROBOROCK_S6: S6PureStatus,
|
|
576
641
|
ROBOROCK_S6_MAXV: S6MaxVStatus,
|
|
577
642
|
ROBOROCK_S6_PURE: S6PureStatus,
|
|
@@ -612,7 +677,7 @@ class CleanSummary(RoborockBase):
|
|
|
612
677
|
last_clean_t: int | None = None
|
|
613
678
|
|
|
614
679
|
def __post_init__(self) -> None:
|
|
615
|
-
if isinstance(self.clean_area, list):
|
|
680
|
+
if isinstance(self.clean_area, list | str):
|
|
616
681
|
_LOGGER.warning(f"Clean area is a unexpected type! Please give the following in a issue: {self.clean_area}")
|
|
617
682
|
else:
|
|
618
683
|
self.square_meter_clean_area = round(self.clean_area / 1000000, 1) if self.clean_area is not None else None
|
|
@@ -4,9 +4,9 @@ import json
|
|
|
4
4
|
import math
|
|
5
5
|
import time
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
-
from random import randint
|
|
8
7
|
|
|
9
8
|
from roborock import RoborockEnum
|
|
9
|
+
from roborock.util import get_next_int
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class RoborockMessageProtocol(RoborockEnum):
|
|
@@ -155,9 +155,9 @@ class MessageRetry:
|
|
|
155
155
|
class RoborockMessage:
|
|
156
156
|
protocol: RoborockMessageProtocol
|
|
157
157
|
payload: bytes | None = None
|
|
158
|
-
seq: int =
|
|
158
|
+
seq: int = get_next_int(100000, 999999)
|
|
159
159
|
version: bytes = b"1.0"
|
|
160
|
-
random: int =
|
|
160
|
+
random: int = get_next_int(10000, 99999)
|
|
161
161
|
timestamp: int = math.floor(time.time())
|
|
162
162
|
message_retry: MessageRetry | None = None
|
|
163
163
|
|
|
@@ -116,6 +116,7 @@ class RoborockCommand(str, Enum):
|
|
|
116
116
|
GET_MAP_STATUS = "get_map_status"
|
|
117
117
|
GET_MAP_V1 = "get_map_v1"
|
|
118
118
|
GET_MAP_V2 = "get_map_v2"
|
|
119
|
+
GET_MAP_CALIBRATION = "get_map_calibration" # Custom command
|
|
119
120
|
GET_MOP_MOTOR_STATUS = "get_mop_motor_status"
|
|
120
121
|
GET_MOP_TEMPLATE_PARAMS_BY_ID = "get_mop_template_params_by_id"
|
|
121
122
|
GET_MOP_TEMPLATE_PARAMS_SUMMARY = "get_mop_template_params_summary"
|
|
@@ -108,3 +108,15 @@ class RoborockLoggerAdapter(logging.LoggerAdapter):
|
|
|
108
108
|
|
|
109
109
|
def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, MutableMapping[str, Any]]:
|
|
110
110
|
return f"[{self.prefix}] {msg}", kwargs
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
counter_map: dict[tuple[int, int], int] = {}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def get_next_int(min_val: int, max_val: int):
|
|
117
|
+
"""Gets a random int in the range, precached to help keep it fast."""
|
|
118
|
+
if (min_val, max_val) not in counter_map:
|
|
119
|
+
# If we have never seen this range, or if the cache is getting low, make a bunch of preshuffled values.
|
|
120
|
+
counter_map[(min_val, max_val)] = min_val
|
|
121
|
+
counter_map[(min_val, max_val)] += 1
|
|
122
|
+
return counter_map[(min_val, max_val)] % max_val + min_val
|
{python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/version_1_apis/roborock_client_v1.py
RENAMED
|
@@ -5,7 +5,6 @@ import math
|
|
|
5
5
|
import struct
|
|
6
6
|
import time
|
|
7
7
|
from collections.abc import Callable, Coroutine
|
|
8
|
-
from random import randint
|
|
9
8
|
from typing import Any, TypeVar, final
|
|
10
9
|
|
|
11
10
|
from roborock import (
|
|
@@ -54,12 +53,16 @@ from roborock.roborock_message import (
|
|
|
54
53
|
RoborockMessage,
|
|
55
54
|
RoborockMessageProtocol,
|
|
56
55
|
)
|
|
57
|
-
from roborock.util import RepeatableTask, unpack_list
|
|
56
|
+
from roborock.util import RepeatableTask, get_next_int, unpack_list
|
|
58
57
|
|
|
59
|
-
COMMANDS_SECURED =
|
|
58
|
+
COMMANDS_SECURED = {
|
|
60
59
|
RoborockCommand.GET_MAP_V1,
|
|
61
60
|
RoborockCommand.GET_MULTI_MAP,
|
|
62
|
-
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
CUSTOM_COMMANDS = {RoborockCommand.GET_MAP_CALIBRATION}
|
|
64
|
+
|
|
65
|
+
CLOUD_REQUIRED = COMMANDS_SECURED.union(CUSTOM_COMMANDS)
|
|
63
66
|
|
|
64
67
|
WASH_N_FILL_DOCK = [
|
|
65
68
|
RoborockDockTypeCode.empty_wash_fill_dock,
|
|
@@ -334,7 +337,7 @@ class RoborockClientV1(RoborockClient):
|
|
|
334
337
|
secured=False,
|
|
335
338
|
):
|
|
336
339
|
timestamp = math.floor(time.time())
|
|
337
|
-
request_id =
|
|
340
|
+
request_id = get_next_int(10000, 32767)
|
|
338
341
|
inner = {
|
|
339
342
|
"id": request_id,
|
|
340
343
|
"method": method,
|
{python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/version_1_apis/roborock_mqtt_client_v1.py
RENAMED
|
@@ -2,6 +2,10 @@ import asyncio
|
|
|
2
2
|
import base64
|
|
3
3
|
|
|
4
4
|
import paho.mqtt.client as mqtt
|
|
5
|
+
from vacuum_map_parser_base.config.color import ColorsPalette
|
|
6
|
+
from vacuum_map_parser_base.config.image_config import ImageConfig
|
|
7
|
+
from vacuum_map_parser_base.config.size import Sizes
|
|
8
|
+
from vacuum_map_parser_roborock.map_data_parser import RoborockMapDataParser
|
|
5
9
|
|
|
6
10
|
from roborock.cloud_api import RoborockMqttClient
|
|
7
11
|
|
|
@@ -13,7 +17,7 @@ from ..roborock_message import (
|
|
|
13
17
|
RoborockMessageProtocol,
|
|
14
18
|
)
|
|
15
19
|
from ..roborock_typing import RoborockCommand
|
|
16
|
-
from .roborock_client_v1 import COMMANDS_SECURED, RoborockClientV1
|
|
20
|
+
from .roborock_client_v1 import COMMANDS_SECURED, CUSTOM_COMMANDS, RoborockClientV1
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
class RoborockMqttClientV1(RoborockMqttClient, RoborockClientV1):
|
|
@@ -66,10 +70,21 @@ class RoborockMqttClientV1(RoborockMqttClient, RoborockClientV1):
|
|
|
66
70
|
method: RoborockCommand | str,
|
|
67
71
|
params: list | dict | int | None = None,
|
|
68
72
|
):
|
|
73
|
+
if method in CUSTOM_COMMANDS:
|
|
74
|
+
# When we have more custom commands do something more complicated here
|
|
75
|
+
return await self._get_calibration_points()
|
|
69
76
|
request_id, timestamp, payload = self._get_payload(method, params, True)
|
|
70
77
|
request_protocol = RoborockMessageProtocol.RPC_REQUEST
|
|
71
78
|
roborock_message = RoborockMessage(timestamp=timestamp, protocol=request_protocol, payload=payload)
|
|
72
79
|
return await self.send_message(roborock_message)
|
|
73
80
|
|
|
81
|
+
async def _get_calibration_points(self):
|
|
82
|
+
map: bytes = await self.send_command(RoborockCommand.GET_MAP_V1)
|
|
83
|
+
parser = RoborockMapDataParser(ColorsPalette(), Sizes(), [], ImageConfig(), [])
|
|
84
|
+
parsed_map = parser.parse(map)
|
|
85
|
+
calibration = parsed_map.calibration()
|
|
86
|
+
self._logger.info(parsed_map.calibration())
|
|
87
|
+
return calibration
|
|
88
|
+
|
|
74
89
|
async def get_map_v1(self) -> bytes | None:
|
|
75
90
|
return await self.send_command(RoborockCommand.GET_MAP_V1)
|
|
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.6.1 → python_roborock-2.8.1}/roborock/version_1_apis/roborock_local_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-2.6.1 → python_roborock-2.8.1}/roborock/version_a01_apis/roborock_client_a01.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|