python-roborock 6.0.0__tar.gz → 6.2.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 (128) hide show
  1. {python_roborock-6.0.0 → python_roborock-6.2.0}/PKG-INFO +1 -1
  2. {python_roborock-6.0.0 → python_roborock-6.2.0}/pyproject.toml +1 -1
  3. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/code_mappings.py +19 -2
  4. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/dyad/dyad_code_mappings.py +1 -1
  5. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/v1/v1_code_mappings.py +32 -32
  6. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/v1/v1_containers.py +2 -2
  7. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/map/b01_q10_map_parser.py +34 -16
  8. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/map/map_parser.py +61 -3
  9. python_roborock-6.2.0/roborock/map/room_colors.py +59 -0
  10. {python_roborock-6.0.0 → python_roborock-6.2.0}/.gitignore +0 -0
  11. {python_roborock-6.0.0 → python_roborock-6.2.0}/LICENSE +0 -0
  12. {python_roborock-6.0.0 → python_roborock-6.2.0}/README.md +0 -0
  13. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/__init__.py +0 -0
  14. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/broadcast_protocol.py +0 -0
  15. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/callbacks.py +0 -0
  16. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/cli.py +0 -0
  17. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/const.py +0 -0
  18. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/__init__.py +0 -0
  19. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/b01_q10/__init__.py +0 -0
  20. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
  21. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
  22. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/b01_q7/__init__.py +0 -0
  23. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
  24. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
  25. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/containers.py +0 -0
  26. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/dyad/__init__.py +0 -0
  27. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/dyad/dyad_containers.py +0 -0
  28. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/mower/__init__.py +0 -0
  29. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/mower/mower_containers.py +0 -0
  30. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/v1/__init__.py +0 -0
  31. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/v1/v1_clean_modes.py +0 -0
  32. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/zeo/__init__.py +0 -0
  33. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/zeo/zeo_code_mappings.py +0 -0
  34. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/data/zeo/zeo_containers.py +0 -0
  35. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/device_features.py +0 -0
  36. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/README.md +0 -0
  37. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/__init__.py +0 -0
  38. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/cache.py +0 -0
  39. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/device.py +0 -0
  40. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/device_manager.py +0 -0
  41. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/file_cache.py +0 -0
  42. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/rpc/__init__.py +0 -0
  43. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/rpc/a01_channel.py +0 -0
  44. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/rpc/b01_q10_channel.py +0 -0
  45. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/rpc/b01_q7_channel.py +0 -0
  46. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/rpc/v1_channel.py +0 -0
  47. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/__init__.py +0 -0
  48. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/a01/__init__.py +0 -0
  49. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/__init__.py +0 -0
  50. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/__init__.py +0 -0
  51. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/button_light.py +0 -0
  52. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/child_lock.py +0 -0
  53. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/clean_history.py +0 -0
  54. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/command.py +0 -0
  55. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/common.py +0 -0
  56. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/consumable.py +0 -0
  57. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/do_not_disturb.py +0 -0
  58. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/dust_collection.py +0 -0
  59. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/map.py +0 -0
  60. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/network_info.py +0 -0
  61. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/remote.py +0 -0
  62. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/status.py +0 -0
  63. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/vacuum.py +0 -0
  64. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q10/volume.py +0 -0
  65. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q7/__init__.py +0 -0
  66. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q7/clean_summary.py +0 -0
  67. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q7/map.py +0 -0
  68. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/b01/q7/map_content.py +0 -0
  69. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/common.py +0 -0
  70. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/traits_mixin.py +0 -0
  71. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/__init__.py +0 -0
  72. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/child_lock.py +0 -0
  73. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
  74. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/command.py +0 -0
  75. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/common.py +0 -0
  76. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/consumeable.py +0 -0
  77. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/device_features.py +0 -0
  78. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
  79. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
  80. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/flow_led_status.py +0 -0
  81. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/home.py +0 -0
  82. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/led_status.py +0 -0
  83. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/map_content.py +0 -0
  84. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/maps.py +0 -0
  85. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/network_info.py +0 -0
  86. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/obstacle_photos.py +0 -0
  87. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/rooms.py +0 -0
  88. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/routines.py +0 -0
  89. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
  90. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/status.py +0 -0
  91. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
  92. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/volume.py +0 -0
  93. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
  94. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/transport/__init__.py +0 -0
  95. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/transport/channel.py +0 -0
  96. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/transport/local_channel.py +0 -0
  97. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/devices/transport/mqtt_channel.py +0 -0
  98. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/diagnostics.py +0 -0
  99. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/exceptions.py +0 -0
  100. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/map/__init__.py +0 -0
  101. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/map/b01_grid_layers.py +0 -0
  102. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/map/b01_map_parser.py +0 -0
  103. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/map/b01_q10_overlays.py +0 -0
  104. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/map/b01_q10_render.py +0 -0
  105. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/map/proto/__init__.py +0 -0
  106. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/map/proto/b01_scmap.proto +0 -0
  107. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/map/proto/b01_scmap_pb2.py +0 -0
  108. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/mqtt/__init__.py +0 -0
  109. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/mqtt/health_manager.py +0 -0
  110. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/mqtt/roborock_session.py +0 -0
  111. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/mqtt/session.py +0 -0
  112. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/protocol.py +0 -0
  113. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/protocols/__init__.py +0 -0
  114. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/protocols/a01_protocol.py +0 -0
  115. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/protocols/b01_q10_protocol.py +0 -0
  116. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/protocols/b01_q7_protocol.py +0 -0
  117. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/protocols/v1_protocol.py +0 -0
  118. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/py.typed +0 -0
  119. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/roborock_message.py +0 -0
  120. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/roborock_typing.py +0 -0
  121. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/testing/__init__.py +0 -0
  122. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/testing/b01_q10_simulator.py +0 -0
  123. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/testing/channel.py +0 -0
  124. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/testing/cloud.py +0 -0
  125. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/testing/simulator.py +0 -0
  126. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/testing/v1_simulator.py +0 -0
  127. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/util.py +0 -0
  128. {python_roborock-6.0.0 → python_roborock-6.2.0}/roborock/web_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-roborock
3
- Version: 6.0.0
3
+ Version: 6.2.0
4
4
  Summary: A package to control Roborock vacuums.
5
5
  Project-URL: Repository, https://github.com/python-roborock/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 = "6.0.0"
3
+ version = "6.2.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"
@@ -10,10 +10,22 @@ completed_warnings = set()
10
10
  class RoborockEnum(IntEnum):
11
11
  """Roborock Enum for codes with int values"""
12
12
 
13
+ _display_name_: str | None
14
+
15
+ def __new__(cls, value: int, display_name: str | None = None) -> Self:
16
+ member = int.__new__(cls, value)
17
+ member._value_ = value
18
+ member._display_name_ = display_name
19
+ return member
20
+
13
21
  @property
14
22
  def name(self) -> str:
15
23
  return super().name.lower()
16
24
 
25
+ @property
26
+ def display_name(self) -> str:
27
+ return self._display_name_ or self.name
28
+
17
29
  @classmethod
18
30
  def _missing_(cls: type[Self], key) -> Self:
19
31
  if hasattr(cls, "unknown"):
@@ -30,8 +42,13 @@ class RoborockEnum(IntEnum):
30
42
  return default_value
31
43
 
32
44
  @classmethod
33
- def as_dict(cls: type[Self]):
34
- return {i.name: i.value for i in cls if i.name != "missing"}
45
+ def as_dict(cls: type[Self]) -> dict[str, int]:
46
+ result: dict[str, int] = {}
47
+ for item in cls:
48
+ if item.name == "missing":
49
+ continue
50
+ result.setdefault(item.display_name, item.value)
51
+ return result
35
52
 
36
53
  @classmethod
37
54
  def as_enum_dict(cls: type[Self]):
@@ -95,7 +95,7 @@ class DyadError(RoborockEnum):
95
95
  battery_temperature_protection = (
96
96
  20007 # Battery temperature protection. Wait for the temperature to return to a normal range.
97
97
  )
98
- battery_temperature_protection_2 = 20008
98
+ battery_temperature_protection_2 = (20008, "battery_temperature_protection")
99
99
  power_adapter_error = 20009 # Check if the power adapter is working properly.
100
100
  dirty_charging_contacts = 10007 # Disconnection between the device and dock. Wipe charging contacts.
101
101
  low_battery = 20017 # Low battery level. Charge before starting self-cleaning.
@@ -8,40 +8,40 @@ class RoborockFinishReason(RoborockEnum):
8
8
  manual_interrupt = 21 # Cleaning interrupted by user
9
9
  cleanup_interrupted = 24 # Cleanup interrupted
10
10
  manual_interrupt_2 = 21
11
- manual_interrupt_12 = 29
11
+ manual_interrupt_12 = (29, "manual_interrupt")
12
12
  breakpoint = 32 # Could not continue cleaning
13
- breakpoint_2 = 33
14
- cleanup_interrupted_2 = 34
15
- manual_interrupt_3 = 35
16
- manual_interrupt_4 = 36
17
- manual_interrupt_5 = 37
18
- manual_interrupt_6 = 43
13
+ breakpoint_2 = (33, "breakpoint")
14
+ cleanup_interrupted_2 = (34, "cleanup_interrupted")
15
+ manual_interrupt_3 = (35, "manual_interrupt")
16
+ manual_interrupt_4 = (36, "manual_interrupt")
17
+ manual_interrupt_5 = (37, "manual_interrupt")
18
+ manual_interrupt_6 = (43, "manual_interrupt")
19
19
  locate_fail = 45 # Positioning Failed
20
- cleanup_interrupted_3 = 64
21
- locate_fail_2 = 65
22
- manual_interrupt_7 = 48
23
- manual_interrupt_8 = 49
24
- manual_interrupt_9 = 50
25
- cleanup_interrupted_4 = 51
20
+ cleanup_interrupted_3 = (64, "cleanup_interrupted")
21
+ locate_fail_2 = (65, "locate_fail")
22
+ manual_interrupt_7 = (48, "manual_interrupt")
23
+ manual_interrupt_8 = (49, "manual_interrupt")
24
+ manual_interrupt_9 = (50, "manual_interrupt")
25
+ cleanup_interrupted_4 = (51, "cleanup_interrupted")
26
26
  finished_cleaning = 52 # Finished cleaning
27
- finished_cleaning_2 = 54
28
- finished_cleaning_3 = 55
29
- finished_cleaning_4 = 56
30
- finished_clenaing_5 = 57
31
- manual_interrupt_10 = 60
27
+ finished_cleaning_2 = (54, "finished_cleaning")
28
+ finished_cleaning_3 = (55, "finished_cleaning")
29
+ finished_cleaning_4 = (56, "finished_cleaning")
30
+ finished_clenaing_5 = (57, "finished_cleaning")
31
+ manual_interrupt_10 = (60, "manual_interrupt")
32
32
  area_unreachable = 61 # Area unreachable
33
- area_unreachable_2 = 62
33
+ area_unreachable_2 = (62, "area_unreachable")
34
34
  washing_error = 67 # Washing error
35
35
  back_to_wash_failure = 68 # Failed to return to the dock
36
- cleanup_interrupted_5 = 101
37
- breakpoint_4 = 102
38
- manual_interrupt_11 = 103
39
- cleanup_interrupted_6 = 104
40
- cleanup_interrupted_7 = 105
41
- cleanup_interrupted_8 = 106
42
- cleanup_interrupted_9 = 107
43
- cleanup_interrupted_10 = 109
44
- cleanup_interrupted_11 = 110
36
+ cleanup_interrupted_5 = (101, "cleanup_interrupted")
37
+ breakpoint_4 = (102, "breakpoint")
38
+ manual_interrupt_11 = (103, "manual_interrupt")
39
+ cleanup_interrupted_6 = (104, "cleanup_interrupted")
40
+ cleanup_interrupted_7 = (105, "cleanup_interrupted")
41
+ cleanup_interrupted_8 = (106, "cleanup_interrupted")
42
+ cleanup_interrupted_9 = (107, "cleanup_interrupted")
43
+ cleanup_interrupted_10 = (109, "cleanup_interrupted")
44
+ cleanup_interrupted_11 = (110, "cleanup_interrupted")
45
45
  patrol_success = 114 # Cruise completed
46
46
  patrol_fail = 115 # Cruise failed
47
47
  pet_patrol_success = 116 # Pet found
@@ -159,7 +159,7 @@ class ClearWaterBoxStatus(RoborockDssCodes):
159
159
 
160
160
  okay = 0
161
161
  out_of_water = 1
162
- out_of_water_2 = 38
162
+ out_of_water_2 = (38, "out_of_water")
163
163
  refill_error = 48
164
164
 
165
165
 
@@ -168,7 +168,7 @@ class DirtyWaterBoxStatus(RoborockDssCodes):
168
168
 
169
169
  okay = 0
170
170
  full_not_installed = 1
171
- full_not_installed_2 = 39
171
+ full_not_installed_2 = (39, "full_not_installed")
172
172
  drain_error = 49
173
173
 
174
174
 
@@ -232,7 +232,7 @@ class RoborockErrorCode(RoborockEnum):
232
232
  clear_brush_exception = 42 # Check that the water filter has been correctly installed
233
233
  clear_brush_exception_2 = 43 # Positioning button error
234
234
  filter_screen_exception = 44 # Clean the dock water filter
235
- mopping_roller_2 = 45 # Wash roller may be jammed
235
+ mopping_roller_2 = (45, "mopping_roller_1") # Wash roller may be jammed
236
236
  up_water_exception = 48
237
237
  drain_water_exception = 49
238
238
  temperature_protection = 51 # Unit temperature protection
@@ -421,7 +421,7 @@ class RoborockStateCode(RoborockEnum):
421
421
  segment_cleaning = 18
422
422
  emptying_the_bin = 22 # on s7+
423
423
  washing_the_mop = 23 # on a46
424
- washing_the_mop_2 = 25
424
+ washing_the_mop_2 = (25, "washing_the_mop")
425
425
  going_to_wash_the_mop = 26 # on a46
426
426
  in_call = 28
427
427
  mapping = 29
@@ -144,11 +144,11 @@ class StatusV2(RoborockBase):
144
144
 
145
145
  @property
146
146
  def error_code_name(self) -> str | None:
147
- return self.error_code.name if self.error_code is not None else None
147
+ return self.error_code.display_name if self.error_code is not None else None
148
148
 
149
149
  @property
150
150
  def state_name(self) -> str | None:
151
- return self.state.name if self.state is not None else None
151
+ return self.state.display_name if self.state is not None else None
152
152
 
153
153
  @property
154
154
  def current_map(self) -> int | None:
@@ -19,13 +19,13 @@ documentation that informed this clean-room implementation comes from the
19
19
  https://github.com/v1b3c0d3x3r/roborock-qseries-map-bridge
20
20
  """
21
21
 
22
- import colorsys
23
22
  import io
24
23
  import math
25
24
  import statistics
26
25
  from dataclasses import dataclass, field, replace
27
26
 
28
27
  from PIL import Image
28
+ from vacuum_map_parser_base.config.color import ColorsPalette, SupportedColor
29
29
  from vacuum_map_parser_base.config.image_config import ImageConfig
30
30
  from vacuum_map_parser_base.map_data import ImageData, MapData
31
31
 
@@ -40,6 +40,7 @@ from .b01_grid_layers import (
40
40
  decompose_grid,
41
41
  )
42
42
  from .map_parser import ParsedMapData
43
+ from .room_colors import adjacency_aware_room_colors
43
44
 
44
45
  _MAP_FILE_FORMAT = "PNG"
45
46
 
@@ -655,12 +656,12 @@ class B01Q10MapParser:
655
656
  return ParsedMapData(image_content=image_bytes.getvalue(), map_data=map_data)
656
657
 
657
658
  def _render(self, packet: Q10MapPacket) -> Image.Image:
658
- """Render the Q10 grid: rooms get distinct colors, walls white, rest dark."""
659
- palette = _build_palette(packet.grid)
660
- rgb = bytearray()
659
+ """Render the Q10 grid with the V1 map palette."""
660
+ palette = _build_palette(packet.grid, packet.width)
661
+ rgba = bytearray()
661
662
  for value in packet.grid:
662
- rgb.extend(palette[value])
663
- img = Image.frombytes("RGB", (packet.width, packet.height), bytes(rgb))
663
+ rgba.extend(palette[value])
664
+ img = Image.frombytes("RGBA", (packet.width, packet.height), bytes(rgba))
664
665
  # The ss07 grid is stored top-down (row 0 = top of the home), so it is
665
666
  # rendered as-is -- unlike the V1/Q7 convention, no vertical flip.
666
667
  scale = self._config.map_scale
@@ -669,15 +670,32 @@ class B01Q10MapParser:
669
670
  return img
670
671
 
671
672
 
672
- def _build_palette(grid: bytes) -> list[tuple[int, int, int]]:
673
- """Map each grid value to an RGB color (rooms distinct, walls white)."""
674
- palette: list[tuple[int, int, int]] = [(28, 30, 38)] * 256 # default: unknown/outside
675
- room_values = sorted({v for v in set(grid) if 0 < v < _WALL_THRESHOLD})
676
- for index, value in enumerate(room_values):
677
- hue = (index * 0.139) % 1.0
678
- r, g, b = colorsys.hsv_to_rgb(hue, 0.5, 0.95)
679
- palette[value] = (int(r * 255), int(g * 255), int(b * 255))
673
+ def _opaque(color: tuple[int, ...]) -> tuple[int, int, int, int]:
674
+ """Return a palette color as RGBA."""
675
+ return (color[0], color[1], color[2], color[3] if len(color) == 4 else 255)
676
+
677
+
678
+ def _build_palette(grid: bytes, width: int) -> list[tuple[int, int, int, int]]:
679
+ """Map Q10 cells onto the same colors used by the V1 map renderer."""
680
+
681
+ def room_id(value: int) -> int | None:
682
+ return max(1, value // 4) if 0 < value < _WALL_THRESHOLD else None
683
+
684
+ colors = ColorsPalette()
685
+ room_colors = adjacency_aware_room_colors(
686
+ grid,
687
+ width,
688
+ colors,
689
+ room_id,
690
+ )
691
+ outside = (0, 0, 0, 0)
692
+ palette = [outside] * 256
693
+ for value in {value for value in grid if 0 < value < _WALL_THRESHOLD}:
694
+ palette[value] = _opaque(room_colors[max(1, value // 4)])
695
+ wall = _opaque(colors.get_color(SupportedColor.GREY_WALL))
680
696
  for value in range(_WALL_THRESHOLD, 256):
681
- palette[value] = (235, 235, 240) # walls / borders
682
- palette[0] = (28, 30, 38)
697
+ palette[value] = wall
698
+ palette[_UNSEGMENTED_FLOOR_VALUE] = _opaque(colors.get_color(SupportedColor.MAP_INSIDE))
699
+ palette[_BACKGROUND_VALUE] = outside
700
+ palette[0] = outside
683
701
  return palette
@@ -2,6 +2,7 @@
2
2
 
3
3
  import io
4
4
  import logging
5
+ import threading
5
6
  from dataclasses import dataclass, field
6
7
 
7
8
  from vacuum_map_parser_base.config.color import ColorsPalette, SupportedColor
@@ -9,10 +10,13 @@ from vacuum_map_parser_base.config.drawable import Drawable
9
10
  from vacuum_map_parser_base.config.image_config import ImageConfig
10
11
  from vacuum_map_parser_base.config.size import Size, Sizes
11
12
  from vacuum_map_parser_base.map_data import MapData
13
+ from vacuum_map_parser_roborock.image_parser import RoborockImageParser
12
14
  from vacuum_map_parser_roborock.map_data_parser import RoborockMapDataParser
13
15
 
14
16
  from roborock.exceptions import RoborockException
15
17
 
18
+ from .room_colors import adjacency_aware_room_colors
19
+
16
20
  _LOGGER = logging.getLogger(__name__)
17
21
 
18
22
  DEFAULT_DRAWABLES = {
@@ -98,6 +102,52 @@ class MapParser:
98
102
  return ParsedMapData(image_content=img_byte_arr.getvalue(), map_data=parsed_map)
99
103
 
100
104
 
105
+ class _AdjacencyAwareRoborockImageParser(RoborockImageParser):
106
+ """Apply the shared adjacency color policy to V1 room cells."""
107
+
108
+ def __init__(
109
+ self,
110
+ palette: ColorsPalette,
111
+ image_config: ImageConfig,
112
+ *,
113
+ recolor_rooms: bool = True,
114
+ ) -> None:
115
+ super().__init__(palette, image_config)
116
+ self._room_palette = palette
117
+ self._base_room_colors = palette.cached_room_colors.copy()
118
+ self._recolor_rooms = recolor_rooms
119
+ self._palette_lock = threading.Lock()
120
+
121
+ def parse(
122
+ self,
123
+ raw_data: bytes,
124
+ width: int,
125
+ height: int,
126
+ carpet_map: set[int] | None,
127
+ removed_map: set[int] | None = None,
128
+ ):
129
+ """Assign non-conflicting room colors before the V1 image pass."""
130
+ with self._palette_lock:
131
+ # cached_room_colors is a read-only property, so reset its dict in place.
132
+ cached_room_colors = self._room_palette.cached_room_colors
133
+ cached_room_colors.clear()
134
+ cached_room_colors.update(self._base_room_colors)
135
+
136
+ if self._recolor_rooms:
137
+
138
+ def room_id(value: int) -> int | None:
139
+ if value in (self.MAP_OUTSIDE, self.MAP_WALL, self.MAP_INSIDE, self.MAP_SCAN):
140
+ return None
141
+ return self._get_room_number(value) if value & 0x07 == 0x07 else None
142
+
143
+ room_colors = adjacency_aware_room_colors(raw_data, width, self._room_palette, room_id)
144
+ for number, color in room_colors.items():
145
+ # ColorsPalette caches both forms for get_room_color(str | int).
146
+ cached_room_colors[number] = color
147
+ cached_room_colors[str(number)] = color
148
+ return super().parse(raw_data, width, height, carpet_map, removed_map)
149
+
150
+
101
151
  def _create_map_data_parser(config: MapParserConfig) -> RoborockMapDataParser:
102
152
  """Create a RoborockMapDataParser based on the config entry."""
103
153
  color_dicts = {}
@@ -114,10 +164,18 @@ def _create_map_data_parser(config: MapParserConfig) -> RoborockMapDataParser:
114
164
  if not config.show_rooms:
115
165
  room_colors = {str(x): (0, 0, 0, 0) for x in range(1, 32)}
116
166
 
117
- return RoborockMapDataParser(
118
- ColorsPalette(color_dicts, room_colors),
167
+ palette = ColorsPalette(color_dicts, room_colors)
168
+ image_config = ImageConfig(scale=config.map_scale)
169
+ parser = RoborockMapDataParser(
170
+ palette,
119
171
  Sizes({k: v * config.map_scale for k, v in Sizes.SIZES.items() if k != Size.MOP_PATH_WIDTH}),
120
172
  config.drawables,
121
- ImageConfig(scale=config.map_scale),
173
+ image_config,
122
174
  [],
123
175
  )
176
+ parser._image_parser = _AdjacencyAwareRoborockImageParser(
177
+ palette,
178
+ image_config,
179
+ recolor_rooms=config.show_rooms,
180
+ )
181
+ return parser
@@ -0,0 +1,59 @@
1
+ """Deterministic room colors that keep adjacent segments distinguishable."""
2
+
3
+ from collections.abc import Callable, Sequence
4
+
5
+ from vacuum_map_parser_base.config.color import Color, ColorsPalette
6
+
7
+ RoomIdFromCell = Callable[[int], int | None]
8
+
9
+
10
+ def adjacency_aware_room_colors(
11
+ grid: Sequence[int],
12
+ width: int,
13
+ palette: ColorsPalette,
14
+ room_id_from_cell: RoomIdFromCell,
15
+ ) -> dict[int, Color]:
16
+ """Return room colors, changing only adjacent same-color conflicts.
17
+
18
+ Room IDs remain the stable preference, matching the existing V1 palette.
19
+ When two rooms sharing an edge resolve to the same RGB value, the
20
+ higher-numbered room receives the first palette color not already used by
21
+ one of its colored neighbors.
22
+ """
23
+ if width <= 0:
24
+ return {}
25
+
26
+ room_ids: set[int] = set()
27
+ neighbors: dict[int, set[int]] = {}
28
+ for index, value in enumerate(grid):
29
+ room_id = room_id_from_cell(value)
30
+ if room_id is None:
31
+ continue
32
+ room_ids.add(room_id)
33
+ neighbors.setdefault(room_id, set())
34
+
35
+ for neighbor_index in (index - 1 if index % width else -1, index - width):
36
+ if neighbor_index < 0:
37
+ continue
38
+ neighbor_id = room_id_from_cell(grid[neighbor_index])
39
+ if neighbor_id is None or neighbor_id == room_id:
40
+ continue
41
+ neighbors[room_id].add(neighbor_id)
42
+ neighbors.setdefault(neighbor_id, set()).add(room_id)
43
+
44
+ candidates: list[Color] = []
45
+ for palette_id in map(int, ColorsPalette.ROOM_COLORS):
46
+ color = palette.get_room_color(palette_id)
47
+ if color not in candidates:
48
+ candidates.append(color)
49
+
50
+ assigned: dict[int, Color] = {}
51
+ for room_id in sorted(room_ids):
52
+ preferred = palette.get_room_color(room_id)
53
+ neighbor_colors = {assigned[neighbor] for neighbor in neighbors[room_id] if neighbor in assigned}
54
+ assigned[room_id] = (
55
+ preferred
56
+ if preferred not in neighbor_colors
57
+ else next((color for color in candidates if color not in neighbor_colors), preferred)
58
+ )
59
+ return assigned
File without changes