python-roborock 7.4.3__tar.gz → 7.6.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 (135) hide show
  1. {python_roborock-7.4.3 → python_roborock-7.6.0}/PKG-INFO +1 -1
  2. {python_roborock-7.4.3 → python_roborock-7.6.0}/pyproject.toml +1 -1
  3. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q7/__init__.py +10 -0
  4. python_roborock-7.6.0/roborock/map/b01_map_parser.py +383 -0
  5. python_roborock-7.6.0/roborock/map/proto/b01_scmap.proto +151 -0
  6. python_roborock-7.6.0/roborock/map/proto/b01_scmap_pb2.py +72 -0
  7. python_roborock-7.4.3/roborock/map/b01_map_parser.py +0 -133
  8. python_roborock-7.4.3/roborock/map/proto/b01_scmap.proto +0 -70
  9. python_roborock-7.4.3/roborock/map/proto/b01_scmap_pb2.py +0 -48
  10. {python_roborock-7.4.3 → python_roborock-7.6.0}/.gitignore +0 -0
  11. {python_roborock-7.4.3 → python_roborock-7.6.0}/LICENSE +0 -0
  12. {python_roborock-7.4.3 → python_roborock-7.6.0}/README.md +0 -0
  13. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/__init__.py +0 -0
  14. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/broadcast_protocol.py +0 -0
  15. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/callbacks.py +0 -0
  16. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/cli.py +0 -0
  17. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/const.py +0 -0
  18. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/__init__.py +0 -0
  19. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/b01_q10/__init__.py +0 -0
  20. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
  21. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
  22. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/b01_q7/__init__.py +0 -0
  23. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
  24. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
  25. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/code_mappings.py +0 -0
  26. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/containers.py +0 -0
  27. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/dyad/__init__.py +0 -0
  28. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/dyad/dyad_code_mappings.py +0 -0
  29. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/dyad/dyad_containers.py +0 -0
  30. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/mower/__init__.py +0 -0
  31. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/mower/mower_code_mappings.py +0 -0
  32. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/mower/mower_containers.py +0 -0
  33. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/v1/__init__.py +0 -0
  34. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/v1/v1_clean_modes.py +0 -0
  35. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/v1/v1_code_mappings.py +0 -0
  36. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/v1/v1_containers.py +0 -0
  37. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/zeo/__init__.py +0 -0
  38. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/zeo/zeo_code_mappings.py +0 -0
  39. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/data/zeo/zeo_containers.py +0 -0
  40. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/device_features.py +0 -0
  41. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/README.md +0 -0
  42. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/__init__.py +0 -0
  43. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/cache.py +0 -0
  44. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/device.py +0 -0
  45. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/device_manager.py +0 -0
  46. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/file_cache.py +0 -0
  47. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/rpc/__init__.py +0 -0
  48. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/rpc/a01_channel.py +0 -0
  49. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/rpc/b01_q10_channel.py +0 -0
  50. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/rpc/b01_q7_channel.py +0 -0
  51. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/rpc/v1_channel.py +0 -0
  52. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/__init__.py +0 -0
  53. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/a01/__init__.py +0 -0
  54. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/a01/device_feature.py +0 -0
  55. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/__init__.py +0 -0
  56. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/__init__.py +0 -0
  57. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/button_light.py +0 -0
  58. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/child_lock.py +0 -0
  59. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/clean_history.py +0 -0
  60. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/command.py +0 -0
  61. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/common.py +0 -0
  62. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/consumable.py +0 -0
  63. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/do_not_disturb.py +0 -0
  64. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/dust_collection.py +0 -0
  65. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/map.py +0 -0
  66. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/maps.py +0 -0
  67. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/network_info.py +0 -0
  68. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/remote.py +0 -0
  69. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/status.py +0 -0
  70. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/vacuum.py +0 -0
  71. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q10/volume.py +0 -0
  72. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q7/clean_summary.py +0 -0
  73. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q7/map.py +0 -0
  74. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/b01/q7/map_content.py +0 -0
  75. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/common.py +0 -0
  76. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/traits_mixin.py +0 -0
  77. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/__init__.py +0 -0
  78. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/child_lock.py +0 -0
  79. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
  80. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/command.py +0 -0
  81. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/common.py +0 -0
  82. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/consumeable.py +0 -0
  83. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/device_features.py +0 -0
  84. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
  85. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
  86. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/flow_led_status.py +0 -0
  87. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/home.py +0 -0
  88. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/led_status.py +0 -0
  89. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/map_content.py +0 -0
  90. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/maps.py +0 -0
  91. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/network_info.py +0 -0
  92. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/obstacle_photos.py +0 -0
  93. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/rooms.py +0 -0
  94. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/routines.py +0 -0
  95. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
  96. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/status.py +0 -0
  97. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
  98. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/volume.py +0 -0
  99. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
  100. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/transport/__init__.py +0 -0
  101. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/transport/channel.py +0 -0
  102. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/transport/local_channel.py +0 -0
  103. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/devices/transport/mqtt_channel.py +0 -0
  104. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/diagnostics.py +0 -0
  105. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/exceptions.py +0 -0
  106. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/map/__init__.py +0 -0
  107. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/map/b01_grid_layers.py +0 -0
  108. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/map/b01_q10_map_parser.py +0 -0
  109. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/map/b01_q10_overlays.py +0 -0
  110. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/map/b01_q10_render.py +0 -0
  111. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/map/map_parser.py +0 -0
  112. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/map/proto/__init__.py +0 -0
  113. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/map/room_colors.py +0 -0
  114. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/mqtt/__init__.py +0 -0
  115. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/mqtt/health_manager.py +0 -0
  116. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/mqtt/roborock_session.py +0 -0
  117. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/mqtt/session.py +0 -0
  118. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/protocol.py +0 -0
  119. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/protocols/__init__.py +0 -0
  120. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/protocols/a01_protocol.py +0 -0
  121. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/protocols/b01_q10_protocol.py +0 -0
  122. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/protocols/b01_q7_protocol.py +0 -0
  123. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/protocols/v1_protocol.py +0 -0
  124. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/py.typed +0 -0
  125. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/roborock_message.py +0 -0
  126. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/roborock_typing.py +0 -0
  127. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/testing/__init__.py +0 -0
  128. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/testing/a01_simulator.py +0 -0
  129. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/testing/b01_q10_simulator.py +0 -0
  130. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/testing/channel.py +0 -0
  131. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/testing/cloud.py +0 -0
  132. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/testing/simulator.py +0 -0
  133. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/testing/v1_simulator.py +0 -0
  134. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/util.py +0 -0
  135. {python_roborock-7.4.3 → python_roborock-7.6.0}/roborock/web_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-roborock
3
- Version: 7.4.3
3
+ Version: 7.6.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 = "7.4.3"
3
+ version = "7.6.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"
@@ -131,6 +131,16 @@ class Q7PropertiesApi(Trait):
131
131
  """Enable or disable the child lock."""
132
132
  await self.set_prop(RoborockB01Props.CHILD_LOCK, int(enabled))
133
133
 
134
+ async def set_dust_collection(self, enabled: bool) -> None:
135
+ """Enable or disable automatic dust collection at the dock."""
136
+ await self.set_prop(RoborockB01Props.DUST_AUTO_STATE, int(enabled))
137
+
138
+ async def set_dust_collection_frequency(self, frequency: int) -> None:
139
+ """Set how often the dock auto-empties, in cleans per emptying (1 = every clean)."""
140
+ if frequency < 1:
141
+ raise ValueError(f"frequency must be a positive number of cleans, got {frequency}")
142
+ await self.set_prop(RoborockB01Props.DUST_FREQUENCY, frequency)
143
+
134
144
  async def set_do_not_disturb(self, enabled: bool, begin_time: int, end_time: int) -> None:
135
145
  """Configure do-not-disturb.
136
146
 
@@ -0,0 +1,383 @@
1
+ """Module for parsing B01/Q7 map content.
2
+
3
+ The inner SCMap blob is parsed with protobuf messages generated from
4
+ `roborock/map/proto/b01_scmap.proto`.
5
+ """
6
+
7
+ import io
8
+ import math
9
+ from collections import deque
10
+ from dataclasses import dataclass
11
+
12
+ from google.protobuf.message import DecodeError
13
+ from PIL import Image
14
+ from vacuum_map_parser_base.config.color import ColorsPalette, SupportedColor
15
+ from vacuum_map_parser_base.config.drawable import Drawable
16
+ from vacuum_map_parser_base.config.image_config import ImageConfig
17
+ from vacuum_map_parser_base.map_data import Area, ImageData, MapData, Path, Point, Room
18
+
19
+ from roborock.exceptions import RoborockException
20
+ from roborock.map.proto.b01_scmap_pb2 import RobotMap # type: ignore[attr-defined]
21
+
22
+ from .map_parser import MapParserConfig, ParsedMapData, _create_image_generator
23
+ from .room_colors import adjacency_aware_room_colors
24
+
25
+ _MAP_FILE_FORMAT = "PNG"
26
+
27
+ _FLOOR = 127
28
+ _WALL = 128
29
+
30
+ _B01_DRAWABLES = [
31
+ Drawable.CHARGER,
32
+ Drawable.NO_GO_AREAS,
33
+ Drawable.PATH,
34
+ Drawable.ROOM_NAMES,
35
+ Drawable.VACUUM_POSITION,
36
+ ]
37
+
38
+
39
+ @dataclass
40
+ class B01MapParserConfig:
41
+ """Configuration for the B01/Q7 map parser."""
42
+
43
+ map_scale: int = 4
44
+ """Scale factor for the rendered map image."""
45
+
46
+
47
+ class B01MapParser:
48
+ """Decoder/parser for B01/Q7 SCMap payloads."""
49
+
50
+ def __init__(self, config: B01MapParserConfig | None = None) -> None:
51
+ self._config = config or B01MapParserConfig()
52
+
53
+ def parse(self, payload: bytes) -> ParsedMapData:
54
+ """Parse an inflated SCMap payload and return a PNG + MapData."""
55
+ parsed = _parse_scmap_payload(payload)
56
+ size_x, size_y, grid = _extract_grid(parsed)
57
+ room_names = _extract_room_names(parsed)
58
+
59
+ room_pixels = _assign_room_pixels(parsed, grid, size_x=size_x, size_y=size_y)
60
+ carpet_pixels = _carpet_pixel_indices(parsed, grid, size_x=size_x, size_y=size_y)
61
+ image = _render_occupancy_image(
62
+ grid, room_pixels, carpet_pixels, size_x=size_x, size_y=size_y, scale=self._config.map_scale
63
+ )
64
+
65
+ map_data = MapData()
66
+ map_data.image = ImageData(
67
+ size=size_x * size_y,
68
+ top=0,
69
+ left=0,
70
+ height=size_y,
71
+ width=size_x,
72
+ image_config=ImageConfig(scale=self._config.map_scale),
73
+ data=image,
74
+ # Overlay points are stored in the rendered image's top-down pixel
75
+ # space. ImageDimensions applies V1's bottom-up flip before drawing,
76
+ # so this adapter cancels it (same approach as the Q10 renderer).
77
+ img_transformation=lambda p: Point(p.x, size_y - p.y - 1, p.a),
78
+ )
79
+ if room_names:
80
+ map_data.additional_parameters["room_names"] = room_names
81
+
82
+ projector = _WorldToPixel(parsed)
83
+ has_drawables = _place_poses(map_data, parsed, projector)
84
+ map_data.rooms = _extract_rooms(parsed, projector, room_names)
85
+ has_drawables = has_drawables or bool(map_data.rooms)
86
+ if carpet_pixels:
87
+ # Same contract as the Q10 parser: flat top-down grid indices.
88
+ map_data.carpet_map = {(size_y - 1 - index // size_x) * size_x + index % size_x for index in carpet_pixels}
89
+
90
+ if has_drawables:
91
+ generator = _create_image_generator(
92
+ MapParserConfig(map_scale=self._config.map_scale),
93
+ drawables=_B01_DRAWABLES,
94
+ )
95
+ generator.draw_map(map_data)
96
+ image = map_data.image.data
97
+
98
+ image_bytes = io.BytesIO()
99
+ image.save(image_bytes, format=_MAP_FILE_FORMAT)
100
+
101
+ return ParsedMapData(
102
+ image_content=image_bytes.getvalue(),
103
+ map_data=map_data,
104
+ )
105
+
106
+
107
+ def parse_map_type(payload: bytes) -> int:
108
+ """Return the SCMap frame's map type without rendering the image.
109
+
110
+ Type 0 is the live map of the current cleaning run, other types are
111
+ historic or saved maps.
112
+ """
113
+ return _parse_scmap_payload(payload).mapType
114
+
115
+
116
+ def _parse_scmap_payload(payload: bytes) -> RobotMap:
117
+ """Parse inflated SCMap bytes into a generated protobuf message."""
118
+ parsed = RobotMap()
119
+ try:
120
+ parsed.ParseFromString(payload)
121
+ except DecodeError as err:
122
+ raise RoborockException("Failed to parse B01 SCMap") from err
123
+ return parsed
124
+
125
+
126
+ def _extract_grid(parsed: RobotMap) -> tuple[int, int, bytes]:
127
+ if not parsed.HasField("mapHead") or not parsed.HasField("mapData"):
128
+ raise RoborockException("Failed to parse B01 map header/grid")
129
+
130
+ size_x = parsed.mapHead.sizeX if parsed.mapHead.HasField("sizeX") else 0
131
+ size_y = parsed.mapHead.sizeY if parsed.mapHead.HasField("sizeY") else 0
132
+ if not size_x or not size_y or not parsed.mapData.HasField("mapData"):
133
+ raise RoborockException("Failed to parse B01 map header/grid")
134
+
135
+ map_data = parsed.mapData.mapData
136
+ expected_len = size_x * size_y
137
+ if len(map_data) < expected_len:
138
+ raise RoborockException("B01 map data shorter than expected dimensions")
139
+
140
+ return size_x, size_y, map_data[:expected_len]
141
+
142
+
143
+ class _WorldToPixel:
144
+ """Project SCMap world coordinates (meters) into top-down image pixels."""
145
+
146
+ def __init__(self, parsed: RobotMap) -> None:
147
+ head = parsed.mapHead
148
+ self._min_x = head.minX
149
+ self._min_y = head.minY
150
+ self._max_x = head.maxX
151
+ self._max_y = head.maxY
152
+ self._resolution = head.resolution or 0.05
153
+ self._size_y = head.sizeY
154
+
155
+ def in_bounds(self, x: float, y: float) -> bool:
156
+ """Whether a world point lies inside the map (rejects placeholder poses)."""
157
+ return self._min_x <= x <= self._max_x and self._min_y <= y <= self._max_y
158
+
159
+ def to_pixel(self, x: float, y: float) -> tuple[float, float]:
160
+ """World meters to top-down image pixel coordinates."""
161
+ px = (x - self._min_x) / self._resolution
162
+ py = self._size_y - 1 - (y - self._min_y) / self._resolution
163
+ return px, py
164
+
165
+
166
+ def _place_poses(map_data: MapData, parsed: RobotMap, projector: _WorldToPixel) -> bool:
167
+ """Populate charger, robot position and path from the decoded SCMap."""
168
+ has_drawables = False
169
+
170
+ if parsed.HasField("chargeStation") and projector.in_bounds(parsed.chargeStation.x, parsed.chargeStation.y):
171
+ px, py = projector.to_pixel(parsed.chargeStation.x, parsed.chargeStation.y)
172
+ map_data.charger = Point(px, py, math.degrees(parsed.chargeStation.phi))
173
+ has_drawables = True
174
+
175
+ if parsed.HasField("currentPose") and projector.in_bounds(parsed.currentPose.x, parsed.currentPose.y):
176
+ px, py = projector.to_pixel(parsed.currentPose.x, parsed.currentPose.y)
177
+ map_data.vacuum_position = Point(px, py, math.degrees(parsed.currentPose.phi))
178
+ has_drawables = True
179
+ elif map_data.charger is not None:
180
+ # A saved map carries no live pose; show the robot at its dock.
181
+ map_data.vacuum_position = Point(map_data.charger.x, map_data.charger.y, map_data.charger.a)
182
+
183
+ areas = [
184
+ Area(*(coord for point in area.points for coord in projector.to_pixel(point.x, point.y)))
185
+ for area in parsed.areaInfo
186
+ if len(area.points) == 4
187
+ ]
188
+ if areas:
189
+ # areaInfo type semantics are not yet mapped per zone kind; render all
190
+ # restricted areas through the no-go drawable for now.
191
+ map_data.no_go_areas = areas
192
+ has_drawables = True
193
+
194
+ if parsed.HasField("historyPose"):
195
+ pixels = [
196
+ Point(*projector.to_pixel(point.x, point.y))
197
+ for point in parsed.historyPose.points
198
+ if projector.in_bounds(point.x, point.y)
199
+ ]
200
+ if pixels:
201
+ map_data.path = Path(len(pixels), 1, 0, [pixels])
202
+ has_drawables = True
203
+
204
+ return has_drawables
205
+
206
+
207
+ def _extract_rooms(parsed: RobotMap, projector: _WorldToPixel, room_names: dict[int, str]) -> dict[int, Room] | None:
208
+ """Build room bounding boxes (image-pixel space) from room outlines."""
209
+ rooms: dict[int, Room] = {}
210
+ label_positions = {
211
+ room.roomId: projector.to_pixel(room.roomNamePost.x, room.roomNamePost.y)
212
+ for room in parsed.roomDataInfo
213
+ if room.HasField("roomNamePost")
214
+ }
215
+ size_y = parsed.mapHead.sizeY
216
+ for outline in parsed.roomOutline:
217
+ if not outline.points:
218
+ continue
219
+ room_id = outline.roomId
220
+ # Outline points are top-down after the same vertical flip as the raster.
221
+ xs = [point.x for point in outline.points]
222
+ ys = [size_y - 1 - point.y for point in outline.points]
223
+ pos = label_positions.get(room_id)
224
+ rooms[room_id] = Room(
225
+ min(xs),
226
+ min(ys),
227
+ max(xs),
228
+ max(ys),
229
+ room_id,
230
+ room_names.get(room_id),
231
+ pos[0] if pos else None,
232
+ pos[1] if pos else None,
233
+ )
234
+ return rooms or None
235
+
236
+
237
+ def _extract_room_names(parsed: RobotMap) -> dict[int, str]:
238
+ # Expose room id/name mapping without inventing room geometry/polygons.
239
+ room_names: dict[int, str] = {}
240
+ for room in parsed.roomDataInfo:
241
+ if room.HasField("roomId"):
242
+ room_id = room.roomId
243
+ room_names[room_id] = room.roomName if room.HasField("roomName") else f"Room {room_id}"
244
+ return room_names
245
+
246
+
247
+ def _assign_room_pixels(parsed: RobotMap, grid: bytes, *, size_x: int, size_y: int) -> bytearray:
248
+ """Assign a room id to each floor pixel by flood-filling from room labels.
249
+
250
+ The grid itself carries no room ids; room geometry arrives as boundary
251
+ pixel chains (``roomOutline``). Each room is filled from its label
252
+ position, bounded by walls and by any room's outline pixels, all in the
253
+ raw (bottom-up) grid space.
254
+ """
255
+ assignment = bytearray(len(grid))
256
+ outlines = {outline.roomId: outline for outline in parsed.roomOutline if outline.points}
257
+ if not outlines:
258
+ return assignment
259
+
260
+ barrier = {
261
+ point.y * size_x + point.x
262
+ for outline in outlines.values()
263
+ for point in outline.points
264
+ if point.x < size_x and point.y < size_y
265
+ }
266
+ floor_count = grid.count(_FLOOR)
267
+ # ponytail: leak guard — a gapped outline would flood the whole floor, so a
268
+ # fill larger than half of it is discarded instead of tracing outline gaps.
269
+ max_fill = floor_count // 2
270
+
271
+ head = parsed.mapHead
272
+ label_positions = {
273
+ room.roomId: (
274
+ int((room.roomNamePost.x - head.minX) / head.resolution),
275
+ int((room.roomNamePost.y - head.minY) / head.resolution),
276
+ )
277
+ for room in parsed.roomDataInfo
278
+ if room.HasField("roomNamePost")
279
+ }
280
+
281
+ for room_id, outline in outlines.items():
282
+ seed = label_positions.get(room_id)
283
+ if seed is None:
284
+ continue
285
+ col, row = seed
286
+ start = row * size_x + col
287
+ if not (0 <= col < size_x and 0 <= row < size_y) or grid[start] != _FLOOR:
288
+ continue
289
+ filled: list[int] = []
290
+ queue = deque([start])
291
+ seen = {start}
292
+ while queue and len(filled) <= max_fill:
293
+ index = queue.popleft()
294
+ filled.append(index)
295
+ for neighbor in (index - 1, index + 1, index - size_x, index + size_x):
296
+ if (
297
+ 0 <= neighbor < len(grid)
298
+ and neighbor not in seen
299
+ and grid[neighbor] == _FLOOR
300
+ and assignment[neighbor] == 0
301
+ and neighbor not in barrier
302
+ # Row-wrap guard for the horizontal neighbors.
303
+ and abs(neighbor % size_x - index % size_x) <= 1
304
+ ):
305
+ seen.add(neighbor)
306
+ queue.append(neighbor)
307
+ if len(filled) > max_fill:
308
+ continue
309
+ for index in filled:
310
+ assignment[index] = room_id
311
+ # Color the room's own boundary ring too where it sits on floor.
312
+ for point in outline.points:
313
+ index = point.y * size_x + point.x
314
+ if index < len(grid) and grid[index] == _FLOOR and assignment[index] == 0:
315
+ assignment[index] = room_id
316
+
317
+ return assignment
318
+
319
+
320
+ def _carpet_pixel_indices(parsed: RobotMap, grid: bytes, *, size_x: int, size_y: int) -> set[int]:
321
+ """Raw-grid indices of floor pixels covered by enabled carpets."""
322
+ head = parsed.mapHead
323
+ resolution = head.resolution or 0.05
324
+ indices: set[int] = set()
325
+ for carpet in parsed.carpetInfo:
326
+ if not carpet.points or (carpet.HasField("enabled") and not carpet.enabled):
327
+ continue
328
+ cols = [int((point.x - head.minX) / resolution) for point in carpet.points]
329
+ rows = [int((point.y - head.minY) / resolution) for point in carpet.points]
330
+ for row in range(max(min(rows), 0), min(max(rows), size_y - 1) + 1):
331
+ for col in range(max(min(cols), 0), min(max(cols), size_x - 1) + 1):
332
+ index = row * size_x + col
333
+ if grid[index] == _FLOOR:
334
+ indices.add(index)
335
+ return indices
336
+
337
+
338
+ def _render_occupancy_image(
339
+ grid: bytes, room_pixels: bytearray, carpet_pixels: set[int], *, size_x: int, size_y: int, scale: int
340
+ ) -> Image.Image:
341
+ """Render the B01 occupancy grid with per-room colors."""
342
+
343
+ colors = ColorsPalette()
344
+ room_colors = {
345
+ room_id: tuple(color[:3]) + (255,)
346
+ for room_id, color in adjacency_aware_room_colors(
347
+ room_pixels, size_x, colors, lambda value: value or None
348
+ ).items()
349
+ }
350
+
351
+ # The observed occupancy grid contains only:
352
+ # - 0: outside/unknown
353
+ # - 127: floor/free
354
+ # - 128: wall/obstacle
355
+ # Same V1 palette roles as the Q10 renderer: transparent outside, grey
356
+ # walls/obstacles, MAP_INSIDE for floor not assigned to any room.
357
+ outside = (0, 0, 0, 0)
358
+ floor = tuple(colors.get_color(SupportedColor.MAP_INSIDE)[:3]) + (255,)
359
+ base_colors = {
360
+ 0: outside,
361
+ _FLOOR: floor,
362
+ _WALL: tuple(colors.get_color(SupportedColor.GREY_WALL)[:3]) + (255,),
363
+ }
364
+
365
+ rgba = bytearray()
366
+ for index, value in enumerate(grid):
367
+ if value == _FLOOR and (room_id := room_pixels[index]):
368
+ color = room_colors.get(room_id, floor)
369
+ else:
370
+ color = base_colors.get(value, floor)
371
+ if index in carpet_pixels and (index // size_x + index % size_x) % 2 == 0:
372
+ # Checkerboard stipple, like the V1 carpet texture.
373
+ color = tuple(min(channel + 60, 255) for channel in color[:3]) + (255,)
374
+ rgba.extend(color)
375
+
376
+ # RGBA so the shared V1 ImageGenerator can alpha-composite overlay glyphs.
377
+ img = Image.frombytes("RGBA", (size_x, size_y), bytes(rgba))
378
+ img = img.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
379
+
380
+ if scale > 1:
381
+ img = img.resize((size_x * scale, size_y * scale), resample=Image.Resampling.NEAREST)
382
+
383
+ return img
@@ -0,0 +1,151 @@
1
+ // Checked-in B01/Q7 SCMap schema for the generated runtime protobuf module.
2
+ // Regenerate the checked-in Python module after edits with:
3
+ // python -m grpc_tools.protoc -I. --python_out=. roborock/map/proto/b01_scmap.proto
4
+ // The generated file `b01_scmap_pb2.py` is checked in for runtime use and should
5
+ // not be edited by hand.
6
+ syntax = "proto2";
7
+
8
+ package b01.scmap;
9
+
10
+ message DevicePointInfo {
11
+ optional float x = 1;
12
+ optional float y = 2;
13
+ }
14
+
15
+ message MapBoundaryInfo {
16
+ optional string mapMd5 = 1;
17
+ optional uint32 vMinX = 2;
18
+ optional uint32 vMaxX = 3;
19
+ optional uint32 vMinY = 4;
20
+ optional uint32 vMaxY = 5;
21
+ }
22
+
23
+ message MapExtInfo {
24
+ optional uint32 taskBeginDate = 1;
25
+ optional uint32 mapUploadDate = 2;
26
+ optional uint32 mapValid = 3;
27
+ optional uint32 radian = 4;
28
+ optional uint32 force = 5;
29
+ optional uint32 cleanPath = 6;
30
+ optional MapBoundaryInfo boudaryInfo = 7;
31
+ optional uint32 mapVersion = 8;
32
+ optional uint32 mapValueType = 9;
33
+ }
34
+
35
+ message MapHeadInfo {
36
+ optional uint32 mapHeadId = 1;
37
+ optional uint32 sizeX = 2;
38
+ optional uint32 sizeY = 3;
39
+ optional float minX = 4;
40
+ optional float minY = 5;
41
+ optional float maxX = 6;
42
+ optional float maxY = 7;
43
+ optional float resolution = 8;
44
+ }
45
+
46
+ message MapDataInfo {
47
+ optional bytes mapData = 1;
48
+ }
49
+
50
+ message MapInfo {
51
+ optional uint32 mapId = 1;
52
+ optional string mapName = 2;
53
+ }
54
+
55
+ message DevicePoseDataInfo {
56
+ optional uint32 update = 1;
57
+ optional float x = 2;
58
+ optional float y = 3;
59
+ }
60
+
61
+ message DeviceHistoryPoseInfo {
62
+ optional uint32 poseId = 1;
63
+ repeated DevicePoseDataInfo points = 2;
64
+ }
65
+
66
+ message DevicePoseInfo {
67
+ optional float x = 1;
68
+ optional float y = 2;
69
+ optional float phi = 3;
70
+ }
71
+
72
+ message DeviceCurrentPoseInfo {
73
+ optional uint32 poseId = 1;
74
+ optional uint32 update = 2;
75
+ optional float x = 3;
76
+ optional float y = 4;
77
+ optional float phi = 5;
78
+ }
79
+
80
+ message DeviceAreaDataInfo {
81
+ optional uint32 status = 1;
82
+ optional uint32 type = 2;
83
+ optional uint32 areaIndex = 3;
84
+ repeated DevicePointInfo points = 4;
85
+ }
86
+
87
+ message RoomMatrixInfo {
88
+ optional bytes matrix = 1;
89
+ }
90
+
91
+ message RoomOutlinePointInfo {
92
+ optional uint32 x = 1;
93
+ optional uint32 y = 2;
94
+ optional uint32 value = 3;
95
+ }
96
+
97
+ message RoomBorderPointInfo {
98
+ optional uint32 x = 1;
99
+ optional uint32 y = 2;
100
+ }
101
+
102
+ message RoomBorderInfo {
103
+ repeated RoomBorderPointInfo points = 1;
104
+ repeated uint32 roomIds = 2;
105
+ }
106
+
107
+ message RoomOutlineInfo {
108
+ optional uint32 roomId = 1;
109
+ repeated RoomOutlinePointInfo points = 2;
110
+ repeated RoomBorderInfo borders = 3;
111
+ }
112
+
113
+ message CarpetDataInfo {
114
+ optional uint32 carpetId = 1;
115
+ optional uint32 type = 2;
116
+ optional uint32 unknown3 = 3;
117
+ repeated DevicePointInfo points = 4;
118
+ optional uint32 enabled = 6;
119
+ optional uint32 unknown7 = 7;
120
+ optional uint32 unknown8 = 8;
121
+ }
122
+
123
+ message RoomDataInfo {
124
+ optional uint32 roomId = 1;
125
+ optional string roomName = 2;
126
+ optional uint32 roomTypeId = 3;
127
+ optional uint32 meterialId = 4;
128
+ optional uint32 cleanState = 5;
129
+ optional uint32 roomClean = 6;
130
+ optional uint32 roomCleanIndex = 7;
131
+ optional DevicePointInfo roomNamePost = 8;
132
+ optional uint32 colorId = 10;
133
+ optional uint32 floor_direction = 11;
134
+ optional uint32 global_seq = 12;
135
+ }
136
+
137
+ message RobotMap {
138
+ optional uint32 mapType = 1;
139
+ optional MapExtInfo mapExtInfo = 2;
140
+ optional MapHeadInfo mapHead = 3;
141
+ optional MapDataInfo mapData = 4;
142
+ repeated MapInfo mapInfo = 5;
143
+ optional DeviceHistoryPoseInfo historyPose = 6;
144
+ optional DevicePoseInfo chargeStation = 7;
145
+ optional DeviceCurrentPoseInfo currentPose = 8;
146
+ repeated DeviceAreaDataInfo areaInfo = 9;
147
+ repeated RoomDataInfo roomDataInfo = 12;
148
+ optional RoomMatrixInfo roomMatrix = 13;
149
+ repeated RoomOutlineInfo roomOutline = 14;
150
+ repeated CarpetDataInfo carpetInfo = 20;
151
+ }
@@ -0,0 +1,72 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: roborock/map/proto/b01_scmap.proto
5
+ # Protobuf Python Version: 6.31.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 31,
16
+ 1,
17
+ '',
18
+ 'roborock/map/proto/b01_scmap.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+
26
+
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"roborock/map/proto/b01_scmap.proto\x12\tb01.scmap\"\'\n\x0f\x44\x65vicePointInfo\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\"]\n\x0fMapBoundaryInfo\x12\x0e\n\x06mapMd5\x18\x01 \x01(\t\x12\r\n\x05vMinX\x18\x02 \x01(\r\x12\r\n\x05vMaxX\x18\x03 \x01(\r\x12\r\n\x05vMinY\x18\x04 \x01(\r\x12\r\n\x05vMaxY\x18\x05 \x01(\r\"\xd9\x01\n\nMapExtInfo\x12\x15\n\rtaskBeginDate\x18\x01 \x01(\r\x12\x15\n\rmapUploadDate\x18\x02 \x01(\r\x12\x10\n\x08mapValid\x18\x03 \x01(\r\x12\x0e\n\x06radian\x18\x04 \x01(\r\x12\r\n\x05\x66orce\x18\x05 \x01(\r\x12\x11\n\tcleanPath\x18\x06 \x01(\r\x12/\n\x0b\x62oudaryInfo\x18\x07 \x01(\x0b\x32\x1a.b01.scmap.MapBoundaryInfo\x12\x12\n\nmapVersion\x18\x08 \x01(\r\x12\x14\n\x0cmapValueType\x18\t \x01(\r\"\x8a\x01\n\x0bMapHeadInfo\x12\x11\n\tmapHeadId\x18\x01 \x01(\r\x12\r\n\x05sizeX\x18\x02 \x01(\r\x12\r\n\x05sizeY\x18\x03 \x01(\r\x12\x0c\n\x04minX\x18\x04 \x01(\x02\x12\x0c\n\x04minY\x18\x05 \x01(\x02\x12\x0c\n\x04maxX\x18\x06 \x01(\x02\x12\x0c\n\x04maxY\x18\x07 \x01(\x02\x12\x12\n\nresolution\x18\x08 \x01(\x02\"\x1e\n\x0bMapDataInfo\x12\x0f\n\x07mapData\x18\x01 \x01(\x0c\")\n\x07MapInfo\x12\r\n\x05mapId\x18\x01 \x01(\r\x12\x0f\n\x07mapName\x18\x02 \x01(\t\":\n\x12\x44\x65vicePoseDataInfo\x12\x0e\n\x06update\x18\x01 \x01(\r\x12\t\n\x01x\x18\x02 \x01(\x02\x12\t\n\x01y\x18\x03 \x01(\x02\"V\n\x15\x44\x65viceHistoryPoseInfo\x12\x0e\n\x06poseId\x18\x01 \x01(\r\x12-\n\x06points\x18\x02 \x03(\x0b\x32\x1d.b01.scmap.DevicePoseDataInfo\"3\n\x0e\x44\x65vicePoseInfo\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\x0b\n\x03phi\x18\x03 \x01(\x02\"Z\n\x15\x44\x65viceCurrentPoseInfo\x12\x0e\n\x06poseId\x18\x01 \x01(\r\x12\x0e\n\x06update\x18\x02 \x01(\r\x12\t\n\x01x\x18\x03 \x01(\x02\x12\t\n\x01y\x18\x04 \x01(\x02\x12\x0b\n\x03phi\x18\x05 \x01(\x02\"q\n\x12\x44\x65viceAreaDataInfo\x12\x0e\n\x06status\x18\x01 \x01(\r\x12\x0c\n\x04type\x18\x02 \x01(\r\x12\x11\n\tareaIndex\x18\x03 \x01(\r\x12*\n\x06points\x18\x04 \x03(\x0b\x32\x1a.b01.scmap.DevicePointInfo\" \n\x0eRoomMatrixInfo\x12\x0e\n\x06matrix\x18\x01 \x01(\x0c\";\n\x14RoomOutlinePointInfo\x12\t\n\x01x\x18\x01 \x01(\r\x12\t\n\x01y\x18\x02 \x01(\r\x12\r\n\x05value\x18\x03 \x01(\r\"+\n\x13RoomBorderPointInfo\x12\t\n\x01x\x18\x01 \x01(\r\x12\t\n\x01y\x18\x02 \x01(\r\"Q\n\x0eRoomBorderInfo\x12.\n\x06points\x18\x01 \x03(\x0b\x32\x1e.b01.scmap.RoomBorderPointInfo\x12\x0f\n\x07roomIds\x18\x02 \x03(\r\"~\n\x0fRoomOutlineInfo\x12\x0e\n\x06roomId\x18\x01 \x01(\r\x12/\n\x06points\x18\x02 \x03(\x0b\x32\x1f.b01.scmap.RoomOutlinePointInfo\x12*\n\x07\x62orders\x18\x03 \x03(\x0b\x32\x19.b01.scmap.RoomBorderInfo\"\xa3\x01\n\x0e\x43\x61rpetDataInfo\x12\x10\n\x08\x63\x61rpetId\x18\x01 \x01(\r\x12\x0c\n\x04type\x18\x02 \x01(\r\x12\x10\n\x08unknown3\x18\x03 \x01(\r\x12*\n\x06points\x18\x04 \x03(\x0b\x32\x1a.b01.scmap.DevicePointInfo\x12\x0f\n\x07\x65nabled\x18\x06 \x01(\r\x12\x10\n\x08unknown7\x18\x07 \x01(\r\x12\x10\n\x08unknown8\x18\x08 \x01(\r\"\x87\x02\n\x0cRoomDataInfo\x12\x0e\n\x06roomId\x18\x01 \x01(\r\x12\x10\n\x08roomName\x18\x02 \x01(\t\x12\x12\n\nroomTypeId\x18\x03 \x01(\r\x12\x12\n\nmeterialId\x18\x04 \x01(\r\x12\x12\n\ncleanState\x18\x05 \x01(\r\x12\x11\n\troomClean\x18\x06 \x01(\r\x12\x16\n\x0eroomCleanIndex\x18\x07 \x01(\r\x12\x30\n\x0croomNamePost\x18\x08 \x01(\x0b\x32\x1a.b01.scmap.DevicePointInfo\x12\x0f\n\x07\x63olorId\x18\n \x01(\r\x12\x17\n\x0f\x66loor_direction\x18\x0b \x01(\r\x12\x12\n\nglobal_seq\x18\x0c \x01(\r\"\xcc\x04\n\x08RobotMap\x12\x0f\n\x07mapType\x18\x01 \x01(\r\x12)\n\nmapExtInfo\x18\x02 \x01(\x0b\x32\x15.b01.scmap.MapExtInfo\x12\'\n\x07mapHead\x18\x03 \x01(\x0b\x32\x16.b01.scmap.MapHeadInfo\x12\'\n\x07mapData\x18\x04 \x01(\x0b\x32\x16.b01.scmap.MapDataInfo\x12#\n\x07mapInfo\x18\x05 \x03(\x0b\x32\x12.b01.scmap.MapInfo\x12\x35\n\x0bhistoryPose\x18\x06 \x01(\x0b\x32 .b01.scmap.DeviceHistoryPoseInfo\x12\x30\n\rchargeStation\x18\x07 \x01(\x0b\x32\x19.b01.scmap.DevicePoseInfo\x12\x35\n\x0b\x63urrentPose\x18\x08 \x01(\x0b\x32 .b01.scmap.DeviceCurrentPoseInfo\x12/\n\x08\x61reaInfo\x18\t \x03(\x0b\x32\x1d.b01.scmap.DeviceAreaDataInfo\x12-\n\x0croomDataInfo\x18\x0c \x03(\x0b\x32\x17.b01.scmap.RoomDataInfo\x12-\n\nroomMatrix\x18\r \x01(\x0b\x32\x19.b01.scmap.RoomMatrixInfo\x12/\n\x0broomOutline\x18\x0e \x03(\x0b\x32\x1a.b01.scmap.RoomOutlineInfo\x12-\n\ncarpetInfo\x18\x14 \x03(\x0b\x32\x19.b01.scmap.CarpetDataInfo')
28
+
29
+ _globals = globals()
30
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
31
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'roborock.map.proto.b01_scmap_pb2', _globals)
32
+ if not _descriptor._USE_C_DESCRIPTORS:
33
+ DESCRIPTOR._loaded_options = None
34
+ _globals['_DEVICEPOINTINFO']._serialized_start=49
35
+ _globals['_DEVICEPOINTINFO']._serialized_end=88
36
+ _globals['_MAPBOUNDARYINFO']._serialized_start=90
37
+ _globals['_MAPBOUNDARYINFO']._serialized_end=183
38
+ _globals['_MAPEXTINFO']._serialized_start=186
39
+ _globals['_MAPEXTINFO']._serialized_end=403
40
+ _globals['_MAPHEADINFO']._serialized_start=406
41
+ _globals['_MAPHEADINFO']._serialized_end=544
42
+ _globals['_MAPDATAINFO']._serialized_start=546
43
+ _globals['_MAPDATAINFO']._serialized_end=576
44
+ _globals['_MAPINFO']._serialized_start=578
45
+ _globals['_MAPINFO']._serialized_end=619
46
+ _globals['_DEVICEPOSEDATAINFO']._serialized_start=621
47
+ _globals['_DEVICEPOSEDATAINFO']._serialized_end=679
48
+ _globals['_DEVICEHISTORYPOSEINFO']._serialized_start=681
49
+ _globals['_DEVICEHISTORYPOSEINFO']._serialized_end=767
50
+ _globals['_DEVICEPOSEINFO']._serialized_start=769
51
+ _globals['_DEVICEPOSEINFO']._serialized_end=820
52
+ _globals['_DEVICECURRENTPOSEINFO']._serialized_start=822
53
+ _globals['_DEVICECURRENTPOSEINFO']._serialized_end=912
54
+ _globals['_DEVICEAREADATAINFO']._serialized_start=914
55
+ _globals['_DEVICEAREADATAINFO']._serialized_end=1027
56
+ _globals['_ROOMMATRIXINFO']._serialized_start=1029
57
+ _globals['_ROOMMATRIXINFO']._serialized_end=1061
58
+ _globals['_ROOMOUTLINEPOINTINFO']._serialized_start=1063
59
+ _globals['_ROOMOUTLINEPOINTINFO']._serialized_end=1122
60
+ _globals['_ROOMBORDERPOINTINFO']._serialized_start=1124
61
+ _globals['_ROOMBORDERPOINTINFO']._serialized_end=1167
62
+ _globals['_ROOMBORDERINFO']._serialized_start=1169
63
+ _globals['_ROOMBORDERINFO']._serialized_end=1250
64
+ _globals['_ROOMOUTLINEINFO']._serialized_start=1252
65
+ _globals['_ROOMOUTLINEINFO']._serialized_end=1378
66
+ _globals['_CARPETDATAINFO']._serialized_start=1381
67
+ _globals['_CARPETDATAINFO']._serialized_end=1544
68
+ _globals['_ROOMDATAINFO']._serialized_start=1547
69
+ _globals['_ROOMDATAINFO']._serialized_end=1810
70
+ _globals['_ROBOTMAP']._serialized_start=1813
71
+ _globals['_ROBOTMAP']._serialized_end=2401
72
+ # @@protoc_insertion_point(module_scope)