python-roborock 2.9.3__tar.gz → 2.9.5__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.9.3 → python_roborock-2.9.5}/PKG-INFO +2 -2
- {python_roborock-2.9.3 → python_roborock-2.9.5}/pyproject.toml +6 -2
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/api.py +3 -2
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/cloud_api.py +7 -6
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/local_api.py +2 -2
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/roborock_message.py +4 -4
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/roborock_local_client_v1.py +2 -1
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/roborock_mqtt_client_v1.py +2 -1
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_a01_apis/roborock_client_a01.py +3 -3
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +3 -2
- {python_roborock-2.9.3 → python_roborock-2.9.5}/LICENSE +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/README.md +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/__init__.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/cli.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/code_mappings.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/command_cache.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/const.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/containers.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/exceptions.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/protocol.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/py.typed +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/roborock_future.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/roborock_typing.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/util.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/__init__.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/roborock_client_v1.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_a01_apis/__init__.py +0 -0
- {python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/web_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: python-roborock
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.5
|
|
4
4
|
Summary: A package to control Roborock vacuums.
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Keywords: roborock,vacuum,homeassistant
|
|
@@ -21,7 +21,7 @@ Requires-Dist: aiohttp (>=3.8.2,<4.0.0)
|
|
|
21
21
|
Requires-Dist: async-timeout
|
|
22
22
|
Requires-Dist: click (>=8)
|
|
23
23
|
Requires-Dist: construct (>=2.10.57,<3.0.0)
|
|
24
|
-
Requires-Dist: paho-mqtt (>=1.6.1,<
|
|
24
|
+
Requires-Dist: paho-mqtt (>=1.6.1,<3.0.0)
|
|
25
25
|
Requires-Dist: pycryptodome (>=3.18,<4.0)
|
|
26
26
|
Requires-Dist: pycryptodomex (>=3.18,<4.0) ; sys_platform == "darwin"
|
|
27
27
|
Requires-Dist: vacuum-map-parser-roborock
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-roborock"
|
|
3
|
-
version = "2.9.
|
|
3
|
+
version = "2.9.5"
|
|
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"
|
|
@@ -27,7 +27,7 @@ aiohttp = "^3.8.2"
|
|
|
27
27
|
async-timeout = "*"
|
|
28
28
|
pycryptodome = "^3.18"
|
|
29
29
|
pycryptodomex = {version = "^3.18", markers = "sys_platform == 'darwin'"}
|
|
30
|
-
paho-mqtt = "
|
|
30
|
+
paho-mqtt = ">=1.6.1,<3.0.0"
|
|
31
31
|
construct = "^2.10.57"
|
|
32
32
|
vacuum-map-parser-roborock = "*"
|
|
33
33
|
|
|
@@ -45,6 +45,8 @@ ruff = "*"
|
|
|
45
45
|
codespell = "*"
|
|
46
46
|
pyshark = "^0.6"
|
|
47
47
|
aioresponses = "^0.7.7"
|
|
48
|
+
freezegun = "^1.5.1"
|
|
49
|
+
pytest-timeout = "^2.3.1"
|
|
48
50
|
|
|
49
51
|
[tool.semantic_release]
|
|
50
52
|
branch = "main"
|
|
@@ -70,3 +72,5 @@ select=["E", "F", "UP", "I"]
|
|
|
70
72
|
|
|
71
73
|
[tool.pytest.ini_options]
|
|
72
74
|
asyncio_mode = "auto"
|
|
75
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
76
|
+
timeout = 20
|
|
@@ -31,8 +31,9 @@ class RoborockClient(ABC):
|
|
|
31
31
|
"""Roborock client base class."""
|
|
32
32
|
|
|
33
33
|
_logger: logging.LoggerAdapter
|
|
34
|
+
queue_timeout: int
|
|
34
35
|
|
|
35
|
-
def __init__(self, device_info: DeviceData
|
|
36
|
+
def __init__(self, device_info: DeviceData) -> None:
|
|
36
37
|
"""Initialize RoborockClient."""
|
|
37
38
|
self.event_loop = get_running_loop_or_create_one()
|
|
38
39
|
self.device_info = device_info
|
|
@@ -45,7 +46,6 @@ class RoborockClient(ABC):
|
|
|
45
46
|
"misc_info": {"Nonce": base64.b64encode(self._nonce).decode("utf-8")}
|
|
46
47
|
}
|
|
47
48
|
self.is_available: bool = True
|
|
48
|
-
self.queue_timeout = queue_timeout
|
|
49
49
|
|
|
50
50
|
def __del__(self) -> None:
|
|
51
51
|
self.release()
|
|
@@ -91,6 +91,7 @@ class RoborockClient(ABC):
|
|
|
91
91
|
|
|
92
92
|
async def validate_connection(self) -> None:
|
|
93
93
|
if not self.should_keepalive():
|
|
94
|
+
self._logger.info("Resetting Roborock connection due to kepalive timeout")
|
|
94
95
|
await self.async_disconnect()
|
|
95
96
|
await self.async_connect()
|
|
96
97
|
|
|
@@ -46,12 +46,12 @@ class _Mqtt(mqtt.Client):
|
|
|
46
46
|
class RoborockMqttClient(RoborockClient, ABC):
|
|
47
47
|
"""Roborock MQTT client base class."""
|
|
48
48
|
|
|
49
|
-
def __init__(self, user_data: UserData, device_info: DeviceData
|
|
49
|
+
def __init__(self, user_data: UserData, device_info: DeviceData) -> None:
|
|
50
50
|
"""Initialize the Roborock MQTT client."""
|
|
51
51
|
rriot = user_data.rriot
|
|
52
52
|
if rriot is None:
|
|
53
53
|
raise RoborockException("Got no rriot data from user_data")
|
|
54
|
-
RoborockClient.__init__(self, device_info
|
|
54
|
+
RoborockClient.__init__(self, device_info)
|
|
55
55
|
self._mqtt_user = rriot.u
|
|
56
56
|
self._hashed_user = md5hex(self._mqtt_user + ":" + rriot.k)[2:10]
|
|
57
57
|
url = urlparse(rriot.r.m)
|
|
@@ -82,6 +82,8 @@ class RoborockMqttClient(RoborockClient, ABC):
|
|
|
82
82
|
self._logger.error(message)
|
|
83
83
|
if connection_queue:
|
|
84
84
|
connection_queue.set_exception(VacuumError(message))
|
|
85
|
+
else:
|
|
86
|
+
self._logger.debug("Failed to notify connect future, not in queue")
|
|
85
87
|
return
|
|
86
88
|
self._logger.info(f"Connected to mqtt {self._mqtt_host}:{self._mqtt_port}")
|
|
87
89
|
topic = f"rr/m/o/{self._mqtt_user}/{self._hashed_user}/{self.device_info.device.duid}"
|
|
@@ -154,10 +156,9 @@ class RoborockMqttClient(RoborockClient, ABC):
|
|
|
154
156
|
async def async_disconnect(self) -> None:
|
|
155
157
|
async with self._mutex:
|
|
156
158
|
if disconnected_future := self.sync_disconnect():
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
raise RoborockException(err) from err
|
|
159
|
+
# There are no errors set on this future
|
|
160
|
+
await disconnected_future
|
|
161
|
+
await self.event_loop.run_in_executor(None, self._mqtt_client.loop_stop)
|
|
161
162
|
|
|
162
163
|
async def async_connect(self) -> None:
|
|
163
164
|
async with self._mutex:
|
|
@@ -37,7 +37,7 @@ class _LocalProtocol(asyncio.Protocol):
|
|
|
37
37
|
class RoborockLocalClient(RoborockClient, ABC):
|
|
38
38
|
"""Roborock local client base class."""
|
|
39
39
|
|
|
40
|
-
def __init__(self, device_data: DeviceData
|
|
40
|
+
def __init__(self, device_data: DeviceData):
|
|
41
41
|
"""Initialize the Roborock local client."""
|
|
42
42
|
if device_data.host is None:
|
|
43
43
|
raise RoborockException("Host is required")
|
|
@@ -48,7 +48,7 @@ class RoborockLocalClient(RoborockClient, ABC):
|
|
|
48
48
|
self.transport: Transport | None = None
|
|
49
49
|
self._mutex = Lock()
|
|
50
50
|
self.keep_alive_task: TimerHandle | None = None
|
|
51
|
-
RoborockClient.__init__(self, device_data
|
|
51
|
+
RoborockClient.__init__(self, device_data)
|
|
52
52
|
self._local_protocol = _LocalProtocol(self._data_received, self._connection_lost)
|
|
53
53
|
|
|
54
54
|
def _data_received(self, message):
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
import math
|
|
5
5
|
import time
|
|
6
|
-
from dataclasses import dataclass
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
7
|
|
|
8
8
|
from roborock import RoborockEnum
|
|
9
9
|
from roborock.util import get_next_int
|
|
@@ -155,10 +155,10 @@ class MessageRetry:
|
|
|
155
155
|
class RoborockMessage:
|
|
156
156
|
protocol: RoborockMessageProtocol
|
|
157
157
|
payload: bytes | None = None
|
|
158
|
-
seq: int = get_next_int(100000, 999999)
|
|
158
|
+
seq: int = field(default_factory=lambda: get_next_int(100000, 999999))
|
|
159
159
|
version: bytes = b"1.0"
|
|
160
|
-
random: int = get_next_int(10000, 99999)
|
|
161
|
-
timestamp: int = math.floor(time.time())
|
|
160
|
+
random: int = field(default_factory=lambda: get_next_int(10000, 99999))
|
|
161
|
+
timestamp: int = field(default_factory=lambda: math.floor(time.time()))
|
|
162
162
|
message_retry: MessageRetry | None = None
|
|
163
163
|
|
|
164
164
|
def get_request_id(self) -> int | None:
|
{python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/roborock_local_client_v1.py
RENAMED
|
@@ -17,8 +17,9 @@ class RoborockLocalClientV1(RoborockLocalClient, RoborockClientV1):
|
|
|
17
17
|
|
|
18
18
|
def __init__(self, device_data: DeviceData, queue_timeout: int = 4):
|
|
19
19
|
"""Initialize the Roborock local client."""
|
|
20
|
-
RoborockLocalClient.__init__(self, device_data
|
|
20
|
+
RoborockLocalClient.__init__(self, device_data)
|
|
21
21
|
RoborockClientV1.__init__(self, device_data, "abc")
|
|
22
|
+
self.queue_timeout = queue_timeout
|
|
22
23
|
self._logger = RoborockLoggerAdapter(device_data.device.name, _LOGGER)
|
|
23
24
|
|
|
24
25
|
def build_roborock_message(
|
{python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/roborock_mqtt_client_v1.py
RENAMED
|
@@ -32,8 +32,9 @@ class RoborockMqttClientV1(RoborockMqttClient, RoborockClientV1):
|
|
|
32
32
|
raise RoborockException("Got no rriot data from user_data")
|
|
33
33
|
endpoint = base64.b64encode(Utils.md5(rriot.k.encode())[8:14]).decode()
|
|
34
34
|
|
|
35
|
-
RoborockMqttClient.__init__(self, user_data, device_info
|
|
35
|
+
RoborockMqttClient.__init__(self, user_data, device_info)
|
|
36
36
|
RoborockClientV1.__init__(self, device_info, endpoint)
|
|
37
|
+
self.queue_timeout = queue_timeout
|
|
37
38
|
self._logger = RoborockLoggerAdapter(device_info.device.name, _LOGGER)
|
|
38
39
|
|
|
39
40
|
async def send_message(self, roborock_message: RoborockMessage):
|
{python_roborock-2.9.3 → python_roborock-2.9.5}/roborock/version_a01_apis/roborock_client_a01.py
RENAMED
|
@@ -108,9 +108,9 @@ zeo_data_protocol_entries = {
|
|
|
108
108
|
class RoborockClientA01(RoborockClient, ABC):
|
|
109
109
|
"""Roborock client base class for A01 devices."""
|
|
110
110
|
|
|
111
|
-
def __init__(self, device_info: DeviceData, category: RoborockCategory
|
|
111
|
+
def __init__(self, device_info: DeviceData, category: RoborockCategory):
|
|
112
112
|
"""Initialize the Roborock client."""
|
|
113
|
-
super().__init__(device_info
|
|
113
|
+
super().__init__(device_info)
|
|
114
114
|
self.category = category
|
|
115
115
|
|
|
116
116
|
def on_message_received(self, messages: list[RoborockMessage]) -> None:
|
|
@@ -128,8 +128,8 @@ class RoborockClientA01(RoborockClient, ABC):
|
|
|
128
128
|
continue
|
|
129
129
|
payload_json = json.loads(payload.decode())
|
|
130
130
|
for data_point_number, data_point in payload_json.get("dps").items():
|
|
131
|
-
self._logger.debug("data point number=%s", data_point_number)
|
|
132
131
|
data_point_protocol: RoborockDyadDataProtocol | RoborockZeoProtocol
|
|
132
|
+
self._logger.debug("received msg with dps, protocol: %s, %s", data_point_number, protocol)
|
|
133
133
|
entries: dict
|
|
134
134
|
if self.category == RoborockCategory.WET_DRY_VAC:
|
|
135
135
|
data_point_protocol = RoborockDyadDataProtocol(int(data_point_number))
|
|
@@ -34,8 +34,9 @@ class RoborockMqttClientA01(RoborockMqttClient, RoborockClientA01):
|
|
|
34
34
|
if rriot is None:
|
|
35
35
|
raise RoborockException("Got no rriot data from user_data")
|
|
36
36
|
|
|
37
|
-
RoborockMqttClient.__init__(self, user_data, device_info
|
|
38
|
-
RoborockClientA01.__init__(self, device_info, category
|
|
37
|
+
RoborockMqttClient.__init__(self, user_data, device_info)
|
|
38
|
+
RoborockClientA01.__init__(self, device_info, category)
|
|
39
|
+
self.queue_timeout = queue_timeout
|
|
39
40
|
self._logger = RoborockLoggerAdapter(device_info.device.name, _LOGGER)
|
|
40
41
|
|
|
41
42
|
async def send_message(self, roborock_message: RoborockMessage):
|
|
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.9.3 → python_roborock-2.9.5}/roborock/version_1_apis/roborock_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|