python-roborock 2.6.0__tar.gz → 2.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-2.6.0 → python_roborock-2.7.1}/PKG-INFO +2 -1
- {python_roborock-2.6.0 → python_roborock-2.7.1}/pyproject.toml +1 -1
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/api.py +8 -1
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/code_mappings.py +1 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/containers.py +67 -9
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/roborock_message.py +3 -3
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/util.py +12 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/version_1_apis/roborock_client_v1.py +3 -3
- {python_roborock-2.6.0 → python_roborock-2.7.1}/LICENSE +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/README.md +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/__init__.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/cli.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/cloud_api.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/command_cache.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/const.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/exceptions.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/local_api.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/protocol.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/py.typed +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/roborock_future.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/roborock_typing.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/version_1_apis/__init__.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/version_1_apis/roborock_local_client_v1.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/version_1_apis/roborock_mqtt_client_v1.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/version_a01_apis/__init__.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +0 -0
- {python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/web_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-roborock
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.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
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
21
|
Classifier: Topic :: Software Development :: Libraries
|
|
21
22
|
Requires-Dist: aiohttp (>=3.8.2,<4.0.0)
|
|
22
23
|
Requires-Dist: async-timeout
|
|
@@ -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
|
|
|
@@ -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,
|
|
@@ -102,14 +100,70 @@ class RoborockBase:
|
|
|
102
100
|
_ignore_keys = [] # type: ignore
|
|
103
101
|
is_cached = False
|
|
104
102
|
|
|
103
|
+
@staticmethod
|
|
104
|
+
def convert_to_class_obj(type, value):
|
|
105
|
+
try:
|
|
106
|
+
class_type = eval(type)
|
|
107
|
+
if get_origin(class_type) is list:
|
|
108
|
+
return_list = []
|
|
109
|
+
cls_type = get_args(class_type)[0]
|
|
110
|
+
for obj in value:
|
|
111
|
+
if issubclass(cls_type, RoborockBase):
|
|
112
|
+
return_list.append(cls_type.from_dict(obj))
|
|
113
|
+
elif cls_type in {str, int, float}:
|
|
114
|
+
return_list.append(cls_type(obj))
|
|
115
|
+
else:
|
|
116
|
+
return_list.append(cls_type(**obj))
|
|
117
|
+
return return_list
|
|
118
|
+
if issubclass(class_type, RoborockBase):
|
|
119
|
+
converted_value = class_type.from_dict(value)
|
|
120
|
+
else:
|
|
121
|
+
converted_value = class_type(value)
|
|
122
|
+
return converted_value
|
|
123
|
+
except NameError as err:
|
|
124
|
+
_LOGGER.exception(err)
|
|
125
|
+
except ValueError as err:
|
|
126
|
+
_LOGGER.exception(err)
|
|
127
|
+
except Exception as err:
|
|
128
|
+
_LOGGER.exception(err)
|
|
129
|
+
raise Exception("Fail")
|
|
130
|
+
|
|
105
131
|
@classmethod
|
|
106
132
|
def from_dict(cls, data: dict[str, Any]):
|
|
107
133
|
if isinstance(data, dict):
|
|
108
134
|
ignore_keys = cls._ignore_keys
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
135
|
+
data = decamelize_obj(data, ignore_keys)
|
|
136
|
+
cls_annotations: dict[str, str] = {}
|
|
137
|
+
for base in reversed(cls.__mro__):
|
|
138
|
+
cls_annotations.update(getattr(base, "__annotations__", {}))
|
|
139
|
+
remove_keys = []
|
|
140
|
+
for key, value in data.items():
|
|
141
|
+
if value == "None" or value is None:
|
|
142
|
+
data[key] = None
|
|
143
|
+
continue
|
|
144
|
+
if key not in cls_annotations:
|
|
145
|
+
remove_keys.append(key)
|
|
146
|
+
continue
|
|
147
|
+
field_type: str = cls_annotations[key]
|
|
148
|
+
if "|" in field_type:
|
|
149
|
+
# It's a union
|
|
150
|
+
types = field_type.split("|")
|
|
151
|
+
for type in types:
|
|
152
|
+
if "None" in type or "Any" in type:
|
|
153
|
+
continue
|
|
154
|
+
try:
|
|
155
|
+
data[key] = RoborockBase.convert_to_class_obj(type, value)
|
|
156
|
+
break
|
|
157
|
+
except Exception:
|
|
158
|
+
...
|
|
159
|
+
else:
|
|
160
|
+
try:
|
|
161
|
+
data[key] = RoborockBase.convert_to_class_obj(field_type, value)
|
|
162
|
+
except Exception:
|
|
163
|
+
...
|
|
164
|
+
for key in remove_keys:
|
|
165
|
+
del data[key]
|
|
166
|
+
return cls(**data)
|
|
113
167
|
|
|
114
168
|
def as_dict(self) -> dict:
|
|
115
169
|
return asdict(
|
|
@@ -185,6 +239,7 @@ class HomeDataProductSchema(RoborockBase):
|
|
|
185
239
|
mode: Any | None = None
|
|
186
240
|
type: Any | None = None
|
|
187
241
|
product_property: Any | None = None
|
|
242
|
+
property: Any | None = None
|
|
188
243
|
desc: Any | None = None
|
|
189
244
|
|
|
190
245
|
|
|
@@ -195,7 +250,7 @@ class HomeDataProduct(RoborockBase):
|
|
|
195
250
|
model: str
|
|
196
251
|
category: RoborockCategory
|
|
197
252
|
code: str | None = None
|
|
198
|
-
|
|
253
|
+
icon_url: str | None = None
|
|
199
254
|
attribute: Any | None = None
|
|
200
255
|
capability: int | None = None
|
|
201
256
|
schema: list[HomeDataProductSchema] | None = None
|
|
@@ -612,7 +667,10 @@ class CleanSummary(RoborockBase):
|
|
|
612
667
|
last_clean_t: int | None = None
|
|
613
668
|
|
|
614
669
|
def __post_init__(self) -> None:
|
|
615
|
-
|
|
670
|
+
if isinstance(self.clean_area, list | str):
|
|
671
|
+
_LOGGER.warning(f"Clean area is a unexpected type! Please give the following in a issue: {self.clean_area}")
|
|
672
|
+
else:
|
|
673
|
+
self.square_meter_clean_area = round(self.clean_area / 1000000, 1) if self.clean_area is not None else None
|
|
616
674
|
|
|
617
675
|
|
|
618
676
|
@dataclass
|
|
@@ -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
|
|
|
@@ -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.0 → python_roborock-2.7.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,7 +53,7 @@ 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
58
|
COMMANDS_SECURED = [
|
|
60
59
|
RoborockCommand.GET_MAP_V1,
|
|
@@ -67,6 +66,7 @@ WASH_N_FILL_DOCK = [
|
|
|
67
66
|
RoborockDockTypeCode.p10_dock,
|
|
68
67
|
RoborockDockTypeCode.p10_pro_dock,
|
|
69
68
|
RoborockDockTypeCode.s8_maxv_ultra_dock,
|
|
69
|
+
RoborockDockTypeCode.qrevo_s_dock,
|
|
70
70
|
]
|
|
71
71
|
RT = TypeVar("RT", bound=RoborockBase)
|
|
72
72
|
EVICT_TIME = 60
|
|
@@ -333,7 +333,7 @@ class RoborockClientV1(RoborockClient):
|
|
|
333
333
|
secured=False,
|
|
334
334
|
):
|
|
335
335
|
timestamp = math.floor(time.time())
|
|
336
|
-
request_id =
|
|
336
|
+
request_id = get_next_int(10000, 32767)
|
|
337
337
|
inner = {
|
|
338
338
|
"id": request_id,
|
|
339
339
|
"method": method,
|
|
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.6.0 → python_roborock-2.7.1}/roborock/version_1_apis/roborock_local_client_v1.py
RENAMED
|
File without changes
|
{python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/version_1_apis/roborock_mqtt_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-2.6.0 → python_roborock-2.7.1}/roborock/version_a01_apis/roborock_client_a01.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|