valetudo-map-parser 0.1.8__py3-none-any.whl → 0.1.9a2__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.
- valetudo_map_parser/__init__.py +19 -12
- valetudo_map_parser/config/auto_crop.py +174 -116
- valetudo_map_parser/config/color_utils.py +105 -0
- valetudo_map_parser/config/colors.py +662 -13
- valetudo_map_parser/config/drawable.py +624 -279
- valetudo_map_parser/config/drawable_elements.py +292 -0
- valetudo_map_parser/config/enhanced_drawable.py +324 -0
- valetudo_map_parser/config/optimized_element_map.py +406 -0
- valetudo_map_parser/config/rand25_parser.py +42 -28
- valetudo_map_parser/config/room_outline.py +148 -0
- valetudo_map_parser/config/shared.py +73 -6
- valetudo_map_parser/config/types.py +102 -51
- valetudo_map_parser/config/utils.py +841 -0
- valetudo_map_parser/hypfer_draw.py +398 -132
- valetudo_map_parser/hypfer_handler.py +259 -241
- valetudo_map_parser/hypfer_rooms_handler.py +599 -0
- valetudo_map_parser/map_data.py +45 -64
- valetudo_map_parser/rand25_handler.py +429 -310
- valetudo_map_parser/reimg_draw.py +55 -74
- valetudo_map_parser/rooms_handler.py +470 -0
- valetudo_map_parser-0.1.9a2.dist-info/METADATA +93 -0
- valetudo_map_parser-0.1.9a2.dist-info/RECORD +27 -0
- {valetudo_map_parser-0.1.8.dist-info → valetudo_map_parser-0.1.9a2.dist-info}/WHEEL +1 -1
- valetudo_map_parser/images_utils.py +0 -398
- valetudo_map_parser-0.1.8.dist-info/METADATA +0 -23
- valetudo_map_parser-0.1.8.dist-info/RECORD +0 -20
- {valetudo_map_parser-0.1.8.dist-info → valetudo_map_parser-0.1.9a2.dist-info}/LICENSE +0 -0
- {valetudo_map_parser-0.1.8.dist-info → valetudo_map_parser-0.1.9a2.dist-info}/NOTICE.txt +0 -0
@@ -0,0 +1,470 @@
|
|
1
|
+
"""
|
2
|
+
Hipfer Rooms Handler Module.
|
3
|
+
Handles room data extraction and processing for Valetudo Hipfer vacuum maps.
|
4
|
+
Provides async methods for room outline extraction and properties management.
|
5
|
+
Version: 0.1.9
|
6
|
+
"""
|
7
|
+
|
8
|
+
from __future__ import annotations
|
9
|
+
|
10
|
+
import time
|
11
|
+
from typing import Any, Dict, List, Optional, Tuple
|
12
|
+
|
13
|
+
import numpy as np
|
14
|
+
from scipy.ndimage import binary_dilation, binary_erosion
|
15
|
+
from scipy.spatial import ConvexHull
|
16
|
+
|
17
|
+
from .config.drawable_elements import DrawableElement, DrawingConfig
|
18
|
+
from .config.types import LOGGER, RoomsProperties
|
19
|
+
|
20
|
+
from .map_data import RandImageData, ImageData
|
21
|
+
|
22
|
+
class RoomsHandler:
|
23
|
+
"""
|
24
|
+
Handler for extracting and managing room data from Hipfer vacuum maps.
|
25
|
+
|
26
|
+
This class provides methods to:
|
27
|
+
- Extract room outlines using the Ramer-Douglas-Peucker algorithm
|
28
|
+
- Process room properties from JSON data
|
29
|
+
- Generate room masks and extract contours
|
30
|
+
|
31
|
+
All methods are async for better integration with the rest of the codebase.
|
32
|
+
"""
|
33
|
+
|
34
|
+
def __init__(self, vacuum_id: str, drawing_config: Optional[DrawingConfig] = None):
|
35
|
+
"""
|
36
|
+
Initialize the HipferRoomsHandler.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
vacuum_id: Identifier for the vacuum
|
40
|
+
drawing_config: Configuration for which elements to draw (optional)
|
41
|
+
"""
|
42
|
+
self.vacuum_id = vacuum_id
|
43
|
+
self.drawing_config = drawing_config
|
44
|
+
self.current_json_data = (
|
45
|
+
None # Will store the current JSON data being processed
|
46
|
+
)
|
47
|
+
|
48
|
+
@staticmethod
|
49
|
+
def sublist(data: list, chunk_size: int) -> list:
|
50
|
+
return [data[i : i + chunk_size] for i in range(0, len(data), chunk_size)]
|
51
|
+
|
52
|
+
@staticmethod
|
53
|
+
def convex_hull_outline(mask: np.ndarray) -> list[tuple[int, int]]:
|
54
|
+
y_indices, x_indices = np.where(mask > 0)
|
55
|
+
if len(x_indices) == 0 or len(y_indices) == 0:
|
56
|
+
return []
|
57
|
+
|
58
|
+
points = np.column_stack((x_indices, y_indices))
|
59
|
+
if len(points) < 3:
|
60
|
+
return [(int(x), int(y)) for x, y in points]
|
61
|
+
|
62
|
+
hull = ConvexHull(points)
|
63
|
+
# Convert numpy.int64 values to regular Python integers
|
64
|
+
hull_points = [
|
65
|
+
(int(points[vertex][0]), int(points[vertex][1])) for vertex in hull.vertices
|
66
|
+
]
|
67
|
+
if hull_points[0] != hull_points[-1]:
|
68
|
+
hull_points.append(hull_points[0])
|
69
|
+
return hull_points
|
70
|
+
|
71
|
+
async def _process_room_layer(
|
72
|
+
self, layer: Dict[str, Any], width: int, height: int, pixel_size: int
|
73
|
+
) -> Tuple[Optional[str], Optional[Dict[str, Any]]]:
|
74
|
+
"""Process a single room layer and extract its outline.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
layer: The layer data from the JSON
|
78
|
+
width: The width of the map
|
79
|
+
height: The height of the map
|
80
|
+
pixel_size: The size of each pixel
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
Tuple of (room_id, room_data) or (None, None) if processing failed
|
84
|
+
"""
|
85
|
+
meta_data = layer.get("metaData", {})
|
86
|
+
segment_id = meta_data.get("segmentId")
|
87
|
+
name = meta_data.get("name", "Room {}".format(segment_id))
|
88
|
+
compressed_pixels = layer.get("compressedPixels", [])
|
89
|
+
pixels = self.sublist(compressed_pixels, 3)
|
90
|
+
|
91
|
+
# Check if this room is enabled in the drawing configuration
|
92
|
+
if self.drawing_config is not None:
|
93
|
+
# Convert segment_id to room element (ROOM_1 to ROOM_15)
|
94
|
+
try:
|
95
|
+
# Segment IDs might not be sequential, so we need to map them to room elements
|
96
|
+
# We'll use a simple approach: if segment_id is an integer, use it directly
|
97
|
+
room_element_id = int(segment_id)
|
98
|
+
if 1 <= room_element_id <= 15:
|
99
|
+
room_element = getattr(
|
100
|
+
DrawableElement, f"ROOM_{room_element_id}", None
|
101
|
+
)
|
102
|
+
if room_element:
|
103
|
+
is_enabled = self.drawing_config.is_enabled(room_element)
|
104
|
+
if not is_enabled:
|
105
|
+
# Skip this room if it's disabled
|
106
|
+
LOGGER.debug("Skipping disabled room %s", segment_id)
|
107
|
+
return None, None
|
108
|
+
except (ValueError, TypeError):
|
109
|
+
# If segment_id is not a valid integer, we can't map it to a room element
|
110
|
+
# In this case, we'll include the room (fail open)
|
111
|
+
LOGGER.debug(
|
112
|
+
"Could not convert segment_id %s to room element", segment_id
|
113
|
+
)
|
114
|
+
|
115
|
+
# Optimization: Create a smaller mask for just the room area
|
116
|
+
if not pixels:
|
117
|
+
# Skip if no pixels
|
118
|
+
return None, None
|
119
|
+
|
120
|
+
# Convert to numpy arrays for vectorized operations
|
121
|
+
pixel_data = np.array(pixels)
|
122
|
+
|
123
|
+
if pixel_data.size == 0:
|
124
|
+
return None, None
|
125
|
+
|
126
|
+
# Find the actual bounds of the room to create a smaller mask
|
127
|
+
# Add padding to ensure we don't lose edge details
|
128
|
+
padding = 10 # Add padding pixels around the room
|
129
|
+
min_x = max(0, int(np.min(pixel_data[:, 0])) - padding)
|
130
|
+
max_x = min(
|
131
|
+
width, int(np.max(pixel_data[:, 0]) + np.max(pixel_data[:, 2])) + padding
|
132
|
+
)
|
133
|
+
min_y = max(0, int(np.min(pixel_data[:, 1])) - padding)
|
134
|
+
max_y = min(height, int(np.max(pixel_data[:, 1]) + 1) + padding)
|
135
|
+
|
136
|
+
# Create a smaller mask for just the room area (much faster)
|
137
|
+
local_width = max_x - min_x
|
138
|
+
local_height = max_y - min_y
|
139
|
+
|
140
|
+
# Skip if dimensions are invalid
|
141
|
+
if local_width <= 0 or local_height <= 0:
|
142
|
+
return None, None
|
143
|
+
|
144
|
+
# Create a smaller mask
|
145
|
+
local_mask = np.zeros((local_height, local_width), dtype=np.uint8)
|
146
|
+
|
147
|
+
# Fill the mask efficiently
|
148
|
+
for x, y, length in pixel_data:
|
149
|
+
x, y, length = int(x), int(y), int(length)
|
150
|
+
# Adjust coordinates to local mask
|
151
|
+
local_x = x - min_x
|
152
|
+
local_y = y - min_y
|
153
|
+
|
154
|
+
# Ensure we're within bounds
|
155
|
+
if 0 <= local_y < local_height and 0 <= local_x < local_width:
|
156
|
+
# Calculate the end point, clamping to mask width
|
157
|
+
end_x = min(local_x + length, local_width)
|
158
|
+
if end_x > local_x: # Only process if there's a valid segment
|
159
|
+
local_mask[local_y, local_x:end_x] = 1
|
160
|
+
|
161
|
+
# Apply morphological operations
|
162
|
+
struct_elem = np.ones((3, 3), dtype=np.uint8)
|
163
|
+
eroded = binary_erosion(local_mask, structure=struct_elem, iterations=1)
|
164
|
+
mask = binary_dilation(eroded, structure=struct_elem, iterations=1).astype(
|
165
|
+
np.uint8
|
166
|
+
)
|
167
|
+
|
168
|
+
# Extract contour from the mask
|
169
|
+
outline = self.convex_hull_outline(mask)
|
170
|
+
if not outline:
|
171
|
+
return None, None
|
172
|
+
|
173
|
+
# Adjust coordinates back to global space
|
174
|
+
outline = [(x + min_x, y + min_y) for (x, y) in outline]
|
175
|
+
|
176
|
+
# Use coordinates as-is without flipping Y coordinates
|
177
|
+
xs, ys = zip(*outline)
|
178
|
+
x_min, x_max = min(xs), max(xs)
|
179
|
+
y_min, y_max = min(ys), max(ys)
|
180
|
+
|
181
|
+
room_id = str(segment_id)
|
182
|
+
|
183
|
+
# Scale coordinates by pixel_size and convert to regular Python integers
|
184
|
+
scaled_outline = [
|
185
|
+
(int(x * pixel_size), int(y * pixel_size)) for x, y in outline
|
186
|
+
]
|
187
|
+
room_data = {
|
188
|
+
"number": segment_id,
|
189
|
+
"outline": scaled_outline,
|
190
|
+
"name": name,
|
191
|
+
"x": int(((x_min + x_max) * pixel_size) // 2),
|
192
|
+
"y": int(((y_min + y_max) * pixel_size) // 2),
|
193
|
+
}
|
194
|
+
|
195
|
+
return room_id, room_data
|
196
|
+
|
197
|
+
async def async_extract_room_properties(self, json_data) -> RoomsProperties:
|
198
|
+
"""Extract room properties from the JSON data.
|
199
|
+
|
200
|
+
This method processes all room layers in the JSON data and extracts their outlines.
|
201
|
+
It respects the drawing configuration, skipping rooms that are disabled.
|
202
|
+
|
203
|
+
Args:
|
204
|
+
json_data: The JSON data from the vacuum
|
205
|
+
|
206
|
+
Returns:
|
207
|
+
Dictionary of room properties
|
208
|
+
"""
|
209
|
+
start_total = time.time()
|
210
|
+
room_properties = {}
|
211
|
+
pixel_size = json_data.get("pixelSize", 5)
|
212
|
+
height = json_data["size"]["y"]
|
213
|
+
width = json_data["size"]["x"]
|
214
|
+
|
215
|
+
for layer in json_data.get("layers", []):
|
216
|
+
if layer.get("__class") == "MapLayer" and layer.get("type") == "segment":
|
217
|
+
room_id, room_data = await self._process_room_layer(
|
218
|
+
layer, width, height, pixel_size
|
219
|
+
)
|
220
|
+
if room_id is not None and room_data is not None:
|
221
|
+
room_properties[room_id] = room_data
|
222
|
+
|
223
|
+
# Log timing information
|
224
|
+
total_time = time.time() - start_total
|
225
|
+
LOGGER.debug("Room extraction Total time: %.3fs", total_time)
|
226
|
+
return room_properties
|
227
|
+
|
228
|
+
class RandRoomsHandler:
|
229
|
+
"""
|
230
|
+
Handler for extracting and managing room data from Rand25 vacuum maps.
|
231
|
+
|
232
|
+
This class provides methods to:
|
233
|
+
- Extract room outlines using the Convex Hull algorithm
|
234
|
+
- Process room properties from JSON data and destinations JSON
|
235
|
+
- Generate room masks and extract contours
|
236
|
+
|
237
|
+
All methods are async for better integration with the rest of the codebase.
|
238
|
+
"""
|
239
|
+
|
240
|
+
def __init__(self, vacuum_id: str, drawing_config: Optional[DrawingConfig] = None):
|
241
|
+
"""
|
242
|
+
Initialize the RandRoomsHandler.
|
243
|
+
|
244
|
+
Args:
|
245
|
+
vacuum_id: Identifier for the vacuum
|
246
|
+
drawing_config: Configuration for which elements to draw (optional)
|
247
|
+
"""
|
248
|
+
self.vacuum_id = vacuum_id
|
249
|
+
self.drawing_config = drawing_config
|
250
|
+
self.current_json_data = None # Will store the current JSON data being processed
|
251
|
+
self.segment_data = None # Segment data
|
252
|
+
self.outlines = None # Outlines data
|
253
|
+
|
254
|
+
@staticmethod
|
255
|
+
def sublist(data: list, chunk_size: int) -> list:
|
256
|
+
"""Split a list into chunks of specified size."""
|
257
|
+
return [data[i : i + chunk_size] for i in range(0, len(data), chunk_size)]
|
258
|
+
|
259
|
+
@staticmethod
|
260
|
+
def convex_hull_outline(points: List[Tuple[int, int]]) -> List[Tuple[int, int]]:
|
261
|
+
"""
|
262
|
+
Generate a convex hull outline from a set of points.
|
263
|
+
|
264
|
+
Args:
|
265
|
+
points: List of (x, y) coordinate tuples
|
266
|
+
|
267
|
+
Returns:
|
268
|
+
List of (x, y) tuples forming the convex hull outline
|
269
|
+
"""
|
270
|
+
if len(points) == 0:
|
271
|
+
return []
|
272
|
+
|
273
|
+
# Convert to numpy array for processing
|
274
|
+
points_array = np.array(points)
|
275
|
+
|
276
|
+
if len(points) < 3:
|
277
|
+
# Not enough points for a convex hull, return the points as is
|
278
|
+
return [(int(x), int(y)) for x, y in points_array]
|
279
|
+
|
280
|
+
try:
|
281
|
+
# Calculate the convex hull
|
282
|
+
hull = ConvexHull(points_array)
|
283
|
+
|
284
|
+
# Extract the vertices in order
|
285
|
+
hull_points = [
|
286
|
+
(int(points_array[vertex][0]), int(points_array[vertex][1]))
|
287
|
+
for vertex in hull.vertices
|
288
|
+
]
|
289
|
+
|
290
|
+
# Close the polygon by adding the first point at the end
|
291
|
+
if hull_points[0] != hull_points[-1]:
|
292
|
+
hull_points.append(hull_points[0])
|
293
|
+
|
294
|
+
return hull_points
|
295
|
+
|
296
|
+
except Exception as e:
|
297
|
+
LOGGER.warning(f"Error calculating convex hull: {e}")
|
298
|
+
|
299
|
+
# Fallback to bounding box if convex hull fails
|
300
|
+
x_min, y_min = np.min(points_array, axis=0)
|
301
|
+
x_max, y_max = np.max(points_array, axis=0)
|
302
|
+
|
303
|
+
return [
|
304
|
+
(int(x_min), int(y_min)), # Top-left
|
305
|
+
(int(x_max), int(y_min)), # Top-right
|
306
|
+
(int(x_max), int(y_max)), # Bottom-right
|
307
|
+
(int(x_min), int(y_max)), # Bottom-left
|
308
|
+
(int(x_min), int(y_min)), # Back to top-left to close the polygon
|
309
|
+
]
|
310
|
+
|
311
|
+
async def _process_segment_data(
|
312
|
+
self, segment_data: List, segment_id: int, pixel_size: int
|
313
|
+
) -> Tuple[Optional[str], Optional[Dict[str, Any]]]:
|
314
|
+
"""
|
315
|
+
Process a single segment and extract its outline.
|
316
|
+
|
317
|
+
Args:
|
318
|
+
segment_data: The segment pixel data
|
319
|
+
segment_id: The ID of the segment
|
320
|
+
pixel_size: The size of each pixel
|
321
|
+
|
322
|
+
Returns:
|
323
|
+
Tuple of (room_id, room_data) or (None, None) if processing failed
|
324
|
+
"""
|
325
|
+
# Check if this room is enabled in the drawing configuration
|
326
|
+
if self.drawing_config is not None:
|
327
|
+
try:
|
328
|
+
# Convert segment_id to room element (ROOM_1 to ROOM_15)
|
329
|
+
room_element_id = int(segment_id)
|
330
|
+
if 1 <= room_element_id <= 15:
|
331
|
+
room_element = getattr(
|
332
|
+
DrawableElement, f"ROOM_{room_element_id}", None
|
333
|
+
)
|
334
|
+
if room_element:
|
335
|
+
is_enabled = self.drawing_config.is_enabled(room_element)
|
336
|
+
if not is_enabled:
|
337
|
+
# Skip this room if it's disabled
|
338
|
+
LOGGER.debug("Skipping disabled room %s", segment_id)
|
339
|
+
return None, None
|
340
|
+
except (ValueError, TypeError):
|
341
|
+
# If segment_id is not a valid integer, we can't map it to a room element
|
342
|
+
# In this case, we'll include the room (fail open)
|
343
|
+
LOGGER.debug(
|
344
|
+
"Could not convert segment_id %s to room element", segment_id
|
345
|
+
)
|
346
|
+
|
347
|
+
# Skip if no pixels
|
348
|
+
if not segment_data:
|
349
|
+
return None, None
|
350
|
+
|
351
|
+
# Extract points from segment data
|
352
|
+
points = []
|
353
|
+
for x, y, _ in segment_data:
|
354
|
+
points.append((int(x), int(y)))
|
355
|
+
|
356
|
+
if not points:
|
357
|
+
return None, None
|
358
|
+
|
359
|
+
# Use convex hull to get the outline
|
360
|
+
outline = self.convex_hull_outline(points)
|
361
|
+
if not outline:
|
362
|
+
return None, None
|
363
|
+
|
364
|
+
# Calculate bounding box for the room
|
365
|
+
xs, ys = zip(*outline)
|
366
|
+
x_min, x_max = min(xs), max(xs)
|
367
|
+
y_min, y_max = min(ys), max(ys)
|
368
|
+
|
369
|
+
# Scale coordinates by pixel_size
|
370
|
+
scaled_outline = [
|
371
|
+
(int(x * pixel_size), int(y * pixel_size)) for x, y in outline
|
372
|
+
]
|
373
|
+
|
374
|
+
room_id = str(segment_id)
|
375
|
+
room_data = {
|
376
|
+
"number": segment_id,
|
377
|
+
"outline": scaled_outline,
|
378
|
+
"name": f"Room {segment_id}", # Default name, will be updated from destinations
|
379
|
+
"x": int(((x_min + x_max) * pixel_size) // 2),
|
380
|
+
"y": int(((y_min + y_max) * pixel_size) // 2),
|
381
|
+
}
|
382
|
+
|
383
|
+
return room_id, room_data
|
384
|
+
|
385
|
+
async def async_extract_room_properties(
|
386
|
+
self, json_data: Dict[str, Any], destinations: Dict[str, Any]
|
387
|
+
) -> RoomsProperties:
|
388
|
+
"""
|
389
|
+
Extract room properties from the JSON data and destinations.
|
390
|
+
|
391
|
+
Args:
|
392
|
+
json_data: The JSON data from the vacuum
|
393
|
+
destinations: The destinations JSON containing room names and IDs
|
394
|
+
|
395
|
+
Returns:
|
396
|
+
Dictionary of room properties
|
397
|
+
"""
|
398
|
+
start_total = time.time()
|
399
|
+
room_properties = {}
|
400
|
+
|
401
|
+
# Get basic map information
|
402
|
+
unsorted_id = RandImageData.get_rrm_segments_ids(json_data)
|
403
|
+
size_x, size_y = RandImageData.get_rrm_image_size(json_data)
|
404
|
+
top, left = RandImageData.get_rrm_image_position(json_data)
|
405
|
+
pixel_size = 50 # Rand25 vacuums use a larger pixel size to match the original implementation
|
406
|
+
|
407
|
+
# Get segment data and outlines if not already available
|
408
|
+
if not self.segment_data or not self.outlines:
|
409
|
+
(
|
410
|
+
self.segment_data,
|
411
|
+
self.outlines,
|
412
|
+
) = await RandImageData.async_get_rrm_segments(
|
413
|
+
json_data, size_x, size_y, top, left, True
|
414
|
+
)
|
415
|
+
|
416
|
+
# Process destinations JSON to get room names
|
417
|
+
dest_json = destinations
|
418
|
+
room_data = dest_json.get("rooms", [])
|
419
|
+
room_id_to_data = {room["id"]: room for room in room_data}
|
420
|
+
|
421
|
+
# Process each segment
|
422
|
+
if unsorted_id and self.segment_data and self.outlines:
|
423
|
+
for idx, segment_id in enumerate(unsorted_id):
|
424
|
+
# Extract points from segment data
|
425
|
+
points = []
|
426
|
+
for x, y, _ in self.segment_data[idx]:
|
427
|
+
points.append((int(x), int(y)))
|
428
|
+
|
429
|
+
if not points:
|
430
|
+
continue
|
431
|
+
|
432
|
+
# Use convex hull to get the outline
|
433
|
+
outline = self.convex_hull_outline(points)
|
434
|
+
if not outline:
|
435
|
+
continue
|
436
|
+
|
437
|
+
# Scale coordinates by pixel_size
|
438
|
+
scaled_outline = [
|
439
|
+
(int(x * pixel_size), int(y * pixel_size)) for x, y in outline
|
440
|
+
]
|
441
|
+
|
442
|
+
# Calculate center point
|
443
|
+
xs, ys = zip(*outline)
|
444
|
+
x_min, x_max = min(xs), max(xs)
|
445
|
+
y_min, y_max = min(ys), max(ys)
|
446
|
+
center_x = int(((x_min + x_max) * pixel_size) // 2)
|
447
|
+
center_y = int(((y_min + y_max) * pixel_size) // 2)
|
448
|
+
|
449
|
+
# Create room data
|
450
|
+
room_id = str(segment_id)
|
451
|
+
room_data = {
|
452
|
+
"number": segment_id,
|
453
|
+
"outline": scaled_outline,
|
454
|
+
"name": f"Room {segment_id}", # Default name, will be updated from destinations
|
455
|
+
"x": center_x,
|
456
|
+
"y": center_y,
|
457
|
+
}
|
458
|
+
|
459
|
+
# Update room name from destinations if available
|
460
|
+
if segment_id in room_id_to_data:
|
461
|
+
room_info = room_id_to_data[segment_id]
|
462
|
+
room_data["name"] = room_info.get("name", room_data["name"])
|
463
|
+
|
464
|
+
room_properties[room_id] = room_data
|
465
|
+
|
466
|
+
# Log timing information
|
467
|
+
total_time = time.time() - start_total
|
468
|
+
LOGGER.debug("Room extraction Total time: %.3fs", total_time)
|
469
|
+
|
470
|
+
return room_properties
|
@@ -0,0 +1,93 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: valetudo-map-parser
|
3
|
+
Version: 0.1.9a2
|
4
|
+
Summary: A Python library to parse Valetudo map data returning a PIL Image object.
|
5
|
+
License: Apache-2.0
|
6
|
+
Author: Sandro Cantarella
|
7
|
+
Author-email: gsca075@gmail.com
|
8
|
+
Requires-Python: >=3.12
|
9
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
13
|
+
Requires-Dist: Pillow (>=10.3.0)
|
14
|
+
Requires-Dist: numpy (>=1.26.4)
|
15
|
+
Requires-Dist: scipy (>=1.12.0)
|
16
|
+
Project-URL: Bug Tracker, https://github.com/sca075/Python-package-valetudo-map-parser/issues
|
17
|
+
Project-URL: Changelog, https://github.com/sca075/Python-package-valetudo-map-parser/releases
|
18
|
+
Project-URL: Homepage, https://github.com/sca075/Python-package-valetudo-map-parser
|
19
|
+
Project-URL: Repository, https://github.com/sca075/Python-package-valetudo-map-parser
|
20
|
+
Description-Content-Type: text/markdown
|
21
|
+
|
22
|
+
# Python-package-valetudo-map-parser
|
23
|
+
|
24
|
+
---
|
25
|
+
### What is it:
|
26
|
+
❗This is an _unofficial_ project and is not created, maintained, or in any sense linked to [valetudo.cloud](https://valetudo.cloud)
|
27
|
+
|
28
|
+
A Python library that converts Valetudo vacuum JSON map data into PIL (Python Imaging Library) images. This package is primarily developed for and used in the [MQTT Vacuum Camera](https://github.com/sca075/mqtt_vacuum_camera) project.
|
29
|
+
|
30
|
+
---
|
31
|
+
|
32
|
+
### Features:
|
33
|
+
- Processes map data from Valetudo-compatible robot vacuums
|
34
|
+
- Supports both Hypfer and Rand256 vacuum data formats
|
35
|
+
- Renders comprehensive map visualizations including:
|
36
|
+
- Walls and obstacles
|
37
|
+
- Robot position and cleaning path
|
38
|
+
- Room segments and boundaries
|
39
|
+
- Cleaning zones
|
40
|
+
- Virtual restrictions
|
41
|
+
- LiDAR data
|
42
|
+
- Provides auto-cropping and dynamic zooming
|
43
|
+
- Supports image rotation and aspect ratio management
|
44
|
+
- Enables custom color schemes
|
45
|
+
- Handles multilingual labels
|
46
|
+
- Implements thread-safe data sharing
|
47
|
+
|
48
|
+
### Installation:
|
49
|
+
```bash
|
50
|
+
pip install valetudo_map_parser
|
51
|
+
```
|
52
|
+
|
53
|
+
### Requirements:
|
54
|
+
- Python 3.12 or higher
|
55
|
+
- Dependencies:
|
56
|
+
- Pillow (PIL) for image processing
|
57
|
+
- NumPy for array operations
|
58
|
+
|
59
|
+
### Usage:
|
60
|
+
The library is configured using a dictionary format. See our [sample code](https://github.com/sca075/Python-package-valetudo-map-parser/blob/main/tests/test.py) for implementation examples.
|
61
|
+
|
62
|
+
Key functionalities:
|
63
|
+
- Decodes raw data from Rand256 format
|
64
|
+
- Processes JSON data from compatible vacuums
|
65
|
+
- Returns Pillow PNG images
|
66
|
+
- Provides calibration and room property extraction
|
67
|
+
- Supports asynchronous operations
|
68
|
+
|
69
|
+
### Development Status:
|
70
|
+
Current version: 0.1.9.b41
|
71
|
+
- Full functionality available in versions >= 0.1.9
|
72
|
+
- Actively maintained and enhanced
|
73
|
+
- Uses Poetry for dependency management
|
74
|
+
- Implements comprehensive testing
|
75
|
+
- Enforces code quality through ruff, isort, and pylint
|
76
|
+
|
77
|
+
### Contributing:
|
78
|
+
Contributions are welcome! You can help by:
|
79
|
+
- Submitting code improvements
|
80
|
+
- Enhancing documentation
|
81
|
+
- Reporting issues
|
82
|
+
- Suggesting new features
|
83
|
+
|
84
|
+
### Disclaimer:
|
85
|
+
This project is provided "as is" without warranty of any kind. Users assume all risks associated with its use.
|
86
|
+
|
87
|
+
### License:
|
88
|
+
Apache-2.0
|
89
|
+
|
90
|
+
---
|
91
|
+
For more information about Valetudo, visit [valetudo.cloud](https://valetudo.cloud)
|
92
|
+
Integration with Home Assistant: [MQTT Vacuum Camera](https://github.com/sca075/mqtt_vacuum_camera)
|
93
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
valetudo_map_parser/__init__.py,sha256=Fz-gtKf_OlZcDQqVfGlBwIWi5DJAiRucMbBMdQ2tX_U,1060
|
2
|
+
valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
|
3
|
+
valetudo_map_parser/config/auto_crop.py,sha256=6xt_wJQqphddWhlrr7MNUkodCi8ZYdRk42qvAaxlYCM,13546
|
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/room_outline.py,sha256=D20D-yeyKnlmVbW9lI7bsPtQGn2XkcWow6YNOEPnWVg,4800
|
12
|
+
valetudo_map_parser/config/shared.py,sha256=Yd0MlAH6DaAfqxrUw1NW2uHxYoLe9IwKxyFowQ6VJWg,12133
|
13
|
+
valetudo_map_parser/config/types.py,sha256=TaRKoo7G7WIUw7ljOz2Vn5oYzKaLyQH-7Eb8ZYql8Ls,17464
|
14
|
+
valetudo_map_parser/config/utils.py,sha256=CFuuiS5IufEu9aeaZwi7xa1jEF1z6yDZB0mcyVX79Xo,29261
|
15
|
+
valetudo_map_parser/hypfer_draw.py,sha256=L1eM8dDLNsi4SOUt9499v9jLbQa1MwDKPfMYHcUEsXQ,26722
|
16
|
+
valetudo_map_parser/hypfer_handler.py,sha256=f7y5It8tr__FCadojQsk_FPuMgsnjyUST1scXLZ1tws,20108
|
17
|
+
valetudo_map_parser/hypfer_rooms_handler.py,sha256=NkpOA6Gdq-2D3lLAxvtNuuWMvPXHxeMY2TO5RZLSHlU,22652
|
18
|
+
valetudo_map_parser/map_data.py,sha256=3CG3l_fWeEwWCT5j9nfnqPuClU01m7exwuYWV3K9jIk,18618
|
19
|
+
valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
+
valetudo_map_parser/rand25_handler.py,sha256=EKf7dhBtLKt_lEFi3hiJwSL0wgX0ZmvWlNMPxS8TDQE,22875
|
21
|
+
valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
|
22
|
+
valetudo_map_parser/rooms_handler.py,sha256=YP8OLotBH-RXluv398l7TTT2zIBHJp91b8THWxl3NdI,17794
|
23
|
+
valetudo_map_parser-0.1.9a2.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
|
24
|
+
valetudo_map_parser-0.1.9a2.dist-info/METADATA,sha256=DZkyXziGLLJ2QK6P29Unz2P8tBWHVbJNUYe072gg5yU,3320
|
25
|
+
valetudo_map_parser-0.1.9a2.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
|
26
|
+
valetudo_map_parser-0.1.9a2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
27
|
+
valetudo_map_parser-0.1.9a2.dist-info/RECORD,,
|