Skip to content

Commit 8838626

Browse files
Merge branch 'main' into q10-map-layers
2 parents f9f085c + 862aab5 commit 8838626

34 files changed

Lines changed: 2108 additions & 227 deletions

CHANGELOG.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,153 @@
22

33
<!-- version list -->
44

5+
## v5.28.0 (2026-07-08)
6+
7+
### Features
8+
9+
- Add better dock feature control
10+
([#867](https://github.com/Python-roborock/python-roborock/pull/867),
11+
[`73ba2bf`](https://github.com/Python-roborock/python-roborock/commit/73ba2bf152d1c43b4d46d1e7f397043dd195d046))
12+
13+
14+
## v5.27.0 (2026-07-07)
15+
16+
### Documentation
17+
18+
- Add comment explaining dss field in v1_simulator
19+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
20+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
21+
22+
### Features
23+
24+
- Add roborock.testing module for stateful integration testing
25+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
26+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
27+
28+
- Expose dock_type configuration in V1VacuumSimulator
29+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
30+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
31+
32+
- Expose in_cleaning, in_returning, charge_status, and dss on V1VacuumSimulator
33+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
34+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
35+
36+
- Honor publish_side_effect in simulator and raise NotImplementedError in base _handle_publish
37+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
38+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
39+
40+
### Refactoring
41+
42+
- Decouple default product id from device duid and share DEFAULT_PRODUCT_ID constant
43+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
44+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
45+
46+
- Delete redundant properties from V1VacuumSimulator and update trigger_push_update
47+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
48+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
49+
50+
- Export and define all simulator state default constants at package level
51+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
52+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
53+
54+
- Expose raw status and app_init dataclasses on simulator for direct test manipulation
55+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
56+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
57+
58+
- Inherit FakeChannel from Channel Protocol
59+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
60+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
61+
62+
- Rename self.servers to self.simulated_devices and restore hardcoded qiCNieZa in mock_data
63+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
64+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
65+
66+
- Replace FakeUserState with library UserData dataclass on FakeRoborockCloud
67+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
68+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
69+
70+
- Type simulator consumables state using Consumable dataclass
71+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
72+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
73+
74+
- Type simulator dnd, clean summary and record using real dataclasses
75+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
76+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
77+
78+
- Type simulator status, network info and init status using real dataclasses
79+
([#860](https://github.com/Python-roborock/python-roborock/pull/860),
80+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
81+
82+
- Update _handle_app_get_init_status to serialize self.app_init and dynamically resolve status_info
83+
fields from self.status ([#860](https://github.com/Python-roborock/python-roborock/pull/860),
84+
[`c963ad7`](https://github.com/Python-roborock/python-roborock/commit/c963ad79b7d7206dd1b97cd7f377305a67f90364))
85+
86+
87+
## v5.26.0 (2026-07-06)
88+
89+
### Documentation
90+
91+
- Add 569 back to the docstring's Q7-divergence list
92+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
93+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
94+
95+
- Mark fault 12 hw-confirmed (fired at 14% battery, corpus recount)
96+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
97+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
98+
99+
- Mark fault 2 hw-confirmed (bumper held during commanded motion)
100+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
101+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
102+
103+
- Mark fault 407 hw-confirmed (deterministically reproduced: due schedule ignored mid-clean)
104+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
105+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
106+
107+
- Mark fault 46 hw-confirmed (docked with dust bag removed)
108+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
109+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
110+
111+
- Mark fault 5 hw-confirmed (physically ground-truthed brush jams)
112+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
113+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
114+
115+
- Note fault 501 fires per completed task
116+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
117+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
118+
119+
- Note the error state can carry no fault code (observed live)
120+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
121+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
122+
123+
- Refer to the ss07 fault spec, not the app (review feedback)
124+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
125+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
126+
127+
### Features
128+
129+
- Decode Q10 (B01/ss07) dpFault codes into a YXFault map
130+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
131+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
132+
133+
- Decode Q10 (B01/ss07) dpFault codes with a YXFault enum
134+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
135+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
136+
137+
- Re-add fault 569 as too_frequent_emptying, hw-confirmed (dock refuses a too-soon empty)
138+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
139+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
140+
141+
### Refactoring
142+
143+
- Decouple B01 (Q7/Q10) protocol layer from transport layer
144+
([#859](https://github.com/Python-roborock/python-roborock/pull/859),
145+
[`b00d60b`](https://github.com/Python-roborock/python-roborock/commit/b00d60b9f70d6a5da9089e470db60b04437c0b14))
146+
147+
- Type Q10Status.fault as YXFault, mirroring the Q7 fault field
148+
([#863](https://github.com/Python-roborock/python-roborock/pull/863),
149+
[`32f9747`](https://github.com/Python-roborock/python-roborock/commit/32f9747b368c4d58a78691a0106a8c9676a07e2c))
150+
151+
5152
## v5.25.0 (2026-07-03)
6153

7154
### Chores

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "python-roborock"
3-
version = "5.25.0"
3+
version = "5.28.0"
44
description = "A package to control Roborock vacuums."
55
authors = [{ name = "humbertogontijo", email = "humbertogontijo@users.noreply.github.com" }, {name="Lash-L"}, {name="allenporter"}]
66
requires-python = ">=3.11, <4"

roborock/data/b01_q10/b01_q10_code_mappings.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,70 @@ class YXCarpetCleanType(RoborockModeEnum):
289289
CROSS = "cross", 3
290290

291291

292+
class YXFault(RoborockModeEnum):
293+
"""Q10 (B01/ss07) ``dpFault`` (90) codes, from the ss07 fault spec.
294+
295+
``dpFault`` is *overloaded*: several values are lifecycle/status rather than
296+
errors (e.g. 400 = scheduled clean starting, 501 = returning to dock,
297+
502 = recharge). A non-zero fault is not necessarily a blocking error.
298+
The converse also holds: the device can sit in its error state (``dpStatus``
299+
12) with ``dpFault`` still 0 (observed live with the dust-bin module
300+
removed), so a ``None``/``NONE`` fault does not imply the absence of an
301+
error condition.
302+
303+
These labels differ from the Q7 ``B01Fault`` for several shared numbers
304+
(500, 501, 503, 569, 570) -- so this is a Q10-specific map, not a reuse of
305+
``B01Fault``. Codes marked "hw-confirmed" were observed firing live on a
306+
physical ss07 in a context matching the label; the rest are from the spec
307+
only, not yet observed live (single device).
308+
"""
309+
310+
UNKNOWN = "unknown", -1
311+
NONE = "none", 0
312+
LIDAR_BLOCKED = "lidar_blocked", 1
313+
BUMPER_STUCK = "bumper_stuck", 2 # hw-confirmed (bumper held in during commanded motion)
314+
ROBOT_SUSPENDED = "robot_suspended", 3 # hw-confirmed (wheels lifted off the floor mid-task)
315+
CLIFF_SENSOR_ERROR = "cliff_sensor_error", 4 # hw-confirmed (one side lifted mid-task)
316+
MAIN_BRUSH_STUCK = "main_brush_stuck", 5 # hw-confirmed (two physical brush jams)
317+
MAIN_WHEELS_STUCK = "main_wheels_stuck", 7
318+
ROBOT_TRAPPED = "robot_trapped", 8 # hw-confirmed
319+
CHECK_DUSTBIN_FILTER = "check_dustbin_filter", 9
320+
LOW_BATTERY = "low_battery", 12 # hw-confirmed (fired at 14% mid clean)
321+
TEMPERATURE_THRESHOLD = "temperature_threshold", 14
322+
ROBOT_TILTED = "robot_tilted", 16
323+
LIDAR_COVER_OBSTRUCTED = "lidar_cover_obstructed", 21
324+
NO_GO_ZONE_DETECTED = "no_go_zone_detected", 24
325+
MOPPING_MODULE_STUCK = "mopping_module_stuck", 27
326+
CARPET_AVOIDANCE = "carpet_avoidance", 28
327+
CANNOT_CROSS_CARPET = "cannot_cross_carpet", 29
328+
INSTALL_DUST_BAG = "install_dust_bag", 46 # hw-confirmed (docked with bag removed; fires at dock contact)
329+
MOP_MOUNT_FELL_OFF = "mop_mount_fell_off", 54
330+
LIDAR_DIRTY = "lidar_dirty", 58
331+
FILTER_SERVICE_LIFE = "filter_service_life", 301
332+
MAIN_BRUSH_SERVICE_LIFE = "main_brush_service_life", 302
333+
SIDE_BRUSH_SERVICE_LIFE = "side_brush_service_life", 303
334+
SENSOR_NEEDS_CLEANING = "sensor_needs_cleaning", 304 # hw-confirmed (fired during auto-empty)
335+
DUST_BAG_FULL = "dust_bag_full", 310 # inferred from auto-empty context; not hw-confirmed
336+
STARTING_SCHEDULED_CLEAN = "starting_scheduled_clean", 400 # hw-confirmed x3; lifecycle, not an error
337+
# hw-confirmed (a due scheduled clean fired mid-clean and was ignored); lifecycle, not an error
338+
CLEANING_IN_PROGRESS = "cleaning_in_progress", 407
339+
EMPTY_DUSTBIN = "empty_dustbin", 500 # ss07 != Q7 B01Fault (lidar_blocked); spec-only
340+
# hw-confirmed, fires per completed task; ss07 != Q7 (robot_suspended)
341+
CLEANING_COMPLETED_RETURNING = "cleaning_completed_returning", 501
342+
LOW_BATTERY_RESUME = "low_battery_resume", 502 # hw-confirmed; lifecycle
343+
DOCKING_ERROR = "docking_error", 503 # hw-confirmed; ss07 != Q7 (dustbin_not_installed)
344+
POSITIONING_FAILED = "positioning_failed", 556 # hw-confirmed; relocalization
345+
# hw-confirmed (3rd auto-empty in ~15 min: dock refuses to run the cycle); ss07 != Q7 (main_wheels_entangled)
346+
TOO_FREQUENT_EMPTYING = "too_frequent_emptying", 569
347+
CANNOT_REACH_TARGET = "cannot_reach_target", 570 # hw-confirmed; ss07 != Q7 (main_brush_entangled)
348+
OFFLINE_WARNING_ASLEEP = "offline_warning_asleep", 588
349+
OFFLINE_WARNING_LOW_BATTERY = "offline_warning_low_battery", 589
350+
DND_AUTO_TOPUP_DISABLED = "dnd_auto_topup_disabled", 591
351+
CLEAN_CARPET_ULTRASONIC_SENSORS = "clean_carpet_ultrasonic_sensors", 707
352+
ROBOT_ERROR_RESET = "robot_error_reset", 1002
353+
VOICE_PACK_UPDATE_AVAILABLE = "voice_pack_update_available", 3001
354+
355+
292356
class RemoteCommand(IntEnum):
293357
FORWARD = 0
294358
LEFT = 2

roborock/data/b01_q10/b01_q10_containers.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
YXDeviceDustCollectionFrequency,
2424
YXDeviceState,
2525
YXFanLevel,
26+
YXFault,
2627
YXStartMethod,
2728
YXWaterLevel,
2829
)
@@ -165,7 +166,7 @@ class Q10Status(RoborockBase):
165166
clean_task_type: YXDeviceCleanTask | None = field(default=None, metadata={"dps": B01_Q10_DP.CLEAN_TASK_TYPE})
166167
back_type: YXBackType | None = field(default=None, metadata={"dps": B01_Q10_DP.BACK_TYPE})
167168
cleaning_progress: int | None = field(default=None, metadata={"dps": B01_Q10_DP.CLEAN_PROGRESS})
168-
fault: int | None = field(default=None, metadata={"dps": B01_Q10_DP.FAULT})
169+
fault: YXFault | None = field(default=None, metadata={"dps": B01_Q10_DP.FAULT})
169170

170171
# Raw base64 map-overlay blobs (decoded by roborock.map.b01_q10_overlays).
171172
restricted_zone_up: str | None = field(default=None, metadata={"dps": B01_Q10_DP.RESTRICTED_ZONE_UP})
@@ -208,6 +209,11 @@ class Q10Status(RoborockBase):
208209
filter_life: int | None = field(default=None, metadata={"dps": B01_Q10_DP.FILTER_LIFE})
209210
sensor_life: int | None = field(default=None, metadata={"dps": B01_Q10_DP.SENSOR_LIFE})
210211

212+
@property
213+
def fault_name(self) -> str | None:
214+
"""Returns the name of the current fault."""
215+
return self.fault.value if self.fault is not None else None
216+
211217

212218
@dataclass
213219
class SoundVolume(RoborockBase):

roborock/data/v1/v1_code_mappings.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -650,22 +650,31 @@ class RoborockDockErrorCode(RoborockEnum):
650650

651651
class RoborockDockTypeCode(RoborockEnum):
652652
unknown = -9999
653-
no_dock = 0
654-
auto_empty_dock = 1
655-
empty_wash_fill_dock = 3
656-
auto_empty_dock_pure = 5
657-
s7_max_ultra_dock = 6
658-
s8_dock = 7
659-
p10_dock = 8
660-
p10_pro_dock = 9
661-
s8_maxv_ultra_dock = 10
662-
qrevo_master_dock = 14
663-
qrevo_s_dock = 15
664-
saros_r10_dock = 16
665-
qrevo_curv_dock = 17
666-
saros_10_dock = 18
667-
qrevo_s5v_dock = 22
668-
saros_20_dock = 27
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
669678

670679

671680
class RoborockDockDustCollectionModeCode(RoborockEnum):

roborock/data/v1/v1_containers.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,12 @@ def current_map(self) -> int | None:
239239
return map_flag
240240
return None
241241

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+
242248
@property
243249
def clear_water_box_status(self) -> ClearWaterBoxStatus | None:
244250
if self.dss:
@@ -376,6 +382,12 @@ def current_map(self) -> int | None:
376382
return map_flag
377383
return None
378384

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+
379391
@property
380392
def clear_water_box_status(self) -> ClearWaterBoxStatus | None:
381393
if self.dss:

0 commit comments

Comments
 (0)