roborock-cli 0.1.1__py3-none-any.whl

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.
Files changed (106) hide show
  1. roborock_cli/__init__.py +3 -0
  2. roborock_cli/__main__.py +76 -0
  3. roborock_cli/_vendor/VERSION +6 -0
  4. roborock_cli/_vendor/__init__.py +0 -0
  5. roborock_cli/_vendor/roborock/__init__.py +27 -0
  6. roborock_cli/_vendor/roborock/broadcast_protocol.py +114 -0
  7. roborock_cli/_vendor/roborock/callbacks.py +130 -0
  8. roborock_cli/_vendor/roborock/cli.py +1338 -0
  9. roborock_cli/_vendor/roborock/const.py +84 -0
  10. roborock_cli/_vendor/roborock/data/__init__.py +9 -0
  11. roborock_cli/_vendor/roborock/data/b01_q10/__init__.py +2 -0
  12. roborock_cli/_vendor/roborock/data/b01_q10/b01_q10_code_mappings.py +213 -0
  13. roborock_cli/_vendor/roborock/data/b01_q10/b01_q10_containers.py +102 -0
  14. roborock_cli/_vendor/roborock/data/b01_q7/__init__.py +2 -0
  15. roborock_cli/_vendor/roborock/data/b01_q7/b01_q7_code_mappings.py +303 -0
  16. roborock_cli/_vendor/roborock/data/b01_q7/b01_q7_containers.py +302 -0
  17. roborock_cli/_vendor/roborock/data/code_mappings.py +198 -0
  18. roborock_cli/_vendor/roborock/data/containers.py +530 -0
  19. roborock_cli/_vendor/roborock/data/dyad/__init__.py +2 -0
  20. roborock_cli/_vendor/roborock/data/dyad/dyad_code_mappings.py +102 -0
  21. roborock_cli/_vendor/roborock/data/dyad/dyad_containers.py +28 -0
  22. roborock_cli/_vendor/roborock/data/v1/__init__.py +3 -0
  23. roborock_cli/_vendor/roborock/data/v1/v1_clean_modes.py +192 -0
  24. roborock_cli/_vendor/roborock/data/v1/v1_code_mappings.py +644 -0
  25. roborock_cli/_vendor/roborock/data/v1/v1_containers.py +800 -0
  26. roborock_cli/_vendor/roborock/data/zeo/__init__.py +2 -0
  27. roborock_cli/_vendor/roborock/data/zeo/zeo_code_mappings.py +138 -0
  28. roborock_cli/_vendor/roborock/data/zeo/zeo_containers.py +0 -0
  29. roborock_cli/_vendor/roborock/device_features.py +668 -0
  30. roborock_cli/_vendor/roborock/devices/README.md +41 -0
  31. roborock_cli/_vendor/roborock/devices/__init__.py +11 -0
  32. roborock_cli/_vendor/roborock/devices/cache.py +143 -0
  33. roborock_cli/_vendor/roborock/devices/device.py +240 -0
  34. roborock_cli/_vendor/roborock/devices/device_manager.py +269 -0
  35. roborock_cli/_vendor/roborock/devices/file_cache.py +79 -0
  36. roborock_cli/_vendor/roborock/devices/rpc/__init__.py +14 -0
  37. roborock_cli/_vendor/roborock/devices/rpc/a01_channel.py +94 -0
  38. roborock_cli/_vendor/roborock/devices/rpc/b01_q10_channel.py +57 -0
  39. roborock_cli/_vendor/roborock/devices/rpc/b01_q7_channel.py +101 -0
  40. roborock_cli/_vendor/roborock/devices/rpc/v1_channel.py +457 -0
  41. roborock_cli/_vendor/roborock/devices/traits/__init__.py +28 -0
  42. roborock_cli/_vendor/roborock/devices/traits/a01/__init__.py +191 -0
  43. roborock_cli/_vendor/roborock/devices/traits/b01/__init__.py +12 -0
  44. roborock_cli/_vendor/roborock/devices/traits/b01/q10/__init__.py +76 -0
  45. roborock_cli/_vendor/roborock/devices/traits/b01/q10/command.py +32 -0
  46. roborock_cli/_vendor/roborock/devices/traits/b01/q10/common.py +115 -0
  47. roborock_cli/_vendor/roborock/devices/traits/b01/q10/status.py +32 -0
  48. roborock_cli/_vendor/roborock/devices/traits/b01/q10/vacuum.py +81 -0
  49. roborock_cli/_vendor/roborock/devices/traits/b01/q7/__init__.py +136 -0
  50. roborock_cli/_vendor/roborock/devices/traits/b01/q7/clean_summary.py +75 -0
  51. roborock_cli/_vendor/roborock/devices/traits/traits_mixin.py +64 -0
  52. roborock_cli/_vendor/roborock/devices/traits/v1/__init__.py +344 -0
  53. roborock_cli/_vendor/roborock/devices/traits/v1/child_lock.py +29 -0
  54. roborock_cli/_vendor/roborock/devices/traits/v1/clean_summary.py +83 -0
  55. roborock_cli/_vendor/roborock/devices/traits/v1/command.py +38 -0
  56. roborock_cli/_vendor/roborock/devices/traits/v1/common.py +172 -0
  57. roborock_cli/_vendor/roborock/devices/traits/v1/consumeable.py +48 -0
  58. roborock_cli/_vendor/roborock/devices/traits/v1/device_features.py +74 -0
  59. roborock_cli/_vendor/roborock/devices/traits/v1/do_not_disturb.py +41 -0
  60. roborock_cli/_vendor/roborock/devices/traits/v1/dust_collection_mode.py +13 -0
  61. roborock_cli/_vendor/roborock/devices/traits/v1/flow_led_status.py +29 -0
  62. roborock_cli/_vendor/roborock/devices/traits/v1/home.py +285 -0
  63. roborock_cli/_vendor/roborock/devices/traits/v1/led_status.py +43 -0
  64. roborock_cli/_vendor/roborock/devices/traits/v1/map_content.py +83 -0
  65. roborock_cli/_vendor/roborock/devices/traits/v1/maps.py +80 -0
  66. roborock_cli/_vendor/roborock/devices/traits/v1/network_info.py +55 -0
  67. roborock_cli/_vendor/roborock/devices/traits/v1/rooms.py +105 -0
  68. roborock_cli/_vendor/roborock/devices/traits/v1/routines.py +26 -0
  69. roborock_cli/_vendor/roborock/devices/traits/v1/smart_wash_params.py +13 -0
  70. roborock_cli/_vendor/roborock/devices/traits/v1/status.py +101 -0
  71. roborock_cli/_vendor/roborock/devices/traits/v1/valley_electricity_timer.py +44 -0
  72. roborock_cli/_vendor/roborock/devices/traits/v1/volume.py +27 -0
  73. roborock_cli/_vendor/roborock/devices/traits/v1/wash_towel_mode.py +13 -0
  74. roborock_cli/_vendor/roborock/devices/transport/__init__.py +8 -0
  75. roborock_cli/_vendor/roborock/devices/transport/channel.py +32 -0
  76. roborock_cli/_vendor/roborock/devices/transport/local_channel.py +295 -0
  77. roborock_cli/_vendor/roborock/devices/transport/mqtt_channel.py +118 -0
  78. roborock_cli/_vendor/roborock/diagnostics.py +166 -0
  79. roborock_cli/_vendor/roborock/exceptions.py +95 -0
  80. roborock_cli/_vendor/roborock/map/__init__.py +7 -0
  81. roborock_cli/_vendor/roborock/map/map_parser.py +123 -0
  82. roborock_cli/_vendor/roborock/mqtt/__init__.py +10 -0
  83. roborock_cli/_vendor/roborock/mqtt/health_manager.py +60 -0
  84. roborock_cli/_vendor/roborock/mqtt/roborock_session.py +463 -0
  85. roborock_cli/_vendor/roborock/mqtt/session.py +108 -0
  86. roborock_cli/_vendor/roborock/protocol.py +558 -0
  87. roborock_cli/_vendor/roborock/protocols/__init__.py +3 -0
  88. roborock_cli/_vendor/roborock/protocols/a01_protocol.py +74 -0
  89. roborock_cli/_vendor/roborock/protocols/b01_q10_protocol.py +87 -0
  90. roborock_cli/_vendor/roborock/protocols/b01_q7_protocol.py +81 -0
  91. roborock_cli/_vendor/roborock/protocols/v1_protocol.py +271 -0
  92. roborock_cli/_vendor/roborock/py.typed +0 -0
  93. roborock_cli/_vendor/roborock/roborock_message.py +246 -0
  94. roborock_cli/_vendor/roborock/roborock_typing.py +382 -0
  95. roborock_cli/_vendor/roborock/util.py +54 -0
  96. roborock_cli/_vendor/roborock/web_api.py +761 -0
  97. roborock_cli/cli.py +715 -0
  98. roborock_cli/connection.py +202 -0
  99. roborock_cli/helpers.py +71 -0
  100. roborock_cli/server.py +759 -0
  101. roborock_cli/setup_auth.py +92 -0
  102. roborock_cli-0.1.1.dist-info/METADATA +172 -0
  103. roborock_cli-0.1.1.dist-info/RECORD +106 -0
  104. roborock_cli-0.1.1.dist-info/WHEEL +4 -0
  105. roborock_cli-0.1.1.dist-info/entry_points.txt +2 -0
  106. roborock_cli-0.1.1.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,800 @@
1
+ import datetime
2
+ import logging
3
+ from dataclasses import dataclass, field
4
+ from enum import StrEnum
5
+ from typing import Any
6
+
7
+ from roborock_cli._vendor.roborock.const import (
8
+ CLEANING_BRUSH_REPLACE_TIME,
9
+ DUST_COLLECTION_REPLACE_TIME,
10
+ FILTER_REPLACE_TIME,
11
+ MAIN_BRUSH_REPLACE_TIME,
12
+ MOP_ROLLER_REPLACE_TIME,
13
+ NO_MAP,
14
+ ROBOROCK_G10S_PRO,
15
+ ROBOROCK_P10,
16
+ ROBOROCK_Q7_MAX,
17
+ ROBOROCK_QREVO_CURV,
18
+ ROBOROCK_QREVO_MASTER,
19
+ ROBOROCK_QREVO_MAXV,
20
+ ROBOROCK_QREVO_PRO,
21
+ ROBOROCK_QREVO_S,
22
+ ROBOROCK_S4_MAX,
23
+ ROBOROCK_S5_MAX,
24
+ ROBOROCK_S6,
25
+ ROBOROCK_S6_MAXV,
26
+ ROBOROCK_S6_PURE,
27
+ ROBOROCK_S7,
28
+ ROBOROCK_S7_MAXV,
29
+ ROBOROCK_S8,
30
+ ROBOROCK_S8_MAXV_ULTRA,
31
+ ROBOROCK_S8_PRO_ULTRA,
32
+ ROBOROCK_SAROS_10,
33
+ ROBOROCK_SAROS_10R,
34
+ SENSOR_DIRTY_REPLACE_TIME,
35
+ SIDE_BRUSH_REPLACE_TIME,
36
+ STRAINER_REPLACE_TIME,
37
+ ROBOROCK_G20S_Ultra,
38
+ )
39
+ from roborock_cli._vendor.roborock.exceptions import RoborockException
40
+
41
+ from ..containers import NamedRoomMapping, RoborockBase, RoborockBaseTimer, _attr_repr
42
+ from .v1_code_mappings import (
43
+ CleanFluidStatus,
44
+ ClearWaterBoxStatus,
45
+ DirtyWaterBoxStatus,
46
+ DustBagStatus,
47
+ RoborockCleanType,
48
+ RoborockDockDustCollectionModeCode,
49
+ RoborockDockErrorCode,
50
+ RoborockDockTypeCode,
51
+ RoborockDockWashTowelModeCode,
52
+ RoborockErrorCode,
53
+ RoborockFanPowerCode,
54
+ RoborockFanSpeedP10,
55
+ RoborockFanSpeedQ7Max,
56
+ RoborockFanSpeedQRevoCurv,
57
+ RoborockFanSpeedQRevoMaster,
58
+ RoborockFanSpeedQRevoMaxV,
59
+ RoborockFanSpeedS6Pure,
60
+ RoborockFanSpeedS7,
61
+ RoborockFanSpeedS7MaxV,
62
+ RoborockFanSpeedS8MaxVUltra,
63
+ RoborockFanSpeedSaros10,
64
+ RoborockFanSpeedSaros10R,
65
+ RoborockFinishReason,
66
+ RoborockInCleaning,
67
+ RoborockMopIntensityCode,
68
+ RoborockMopIntensityP10,
69
+ RoborockMopIntensityQ7Max,
70
+ RoborockMopIntensityQRevoCurv,
71
+ RoborockMopIntensityQRevoMaster,
72
+ RoborockMopIntensityQRevoMaxV,
73
+ RoborockMopIntensityS5Max,
74
+ RoborockMopIntensityS6MaxV,
75
+ RoborockMopIntensityS7,
76
+ RoborockMopIntensityS8MaxVUltra,
77
+ RoborockMopIntensitySaros10,
78
+ RoborockMopIntensitySaros10R,
79
+ RoborockMopModeCode,
80
+ RoborockMopModeQRevoCurv,
81
+ RoborockMopModeQRevoMaster,
82
+ RoborockMopModeQRevoMaxV,
83
+ RoborockMopModeS7,
84
+ RoborockMopModeS8MaxVUltra,
85
+ RoborockMopModeS8ProUltra,
86
+ RoborockMopModeSaros10,
87
+ RoborockMopModeSaros10R,
88
+ RoborockStartType,
89
+ RoborockStateCode,
90
+ )
91
+
92
+ _LOGGER = logging.getLogger(__name__)
93
+
94
+
95
+ class FieldNameBase(StrEnum):
96
+ """A base enum class that represents a field name in a RoborockBase dataclass."""
97
+
98
+
99
+ class StatusField(FieldNameBase):
100
+ """An enum that represents a field in the `Status` class.
101
+
102
+ This is used with `roborock.devices.traits.v1.status.DeviceFeaturesTrait`
103
+ to understand if a feature is supported by the device using `is_field_supported`.
104
+
105
+ The enum values are names of fields in the `Status` class. Each field is
106
+ annotated with `requires_schema_code` metadata to map the field to a schema
107
+ code in the product schema, which may have a different name than the field/attribute name.
108
+ """
109
+
110
+ STATE = "state"
111
+ BATTERY = "battery"
112
+ FAN_POWER = "fan_power"
113
+ WATER_BOX_MODE = "water_box_mode"
114
+ CHARGE_STATUS = "charge_status"
115
+ DRY_STATUS = "dry_status"
116
+
117
+
118
+ def _requires_schema_code(requires_schema_code: str, default=None) -> Any:
119
+ return field(metadata={"requires_schema_code": requires_schema_code}, default=default)
120
+
121
+
122
+ @dataclass
123
+ class Status(RoborockBase):
124
+ """This status will be deprecated in favor of StatusV2."""
125
+
126
+ msg_ver: int | None = None
127
+ msg_seq: int | None = None
128
+ state: RoborockStateCode | None = _requires_schema_code("state", default=None)
129
+ battery: int | None = _requires_schema_code("battery", default=None)
130
+ clean_time: int | None = None
131
+ clean_area: int | None = None
132
+ error_code: RoborockErrorCode | None = None
133
+ map_present: int | None = None
134
+ in_cleaning: RoborockInCleaning | None = None
135
+ in_returning: int | None = None
136
+ in_fresh_state: int | None = None
137
+ lab_status: int | None = None
138
+ water_box_status: int | None = None
139
+ back_type: int | None = None
140
+ wash_phase: int | None = None
141
+ wash_ready: int | None = None
142
+ fan_power: RoborockFanPowerCode | None = _requires_schema_code("fan_power", default=None)
143
+ dnd_enabled: int | None = None
144
+ map_status: int | None = None
145
+ is_locating: int | None = None
146
+ lock_status: int | None = None
147
+ water_box_mode: RoborockMopIntensityCode | None = _requires_schema_code("water_box_mode", default=None)
148
+ water_box_carriage_status: int | None = None
149
+ mop_forbidden_enable: int | None = None
150
+ camera_status: int | None = None
151
+ is_exploring: int | None = None
152
+ home_sec_status: int | None = None
153
+ home_sec_enable_password: int | None = None
154
+ adbumper_status: list[int] | None = None
155
+ water_shortage_status: int | None = None
156
+ dock_type: RoborockDockTypeCode | None = None
157
+ dust_collection_status: int | None = None
158
+ auto_dust_collection: int | None = None
159
+ avoid_count: int | None = None
160
+ mop_mode: RoborockMopModeCode | None = None
161
+ debug_mode: int | None = None
162
+ collision_avoid_status: int | None = None
163
+ switch_map_mode: int | None = None
164
+ dock_error_status: RoborockDockErrorCode | None = None
165
+ charge_status: int | None = _requires_schema_code("charge_status", default=None)
166
+ unsave_map_reason: int | None = None
167
+ unsave_map_flag: int | None = None
168
+ wash_status: int | None = None
169
+ distance_off: int | None = None
170
+ in_warmup: int | None = None
171
+ dry_status: int | None = _requires_schema_code("drying_status", default=None)
172
+ rdt: int | None = None
173
+ clean_percent: int | None = None
174
+ rss: int | None = None
175
+ dss: int | None = None
176
+ common_status: int | None = None
177
+ corner_clean_mode: int | None = None
178
+ last_clean_t: int | None = None
179
+ replenish_mode: int | None = None
180
+ repeat: int | None = None
181
+ kct: int | None = None
182
+ subdivision_sets: int | None = None
183
+
184
+ @property
185
+ def square_meter_clean_area(self) -> float | None:
186
+ return round(self.clean_area / 1000000, 1) if self.clean_area is not None else None
187
+
188
+ @property
189
+ def error_code_name(self) -> str | None:
190
+ return self.error_code.name if self.error_code is not None else None
191
+
192
+ @property
193
+ def state_name(self) -> str | None:
194
+ return self.state.name if self.state is not None else None
195
+
196
+ @property
197
+ def water_box_mode_name(self) -> str | None:
198
+ return self.water_box_mode.name if self.water_box_mode is not None else None
199
+
200
+ @property
201
+ def fan_power_options(self) -> list[str]:
202
+ if self.fan_power is None:
203
+ return []
204
+ return list(self.fan_power.keys())
205
+
206
+ @property
207
+ def fan_power_name(self) -> str | None:
208
+ return self.fan_power.name if self.fan_power is not None else None
209
+
210
+ @property
211
+ def mop_mode_name(self) -> str | None:
212
+ return self.mop_mode.name if self.mop_mode is not None else None
213
+
214
+ def get_fan_speed_code(self, fan_speed: str) -> int:
215
+ if self.fan_power is None:
216
+ raise RoborockException("Attempted to get fan speed before status has been updated.")
217
+ return self.fan_power.as_dict().get(fan_speed)
218
+
219
+ def get_mop_intensity_code(self, mop_intensity: str) -> int:
220
+ if self.water_box_mode is None:
221
+ raise RoborockException("Attempted to get mop_intensity before status has been updated.")
222
+ return self.water_box_mode.as_dict().get(mop_intensity)
223
+
224
+ def get_mop_mode_code(self, mop_mode: str) -> int:
225
+ if self.mop_mode is None:
226
+ raise RoborockException("Attempted to get mop_mode before status has been updated.")
227
+ return self.mop_mode.as_dict().get(mop_mode)
228
+
229
+ @property
230
+ def current_map(self) -> int | None:
231
+ """Returns the current map ID if the map is present."""
232
+ if self.map_status is not None:
233
+ map_flag = self.map_status >> 2
234
+ if map_flag != NO_MAP:
235
+ return map_flag
236
+ return None
237
+
238
+ @property
239
+ def clear_water_box_status(self) -> ClearWaterBoxStatus | None:
240
+ if self.dss:
241
+ return ClearWaterBoxStatus((self.dss >> 2) & 3)
242
+ return None
243
+
244
+ @property
245
+ def dirty_water_box_status(self) -> DirtyWaterBoxStatus | None:
246
+ if self.dss:
247
+ return DirtyWaterBoxStatus((self.dss >> 4) & 3)
248
+ return None
249
+
250
+ @property
251
+ def dust_bag_status(self) -> DustBagStatus | None:
252
+ if self.dss:
253
+ return DustBagStatus((self.dss >> 6) & 3)
254
+ return None
255
+
256
+ @property
257
+ def water_box_filter_status(self) -> int | None:
258
+ if self.dss:
259
+ return (self.dss >> 8) & 3
260
+ return None
261
+
262
+ @property
263
+ def clean_fluid_status(self) -> CleanFluidStatus | None:
264
+ if self.dss:
265
+ value = (self.dss >> 10) & 3
266
+ if value == 0:
267
+ return None # Feature not supported by this device
268
+ return CleanFluidStatus(value)
269
+ return None
270
+
271
+ @property
272
+ def hatch_door_status(self) -> int | None:
273
+ if self.dss:
274
+ return (self.dss >> 12) & 7
275
+ return None
276
+
277
+ @property
278
+ def dock_cool_fan_status(self) -> int | None:
279
+ if self.dss:
280
+ return (self.dss >> 15) & 3
281
+ return None
282
+
283
+ def __repr__(self) -> str:
284
+ return _attr_repr(self)
285
+
286
+
287
+ @dataclass
288
+ class StatusV2(RoborockBase):
289
+ """
290
+ This is a new version of the Status object.
291
+ This is the result of GET_STATUS from the api.
292
+ """
293
+
294
+ msg_ver: int | None = None
295
+ msg_seq: int | None = None
296
+ state: RoborockStateCode | None = None
297
+ battery: int | None = None
298
+ clean_time: int | None = None
299
+ clean_area: int | None = None
300
+ error_code: RoborockErrorCode | None = None
301
+ map_present: int | None = None
302
+ in_cleaning: RoborockInCleaning | None = None
303
+ in_returning: int | None = None
304
+ in_fresh_state: int | None = None
305
+ lab_status: int | None = None
306
+ water_box_status: int | None = None
307
+ back_type: int | None = None
308
+ wash_phase: int | None = None
309
+ wash_ready: int | None = None
310
+ fan_power: int | None = None
311
+ dnd_enabled: int | None = None
312
+ map_status: int | None = None
313
+ is_locating: int | None = None
314
+ lock_status: int | None = None
315
+ water_box_mode: int | None = None
316
+ water_box_carriage_status: int | None = None
317
+ mop_forbidden_enable: int | None = None
318
+ camera_status: int | None = None
319
+ is_exploring: int | None = None
320
+ home_sec_status: int | None = None
321
+ home_sec_enable_password: int | None = None
322
+ adbumper_status: list[int] | None = None
323
+ water_shortage_status: int | None = None
324
+ dock_type: RoborockDockTypeCode | None = None
325
+ dust_collection_status: int | None = None
326
+ auto_dust_collection: int | None = None
327
+ avoid_count: int | None = None
328
+ mop_mode: int | None = None
329
+ debug_mode: int | None = None
330
+ collision_avoid_status: int | None = None
331
+ switch_map_mode: int | None = None
332
+ dock_error_status: RoborockDockErrorCode | None = None
333
+ charge_status: int | None = None
334
+ unsave_map_reason: int | None = None
335
+ unsave_map_flag: int | None = None
336
+ wash_status: int | None = None
337
+ distance_off: int | None = None
338
+ in_warmup: int | None = None
339
+ dry_status: int | None = None
340
+ rdt: int | None = None
341
+ clean_percent: int | None = None
342
+ rss: int | None = None
343
+ dss: int | None = None
344
+ common_status: int | None = None
345
+ corner_clean_mode: int | None = None
346
+ last_clean_t: int | None = None
347
+ replenish_mode: int | None = None
348
+ repeat: int | None = None
349
+ kct: int | None = None
350
+ subdivision_sets: int | None = None
351
+
352
+ @property
353
+ def square_meter_clean_area(self) -> float | None:
354
+ return round(self.clean_area / 1000000, 1) if self.clean_area is not None else None
355
+
356
+ @property
357
+ def error_code_name(self) -> str | None:
358
+ return self.error_code.name if self.error_code is not None else None
359
+
360
+ @property
361
+ def state_name(self) -> str | None:
362
+ return self.state.name if self.state is not None else None
363
+
364
+ @property
365
+ def current_map(self) -> int | None:
366
+ """Returns the current map ID if the map is present."""
367
+ if self.map_status is not None:
368
+ map_flag = self.map_status >> 2
369
+ if map_flag != NO_MAP:
370
+ return map_flag
371
+ return None
372
+
373
+ @property
374
+ def clear_water_box_status(self) -> ClearWaterBoxStatus | None:
375
+ if self.dss:
376
+ return ClearWaterBoxStatus((self.dss >> 2) & 3)
377
+ return None
378
+
379
+ @property
380
+ def dirty_water_box_status(self) -> DirtyWaterBoxStatus | None:
381
+ if self.dss:
382
+ return DirtyWaterBoxStatus((self.dss >> 4) & 3)
383
+ return None
384
+
385
+ @property
386
+ def dust_bag_status(self) -> DustBagStatus | None:
387
+ if self.dss:
388
+ return DustBagStatus((self.dss >> 6) & 3)
389
+ return None
390
+
391
+ @property
392
+ def water_box_filter_status(self) -> int | None:
393
+ if self.dss:
394
+ return (self.dss >> 8) & 3
395
+ return None
396
+
397
+ @property
398
+ def clean_fluid_status(self) -> CleanFluidStatus | None:
399
+ if self.dss:
400
+ value = (self.dss >> 10) & 3
401
+ if value == 0:
402
+ return None # Feature not supported by this device
403
+ return CleanFluidStatus(value)
404
+ return None
405
+
406
+ @property
407
+ def hatch_door_status(self) -> int | None:
408
+ if self.dss:
409
+ return (self.dss >> 12) & 7
410
+ return None
411
+
412
+ @property
413
+ def dock_cool_fan_status(self) -> int | None:
414
+ if self.dss:
415
+ return (self.dss >> 15) & 3
416
+ return None
417
+
418
+ def __repr__(self) -> str:
419
+ return _attr_repr(self)
420
+
421
+
422
+ @dataclass
423
+ class S4MaxStatus(Status):
424
+ fan_power: RoborockFanSpeedS6Pure | None = None
425
+ water_box_mode: RoborockMopIntensityS7 | None = None
426
+ mop_mode: RoborockMopModeS7 | None = None
427
+
428
+
429
+ @dataclass
430
+ class S5MaxStatus(Status):
431
+ fan_power: RoborockFanSpeedS6Pure | None = None
432
+ water_box_mode: RoborockMopIntensityS5Max | None = None
433
+
434
+
435
+ @dataclass
436
+ class Q7MaxStatus(Status):
437
+ fan_power: RoborockFanSpeedQ7Max | None = None
438
+ water_box_mode: RoborockMopIntensityQ7Max | None = None
439
+
440
+
441
+ @dataclass
442
+ class QRevoMasterStatus(Status):
443
+ fan_power: RoborockFanSpeedQRevoMaster | None = None
444
+ water_box_mode: RoborockMopIntensityQRevoMaster | None = None
445
+ mop_mode: RoborockMopModeQRevoMaster | None = None
446
+
447
+
448
+ @dataclass
449
+ class QRevoCurvStatus(Status):
450
+ fan_power: RoborockFanSpeedQRevoCurv | None = None
451
+ water_box_mode: RoborockMopIntensityQRevoCurv | None = None
452
+ mop_mode: RoborockMopModeQRevoCurv | None = None
453
+
454
+
455
+ @dataclass
456
+ class QRevoMaxVStatus(Status):
457
+ fan_power: RoborockFanSpeedQRevoMaxV | None = None
458
+ water_box_mode: RoborockMopIntensityQRevoMaxV | None = None
459
+ mop_mode: RoborockMopModeQRevoMaxV | None = None
460
+
461
+
462
+ @dataclass
463
+ class S6MaxVStatus(Status):
464
+ fan_power: RoborockFanSpeedS7MaxV | None = None
465
+ water_box_mode: RoborockMopIntensityS6MaxV | None = None
466
+
467
+
468
+ @dataclass
469
+ class S6PureStatus(Status):
470
+ fan_power: RoborockFanSpeedS6Pure | None = None
471
+
472
+
473
+ @dataclass
474
+ class S7MaxVStatus(Status):
475
+ fan_power: RoborockFanSpeedS7MaxV | None = None
476
+ water_box_mode: RoborockMopIntensityS7 | None = None
477
+ mop_mode: RoborockMopModeS7 | None = None
478
+
479
+
480
+ @dataclass
481
+ class S7Status(Status):
482
+ fan_power: RoborockFanSpeedS7 | None = None
483
+ water_box_mode: RoborockMopIntensityS7 | None = None
484
+ mop_mode: RoborockMopModeS7 | None = None
485
+
486
+
487
+ @dataclass
488
+ class S8ProUltraStatus(Status):
489
+ fan_power: RoborockFanSpeedS7MaxV | None = None
490
+ water_box_mode: RoborockMopIntensityS7 | None = None
491
+ mop_mode: RoborockMopModeS8ProUltra | None = None
492
+
493
+
494
+ @dataclass
495
+ class S8Status(Status):
496
+ fan_power: RoborockFanSpeedS7MaxV | None = None
497
+ water_box_mode: RoborockMopIntensityS7 | None = None
498
+ mop_mode: RoborockMopModeS8ProUltra | None = None
499
+
500
+
501
+ @dataclass
502
+ class P10Status(Status):
503
+ fan_power: RoborockFanSpeedP10 | None = None
504
+ water_box_mode: RoborockMopIntensityP10 | None = None
505
+ mop_mode: RoborockMopModeS8ProUltra | None = None
506
+
507
+
508
+ @dataclass
509
+ class S8MaxvUltraStatus(Status):
510
+ fan_power: RoborockFanSpeedS8MaxVUltra | None = None
511
+ water_box_mode: RoborockMopIntensityS8MaxVUltra | None = None
512
+ mop_mode: RoborockMopModeS8MaxVUltra | None = None
513
+
514
+
515
+ @dataclass
516
+ class Saros10RStatus(Status):
517
+ fan_power: RoborockFanSpeedSaros10R | None = None
518
+ water_box_mode: RoborockMopIntensitySaros10R | None = None
519
+ mop_mode: RoborockMopModeSaros10R | None = None
520
+
521
+
522
+ @dataclass
523
+ class Saros10Status(Status):
524
+ fan_power: RoborockFanSpeedSaros10 | None = None
525
+ water_box_mode: RoborockMopIntensitySaros10 | None = None
526
+ mop_mode: RoborockMopModeSaros10 | None = None
527
+
528
+
529
+ ModelStatus: dict[str, type[Status]] = {
530
+ ROBOROCK_S4_MAX: S4MaxStatus,
531
+ ROBOROCK_S5_MAX: S5MaxStatus,
532
+ ROBOROCK_Q7_MAX: Q7MaxStatus,
533
+ ROBOROCK_QREVO_MASTER: QRevoMasterStatus,
534
+ ROBOROCK_QREVO_CURV: QRevoCurvStatus,
535
+ ROBOROCK_S6: S6PureStatus,
536
+ ROBOROCK_S6_MAXV: S6MaxVStatus,
537
+ ROBOROCK_S6_PURE: S6PureStatus,
538
+ ROBOROCK_S7_MAXV: S7MaxVStatus,
539
+ ROBOROCK_S7: S7Status,
540
+ ROBOROCK_S8: S8Status,
541
+ ROBOROCK_S8_PRO_ULTRA: S8ProUltraStatus,
542
+ ROBOROCK_G10S_PRO: S7MaxVStatus,
543
+ ROBOROCK_G20S_Ultra: QRevoMasterStatus,
544
+ ROBOROCK_P10: P10Status,
545
+ # These likely are not correct,
546
+ # but i am currently unable to do my typical reverse engineering/ get any data from users on this,
547
+ # so this will be here in the mean time.
548
+ ROBOROCK_QREVO_S: P10Status,
549
+ ROBOROCK_QREVO_MAXV: QRevoMaxVStatus,
550
+ ROBOROCK_QREVO_PRO: P10Status,
551
+ ROBOROCK_S8_MAXV_ULTRA: S8MaxvUltraStatus,
552
+ ROBOROCK_SAROS_10R: Saros10RStatus,
553
+ ROBOROCK_SAROS_10: Saros10Status,
554
+ }
555
+
556
+
557
+ @dataclass
558
+ class DnDTimer(RoborockBaseTimer):
559
+ """DnDTimer"""
560
+
561
+
562
+ @dataclass
563
+ class ValleyElectricityTimer(RoborockBaseTimer):
564
+ """ValleyElectricityTimer"""
565
+
566
+
567
+ @dataclass
568
+ class CleanSummary(RoborockBase):
569
+ clean_time: int | None = None
570
+ clean_area: int | None = None
571
+ clean_count: int | None = None
572
+ dust_collection_count: int | None = None
573
+ records: list[int] | None = None
574
+ last_clean_t: int | None = None
575
+
576
+ @property
577
+ def square_meter_clean_area(self) -> float | None:
578
+ """Returns the clean area in square meters."""
579
+ if isinstance(self.clean_area, list | str):
580
+ _LOGGER.warning(f"Clean area is a unexpected type! Please give the following in a issue: {self.clean_area}")
581
+ return None
582
+ return round(self.clean_area / 1000000, 1) if self.clean_area is not None else None
583
+
584
+ def __repr__(self) -> str:
585
+ """Return a string representation of the object including all attributes."""
586
+ return _attr_repr(self)
587
+
588
+
589
+ @dataclass
590
+ class CleanRecord(RoborockBase):
591
+ begin: int | None = None
592
+ end: int | None = None
593
+ duration: int | None = None
594
+ area: int | None = None
595
+ error: int | None = None
596
+ complete: int | None = None
597
+ start_type: RoborockStartType | None = None
598
+ clean_type: RoborockCleanType | None = None
599
+ finish_reason: RoborockFinishReason | None = None
600
+ dust_collection_status: int | None = None
601
+ avoid_count: int | None = None
602
+ wash_count: int | None = None
603
+ map_flag: int | None = None
604
+
605
+ @property
606
+ def square_meter_area(self) -> float | None:
607
+ return round(self.area / 1000000, 1) if self.area is not None else None
608
+
609
+ @property
610
+ def begin_datetime(self) -> datetime.datetime | None:
611
+ return datetime.datetime.fromtimestamp(self.begin).astimezone(datetime.UTC) if self.begin else None
612
+
613
+ @property
614
+ def end_datetime(self) -> datetime.datetime | None:
615
+ return datetime.datetime.fromtimestamp(self.end).astimezone(datetime.UTC) if self.end else None
616
+
617
+ def __repr__(self) -> str:
618
+ return _attr_repr(self)
619
+
620
+
621
+ class CleanSummaryWithDetail(CleanSummary):
622
+ """CleanSummary with the last CleanRecord included."""
623
+
624
+ last_clean_record: CleanRecord | None = None
625
+
626
+
627
+ @dataclass
628
+ class Consumable(RoborockBase):
629
+ main_brush_work_time: int | None = None
630
+ side_brush_work_time: int | None = None
631
+ filter_work_time: int | None = None
632
+ filter_element_work_time: int | None = None
633
+ sensor_dirty_time: int | None = None
634
+ strainer_work_times: int | None = None
635
+ dust_collection_work_times: int | None = None
636
+ cleaning_brush_work_times: int | None = None
637
+ moproller_work_time: int | None = None
638
+
639
+ @property
640
+ def main_brush_time_left(self) -> int | None:
641
+ return MAIN_BRUSH_REPLACE_TIME - self.main_brush_work_time if self.main_brush_work_time is not None else None
642
+
643
+ @property
644
+ def side_brush_time_left(self) -> int | None:
645
+ return SIDE_BRUSH_REPLACE_TIME - self.side_brush_work_time if self.side_brush_work_time is not None else None
646
+
647
+ @property
648
+ def filter_time_left(self) -> int | None:
649
+ return FILTER_REPLACE_TIME - self.filter_work_time if self.filter_work_time is not None else None
650
+
651
+ @property
652
+ def sensor_time_left(self) -> int | None:
653
+ return SENSOR_DIRTY_REPLACE_TIME - self.sensor_dirty_time if self.sensor_dirty_time is not None else None
654
+
655
+ @property
656
+ def strainer_time_left(self) -> int | None:
657
+ return STRAINER_REPLACE_TIME - self.strainer_work_times if self.strainer_work_times is not None else None
658
+
659
+ @property
660
+ def dust_collection_time_left(self) -> int | None:
661
+ return (
662
+ DUST_COLLECTION_REPLACE_TIME - self.dust_collection_work_times
663
+ if self.dust_collection_work_times is not None
664
+ else None
665
+ )
666
+
667
+ @property
668
+ def cleaning_brush_time_left(self) -> int | None:
669
+ return (
670
+ CLEANING_BRUSH_REPLACE_TIME - self.cleaning_brush_work_times
671
+ if self.cleaning_brush_work_times is not None
672
+ else None
673
+ )
674
+
675
+ @property
676
+ def mop_roller_time_left(self) -> int | None:
677
+ return MOP_ROLLER_REPLACE_TIME - self.moproller_work_time if self.moproller_work_time is not None else None
678
+
679
+ def __repr__(self) -> str:
680
+ return _attr_repr(self)
681
+
682
+
683
+ @dataclass
684
+ class MultiMapsListRoom(RoborockBase):
685
+ id: int | None = None
686
+ tag: int | None = None
687
+ iot_name_id: str | None = None
688
+ iot_name: str | None = None
689
+
690
+ @property
691
+ def named_room_mapping(self) -> NamedRoomMapping | None:
692
+ """Returns a NamedRoomMapping object if valid."""
693
+ if self.id is None or self.iot_name_id is None:
694
+ return None
695
+ return NamedRoomMapping(
696
+ segment_id=self.id,
697
+ iot_id=self.iot_name_id,
698
+ raw_name=self.iot_name,
699
+ )
700
+
701
+
702
+ @dataclass
703
+ class MultiMapsListMapInfoBakMaps(RoborockBase):
704
+ mapflag: Any | None = None
705
+ add_time: Any | None = None
706
+
707
+
708
+ @dataclass
709
+ class MultiMapsListMapInfo(RoborockBase):
710
+ map_flag: int
711
+ name: str
712
+ add_time: Any | None = None
713
+ length: Any | None = None
714
+ bak_maps: list[MultiMapsListMapInfoBakMaps] | None = None
715
+ rooms: list[MultiMapsListRoom] | None = None
716
+
717
+ @property
718
+ def mapFlag(self) -> int:
719
+ """Alias for map_flag, returns the map flag as an integer."""
720
+ return self.map_flag
721
+
722
+ @property
723
+ def rooms_map(self) -> dict[int, NamedRoomMapping]:
724
+ """Returns a dictionary of room mappings by segment id."""
725
+ return {
726
+ room.id: mapping
727
+ for room in self.rooms or ()
728
+ if room.id is not None and (mapping := room.named_room_mapping) is not None
729
+ }
730
+
731
+
732
+ @dataclass
733
+ class MultiMapsList(RoborockBase):
734
+ max_multi_map: int | None = None
735
+ max_bak_map: int | None = None
736
+ multi_map_count: int | None = None
737
+ map_info: list[MultiMapsListMapInfo] | None = None
738
+
739
+
740
+ @dataclass
741
+ class SmartWashParams(RoborockBase):
742
+ smart_wash: int | None = None
743
+ wash_interval: int | None = None
744
+
745
+
746
+ @dataclass
747
+ class DustCollectionMode(RoborockBase):
748
+ mode: RoborockDockDustCollectionModeCode | None = None
749
+
750
+
751
+ @dataclass
752
+ class WashTowelMode(RoborockBase):
753
+ wash_mode: RoborockDockWashTowelModeCode | None = None
754
+
755
+
756
+ @dataclass
757
+ class NetworkInfo(RoborockBase):
758
+ ip: str
759
+ ssid: str | None = None
760
+ mac: str | None = None
761
+ bssid: str | None = None
762
+ rssi: int | None = None
763
+
764
+
765
+ @dataclass
766
+ class AppInitStatusLocalInfo(RoborockBase):
767
+ location: str
768
+ bom: str | None = None
769
+ featureset: int | None = None
770
+ language: str | None = None
771
+ logserver: str | None = None
772
+ wifiplan: str | None = None
773
+ timezone: str | None = None
774
+ name: str | None = None
775
+
776
+
777
+ @dataclass
778
+ class AppInitStatus(RoborockBase):
779
+ local_info: AppInitStatusLocalInfo
780
+ feature_info: list[int]
781
+ new_feature_info: int
782
+ new_feature_info_str: str = ""
783
+ new_feature_info_2: int | None = None
784
+ carriage_type: int | None = None
785
+ dsp_version: str | None = None
786
+
787
+
788
+ @dataclass
789
+ class ChildLockStatus(RoborockBase):
790
+ lock_status: int = 0
791
+
792
+
793
+ @dataclass
794
+ class FlowLedStatus(RoborockBase):
795
+ status: int = 0
796
+
797
+
798
+ @dataclass
799
+ class LedStatus(RoborockBase):
800
+ status: int = 0