python-roborock 5.36.0__tar.gz → 5.37.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.36.0 → python_roborock-5.37.1}/PKG-INFO +1 -1
- {python_roborock-5.36.0 → python_roborock-5.37.1}/pyproject.toml +1 -1
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/v1/v1_code_mappings.py +17 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/device_features.py +110 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/device_manager.py +1 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/rpc/v1_channel.py +52 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/__init__.py +17 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/common.py +1 -1
- python_roborock-5.37.1/roborock/devices/traits/v1/obstacle_photos.py +104 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/protocols/v1_protocol.py +45 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/roborock_typing.py +1 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/.gitignore +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/LICENSE +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/README.md +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/broadcast_protocol.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/callbacks.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/cli.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/const.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/b01_q10/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/b01_q7/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/code_mappings.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/containers.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/dyad/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/dyad/dyad_code_mappings.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/dyad/dyad_containers.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/mower/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/mower/mower_containers.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/v1/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/v1/v1_clean_modes.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/v1/v1_containers.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/zeo/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/zeo/zeo_code_mappings.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/zeo/zeo_containers.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/README.md +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/cache.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/device.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/file_cache.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/rpc/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/rpc/a01_channel.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/rpc/b01_q10_channel.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/rpc/b01_q7_channel.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/a01/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/button_light.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/child_lock.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/clean_history.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/command.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/common.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/consumable.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/do_not_disturb.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/dust_collection.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/map.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/network_info.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/remote.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/status.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/vacuum.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/volume.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q7/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q7/clean_summary.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q7/map.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q7/map_content.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/common.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/traits_mixin.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/child_lock.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/clean_summary.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/command.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/consumeable.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/device_features.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/flow_led_status.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/home.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/led_status.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/map_content.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/maps.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/network_info.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/rooms.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/routines.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/status.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/volume.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/transport/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/transport/channel.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/transport/local_channel.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/transport/mqtt_channel.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/diagnostics.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/exceptions.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/map/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/map/b01_grid_layers.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/map/b01_map_parser.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/map/b01_q10_map_parser.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/map/b01_q10_overlays.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/map/b01_q10_render.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/map/map_parser.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/map/proto/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/map/proto/b01_scmap.proto +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/map/proto/b01_scmap_pb2.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/mqtt/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/mqtt/health_manager.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/mqtt/roborock_session.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/mqtt/session.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/protocol.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/protocols/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/protocols/a01_protocol.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/protocols/b01_q10_protocol.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/protocols/b01_q7_protocol.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/py.typed +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/roborock_message.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/testing/__init__.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/testing/b01_q10_simulator.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/testing/channel.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/testing/cloud.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/testing/simulator.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/testing/v1_simulator.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/util.py +0 -0
- {python_roborock-5.36.0 → python_roborock-5.37.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.37.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.37.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"
|
|
@@ -365,10 +365,27 @@ class RoborockDockTypeCode(RoborockEnum):
|
|
|
365
365
|
shell_e_dock = 21
|
|
366
366
|
shell_2e_dock = 22
|
|
367
367
|
shell_3c_dock = 23
|
|
368
|
+
hera_dock = 24
|
|
369
|
+
k1s_pro_dock = 26
|
|
368
370
|
type_27_dock = 27
|
|
369
371
|
k1c_lite_dock = 28
|
|
372
|
+
k1r_dock = 29
|
|
370
373
|
shell_2e_lite_dock = 30
|
|
374
|
+
shell_4rc_dock = 31
|
|
375
|
+
shell_4r_dock = 32
|
|
376
|
+
shell_4p_dock = 33
|
|
377
|
+
shell_4s_dock = 34
|
|
378
|
+
o7_dock = 35
|
|
379
|
+
k1d_dock = 37
|
|
380
|
+
shell_3p_dock = 38
|
|
381
|
+
shell_4d_dock = 39
|
|
371
382
|
shell_2e_heat_dock = 40
|
|
383
|
+
f1_dock = 41
|
|
384
|
+
f1r_dock = 42
|
|
385
|
+
f2r_dock = 43
|
|
386
|
+
f1s_dock = 44
|
|
387
|
+
o7h_dock = 45
|
|
388
|
+
f1c_dock = 46
|
|
372
389
|
|
|
373
390
|
|
|
374
391
|
class RoborockDockDustCollectionModeCode(RoborockEnum):
|
|
@@ -539,6 +539,12 @@ class DeviceFeatures(RoborockBase):
|
|
|
539
539
|
]
|
|
540
540
|
}
|
|
541
541
|
)
|
|
542
|
+
is_ai_recognition_setting_supported: bool = field(
|
|
543
|
+
metadata={"product_features": [ProductFeatures.AIRECOGNITION_SETTING]}
|
|
544
|
+
)
|
|
545
|
+
is_ai_recognition_obstacle_supported: bool = field(
|
|
546
|
+
metadata={"product_features": [ProductFeatures.AIRECOGNITION_OBSTACLE]}
|
|
547
|
+
)
|
|
542
548
|
is_pure_clean_mop_supported: bool = field(metadata={"product_features": [ProductFeatures.CLEANMODE_PURECLEANMOP]})
|
|
543
549
|
is_new_remote_view_supported: bool = field(metadata={"product_features": [ProductFeatures.REMOTE_BACK]})
|
|
544
550
|
is_max_plus_mode_supported: bool = field(metadata={"product_features": [ProductFeatures.CLEANMODE_MAXPLUS]})
|
|
@@ -670,6 +676,8 @@ _ONLY_X_SERIES_DOCK_TYPES = {
|
|
|
670
676
|
RoborockDockTypeCode.o4_dock,
|
|
671
677
|
RoborockDockTypeCode.o5_dock,
|
|
672
678
|
RoborockDockTypeCode.o6_dock,
|
|
679
|
+
RoborockDockTypeCode.o7_dock,
|
|
680
|
+
RoborockDockTypeCode.o7h_dock,
|
|
673
681
|
}
|
|
674
682
|
|
|
675
683
|
_CLEAN_FLUID_AUTO_DELIVERY_DOCK_TYPES = {
|
|
@@ -679,6 +687,10 @@ _CLEAN_FLUID_AUTO_DELIVERY_DOCK_TYPES = {
|
|
|
679
687
|
RoborockDockTypeCode.shell_3s_dock,
|
|
680
688
|
RoborockDockTypeCode.o6_dock,
|
|
681
689
|
RoborockDockTypeCode.couple_dock,
|
|
690
|
+
RoborockDockTypeCode.type_27_dock,
|
|
691
|
+
RoborockDockTypeCode.o7_dock,
|
|
692
|
+
RoborockDockTypeCode.f1s_dock,
|
|
693
|
+
RoborockDockTypeCode.o7h_dock,
|
|
682
694
|
}
|
|
683
695
|
|
|
684
696
|
_CLEAN_FLUID_AUTO_DELIVERY_AM_DOCK_TYPES = {
|
|
@@ -690,6 +702,7 @@ _CLEAN_FLUID_AUTO_DELIVERY_AM_DOCK_TYPES = {
|
|
|
690
702
|
RoborockDockTypeCode.shell_e_dock,
|
|
691
703
|
RoborockDockTypeCode.shell_2e_dock,
|
|
692
704
|
RoborockDockTypeCode.shell_3c_dock,
|
|
705
|
+
RoborockDockTypeCode.k1c_dock,
|
|
693
706
|
}
|
|
694
707
|
|
|
695
708
|
_AUTO_STERILIZE_DOCK_TYPES = {
|
|
@@ -719,6 +732,18 @@ _CLEAN_CAROUSEL_SELF_CLEAN_DOCK_TYPES = {
|
|
|
719
732
|
RoborockDockTypeCode.shell_3s_dock,
|
|
720
733
|
RoborockDockTypeCode.k1s_dock,
|
|
721
734
|
RoborockDockTypeCode.shell_3c_dock,
|
|
735
|
+
RoborockDockTypeCode.k1s_pro_dock,
|
|
736
|
+
RoborockDockTypeCode.type_27_dock,
|
|
737
|
+
RoborockDockTypeCode.k1r_dock,
|
|
738
|
+
RoborockDockTypeCode.shell_4rc_dock,
|
|
739
|
+
RoborockDockTypeCode.shell_4p_dock,
|
|
740
|
+
RoborockDockTypeCode.shell_4s_dock,
|
|
741
|
+
RoborockDockTypeCode.k1d_dock,
|
|
742
|
+
RoborockDockTypeCode.shell_3p_dock,
|
|
743
|
+
RoborockDockTypeCode.f1_dock,
|
|
744
|
+
RoborockDockTypeCode.f2r_dock,
|
|
745
|
+
RoborockDockTypeCode.f1s_dock,
|
|
746
|
+
RoborockDockTypeCode.f1c_dock,
|
|
722
747
|
}
|
|
723
748
|
|
|
724
749
|
_WATER_UPDOWN_DRAIN_DOCK_TYPES = {
|
|
@@ -739,6 +764,19 @@ _WATER_UPDOWN_DRAIN_AM_DOCK_TYPES = {
|
|
|
739
764
|
RoborockDockTypeCode.shell_e_dock,
|
|
740
765
|
RoborockDockTypeCode.shell_2e_dock,
|
|
741
766
|
RoborockDockTypeCode.shell_3c_dock,
|
|
767
|
+
RoborockDockTypeCode.k1c_dock,
|
|
768
|
+
RoborockDockTypeCode.hera_dock,
|
|
769
|
+
RoborockDockTypeCode.k1s_pro_dock,
|
|
770
|
+
RoborockDockTypeCode.type_27_dock,
|
|
771
|
+
RoborockDockTypeCode.shell_4rc_dock,
|
|
772
|
+
RoborockDockTypeCode.shell_4r_dock,
|
|
773
|
+
RoborockDockTypeCode.shell_4p_dock,
|
|
774
|
+
RoborockDockTypeCode.shell_4s_dock,
|
|
775
|
+
RoborockDockTypeCode.o7_dock,
|
|
776
|
+
RoborockDockTypeCode.shell_4d_dock,
|
|
777
|
+
RoborockDockTypeCode.f1r_dock,
|
|
778
|
+
RoborockDockTypeCode.f1s_dock,
|
|
779
|
+
RoborockDockTypeCode.o7h_dock,
|
|
742
780
|
}
|
|
743
781
|
|
|
744
782
|
_HATCH_DOOR_DOCK_COOL_FAN_DOCK_TYPES = {
|
|
@@ -749,6 +787,56 @@ _SPECIAL_WASH_TEMP_DOCK_TYPES = {
|
|
|
749
787
|
RoborockDockTypeCode.shell_2e_lite_dock,
|
|
750
788
|
}
|
|
751
789
|
|
|
790
|
+
_HIGH_TMP_WATER_DOCK_TYPES = {
|
|
791
|
+
RoborockDockTypeCode.k1r_dock,
|
|
792
|
+
RoborockDockTypeCode.shell_4rc_dock,
|
|
793
|
+
RoborockDockTypeCode.shell_2e_heat_dock,
|
|
794
|
+
RoborockDockTypeCode.f1_dock,
|
|
795
|
+
RoborockDockTypeCode.f1r_dock,
|
|
796
|
+
RoborockDockTypeCode.f2r_dock,
|
|
797
|
+
RoborockDockTypeCode.f1c_dock,
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
_DOUBLE_SERIAL_COMMUNICATION_DOCK_TYPES = {
|
|
801
|
+
RoborockDockTypeCode.k1s_pro_dock,
|
|
802
|
+
RoborockDockTypeCode.type_27_dock,
|
|
803
|
+
RoborockDockTypeCode.k1r_dock,
|
|
804
|
+
RoborockDockTypeCode.shell_4r_dock,
|
|
805
|
+
RoborockDockTypeCode.shell_4p_dock,
|
|
806
|
+
RoborockDockTypeCode.shell_4s_dock,
|
|
807
|
+
RoborockDockTypeCode.o7_dock,
|
|
808
|
+
RoborockDockTypeCode.k1d_dock,
|
|
809
|
+
RoborockDockTypeCode.shell_3p_dock,
|
|
810
|
+
RoborockDockTypeCode.f1_dock,
|
|
811
|
+
RoborockDockTypeCode.f1r_dock,
|
|
812
|
+
RoborockDockTypeCode.f2r_dock,
|
|
813
|
+
RoborockDockTypeCode.f1s_dock,
|
|
814
|
+
RoborockDockTypeCode.o7h_dock,
|
|
815
|
+
RoborockDockTypeCode.f1c_dock,
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
_DOUBLE_SERIAL_COMMUNICATION_AM_DOCK_TYPES = {
|
|
819
|
+
RoborockDockTypeCode.shell_4rc_dock,
|
|
820
|
+
RoborockDockTypeCode.shell_4d_dock,
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
_NO_WATER_FLOW_CAROUSEL_DOCK_TYPES = {
|
|
824
|
+
RoborockDockTypeCode.f1r_dock,
|
|
825
|
+
RoborockDockTypeCode.f2r_dock,
|
|
826
|
+
RoborockDockTypeCode.f1s_dock,
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
_SIMPLE_WASH_MODE_DOCK_TYPES = {
|
|
830
|
+
RoborockDockTypeCode.f1_dock,
|
|
831
|
+
RoborockDockTypeCode.f1r_dock,
|
|
832
|
+
RoborockDockTypeCode.f2r_dock,
|
|
833
|
+
RoborockDockTypeCode.f1c_dock,
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
_STEAM_WASH_DOCK_TYPES = {
|
|
837
|
+
RoborockDockTypeCode.o7h_dock,
|
|
838
|
+
}
|
|
839
|
+
|
|
752
840
|
|
|
753
841
|
@dataclass(frozen=True)
|
|
754
842
|
class RoborockDockFeatures:
|
|
@@ -849,6 +937,28 @@ class RoborockDockFeatures:
|
|
|
849
937
|
def is_special_support_wash_temp(self) -> bool:
|
|
850
938
|
return self.dock_type in _SPECIAL_WASH_TEMP_DOCK_TYPES
|
|
851
939
|
|
|
940
|
+
@property
|
|
941
|
+
def is_high_tmp_water_supported(self) -> bool:
|
|
942
|
+
return self.dock_type in _HIGH_TMP_WATER_DOCK_TYPES
|
|
943
|
+
|
|
944
|
+
@property
|
|
945
|
+
def is_double_serial_communication_supported(self) -> bool:
|
|
946
|
+
return self.dock_type in _DOUBLE_SERIAL_COMMUNICATION_DOCK_TYPES or self._matches_am_variant(
|
|
947
|
+
_DOUBLE_SERIAL_COMMUNICATION_AM_DOCK_TYPES
|
|
948
|
+
)
|
|
949
|
+
|
|
950
|
+
@property
|
|
951
|
+
def is_no_water_flow_carousel(self) -> bool:
|
|
952
|
+
return self.dock_type in _NO_WATER_FLOW_CAROUSEL_DOCK_TYPES
|
|
953
|
+
|
|
954
|
+
@property
|
|
955
|
+
def is_simple_wash_mode_supported(self) -> bool:
|
|
956
|
+
return self.dock_type in _SIMPLE_WASH_MODE_DOCK_TYPES
|
|
957
|
+
|
|
958
|
+
@property
|
|
959
|
+
def is_steam_wash_supported(self) -> bool:
|
|
960
|
+
return self.dock_type in _STEAM_WASH_DOCK_TYPES
|
|
961
|
+
|
|
852
962
|
|
|
853
963
|
WASH_N_FILL_DOCK_TYPES = [
|
|
854
964
|
dock_type for dock_type in RoborockDockTypeCode if RoborockDockFeatures.from_dock_type(dock_type).is_washable
|
|
@@ -29,6 +29,7 @@ from roborock.protocols.v1_protocol import (
|
|
|
29
29
|
ResponseMessage,
|
|
30
30
|
SecurityData,
|
|
31
31
|
V1RpcChannel,
|
|
32
|
+
create_blob_response_decoder,
|
|
32
33
|
create_map_response_decoder,
|
|
33
34
|
create_security_data,
|
|
34
35
|
decode_data_protocol_message,
|
|
@@ -161,6 +162,50 @@ class RpcChannel(V1RpcChannel):
|
|
|
161
162
|
return result
|
|
162
163
|
|
|
163
164
|
|
|
165
|
+
class BlobRpcChannel(V1RpcChannel):
|
|
166
|
+
"""RPC channel that adds the security envelope required for blob requests."""
|
|
167
|
+
|
|
168
|
+
def __init__(
|
|
169
|
+
self,
|
|
170
|
+
rpc_channel: V1RpcChannel,
|
|
171
|
+
raw_blob_rpc_channel: V1RpcChannel,
|
|
172
|
+
security_data: SecurityData,
|
|
173
|
+
) -> None:
|
|
174
|
+
"""Initialize the blob RPC channel."""
|
|
175
|
+
self._rpc_channel = rpc_channel
|
|
176
|
+
self._raw_blob_rpc_channel = raw_blob_rpc_channel
|
|
177
|
+
self._security_data = security_data
|
|
178
|
+
|
|
179
|
+
async def send_command(
|
|
180
|
+
self,
|
|
181
|
+
method: CommandType,
|
|
182
|
+
*,
|
|
183
|
+
response_type: type[_T] | None = None,
|
|
184
|
+
params: ParamsType = None,
|
|
185
|
+
) -> _T | Any:
|
|
186
|
+
"""Send a blob command after adding the device security parameters."""
|
|
187
|
+
public_key = await self._rpc_channel.send_command(RoborockCommand.GET_RANDOM_PKEY)
|
|
188
|
+
if not isinstance(public_key, dict) or not isinstance(public_key.get("pub_key"), dict):
|
|
189
|
+
raise RoborockException("get_random_pkey response did not contain a public key")
|
|
190
|
+
security = self._security_data.to_dict()["security"]
|
|
191
|
+
blob_params = {
|
|
192
|
+
"security": {
|
|
193
|
+
"pub_key": public_key["pub_key"],
|
|
194
|
+
"cipher_suite": 0,
|
|
195
|
+
},
|
|
196
|
+
"endpoint": security["endpoint"],
|
|
197
|
+
"nonce": security["nonce"],
|
|
198
|
+
"data_filter": params,
|
|
199
|
+
}
|
|
200
|
+
if response_type is not None:
|
|
201
|
+
return await self._raw_blob_rpc_channel.send_command(
|
|
202
|
+
method,
|
|
203
|
+
response_type=response_type,
|
|
204
|
+
params=blob_params,
|
|
205
|
+
)
|
|
206
|
+
return await self._raw_blob_rpc_channel.send_command(method, params=blob_params)
|
|
207
|
+
|
|
208
|
+
|
|
164
209
|
class V1Channel(Channel):
|
|
165
210
|
"""Unified V1 protocol channel with automatic MQTT/local connection handling.
|
|
166
211
|
|
|
@@ -245,6 +290,13 @@ class V1Channel(Channel):
|
|
|
245
290
|
decoder = create_map_response_decoder(security_data=self._security_data)
|
|
246
291
|
return RpcChannel(lambda: [self._create_mqtt_rpc_strategy(decoder)], self._logger)
|
|
247
292
|
|
|
293
|
+
@property
|
|
294
|
+
def blob_rpc_channel(self) -> V1RpcChannel:
|
|
295
|
+
"""Return the blob RPC channel used for fetching binary content."""
|
|
296
|
+
decoder = create_blob_response_decoder()
|
|
297
|
+
raw_blob_rpc_channel = RpcChannel(lambda: [self._create_mqtt_rpc_strategy(decoder)], self._logger)
|
|
298
|
+
return BlobRpcChannel(self.rpc_channel, raw_blob_rpc_channel, self._security_data)
|
|
299
|
+
|
|
248
300
|
def _create_local_rpc_strategy(self) -> RpcStrategy | None:
|
|
249
301
|
"""Create the RPC strategy for local transport."""
|
|
250
302
|
if self._local_channel is None or not self.is_local_connected:
|
|
@@ -83,6 +83,7 @@ from . import (
|
|
|
83
83
|
map_content,
|
|
84
84
|
maps,
|
|
85
85
|
network_info,
|
|
86
|
+
obstacle_photos,
|
|
86
87
|
rooms,
|
|
87
88
|
routines,
|
|
88
89
|
smart_wash_params,
|
|
@@ -105,6 +106,7 @@ from .led_status import LedStatusTrait
|
|
|
105
106
|
from .map_content import MapContentTrait
|
|
106
107
|
from .maps import MapsTrait
|
|
107
108
|
from .network_info import NetworkInfoTrait
|
|
109
|
+
from .obstacle_photos import ObstaclePhotoTrait
|
|
108
110
|
from .rooms import RoomsTrait
|
|
109
111
|
from .routines import RoutinesTrait
|
|
110
112
|
from .smart_wash_params import SmartWashParamsTrait
|
|
@@ -131,6 +133,7 @@ __all__ = [
|
|
|
131
133
|
"map_content",
|
|
132
134
|
"maps",
|
|
133
135
|
"network_info",
|
|
136
|
+
"obstacle_photos",
|
|
134
137
|
"rooms",
|
|
135
138
|
"routines",
|
|
136
139
|
"smart_wash_params",
|
|
@@ -171,6 +174,7 @@ class PropertiesApi(Trait):
|
|
|
171
174
|
dust_collection_mode: DustCollectionModeTrait | None = None
|
|
172
175
|
wash_towel_mode: WashTowelModeTrait | None = None
|
|
173
176
|
smart_wash_params: SmartWashParamsTrait | None = None
|
|
177
|
+
obstacle_photos: ObstaclePhotoTrait | None = None
|
|
174
178
|
|
|
175
179
|
def __init__(
|
|
176
180
|
self,
|
|
@@ -180,6 +184,7 @@ class PropertiesApi(Trait):
|
|
|
180
184
|
rpc_channel: V1RpcChannel,
|
|
181
185
|
mqtt_rpc_channel: V1RpcChannel,
|
|
182
186
|
map_rpc_channel: V1RpcChannel,
|
|
187
|
+
blob_rpc_channel: V1RpcChannel,
|
|
183
188
|
add_dps_listener: Callable[[Callable[[dict[RoborockDataProtocol, Any]], None]], Callable[[], None]],
|
|
184
189
|
web_api: UserWebApiClient,
|
|
185
190
|
device_cache: DeviceCache,
|
|
@@ -191,6 +196,7 @@ class PropertiesApi(Trait):
|
|
|
191
196
|
self._rpc_channel = rpc_channel
|
|
192
197
|
self._mqtt_rpc_channel = mqtt_rpc_channel
|
|
193
198
|
self._map_rpc_channel = map_rpc_channel
|
|
199
|
+
self._blob_rpc_channel = blob_rpc_channel
|
|
194
200
|
self._web_api = web_api
|
|
195
201
|
self._device_cache = device_cache
|
|
196
202
|
self._region = region
|
|
@@ -229,6 +235,8 @@ class PropertiesApi(Trait):
|
|
|
229
235
|
# to use the mqtt_rpc_channel (cloud only) instead of the rpc_channel (adaptive)
|
|
230
236
|
if hasattr(trait, "mqtt_rpc_channel"):
|
|
231
237
|
return self._mqtt_rpc_channel
|
|
238
|
+
elif hasattr(trait, "blob_rpc_channel"):
|
|
239
|
+
return self._blob_rpc_channel
|
|
232
240
|
elif hasattr(trait, "map_rpc_channel"):
|
|
233
241
|
return self._map_rpc_channel
|
|
234
242
|
else:
|
|
@@ -272,6 +280,11 @@ class PropertiesApi(Trait):
|
|
|
272
280
|
wash_towel_mode._rpc_channel = self._get_rpc_channel(wash_towel_mode) # type: ignore[assignment]
|
|
273
281
|
self.wash_towel_mode = wash_towel_mode
|
|
274
282
|
|
|
283
|
+
if self.obstacle_photos is None and self._is_supported(ObstaclePhotoTrait, "obstacle_photos", dock_features):
|
|
284
|
+
obstacle_photos = ObstaclePhotoTrait(self._rpc_channel)
|
|
285
|
+
obstacle_photos._rpc_channel = self._get_rpc_channel(obstacle_photos)
|
|
286
|
+
self.obstacle_photos = obstacle_photos
|
|
287
|
+
|
|
275
288
|
# Dynamically create any traits that need to be populated
|
|
276
289
|
for item in fields(self):
|
|
277
290
|
if (trait := getattr(self, item.name, None)) is not None:
|
|
@@ -283,6 +296,8 @@ class PropertiesApi(Trait):
|
|
|
283
296
|
|
|
284
297
|
# Union args may not be in declared order
|
|
285
298
|
item_type = union_args[0] if union_args[1] is type(None) else union_args[1]
|
|
299
|
+
if item_type is ObstaclePhotoTrait:
|
|
300
|
+
continue
|
|
286
301
|
if not self._is_supported(item_type, item.name, dock_features):
|
|
287
302
|
_LOGGER.debug("Trait '%s' not supported, skipping", item.name)
|
|
288
303
|
continue
|
|
@@ -369,6 +384,7 @@ def create(
|
|
|
369
384
|
rpc_channel: V1RpcChannel,
|
|
370
385
|
mqtt_rpc_channel: V1RpcChannel,
|
|
371
386
|
map_rpc_channel: V1RpcChannel,
|
|
387
|
+
blob_rpc_channel: V1RpcChannel,
|
|
372
388
|
add_dps_listener: Callable[[Callable[[dict[RoborockDataProtocol, Any]], None]], Callable[[], None]],
|
|
373
389
|
web_api: UserWebApiClient,
|
|
374
390
|
device_cache: DeviceCache,
|
|
@@ -383,6 +399,7 @@ def create(
|
|
|
383
399
|
rpc_channel,
|
|
384
400
|
mqtt_rpc_channel,
|
|
385
401
|
map_rpc_channel,
|
|
402
|
+
blob_rpc_channel,
|
|
386
403
|
add_dps_listener,
|
|
387
404
|
web_api,
|
|
388
405
|
device_cache,
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Trait for fetching obstacle photos from V1 vacuums."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from roborock.data import RoborockBase
|
|
6
|
+
from roborock.devices.traits.v1 import common
|
|
7
|
+
from roborock.exceptions import RoborockException
|
|
8
|
+
from roborock.protocols.v1_protocol import V1RpcChannel
|
|
9
|
+
from roborock.roborock_typing import RoborockCommand
|
|
10
|
+
|
|
11
|
+
_PHOTO_TYPE_SMALL = 1
|
|
12
|
+
_PHOTO_DATA_BLOCK_TYPE = 3
|
|
13
|
+
_MAP_OBJECT_PHOTO_ENABLED_BIT = 10
|
|
14
|
+
_TYPE_SIZE = 2
|
|
15
|
+
_HEADER_SIZE_SIZE = 2
|
|
16
|
+
_PAYLOAD_SIZE_SIZE = 4
|
|
17
|
+
_MIN_BLOCK_HEADER_SIZE = _TYPE_SIZE + _HEADER_SIZE_SIZE + _PAYLOAD_SIZE_SIZE
|
|
18
|
+
_IMAGE_HEADERS = (b"\x89PNG\r\n\x1a\n", b"\xff\xd8\xff")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class ObstaclePhoto(RoborockBase):
|
|
23
|
+
"""Obstacle photo content."""
|
|
24
|
+
|
|
25
|
+
photo_id: str
|
|
26
|
+
image_content: bytes
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ObstaclePhotoConverter(common.V1TraitDataConverter):
|
|
30
|
+
"""Convert a decrypted get_photo payload to an obstacle photo."""
|
|
31
|
+
|
|
32
|
+
def convert(self, response: common.V1ResponseData) -> ObstaclePhoto:
|
|
33
|
+
"""Parse the response from the device into an obstacle photo."""
|
|
34
|
+
if not isinstance(response, bytes):
|
|
35
|
+
raise ValueError(f"Unexpected ObstaclePhotoTrait response format: {type(response)}")
|
|
36
|
+
return ObstaclePhoto(photo_id="", image_content=parse_photo_data(response))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def parse_photo_data(response: bytes) -> bytes:
|
|
40
|
+
"""Parse the get_photo response payload and return image bytes.
|
|
41
|
+
|
|
42
|
+
Roborock's app parses get_photo as a sequence of little-endian typed blocks.
|
|
43
|
+
Block type 3 contains the image bytes.
|
|
44
|
+
"""
|
|
45
|
+
offset = 0
|
|
46
|
+
while offset + _MIN_BLOCK_HEADER_SIZE <= len(response):
|
|
47
|
+
block_type = int.from_bytes(response[offset : offset + _TYPE_SIZE], "little")
|
|
48
|
+
header_size = int.from_bytes(
|
|
49
|
+
response[offset + _TYPE_SIZE : offset + _TYPE_SIZE + _HEADER_SIZE_SIZE],
|
|
50
|
+
"little",
|
|
51
|
+
)
|
|
52
|
+
payload_size = int.from_bytes(
|
|
53
|
+
response[offset + _TYPE_SIZE + _HEADER_SIZE_SIZE : offset + _MIN_BLOCK_HEADER_SIZE],
|
|
54
|
+
"little",
|
|
55
|
+
)
|
|
56
|
+
next_offset = offset + header_size + payload_size
|
|
57
|
+
if header_size < _MIN_BLOCK_HEADER_SIZE or next_offset > len(response):
|
|
58
|
+
raise RoborockException("Invalid obstacle photo payload")
|
|
59
|
+
|
|
60
|
+
if block_type == _PHOTO_DATA_BLOCK_TYPE:
|
|
61
|
+
image_content = response[offset + header_size : next_offset]
|
|
62
|
+
if not image_content.startswith(_IMAGE_HEADERS):
|
|
63
|
+
raise RoborockException("Obstacle photo payload is not a supported image")
|
|
64
|
+
return image_content
|
|
65
|
+
|
|
66
|
+
offset = next_offset
|
|
67
|
+
|
|
68
|
+
raise RoborockException("Obstacle photo payload does not contain photo data")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ObstaclePhotoTrait(RoborockBase, common.V1TraitMixin):
|
|
72
|
+
"""Trait for fetching obstacle photos."""
|
|
73
|
+
|
|
74
|
+
command = RoborockCommand.GET_PHOTO
|
|
75
|
+
converter = ObstaclePhotoConverter()
|
|
76
|
+
blob_rpc_channel = True
|
|
77
|
+
requires_feature = "is_ai_recognition_obstacle_supported"
|
|
78
|
+
|
|
79
|
+
def __init__(self, standard_rpc_channel: V1RpcChannel) -> None:
|
|
80
|
+
"""Initialize the obstacle photo trait."""
|
|
81
|
+
super().__init__()
|
|
82
|
+
self._standard_rpc_channel = standard_rpc_channel
|
|
83
|
+
|
|
84
|
+
async def get_enabled(self) -> bool:
|
|
85
|
+
"""Return whether map object photo capture is enabled on the vacuum."""
|
|
86
|
+
response = await self._standard_rpc_channel.send_command(RoborockCommand.GET_CAMERA_STATUS)
|
|
87
|
+
if not isinstance(response, list) or not response or not isinstance(response[0], int):
|
|
88
|
+
raise RoborockException("get_camera_status response did not contain camera status")
|
|
89
|
+
return bool((response[0] >> _MAP_OBJECT_PHOTO_ENABLED_BIT) & 1)
|
|
90
|
+
|
|
91
|
+
async def get_photo(self, photo_id: str) -> ObstaclePhoto:
|
|
92
|
+
"""Fetch the small obstacle photo for a map photo ID.
|
|
93
|
+
|
|
94
|
+
Photo IDs are available as ``Obstacle.details.photo_name`` in
|
|
95
|
+
``ParsedMapData.map_data.obstacles_with_photo`` and
|
|
96
|
+
``ParsedMapData.map_data.ignored_obstacles_with_photo``.
|
|
97
|
+
"""
|
|
98
|
+
response = await self.rpc_channel.send_command(
|
|
99
|
+
self.command,
|
|
100
|
+
params={"img_id": photo_id, "type": _PHOTO_TYPE_SMALL},
|
|
101
|
+
)
|
|
102
|
+
photo = self.converter.convert(response)
|
|
103
|
+
photo.photo_id = photo_id
|
|
104
|
+
return photo
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import base64
|
|
6
|
+
import gzip
|
|
6
7
|
import json
|
|
7
8
|
import logging
|
|
8
9
|
import secrets
|
|
9
10
|
import struct
|
|
11
|
+
import zlib
|
|
10
12
|
from collections.abc import Callable
|
|
11
13
|
from dataclasses import dataclass, field
|
|
12
14
|
from enum import StrEnum
|
|
@@ -24,6 +26,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
24
26
|
__all__ = [
|
|
25
27
|
"SecurityData",
|
|
26
28
|
"create_security_data",
|
|
29
|
+
"create_blob_response_decoder",
|
|
27
30
|
"decode_data_protocol_message",
|
|
28
31
|
"decode_rpc_response",
|
|
29
32
|
"V1RpcChannel",
|
|
@@ -261,6 +264,48 @@ class MapResponse:
|
|
|
261
264
|
"""The map data, decrypted and decompressed."""
|
|
262
265
|
|
|
263
266
|
|
|
267
|
+
@dataclass
|
|
268
|
+
class BlobResponse:
|
|
269
|
+
"""Data structure for V1 blob responses."""
|
|
270
|
+
|
|
271
|
+
request_id: int
|
|
272
|
+
"""The request ID of the blob response."""
|
|
273
|
+
|
|
274
|
+
data: bytes
|
|
275
|
+
"""The blob data, decompressed."""
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def create_blob_response_decoder() -> Callable[[RoborockMessage], BlobResponse | None]:
|
|
279
|
+
"""Create a decoder for V1 blob response messages.
|
|
280
|
+
|
|
281
|
+
Obstacle photos are acknowledged through the normal RPC response with
|
|
282
|
+
``["ok"]`` and delivered later as a protocol-301 blob frame. The frame starts
|
|
283
|
+
with ``ROBOROCK``, stores the RPC request id at bytes 8-11, the header size
|
|
284
|
+
at bytes 16-17, and the gzip payload length at bytes 20-23.
|
|
285
|
+
"""
|
|
286
|
+
|
|
287
|
+
def _decode_blob_response(message: RoborockMessage) -> BlobResponse | None:
|
|
288
|
+
"""Decode a V1 blob response message."""
|
|
289
|
+
if message.protocol != RoborockMessageProtocol.MAP_RESPONSE:
|
|
290
|
+
return None
|
|
291
|
+
payload = message.payload
|
|
292
|
+
if not payload or not payload.startswith(b"ROBOROCK") or len(payload) < 24:
|
|
293
|
+
return None
|
|
294
|
+
request_id = int.from_bytes(payload[8:12], "little")
|
|
295
|
+
header_size = int.from_bytes(payload[16:18], "little")
|
|
296
|
+
payload_size = int.from_bytes(payload[20:24], "little")
|
|
297
|
+
end_offset = header_size + payload_size
|
|
298
|
+
if header_size < 24 or end_offset > len(payload):
|
|
299
|
+
raise RoborockException("Invalid V1 blob response format")
|
|
300
|
+
try:
|
|
301
|
+
data = Utils.decompress(payload[header_size:end_offset])
|
|
302
|
+
except (gzip.BadGzipFile, EOFError, zlib.error) as err:
|
|
303
|
+
raise RoborockException("Failed to decode blob message payload") from err
|
|
304
|
+
return BlobResponse(request_id=request_id, data=data)
|
|
305
|
+
|
|
306
|
+
return _decode_blob_response
|
|
307
|
+
|
|
308
|
+
|
|
264
309
|
def create_map_response_decoder(security_data: SecurityData) -> Callable[[RoborockMessage], MapResponse | None]:
|
|
265
310
|
"""Create a decoder for V1 map response messages."""
|
|
266
311
|
|
|
@@ -117,6 +117,7 @@ class RoborockCommand(str, Enum):
|
|
|
117
117
|
GET_NETWORK_INFO = "get_network_info"
|
|
118
118
|
GET_OFFLINE_MAP_STATUS = "get_offline_map_status"
|
|
119
119
|
GET_PERSIST = "get_persist_map"
|
|
120
|
+
GET_PHOTO = "get_photo"
|
|
120
121
|
GET_PROP = "get_prop"
|
|
121
122
|
GET_RANDOM_PKEY = "get_random_pkey"
|
|
122
123
|
GET_RECOVER_MAP = "get_recover_map"
|
|
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.36.0 → python_roborock-5.37.1}/roborock/data/b01_q10/b01_q10_code_mappings.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/data/b01_q10/b01_q10_containers.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.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
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/__init__.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/button_light.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/child_lock.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/clean_history.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/command.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/consumable.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/do_not_disturb.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q10/dust_collection.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.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.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q7/__init__.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/b01/q7/clean_summary.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.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.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/clean_summary.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/device_features.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/do_not_disturb.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/dust_collection_mode.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.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.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/network_info.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/smart_wash_params.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/traits/v1/wash_towel_mode.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.1}/roborock/devices/transport/local_channel.py
RENAMED
|
File without changes
|
{python_roborock-5.36.0 → python_roborock-5.37.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|