python-roborock 4.16.0__tar.gz → 4.17.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.
Files changed (98) hide show
  1. {python_roborock-4.16.0 → python_roborock-4.17.0}/PKG-INFO +1 -1
  2. {python_roborock-4.16.0 → python_roborock-4.17.0}/pyproject.toml +1 -1
  3. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/code_mappings.py +16 -1
  4. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/v1/v1_clean_modes.py +10 -1
  5. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/v1/v1_containers.py +136 -0
  6. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/device_manager.py +1 -0
  7. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/__init__.py +8 -3
  8. python_roborock-4.17.0/roborock/devices/traits/v1/status.py +92 -0
  9. python_roborock-4.16.0/roborock/devices/traits/v1/status.py +0 -24
  10. {python_roborock-4.16.0 → python_roborock-4.17.0}/.gitignore +0 -0
  11. {python_roborock-4.16.0 → python_roborock-4.17.0}/LICENSE +0 -0
  12. {python_roborock-4.16.0 → python_roborock-4.17.0}/README.md +0 -0
  13. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/__init__.py +0 -0
  14. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/broadcast_protocol.py +0 -0
  15. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/callbacks.py +0 -0
  16. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/cli.py +0 -0
  17. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/const.py +0 -0
  18. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/__init__.py +0 -0
  19. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/b01_q10/__init__.py +0 -0
  20. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
  21. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
  22. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/b01_q7/__init__.py +0 -0
  23. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
  24. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
  25. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/containers.py +0 -0
  26. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/dyad/__init__.py +0 -0
  27. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/dyad/dyad_code_mappings.py +0 -0
  28. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/dyad/dyad_containers.py +0 -0
  29. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/v1/__init__.py +0 -0
  30. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/v1/v1_code_mappings.py +0 -0
  31. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/zeo/__init__.py +0 -0
  32. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/zeo/zeo_code_mappings.py +0 -0
  33. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/data/zeo/zeo_containers.py +0 -0
  34. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/device_features.py +0 -0
  35. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/README.md +0 -0
  36. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/__init__.py +0 -0
  37. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/cache.py +0 -0
  38. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/device.py +0 -0
  39. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/file_cache.py +0 -0
  40. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/rpc/__init__.py +0 -0
  41. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/rpc/a01_channel.py +0 -0
  42. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/rpc/b01_q10_channel.py +0 -0
  43. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/rpc/b01_q7_channel.py +0 -0
  44. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/rpc/v1_channel.py +0 -0
  45. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/__init__.py +0 -0
  46. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/a01/__init__.py +0 -0
  47. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/b01/__init__.py +0 -0
  48. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/b01/q10/__init__.py +0 -0
  49. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/b01/q10/command.py +0 -0
  50. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/b01/q10/common.py +0 -0
  51. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/b01/q10/status.py +0 -0
  52. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/b01/q10/vacuum.py +0 -0
  53. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/b01/q7/__init__.py +0 -0
  54. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/b01/q7/clean_summary.py +0 -0
  55. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/traits_mixin.py +0 -0
  56. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/child_lock.py +0 -0
  57. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
  58. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/command.py +0 -0
  59. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/common.py +0 -0
  60. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/consumeable.py +0 -0
  61. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/device_features.py +0 -0
  62. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
  63. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
  64. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/flow_led_status.py +0 -0
  65. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/home.py +0 -0
  66. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/led_status.py +0 -0
  67. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/map_content.py +0 -0
  68. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/maps.py +0 -0
  69. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/network_info.py +0 -0
  70. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/rooms.py +0 -0
  71. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/routines.py +0 -0
  72. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
  73. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
  74. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/volume.py +0 -0
  75. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
  76. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/transport/__init__.py +0 -0
  77. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/transport/channel.py +0 -0
  78. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/transport/local_channel.py +0 -0
  79. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/devices/transport/mqtt_channel.py +0 -0
  80. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/diagnostics.py +0 -0
  81. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/exceptions.py +0 -0
  82. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/map/__init__.py +0 -0
  83. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/map/map_parser.py +0 -0
  84. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/mqtt/__init__.py +0 -0
  85. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/mqtt/health_manager.py +0 -0
  86. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/mqtt/roborock_session.py +0 -0
  87. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/mqtt/session.py +0 -0
  88. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/protocol.py +0 -0
  89. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/protocols/__init__.py +0 -0
  90. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/protocols/a01_protocol.py +0 -0
  91. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/protocols/b01_q10_protocol.py +0 -0
  92. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/protocols/b01_q7_protocol.py +0 -0
  93. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/protocols/v1_protocol.py +0 -0
  94. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/py.typed +0 -0
  95. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/roborock_message.py +0 -0
  96. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/roborock_typing.py +0 -0
  97. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/util.py +0 -0
  98. {python_roborock-4.16.0 → python_roborock-4.17.0}/roborock/web_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-roborock
3
- Version: 4.16.0
3
+ Version: 4.17.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 = "4.16.0"
3
+ version = "4.17.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"
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import logging
4
4
  from collections import namedtuple
5
5
  from enum import Enum, IntEnum, StrEnum
6
- from typing import Self
6
+ from typing import Any, Self
7
7
 
8
8
  _LOGGER = logging.getLogger(__name__)
9
9
  completed_warnings = set()
@@ -105,6 +105,21 @@ class RoborockModeEnum(StrEnum):
105
105
  """Returns a list of all member values."""
106
106
  return [member.value for member in cls]
107
107
 
108
+ def __eq__(self, other: Any) -> bool:
109
+ if isinstance(other, str):
110
+ return self.value == other or self.name == other
111
+ if isinstance(other, int):
112
+ return self.code == other
113
+ return super().__eq__(other)
114
+
115
+ def __hash__(self) -> int:
116
+ """Hash a RoborockModeEnum.
117
+
118
+ It is critical that you do not mix RoborockModeEnums with raw strings or ints in hashed situations
119
+ (i.e. sets or keys in dictionaries)
120
+ """
121
+ return hash((self.code, self._value_))
122
+
108
123
 
109
124
  ProductInfo = namedtuple("ProductInfo", ["nickname", "short_models"])
110
125
 
@@ -16,6 +16,8 @@ class VacuumModes(RoborockModeEnum):
16
16
  TURBO = ("turbo", 103)
17
17
  MAX = ("max", 104)
18
18
  MAX_PLUS = ("max_plus", 108)
19
+ CARPET = ("carpet", 107)
20
+ OFF_RAISE_MAIN_BRUSH = ("off_raise_main_brush", 109)
19
21
  CUSTOMIZED = ("custom", 106)
20
22
  SMART_MODE = ("smart_mode", 110)
21
23
 
@@ -45,6 +47,8 @@ class WaterModes(RoborockModeEnum):
45
47
  STANDARD = ("standard", 202)
46
48
  HIGH = ("high", 203)
47
49
  INTENSE = ("intense", 203)
50
+ MIN = ("min", 205)
51
+ MAX = ("max", 206)
48
52
  CUSTOMIZED = ("custom", 204)
49
53
  CUSTOM = ("custom_water_flow", 207)
50
54
  EXTREME = ("extreme", 208)
@@ -81,9 +85,14 @@ def get_clean_modes(features: DeviceFeatures) -> list[VacuumModes]:
81
85
  if features.is_max_plus_mode_supported or features.is_none_pure_clean_mop_with_max_plus:
82
86
  # If the vacuum has max plus mode supported
83
87
  modes.append(VacuumModes.MAX_PLUS)
88
+ if features.is_carpet_deep_clean_supported:
89
+ modes.append(VacuumModes.CARPET)
84
90
  if features.is_pure_clean_mop_supported:
85
91
  # If the vacuum is capable of 'pure mop clean' aka no vacuum
86
- modes.append(VacuumModes.OFF)
92
+ if features.is_support_main_brush_up_down_supported:
93
+ modes.append(VacuumModes.OFF_RAISE_MAIN_BRUSH)
94
+ else:
95
+ modes.append(VacuumModes.OFF)
87
96
  else:
88
97
  # If not, we can add gentle
89
98
  modes.append(VacuumModes.GENTLE)
@@ -121,6 +121,8 @@ def _requires_schema_code(requires_schema_code: str, default=None) -> Any:
121
121
 
122
122
  @dataclass
123
123
  class Status(RoborockBase):
124
+ """This status will be deprecated in favor of StatusV2."""
125
+
124
126
  msg_ver: int | None = None
125
127
  msg_seq: int | None = None
126
128
  state: RoborockStateCode | None = _requires_schema_code("state", default=None)
@@ -282,6 +284,140 @@ class Status(RoborockBase):
282
284
  return _attr_repr(self)
283
285
 
284
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 None
404
+
405
+ @property
406
+ def hatch_door_status(self) -> int | None:
407
+ if self.dss:
408
+ return (self.dss >> 12) & 7
409
+ return None
410
+
411
+ @property
412
+ def dock_cool_fan_status(self) -> int | None:
413
+ if self.dss:
414
+ return (self.dss >> 15) & 3
415
+ return None
416
+
417
+ def __repr__(self) -> str:
418
+ return _attr_repr(self)
419
+
420
+
285
421
  @dataclass
286
422
  class S4MaxStatus(Status):
287
423
  fan_power: RoborockFanSpeedS6Pure | None = None
@@ -239,6 +239,7 @@ async def create_device_manager(
239
239
  web_api,
240
240
  device_cache=device_cache,
241
241
  map_parser_config=map_parser_config,
242
+ region=user_data.region,
242
243
  )
243
244
  case DeviceVersion.A01:
244
245
  channel = create_mqtt_channel(user_data, mqtt_params, mqtt_session, device)
@@ -54,7 +54,6 @@ code in HomeDataProduct Schema that is required for the field to be supported.
54
54
 
55
55
  import logging
56
56
  from dataclasses import dataclass, field, fields
57
- from functools import cache
58
57
  from typing import Any, get_args
59
58
 
60
59
  from roborock.data.containers import HomeData, HomeDataProduct, RoborockBase
@@ -180,6 +179,7 @@ class PropertiesApi(Trait):
180
179
  web_api: UserWebApiClient,
181
180
  device_cache: DeviceCache,
182
181
  map_parser_config: MapParserConfig | None = None,
182
+ region: str | None = None,
183
183
  ) -> None:
184
184
  """Initialize the V1TraitProps."""
185
185
  self._device_uid = device_uid
@@ -188,14 +188,15 @@ class PropertiesApi(Trait):
188
188
  self._map_rpc_channel = map_rpc_channel
189
189
  self._web_api = web_api
190
190
  self._device_cache = device_cache
191
+ self._region = region
191
192
 
192
- self.status = StatusTrait(product)
193
+ self.device_features = DeviceFeaturesTrait(product, self._device_cache)
194
+ self.status = StatusTrait(self.device_features, region=self._region)
193
195
  self.consumables = ConsumableTrait()
194
196
  self.rooms = RoomsTrait(home_data)
195
197
  self.maps = MapsTrait(self.status)
196
198
  self.map_content = MapContentTrait(map_parser_config)
197
199
  self.home = HomeTrait(self.status, self.maps, self.map_content, self.rooms, self._device_cache)
198
- self.device_features = DeviceFeaturesTrait(product, self._device_cache)
199
200
  self.network_info = NetworkInfoTrait(device_uid, self._device_cache)
200
201
  self.routines = RoutinesTrait(device_uid, web_api)
201
202
 
@@ -206,6 +207,8 @@ class PropertiesApi(Trait):
206
207
  if (union_args := get_args(item.type)) is None or len(union_args) > 0:
207
208
  continue
208
209
  _LOGGER.debug("Trait '%s' is supported, initializing", item.name)
210
+ if not callable(item.type):
211
+ continue
209
212
  trait = item.type()
210
213
  setattr(self, item.name, trait)
211
214
  # This is a hack to allow setting the rpc_channel on all traits. This is
@@ -324,6 +327,7 @@ def create(
324
327
  web_api: UserWebApiClient,
325
328
  device_cache: DeviceCache,
326
329
  map_parser_config: MapParserConfig | None = None,
330
+ region: str | None = None,
327
331
  ) -> PropertiesApi:
328
332
  """Create traits for V1 devices."""
329
333
  return PropertiesApi(
@@ -336,4 +340,5 @@ def create(
336
340
  web_api,
337
341
  device_cache,
338
342
  map_parser_config,
343
+ region=region,
339
344
  )
@@ -0,0 +1,92 @@
1
+ from functools import cached_property
2
+ from typing import Self
3
+
4
+ from roborock import CleanRoutes, StatusV2, VacuumModes, WaterModes, get_clean_modes, get_clean_routes, get_water_modes
5
+ from roborock.roborock_typing import RoborockCommand
6
+
7
+ from . import common
8
+ from .device_features import DeviceFeaturesTrait
9
+
10
+
11
+ class StatusTrait(StatusV2, common.V1TraitMixin):
12
+ """Trait for managing the status of Roborock devices.
13
+
14
+ The StatusTrait gives you the access to the state of a Roborock vacuum.
15
+ The various attribute options on state change per each device.
16
+ Values like fan speed, mop mode, etc. have different options for every device
17
+ and are dynamically determined.
18
+
19
+ Usage:
20
+ Before accessing status properties, you should call `refresh()` to fetch
21
+ the latest data from the device. You must pass in the device feature trait
22
+ to this trait so that the dynamic attributes can be pre-determined.
23
+
24
+ The current dynamic attributes are:
25
+ - Fan Speed
26
+ - Water Mode
27
+ - Mop Route
28
+
29
+ You should call the _options() version of the attribute to know which are supported for your device
30
+ (i.e. fan_speed_options())
31
+ Then you can call the _mapping to convert an int value to the actual Enum. (i.e. fan_speed_mapping())
32
+ You can call the _name property to get the str value of the enum. (i.e. fan_speed_name)
33
+
34
+ """
35
+
36
+ command = RoborockCommand.GET_STATUS
37
+
38
+ def __init__(self, device_feature_trait: DeviceFeaturesTrait, region: str | None = None) -> None:
39
+ """Initialize the StatusTrait."""
40
+ super().__init__()
41
+ self._device_features_trait = device_feature_trait
42
+ self._region = region
43
+
44
+ @cached_property
45
+ def fan_speed_options(self) -> list[VacuumModes]:
46
+ return get_clean_modes(self._device_features_trait)
47
+
48
+ @cached_property
49
+ def fan_speed_mapping(self) -> dict[int, str]:
50
+ return {fan.code: fan.value for fan in self.fan_speed_options}
51
+
52
+ @cached_property
53
+ def water_mode_options(self) -> list[WaterModes]:
54
+ return get_water_modes(self._device_features_trait)
55
+
56
+ @cached_property
57
+ def water_mode_mapping(self) -> dict[int, str]:
58
+ return {mop.code: mop.value for mop in self.water_mode_options}
59
+
60
+ @cached_property
61
+ def mop_route_options(self) -> list[CleanRoutes]:
62
+ return get_clean_routes(self._device_features_trait, self._region or "us")
63
+
64
+ @cached_property
65
+ def mop_route_mapping(self) -> dict[int, str]:
66
+ return {route.code: route.value for route in self.mop_route_options}
67
+
68
+ @property
69
+ def fan_speed_name(self) -> str | None:
70
+ if self.fan_power is None:
71
+ return None
72
+ return self.fan_speed_mapping.get(self.fan_power)
73
+
74
+ @property
75
+ def water_mode_name(self) -> str | None:
76
+ if self.water_box_mode is None:
77
+ return None
78
+ return self.water_mode_mapping.get(self.water_box_mode)
79
+
80
+ @property
81
+ def mop_route_name(self) -> str | None:
82
+ if self.mop_mode is None:
83
+ return None
84
+ return self.mop_route_mapping.get(self.mop_mode)
85
+
86
+ def _parse_response(self, response: common.V1ResponseData) -> Self:
87
+ """Parse the response from the device into a StatusV2-based status object."""
88
+ if isinstance(response, list):
89
+ response = response[0]
90
+ if isinstance(response, dict):
91
+ return StatusV2.from_dict(response)
92
+ raise ValueError(f"Unexpected status format: {response!r}")
@@ -1,24 +0,0 @@
1
- from typing import Self
2
-
3
- from roborock.data import HomeDataProduct, ModelStatus, S7MaxVStatus, Status
4
- from roborock.devices.traits.v1 import common
5
- from roborock.roborock_typing import RoborockCommand
6
-
7
-
8
- class StatusTrait(Status, common.V1TraitMixin):
9
- """Trait for managing the status of Roborock devices."""
10
-
11
- command = RoborockCommand.GET_STATUS
12
-
13
- def __init__(self, product_info: HomeDataProduct) -> None:
14
- """Initialize the StatusTrait."""
15
- self._product_info = product_info
16
-
17
- def _parse_response(self, response: common.V1ResponseData) -> Self:
18
- """Parse the response from the device into a CleanSummary."""
19
- status_type: type[Status] = ModelStatus.get(self._product_info.model, S7MaxVStatus)
20
- if isinstance(response, list):
21
- response = response[0]
22
- if isinstance(response, dict):
23
- return status_type.from_dict(response)
24
- raise ValueError(f"Unexpected status format: {response!r}")