valetudo-map-parser 0.1.9b55__py3-none-any.whl → 0.1.9b57__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  Image Handler Module for Valetudo Re Vacuums.
3
3
  It returns the PIL PNG image frame relative to the Map Data extrapolated from the vacuum json.
4
4
  It also returns calibration, rooms data to the card and other images information to the camera.
5
- Version: 0.1.9.b42
5
+ Version: 0.1.9.a6
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -26,12 +26,13 @@ from .config.types import (
26
26
  RobotPosition,
27
27
  RoomsProperties,
28
28
  RoomStore,
29
+ WebPBytes,
29
30
  )
30
31
  from .config.utils import (
31
32
  BaseHandler,
32
- # async_extract_room_outline,
33
33
  initialize_drawing_config,
34
34
  manage_drawable_elements,
35
+ numpy_to_webp_bytes,
35
36
  prepare_resize_params,
36
37
  )
37
38
  from .map_data import RandImageData
@@ -69,39 +70,15 @@ class ReImageHandler(BaseHandler, AutoCrop):
69
70
  self.active_zones = None # Active zones
70
71
  self.file_name = self.shared.file_name # File name
71
72
  self.imd = ImageDraw(self) # Image Draw
72
- self.rooms_handler = RandRoomsHandler(self.file_name, self.drawing_config) # Room data handler
73
-
74
- async def extract_room_outline_from_map(self, room_id_int, pixels):
75
- """Extract the outline of a room using the pixel data and element map.
76
-
77
- Args:
78
- room_id_int: The room ID as an integer
79
- pixels: List of pixel coordinates in the format [[x, y, z], ...]
80
-
81
- Returns:
82
- List of points forming the outline of the room
83
- """
84
- # Calculate x and y min/max from compressed pixels for rectangular fallback
85
- x_values = []
86
- y_values = []
87
- for x, y, _ in pixels:
88
- x_values.append(x)
89
- y_values.append(y)
90
-
91
- if not x_values or not y_values:
92
- return []
93
-
94
- min_x, max_x = min(x_values), max(x_values)
95
- min_y, max_y = min(y_values), max(y_values)
96
-
97
- # Always return a rectangular outline since element_map is removed
98
- return [(min_x, min_y), (max_x, min_y), (max_x, max_y), (min_x, max_y)]
73
+ self.rooms_handler = RandRoomsHandler(
74
+ self.file_name, self.drawing_config
75
+ ) # Room data handler
99
76
 
100
77
  async def extract_room_properties(
101
78
  self, json_data: JsonType, destinations: JsonType
102
79
  ) -> RoomsProperties:
103
80
  """Extract the room properties."""
104
- unsorted_id = RandImageData.get_rrm_segments_ids(json_data)
81
+ # unsorted_id = RandImageData.get_rrm_segments_ids(json_data)
105
82
  size_x, size_y = RandImageData.get_rrm_image_size(json_data)
106
83
  top, left = RandImageData.get_rrm_image_position(json_data)
107
84
  try:
@@ -124,11 +101,17 @@ class ReImageHandler(BaseHandler, AutoCrop):
124
101
 
125
102
  # Update self.rooms_pos from room_properties for compatibility with other methods
126
103
  self.rooms_pos = []
127
- for room_id, props in room_properties.items():
128
- self.rooms_pos.append({
129
- "name": props["name"],
130
- "corners": props["outline"], # Use the enhanced outline
131
- })
104
+ room_ids = [] # Collect room IDs for shared.map_rooms
105
+ for room_id, room_data in room_properties.items():
106
+ self.rooms_pos.append(
107
+ {"name": room_data["name"], "outline": room_data["outline"]}
108
+ )
109
+ # Store the room number (segment ID) for MQTT active zone mapping
110
+ room_ids.append(room_data["number"])
111
+
112
+ # Update shared.map_rooms with the room IDs for MQTT active zone mapping
113
+ self.shared.map_rooms = room_ids
114
+ _LOGGER.debug("Updated shared.map_rooms with room IDs: %s", room_ids)
132
115
 
133
116
  # get the zones and points data
134
117
  zone_properties = await self.async_zone_propriety(zones_data)
@@ -144,9 +127,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
144
127
  _LOGGER.debug("Extracted data: %s", extracted_data)
145
128
  else:
146
129
  self.rooms_pos = None
147
- _LOGGER.debug(
148
- "%s: Rooms and Zones data not available!", self.file_name
149
- )
130
+ _LOGGER.debug("%s: Rooms and Zones data not available!", self.file_name)
150
131
 
151
132
  rooms = RoomStore(self.file_name, room_properties)
152
133
  _LOGGER.debug("Rooms Data: %s", rooms.get_rooms())
@@ -163,8 +144,14 @@ class ReImageHandler(BaseHandler, AutoCrop):
163
144
  self,
164
145
  m_json: JsonType, # json data
165
146
  destinations: None = None, # MQTT destinations for labels
166
- ) -> PilPNG or None:
167
- """Generate Images from the json data."""
147
+ return_webp: bool = False,
148
+ ) -> WebPBytes | Image.Image | None:
149
+ """Generate Images from the json data.
150
+ @param m_json: The JSON data to use to draw the image.
151
+ @param destinations: MQTT destinations for labels (unused).
152
+ @param return_webp: If True, return WebP bytes; if False, return PIL Image (default).
153
+ @return WebPBytes | Image.Image: WebP bytes or PIL Image depending on return_webp parameter.
154
+ """
168
155
  colors: Colors = {
169
156
  name: self.shared.user_colors[idx] for idx, name in enumerate(COLORS)
170
157
  }
@@ -201,11 +188,21 @@ class ReImageHandler(BaseHandler, AutoCrop):
201
188
  img_np_array, m_json, colors, robot_position, robot_position_angle
202
189
  )
203
190
 
204
- # Final adjustments
205
- pil_img = Image.fromarray(img_np_array, mode="RGBA")
206
- del img_np_array # free memory
207
-
208
- return await self._finalize_image(pil_img)
191
+ # Return WebP bytes or PIL Image based on parameter
192
+ if return_webp:
193
+ # Convert directly to WebP bytes for better performance
194
+ webp_bytes = await numpy_to_webp_bytes(
195
+ img_np_array,
196
+ quality=90, # High quality for vacuum maps
197
+ lossless=False # Use lossy compression for smaller size
198
+ )
199
+ del img_np_array # free memory
200
+ return webp_bytes
201
+ else:
202
+ # Convert to PIL Image (original behavior)
203
+ pil_img = Image.fromarray(img_np_array, mode="RGBA")
204
+ del img_np_array # free memory
205
+ return await self._finalize_image(pil_img)
209
206
 
210
207
  except (RuntimeError, RuntimeWarning) as e:
211
208
  _LOGGER.warning(
@@ -254,12 +251,18 @@ class ReImageHandler(BaseHandler, AutoCrop):
254
251
 
255
252
  if room_id > 0 and not self.room_propriety:
256
253
  self.room_propriety = await self.get_rooms_attributes(destinations)
257
- if self.rooms_pos:
258
- self.robot_pos = await self.async_get_robot_in_room(
259
- (robot_position[0] * 10),
260
- (robot_position[1] * 10),
261
- robot_position_angle,
262
- )
254
+
255
+ # Ensure room data is available for robot room detection (even if not extracted above)
256
+ if not self.rooms_pos and not self.room_propriety:
257
+ self.room_propriety = await self.get_rooms_attributes(destinations)
258
+
259
+ # Always check robot position for zooming (fallback)
260
+ if self.rooms_pos and robot_position and not hasattr(self, "robot_pos"):
261
+ self.robot_pos = await self.async_get_robot_in_room(
262
+ (robot_position[0] * 10),
263
+ (robot_position[1] * 10),
264
+ robot_position_angle,
265
+ )
263
266
  self.img_base_layer = await self.async_copy_array(img_np_array)
264
267
  else:
265
268
  # If floor is disabled, create an empty image
@@ -270,6 +273,60 @@ class ReImageHandler(BaseHandler, AutoCrop):
270
273
  size_x, size_y, background_color
271
274
  )
272
275
  self.img_base_layer = await self.async_copy_array(img_np_array)
276
+
277
+ # Check active zones BEFORE auto-crop to enable proper zoom functionality
278
+ # This needs to run on every frame, not just frame 0
279
+ if (
280
+ self.shared.image_auto_zoom
281
+ and self.shared.vacuum_state == "cleaning"
282
+ and robot_position
283
+ and destinations # Check if we have destinations data for room extraction
284
+ ):
285
+ # Extract room data early if we have destinations
286
+ try:
287
+ temp_room_properties = (
288
+ await self.rooms_handler.async_extract_room_properties(
289
+ m_json, destinations
290
+ )
291
+ )
292
+ if temp_room_properties:
293
+ # Create temporary rooms_pos for robot room detection
294
+ temp_rooms_pos = []
295
+ for room_id, room_data in temp_room_properties.items():
296
+ temp_rooms_pos.append(
297
+ {"name": room_data["name"], "outline": room_data["outline"]}
298
+ )
299
+
300
+ # Store original rooms_pos and temporarily use the new one
301
+ original_rooms_pos = self.rooms_pos
302
+ self.rooms_pos = temp_rooms_pos
303
+
304
+ # Perform robot room detection to check active zones
305
+ robot_room_result = await self.async_get_robot_in_room(
306
+ robot_position[0], robot_position[1], robot_position_angle
307
+ )
308
+
309
+ # Restore original rooms_pos
310
+ self.rooms_pos = original_rooms_pos
311
+
312
+ except Exception as e:
313
+ _LOGGER.debug(
314
+ "%s: Early room extraction failed: %s, falling back to robot-position zoom",
315
+ self.file_name,
316
+ e,
317
+ )
318
+ # Fallback to robot-position-based zoom if room extraction fails
319
+ if (
320
+ self.shared.image_auto_zoom
321
+ and self.shared.vacuum_state == "cleaning"
322
+ and robot_position
323
+ ):
324
+ self.zooming = True
325
+ _LOGGER.debug(
326
+ "%s: Enabling fallback robot-position-based zoom",
327
+ self.file_name,
328
+ )
329
+
273
330
  return self.img_base_layer, robot_position, robot_position_angle
274
331
 
275
332
  async def _draw_map_elements(
@@ -316,6 +373,33 @@ class ReImageHandler(BaseHandler, AutoCrop):
316
373
  img_np_array, robot_position, robot_position_angle, robot_color
317
374
  )
318
375
 
376
+ # Store robot position for potential zoom function use
377
+ if robot_position:
378
+ self.robot_position = robot_position
379
+
380
+ # Check if Zoom should be enabled based on active zones
381
+ if (
382
+ self.shared.image_auto_zoom
383
+ and self.shared.vacuum_state == "cleaning"
384
+ and robot_position
385
+ ):
386
+ # For Rand256, we need to check active zones differently since room data is not available yet
387
+ # Use a simplified approach: enable zoom if any active zones are set
388
+ active_zones = self.shared.rand256_active_zone
389
+ if active_zones and any(zone for zone in active_zones):
390
+ self.zooming = True
391
+ _LOGGER.debug(
392
+ "%s: Enabling zoom for Rand256 - active zones detected: %s",
393
+ self.file_name,
394
+ active_zones,
395
+ )
396
+ else:
397
+ self.zooming = False
398
+ _LOGGER.debug(
399
+ "%s: Zoom disabled for Rand256 - no active zones set",
400
+ self.file_name,
401
+ )
402
+
319
403
  img_np_array = await self.async_auto_trim_and_zoom_image(
320
404
  img_np_array,
321
405
  detect_colour=colors["background"],
@@ -353,79 +437,196 @@ class ReImageHandler(BaseHandler, AutoCrop):
353
437
  _LOGGER.debug("Got Rooms Attributes.")
354
438
  return self.room_propriety
355
439
 
440
+ @staticmethod
441
+ def point_in_polygon(x: int, y: int, polygon: list) -> bool:
442
+ """
443
+ Check if a point is inside a polygon using ray casting algorithm.
444
+ Enhanced version with better handling of edge cases.
445
+
446
+ Args:
447
+ x: X coordinate of the point
448
+ y: Y coordinate of the point
449
+ polygon: List of (x, y) tuples forming the polygon
450
+
451
+ Returns:
452
+ True if the point is inside the polygon, False otherwise
453
+ """
454
+ # Ensure we have a valid polygon with at least 3 points
455
+ if len(polygon) < 3:
456
+ return False
457
+
458
+ # Make sure the polygon is closed (last point equals first point)
459
+ if polygon[0] != polygon[-1]:
460
+ polygon = polygon + [polygon[0]]
461
+
462
+ # Use winding number algorithm for better accuracy
463
+ wn = 0 # Winding number counter
464
+
465
+ # Loop through all edges of the polygon
466
+ for i in range(len(polygon) - 1): # Last vertex is first vertex
467
+ p1x, p1y = polygon[i]
468
+ p2x, p2y = polygon[i + 1]
469
+
470
+ # Test if a point is left/right/on the edge defined by two vertices
471
+ if p1y <= y: # Start y <= P.y
472
+ if p2y > y: # End y > P.y (upward crossing)
473
+ # Point left of edge
474
+ if ((p2x - p1x) * (y - p1y) - (x - p1x) * (p2y - p1y)) > 0:
475
+ wn += 1 # Valid up intersect
476
+ else: # Start y > P.y
477
+ if p2y <= y: # End y <= P.y (downward crossing)
478
+ # Point right of edge
479
+ if ((p2x - p1x) * (y - p1y) - (x - p1x) * (p2y - p1y)) < 0:
480
+ wn -= 1 # Valid down intersect
481
+
482
+ # If winding number is not 0, the point is inside the polygon
483
+ return wn != 0
484
+
356
485
  async def async_get_robot_in_room(
357
486
  self, robot_x: int, robot_y: int, angle: float
358
487
  ) -> RobotPosition:
359
488
  """Get the robot position and return in what room is."""
489
+ # First check if we already have a cached room and if the robot is still in it
490
+ if self.robot_in_room:
491
+ # If we have outline data, use point_in_polygon for accurate detection
492
+ if "outline" in self.robot_in_room:
493
+ outline = self.robot_in_room["outline"]
494
+ if self.point_in_polygon(int(robot_x), int(robot_y), outline):
495
+ temp = {
496
+ "x": robot_x,
497
+ "y": robot_y,
498
+ "angle": angle,
499
+ "in_room": self.robot_in_room["room"],
500
+ }
501
+ # Handle active zones
502
+ self.active_zones = self.shared.rand256_active_zone
503
+ self.zooming = False
504
+ if self.active_zones and (
505
+ self.robot_in_room["id"] in range(len(self.active_zones))
506
+ ):
507
+ self.zooming = bool(self.active_zones[self.robot_in_room["id"]])
508
+ else:
509
+ self.zooming = False
510
+ return temp
511
+ # Fallback to bounding box check if no outline data
512
+ elif all(k in self.robot_in_room for k in ["left", "right", "up", "down"]):
513
+ if (
514
+ self.robot_in_room["right"]
515
+ <= int(robot_x)
516
+ <= self.robot_in_room["left"]
517
+ ) and (
518
+ self.robot_in_room["up"]
519
+ <= int(robot_y)
520
+ <= self.robot_in_room["down"]
521
+ ):
522
+ temp = {
523
+ "x": robot_x,
524
+ "y": robot_y,
525
+ "angle": angle,
526
+ "in_room": self.robot_in_room["room"],
527
+ }
528
+ # Handle active zones
529
+ self.active_zones = self.shared.rand256_active_zone
530
+ self.zooming = False
531
+ if self.active_zones and (
532
+ self.robot_in_room["id"] in range(len(self.active_zones))
533
+ ):
534
+ self.zooming = bool(self.active_zones[self.robot_in_room["id"]])
535
+ else:
536
+ self.zooming = False
537
+ return temp
538
+
539
+ # If we don't have a cached room or the robot is not in it, search all rooms
540
+ last_room = None
541
+ room_count = 0
542
+ if self.robot_in_room:
543
+ last_room = self.robot_in_room
360
544
 
361
- def _check_robot_position(x: int, y: int) -> bool:
362
- # Check if the robot coordinates are inside the room's corners
363
- return (
364
- self.robot_in_room["left"] >= x >= self.robot_in_room["right"]
365
- and self.robot_in_room["up"] >= y >= self.robot_in_room["down"]
545
+ # Check if the robot is far outside the normal map boundaries
546
+ # This helps prevent false positives for points very far from any room
547
+ map_boundary = 50000 # Typical map size is around 25000-30000 units for Rand25
548
+ if abs(robot_x) > map_boundary or abs(robot_y) > map_boundary:
549
+ _LOGGER.debug(
550
+ "%s robot position (%s, %s) is far outside map boundaries.",
551
+ self.file_name,
552
+ robot_x,
553
+ robot_y,
366
554
  )
367
-
368
- # If the robot coordinates are inside the room's
369
- if self.robot_in_room and _check_robot_position(robot_x, robot_y):
555
+ self.robot_in_room = last_room
556
+ self.zooming = False
370
557
  temp = {
371
558
  "x": robot_x,
372
559
  "y": robot_y,
373
560
  "angle": angle,
374
- "in_room": self.robot_in_room["room"],
561
+ "in_room": last_room["room"] if last_room else "unknown",
375
562
  }
376
- self.active_zones = self.shared.rand256_active_zone
377
- self.zooming = False
378
- if self.active_zones and (
379
- (self.robot_in_room["id"]) in range(len(self.active_zones))
380
- ): # issue #100 Index out of range
381
- self.zooming = bool(self.active_zones[self.robot_in_room["id"]])
382
563
  return temp
383
- # else we need to search and use the async method
384
- _LOGGER.debug("%s Changed room.. searching..", self.file_name)
385
- room_count = -1
386
- last_room = None
387
564
 
388
- # If no rooms data is available, return a default position
565
+ # Search through all rooms to find which one contains the robot
389
566
  if not self.rooms_pos:
390
- _LOGGER.debug("%s: No rooms data available", self.file_name)
391
- return {"x": robot_x, "y": robot_y, "angle": angle, "in_room": "unknown"}
567
+ _LOGGER.debug(
568
+ "%s: No rooms data available for robot position detection.",
569
+ self.file_name,
570
+ )
571
+ self.robot_in_room = last_room
572
+ self.zooming = False
573
+ temp = {
574
+ "x": robot_x,
575
+ "y": robot_y,
576
+ "angle": angle,
577
+ "in_room": last_room["room"] if last_room else "unknown",
578
+ }
579
+ return temp
392
580
 
393
- # If rooms data is available, search for the room
394
- if self.robot_in_room:
395
- last_room = self.robot_in_room
581
+ _LOGGER.debug("%s: Searching for robot in rooms...", self.file_name)
396
582
  for room in self.rooms_pos:
397
- corners = room["corners"]
583
+ # Check if the room has an outline (polygon points)
584
+ if "outline" in room:
585
+ outline = room["outline"]
586
+ # Use point_in_polygon for accurate detection with complex shapes
587
+ if self.point_in_polygon(int(robot_x), int(robot_y), outline):
588
+ # Robot is in this room
589
+ self.robot_in_room = {
590
+ "id": room_count,
591
+ "room": str(room["name"]),
592
+ "outline": outline,
593
+ }
594
+ temp = {
595
+ "x": robot_x,
596
+ "y": robot_y,
597
+ "angle": angle,
598
+ "in_room": self.robot_in_room["room"],
599
+ }
600
+
601
+ # Handle active zones - Set zooming based on active zones
602
+ self.active_zones = self.shared.rand256_active_zone
603
+ if self.active_zones and (
604
+ self.robot_in_room["id"] in range(len(self.active_zones))
605
+ ):
606
+ self.zooming = bool(self.active_zones[self.robot_in_room["id"]])
607
+ else:
608
+ self.zooming = False
609
+
610
+ _LOGGER.debug(
611
+ "%s is in %s room (polygon detection).",
612
+ self.file_name,
613
+ self.robot_in_room["room"],
614
+ )
615
+ return temp
398
616
  room_count += 1
399
- self.robot_in_room = {
400
- "id": room_count,
401
- "left": corners[0][0],
402
- "right": corners[2][0],
403
- "up": corners[0][1],
404
- "down": corners[2][1],
405
- "room": room["name"],
406
- }
407
- # Check if the robot coordinates are inside the room's corners
408
- if _check_robot_position(robot_x, robot_y):
409
- temp = {
410
- "x": robot_x,
411
- "y": robot_y,
412
- "angle": angle,
413
- "in_room": self.robot_in_room["room"],
414
- }
415
- _LOGGER.debug("%s is in %s", self.file_name, self.robot_in_room["room"])
416
- del room, corners, robot_x, robot_y # free memory.
417
- return temp
418
- # After checking all rooms and not finding a match
617
+
618
+ # Robot not found in any room
419
619
  _LOGGER.debug(
420
- "%s: Not located within Camera Rooms coordinates.", self.file_name
620
+ "%s not located within any room coordinates.",
621
+ self.file_name,
421
622
  )
422
- self.zooming = False
423
623
  self.robot_in_room = last_room
624
+ self.zooming = False
424
625
  temp = {
425
626
  "x": robot_x,
426
627
  "y": robot_y,
427
628
  "angle": angle,
428
- "in_room": self.robot_in_room["room"] if self.robot_in_room else "unknown",
629
+ "in_room": last_room["room"] if last_room else "unknown",
429
630
  }
430
631
  return temp
431
632
 
@@ -19,6 +19,7 @@ from .config.types import LOGGER, RoomsProperties
19
19
 
20
20
  from .map_data import RandImageData, ImageData
21
21
 
22
+
22
23
  class RoomsHandler:
23
24
  """
24
25
  Handler for extracting and managing room data from Hipfer vacuum maps.
@@ -225,6 +226,7 @@ class RoomsHandler:
225
226
  LOGGER.debug("Room extraction Total time: %.3fs", total_time)
226
227
  return room_properties
227
228
 
229
+
228
230
  class RandRoomsHandler:
229
231
  """
230
232
  Handler for extracting and managing room data from Rand25 vacuum maps.
@@ -247,7 +249,9 @@ class RandRoomsHandler:
247
249
  """
248
250
  self.vacuum_id = vacuum_id
249
251
  self.drawing_config = drawing_config
250
- self.current_json_data = None # Will store the current JSON data being processed
252
+ self.current_json_data = (
253
+ None # Will store the current JSON data being processed
254
+ )
251
255
  self.segment_data = None # Segment data
252
256
  self.outlines = None # Outlines data
253
257
 
@@ -467,4 +471,4 @@ class RandRoomsHandler:
467
471
  total_time = time.time() - start_total
468
472
  LOGGER.debug("Room extraction Total time: %.3fs", total_time)
469
473
 
470
- return room_properties
474
+ return room_properties
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: valetudo-map-parser
3
- Version: 0.1.9b55
3
+ Version: 0.1.9b57
4
4
  Summary: A Python library to parse Valetudo map data returning a PIL Image object.
5
5
  License: Apache-2.0
6
6
  Author: Sandro Cantarella
@@ -0,0 +1,26 @@
1
+ valetudo_map_parser/__init__.py,sha256=XO_eJwFDyU7hXJ4tAa2zY-n-SM2_kmIGMWDKY3GcauY,1163
2
+ valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
3
+ valetudo_map_parser/config/auto_crop.py,sha256=1TGjUSbVHV15_sx1iFsEBWwD5BnWHPjgGIuPAJB6aiY,19142
4
+ valetudo_map_parser/config/color_utils.py,sha256=nXD6WeNmdFdoMxPDW-JFpjnxJSaZR1jX-ouNfrx6zvE,4502
5
+ valetudo_map_parser/config/colors.py,sha256=DG-oPQoN5gsnwDbEsuFr8a0hRCxmbFHObWa4_5pr-70,29910
6
+ valetudo_map_parser/config/drawable.py,sha256=2MeVHXqZuVuJk3eerMJYGwo25rVetHx3xB_vxecEFOQ,34168
7
+ valetudo_map_parser/config/drawable_elements.py,sha256=o-5oiXmfqPwNQLzKIhkEcZD_A47rIU9E0CqKgWipxgc,11516
8
+ valetudo_map_parser/config/enhanced_drawable.py,sha256=QlGxlUMVgECUXPtFwIslyjubWxQuhIixsRymWV3lEvk,12586
9
+ valetudo_map_parser/config/optimized_element_map.py,sha256=52BCnkvVv9bre52LeVIfT8nhnEIpc0TuWTv1xcNu0Rk,15744
10
+ valetudo_map_parser/config/rand25_parser.py,sha256=kIayyqVZBfQfAMkiArzqrrj9vqZB3pkgT0Y5ufrQmGA,16448
11
+ valetudo_map_parser/config/shared.py,sha256=ogXR5qC2HuLJ3KgYc9KDozXzDQptd8nhvNc-CepN6S0,12301
12
+ valetudo_map_parser/config/types.py,sha256=HO5fowAjbGmS2rEOFbFe7eFljvpfBr0fsvhcVRTByhI,17540
13
+ valetudo_map_parser/config/utils.py,sha256=w8r53KcCgYNq7CoIziQ3ktJu8ESFit5TYza-7g8ndhE,31203
14
+ valetudo_map_parser/hypfer_draw.py,sha256=Bi03FiYdxw2Kp8BwkggAkdKABNoNf-j7v7J-rER8tnQ,28953
15
+ valetudo_map_parser/hypfer_handler.py,sha256=-tMYd1lMH6_l8ufQH1A95uAgbt2P-1YYTfNkl9NXAc8,22269
16
+ valetudo_map_parser/hypfer_rooms_handler.py,sha256=NkpOA6Gdq-2D3lLAxvtNuuWMvPXHxeMY2TO5RZLSHlU,22652
17
+ valetudo_map_parser/map_data.py,sha256=5DDT5ABJCMSaWd8YrMY9SG4uhD3Rga_F0DEBNNNPipc,18618
18
+ valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ valetudo_map_parser/rand256_handler.py,sha256=kLurqSQvmrweCrOpLT9oIuVKkQ_SmCXcRuSL4dDddO8,27893
20
+ valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
21
+ valetudo_map_parser/rooms_handler.py,sha256=ovqQtAjauAqwUNPR0aX27P2zhheQmqfaFhDE3_AwYWk,17821
22
+ valetudo_map_parser-0.1.9b57.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
23
+ valetudo_map_parser-0.1.9b57.dist-info/METADATA,sha256=f2DJy0uFpMdGp1e0gsdG2OdpGzkB1A429ZKf2c49MOE,3321
24
+ valetudo_map_parser-0.1.9b57.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
25
+ valetudo_map_parser-0.1.9b57.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
26
+ valetudo_map_parser-0.1.9b57.dist-info/RECORD,,