python-roborock 5.28.0__tar.gz → 5.29.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-5.28.0 → python_roborock-5.29.0}/PKG-INFO +1 -1
- {python_roborock-5.28.0 → python_roborock-5.29.0}/pyproject.toml +1 -1
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/v1/v1_code_mappings.py +1 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/device_features.py +3 -1
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/roborock_message.py +18 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/testing/channel.py +27 -5
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/testing/cloud.py +44 -1
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/testing/simulator.py +2 -8
- {python_roborock-5.28.0 → python_roborock-5.29.0}/.gitignore +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/LICENSE +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/README.md +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/broadcast_protocol.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/callbacks.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/cli.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/const.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/b01_q10/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/b01_q7/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/code_mappings.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/containers.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/dyad/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/dyad/dyad_code_mappings.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/dyad/dyad_containers.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/mower/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/mower/mower_containers.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/v1/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/v1/v1_clean_modes.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/v1/v1_containers.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/zeo/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/zeo/zeo_code_mappings.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/zeo/zeo_containers.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/README.md +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/cache.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/device.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/device_manager.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/file_cache.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/rpc/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/rpc/a01_channel.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/rpc/b01_q10_channel.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/rpc/b01_q7_channel.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/rpc/v1_channel.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/a01/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/button_light.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/child_lock.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/clean_history.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/command.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/common.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/consumable.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/do_not_disturb.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/dust_collection.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/map.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/network_info.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/remote.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/status.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/vacuum.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/volume.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q7/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q7/clean_summary.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q7/map.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q7/map_content.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/common.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/traits_mixin.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/child_lock.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/command.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/common.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/consumeable.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/device_features.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/flow_led_status.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/home.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/led_status.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/map_content.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/maps.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/network_info.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/rooms.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/routines.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/status.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/volume.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/transport/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/transport/channel.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/transport/local_channel.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/transport/mqtt_channel.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/diagnostics.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/exceptions.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/map/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/map/b01_grid_layers.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/map/b01_map_parser.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/map/b01_q10_map_parser.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/map/b01_q10_overlays.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/map/map_parser.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/map/proto/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/map/proto/b01_scmap.proto +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/map/proto/b01_scmap_pb2.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/mqtt/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/mqtt/health_manager.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/mqtt/roborock_session.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/mqtt/session.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/protocol.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/protocols/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/protocols/a01_protocol.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/protocols/b01_q10_protocol.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/protocols/b01_q7_protocol.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/protocols/v1_protocol.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/py.typed +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/roborock_typing.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/testing/__init__.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/testing/v1_simulator.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/util.py +0 -0
- {python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/web_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-roborock
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.29.0
|
|
4
4
|
Summary: A package to control Roborock vacuums.
|
|
5
5
|
Project-URL: Repository, https://github.com/python-roborock/python-roborock
|
|
6
6
|
Project-URL: Documentation, https://python-roborock.readthedocs.io/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "python-roborock"
|
|
3
|
-
version = "5.
|
|
3
|
+
version = "5.29.0"
|
|
4
4
|
description = "A package to control Roborock vacuums."
|
|
5
5
|
authors = [{ name = "humbertogontijo", email = "humbertogontijo@users.noreply.github.com" }, {name="Lash-L"}, {name="allenporter"}]
|
|
6
6
|
requires-python = ">=3.11, <4"
|
|
@@ -674,6 +674,7 @@ _ONLY_X_SERIES_DOCK_TYPES = {
|
|
|
674
674
|
|
|
675
675
|
_CLEAN_FLUID_AUTO_DELIVERY_DOCK_TYPES = {
|
|
676
676
|
RoborockDockTypeCode.o4_dock,
|
|
677
|
+
RoborockDockTypeCode.o5_dock,
|
|
677
678
|
RoborockDockTypeCode.pearl_plus_dock,
|
|
678
679
|
RoborockDockTypeCode.shell_3s_dock,
|
|
679
680
|
RoborockDockTypeCode.o6_dock,
|
|
@@ -681,7 +682,6 @@ _CLEAN_FLUID_AUTO_DELIVERY_DOCK_TYPES = {
|
|
|
681
682
|
}
|
|
682
683
|
|
|
683
684
|
_CLEAN_FLUID_AUTO_DELIVERY_AM_DOCK_TYPES = {
|
|
684
|
-
RoborockDockTypeCode.o5_dock,
|
|
685
685
|
RoborockDockTypeCode.shell_2s_dock,
|
|
686
686
|
RoborockDockTypeCode.shell_3_dock,
|
|
687
687
|
RoborockDockTypeCode.shell_2c_dock,
|
|
@@ -734,9 +734,11 @@ _WATER_UPDOWN_DRAIN_AM_DOCK_TYPES = {
|
|
|
734
734
|
RoborockDockTypeCode.shell_2c_dock,
|
|
735
735
|
RoborockDockTypeCode.shell_3s_dock,
|
|
736
736
|
RoborockDockTypeCode.k1_dock,
|
|
737
|
+
RoborockDockTypeCode.k1s_dock,
|
|
737
738
|
RoborockDockTypeCode.o6_dock,
|
|
738
739
|
RoborockDockTypeCode.shell_e_dock,
|
|
739
740
|
RoborockDockTypeCode.shell_2e_dock,
|
|
741
|
+
RoborockDockTypeCode.shell_3c_dock,
|
|
740
742
|
}
|
|
741
743
|
|
|
742
744
|
_HATCH_DOOR_DOCK_COOL_FAN_DOCK_TYPES = {
|
|
@@ -274,6 +274,9 @@ ROBOROCK_DATA_CONSUMABLE_PROTOCOL = [
|
|
|
274
274
|
]
|
|
275
275
|
|
|
276
276
|
|
|
277
|
+
MAX_PAYLOAD_REPR_LEN = 1024
|
|
278
|
+
|
|
279
|
+
|
|
277
280
|
@dataclass
|
|
278
281
|
class RoborockMessage:
|
|
279
282
|
protocol: RoborockMessageProtocol
|
|
@@ -282,3 +285,18 @@ class RoborockMessage:
|
|
|
282
285
|
version: bytes = b"1.0"
|
|
283
286
|
random: int = field(default_factory=lambda: get_next_int(10000, 99999))
|
|
284
287
|
timestamp: int = field(default_factory=lambda: get_timestamp())
|
|
288
|
+
|
|
289
|
+
def __repr__(self) -> str:
|
|
290
|
+
payload_repr = "None"
|
|
291
|
+
if self.payload is not None:
|
|
292
|
+
if isinstance(self.payload, (bytes, bytearray, str)) and len(self.payload) > MAX_PAYLOAD_REPR_LEN:
|
|
293
|
+
r = repr(self.payload[:MAX_PAYLOAD_REPR_LEN])
|
|
294
|
+
quote = r[-1]
|
|
295
|
+
payload_repr = f"{r[:-1]}...{quote} (length: {len(self.payload)})"
|
|
296
|
+
else:
|
|
297
|
+
payload_repr = repr(self.payload)
|
|
298
|
+
return (
|
|
299
|
+
f"RoborockMessage(protocol={self.protocol}, payload={payload_repr}, "
|
|
300
|
+
f"seq={self.seq}, version={self.version!r}, random={self.random}, "
|
|
301
|
+
f"timestamp={self.timestamp})"
|
|
302
|
+
)
|
|
@@ -5,7 +5,7 @@ subscription, and publishing logic at the message boundary. It acts as an
|
|
|
5
5
|
in-memory replacement for `MqttChannel` and `LocalChannel` during testing.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from collections.abc import Callable
|
|
8
|
+
from collections.abc import Awaitable, Callable
|
|
9
9
|
from typing import Any
|
|
10
10
|
from unittest.mock import AsyncMock, MagicMock
|
|
11
11
|
|
|
@@ -37,6 +37,9 @@ class FakeChannel(Channel):
|
|
|
37
37
|
publish (useful for low-level RPC request/response testing).
|
|
38
38
|
- **Push unsolicited messages**: Call ``channel.notify_subscribers(msg)``
|
|
39
39
|
to simulate the device broadcasting a state change.
|
|
40
|
+
- **Intercept published messages**: Register a handler/callback via
|
|
41
|
+
``channel.publish_handler = my_handler`` (e.g. stateful simulator)
|
|
42
|
+
to reactively process commands.
|
|
40
43
|
"""
|
|
41
44
|
|
|
42
45
|
subscribe: Any
|
|
@@ -49,6 +52,11 @@ class FakeChannel(Channel):
|
|
|
49
52
|
self._is_connected = False
|
|
50
53
|
self._is_local = is_local
|
|
51
54
|
|
|
55
|
+
# A callback to intercept published messages (e.g., bound simulator handler).
|
|
56
|
+
# Can be either synchronous or asynchronous: Callable[[RoborockMessage], Awaitable[Any]].
|
|
57
|
+
# By default, routes to self._default_publish_handler to handle the response_queue.
|
|
58
|
+
self.publish_handler: Callable[[RoborockMessage], Awaitable[Any]] | None = self._default_publish_handler
|
|
59
|
+
|
|
52
60
|
# Set this to an exception instance to make the next publish raise it.
|
|
53
61
|
# This is a convenience shortcut; callers can also replace
|
|
54
62
|
# ``publish.side_effect`` directly for more control.
|
|
@@ -96,14 +104,16 @@ class FakeChannel(Channel):
|
|
|
96
104
|
async def _publish(self, message: RoborockMessage) -> None:
|
|
97
105
|
"""Default publish implementation.
|
|
98
106
|
|
|
99
|
-
Records the message in ``published_messages`` and
|
|
100
|
-
``response_queue`` is non-empty, pops the first response and
|
|
101
|
-
delivers it to all current subscribers (simulating a
|
|
102
|
-
request/response round-trip).
|
|
107
|
+
Records the message in ``published_messages`` and executes ``publish_handler``.
|
|
103
108
|
"""
|
|
104
109
|
self.published_messages.append(message)
|
|
105
110
|
if self.publish_side_effect:
|
|
106
111
|
raise self.publish_side_effect
|
|
112
|
+
if self.publish_handler:
|
|
113
|
+
await self.publish_handler(message)
|
|
114
|
+
|
|
115
|
+
async def _default_publish_handler(self, message: RoborockMessage) -> None:
|
|
116
|
+
"""Default handler that pops canned responses from response_queue."""
|
|
107
117
|
if self.response_queue:
|
|
108
118
|
response = self.response_queue.pop(0)
|
|
109
119
|
self.notify_subscribers(response)
|
|
@@ -124,3 +134,15 @@ class FakeChannel(Channel):
|
|
|
124
134
|
"""
|
|
125
135
|
for subscriber in list(self.subscribers):
|
|
126
136
|
subscriber(message)
|
|
137
|
+
|
|
138
|
+
def inject_error(self, exception: Exception) -> None:
|
|
139
|
+
"""Inject a transient failure into all channel operations (publish, subscribe, connect)."""
|
|
140
|
+
self.publish.side_effect = exception
|
|
141
|
+
self.subscribe.side_effect = exception
|
|
142
|
+
self.connect.side_effect = exception
|
|
143
|
+
|
|
144
|
+
def clear_error(self) -> None:
|
|
145
|
+
"""Restore default success behaviors on all channel operations."""
|
|
146
|
+
self.publish.side_effect = self._publish
|
|
147
|
+
self.subscribe.side_effect = self._subscribe
|
|
148
|
+
self.connect.side_effect = self._connect
|
|
@@ -6,6 +6,7 @@ to simulate physical devices connected to the Roborock Cloud.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import contextlib
|
|
9
|
+
import datetime
|
|
9
10
|
import re
|
|
10
11
|
from typing import Any
|
|
11
12
|
from unittest.mock import AsyncMock, patch
|
|
@@ -173,6 +174,44 @@ class FakeWebApiClient:
|
|
|
173
174
|
callback=get_homes_callback,
|
|
174
175
|
)
|
|
175
176
|
|
|
177
|
+
def get_default_home_data(self) -> HomeData:
|
|
178
|
+
"""Construct the default HomeData using simulated devices registered in the cloud."""
|
|
179
|
+
devices = [d.device_info for d in self.cloud.simulated_devices.values()]
|
|
180
|
+
products = [d.product for d in self.cloud.simulated_devices.values()]
|
|
181
|
+
return HomeData(
|
|
182
|
+
id=self.cloud.home_id,
|
|
183
|
+
name=self.cloud.home_name,
|
|
184
|
+
devices=devices,
|
|
185
|
+
products=products,
|
|
186
|
+
received_devices=[],
|
|
187
|
+
rooms=[],
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
def set_homes_response(
|
|
191
|
+
self,
|
|
192
|
+
home_data: HomeData | None = None,
|
|
193
|
+
status: int = 200,
|
|
194
|
+
) -> None:
|
|
195
|
+
"""Easily set the faked response payload for the homes endpoint using a HomeData dataclass.
|
|
196
|
+
|
|
197
|
+
If home_data is None, it defaults to the active get_default_home_data() state.
|
|
198
|
+
"""
|
|
199
|
+
self.homes_status = status
|
|
200
|
+
if status != 200:
|
|
201
|
+
self.homes_payload_override = None
|
|
202
|
+
return
|
|
203
|
+
|
|
204
|
+
if home_data is None:
|
|
205
|
+
home_data = self.get_default_home_data()
|
|
206
|
+
|
|
207
|
+
self.homes_payload_override = {
|
|
208
|
+
"api": None,
|
|
209
|
+
"code": 200,
|
|
210
|
+
"result": home_data.as_dict(),
|
|
211
|
+
"status": "ok",
|
|
212
|
+
"success": True,
|
|
213
|
+
}
|
|
214
|
+
|
|
176
215
|
|
|
177
216
|
class FakeRoborockCloud:
|
|
178
217
|
"""A central state object representing the Roborock Cloud environment under test."""
|
|
@@ -234,7 +273,9 @@ class FakeRoborockCloud:
|
|
|
234
273
|
with aioresponses() as mocked:
|
|
235
274
|
self.web_api.mock_requests(mocked)
|
|
236
275
|
|
|
237
|
-
#
|
|
276
|
+
# Mock sleep logic to speed up tests.
|
|
277
|
+
sleep_time = datetime.timedelta(seconds=0.001)
|
|
278
|
+
# Patch Channel factories, rate limiters, and backoff sleep intervals
|
|
238
279
|
with (
|
|
239
280
|
patch(
|
|
240
281
|
"roborock.web_api.RoborockApiClient._login_limiter.try_acquire_async",
|
|
@@ -243,5 +284,7 @@ class FakeRoborockCloud:
|
|
|
243
284
|
patch("roborock.web_api.RoborockApiClient._home_data_limiter.try_acquire", return_value=True),
|
|
244
285
|
patch("roborock.devices.device_manager.create_v1_channel", side_effect=mock_create_v1_channel),
|
|
245
286
|
patch("roborock.devices.device_manager.create_mqtt_channel", side_effect=mock_create_mqtt_channel),
|
|
287
|
+
patch("roborock.devices.device.MIN_BACKOFF_INTERVAL", sleep_time),
|
|
288
|
+
patch("roborock.devices.device.MAX_BACKOFF_INTERVAL", sleep_time),
|
|
246
289
|
):
|
|
247
290
|
yield
|
|
@@ -73,26 +73,20 @@ class RoborockDeviceSimulator:
|
|
|
73
73
|
|
|
74
74
|
# MQTT channel is always present — all protocols use it.
|
|
75
75
|
self.mqtt_channel = FakeChannel(is_local=False)
|
|
76
|
-
self.mqtt_channel.
|
|
76
|
+
self.mqtt_channel.publish_handler = self._handle_mqtt_publish
|
|
77
77
|
|
|
78
78
|
# Local channel is only used by V1 devices. A01/B01 (MQTT-only)
|
|
79
79
|
# simulators should pass has_local_channel=False.
|
|
80
80
|
self.local_channel: FakeChannel | None = None
|
|
81
81
|
if has_local_channel:
|
|
82
82
|
self.local_channel = FakeChannel(is_local=True)
|
|
83
|
-
self.local_channel.
|
|
83
|
+
self.local_channel.publish_handler = self._handle_local_publish
|
|
84
84
|
|
|
85
85
|
async def _handle_local_publish(self, message: RoborockMessage) -> None:
|
|
86
86
|
assert self.local_channel is not None
|
|
87
|
-
self.local_channel.published_messages.append(message)
|
|
88
|
-
if self.local_channel.publish_side_effect:
|
|
89
|
-
raise self.local_channel.publish_side_effect
|
|
90
87
|
await self._handle_publish(message, self.local_channel)
|
|
91
88
|
|
|
92
89
|
async def _handle_mqtt_publish(self, message: RoborockMessage) -> None:
|
|
93
|
-
self.mqtt_channel.published_messages.append(message)
|
|
94
|
-
if self.mqtt_channel.publish_side_effect:
|
|
95
|
-
raise self.mqtt_channel.publish_side_effect
|
|
96
90
|
await self._handle_publish(message, self.mqtt_channel)
|
|
97
91
|
|
|
98
92
|
async def _handle_publish(self, message: RoborockMessage, channel: FakeChannel) -> None:
|
|
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-5.28.0 → python_roborock-5.29.0}/roborock/data/b01_q10/b01_q10_code_mappings.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/b01_q10/b01_q10_containers.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/data/b01_q7/b01_q7_code_mappings.py
RENAMED
|
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-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/__init__.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/button_light.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/child_lock.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/clean_history.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/command.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/consumable.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/do_not_disturb.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/dust_collection.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q10/network_info.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q7/__init__.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q7/clean_summary.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/b01/q7/map_content.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/clean_summary.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/device_features.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/do_not_disturb.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/dust_collection_mode.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/flow_led_status.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/network_info.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/smart_wash_params.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/traits/v1/wash_towel_mode.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/transport/local_channel.py
RENAMED
|
File without changes
|
{python_roborock-5.28.0 → python_roborock-5.29.0}/roborock/devices/transport/mqtt_channel.py
RENAMED
|
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
|