python-roborock 5.27.0__tar.gz → 5.28.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_roborock-5.27.0 → python_roborock-5.28.1}/PKG-INFO +1 -1
- {python_roborock-5.27.0 → python_roborock-5.28.1}/pyproject.toml +1 -1
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/v1/v1_code_mappings.py +26 -16
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/v1/v1_containers.py +12 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/device_features.py +209 -12
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/__init__.py +18 -9
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/dust_collection_mode.py +6 -2
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/smart_wash_params.py +6 -2
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/wash_towel_mode.py +6 -2
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/testing/channel.py +27 -5
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/testing/cloud.py +44 -1
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/testing/simulator.py +2 -8
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/testing/v1_simulator.py +1 -1
- {python_roborock-5.27.0 → python_roborock-5.28.1}/.gitignore +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/LICENSE +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/README.md +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/broadcast_protocol.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/callbacks.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/cli.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/const.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/b01_q10/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/b01_q7/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/code_mappings.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/containers.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/dyad/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/dyad/dyad_code_mappings.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/dyad/dyad_containers.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/mower/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/mower/mower_containers.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/v1/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/v1/v1_clean_modes.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/zeo/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/zeo/zeo_code_mappings.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/zeo/zeo_containers.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/README.md +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/cache.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/device.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/device_manager.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/file_cache.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/rpc/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/rpc/a01_channel.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/rpc/b01_q10_channel.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/rpc/b01_q7_channel.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/rpc/v1_channel.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/a01/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/button_light.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/child_lock.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/clean_history.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/command.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/common.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/consumable.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/do_not_disturb.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/dust_collection.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/map.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/network_info.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/remote.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/status.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/vacuum.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/volume.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q7/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q7/clean_summary.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q7/map.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q7/map_content.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/common.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/traits_mixin.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/child_lock.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/clean_summary.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/command.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/common.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/consumeable.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/device_features.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/flow_led_status.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/home.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/led_status.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/map_content.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/maps.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/network_info.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/rooms.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/routines.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/status.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/volume.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/transport/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/transport/channel.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/transport/local_channel.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/transport/mqtt_channel.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/diagnostics.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/exceptions.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/map/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/map/b01_grid_layers.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/map/b01_map_parser.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/map/b01_q10_map_parser.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/map/b01_q10_overlays.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/map/map_parser.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/map/proto/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/map/proto/b01_scmap.proto +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/map/proto/b01_scmap_pb2.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/mqtt/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/mqtt/health_manager.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/mqtt/roborock_session.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/mqtt/session.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/protocol.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/protocols/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/protocols/a01_protocol.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/protocols/b01_q10_protocol.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/protocols/b01_q7_protocol.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/protocols/v1_protocol.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/py.typed +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/roborock_message.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/roborock_typing.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/testing/__init__.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/util.py +0 -0
- {python_roborock-5.27.0 → python_roborock-5.28.1}/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.28.1
|
|
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.28.1"
|
|
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"
|
|
@@ -650,22 +650,32 @@ class RoborockDockErrorCode(RoborockEnum):
|
|
|
650
650
|
|
|
651
651
|
class RoborockDockTypeCode(RoborockEnum):
|
|
652
652
|
unknown = -9999
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
653
|
+
o0_dock = 0
|
|
654
|
+
o1_dock = 1
|
|
655
|
+
o2_dock = 2
|
|
656
|
+
o3_dock = 3
|
|
657
|
+
oc_dock = 5
|
|
658
|
+
o3_plus_dock = 6
|
|
659
|
+
o4_dock = 7
|
|
660
|
+
pearl_dock = 8
|
|
661
|
+
pearl_plus_dock = 9
|
|
662
|
+
o5_dock = 10
|
|
663
|
+
shell_2s_dock = 11
|
|
664
|
+
couple_dock = 13
|
|
665
|
+
shell_3_dock = 14
|
|
666
|
+
shell_2c_dock = 15
|
|
667
|
+
shell_3s_dock = 16
|
|
668
|
+
k1_dock = 17
|
|
669
|
+
o6_dock = 18
|
|
670
|
+
k1c_dock = 19
|
|
671
|
+
k1s_dock = 20
|
|
672
|
+
shell_e_dock = 21
|
|
673
|
+
shell_2e_dock = 22
|
|
674
|
+
shell_3c_dock = 23
|
|
675
|
+
type_27_dock = 27
|
|
676
|
+
k1c_lite_dock = 28
|
|
677
|
+
shell_2e_lite_dock = 30
|
|
678
|
+
shell_2e_heat_dock = 40
|
|
669
679
|
|
|
670
680
|
|
|
671
681
|
class RoborockDockDustCollectionModeCode(RoborockEnum):
|
|
@@ -239,6 +239,12 @@ class Status(RoborockBase):
|
|
|
239
239
|
return map_flag
|
|
240
240
|
return None
|
|
241
241
|
|
|
242
|
+
@property
|
|
243
|
+
def has_am(self) -> bool | None:
|
|
244
|
+
if self.dss is None:
|
|
245
|
+
return None
|
|
246
|
+
return (self.dss & 3) == 2
|
|
247
|
+
|
|
242
248
|
@property
|
|
243
249
|
def clear_water_box_status(self) -> ClearWaterBoxStatus | None:
|
|
244
250
|
if self.dss:
|
|
@@ -376,6 +382,12 @@ class StatusV2(RoborockBase):
|
|
|
376
382
|
return map_flag
|
|
377
383
|
return None
|
|
378
384
|
|
|
385
|
+
@property
|
|
386
|
+
def has_am(self) -> bool | None:
|
|
387
|
+
if self.dss is None:
|
|
388
|
+
return None
|
|
389
|
+
return (self.dss & 3) == 2
|
|
390
|
+
|
|
379
391
|
@property
|
|
380
392
|
def clear_water_box_status(self) -> ClearWaterBoxStatus | None:
|
|
381
393
|
if self.dss:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass, field, fields
|
|
2
2
|
from enum import IntEnum, StrEnum
|
|
3
|
+
from functools import cache
|
|
3
4
|
from typing import Any, Self
|
|
4
5
|
|
|
5
6
|
from roborock.data.code_mappings import RoborockProductNickname
|
|
@@ -644,25 +645,221 @@ class DeviceFeatures(RoborockBase):
|
|
|
644
645
|
return [k for k, v in vars(self).items() if v]
|
|
645
646
|
|
|
646
647
|
|
|
648
|
+
_NO_DOCK_TYPES = {
|
|
649
|
+
RoborockDockTypeCode.unknown,
|
|
650
|
+
RoborockDockTypeCode.o0_dock,
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
_PURE_COLLECT_DOCK_TYPES = {
|
|
654
|
+
RoborockDockTypeCode.o1_dock,
|
|
655
|
+
RoborockDockTypeCode.oc_dock,
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
_PURE_WASH_DOCK_TYPES = {
|
|
659
|
+
RoborockDockTypeCode.o2_dock,
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
_COLLECT_WASH_DOCK_TYPES = {
|
|
663
|
+
RoborockDockTypeCode.o3_dock,
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
_ONLY_X_SERIES_DOCK_TYPES = {
|
|
667
|
+
RoborockDockTypeCode.o2_dock,
|
|
668
|
+
RoborockDockTypeCode.o3_dock,
|
|
669
|
+
RoborockDockTypeCode.o3_plus_dock,
|
|
670
|
+
RoborockDockTypeCode.o4_dock,
|
|
671
|
+
RoborockDockTypeCode.o5_dock,
|
|
672
|
+
RoborockDockTypeCode.o6_dock,
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
_CLEAN_FLUID_AUTO_DELIVERY_DOCK_TYPES = {
|
|
676
|
+
RoborockDockTypeCode.o4_dock,
|
|
677
|
+
RoborockDockTypeCode.o5_dock,
|
|
678
|
+
RoborockDockTypeCode.pearl_plus_dock,
|
|
679
|
+
RoborockDockTypeCode.shell_3s_dock,
|
|
680
|
+
RoborockDockTypeCode.o6_dock,
|
|
681
|
+
RoborockDockTypeCode.couple_dock,
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
_CLEAN_FLUID_AUTO_DELIVERY_AM_DOCK_TYPES = {
|
|
685
|
+
RoborockDockTypeCode.shell_2s_dock,
|
|
686
|
+
RoborockDockTypeCode.shell_3_dock,
|
|
687
|
+
RoborockDockTypeCode.shell_2c_dock,
|
|
688
|
+
RoborockDockTypeCode.k1_dock,
|
|
689
|
+
RoborockDockTypeCode.k1s_dock,
|
|
690
|
+
RoborockDockTypeCode.shell_e_dock,
|
|
691
|
+
RoborockDockTypeCode.shell_2e_dock,
|
|
692
|
+
RoborockDockTypeCode.shell_3c_dock,
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
_AUTO_STERILIZE_DOCK_TYPES = {
|
|
696
|
+
RoborockDockTypeCode.shell_3s_dock,
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
_INNER_CHARGE_HEAD_DOCK_TYPES = {
|
|
700
|
+
RoborockDockTypeCode.o0_dock,
|
|
701
|
+
RoborockDockTypeCode.o1_dock,
|
|
702
|
+
RoborockDockTypeCode.oc_dock,
|
|
703
|
+
RoborockDockTypeCode.o2_dock,
|
|
704
|
+
RoborockDockTypeCode.o3_dock,
|
|
705
|
+
RoborockDockTypeCode.o3_plus_dock,
|
|
706
|
+
RoborockDockTypeCode.o4_dock,
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
_DUST_BUCKET_DOCK_TYPES = {
|
|
710
|
+
RoborockDockTypeCode.o3_dock,
|
|
711
|
+
RoborockDockTypeCode.o3_plus_dock,
|
|
712
|
+
RoborockDockTypeCode.o4_dock,
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
_CLEAN_CAROUSEL_SELF_CLEAN_DOCK_TYPES = {
|
|
716
|
+
RoborockDockTypeCode.shell_3_dock,
|
|
717
|
+
RoborockDockTypeCode.k1_dock,
|
|
718
|
+
RoborockDockTypeCode.couple_dock,
|
|
719
|
+
RoborockDockTypeCode.shell_3s_dock,
|
|
720
|
+
RoborockDockTypeCode.k1s_dock,
|
|
721
|
+
RoborockDockTypeCode.shell_3c_dock,
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
_WATER_UPDOWN_DRAIN_DOCK_TYPES = {
|
|
725
|
+
RoborockDockTypeCode.o4_dock,
|
|
726
|
+
RoborockDockTypeCode.pearl_plus_dock,
|
|
727
|
+
RoborockDockTypeCode.couple_dock,
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
_WATER_UPDOWN_DRAIN_AM_DOCK_TYPES = {
|
|
731
|
+
RoborockDockTypeCode.o5_dock,
|
|
732
|
+
RoborockDockTypeCode.shell_2s_dock,
|
|
733
|
+
RoborockDockTypeCode.shell_3_dock,
|
|
734
|
+
RoborockDockTypeCode.shell_2c_dock,
|
|
735
|
+
RoborockDockTypeCode.shell_3s_dock,
|
|
736
|
+
RoborockDockTypeCode.k1_dock,
|
|
737
|
+
RoborockDockTypeCode.k1s_dock,
|
|
738
|
+
RoborockDockTypeCode.o6_dock,
|
|
739
|
+
RoborockDockTypeCode.shell_e_dock,
|
|
740
|
+
RoborockDockTypeCode.shell_2e_dock,
|
|
741
|
+
RoborockDockTypeCode.shell_3c_dock,
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
_HATCH_DOOR_DOCK_COOL_FAN_DOCK_TYPES = {
|
|
745
|
+
RoborockDockTypeCode.couple_dock,
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
_SPECIAL_WASH_TEMP_DOCK_TYPES = {
|
|
749
|
+
RoborockDockTypeCode.shell_2e_lite_dock,
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
@dataclass(frozen=True)
|
|
754
|
+
class RoborockDockFeatures:
|
|
755
|
+
"""Capabilities derived from a V1 dock type.
|
|
756
|
+
|
|
757
|
+
This mirrors the Roborock app's DK capability model: feature availability
|
|
758
|
+
should be attached to the dock family instead of repeated per trait.
|
|
759
|
+
"""
|
|
760
|
+
|
|
761
|
+
dock_type: RoborockDockTypeCode
|
|
762
|
+
has_am: bool | None = None
|
|
763
|
+
|
|
764
|
+
@classmethod
|
|
765
|
+
@cache
|
|
766
|
+
def from_dock_type(cls, dock_type: RoborockDockTypeCode | None, has_am: bool | None = None) -> Self:
|
|
767
|
+
"""Return cached capabilities for a V1 dock type."""
|
|
768
|
+
return cls(dock_type or RoborockDockTypeCode.o0_dock, has_am=has_am)
|
|
769
|
+
|
|
770
|
+
def _matches_am_variant(self, dock_types: set[RoborockDockTypeCode]) -> bool:
|
|
771
|
+
return self.has_am is True and self.dock_type in dock_types
|
|
772
|
+
|
|
773
|
+
def _matches_non_am_variant(self, dock_types: set[RoborockDockTypeCode]) -> bool:
|
|
774
|
+
return self.has_am is not True and self.dock_type in dock_types
|
|
775
|
+
|
|
776
|
+
@property
|
|
777
|
+
def has_dock(self) -> bool:
|
|
778
|
+
return self.dock_type not in _NO_DOCK_TYPES
|
|
779
|
+
|
|
780
|
+
@property
|
|
781
|
+
def is_pure_collect(self) -> bool:
|
|
782
|
+
return self.dock_type in _PURE_COLLECT_DOCK_TYPES
|
|
783
|
+
|
|
784
|
+
@property
|
|
785
|
+
def is_pure_wash(self) -> bool:
|
|
786
|
+
return self.dock_type in _PURE_WASH_DOCK_TYPES
|
|
787
|
+
|
|
788
|
+
@property
|
|
789
|
+
def is_collect_wash(self) -> bool:
|
|
790
|
+
return self.dock_type in _COLLECT_WASH_DOCK_TYPES
|
|
791
|
+
|
|
792
|
+
@property
|
|
793
|
+
def is_collect_wash_dry(self) -> bool:
|
|
794
|
+
return self.has_dock and not (self.is_pure_collect or self.is_pure_wash or self.is_collect_wash)
|
|
795
|
+
|
|
796
|
+
@property
|
|
797
|
+
def is_collectable(self) -> bool:
|
|
798
|
+
return self.is_collect_wash or self.is_collect_wash_dry or self.is_pure_collect
|
|
799
|
+
|
|
800
|
+
@property
|
|
801
|
+
def is_washable(self) -> bool:
|
|
802
|
+
return self.is_pure_wash or self.is_collect_wash or self.is_collect_wash_dry
|
|
803
|
+
|
|
804
|
+
@property
|
|
805
|
+
def is_dryable(self) -> bool:
|
|
806
|
+
return self.is_collect_wash_dry
|
|
807
|
+
|
|
808
|
+
@property
|
|
809
|
+
def is_only_x_series(self) -> bool:
|
|
810
|
+
return self.dock_type in _ONLY_X_SERIES_DOCK_TYPES
|
|
811
|
+
|
|
812
|
+
@property
|
|
813
|
+
def is_cleaning_brush_supported(self) -> bool:
|
|
814
|
+
return self.is_only_x_series
|
|
815
|
+
|
|
816
|
+
@property
|
|
817
|
+
def is_clean_fluid_auto_delivery_supported(self) -> bool:
|
|
818
|
+
return self.dock_type in _CLEAN_FLUID_AUTO_DELIVERY_DOCK_TYPES or self._matches_am_variant(
|
|
819
|
+
_CLEAN_FLUID_AUTO_DELIVERY_AM_DOCK_TYPES
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
@property
|
|
823
|
+
def is_auto_sterilize_supported(self) -> bool:
|
|
824
|
+
return self.dock_type in _AUTO_STERILIZE_DOCK_TYPES
|
|
825
|
+
|
|
826
|
+
@property
|
|
827
|
+
def is_inner_charge_head(self) -> bool:
|
|
828
|
+
return self.dock_type in _INNER_CHARGE_HEAD_DOCK_TYPES
|
|
829
|
+
|
|
830
|
+
@property
|
|
831
|
+
def is_dust_bucket_supported(self) -> bool:
|
|
832
|
+
return self.dock_type in _DUST_BUCKET_DOCK_TYPES
|
|
833
|
+
|
|
834
|
+
@property
|
|
835
|
+
def is_clean_carousel_self_clean_supported(self) -> bool:
|
|
836
|
+
return self._matches_non_am_variant(_CLEAN_CAROUSEL_SELF_CLEAN_DOCK_TYPES)
|
|
837
|
+
|
|
838
|
+
@property
|
|
839
|
+
def is_water_updown_drain_supported(self) -> bool:
|
|
840
|
+
return self.dock_type in _WATER_UPDOWN_DRAIN_DOCK_TYPES or self._matches_am_variant(
|
|
841
|
+
_WATER_UPDOWN_DRAIN_AM_DOCK_TYPES
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
@property
|
|
845
|
+
def is_hatch_door_dock_cool_fan_supported(self) -> bool:
|
|
846
|
+
return self.dock_type in _HATCH_DOOR_DOCK_COOL_FAN_DOCK_TYPES
|
|
847
|
+
|
|
848
|
+
@property
|
|
849
|
+
def is_special_support_wash_temp(self) -> bool:
|
|
850
|
+
return self.dock_type in _SPECIAL_WASH_TEMP_DOCK_TYPES
|
|
851
|
+
|
|
852
|
+
|
|
647
853
|
WASH_N_FILL_DOCK_TYPES = [
|
|
648
|
-
RoborockDockTypeCode.
|
|
649
|
-
RoborockDockTypeCode.s8_dock,
|
|
650
|
-
RoborockDockTypeCode.p10_dock,
|
|
651
|
-
RoborockDockTypeCode.p10_pro_dock,
|
|
652
|
-
RoborockDockTypeCode.s8_maxv_ultra_dock,
|
|
653
|
-
RoborockDockTypeCode.qrevo_s_dock,
|
|
654
|
-
RoborockDockTypeCode.saros_r10_dock,
|
|
655
|
-
RoborockDockTypeCode.qrevo_curv_dock,
|
|
656
|
-
RoborockDockTypeCode.qrevo_s5v_dock,
|
|
657
|
-
RoborockDockTypeCode.saros_20_dock,
|
|
854
|
+
dock_type for dock_type in RoborockDockTypeCode if RoborockDockFeatures.from_dock_type(dock_type).is_washable
|
|
658
855
|
]
|
|
659
856
|
|
|
660
857
|
|
|
661
858
|
def is_wash_n_fill_dock(dock_type: RoborockDockTypeCode) -> bool:
|
|
662
859
|
"""Check if the dock type is a wash and fill dock."""
|
|
663
|
-
return dock_type
|
|
860
|
+
return RoborockDockFeatures.from_dock_type(dock_type).is_washable
|
|
664
861
|
|
|
665
862
|
|
|
666
863
|
def is_valid_dock(dock_type: RoborockDockTypeCode) -> bool:
|
|
667
864
|
"""Check if device supports a dock."""
|
|
668
|
-
return dock_type
|
|
865
|
+
return RoborockDockFeatures.from_dock_type(dock_type).has_dock
|
|
@@ -42,8 +42,8 @@ optional traits:
|
|
|
42
42
|
- `requires_feature` - The string name of the device feature that must be supported
|
|
43
43
|
for this trait to be enabled. See `DeviceFeaturesTrait` for a list of
|
|
44
44
|
available features.
|
|
45
|
-
- `
|
|
46
|
-
and returns a boolean indicating whether the trait is supported for that dock
|
|
45
|
+
- `requires_dock_features` - If set, this is a function that accepts a `RoborockDockFeatures`
|
|
46
|
+
and returns a boolean indicating whether the trait is supported for that dock.
|
|
47
47
|
|
|
48
48
|
Additionally, DeviceFeaturesTrait has a method `is_field_supported` that is used to
|
|
49
49
|
check individual trait field values. This is a more fine grained version to allow
|
|
@@ -59,6 +59,7 @@ from typing import Any, get_args
|
|
|
59
59
|
|
|
60
60
|
from roborock.data.containers import HomeData, HomeDataProduct, RoborockBase
|
|
61
61
|
from roborock.data.v1.v1_code_mappings import RoborockDockTypeCode
|
|
62
|
+
from roborock.device_features import RoborockDockFeatures
|
|
62
63
|
from roborock.devices.cache import DeviceCache
|
|
63
64
|
from roborock.devices.traits import Trait
|
|
64
65
|
from roborock.exceptions import RoborockException
|
|
@@ -262,9 +263,10 @@ class PropertiesApi(Trait):
|
|
|
262
263
|
await self.device_features.refresh()
|
|
263
264
|
# Dock type also acts like a device feature for some traits.
|
|
264
265
|
dock_type = await self._dock_type()
|
|
266
|
+
dock_features = RoborockDockFeatures.from_dock_type(dock_type, has_am=self.status.has_am)
|
|
265
267
|
|
|
266
268
|
# Initialize traits with special arguments before the generic loop
|
|
267
|
-
if self.wash_towel_mode is None and self._is_supported(WashTowelModeTrait, "wash_towel_mode",
|
|
269
|
+
if self.wash_towel_mode is None and self._is_supported(WashTowelModeTrait, "wash_towel_mode", dock_features):
|
|
268
270
|
wash_towel_mode = WashTowelModeTrait(self.device_features)
|
|
269
271
|
wash_towel_mode._rpc_channel = self._get_rpc_channel(wash_towel_mode) # type: ignore[assignment]
|
|
270
272
|
self.wash_towel_mode = wash_towel_mode
|
|
@@ -280,7 +282,7 @@ class PropertiesApi(Trait):
|
|
|
280
282
|
|
|
281
283
|
# Union args may not be in declared order
|
|
282
284
|
item_type = union_args[0] if union_args[1] is type(None) else union_args[1]
|
|
283
|
-
if not self._is_supported(item_type, item.name,
|
|
285
|
+
if not self._is_supported(item_type, item.name, dock_features):
|
|
284
286
|
_LOGGER.debug("Trait '%s' not supported, skipping", item.name)
|
|
285
287
|
continue
|
|
286
288
|
_LOGGER.debug("Trait '%s' is supported, initializing", item.name)
|
|
@@ -288,11 +290,11 @@ class PropertiesApi(Trait):
|
|
|
288
290
|
setattr(self, item.name, trait)
|
|
289
291
|
trait._rpc_channel = self._get_rpc_channel(trait)
|
|
290
292
|
|
|
291
|
-
def _is_supported(self, trait_type: type[V1TraitMixin], name: str,
|
|
293
|
+
def _is_supported(self, trait_type: type[V1TraitMixin], name: str, dock_features: RoborockDockFeatures) -> bool:
|
|
292
294
|
"""Check if a trait is supported by the device."""
|
|
293
295
|
|
|
294
|
-
if (
|
|
295
|
-
return
|
|
296
|
+
if (requires_dock_features := getattr(trait_type, "requires_dock_features", None)) is not None:
|
|
297
|
+
return requires_dock_features(dock_features)
|
|
296
298
|
|
|
297
299
|
if (feature_name := getattr(trait_type, "requires_feature", None)) is None:
|
|
298
300
|
_LOGGER.debug("Optional trait missing 'requires_feature' attribute %s, skipping", name)
|
|
@@ -307,16 +309,23 @@ class PropertiesApi(Trait):
|
|
|
307
309
|
if dock_type is not None:
|
|
308
310
|
_LOGGER.debug("Using cached dock type: %s", dock_type)
|
|
309
311
|
try:
|
|
310
|
-
|
|
312
|
+
dock_type = RoborockDockTypeCode(dock_type)
|
|
311
313
|
except ValueError:
|
|
312
314
|
_LOGGER.debug("Cached dock type %s is invalid, refreshing", dock_type)
|
|
315
|
+
else:
|
|
316
|
+
if self.status.dss is None:
|
|
317
|
+
await self.status.refresh()
|
|
318
|
+
if self.status.dock_type is not None:
|
|
319
|
+
dock_type = self.status.dock_type
|
|
320
|
+
await self._set_cached_trait_data("dock_type", dock_type)
|
|
321
|
+
return dock_type
|
|
313
322
|
|
|
314
323
|
_LOGGER.debug("Starting dock type discovery")
|
|
315
324
|
await self.status.refresh()
|
|
316
325
|
_LOGGER.debug("Fetched dock type: %s", self.status.dock_type)
|
|
317
326
|
if self.status.dock_type is None:
|
|
318
327
|
# Explicitly set so we reuse cached value next type
|
|
319
|
-
dock_type = RoborockDockTypeCode.
|
|
328
|
+
dock_type = RoborockDockTypeCode.o0_dock
|
|
320
329
|
else:
|
|
321
330
|
dock_type = self.status.dock_type
|
|
322
331
|
await self._set_cached_trait_data("dock_type", dock_type)
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/dust_collection_mode.py
RENAMED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
"""Trait for dust collection mode."""
|
|
2
2
|
|
|
3
3
|
from roborock.data import DustCollectionMode
|
|
4
|
-
from roborock.device_features import
|
|
4
|
+
from roborock.device_features import RoborockDockFeatures
|
|
5
5
|
from roborock.devices.traits.v1 import common
|
|
6
6
|
from roborock.roborock_typing import RoborockCommand
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
def _supports_dust_collection_mode(dock_features: RoborockDockFeatures) -> bool:
|
|
10
|
+
return dock_features.is_collectable
|
|
11
|
+
|
|
12
|
+
|
|
9
13
|
class DustCollectionModeTrait(DustCollectionMode, common.V1TraitMixin):
|
|
10
14
|
"""Trait for dust collection mode."""
|
|
11
15
|
|
|
12
16
|
command = RoborockCommand.GET_DUST_COLLECTION_MODE
|
|
13
17
|
converter = common.DefaultConverter(DustCollectionMode)
|
|
14
|
-
|
|
18
|
+
requires_dock_features = _supports_dust_collection_mode
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/smart_wash_params.py
RENAMED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
"""Trait for smart wash parameters."""
|
|
2
2
|
|
|
3
3
|
from roborock.data import SmartWashParams
|
|
4
|
-
from roborock.device_features import
|
|
4
|
+
from roborock.device_features import RoborockDockFeatures
|
|
5
5
|
from roborock.devices.traits.v1 import common
|
|
6
6
|
from roborock.roborock_typing import RoborockCommand
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
def _supports_smart_wash_params(dock_features: RoborockDockFeatures) -> bool:
|
|
10
|
+
return dock_features.is_washable
|
|
11
|
+
|
|
12
|
+
|
|
9
13
|
class SmartWashParamsTrait(SmartWashParams, common.V1TraitMixin):
|
|
10
14
|
"""Trait for smart wash parameters."""
|
|
11
15
|
|
|
12
16
|
command = RoborockCommand.GET_SMART_WASH_PARAMS
|
|
13
17
|
converter = common.DefaultConverter(SmartWashParams)
|
|
14
|
-
|
|
18
|
+
requires_dock_features = _supports_smart_wash_params
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/wash_towel_mode.py
RENAMED
|
@@ -4,18 +4,22 @@ from functools import cached_property
|
|
|
4
4
|
from typing import Self
|
|
5
5
|
|
|
6
6
|
from roborock.data import WashTowelMode, WashTowelModes, get_wash_towel_modes
|
|
7
|
-
from roborock.device_features import
|
|
7
|
+
from roborock.device_features import RoborockDockFeatures
|
|
8
8
|
from roborock.devices.traits.v1 import common
|
|
9
9
|
from roborock.devices.traits.v1.device_features import DeviceFeaturesTrait
|
|
10
10
|
from roborock.roborock_typing import RoborockCommand
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def _supports_wash_towel_mode(dock_features: RoborockDockFeatures) -> bool:
|
|
14
|
+
return dock_features.is_washable
|
|
15
|
+
|
|
16
|
+
|
|
13
17
|
class WashTowelModeTrait(WashTowelMode, common.V1TraitMixin):
|
|
14
18
|
"""Trait for wash towel mode."""
|
|
15
19
|
|
|
16
20
|
command = RoborockCommand.GET_WASH_TOWEL_MODE
|
|
17
21
|
converter = common.DefaultConverter(WashTowelMode)
|
|
18
|
-
|
|
22
|
+
requires_dock_features = _supports_wash_towel_mode
|
|
19
23
|
|
|
20
24
|
def __init__(
|
|
21
25
|
self,
|
|
@@ -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:
|
|
@@ -81,7 +81,7 @@ DEFAULT_STATUS = StatusV2(
|
|
|
81
81
|
home_sec_enable_password=0,
|
|
82
82
|
adbumper_status=[0, 0, 0],
|
|
83
83
|
water_shortage_status=0,
|
|
84
|
-
dock_type=RoborockDockTypeCode.
|
|
84
|
+
dock_type=RoborockDockTypeCode.o4_dock,
|
|
85
85
|
dust_collection_status=0,
|
|
86
86
|
auto_dust_collection=1,
|
|
87
87
|
avoid_count=19,
|
|
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.27.0 → python_roborock-5.28.1}/roborock/data/b01_q10/b01_q10_code_mappings.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/data/b01_q10/b01_q10_containers.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/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
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/__init__.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/button_light.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/child_lock.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/clean_history.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/command.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/consumable.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/do_not_disturb.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q10/dust_collection.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/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.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q7/__init__.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q7/clean_summary.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/b01/q7/map_content.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/clean_summary.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/device_features.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/do_not_disturb.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/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.27.0 → python_roborock-5.28.1}/roborock/devices/traits/v1/network_info.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
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/roborock/devices/transport/local_channel.py
RENAMED
|
File without changes
|
{python_roborock-5.27.0 → python_roborock-5.28.1}/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
|