python-roborock 3.14.2__tar.gz → 3.15.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-3.14.2 → python_roborock-3.15.0}/PKG-INFO +1 -1
- {python_roborock-3.14.2 → python_roborock-3.15.0}/pyproject.toml +1 -1
- python_roborock-3.15.0/roborock/data/b01_q7/b01_q7_containers.py +130 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/containers.py +1 -1
- python_roborock-3.15.0/roborock/devices/b01_channel.py +77 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/b01/q7/__init__.py +5 -3
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/protocol.py +9 -6
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/protocols/b01_protocol.py +5 -5
- python_roborock-3.14.2/roborock/data/b01_q7/b01_q7_containers.py +0 -130
- python_roborock-3.14.2/roborock/devices/b01_channel.py +0 -27
- {python_roborock-3.14.2 → python_roborock-3.15.0}/.gitignore +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/LICENSE +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/README.md +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/api.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/broadcast_protocol.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/callbacks.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/cli.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/cloud_api.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/command_cache.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/const.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/b01_q10/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/b01_q7/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/code_mappings.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/dyad/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/dyad/dyad_code_mappings.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/dyad/dyad_containers.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/v1/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/v1/v1_clean_modes.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/v1/v1_code_mappings.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/v1/v1_containers.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/zeo/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/zeo/zeo_code_mappings.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/zeo/zeo_containers.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/device_features.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/README.md +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/a01_channel.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/cache.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/channel.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/device.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/device_manager.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/file_cache.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/local_channel.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/mqtt_channel.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/a01/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/b01/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/b01/q10/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/traits_mixin.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/child_lock.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/command.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/common.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/consumeable.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/device_features.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/flow_led_status.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/home.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/led_status.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/map_content.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/maps.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/network_info.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/rooms.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/routines.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/status.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/volume.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/v1_channel.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/exceptions.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/map/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/map/map_parser.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/mqtt/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/mqtt/health_manager.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/mqtt/roborock_session.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/mqtt/session.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/protocols/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/protocols/a01_protocol.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/protocols/v1_protocol.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/py.typed +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/roborock_future.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/roborock_message.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/roborock_typing.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/util.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/version_1_apis/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/version_1_apis/roborock_client_v1.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/version_1_apis/roborock_local_client_v1.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/version_a01_apis/__init__.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +0 -0
- {python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/web_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-roborock
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.15.0
|
|
4
4
|
Summary: A package to control Roborock vacuums.
|
|
5
5
|
Project-URL: Repository, https://github.com/humbertogontijo/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 = "3.
|
|
3
|
+
version = "3.15.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"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from ..containers import RoborockBase
|
|
4
|
+
from .b01_q7_code_mappings import B01Fault, SCWindMapping, WorkModeMapping, WorkStatusMapping
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class NetStatus(RoborockBase):
|
|
9
|
+
"""Represents the network status of the device."""
|
|
10
|
+
|
|
11
|
+
rssi: str
|
|
12
|
+
loss: int
|
|
13
|
+
ping: int
|
|
14
|
+
ip: str
|
|
15
|
+
mac: str
|
|
16
|
+
ssid: str
|
|
17
|
+
frequency: int
|
|
18
|
+
bssid: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class OrderTotal(RoborockBase):
|
|
23
|
+
"""Represents the order total information."""
|
|
24
|
+
|
|
25
|
+
total: int
|
|
26
|
+
enable: int
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class Privacy(RoborockBase):
|
|
31
|
+
"""Represents the privacy settings of the device."""
|
|
32
|
+
|
|
33
|
+
ai_recognize: int
|
|
34
|
+
dirt_recognize: int
|
|
35
|
+
pet_recognize: int
|
|
36
|
+
carpet_turbo: int
|
|
37
|
+
carpet_avoid: int
|
|
38
|
+
carpet_show: int
|
|
39
|
+
map_uploads: int
|
|
40
|
+
ai_agent: int
|
|
41
|
+
ai_avoidance: int
|
|
42
|
+
record_uploads: int
|
|
43
|
+
along_floor: int
|
|
44
|
+
auto_upgrade: int
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class PvCharging(RoborockBase):
|
|
49
|
+
"""Represents the photovoltaic charging status."""
|
|
50
|
+
|
|
51
|
+
status: int
|
|
52
|
+
begin_time: int
|
|
53
|
+
end_time: int
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class Recommend(RoborockBase):
|
|
58
|
+
"""Represents cleaning recommendations."""
|
|
59
|
+
|
|
60
|
+
sill: int
|
|
61
|
+
wall: int
|
|
62
|
+
room_id: list[int] = field(default_factory=list)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class B01Props(RoborockBase):
|
|
67
|
+
"""
|
|
68
|
+
Represents the complete properties and status for a Roborock B01 model.
|
|
69
|
+
This dataclass is generated based on the device's status JSON object.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
status: WorkStatusMapping | None = None
|
|
73
|
+
fault: B01Fault | None = None
|
|
74
|
+
wind: SCWindMapping | None = None
|
|
75
|
+
water: int | None = None
|
|
76
|
+
mode: int | None = None
|
|
77
|
+
quantity: int | None = None
|
|
78
|
+
alarm: int | None = None
|
|
79
|
+
volume: int | None = None
|
|
80
|
+
hypa: int | None = None
|
|
81
|
+
main_brush: int | None = None
|
|
82
|
+
side_brush: int | None = None
|
|
83
|
+
mop_life: int | None = None
|
|
84
|
+
main_sensor: int | None = None
|
|
85
|
+
net_status: NetStatus | None = None
|
|
86
|
+
repeat_state: int | None = None
|
|
87
|
+
tank_state: int | None = None
|
|
88
|
+
sweep_type: int | None = None
|
|
89
|
+
clean_path_preference: int | None = None
|
|
90
|
+
cloth_state: int | None = None
|
|
91
|
+
time_zone: int | None = None
|
|
92
|
+
time_zone_info: str | None = None
|
|
93
|
+
language: int | None = None
|
|
94
|
+
cleaning_time: int | None = None
|
|
95
|
+
real_clean_time: int | None = None
|
|
96
|
+
cleaning_area: int | None = None
|
|
97
|
+
custom_type: int | None = None
|
|
98
|
+
sound: int | None = None
|
|
99
|
+
work_mode: WorkModeMapping | None = None
|
|
100
|
+
station_act: int | None = None
|
|
101
|
+
charge_state: int | None = None
|
|
102
|
+
current_map_id: int | None = None
|
|
103
|
+
map_num: int | None = None
|
|
104
|
+
dust_action: int | None = None
|
|
105
|
+
quiet_is_open: int | None = None
|
|
106
|
+
quiet_begin_time: int | None = None
|
|
107
|
+
quiet_end_time: int | None = None
|
|
108
|
+
clean_finish: int | None = None
|
|
109
|
+
voice_type: int | None = None
|
|
110
|
+
voice_type_version: int | None = None
|
|
111
|
+
order_total: OrderTotal | None = None
|
|
112
|
+
build_map: int | None = None
|
|
113
|
+
privacy: Privacy | None = None
|
|
114
|
+
dust_auto_state: int | None = None
|
|
115
|
+
dust_frequency: int | None = None
|
|
116
|
+
child_lock: int | None = None
|
|
117
|
+
multi_floor: int | None = None
|
|
118
|
+
map_save: int | None = None
|
|
119
|
+
light_mode: int | None = None
|
|
120
|
+
green_laser: int | None = None
|
|
121
|
+
dust_bag_used: int | None = None
|
|
122
|
+
order_save_mode: int | None = None
|
|
123
|
+
manufacturer: str | None = None
|
|
124
|
+
back_to_wash: int | None = None
|
|
125
|
+
charge_station_type: int | None = None
|
|
126
|
+
pv_cut_charge: int | None = None
|
|
127
|
+
pv_charging: PvCharging | None = None
|
|
128
|
+
serial_number: str | None = None
|
|
129
|
+
recommend: Recommend | None = None
|
|
130
|
+
add_sweep_status: int | None = None
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Thin wrapper around the MQTT channel for Roborock B01 devices."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from roborock.exceptions import RoborockException
|
|
11
|
+
from roborock.protocols.b01_protocol import (
|
|
12
|
+
CommandType,
|
|
13
|
+
ParamsType,
|
|
14
|
+
decode_rpc_response,
|
|
15
|
+
encode_mqtt_payload,
|
|
16
|
+
)
|
|
17
|
+
from roborock.roborock_message import RoborockMessage
|
|
18
|
+
from roborock.util import get_next_int
|
|
19
|
+
|
|
20
|
+
from .mqtt_channel import MqttChannel
|
|
21
|
+
|
|
22
|
+
_LOGGER = logging.getLogger(__name__)
|
|
23
|
+
_TIMEOUT = 10.0
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def send_decoded_command(
|
|
27
|
+
mqtt_channel: MqttChannel,
|
|
28
|
+
dps: int,
|
|
29
|
+
command: CommandType,
|
|
30
|
+
params: ParamsType,
|
|
31
|
+
) -> dict[str, Any]:
|
|
32
|
+
"""Send a command on the MQTT channel and get a decoded response."""
|
|
33
|
+
_LOGGER.debug("Sending MQTT command: %s", params)
|
|
34
|
+
msg_id = str(get_next_int(100000000000, 999999999999))
|
|
35
|
+
roborock_message = encode_mqtt_payload(dps, command, params, msg_id)
|
|
36
|
+
future: asyncio.Future[dict[str, Any]] = asyncio.get_running_loop().create_future()
|
|
37
|
+
|
|
38
|
+
def find_response(response_message: RoborockMessage) -> None:
|
|
39
|
+
"""Handle incoming messages and resolve the future."""
|
|
40
|
+
try:
|
|
41
|
+
decoded_dps = decode_rpc_response(response_message)
|
|
42
|
+
except RoborockException as ex:
|
|
43
|
+
_LOGGER.info("Failed to decode b01 message: %s: %s", response_message, ex)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
for dps_value in decoded_dps.values():
|
|
47
|
+
# valid responses are JSON strings wrapped in the dps value
|
|
48
|
+
if not isinstance(dps_value, str):
|
|
49
|
+
_LOGGER.debug("Received unexpected response: %s", dps_value)
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
inner = json.loads(dps_value)
|
|
54
|
+
except (json.JSONDecodeError, TypeError):
|
|
55
|
+
_LOGGER.debug("Received unexpected response: %s", dps_value)
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
if isinstance(inner, dict) and inner.get("msgId") == msg_id:
|
|
59
|
+
_LOGGER.debug("Received query response: %s", inner)
|
|
60
|
+
data = inner.get("data")
|
|
61
|
+
if not future.done():
|
|
62
|
+
if isinstance(data, dict):
|
|
63
|
+
future.set_result(data)
|
|
64
|
+
else:
|
|
65
|
+
future.set_exception(RoborockException(f"Unexpected data type for response: {data}"))
|
|
66
|
+
|
|
67
|
+
unsub = await mqtt_channel.subscribe(find_response)
|
|
68
|
+
|
|
69
|
+
_LOGGER.debug("Sending MQTT message: %s", roborock_message)
|
|
70
|
+
try:
|
|
71
|
+
await mqtt_channel.publish(roborock_message)
|
|
72
|
+
try:
|
|
73
|
+
return await asyncio.wait_for(future, timeout=_TIMEOUT)
|
|
74
|
+
except TimeoutError as ex:
|
|
75
|
+
raise RoborockException(f"Command timed out after {_TIMEOUT}s") from ex
|
|
76
|
+
finally:
|
|
77
|
+
unsub()
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/b01/q7/__init__.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Traits for Q7 B01 devices.
|
|
2
2
|
Potentially other devices may fall into this category in the future."""
|
|
3
3
|
|
|
4
|
+
from roborock import B01Props
|
|
4
5
|
from roborock.devices.b01_channel import send_decoded_command
|
|
5
6
|
from roborock.devices.mqtt_channel import MqttChannel
|
|
6
7
|
from roborock.devices.traits import Trait
|
|
@@ -13,17 +14,18 @@ __all__ = [
|
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class Q7PropertiesApi(Trait):
|
|
16
|
-
"""API for interacting with
|
|
17
|
+
"""API for interacting with B01 devices."""
|
|
17
18
|
|
|
18
19
|
def __init__(self, channel: MqttChannel) -> None:
|
|
19
20
|
"""Initialize the B01Props API."""
|
|
20
21
|
self._channel = channel
|
|
21
22
|
|
|
22
|
-
async def query_values(self, props: list[RoborockB01Props]) -> None:
|
|
23
|
+
async def query_values(self, props: list[RoborockB01Props]) -> B01Props | None:
|
|
23
24
|
"""Query the device for the values of the given Q7 properties."""
|
|
24
|
-
await send_decoded_command(
|
|
25
|
+
result = await send_decoded_command(
|
|
25
26
|
self._channel, dps=10000, command=RoborockB01Q7Methods.GET_PROP, params={"property": props}
|
|
26
27
|
)
|
|
28
|
+
return B01Props.from_dict(result)
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
def create(channel: MqttChannel) -> Q7PropertiesApi:
|
|
@@ -276,12 +276,11 @@ class EncryptionAdapter(Construct):
|
|
|
276
276
|
if context.version == b"A01":
|
|
277
277
|
iv = md5hex(format(context.random, "08x") + A01_HASH)[8:24]
|
|
278
278
|
decipher = AES.new(bytes(context.search("local_key"), "utf-8"), AES.MODE_CBC, bytes(iv, "utf-8"))
|
|
279
|
-
|
|
280
|
-
return f
|
|
279
|
+
return decipher.encrypt(pad(obj, AES.block_size))
|
|
281
280
|
elif context.version == b"B01":
|
|
282
281
|
iv = md5hex(f"{context.random:08x}" + B01_HASH)[9:25]
|
|
283
282
|
decipher = AES.new(bytes(context.search("local_key"), "utf-8"), AES.MODE_CBC, bytes(iv, "utf-8"))
|
|
284
|
-
return decipher.encrypt(obj)
|
|
283
|
+
return decipher.encrypt(pad(obj, AES.block_size))
|
|
285
284
|
elif context.version == b"L01":
|
|
286
285
|
return Utils.encrypt_gcm_l01(
|
|
287
286
|
plaintext=obj,
|
|
@@ -301,12 +300,11 @@ class EncryptionAdapter(Construct):
|
|
|
301
300
|
if context.version == b"A01":
|
|
302
301
|
iv = md5hex(format(context.random, "08x") + A01_HASH)[8:24]
|
|
303
302
|
decipher = AES.new(bytes(context.search("local_key"), "utf-8"), AES.MODE_CBC, bytes(iv, "utf-8"))
|
|
304
|
-
|
|
305
|
-
return f
|
|
303
|
+
return unpad(decipher.decrypt(obj), AES.block_size)
|
|
306
304
|
elif context.version == b"B01":
|
|
307
305
|
iv = md5hex(f"{context.random:08x}" + B01_HASH)[9:25]
|
|
308
306
|
decipher = AES.new(bytes(context.search("local_key"), "utf-8"), AES.MODE_CBC, bytes(iv, "utf-8"))
|
|
309
|
-
return decipher.decrypt(obj)
|
|
307
|
+
return unpad(decipher.decrypt(obj), AES.block_size)
|
|
310
308
|
elif context.version == b"L01":
|
|
311
309
|
return Utils.decrypt_gcm_l01(
|
|
312
310
|
payload=obj,
|
|
@@ -350,6 +348,11 @@ class PrefixedStruct(Struct):
|
|
|
350
348
|
# Read remaining data to find a valid header
|
|
351
349
|
data = stream.read()
|
|
352
350
|
|
|
351
|
+
if not data:
|
|
352
|
+
# EOF reached, let the parser fail naturally without logging
|
|
353
|
+
stream_seek(stream, current_pos, 0, path)
|
|
354
|
+
return super()._parse(stream, context, path)
|
|
355
|
+
|
|
353
356
|
start_index = -1
|
|
354
357
|
# Find the earliest occurrence of any valid version in a single pass
|
|
355
358
|
for i in range(len(data) - 2):
|
|
@@ -13,7 +13,6 @@ from roborock.roborock_message import (
|
|
|
13
13
|
RoborockMessage,
|
|
14
14
|
RoborockMessageProtocol,
|
|
15
15
|
)
|
|
16
|
-
from roborock.util import get_next_int
|
|
17
16
|
|
|
18
17
|
_LOGGER = logging.getLogger(__name__)
|
|
19
18
|
|
|
@@ -22,13 +21,13 @@ CommandType = RoborockB01Q7Methods | str
|
|
|
22
21
|
ParamsType = list | dict | int | None
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
def encode_mqtt_payload(dps: int, command: CommandType, params: ParamsType) -> RoborockMessage:
|
|
24
|
+
def encode_mqtt_payload(dps: int, command: CommandType, params: ParamsType, msg_id: str) -> RoborockMessage:
|
|
26
25
|
"""Encode payload for B01 commands over MQTT."""
|
|
27
26
|
dps_data = {
|
|
28
27
|
"dps": {
|
|
29
28
|
dps: {
|
|
30
29
|
"method": str(command),
|
|
31
|
-
"msgId":
|
|
30
|
+
"msgId": msg_id,
|
|
32
31
|
"params": params or [],
|
|
33
32
|
}
|
|
34
33
|
}
|
|
@@ -47,8 +46,9 @@ def decode_rpc_response(message: RoborockMessage) -> dict[int, Any]:
|
|
|
47
46
|
raise RoborockException("Invalid B01 message format: missing payload")
|
|
48
47
|
try:
|
|
49
48
|
unpadded = unpad(message.payload, AES.block_size)
|
|
50
|
-
except ValueError
|
|
51
|
-
|
|
49
|
+
except ValueError:
|
|
50
|
+
# It would be better to fail down the line.
|
|
51
|
+
unpadded = message.payload
|
|
52
52
|
|
|
53
53
|
try:
|
|
54
54
|
payload = json.loads(unpadded.decode())
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass, field
|
|
2
|
-
|
|
3
|
-
from ..containers import RoborockBase
|
|
4
|
-
from .b01_q7_code_mappings import B01Fault, SCWindMapping, WorkModeMapping, WorkStatusMapping
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@dataclass
|
|
8
|
-
class NetStatus(RoborockBase):
|
|
9
|
-
"""Represents the network status of the device."""
|
|
10
|
-
|
|
11
|
-
rssi: str
|
|
12
|
-
loss: int
|
|
13
|
-
ping: int
|
|
14
|
-
ip: str
|
|
15
|
-
mac: str
|
|
16
|
-
ssid: str
|
|
17
|
-
frequency: int
|
|
18
|
-
bssid: str
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@dataclass
|
|
22
|
-
class OrderTotal(RoborockBase):
|
|
23
|
-
"""Represents the order total information."""
|
|
24
|
-
|
|
25
|
-
total: int
|
|
26
|
-
enable: int
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@dataclass
|
|
30
|
-
class Privacy(RoborockBase):
|
|
31
|
-
"""Represents the privacy settings of the device."""
|
|
32
|
-
|
|
33
|
-
ai_recognize: int
|
|
34
|
-
dirt_recognize: int
|
|
35
|
-
pet_recognize: int
|
|
36
|
-
carpet_turbo: int
|
|
37
|
-
carpet_avoid: int
|
|
38
|
-
carpet_show: int
|
|
39
|
-
map_uploads: int
|
|
40
|
-
ai_agent: int
|
|
41
|
-
ai_avoidance: int
|
|
42
|
-
record_uploads: int
|
|
43
|
-
along_floor: int
|
|
44
|
-
auto_upgrade: int
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
@dataclass
|
|
48
|
-
class PvCharging(RoborockBase):
|
|
49
|
-
"""Represents the photovoltaic charging status."""
|
|
50
|
-
|
|
51
|
-
status: int
|
|
52
|
-
begin_time: int
|
|
53
|
-
end_time: int
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@dataclass
|
|
57
|
-
class Recommend(RoborockBase):
|
|
58
|
-
"""Represents cleaning recommendations."""
|
|
59
|
-
|
|
60
|
-
sill: int
|
|
61
|
-
wall: int
|
|
62
|
-
room_id: list[int] = field(default_factory=list)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@dataclass
|
|
66
|
-
class B01Props(RoborockBase):
|
|
67
|
-
"""
|
|
68
|
-
Represents the complete properties and status for a Roborock B01 model.
|
|
69
|
-
This dataclass is generated based on the device's status JSON object.
|
|
70
|
-
"""
|
|
71
|
-
|
|
72
|
-
status: WorkStatusMapping
|
|
73
|
-
fault: B01Fault
|
|
74
|
-
wind: SCWindMapping
|
|
75
|
-
water: int
|
|
76
|
-
mode: int
|
|
77
|
-
quantity: int
|
|
78
|
-
alarm: int
|
|
79
|
-
volume: int
|
|
80
|
-
hypa: int
|
|
81
|
-
main_brush: int
|
|
82
|
-
side_brush: int
|
|
83
|
-
mop_life: int
|
|
84
|
-
main_sensor: int
|
|
85
|
-
net_status: NetStatus
|
|
86
|
-
repeat_state: int
|
|
87
|
-
tank_state: int
|
|
88
|
-
sweep_type: int
|
|
89
|
-
clean_path_preference: int
|
|
90
|
-
cloth_state: int
|
|
91
|
-
time_zone: int
|
|
92
|
-
time_zone_info: str
|
|
93
|
-
language: int
|
|
94
|
-
cleaning_time: int
|
|
95
|
-
real_clean_time: int
|
|
96
|
-
cleaning_area: int
|
|
97
|
-
custom_type: int
|
|
98
|
-
sound: int
|
|
99
|
-
work_mode: WorkModeMapping
|
|
100
|
-
station_act: int
|
|
101
|
-
charge_state: int
|
|
102
|
-
current_map_id: int
|
|
103
|
-
map_num: int
|
|
104
|
-
dust_action: int
|
|
105
|
-
quiet_is_open: int
|
|
106
|
-
quiet_begin_time: int
|
|
107
|
-
quiet_end_time: int
|
|
108
|
-
clean_finish: int
|
|
109
|
-
voice_type: int
|
|
110
|
-
voice_type_version: int
|
|
111
|
-
order_total: OrderTotal
|
|
112
|
-
build_map: int
|
|
113
|
-
privacy: Privacy
|
|
114
|
-
dust_auto_state: int
|
|
115
|
-
dust_frequency: int
|
|
116
|
-
child_lock: int
|
|
117
|
-
multi_floor: int
|
|
118
|
-
map_save: int
|
|
119
|
-
light_mode: int
|
|
120
|
-
green_laser: int
|
|
121
|
-
dust_bag_used: int
|
|
122
|
-
order_save_mode: int
|
|
123
|
-
manufacturer: str
|
|
124
|
-
back_to_wash: int
|
|
125
|
-
charge_station_type: int
|
|
126
|
-
pv_cut_charge: int
|
|
127
|
-
pv_charging: PvCharging
|
|
128
|
-
serial_number: str
|
|
129
|
-
recommend: Recommend
|
|
130
|
-
add_sweep_status: int
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"""Thin wrapper around the MQTT channel for Roborock B01 devices."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
|
|
7
|
-
from roborock.protocols.b01_protocol import (
|
|
8
|
-
CommandType,
|
|
9
|
-
ParamsType,
|
|
10
|
-
encode_mqtt_payload,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
from .mqtt_channel import MqttChannel
|
|
14
|
-
|
|
15
|
-
_LOGGER = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
async def send_decoded_command(
|
|
19
|
-
mqtt_channel: MqttChannel,
|
|
20
|
-
dps: int,
|
|
21
|
-
command: CommandType,
|
|
22
|
-
params: ParamsType,
|
|
23
|
-
) -> None:
|
|
24
|
-
"""Send a command on the MQTT channel and get a decoded response."""
|
|
25
|
-
_LOGGER.debug("Sending MQTT command: %s", params)
|
|
26
|
-
roborock_message = encode_mqtt_payload(dps, command, params)
|
|
27
|
-
await mqtt_channel.publish(roborock_message)
|
|
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-3.14.2 → python_roborock-3.15.0}/roborock/data/b01_q10/b01_q10_code_mappings.py
RENAMED
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/data/b01_q10/b01_q10_containers.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.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
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/b01/q10/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/clean_summary.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/device_features.py
RENAMED
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/do_not_disturb.py
RENAMED
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/dust_collection_mode.py
RENAMED
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.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-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/network_info.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/smart_wash_params.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/devices/traits/v1/wash_towel_mode.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
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/version_1_apis/roborock_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-3.14.2 → python_roborock-3.15.0}/roborock/version_a01_apis/roborock_client_a01.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|