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.
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/PKG-INFO +2 -2
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/rand256_parser.py +129 -47
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/shared.py +8 -9
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/types.py +9 -1
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/utils.py +19 -13
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/hypfer_handler.py +1 -3
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/map_data.py +3 -2
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/rand256_handler.py +13 -17
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/reimg_draw.py +13 -18
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/pyproject.toml +2 -2
- valetudo_map_parser-0.1.10rc6/SCR/valetudo_map_parser/hypfer_rooms_handler.py +0 -599
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/LICENSE +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/NOTICE.txt +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/README.md +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/__init__.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/__init__.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/async_utils.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/auto_crop.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/color_utils.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/colors.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/drawable.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/drawable_elements.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/enhanced_drawable.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/FiraSans.ttf +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/Inter-VF.ttf +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/Lato-Regular.ttf +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/MPLUSRegular.ttf +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/NotoKufiArabic-VF.ttf +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/NotoSansCJKhk-VF.ttf +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/fonts/NotoSansKhojki.ttf +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/optimized_element_map.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/status_text/status_text.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/config/status_text/translations.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/hypfer_draw.py +0 -0
- {valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/py.typed +0 -0
- {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.
|
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.
|
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
|
-
|
146
|
-
|
147
|
-
|
148
|
-
block_data_length, data
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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["
|
369
|
-
|
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
|
-
|
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
|
-
"
|
215
|
-
"size": self.new_image.size if self.new_image else
|
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[
|
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
|
-
|
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
|
-
|
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
|
219
|
-
self
|
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(
|
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(
|
238
|
-
|
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.
|
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),
|
{valetudo_map_parser-0.1.10rc6 → valetudo_map_parser-0.1.10rc7}/SCR/valetudo_map_parser/map_data.py
RENAMED
@@ -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
|
-
|
111
|
+
self.shared.map_pred_zones = await self.async_zone_propriety(zones_data)
|
116
112
|
# get the points data
|
117
|
-
|
113
|
+
self.shared.map_pred_points = await self.async_points_propriety(points_data)
|
118
114
|
|
119
|
-
if not (room_properties or
|
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
|
119
|
+
return room_properties
|
124
120
|
except (RuntimeError, ValueError) as e:
|
125
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
514
|
+
LOGGER.info(
|
519
515
|
"%s: Getting Calibrations points %s",
|
520
516
|
self.file_name,
|
521
517
|
str(self.crop_area),
|