valetudo-map-parser 0.1.10rc6__tar.gz → 0.1.10rc7__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 (36) hide show
  1. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/PKG-INFO +2 -2
  2. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/rand256_parser.py +129 -47
  3. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/shared.py +8 -9
  4. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/types.py +9 -1
  5. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/utils.py +19 -13
  6. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/hypfer_handler.py +1 -3
  7. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/map_data.py +3 -2
  8. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/rand256_handler.py +13 -17
  9. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/reimg_draw.py +13 -18
  10. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/pyproject.toml +2 -2
  11. valetudo_map_parser-0.1.10rc6/SCR/valetudo_map_parser/hypfer_rooms_handler.py +0 -599
  12. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/LICENSE +0 -0
  13. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/NOTICE.txt +0 -0
  14. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/README.md +0 -0
  15. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/__init__.py +0 -0
  16. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/__init__.py +0 -0
  17. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/async_utils.py +0 -0
  18. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/auto_crop.py +0 -0
  19. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/color_utils.py +0 -0
  20. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/colors.py +0 -0
  21. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/drawable.py +0 -0
  22. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/drawable_elements.py +0 -0
  23. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/enhanced_drawable.py +0 -0
  24. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/FiraSans.ttf +0 -0
  25. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/Inter-VF.ttf +0 -0
  26. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/Lato-Regular.ttf +0 -0
  27. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/MPLUSRegular.ttf +0 -0
  28. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/NotoKufiArabic-VF.ttf +0 -0
  29. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/NotoSansCJKhk-VF.ttf +0 -0
  30. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/NotoSansKhojki.ttf +0 -0
  31. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/optimized_element_map.py +0 -0
  32. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/status_text/status_text.py +0 -0
  33. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/status_text/translations.py +0 -0
  34. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/hypfer_draw.py +0 -0
  35. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/py.typed +0 -0
  36. {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/rooms_handler.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: valetudo-map-parser
3
- Version: 0.1.10rc6
3
+ Version: 0.1.10rc7
4
4
  Summary: A Python library to parse Valetudo map data returning a PIL Image object.
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3.13
14
14
  Classifier: Programming Language :: Python :: 3.14
15
15
  Requires-Dist: Pillow (>=10.3.0)
16
- Requires-Dist: mvcrender (>=0.0.2)
16
+ Requires-Dist: mvcrender (>=0.0.4)
17
17
  Requires-Dist: numpy (>=1.26.4)
18
18
  Requires-Dist: scipy (>=1.12.0)
19
19
  Project-URL: Bug Tracker, https://github.com/sca075/Python-package-valetudo-map-parser/issues
@@ -24,6 +24,14 @@ class RRMapParser:
24
24
  VIRTUAL_WALLS = 10
25
25
  CURRENTLY_CLEANED_BLOCKS = 11
26
26
  FORBIDDEN_MOP_ZONES = 12
27
+ OBSTACLES = 13
28
+ IGNORED_OBSTACLES = 14
29
+ OBSTACLES_WITH_PHOTO = 15
30
+ IGNORED_OBSTACLES_WITH_PHOTO = 16
31
+ CARPET_MAP = 17
32
+ MOP_PATH = 18
33
+ NO_CARPET_AREAS = 19
34
+ DIGEST = 1024
27
35
 
28
36
  class Tools:
29
37
  """Tools for coordinate transformations."""
@@ -33,6 +41,7 @@ class RRMapParser:
33
41
 
34
42
  def __init__(self):
35
43
  """Initialize the parser."""
44
+ self.is_valid = False
36
45
  self.map_data: Dict[str, Any] = {}
37
46
 
38
47
  # Xiaomi/Roborock style byte extraction methods
@@ -67,6 +76,61 @@ class RRMapParser:
67
76
  value = RRMapParser._get_int32(data, address)
68
77
  return value if value < 0x80000000 else value - 0x100000000
69
78
 
79
+ @staticmethod
80
+ def _parse_carpet_map(data: bytes) -> set[int]:
81
+ carpet_map = set()
82
+
83
+ for i, v in enumerate(data):
84
+ if v:
85
+ carpet_map.add(i)
86
+ return carpet_map
87
+
88
+ @staticmethod
89
+ def _parse_area(header: bytes, data: bytes) -> list:
90
+ area_pairs = RRMapParser._get_int16(header, 0x08)
91
+ areas = []
92
+ for area_start in range(0, area_pairs * 16, 16):
93
+ x0 = RRMapParser._get_int16(data, area_start + 0)
94
+ y0 = RRMapParser._get_int16(data, area_start + 2)
95
+ x1 = RRMapParser._get_int16(data, area_start + 4)
96
+ y1 = RRMapParser._get_int16(data, area_start + 6)
97
+ x2 = RRMapParser._get_int16(data, area_start + 8)
98
+ y2 = RRMapParser._get_int16(data, area_start + 10)
99
+ x3 = RRMapParser._get_int16(data, area_start + 12)
100
+ y3 = RRMapParser._get_int16(data, area_start + 14)
101
+ areas.append(
102
+ [
103
+ x0,
104
+ RRMapParser.Tools.DIMENSION_MM - y0,
105
+ x1,
106
+ RRMapParser.Tools.DIMENSION_MM - y1,
107
+ x2,
108
+ RRMapParser.Tools.DIMENSION_MM - y2,
109
+ x3,
110
+ RRMapParser.Tools.DIMENSION_MM - y3,
111
+ ]
112
+ )
113
+ return areas
114
+
115
+ @staticmethod
116
+ def _parse_zones(data: bytes, header: bytes) -> list:
117
+ zone_pairs = RRMapParser._get_int16(header, 0x08)
118
+ zones = []
119
+ for zone_start in range(0, zone_pairs * 8, 8):
120
+ x0 = RRMapParser._get_int16(data, zone_start + 0)
121
+ y0 = RRMapParser._get_int16(data, zone_start + 2)
122
+ x1 = RRMapParser._get_int16(data, zone_start + 4)
123
+ y1 = RRMapParser._get_int16(data, zone_start + 6)
124
+ zones.append(
125
+ [
126
+ x0,
127
+ RRMapParser.Tools.DIMENSION_MM - y0,
128
+ x1,
129
+ RRMapParser.Tools.DIMENSION_MM - y1,
130
+ ]
131
+ )
132
+ return zones
133
+
70
134
  @staticmethod
71
135
  def _parse_object_position(block_data_length: int, data: bytes) -> Dict[str, Any]:
72
136
  """Parse object position using Xiaomi method."""
@@ -82,6 +146,19 @@ class RRMapParser:
82
146
  angle = raw_angle
83
147
  return {"position": [x, y], "angle": angle}
84
148
 
149
+
150
+ @staticmethod
151
+ def _parse_walls(data: bytes, header: bytes) -> list:
152
+ wall_pairs = RRMapParser._get_int16(header, 0x08)
153
+ walls = []
154
+ for wall_start in range(0, wall_pairs * 8, 8):
155
+ x0 = RRMapParser._get_int16(data, wall_start + 0)
156
+ y0 = RRMapParser._get_int16(data, wall_start + 2)
157
+ x1 = RRMapParser._get_int16(data, wall_start + 4)
158
+ y1 = RRMapParser._get_int16(data, wall_start + 6)
159
+ walls.append([x0, RRMapParser.Tools.DIMENSION_MM - y0, x1, RRMapParser.Tools.DIMENSION_MM - y1])
160
+ return walls
161
+
85
162
  @staticmethod
86
163
  def _parse_path_block(buf: bytes, offset: int, length: int) -> Dict[str, Any]:
87
164
  """Parse path block using EXACT same method as working parser."""
@@ -127,59 +204,45 @@ class RRMapParser:
127
204
  return {}
128
205
 
129
206
  def parse_blocks(self, raw: bytes, pixels: bool = True) -> Dict[int, Any]:
130
- """Parse all blocks using Xiaomi method."""
131
207
  blocks = {}
132
208
  map_header_length = self._get_int16(raw, 0x02)
133
209
  block_start_position = map_header_length
134
-
135
210
  while block_start_position < len(raw):
136
211
  try:
137
- # Parse block header using Xiaomi method
138
212
  block_header_length = self._get_int16(raw, block_start_position + 0x02)
139
213
  header = self._get_bytes(raw, block_start_position, block_header_length)
140
214
  block_type = self._get_int16(header, 0x00)
141
215
  block_data_length = self._get_int32(header, 0x04)
142
216
  block_data_start = block_start_position + block_header_length
143
217
  data = self._get_bytes(raw, block_data_start, block_data_length)
144
-
145
- # Parse different block types
146
- if block_type == self.Types.ROBOT_POSITION.value:
147
- blocks[block_type] = self._parse_object_position(
148
- block_data_length, data
149
- )
150
- elif block_type == self.Types.CHARGER_LOCATION.value:
151
- blocks[block_type] = self._parse_object_position(
152
- block_data_length, data
153
- )
154
- elif block_type == self.Types.PATH.value:
155
- blocks[block_type] = self._parse_path_block(
156
- raw, block_start_position, block_data_length
157
- )
158
- elif block_type == self.Types.GOTO_PREDICTED_PATH.value:
159
- blocks[block_type] = self._parse_path_block(
160
- raw, block_start_position, block_data_length
161
- )
162
- elif block_type == self.Types.GOTO_TARGET.value:
163
- blocks[block_type] = {"position": self._parse_goto_target(data)}
164
- elif block_type == self.Types.IMAGE.value:
165
- # Get header length for Gen1/Gen3 detection
166
- header_length = self._get_int8(header, 2)
167
- blocks[block_type] = self._parse_image_block(
168
- raw,
169
- block_start_position,
170
- block_data_length,
171
- header_length,
172
- pixels,
173
- )
174
-
175
- # Move to next block using Xiaomi method
176
- block_start_position = (
177
- block_start_position + block_data_length + self._get_int8(header, 2)
178
- )
179
-
218
+ match block_type:
219
+ case self.Types.DIGEST.value:
220
+ self.is_valid = True
221
+ case self.Types.ROBOT_POSITION.value | self.Types.CHARGER_LOCATION.value:
222
+ blocks[block_type] = self._parse_object_position(block_data_length, data)
223
+ case self.Types.PATH.value | self.Types.GOTO_PREDICTED_PATH.value:
224
+ blocks[block_type] = self._parse_path_block(raw, block_start_position, block_data_length)
225
+ case self.Types.CURRENTLY_CLEANED_ZONES.value:
226
+ blocks[block_type] = {"zones": self._parse_zones(data, header)}
227
+ case self.Types.FORBIDDEN_ZONES.value:
228
+ blocks[block_type] = {"forbidden_zones": self._parse_area(header, data)}
229
+ case self.Types.FORBIDDEN_MOP_ZONES.value:
230
+ blocks[block_type] = {"forbidden_mop_zones": self._parse_area(header, data)}
231
+ case self.Types.GOTO_TARGET.value:
232
+ blocks[block_type] = {"position": self._parse_goto_target(data)}
233
+ case self.Types.VIRTUAL_WALLS.value:
234
+ blocks[block_type] = {"virtual_walls": self._parse_walls(data, header)}
235
+ case self.Types.CARPET_MAP.value:
236
+ data = RRMapParser._get_bytes(raw, block_data_start, block_data_length)
237
+ blocks[block_type] = {"carpet_map": self._parse_carpet_map(data)}
238
+ case self.Types.IMAGE.value:
239
+ header_length = self._get_int8(header, 2)
240
+ blocks[block_type] = self._parse_image_block(
241
+ raw, block_start_position, block_data_length, header_length, pixels)
242
+
243
+ block_start_position = block_start_position + block_data_length + self._get_int8(header, 2)
180
244
  except (struct.error, IndexError):
181
245
  break
182
-
183
246
  return blocks
184
247
 
185
248
  def _parse_image_block(
@@ -365,8 +428,32 @@ class RRMapParser:
365
428
  ]
366
429
 
367
430
  # Add missing fields to match expected JSON format
368
- parsed_map_data["forbidden_zones"] = []
369
- parsed_map_data["virtual_walls"] = []
431
+ parsed_map_data["currently_cleaned_zones"] = (
432
+ blocks[self.Types.CURRENTLY_CLEANED_ZONES.value]["zones"]
433
+ if self.Types.CURRENTLY_CLEANED_ZONES.value in blocks
434
+ else []
435
+ )
436
+ parsed_map_data["forbidden_zones"] = (
437
+ blocks[self.Types.FORBIDDEN_ZONES.value]["forbidden_zones"]
438
+ if self.Types.FORBIDDEN_ZONES.value in blocks
439
+ else []
440
+ )
441
+ parsed_map_data["forbidden_mop_zones"] = (
442
+ blocks[self.Types.FORBIDDEN_MOP_ZONES.value]["forbidden_mop_zones"]
443
+ if self.Types.FORBIDDEN_MOP_ZONES.value in blocks
444
+ else []
445
+ )
446
+ parsed_map_data["virtual_walls"] = (
447
+ blocks[self.Types.VIRTUAL_WALLS.value]["virtual_walls"]
448
+ if self.Types.VIRTUAL_WALLS.value in blocks
449
+ else []
450
+ )
451
+ parsed_map_data["carpet_areas"] = (
452
+ blocks[self.Types.CARPET_MAP.value]["carpet_map"]
453
+ if self.Types.CARPET_MAP.value in blocks
454
+ else []
455
+ )
456
+ parsed_map_data["is_valid"] = self.is_valid
370
457
 
371
458
  return parsed_map_data
372
459
 
@@ -388,8 +475,3 @@ class RRMapParser:
388
475
  except (struct.error, IndexError, ValueError):
389
476
  return None
390
477
  return self.map_data
391
-
392
- @staticmethod
393
- def get_int32(data: bytes, address: int) -> int:
394
- """Get a 32-bit integer from the data - kept for compatibility."""
395
- return struct.unpack_from("<i", data, address)[0]
@@ -12,12 +12,13 @@ from PIL import Image
12
12
  from .types import (
13
13
  ATTR_CALIBRATION_POINTS,
14
14
  ATTR_CAMERA_MODE,
15
+ ATTR_CONTENT_TYPE,
15
16
  ATTR_MARGINS,
16
17
  ATTR_OBSTACLES,
17
18
  ATTR_POINTS,
18
19
  ATTR_ROOMS,
19
20
  ATTR_ROTATE,
20
- ATTR_SNAPSHOT,
21
+ ATTR_IMAGE_LAST_UPDATED,
21
22
  ATTR_VACUUM_BATTERY,
22
23
  ATTR_VACUUM_CHARGING,
23
24
  ATTR_VACUUM_JSON_ID,
@@ -179,12 +180,14 @@ class CameraShared:
179
180
  def generate_attributes(self) -> dict:
180
181
  """Generate and return the shared attribute's dictionary."""
181
182
  attrs = {
183
+ ATTR_IMAGE_LAST_UPDATED: self.image_last_updated,
184
+ ATTR_CONTENT_TYPE: self.image_format,
185
+ ATTR_VACUUM_JSON_ID: self.vac_json_id,
182
186
  ATTR_CAMERA_MODE: self.camera_mode,
187
+ ATTR_VACUUM_STATUS: self.vacuum_state,
183
188
  ATTR_VACUUM_BATTERY: f"{self.vacuum_battery}%",
184
189
  ATTR_VACUUM_CHARGING: self.vacuum_bat_charged(),
185
190
  ATTR_VACUUM_POSITION: self.current_room,
186
- ATTR_VACUUM_STATUS: self.vacuum_state,
187
- ATTR_VACUUM_JSON_ID: self.vac_json_id,
188
191
  ATTR_CALIBRATION_POINTS: self.attr_calibration_points,
189
192
  }
190
193
  if self.obstacles_pos and self.vacuum_ips:
@@ -193,8 +196,6 @@ class CameraShared:
193
196
  )
194
197
  attrs[ATTR_OBSTACLES] = self.obstacles_data
195
198
 
196
- attrs[ATTR_SNAPSHOT] = self.snapshot_take if self.enable_snapshots else False
197
-
198
199
  shared_attrs = {
199
200
  ATTR_ROOMS: self.map_rooms,
200
201
  ATTR_ZONES: self.map_pred_zones,
@@ -211,10 +212,8 @@ class CameraShared:
211
212
  return {
212
213
  "image": {
213
214
  "binary": self.binary_image,
214
- "pil_image_size": self.new_image.size,
215
- "size": self.new_image.size if self.new_image else None,
216
- "format": self.image_format,
217
- "updated": self.image_last_updated,
215
+ "pil_image": self.new_image,
216
+ "size": self.new_image.size if self.new_image else (10, 10),
218
217
  },
219
218
  "attributes": self.generate_attributes(),
220
219
  }
@@ -18,23 +18,29 @@ DEFAULT_ROOMS = 1
18
18
 
19
19
  LOGGER = logging.getLogger(__package__)
20
20
 
21
+
21
22
  class Spot(TypedDict):
22
23
  name: str
23
24
  coordinates: List[int] # [x, y]
24
25
 
26
+
25
27
  class Zone(TypedDict):
26
28
  name: str
27
29
  coordinates: List[List[int]] # [[x1, y1, x2, y2, repeats], ...]
28
30
 
31
+
29
32
  class Room(TypedDict):
30
33
  name: str
31
34
  id: int
32
35
 
36
+
37
+ # list[dict[str, str | list[int]]] | list[dict[str, str | list[list[int]]]] | list[dict[str, str | int]] | int]'
33
38
  class Destinations(TypedDict, total=False):
34
39
  spots: NotRequired[Optional[List[Spot]]]
35
40
  zones: NotRequired[Optional[List[Zone]]]
36
41
  rooms: NotRequired[Optional[List[Room]]]
37
- updated: NotRequired[Optional[int]]
42
+ updated: NotRequired[Optional[float]]
43
+
38
44
 
39
45
  class RoomProperty(TypedDict):
40
46
  number: int
@@ -227,9 +233,11 @@ NumpyArray = np.ndarray
227
233
  Point = Tuple[int, int]
228
234
 
229
235
  CAMERA_STORAGE = "valetudo_camera"
236
+ ATTR_IMAGE_LAST_UPDATED = "image_last_updated"
230
237
  ATTR_ROTATE = "rotate_image"
231
238
  ATTR_CROP = "crop_image"
232
239
  ATTR_MARGINS = "margins"
240
+ ATTR_CONTENT_TYPE = "content_type"
233
241
  CONF_OFFSET_TOP = "offset_top"
234
242
  CONF_OFFSET_BOTTOM = "offset_bottom"
235
243
  CONF_OFFSET_LEFT = "offset_left"
@@ -23,7 +23,7 @@ from .types import (
23
23
  NumpyArray,
24
24
  PilPNG,
25
25
  RobotPosition,
26
- Destinations
26
+ Destinations,
27
27
  )
28
28
  from ..map_data import HyperMapData
29
29
  from .async_utils import AsyncNumPy
@@ -197,26 +197,27 @@ class BaseHandler:
197
197
  """Update the shared data with the latest information."""
198
198
 
199
199
  if hasattr(self, "get_rooms_attributes") and (
200
- self.shared.map_rooms is None and destinations is not None
200
+ self.shared.map_rooms is None and destinations is not None
201
201
  ):
202
- (
203
- self.shared.map_rooms,
204
- self.shared.map_pred_zones,
205
- self.shared.map_pred_points,
206
- ) = await self.get_rooms_attributes(destinations)
202
+ (self.shared.map_rooms,) = await self.get_rooms_attributes(destinations)
207
203
  if self.shared.map_rooms:
208
204
  LOGGER.debug("%s: Rand256 attributes rooms updated", self.file_name)
209
205
 
210
206
  if hasattr(self, "async_get_rooms_attributes") and (
211
- self.shared.map_rooms is None
207
+ self.shared.map_rooms is None
212
208
  ):
213
209
  if self.shared.map_rooms is None:
214
210
  self.shared.map_rooms = await self.async_get_rooms_attributes()
215
211
  if self.shared.map_rooms:
216
212
  LOGGER.debug("%s: Hyper attributes rooms updated", self.file_name)
217
213
 
218
- if hasattr(self, "get_calibration_data") and self.shared.attr_calibration_points is None:
219
- self.shared.attr_calibration_points = self.get_calibration_data(self.shared.image_rotate)
214
+ if (
215
+ hasattr(self, "get_calibration_data")
216
+ and self.shared.attr_calibration_points is None
217
+ ):
218
+ self.shared.attr_calibration_points = self.get_calibration_data(
219
+ self.shared.image_rotate
220
+ )
220
221
 
221
222
  if not self.shared.image_size:
222
223
  self.shared.image_size = self.get_img_size()
@@ -228,14 +229,19 @@ class BaseHandler:
228
229
 
229
230
  self.shared.current_room = self.get_robot_position()
230
231
 
231
- def prepare_resize_params(self, pil_img: PilPNG, rand: bool=False) -> ResizeParams:
232
+ def prepare_resize_params(
233
+ self, pil_img: PilPNG, rand: bool = False
234
+ ) -> ResizeParams:
232
235
  """Prepare resize parameters for image resizing."""
233
236
  if self.shared.image_rotate in [0, 180]:
234
237
  width, height = pil_img.size
235
238
  else:
236
239
  height, width = pil_img.size
237
- LOGGER.debug("Shared PIL image size: %s x %s", self.shared.image_ref_width,
238
- self.shared.image_ref_height)
240
+ LOGGER.debug(
241
+ "Shared PIL image size: %s x %s",
242
+ self.shared.image_ref_width,
243
+ self.shared.image_ref_height,
244
+ )
239
245
  return ResizeParams(
240
246
  pil_img=pil_img,
241
247
  width=width,
@@ -14,7 +14,6 @@ from PIL import Image
14
14
 
15
15
  from .config.async_utils import AsyncPIL
16
16
 
17
- # from .config.auto_crop import AutoCrop
18
17
  from mvcrender.autocrop import AutoCrop
19
18
  from .config.drawable_elements import DrawableElement
20
19
  from .config.shared import CameraShared
@@ -60,7 +59,6 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
60
59
  None # persistent working buffer to avoid per-frame allocations
61
60
  )
62
61
  self.active_zones = [] # vacuum active zones.
63
- self.svg_wait = False # SVG image creation wait.
64
62
  self.imd = ImDraw(self) # Image Draw class.
65
63
  self.color_grey = (128, 128, 128, 255)
66
64
  self.file_name = self.shared.file_name # file name of the vacuum.
@@ -362,7 +360,7 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
362
360
  self.zooming = self.imd.img_h.zooming
363
361
 
364
362
  # Resize the image
365
- img_np_array = self.async_auto_trim_and_zoom_image(
363
+ img_np_array = self.auto_trim_and_zoom_image(
366
364
  img_np_array,
367
365
  colors["background"],
368
366
  int(self.shared.margins),
@@ -539,16 +539,17 @@ class RandImageData:
539
539
  return None
540
540
 
541
541
  @staticmethod
542
- def get_rrm_currently_cleaned_zones(json_data: JsonType) -> dict:
542
+ def get_rrm_currently_cleaned_zones(json_data: JsonType) -> list[dict[str, Any]]:
543
543
  """Get the currently cleaned zones from the json."""
544
544
  re_zones = json_data.get("currently_cleaned_zones", [])
545
545
  formatted_zones = RandImageData._rrm_valetudo_format_zone(re_zones)
546
546
  return formatted_zones
547
547
 
548
548
  @staticmethod
549
- def get_rrm_forbidden_zones(json_data: JsonType) -> dict:
549
+ def get_rrm_forbidden_zones(json_data: JsonType) -> list[dict[str, Any]]:
550
550
  """Get the forbidden zones from the json."""
551
551
  re_zones = json_data.get("forbidden_zones", [])
552
+ re_zones.extend(json_data.get("forbidden_mop_zones", []))
552
553
  formatted_zones = RandImageData._rrm_valetudo_format_zone(re_zones)
553
554
  return formatted_zones
554
555
 
@@ -7,7 +7,6 @@ Version: 0.1.9.a6
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- import logging
11
10
  import uuid
12
11
  from typing import Any
13
12
 
@@ -15,7 +14,6 @@ import numpy as np
15
14
 
16
15
  from .config.async_utils import AsyncPIL
17
16
 
18
- # from .config.auto_crop import AutoCrop
19
17
  from mvcrender.autocrop import AutoCrop
20
18
  from .config.drawable_elements import DrawableElement
21
19
  from .config.types import (
@@ -28,6 +26,7 @@ from .config.types import (
28
26
  RobotPosition,
29
27
  RoomsProperties,
30
28
  RoomStore,
29
+ LOGGER,
31
30
  )
32
31
  from .config.utils import (
33
32
  BaseHandler,
@@ -39,9 +38,6 @@ from .reimg_draw import ImageDraw
39
38
  from .rooms_handler import RandRoomsHandler
40
39
 
41
40
 
42
- _LOGGER = logging.getLogger(__name__)
43
-
44
-
45
41
  # noinspection PyTypeChecker
46
42
  class ReImageHandler(BaseHandler, AutoCrop):
47
43
  """
@@ -112,17 +108,17 @@ class ReImageHandler(BaseHandler, AutoCrop):
112
108
  self.shared.map_rooms = room_ids
113
109
 
114
110
  # get the zones and points data
115
- zone_properties = await self.async_zone_propriety(zones_data)
111
+ self.shared.map_pred_zones = await self.async_zone_propriety(zones_data)
116
112
  # get the points data
117
- point_properties = await self.async_points_propriety(points_data)
113
+ self.shared.map_pred_points = await self.async_points_propriety(points_data)
118
114
 
119
- if not (room_properties or zone_properties):
115
+ if not (room_properties or self.shared.map_pred_zones):
120
116
  self.rooms_pos = None
121
117
 
122
118
  rooms = RoomStore(self.file_name, room_properties)
123
- return room_properties, zone_properties, point_properties
119
+ return room_properties
124
120
  except (RuntimeError, ValueError) as e:
125
- _LOGGER.warning(
121
+ LOGGER.warning(
126
122
  "No rooms Data or Error in extract_room_properties: %s",
127
123
  e,
128
124
  exc_info=True,
@@ -146,12 +142,12 @@ class ReImageHandler(BaseHandler, AutoCrop):
146
142
 
147
143
  try:
148
144
  if (m_json is not None) and (not isinstance(m_json, tuple)):
149
- _LOGGER.info("%s: Composing the image for the camera.", self.file_name)
145
+ LOGGER.info("%s: Composing the image for the camera.", self.file_name)
150
146
  self.json_data = m_json
151
147
  size_x, size_y = self.data.get_rrm_image_size(m_json)
152
148
  self.img_size = DEFAULT_IMAGE_SIZE
153
149
  self.json_id = str(uuid.uuid4()) # image id
154
- _LOGGER.info("Vacuum Data ID: %s", self.json_id)
150
+ LOGGER.info("Vacuum Data ID: %s", self.json_id)
155
151
 
156
152
  (
157
153
  img_np_array,
@@ -178,7 +174,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
178
174
  return await self._finalize_image(pil_img)
179
175
 
180
176
  except (RuntimeError, RuntimeWarning) as e:
181
- _LOGGER.warning(
177
+ LOGGER.warning(
182
178
  "%s: Runtime Error %s during image creation.",
183
179
  self.file_name,
184
180
  str(e),
@@ -214,7 +210,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
214
210
  colors["background"],
215
211
  DEFAULT_PIXEL_SIZE,
216
212
  )
217
- _LOGGER.info("%s: Completed base Layers", self.file_name)
213
+ LOGGER.info("%s: Completed base Layers", self.file_name)
218
214
 
219
215
  # Update element map for rooms
220
216
  if 0 < room_id <= 15:
@@ -350,7 +346,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
350
346
  else:
351
347
  self.zooming = False
352
348
 
353
- img_np_array = self.async_auto_trim_and_zoom_image(
349
+ img_np_array = self.auto_trim_and_zoom_image(
354
350
  img_np_array,
355
351
  detect_colour=colors["background"],
356
352
  margin_size=int(self.shared.margins),
@@ -362,7 +358,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
362
358
 
363
359
  async def _finalize_image(self, pil_img):
364
360
  if not self.shared.image_ref_width or not self.shared.image_ref_height:
365
- _LOGGER.warning(
361
+ LOGGER.warning(
366
362
  "Image finalization failed: Invalid image dimensions. Returning original image."
367
363
  )
368
364
  return pil_img
@@ -515,7 +511,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
515
511
  """Return the map calibration data."""
516
512
  if not self.calibration_data and self.crop_img_size:
517
513
  self.calibration_data = []
518
- _LOGGER.info(
514
+ LOGGER.info(
519
515
  "%s: Getting Calibrations points %s",
520
516
  self.file_name,
521
517
  str(self.crop_area),