python-roborock 2.34.1__tar.gz → 2.35.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.34.1 → python_roborock-2.35.0}/PKG-INFO +1 -1
- {python_roborock-2.34.1 → python_roborock-2.35.0}/pyproject.toml +1 -1
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/api.py +0 -4
- python_roborock-2.35.0/roborock/clean_modes.py +113 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/device_features.py +1 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/roborock_message.py +0 -20
- python_roborock-2.34.1/roborock/local_api.py → python_roborock-2.35.0/roborock/version_1_apis/roborock_local_client_v1.py +82 -29
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py +14 -19
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +3 -3
- python_roborock-2.34.1/roborock/version_1_apis/roborock_local_client_v1.py +0 -73
- {python_roborock-2.34.1 → python_roborock-2.35.0}/LICENSE +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/README.md +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/__init__.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/cli.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/cloud_api.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/code_mappings.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/command_cache.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/const.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/containers.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/README.md +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/__init__.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/a01_channel.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/b01_channel.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/cache.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/channel.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/device.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/device_manager.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/local_channel.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/mqtt_channel.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/b01/__init__.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/b01/props.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/dyad.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/status.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/trait.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/zeo.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/v1_channel.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/v1_rpc_channel.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/exceptions.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/mqtt/__init__.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/mqtt/roborock_session.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/mqtt/session.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/protocol.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/protocols/a01_protocol.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/protocols/b01_protocol.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/protocols/v1_protocol.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/py.typed +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/roborock_future.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/roborock_typing.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/util.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_1_apis/__init__.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_1_apis/roborock_client_v1.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_a01_apis/__init__.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
- {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/web_api.py +0 -0
|
@@ -116,7 +116,3 @@ class RoborockClient(ABC):
|
|
|
116
116
|
request_id = new_id
|
|
117
117
|
self._waiting_queue[request_id] = queue
|
|
118
118
|
return asyncio.ensure_future(self._wait_response(request_id, queue))
|
|
119
|
-
|
|
120
|
-
@abstractmethod
|
|
121
|
-
async def send_message(self, roborock_message: RoborockMessage):
|
|
122
|
-
"""Send a message to the Roborock device."""
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
|
|
5
|
+
from roborock import DeviceFeatures
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RoborockModeEnum(StrEnum):
|
|
9
|
+
"""A custom StrEnum that also stores an integer code for each member."""
|
|
10
|
+
|
|
11
|
+
code: int
|
|
12
|
+
|
|
13
|
+
def __new__(cls, value: str, code: int) -> RoborockModeEnum:
|
|
14
|
+
"""Creates a new enum member."""
|
|
15
|
+
member = str.__new__(cls, value)
|
|
16
|
+
member._value_ = value
|
|
17
|
+
member.code = code
|
|
18
|
+
return member
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CleanModes(RoborockModeEnum):
|
|
22
|
+
GENTLE = ("gentle", 105)
|
|
23
|
+
OFF = ("off", 105)
|
|
24
|
+
QUIET = ("quiet", 101)
|
|
25
|
+
BALANCED = ("balanced", 102)
|
|
26
|
+
TURBO = ("turbo", 103)
|
|
27
|
+
MAX = ("max", 104)
|
|
28
|
+
MAX_PLUS = ("max_plus", 108)
|
|
29
|
+
CUSTOMIZED = ("custom", 106)
|
|
30
|
+
SMART_MODE = ("smart_mode", 110)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CleanRoutes(RoborockModeEnum):
|
|
34
|
+
STANDARD = ("standard", 300)
|
|
35
|
+
DEEP = ("deep", 301)
|
|
36
|
+
DEEP_PLUS = ("deep_plus", 303)
|
|
37
|
+
FAST = ("fast", 304)
|
|
38
|
+
DEEP_PLUS_CN = ("deep_plus", 305)
|
|
39
|
+
SMART_MODE = ("smart_mode", 306)
|
|
40
|
+
CUSTOMIZED = ("custom", 302)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CleanModesOld(RoborockModeEnum):
|
|
44
|
+
QUIET = ("quiet", 38)
|
|
45
|
+
BALANCED = ("balanced", 60)
|
|
46
|
+
TURBO = ("turbo", 75)
|
|
47
|
+
MAX = ("max", 100)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class WaterModes(RoborockModeEnum):
|
|
51
|
+
OFF = ("off", 200)
|
|
52
|
+
LOW = ("low", 201)
|
|
53
|
+
MILD = ("mild", 201)
|
|
54
|
+
MEDIUM = ("medium", 202)
|
|
55
|
+
STANDARD = ("standard", 202)
|
|
56
|
+
HIGH = ("high", 203)
|
|
57
|
+
INTENSE = ("intense", 203)
|
|
58
|
+
CUSTOMIZED = ("custom", 204)
|
|
59
|
+
CUSTOM = ("custom_water_flow", 207)
|
|
60
|
+
EXTREME = ("extreme", 208)
|
|
61
|
+
SMART_MODE = ("smart_mode", 209)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_clean_modes(features: DeviceFeatures) -> list[CleanModes]:
|
|
65
|
+
"""Get the valid clean modes for the device - also known as 'fan power' or 'suction mode'"""
|
|
66
|
+
modes = [CleanModes.QUIET, CleanModes.BALANCED, CleanModes.TURBO, CleanModes.MAX]
|
|
67
|
+
if features.is_max_plus_mode_supported or features.is_none_pure_clean_mop_with_max_plus:
|
|
68
|
+
# If the vacuum has max plus mode supported
|
|
69
|
+
modes.append(CleanModes.MAX_PLUS)
|
|
70
|
+
if features.is_pure_clean_mop_supported:
|
|
71
|
+
# If the vacuum is capable of 'pure mop clean' aka no vacuum
|
|
72
|
+
modes.append(CleanModes.OFF)
|
|
73
|
+
else:
|
|
74
|
+
# If not, we can add gentle
|
|
75
|
+
modes.append(CleanModes.GENTLE)
|
|
76
|
+
return modes
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_clean_routes(features: DeviceFeatures, region: str) -> list[CleanRoutes]:
|
|
80
|
+
"""The routes that the vacuum will take while mopping"""
|
|
81
|
+
if features.is_none_pure_clean_mop_with_max_plus:
|
|
82
|
+
return [CleanRoutes.FAST, CleanRoutes.STANDARD]
|
|
83
|
+
supported = [CleanRoutes.STANDARD, CleanRoutes.DEEP]
|
|
84
|
+
if features.is_careful_slow_mop_supported:
|
|
85
|
+
if not (
|
|
86
|
+
features.is_corner_clean_mode_supported
|
|
87
|
+
and features.is_clean_route_deep_slow_plus_supported
|
|
88
|
+
and region == "CN"
|
|
89
|
+
):
|
|
90
|
+
# for some reason there is a china specific deep plus mode
|
|
91
|
+
supported.append(CleanRoutes.DEEP_PLUS_CN)
|
|
92
|
+
else:
|
|
93
|
+
supported.append(CleanRoutes.DEEP_PLUS)
|
|
94
|
+
|
|
95
|
+
if features.is_clean_route_fast_mode_supported:
|
|
96
|
+
supported.append(CleanRoutes.FAST)
|
|
97
|
+
return supported
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_water_modes(features: DeviceFeatures) -> list[WaterModes]:
|
|
101
|
+
"""Get the valid water modes for the device - also known as 'water flow' or 'water level'"""
|
|
102
|
+
supported_modes = [WaterModes.OFF]
|
|
103
|
+
if features.is_mop_shake_module_supported:
|
|
104
|
+
# For mops that have the vibrating mop pad, they do mild standard intense
|
|
105
|
+
supported_modes.extend([WaterModes.MILD, WaterModes.STANDARD, WaterModes.INTENSE])
|
|
106
|
+
else:
|
|
107
|
+
supported_modes.extend([WaterModes.LOW, WaterModes.MEDIUM, WaterModes.HIGH])
|
|
108
|
+
if features.is_custom_water_box_distance_supported:
|
|
109
|
+
# This is for devices that allow you to set a custom water flow from 0-100
|
|
110
|
+
supported_modes.append(WaterModes.CUSTOM)
|
|
111
|
+
if features.is_mop_shake_module_supported and features.is_mop_shake_water_max_supported:
|
|
112
|
+
supported_modes.append(WaterModes.EXTREME)
|
|
113
|
+
return supported_modes
|
|
@@ -423,6 +423,7 @@ class DeviceFeatures:
|
|
|
423
423
|
is_clean_route_setting_supported: bool = field(
|
|
424
424
|
metadata={"product_features": [ProductFeatures.MOP_SHAKE_MODULE, ProductFeatures.MOP_SPIN_MODULE]}
|
|
425
425
|
)
|
|
426
|
+
is_mop_shake_module_supported: bool = field(metadata={"product_features": [ProductFeatures.MOP_SHAKE_MODULE]})
|
|
426
427
|
|
|
427
428
|
@classmethod
|
|
428
429
|
def from_feature_flags(
|
|
@@ -256,23 +256,3 @@ class RoborockMessage:
|
|
|
256
256
|
data_point_response = json.loads(data_point)
|
|
257
257
|
return data_point_response.get("id")
|
|
258
258
|
return None
|
|
259
|
-
|
|
260
|
-
def get_method(self) -> str | None:
|
|
261
|
-
protocol = self.protocol
|
|
262
|
-
if self.payload and protocol in [4, 5, 101, 102]:
|
|
263
|
-
payload = json.loads(self.payload.decode())
|
|
264
|
-
for data_point_number, data_point in payload.get("dps").items():
|
|
265
|
-
if data_point_number in ["101", "102"]:
|
|
266
|
-
data_point_response = json.loads(data_point)
|
|
267
|
-
return data_point_response.get("method")
|
|
268
|
-
return None
|
|
269
|
-
|
|
270
|
-
def get_params(self) -> list | dict | None:
|
|
271
|
-
protocol = self.protocol
|
|
272
|
-
if self.payload and protocol in [4, 101, 102]:
|
|
273
|
-
payload = json.loads(self.payload.decode())
|
|
274
|
-
for data_point_number, data_point in payload.get("dps").items():
|
|
275
|
-
if data_point_number in ["101", "102"]:
|
|
276
|
-
data_point_response = json.loads(data_point)
|
|
277
|
-
return data_point_response.get("params")
|
|
278
|
-
return None
|
|
@@ -1,23 +1,36 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
import asyncio
|
|
4
2
|
import logging
|
|
5
|
-
from abc import ABC
|
|
6
3
|
from asyncio import Lock, TimerHandle, Transport, get_running_loop
|
|
7
4
|
from collections.abc import Callable
|
|
8
5
|
from dataclasses import dataclass
|
|
9
6
|
|
|
10
7
|
import async_timeout
|
|
11
8
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
from
|
|
16
|
-
from .
|
|
9
|
+
from .. import CommandVacuumError, DeviceData, RoborockCommand
|
|
10
|
+
from ..api import RoborockClient
|
|
11
|
+
from ..exceptions import RoborockConnectionException, RoborockException, VacuumError
|
|
12
|
+
from ..protocol import Decoder, Encoder, create_local_decoder, create_local_encoder
|
|
13
|
+
from ..protocols.v1_protocol import encode_local_payload
|
|
14
|
+
from ..roborock_message import RoborockMessage, RoborockMessageProtocol
|
|
15
|
+
from ..util import RoborockLoggerAdapter
|
|
16
|
+
from .roborock_client_v1 import CLOUD_REQUIRED, RoborockClientV1
|
|
17
17
|
|
|
18
18
|
_LOGGER = logging.getLogger(__name__)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
_HELLO_REQUEST_MESSAGE = RoborockMessage(
|
|
22
|
+
protocol=RoborockMessageProtocol.HELLO_REQUEST,
|
|
23
|
+
seq=1,
|
|
24
|
+
random=22,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
_PING_REQUEST_MESSAGE = RoborockMessage(
|
|
28
|
+
protocol=RoborockMessageProtocol.PING_REQUEST,
|
|
29
|
+
seq=2,
|
|
30
|
+
random=23,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
21
34
|
@dataclass
|
|
22
35
|
class _LocalProtocol(asyncio.Protocol):
|
|
23
36
|
"""Callbacks for the Roborock local client transport."""
|
|
@@ -34,10 +47,10 @@ class _LocalProtocol(asyncio.Protocol):
|
|
|
34
47
|
self.connection_lost_cb(exc)
|
|
35
48
|
|
|
36
49
|
|
|
37
|
-
class
|
|
38
|
-
"""Roborock local client
|
|
50
|
+
class RoborockLocalClientV1(RoborockClientV1, RoborockClient):
|
|
51
|
+
"""Roborock local client for v1 devices."""
|
|
39
52
|
|
|
40
|
-
def __init__(self, device_data: DeviceData):
|
|
53
|
+
def __init__(self, device_data: DeviceData, queue_timeout: int = 4):
|
|
41
54
|
"""Initialize the Roborock local client."""
|
|
42
55
|
if device_data.host is None:
|
|
43
56
|
raise RoborockException("Host is required")
|
|
@@ -47,10 +60,13 @@ class RoborockLocalClient(RoborockClient, ABC):
|
|
|
47
60
|
self.transport: Transport | None = None
|
|
48
61
|
self._mutex = Lock()
|
|
49
62
|
self.keep_alive_task: TimerHandle | None = None
|
|
63
|
+
RoborockClientV1.__init__(self, device_data, "abc")
|
|
50
64
|
RoborockClient.__init__(self, device_data)
|
|
51
65
|
self._local_protocol = _LocalProtocol(self._data_received, self._connection_lost)
|
|
52
66
|
self._encoder: Encoder = create_local_encoder(device_data.device.local_key)
|
|
53
67
|
self._decoder: Decoder = create_local_decoder(device_data.device.local_key)
|
|
68
|
+
self.queue_timeout = queue_timeout
|
|
69
|
+
self._logger = RoborockLoggerAdapter(device_data.device.name, _LOGGER)
|
|
54
70
|
|
|
55
71
|
def _data_received(self, message):
|
|
56
72
|
"""Called when data is received from the transport."""
|
|
@@ -106,29 +122,13 @@ class RoborockLocalClient(RoborockClient, ABC):
|
|
|
106
122
|
self._sync_disconnect()
|
|
107
123
|
|
|
108
124
|
async def hello(self):
|
|
109
|
-
request_id = 1
|
|
110
|
-
protocol = RoborockMessageProtocol.HELLO_REQUEST
|
|
111
125
|
try:
|
|
112
|
-
return await self.
|
|
113
|
-
RoborockMessage(
|
|
114
|
-
protocol=protocol,
|
|
115
|
-
seq=request_id,
|
|
116
|
-
random=22,
|
|
117
|
-
)
|
|
118
|
-
)
|
|
126
|
+
return await self._send_message(_HELLO_REQUEST_MESSAGE)
|
|
119
127
|
except Exception as e:
|
|
120
128
|
self._logger.error(e)
|
|
121
129
|
|
|
122
130
|
async def ping(self) -> None:
|
|
123
|
-
|
|
124
|
-
protocol = RoborockMessageProtocol.PING_REQUEST
|
|
125
|
-
return await self.send_message(
|
|
126
|
-
RoborockMessage(
|
|
127
|
-
protocol=protocol,
|
|
128
|
-
seq=request_id,
|
|
129
|
-
random=23,
|
|
130
|
-
)
|
|
131
|
-
)
|
|
131
|
+
await self._send_message(_PING_REQUEST_MESSAGE)
|
|
132
132
|
|
|
133
133
|
def _send_msg_raw(self, data: bytes):
|
|
134
134
|
try:
|
|
@@ -137,3 +137,56 @@ class RoborockLocalClient(RoborockClient, ABC):
|
|
|
137
137
|
self.transport.write(data)
|
|
138
138
|
except Exception as e:
|
|
139
139
|
raise RoborockException(e) from e
|
|
140
|
+
|
|
141
|
+
async def _send_command(
|
|
142
|
+
self,
|
|
143
|
+
method: RoborockCommand | str,
|
|
144
|
+
params: list | dict | int | None = None,
|
|
145
|
+
):
|
|
146
|
+
if method in CLOUD_REQUIRED:
|
|
147
|
+
raise RoborockException(f"Method {method} is not supported over local connection")
|
|
148
|
+
|
|
149
|
+
roborock_message = encode_local_payload(method, params)
|
|
150
|
+
self._logger.debug("Building message id %s for method %s", roborock_message.get_request_id(), method)
|
|
151
|
+
return await self._send_message(roborock_message, method, params)
|
|
152
|
+
|
|
153
|
+
async def _send_message(
|
|
154
|
+
self,
|
|
155
|
+
roborock_message: RoborockMessage,
|
|
156
|
+
method: str | None = None,
|
|
157
|
+
params: list | dict | int | None = None,
|
|
158
|
+
) -> RoborockMessage:
|
|
159
|
+
await self.validate_connection()
|
|
160
|
+
request_id: int | None
|
|
161
|
+
if not method or not method.startswith("get"):
|
|
162
|
+
request_id = roborock_message.seq
|
|
163
|
+
response_protocol = request_id + 1
|
|
164
|
+
else:
|
|
165
|
+
request_id = roborock_message.get_request_id()
|
|
166
|
+
response_protocol = RoborockMessageProtocol.GENERAL_REQUEST
|
|
167
|
+
if request_id is None:
|
|
168
|
+
raise RoborockException(f"Failed build message {roborock_message}")
|
|
169
|
+
msg = self._encoder(roborock_message)
|
|
170
|
+
if method:
|
|
171
|
+
self._logger.debug(f"id={request_id} Requesting method {method} with {params}")
|
|
172
|
+
# Send the command to the Roborock device
|
|
173
|
+
async_response = self._async_response(request_id, response_protocol)
|
|
174
|
+
self._send_msg_raw(msg)
|
|
175
|
+
diagnostic_key = method if method is not None else "unknown"
|
|
176
|
+
try:
|
|
177
|
+
response = await async_response
|
|
178
|
+
except VacuumError as err:
|
|
179
|
+
self._diagnostic_data[diagnostic_key] = {
|
|
180
|
+
"params": params,
|
|
181
|
+
"error": err,
|
|
182
|
+
}
|
|
183
|
+
raise CommandVacuumError(method, err) from err
|
|
184
|
+
self._diagnostic_data[diagnostic_key] = {
|
|
185
|
+
"params": params,
|
|
186
|
+
"response": response,
|
|
187
|
+
}
|
|
188
|
+
if roborock_message.protocol == RoborockMessageProtocol.GENERAL_REQUEST:
|
|
189
|
+
self._logger.debug(f"id={request_id} Response from method {method}: {response}")
|
|
190
|
+
if response == "retry":
|
|
191
|
+
raise RoborockException(f"Command {method} failed with 'retry' message; Device is busy, try again later")
|
|
192
|
+
return response
|
{python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py
RENAMED
|
@@ -13,7 +13,6 @@ from ..exceptions import CommandVacuumError, RoborockException, VacuumError
|
|
|
13
13
|
from ..protocol import Utils
|
|
14
14
|
from ..protocols.v1_protocol import SecurityData, create_mqtt_payload_encoder
|
|
15
15
|
from ..roborock_message import (
|
|
16
|
-
RoborockMessage,
|
|
17
16
|
RoborockMessageProtocol,
|
|
18
17
|
)
|
|
19
18
|
from ..roborock_typing import RoborockCommand
|
|
@@ -41,10 +40,19 @@ class RoborockMqttClientV1(RoborockMqttClient, RoborockClientV1):
|
|
|
41
40
|
SecurityData(endpoint=self._endpoint, nonce=self._nonce),
|
|
42
41
|
)
|
|
43
42
|
|
|
44
|
-
async def
|
|
43
|
+
async def _send_command(
|
|
44
|
+
self,
|
|
45
|
+
method: RoborockCommand | str,
|
|
46
|
+
params: list | dict | int | None = None,
|
|
47
|
+
):
|
|
48
|
+
if method in CUSTOM_COMMANDS:
|
|
49
|
+
# When we have more custom commands do something more complicated here
|
|
50
|
+
return await self._get_calibration_points()
|
|
51
|
+
|
|
52
|
+
roborock_message = self._payload_encoder(method, params)
|
|
53
|
+
self._logger.debug("Building message id %s for method %s", roborock_message.get_request_id, method)
|
|
54
|
+
|
|
45
55
|
await self.validate_connection()
|
|
46
|
-
method = roborock_message.get_method()
|
|
47
|
-
params = roborock_message.get_params()
|
|
48
56
|
request_id = roborock_message.get_request_id()
|
|
49
57
|
if request_id is None:
|
|
50
58
|
raise RoborockException(f"Failed build message {roborock_message}")
|
|
@@ -60,12 +68,12 @@ class RoborockMqttClientV1(RoborockMqttClient, RoborockClientV1):
|
|
|
60
68
|
response = await async_response
|
|
61
69
|
except VacuumError as err:
|
|
62
70
|
self._diagnostic_data[diagnostic_key] = {
|
|
63
|
-
"params":
|
|
71
|
+
"params": params,
|
|
64
72
|
"error": err,
|
|
65
73
|
}
|
|
66
74
|
raise CommandVacuumError(method, err) from err
|
|
67
75
|
self._diagnostic_data[diagnostic_key] = {
|
|
68
|
-
"params":
|
|
76
|
+
"params": params,
|
|
69
77
|
"response": response,
|
|
70
78
|
}
|
|
71
79
|
if response_protocol == RoborockMessageProtocol.MAP_RESPONSE:
|
|
@@ -74,19 +82,6 @@ class RoborockMqttClientV1(RoborockMqttClient, RoborockClientV1):
|
|
|
74
82
|
self._logger.debug(f"id={request_id} Response from {method}: {response}")
|
|
75
83
|
return response
|
|
76
84
|
|
|
77
|
-
async def _send_command(
|
|
78
|
-
self,
|
|
79
|
-
method: RoborockCommand | str,
|
|
80
|
-
params: list | dict | int | None = None,
|
|
81
|
-
):
|
|
82
|
-
if method in CUSTOM_COMMANDS:
|
|
83
|
-
# When we have more custom commands do something more complicated here
|
|
84
|
-
return await self._get_calibration_points()
|
|
85
|
-
|
|
86
|
-
roborock_message = self._payload_encoder(method, params)
|
|
87
|
-
self._logger.debug("Building message id %s for method %s", roborock_message.get_request_id, method)
|
|
88
|
-
return await self.send_message(roborock_message)
|
|
89
|
-
|
|
90
85
|
async def _get_calibration_points(self):
|
|
91
86
|
map: bytes = await self.send_command(RoborockCommand.GET_MAP_V1)
|
|
92
87
|
parser = RoborockMapDataParser(ColorsPalette(), Sizes(), [], ImageConfig(), [])
|
|
@@ -39,7 +39,7 @@ class RoborockMqttClientA01(RoborockMqttClient, RoborockClientA01):
|
|
|
39
39
|
self.queue_timeout = queue_timeout
|
|
40
40
|
self._logger = RoborockLoggerAdapter(device_info.device.name, _LOGGER)
|
|
41
41
|
|
|
42
|
-
async def
|
|
42
|
+
async def _send_message(self, roborock_message: RoborockMessage):
|
|
43
43
|
await self.validate_connection()
|
|
44
44
|
response_protocol = RoborockMessageProtocol.RPC_RESPONSE
|
|
45
45
|
|
|
@@ -67,11 +67,11 @@ class RoborockMqttClientA01(RoborockMqttClient, RoborockClientA01):
|
|
|
67
67
|
message = encode_mqtt_payload(
|
|
68
68
|
{RoborockDyadDataProtocol.ID_QUERY: str([int(protocol) for protocol in dyad_data_protocols])}
|
|
69
69
|
)
|
|
70
|
-
return await self.
|
|
70
|
+
return await self._send_message(message)
|
|
71
71
|
|
|
72
72
|
async def set_value(
|
|
73
73
|
self, protocol: RoborockDyadDataProtocol | RoborockZeoProtocol, value: typing.Any
|
|
74
74
|
) -> dict[int, typing.Any]:
|
|
75
75
|
"""Set a value for a specific protocol on the A01 device."""
|
|
76
76
|
message = encode_mqtt_payload({protocol: value})
|
|
77
|
-
return await self.
|
|
77
|
+
return await self._send_message(message)
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
from roborock.local_api import RoborockLocalClient
|
|
4
|
-
|
|
5
|
-
from .. import CommandVacuumError, DeviceData, RoborockCommand, RoborockException
|
|
6
|
-
from ..exceptions import VacuumError
|
|
7
|
-
from ..protocols.v1_protocol import encode_local_payload
|
|
8
|
-
from ..roborock_message import RoborockMessage, RoborockMessageProtocol
|
|
9
|
-
from ..util import RoborockLoggerAdapter
|
|
10
|
-
from .roborock_client_v1 import CLOUD_REQUIRED, RoborockClientV1
|
|
11
|
-
|
|
12
|
-
_LOGGER = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class RoborockLocalClientV1(RoborockLocalClient, RoborockClientV1):
|
|
16
|
-
"""Roborock local client for v1 devices."""
|
|
17
|
-
|
|
18
|
-
def __init__(self, device_data: DeviceData, queue_timeout: int = 4):
|
|
19
|
-
"""Initialize the Roborock local client."""
|
|
20
|
-
RoborockLocalClient.__init__(self, device_data)
|
|
21
|
-
RoborockClientV1.__init__(self, device_data, "abc")
|
|
22
|
-
self.queue_timeout = queue_timeout
|
|
23
|
-
self._logger = RoborockLoggerAdapter(device_data.device.name, _LOGGER)
|
|
24
|
-
|
|
25
|
-
async def _send_command(
|
|
26
|
-
self,
|
|
27
|
-
method: RoborockCommand | str,
|
|
28
|
-
params: list | dict | int | None = None,
|
|
29
|
-
):
|
|
30
|
-
if method in CLOUD_REQUIRED:
|
|
31
|
-
raise RoborockException(f"Method {method} is not supported over local connection")
|
|
32
|
-
|
|
33
|
-
roborock_message = encode_local_payload(method, params)
|
|
34
|
-
self._logger.debug("Building message id %s for method %s", roborock_message.get_request_id(), method)
|
|
35
|
-
return await self.send_message(roborock_message)
|
|
36
|
-
|
|
37
|
-
async def send_message(self, roborock_message: RoborockMessage):
|
|
38
|
-
await self.validate_connection()
|
|
39
|
-
method = roborock_message.get_method()
|
|
40
|
-
params = roborock_message.get_params()
|
|
41
|
-
request_id: int | None
|
|
42
|
-
if not method or not method.startswith("get"):
|
|
43
|
-
request_id = roborock_message.seq
|
|
44
|
-
response_protocol = request_id + 1
|
|
45
|
-
else:
|
|
46
|
-
request_id = roborock_message.get_request_id()
|
|
47
|
-
response_protocol = RoborockMessageProtocol.GENERAL_REQUEST
|
|
48
|
-
if request_id is None:
|
|
49
|
-
raise RoborockException(f"Failed build message {roborock_message}")
|
|
50
|
-
msg = self._encoder(roborock_message)
|
|
51
|
-
if method:
|
|
52
|
-
self._logger.debug(f"id={request_id} Requesting method {method} with {params}")
|
|
53
|
-
# Send the command to the Roborock device
|
|
54
|
-
async_response = self._async_response(request_id, response_protocol)
|
|
55
|
-
self._send_msg_raw(msg)
|
|
56
|
-
diagnostic_key = method if method is not None else "unknown"
|
|
57
|
-
try:
|
|
58
|
-
response = await async_response
|
|
59
|
-
except VacuumError as err:
|
|
60
|
-
self._diagnostic_data[diagnostic_key] = {
|
|
61
|
-
"params": roborock_message.get_params(),
|
|
62
|
-
"error": err,
|
|
63
|
-
}
|
|
64
|
-
raise CommandVacuumError(method, err) from err
|
|
65
|
-
self._diagnostic_data[diagnostic_key] = {
|
|
66
|
-
"params": roborock_message.get_params(),
|
|
67
|
-
"response": response,
|
|
68
|
-
}
|
|
69
|
-
if roborock_message.protocol == RoborockMessageProtocol.GENERAL_REQUEST:
|
|
70
|
-
self._logger.debug(f"id={request_id} Response from method {roborock_message.get_method()}: {response}")
|
|
71
|
-
if response == "retry":
|
|
72
|
-
raise RoborockException(f"Command {method} failed with 'retry' message; Device is busy, try again later")
|
|
73
|
-
return response
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_1_apis/roborock_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_a01_apis/roborock_client_a01.py
RENAMED
|
File without changes
|
|
File without changes
|