|
1 | 1 | """Traits for Q10 B01 devices.""" |
2 | 2 |
|
| 3 | +from base64 import b64encode |
| 4 | +from struct import error as StructError |
| 5 | +from struct import pack |
| 6 | + |
3 | 7 | from roborock.data.b01_q10.b01_q10_code_mappings import ( |
4 | 8 | B01_Q10_DP, |
5 | 9 | YXCleanType, |
|
8 | 12 | ) |
9 | 13 |
|
10 | 14 | from .command import CommandTrait |
| 15 | +from .coordinates import roborock_to_vector_coordinate |
| 16 | + |
| 17 | +_ZONE_NAME_FIELD_LENGTH = 19 |
| 18 | + |
| 19 | + |
| 20 | +def _encode_zone(x1: int, y1: int, x2: int, y2: int, clean_count: int) -> str: |
| 21 | + """Encode one rectangular Q10 cleaning zone.""" |
| 22 | + if not 1 <= clean_count <= 3: |
| 23 | + raise ValueError("clean_count must be between 1 and 3") |
| 24 | + |
| 25 | + min_x, max_x = sorted((x1, x2)) |
| 26 | + min_y, max_y = sorted((y1, y2)) |
| 27 | + points = ( |
| 28 | + (min_x, min_y), |
| 29 | + (max_x, min_y), |
| 30 | + (max_x, max_y), |
| 31 | + (min_x, max_y), |
| 32 | + ) |
| 33 | + payload = bytearray((1, clean_count, 1, len(points))) |
| 34 | + try: |
| 35 | + for point_x, point_y in points: |
| 36 | + payload.extend( |
| 37 | + pack( |
| 38 | + ">hh", |
| 39 | + roborock_to_vector_coordinate(point_x), |
| 40 | + roborock_to_vector_coordinate(point_y), |
| 41 | + ) |
| 42 | + ) |
| 43 | + except StructError as err: |
| 44 | + raise ValueError("zone coordinates are outside the supported range") from err |
| 45 | + |
| 46 | + # The app protocol reserves a fixed 19-byte UTF-8 name field per zone. |
| 47 | + payload.append(0) |
| 48 | + payload.extend(bytes(_ZONE_NAME_FIELD_LENGTH)) |
| 49 | + return b64encode(payload).decode() |
11 | 50 |
|
12 | 51 |
|
13 | 52 | class VacuumTrait: |
@@ -56,6 +95,25 @@ async def clean_segments(self, segment_ids: list[int]) -> None: |
56 | 95 | params={"cmd": YXDeviceCleanTask.ELECTORAL.code, "clean_paramters": segment_ids}, |
57 | 96 | ) |
58 | 97 |
|
| 98 | + async def clean_zone( |
| 99 | + self, |
| 100 | + x1: int, |
| 101 | + y1: int, |
| 102 | + x2: int, |
| 103 | + y2: int, |
| 104 | + *, |
| 105 | + clean_count: int = 1, |
| 106 | + ) -> None: |
| 107 | + """Clean one rectangular zone in the common Roborock coordinate space.""" |
| 108 | + await self._command.send( |
| 109 | + command=B01_Q10_DP.START_CLEAN, |
| 110 | + params={ |
| 111 | + "cmd": YXDeviceCleanTask.DIVIDE_AREAS.code, |
| 112 | + # "clean_paramters" is the spelling required by the firmware. |
| 113 | + "clean_paramters": _encode_zone(x1, y1, x2, y2, clean_count), |
| 114 | + }, |
| 115 | + ) |
| 116 | + |
59 | 117 | async def spot_clean(self) -> None: |
60 | 118 | """Start a spot / part clean around the robot's current position. |
61 | 119 |
|
|
0 commit comments