valetudo-map-parser 0.1.9b67__py3-none-any.whl → 0.1.9b68__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.
@@ -14,7 +14,6 @@ import logging
14
14
  import math
15
15
  import asyncio
16
16
  import inspect
17
- import threading
18
17
 
19
18
  import numpy as np
20
19
  from PIL import ImageDraw, ImageFont
@@ -27,72 +26,6 @@ from .types import Color, NumpyArray, PilPNG, Point, Tuple, Union
27
26
  _LOGGER = logging.getLogger(__name__)
28
27
 
29
28
 
30
- class ImageArrayPool:
31
- """Thread-safe memory pool for reusing image arrays to reduce allocation overhead."""
32
-
33
- def __init__(self, max_arrays_per_size: int = 3):
34
- self._pools = {} # {(width, height): [array1, array2, ...]}
35
- self._lock = threading.Lock()
36
- self._max_arrays_per_size = max_arrays_per_size
37
-
38
- def get_array(self, width: int, height: int, background_color: Color) -> NumpyArray:
39
- """Get a reusable array or create a new one if none available."""
40
- key = (width, height)
41
-
42
- with self._lock:
43
- if key in self._pools and self._pools[key]:
44
- # Reuse existing array
45
- array = self._pools[key].pop()
46
- _LOGGER.debug("Reused array from pool for size %dx%d", width, height)
47
- else:
48
- # Create new array
49
- array = np.empty((height, width, 4), dtype=np.uint8)
50
- _LOGGER.debug("Created new array for size %dx%d", width, height)
51
-
52
- # Fill with background color (outside lock for better performance)
53
- array[:] = background_color
54
- return array
55
-
56
- def return_array(self, array: NumpyArray) -> None:
57
- """Return an array to the pool for reuse."""
58
- if array is None:
59
- return
60
-
61
- height, width = array.shape[:2]
62
- key = (width, height)
63
-
64
- with self._lock:
65
- if key not in self._pools:
66
- self._pools[key] = []
67
-
68
- # Only keep up to max_arrays_per_size arrays per size
69
- if len(self._pools[key]) < self._max_arrays_per_size:
70
- self._pools[key].append(array)
71
- _LOGGER.debug("Returned array to pool for size %dx%d (pool size: %d)",
72
- width, height, len(self._pools[key]))
73
- else:
74
- _LOGGER.debug("Pool full for size %dx%d, discarding array", width, height)
75
-
76
- def clear_pool(self) -> None:
77
- """Clear all arrays from the pool."""
78
- with self._lock:
79
- total_arrays = sum(len(arrays) for arrays in self._pools.values())
80
- self._pools.clear()
81
- _LOGGER.debug("Cleared image array pool (%d arrays freed)", total_arrays)
82
-
83
- def get_pool_stats(self) -> dict:
84
- """Get statistics about the current pool state."""
85
- with self._lock:
86
- stats = {}
87
- for (width, height), arrays in self._pools.items():
88
- stats[f"{width}x{height}"] = len(arrays)
89
- return stats
90
-
91
-
92
- # Global shared pool instance for both Hypfer and Rand256 handlers
93
- _image_pool = ImageArrayPool()
94
-
95
-
96
29
  class Drawable:
97
30
  """
98
31
  Collection of drawing utility functions for the image handlers.
@@ -112,27 +45,13 @@ class Drawable:
112
45
  async def create_empty_image(
113
46
  width: int, height: int, background_color: Color
114
47
  ) -> NumpyArray:
115
- """Create the empty background image NumPy array using memory pool for better performance.
48
+ """Create the empty background image NumPy array.
116
49
  Background color is specified as an RGBA tuple.
117
- Optimized: Uses shared memory pool to reuse arrays and reduce allocation overhead."""
118
- # Get array from shared pool (reuses memory when possible)
119
- return _image_pool.get_array(width, height, background_color)
120
-
121
- @staticmethod
122
- def return_image_to_pool(image_array: NumpyArray) -> None:
123
- """Return an image array to the memory pool for reuse.
124
- Call this when you're done with an image array to enable memory reuse."""
125
- _image_pool.return_array(image_array)
126
-
127
- @staticmethod
128
- def get_pool_stats() -> dict:
129
- """Get statistics about the current memory pool state."""
130
- return _image_pool.get_pool_stats()
131
-
132
- @staticmethod
133
- def clear_image_pool() -> None:
134
- """Clear all arrays from the memory pool."""
135
- _image_pool.clear_pool()
50
+ Optimized: Uses np.empty + broadcast instead of np.full for better performance."""
51
+ # Use np.empty + broadcast instead of np.full (avoids double initialization)
52
+ img_array = np.empty((height, width, 4), dtype=np.uint8)
53
+ img_array[:] = background_color # Broadcast color to all pixels efficiently
54
+ return img_array
136
55
 
137
56
  @staticmethod
138
57
  async def from_json_to_image(
@@ -39,6 +39,7 @@ from .types import (
39
39
  CameraModes,
40
40
  Colors,
41
41
  TrimsData,
42
+ PilPNG,
42
43
  )
43
44
 
44
45
 
@@ -58,9 +59,10 @@ class CameraShared:
58
59
  self.rand256_active_zone: list = [] # Active zone for rand256
59
60
  self.is_rand: bool = False # MQTT rand data
60
61
  self._new_mqtt_message = False # New MQTT message
61
- self.last_image = None # Last image received
62
- self.current_image = None # Current image
63
- self.binary_image = None # Current image in binary format
62
+ self.last_image = PilPNG | None # Last image received
63
+ self.new_image: PilPNG | None = None # New image received
64
+ self.binary_image: bytes | None = None # Current image in binary format
65
+ self.image_last_updated: float = 0.0 # Last image update time
64
66
  self.image_format = "image/pil" # Image format
65
67
  self.image_size = None # Image size
66
68
  self.image_auto_zoom: bool = False # Auto zoom image
@@ -115,7 +117,7 @@ class CameraShared:
115
117
 
116
118
 
117
119
 
118
- def _state_charging(self) -> bool:
120
+ def vacuum_bat_charged(self) -> bool:
119
121
  """Check if the vacuum is charging."""
120
122
  return (self.vacuum_state == "docked") and (int(self.vacuum_battery) < 100)
121
123
 
@@ -193,7 +195,7 @@ class CameraShared:
193
195
  attrs = {
194
196
  ATTR_CAMERA_MODE: self.camera_mode,
195
197
  ATTR_VACUUM_BATTERY: f"{self.vacuum_battery}%",
196
- ATTR_VACUUM_CHARGING: self._state_charging(),
198
+ ATTR_VACUUM_CHARGING: self.vacuum_bat_charged,
197
199
  ATTR_VACUUM_POSITION: self.current_room,
198
200
  ATTR_VACUUM_STATUS: self.vacuum_state,
199
201
  ATTR_VACUUM_JSON_ID: self.vac_json_id,
@@ -228,12 +230,13 @@ class CameraShared:
228
230
  class CameraSharedManager:
229
231
  """Camera Shared Manager class."""
230
232
 
231
- def __init__(self, file_name, device_info):
233
+ def __init__(self, file_name: str, device_info: dict = None):
232
234
  self._instances = {}
233
235
  self._lock = asyncio.Lock()
234
236
  self.file_name = file_name
235
- self.device_info = device_info
236
- self.update_shared_data(device_info)
237
+ if device_info:
238
+ self.device_info = device_info
239
+ self.update_shared_data(device_info)
237
240
 
238
241
  # Automatically initialize shared data for the instance
239
242
  # self._init_shared_data(device_info)
@@ -1,5 +1,6 @@
1
1
  """Utility code for the valetudo map parser."""
2
2
 
3
+ import datetime
3
4
  import hashlib
4
5
  import json
5
6
  from dataclasses import dataclass
@@ -79,6 +80,67 @@ class BaseHandler:
79
80
  """Return the robot position."""
80
81
  return self.robot_pos
81
82
 
83
+ async def async_get_pil_image(
84
+ self,
85
+ m_json: dict | None,
86
+ destinations: list | None = None,
87
+ ) -> PilPNG | None:
88
+ """
89
+ Unified async function to get PIL image from JSON data for both Hypfer and Rand256 handlers.
90
+
91
+ This function:
92
+ 1. Calls the appropriate async_get_image_from_json method
93
+ 2. Stores the processed data in shared.new_image
94
+ 3. Backs up previous data to shared.last_image
95
+ 4. Updates shared.image_last_updated with current datetime
96
+
97
+ @param m_json: The JSON data to use to draw the image
98
+ @param destinations: MQTT destinations for labels (used by Rand256)
99
+ @return: PIL Image or None
100
+ """
101
+ try:
102
+ # Backup current image to last_image before processing new one
103
+ if hasattr(self.shared, 'new_image') and self.shared.new_image is not None:
104
+ self.shared.last_image = self.shared.new_image
105
+
106
+ # Call the appropriate handler method based on handler type
107
+ if hasattr(self, 'get_image_from_rrm'):
108
+ # This is a Rand256 handler
109
+ new_image = await self.get_image_from_rrm(
110
+ m_json=m_json,
111
+ destinations=destinations,
112
+ return_webp=False # Always return PIL Image
113
+ )
114
+ elif hasattr(self, 'async_get_image_from_json'):
115
+ # This is a Hypfer handler
116
+ new_image = await self.async_get_image_from_json(
117
+ m_json=m_json,
118
+ return_webp=False # Always return PIL Image
119
+ )
120
+ else:
121
+ LOGGER.warning("%s: Handler type not recognized for async_get_pil_image", self.file_name)
122
+ return None
123
+
124
+ # Store the new image in shared data
125
+ if new_image is not None:
126
+ self.shared.new_image = new_image
127
+ # Update the timestamp with current datetime
128
+ self.shared.image_last_updated = datetime.datetime.now().timestamp()
129
+ LOGGER.debug("%s: Image processed and stored in shared data", self.file_name)
130
+ else:
131
+ LOGGER.warning("%s: Failed to generate image from JSON data", self.file_name)
132
+
133
+ return new_image
134
+
135
+ except Exception as e:
136
+ LOGGER.error(
137
+ "%s: Error in async_get_pil_image: %s",
138
+ self.file_name,
139
+ str(e),
140
+ exc_info=True
141
+ )
142
+ return None
143
+
82
144
  def get_charger_position(self) -> ChargerPosition | None:
83
145
  """Return the charger position."""
84
146
  return self.charger_pos
@@ -8,11 +8,10 @@ Version: 0.1.9
8
8
  from __future__ import annotations
9
9
 
10
10
  import asyncio
11
- import json
12
11
 
13
12
  from PIL import Image
14
13
 
15
- from .config.async_utils import AsyncNumPy, AsyncPIL, AsyncParallel
14
+ from .config.async_utils import AsyncNumPy, AsyncPIL
16
15
  from .config.auto_crop import AutoCrop
17
16
  from .config.drawable_elements import DrawableElement
18
17
  from .config.shared import CameraShared
@@ -25,6 +24,7 @@ from .config.types import (
25
24
  RoomsProperties,
26
25
  RoomStore,
27
26
  WebPBytes,
27
+ JsonType,
28
28
  )
29
29
  from .config.utils import (
30
30
  BaseHandler,
@@ -100,7 +100,7 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
100
100
  # noinspection PyUnresolvedReferences,PyUnboundLocalVariable
101
101
  async def async_get_image_from_json(
102
102
  self,
103
- m_json: json | None,
103
+ m_json: JsonType | None,
104
104
  return_webp: bool = False,
105
105
  ) -> WebPBytes | Image.Image | None:
106
106
  """Get the image from the JSON data.
@@ -232,13 +232,6 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
232
232
  disabled_rooms if layer_type == "wall" else None,
233
233
  )
234
234
 
235
- # Update element map for this layer
236
- if is_room_layer and 0 < room_id <= 15:
237
- # Mark the room in the element map
238
- room_element = getattr(
239
- DrawableElement, f"ROOM_{room_id}", None
240
- )
241
-
242
235
  # Draw the virtual walls if enabled
243
236
  if self.drawing_config.is_enabled(DrawableElement.VIRTUAL_WALL):
244
237
  img_np_array = await self.imd.async_draw_virtual_walls(
@@ -313,10 +306,6 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
313
306
  LOGGER.info("%s: Drawing path", self.file_name)
314
307
  data_tasks.append(self._prepare_path_data(m_json))
315
308
 
316
- # Execute data preparation in parallel if we have tasks
317
- if data_tasks:
318
- prepared_data = await AsyncParallel.parallel_data_preparation(*data_tasks)
319
-
320
309
  # Process drawing operations sequentially (since they modify the same array)
321
310
  # Draw zones if enabled
322
311
  if self.drawing_config.is_enabled(DrawableElement.RESTRICTED_AREA):
@@ -390,9 +379,7 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
390
379
  if self.check_zoom_and_aspect_ratio():
391
380
  # Convert to PIL for resizing
392
381
  pil_img = await AsyncPIL.async_fromarray(img_np_array, mode="RGBA")
393
- # Return array to pool for reuse instead of just deleting
394
- from .config.drawable import Drawable
395
- Drawable.return_image_to_pool(img_np_array)
382
+ del img_np_array
396
383
  resize_params = prepare_resize_params(self, pil_img, False)
397
384
  resized_image = await self.async_resize_images(resize_params)
398
385
 
@@ -407,17 +394,13 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
407
394
  if return_webp:
408
395
  # Convert directly from NumPy to WebP for better performance
409
396
  webp_bytes = await numpy_to_webp_bytes(img_np_array)
410
- # Return array to pool for reuse instead of just deleting
411
- from .config.drawable import Drawable
412
- Drawable.return_image_to_pool(img_np_array)
397
+ del img_np_array
413
398
  LOGGER.debug("%s: Frame Completed.", self.file_name)
414
399
  return webp_bytes
415
400
  else:
416
401
  # Convert to PIL Image (original behavior)
417
402
  pil_img = await AsyncPIL.async_fromarray(img_np_array, mode="RGBA")
418
- # Return array to pool for reuse instead of just deleting
419
- from .config.drawable import Drawable
420
- Drawable.return_image_to_pool(img_np_array)
403
+ del img_np_array
421
404
  LOGGER.debug("%s: Frame Completed.", self.file_name)
422
405
  return pil_img
423
406
  except (RuntimeError, RuntimeWarning) as e:
@@ -506,7 +489,8 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
506
489
  except (ValueError, KeyError):
507
490
  return None
508
491
 
509
- async def _prepare_goto_data(self, entity_dict):
492
+ @staticmethod
493
+ async def _prepare_goto_data(entity_dict):
510
494
  """Prepare go-to flag data for parallel processing."""
511
495
  await asyncio.sleep(0) # Yield control
512
496
  # Extract go-to target data from entity_dict
@@ -516,6 +500,6 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
516
500
  """Prepare path data for parallel processing."""
517
501
  await asyncio.sleep(0) # Yield control
518
502
  try:
519
- return self.data.find_path_entities(m_json)
503
+ return self.data.find_paths_entities(m_json)
520
504
  except (ValueError, KeyError):
521
505
  return None
@@ -7,15 +7,13 @@ Version: 0.1.9.a6
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- import asyncio
11
10
  import logging
12
11
  import uuid
13
12
  from typing import Any
14
13
 
15
14
  import numpy as np
16
- from PIL import Image
17
15
 
18
- from .config.async_utils import AsyncNumPy, AsyncPIL, AsyncParallel
16
+ from .config.async_utils import AsyncNumPy, AsyncPIL
19
17
  from .config.auto_crop import AutoCrop
20
18
  from .config.drawable_elements import DrawableElement
21
19
  from .config.types import (
@@ -147,7 +145,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
147
145
  m_json: JsonType, # json data
148
146
  destinations: None = None, # MQTT destinations for labels
149
147
  return_webp: bool = False,
150
- ) -> WebPBytes | Image.Image | None:
148
+ ) -> WebPBytes | PilPNG | None:
151
149
  """Generate Images from the json data.
152
150
  @param m_json: The JSON data to use to draw the image.
153
151
  @param destinations: MQTT destinations for labels (unused).
@@ -168,14 +166,6 @@ class ReImageHandler(BaseHandler, AutoCrop):
168
166
  self.json_id = str(uuid.uuid4()) # image id
169
167
  _LOGGER.info("Vacuum Data ID: %s", self.json_id)
170
168
 
171
- # Prepare parallel data extraction tasks
172
- data_tasks = []
173
- data_tasks.append(self._prepare_zone_data(m_json))
174
- data_tasks.append(self._prepare_path_data(m_json))
175
-
176
- # Execute data preparation tasks in parallel
177
- zone_data, path_data = await asyncio.gather(*data_tasks, return_exceptions=True)
178
-
179
169
  (
180
170
  img_np_array,
181
171
  robot_position,
@@ -202,16 +192,12 @@ class ReImageHandler(BaseHandler, AutoCrop):
202
192
  if return_webp:
203
193
  # Convert directly to WebP bytes for better performance
204
194
  webp_bytes = await numpy_to_webp_bytes(img_np_array)
205
- # Return array to pool for reuse instead of just deleting
206
- from .config.drawable import Drawable
207
- Drawable.return_image_to_pool(img_np_array)
195
+ del img_np_array # free memory
208
196
  return webp_bytes
209
197
  else:
210
198
  # Convert to PIL Image using async utilities
211
199
  pil_img = await AsyncPIL.async_fromarray(img_np_array, mode="RGBA")
212
- # Return array to pool for reuse instead of just deleting
213
- from .config.drawable import Drawable
214
- Drawable.return_image_to_pool(img_np_array)
200
+ del img_np_array # free memory
215
201
  return await self._finalize_image(pil_img)
216
202
 
217
203
  except (RuntimeError, RuntimeWarning) as e:
@@ -311,11 +297,6 @@ class ReImageHandler(BaseHandler, AutoCrop):
311
297
  original_rooms_pos = self.rooms_pos
312
298
  self.rooms_pos = temp_rooms_pos
313
299
 
314
- # Perform robot room detection to check active zones
315
- robot_room_result = await self.async_get_robot_in_room(
316
- robot_position[0], robot_position[1], robot_position_angle
317
- )
318
-
319
300
  # Restore original rooms_pos
320
301
  self.rooms_pos = original_rooms_pos
321
302
 
@@ -692,20 +673,4 @@ class ReImageHandler(BaseHandler, AutoCrop):
692
673
  """Copy the array using async utilities."""
693
674
  return await AsyncNumPy.async_copy(original_array)
694
675
 
695
- async def _prepare_zone_data(self, m_json):
696
- """Prepare zone data for parallel processing."""
697
- await asyncio.sleep(0) # Yield control
698
- try:
699
- return self.data.find_zone_entities(m_json)
700
- except (ValueError, KeyError):
701
- return None
702
-
703
- async def _prepare_path_data(self, m_json):
704
- """Prepare path data for parallel processing."""
705
- await asyncio.sleep(0) # Yield control
706
- try:
707
- return self.data.find_path_entities(m_json)
708
- except (ValueError, KeyError):
709
- return None
710
-
711
676
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: valetudo-map-parser
3
- Version: 0.1.9b67
3
+ Version: 0.1.9b68
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
@@ -4,24 +4,24 @@ valetudo_map_parser/config/async_utils.py,sha256=r96x4rVis2y57KSnWfVxraSGi0cOz0f
4
4
  valetudo_map_parser/config/auto_crop.py,sha256=Aes7vfv4z8ihYvGaH5Nryj6Y9mHDerZLIeyvePjf9aQ,19259
5
5
  valetudo_map_parser/config/color_utils.py,sha256=nXD6WeNmdFdoMxPDW-JFpjnxJSaZR1jX-ouNfrx6zvE,4502
6
6
  valetudo_map_parser/config/colors.py,sha256=DG-oPQoN5gsnwDbEsuFr8a0hRCxmbFHObWa4_5pr-70,29910
7
- valetudo_map_parser/config/drawable.py,sha256=d9hdEjmlXnbnEKgy2RiKVGiUlU8pbWF14MfNlVpDgcw,40947
7
+ valetudo_map_parser/config/drawable.py,sha256=UpmXlCqZX_D7rqepk8IzTrzflgweLQ7n05ZeGIFRur8,37850
8
8
  valetudo_map_parser/config/drawable_elements.py,sha256=o-5oiXmfqPwNQLzKIhkEcZD_A47rIU9E0CqKgWipxgc,11516
9
9
  valetudo_map_parser/config/enhanced_drawable.py,sha256=QlGxlUMVgECUXPtFwIslyjubWxQuhIixsRymWV3lEvk,12586
10
10
  valetudo_map_parser/config/optimized_element_map.py,sha256=52BCnkvVv9bre52LeVIfT8nhnEIpc0TuWTv1xcNu0Rk,15744
11
11
  valetudo_map_parser/config/rand256_parser.py,sha256=LU3y7XvRRQxVen9iwom0dOaDnJJvhZdg97NqOYRZFas,16279
12
- valetudo_map_parser/config/shared.py,sha256=J_66BuhgqRJUjXKBwKh8qih1iyiTq9ZiaZwduIROYmE,12560
12
+ valetudo_map_parser/config/shared.py,sha256=AQ73878TGCxbHQhgrAxSROLqFE-Zz4fJhTdoi9gBvJo,12736
13
13
  valetudo_map_parser/config/types.py,sha256=saL7pULKAdTRQ_ShR2arT8IV472e9MBC_SohTthlGp8,17567
14
- valetudo_map_parser/config/utils.py,sha256=fnzzNywE0Z1qw968hUa_yGslq3YhOP7daFj21miuhjI,31354
14
+ valetudo_map_parser/config/utils.py,sha256=MR1UIOwHWYJ8lFrPYhfyi9IV7C08S-DqCyIPppsB2tM,33913
15
15
  valetudo_map_parser/hypfer_draw.py,sha256=ZK_WybvukHd8nNk2mq5icrOu1Ue3SVCIC6_Hc9bTg0Q,29396
16
- valetudo_map_parser/hypfer_handler.py,sha256=X-okVYDeHRWfwx08XIvqimoI-mghqekTe3GlyzV62gA,24214
16
+ valetudo_map_parser/hypfer_handler.py,sha256=f9SCphArA-LO2ySrTKpxn6k4htM-JRrbrxKFh3AjnD8,23171
17
17
  valetudo_map_parser/hypfer_rooms_handler.py,sha256=NkpOA6Gdq-2D3lLAxvtNuuWMvPXHxeMY2TO5RZLSHlU,22652
18
18
  valetudo_map_parser/map_data.py,sha256=Op0LTCakcTJ1Q0rxQhl6BpgSby_6nJenCQS2Y2FHtRk,17243
19
19
  valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- valetudo_map_parser/rand256_handler.py,sha256=vrfoX7RM8NFhpbzrc2BSLEA8VGx4B_sXBG_uAZSO8W0,29216
20
+ valetudo_map_parser/rand256_handler.py,sha256=6Qt-xFJ3PITQNSAJTaEo6jl6TdgFms7ay1rnk4vekgk,27653
21
21
  valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
22
22
  valetudo_map_parser/rooms_handler.py,sha256=ovqQtAjauAqwUNPR0aX27P2zhheQmqfaFhDE3_AwYWk,17821
23
- valetudo_map_parser-0.1.9b67.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
24
- valetudo_map_parser-0.1.9b67.dist-info/METADATA,sha256=0ItPS5MJeqacL3gkVvxoL9T9jvRJi5o9rFmNwByD7cA,3353
25
- valetudo_map_parser-0.1.9b67.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
26
- valetudo_map_parser-0.1.9b67.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
27
- valetudo_map_parser-0.1.9b67.dist-info/RECORD,,
23
+ valetudo_map_parser-0.1.9b68.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
24
+ valetudo_map_parser-0.1.9b68.dist-info/METADATA,sha256=hBV8Inc5QpIw-TcGWC-sLqhRb60TlFq1OVkMmm1iBek,3353
25
+ valetudo_map_parser-0.1.9b68.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
26
+ valetudo_map_parser-0.1.9b68.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
27
+ valetudo_map_parser-0.1.9b68.dist-info/RECORD,,