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.
Files changed (54) hide show
  1. {python_roborock-2.34.1 → python_roborock-2.35.0}/PKG-INFO +1 -1
  2. {python_roborock-2.34.1 → python_roborock-2.35.0}/pyproject.toml +1 -1
  3. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/api.py +0 -4
  4. python_roborock-2.35.0/roborock/clean_modes.py +113 -0
  5. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/device_features.py +1 -0
  6. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/roborock_message.py +0 -20
  7. 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
  8. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py +14 -19
  9. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +3 -3
  10. python_roborock-2.34.1/roborock/version_1_apis/roborock_local_client_v1.py +0 -73
  11. {python_roborock-2.34.1 → python_roborock-2.35.0}/LICENSE +0 -0
  12. {python_roborock-2.34.1 → python_roborock-2.35.0}/README.md +0 -0
  13. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/__init__.py +0 -0
  14. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/cli.py +0 -0
  15. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/cloud_api.py +0 -0
  16. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/code_mappings.py +0 -0
  17. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/command_cache.py +0 -0
  18. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/const.py +0 -0
  19. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/containers.py +0 -0
  20. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/README.md +0 -0
  21. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/__init__.py +0 -0
  22. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/a01_channel.py +0 -0
  23. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/b01_channel.py +0 -0
  24. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/cache.py +0 -0
  25. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/channel.py +0 -0
  26. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/device.py +0 -0
  27. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/device_manager.py +0 -0
  28. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/local_channel.py +0 -0
  29. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/mqtt_channel.py +0 -0
  30. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/b01/__init__.py +0 -0
  31. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/b01/props.py +0 -0
  32. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/dyad.py +0 -0
  33. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/status.py +0 -0
  34. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/trait.py +0 -0
  35. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/traits/zeo.py +0 -0
  36. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/v1_channel.py +0 -0
  37. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/devices/v1_rpc_channel.py +0 -0
  38. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/exceptions.py +0 -0
  39. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/mqtt/__init__.py +0 -0
  40. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/mqtt/roborock_session.py +0 -0
  41. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/mqtt/session.py +0 -0
  42. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/protocol.py +0 -0
  43. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/protocols/a01_protocol.py +0 -0
  44. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/protocols/b01_protocol.py +0 -0
  45. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/protocols/v1_protocol.py +0 -0
  46. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/py.typed +0 -0
  47. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/roborock_future.py +0 -0
  48. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/roborock_typing.py +0 -0
  49. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/util.py +0 -0
  50. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_1_apis/__init__.py +0 -0
  51. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_1_apis/roborock_client_v1.py +0 -0
  52. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_a01_apis/__init__.py +0 -0
  53. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
  54. {python_roborock-2.34.1 → python_roborock-2.35.0}/roborock/web_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-roborock
3
- Version: 2.34.1
3
+ Version: 2.35.0
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
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-roborock"
3
- version = "2.34.1"
3
+ version = "2.35.0"
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"
@@ -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 . import DeviceData
13
- from .api import RoborockClient
14
- from .exceptions import RoborockConnectionException, RoborockException
15
- from .protocol import Decoder, Encoder, create_local_decoder, create_local_encoder
16
- from .roborock_message import RoborockMessage, RoborockMessageProtocol
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 RoborockLocalClient(RoborockClient, ABC):
38
- """Roborock local client base class."""
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.send_message(
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
- request_id = 2
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
@@ -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 send_message(self, roborock_message: RoborockMessage):
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": roborock_message.get_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": roborock_message.get_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 send_message(self, roborock_message: RoborockMessage):
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.send_message(message)
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.send_message(message)
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