valetudo-map-parser 0.1.11b2__py3-none-any.whl → 0.1.12b0__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.
@@ -1,6 +1,6 @@
1
1
  """
2
2
  This module contains type aliases for the project.
3
- Version 0.0.1
3
+ Version 0.11.1
4
4
  """
5
5
 
6
6
  import asyncio
@@ -20,21 +20,29 @@ LOGGER = logging.getLogger(__package__)
20
20
 
21
21
 
22
22
  class Spot(TypedDict):
23
+ """Type definition for a spot location."""
24
+
23
25
  name: str
24
26
  coordinates: List[int] # [x, y]
25
27
 
26
28
 
27
29
  class Zone(TypedDict):
30
+ """Type definition for a zone area."""
31
+
28
32
  name: str
29
33
  coordinates: List[List[int]] # [[x1, y1, x2, y2, repeats], ...]
30
34
 
31
35
 
32
36
  class Room(TypedDict):
37
+ """Type definition for a room."""
38
+
33
39
  name: str
34
40
  id: int
35
41
 
36
42
 
37
43
  class Destinations(TypedDict, total=False):
44
+ """Type definition for destinations including spots, zones, and rooms."""
45
+
38
46
  spots: NotRequired[Optional[List[Spot]]]
39
47
  zones: NotRequired[Optional[List[Zone]]]
40
48
  rooms: NotRequired[Optional[List[Room]]]
@@ -42,6 +50,8 @@ class Destinations(TypedDict, total=False):
42
50
 
43
51
 
44
52
  class RoomProperty(TypedDict):
53
+ """Type definition for room properties including outline."""
54
+
45
55
  number: int
46
56
  outline: list[tuple[int, int]]
47
57
  name: str
@@ -94,37 +104,72 @@ class TrimCropData:
94
104
 
95
105
 
96
106
  class RoomStore:
107
+ """Singleton storage for room data per vacuum ID.
108
+
109
+ Stores room properties in format: {segment_id: RoomProperty}
110
+ Example: {"16": {"number": 16, "outline": [...], "name": "Living Room", "x": 100, "y": 200}}
111
+ """
112
+
97
113
  _instances: Dict[str, "RoomStore"] = {}
98
114
  _lock = threading.Lock()
99
115
 
100
- def __new__(cls, vacuum_id: str, rooms_data: Optional[dict] = None) -> "RoomStore":
116
+ def __new__(
117
+ cls, vacuum_id: str, rooms_data: Optional[Dict[str, RoomProperty]] = None
118
+ ) -> "RoomStore":
101
119
  with cls._lock:
102
120
  if vacuum_id not in cls._instances:
103
121
  instance = super(RoomStore, cls).__new__(cls)
104
- instance.vacuum_id = vacuum_id
105
- instance.vacuums_data = rooms_data or {}
106
- instance.rooms_count = instance.get_rooms_count()
107
- instance.floor = None
108
122
  cls._instances[vacuum_id] = instance
109
- else:
110
- if rooms_data is not None:
111
- cls._instances[vacuum_id].vacuums_data = rooms_data
112
- return cls._instances[vacuum_id]
123
+ return cls._instances[vacuum_id]
113
124
 
114
- def get_rooms(self) -> dict:
125
+ def __init__(
126
+ self, vacuum_id: str, rooms_data: Optional[Dict[str, RoomProperty]] = None
127
+ ) -> None:
128
+ # Only initialize if this is a new instance (not yet initialized)
129
+ if not hasattr(self, "vacuum_id"):
130
+ self.vacuum_id: str = vacuum_id
131
+ self.vacuums_data: Dict[str, RoomProperty] = rooms_data or {}
132
+ self.rooms_count: int = self.get_rooms_count()
133
+ self.floor: Optional[str] = None
134
+ elif rooms_data is not None:
135
+ # Update only if new data is provided
136
+ self.vacuums_data = rooms_data
137
+ self.rooms_count = self.get_rooms_count()
138
+
139
+ def get_rooms(self) -> Dict[str, RoomProperty]:
140
+ """Get all rooms data."""
115
141
  return self.vacuums_data
116
142
 
117
- def set_rooms(self, rooms_data: dict) -> None:
143
+ def set_rooms(self, rooms_data: Dict[str, RoomProperty]) -> None:
144
+ """Set rooms data and update room count."""
118
145
  self.vacuums_data = rooms_data
146
+ self.rooms_count = self.get_rooms_count()
119
147
 
120
148
  def get_rooms_count(self) -> int:
149
+ """Get the number of rooms, defaulting to 1 if no rooms are present."""
121
150
  if isinstance(self.vacuums_data, dict):
122
151
  count = len(self.vacuums_data)
123
152
  return count if count > 0 else DEFAULT_ROOMS
124
153
  return DEFAULT_ROOMS
125
154
 
155
+ @property
156
+ def room_names(self) -> dict:
157
+ """Return room names in format {'room_0_name': 'SegmentID: RoomName', ...}.
158
+
159
+ Maximum of 16 rooms supported.
160
+ """
161
+ result = {}
162
+ if isinstance(self.vacuums_data, dict):
163
+ for idx, (segment_id, room_data) in enumerate(self.vacuums_data.items()):
164
+ if idx >= 16: # Max 16 rooms supported
165
+ break
166
+ room_name = room_data.get("name", f"Room {segment_id}")
167
+ result[f"room_{idx}_name"] = f"{segment_id}: {room_name}"
168
+ return result
169
+
126
170
  @classmethod
127
171
  def get_all_instances(cls) -> Dict[str, "RoomStore"]:
172
+ """Get all RoomStore instances for all vacuum IDs."""
128
173
  return cls._instances
129
174
 
130
175
 
@@ -218,403 +263,8 @@ class SnapshotStore:
218
263
  self.vacuum_json_data[vacuum_id] = json_data
219
264
 
220
265
 
221
- Color = Union[Tuple[int, int, int], Tuple[int, int, int, int]]
222
- Colors = Dict[str, Color]
223
- CalibrationPoints = list[dict[str, Any]]
224
- RobotPosition: type[tuple[Any, Any, dict[str, int | float] | None]] = tuple[
225
- Any, Any, dict[str, int | float] | None
226
- ]
227
- ChargerPosition = dict[str, Any]
228
- RoomsProperties = dict[str, RoomProperty]
229
- ImageSize = dict[str, int | list[int]]
230
- Size = dict[str, int]
231
- JsonType = Any # json.loads() return type is Any
232
- PilPNG = Image.Image # Keep for backward compatibility
233
- NumpyArray = np.ndarray
234
- Point = Tuple[int, int]
235
-
236
- CAMERA_STORAGE = "valetudo_camera"
237
- ATTR_IMAGE_LAST_UPDATED = "image_last_updated"
238
- ATTR_ROTATE = "rotate_image"
239
- ATTR_CROP = "crop_image"
240
- ATTR_MARGINS = "margins"
241
- ATTR_CONTENT_TYPE = "content_type"
242
- CONF_OFFSET_TOP = "offset_top"
243
- CONF_OFFSET_BOTTOM = "offset_bottom"
244
- CONF_OFFSET_LEFT = "offset_left"
245
- CONF_OFFSET_RIGHT = "offset_right"
246
- CONF_ASPECT_RATIO = "aspect_ratio"
247
- CONF_VAC_STAT = "show_vac_status"
248
- CONF_VAC_STAT_SIZE = "vac_status_size"
249
- CONF_VAC_STAT_POS = "vac_status_position"
250
- CONF_VAC_STAT_FONT = "vac_status_font"
251
- CONF_VACUUM_CONNECTION_STRING = "vacuum_map"
252
- CONF_VACUUM_ENTITY_ID = "vacuum_entity"
253
- CONF_VACUUM_CONFIG_ENTRY_ID = "vacuum_config_entry"
254
- CONF_VACUUM_IDENTIFIERS = "vacuum_identifiers"
255
- CONF_SNAPSHOTS_ENABLE = "enable_www_snapshots"
256
- CONF_EXPORT_SVG = "get_svg_file"
257
- CONF_AUTO_ZOOM = "auto_zoom"
258
- CONF_ZOOM_LOCK_RATIO = "zoom_lock_ratio"
259
- CONF_TRIMS_SAVE = "save_trims"
260
- ICON = "mdi:camera"
261
- NAME = "MQTT Vacuum Camera"
262
-
263
- DEFAULT_IMAGE_SIZE = {
264
- "x": 5120,
265
- "y": 5120,
266
- "centre": [(5120 // 2), (5120 // 2)],
267
- }
268
-
269
- COLORS = [
270
- "wall",
271
- "zone_clean",
272
- "robot",
273
- "background",
274
- "move",
275
- "charger",
276
- "no_go",
277
- "go_to",
278
- ]
279
-
280
- SENSOR_NO_DATA = {
281
- "mainBrush": 0,
282
- "sideBrush": 0,
283
- "filter": 0,
284
- "currentCleanTime": 0,
285
- "currentCleanArea": 0,
286
- "cleanTime": 0,
287
- "cleanArea": 0,
288
- "cleanCount": 0,
289
- "battery": 0,
290
- "state": 0,
291
- "last_run_start": 0,
292
- "last_run_end": 0,
293
- "last_run_duration": 0,
294
- "last_run_area": 0,
295
- "last_bin_out": 0,
296
- "last_bin_full": 0,
297
- "last_loaded_map": "NoMap",
298
- "robot_in_room": "Unsupported",
299
- }
300
-
301
- DEFAULT_PIXEL_SIZE = 5
302
-
303
- DEFAULT_VALUES = {
304
- "rotate_image": "0",
305
- "margins": "100",
306
- "aspect_ratio": "None",
307
- "offset_top": 0,
308
- "offset_bottom": 0,
309
- "offset_left": 0,
310
- "offset_right": 0,
311
- "auto_zoom": False,
312
- "zoom_lock_ratio": True,
313
- "show_vac_status": False,
314
- "vac_status_font": "SCR/valetudo_map_parser/config/fonts/FiraSans.ttf",
315
- "vac_status_size": 50,
316
- "vac_status_position": True,
317
- "get_svg_file": False,
318
- "save_trims": True,
319
- "trims_data": {"trim_left": 0, "trim_up": 0, "trim_right": 0, "trim_down": 0},
320
- "enable_www_snapshots": False,
321
- "color_charger": [255, 128, 0],
322
- "color_move": [238, 247, 255],
323
- "color_wall": [255, 255, 0],
324
- "color_robot": [255, 255, 204],
325
- "color_go_to": [0, 255, 0],
326
- "color_no_go": [255, 0, 0],
327
- "color_zone_clean": [255, 255, 255],
328
- "color_background": [0, 125, 255],
329
- "color_text": [255, 255, 255],
330
- "alpha_charger": 255.0,
331
- "alpha_move": 255.0,
332
- "alpha_wall": 255.0,
333
- "alpha_robot": 255.0,
334
- "alpha_go_to": 255.0,
335
- "alpha_no_go": 125.0,
336
- "alpha_zone_clean": 125.0,
337
- "alpha_background": 255.0,
338
- "alpha_text": 255.0,
339
- "color_room_0": [135, 206, 250],
340
- "color_room_1": [176, 226, 255],
341
- "color_room_2": [165, 105, 18],
342
- "color_room_3": [164, 211, 238],
343
- "color_room_4": [141, 182, 205],
344
- "color_room_5": [96, 123, 139],
345
- "color_room_6": [224, 255, 255],
346
- "color_room_7": [209, 238, 238],
347
- "color_room_8": [180, 205, 205],
348
- "color_room_9": [122, 139, 139],
349
- "color_room_10": [175, 238, 238],
350
- "color_room_11": [84, 153, 199],
351
- "color_room_12": [133, 193, 233],
352
- "color_room_13": [245, 176, 65],
353
- "color_room_14": [82, 190, 128],
354
- "color_room_15": [72, 201, 176],
355
- "alpha_room_0": 255.0,
356
- "alpha_room_1": 255.0,
357
- "alpha_room_2": 255.0,
358
- "alpha_room_3": 255.0,
359
- "alpha_room_4": 255.0,
360
- "alpha_room_5": 255.0,
361
- "alpha_room_6": 255.0,
362
- "alpha_room_7": 255.0,
363
- "alpha_room_8": 255.0,
364
- "alpha_room_9": 255.0,
365
- "alpha_room_10": 255.0,
366
- "alpha_room_11": 255.0,
367
- "alpha_room_12": 255.0,
368
- "alpha_room_13": 255.0,
369
- "alpha_room_14": 255.0,
370
- "alpha_room_15": 255.0,
371
- }
372
-
373
- KEYS_TO_UPDATE = [
374
- "rotate_image",
375
- "margins",
376
- "aspect_ratio",
377
- "offset_top",
378
- "offset_bottom",
379
- "offset_left",
380
- "offset_right",
381
- "trims_data",
382
- "auto_zoom",
383
- "zoom_lock_ratio",
384
- "show_vac_status",
385
- "vac_status_size",
386
- "vac_status_position",
387
- "vac_status_font",
388
- "get_svg_file",
389
- "enable_www_snapshots",
390
- "color_charger",
391
- "color_move",
392
- "color_wall",
393
- "color_robot",
394
- "color_go_to",
395
- "color_no_go",
396
- "color_zone_clean",
397
- "color_background",
398
- "color_text",
399
- "alpha_charger",
400
- "alpha_move",
401
- "alpha_wall",
402
- "alpha_robot",
403
- "alpha_go_to",
404
- "alpha_no_go",
405
- "alpha_zone_clean",
406
- "alpha_background",
407
- "alpha_text",
408
- "color_room_0",
409
- "color_room_1",
410
- "color_room_2",
411
- "color_room_3",
412
- "color_room_4",
413
- "color_room_5",
414
- "color_room_6",
415
- "color_room_7",
416
- "color_room_8",
417
- "color_room_9",
418
- "color_room_10",
419
- "color_room_11",
420
- "color_room_12",
421
- "color_room_13",
422
- "color_room_14",
423
- "color_room_15",
424
- "alpha_room_0",
425
- "alpha_room_1",
426
- "alpha_room_2",
427
- "alpha_room_3",
428
- "alpha_room_4",
429
- "alpha_room_5",
430
- "alpha_room_6",
431
- "alpha_room_7",
432
- "alpha_room_8",
433
- "alpha_room_9",
434
- "alpha_room_10",
435
- "alpha_room_11",
436
- "alpha_room_12",
437
- "alpha_room_13",
438
- "alpha_room_14",
439
- "alpha_room_15",
440
- ]
441
-
442
- ALPHA_VALUES = {
443
- "min": 0.0, # Minimum value
444
- "max": 255.0, # Maximum value
445
- "step": 1.0, # Step value
446
- }
447
-
448
- TEXT_SIZE_VALUES = {
449
- "min": 5, # Minimum value
450
- "max": 51, # Maximum value
451
- "step": 1, # Step value
452
- }
453
-
454
- ROTATION_VALUES = [
455
- {"label": "0", "value": "0"},
456
- {"label": "90", "value": "90"},
457
- {"label": "180", "value": "180"},
458
- {"label": "270", "value": "270"},
459
- ]
460
-
461
- RATIO_VALUES = [
462
- {"label": "Original Ratio.", "value": "None"},
463
- {"label": "1:1", "value": "1, 1"},
464
- {"label": "2:1", "value": "2, 1"},
465
- {"label": "3:2", "value": "3, 2"},
466
- {"label": "5:4", "value": "5, 4"},
467
- {"label": "9:16", "value": "9, 16"},
468
- {"label": "16:9", "value": "16, 9"},
469
- ]
470
-
471
- FONTS_AVAILABLE = [
472
- {
473
- "label": "Fira Sans",
474
- "value": "config/fonts/FiraSans.ttf",
475
- },
476
- {
477
- "label": "Inter",
478
- "value": "config/fonts/Inter-VF.ttf",
479
- },
480
- {
481
- "label": "M Plus Regular",
482
- "value": "config/fonts/MPLUSRegular.ttf",
483
- },
484
- {
485
- "label": "Noto Sans CJKhk",
486
- "value": "config/fonts/NotoSansCJKhk-VF.ttf",
487
- },
488
- {
489
- "label": "Noto Kufi Arabic",
490
- "value": "config/fonts/NotoKufiArabic-VF.ttf",
491
- },
492
- {
493
- "label": "Noto Sans Khojki",
494
- "value": "config/fonts/NotoSansKhojki.ttf",
495
- },
496
- {
497
- "label": "Lato Regular",
498
- "value": "config/fonts/Lato-Regular.ttf",
499
- },
500
- ]
501
-
502
- NOT_STREAMING_STATES = {
503
- "idle",
504
- "paused",
505
- "charging",
506
- "error",
507
- "docked",
508
- }
509
-
510
- DECODED_TOPICS = {
511
- "/MapData/segments",
512
- "/maploader/map",
513
- "/maploader/status",
514
- "/StatusStateAttribute/status",
515
- "/StatusStateAttribute/error_description",
516
- "/$state",
517
- "/BatteryStateAttribute/level",
518
- "/WifiConfigurationCapability/ips",
519
- "/state", # Rand256
520
- "/destinations", # Rand256
521
- "/command", # Rand256
522
- "/custom_command", # Rand256
523
- "/attributes", # Rand256
524
- }
525
-
526
-
527
- # self.command_topic need to be added to this dictionary after init.
528
- NON_DECODED_TOPICS = {
529
- "/MapData/map-data",
530
- "/map_data",
531
- }
532
-
533
- """App Constants. Not in use, and dummy values"""
534
- IDLE_SCAN_INTERVAL = 120
535
- CLEANING_SCAN_INTERVAL = 5
536
- IS_ALPHA = "add_base_alpha"
537
- IS_ALPHA_R1 = "add_room_1_alpha"
538
- IS_ALPHA_R2 = "add_room_2_alpha"
539
- IS_OFFSET = "add_offset"
540
-
541
- """Base Colours RGB"""
542
- COLOR_CHARGER = "color_charger"
543
- COLOR_MOVE = "color_move"
544
- COLOR_ROBOT = "color_robot"
545
- COLOR_NO_GO = "color_no_go"
546
- COLOR_GO_TO = "color_go_to"
547
- COLOR_BACKGROUND = "color_background"
548
- COLOR_ZONE_CLEAN = "color_zone_clean"
549
- COLOR_WALL = "color_wall"
550
- COLOR_TEXT = "color_text"
551
-
552
- "Rooms Colours RGB"
553
- COLOR_ROOM_0 = "color_room_0"
554
- COLOR_ROOM_1 = "color_room_1"
555
- COLOR_ROOM_2 = "color_room_2"
556
- COLOR_ROOM_3 = "color_room_3"
557
- COLOR_ROOM_4 = "color_room_4"
558
- COLOR_ROOM_5 = "color_room_5"
559
- COLOR_ROOM_6 = "color_room_6"
560
- COLOR_ROOM_7 = "color_room_7"
561
- COLOR_ROOM_8 = "color_room_8"
562
- COLOR_ROOM_9 = "color_room_9"
563
- COLOR_ROOM_10 = "color_room_10"
564
- COLOR_ROOM_11 = "color_room_11"
565
- COLOR_ROOM_12 = "color_room_12"
566
- COLOR_ROOM_13 = "color_room_13"
567
- COLOR_ROOM_14 = "color_room_14"
568
- COLOR_ROOM_15 = "color_room_15"
569
-
570
- """Alpha for RGBA Colours"""
571
- ALPHA_CHARGER = "alpha_charger"
572
- ALPHA_MOVE = "alpha_move"
573
- ALPHA_ROBOT = "alpha_robot"
574
- ALPHA_NO_GO = "alpha_no_go"
575
- ALPHA_GO_TO = "alpha_go_to"
576
- ALPHA_BACKGROUND = "alpha_background"
577
- ALPHA_ZONE_CLEAN = "alpha_zone_clean"
578
- ALPHA_WALL = "alpha_wall"
579
- ALPHA_TEXT = "alpha_text"
580
- ALPHA_ROOM_0 = "alpha_room_0"
581
- ALPHA_ROOM_1 = "alpha_room_1"
582
- ALPHA_ROOM_2 = "alpha_room_2"
583
- ALPHA_ROOM_3 = "alpha_room_3"
584
- ALPHA_ROOM_4 = "alpha_room_4"
585
- ALPHA_ROOM_5 = "alpha_room_5"
586
- ALPHA_ROOM_6 = "alpha_room_6"
587
- ALPHA_ROOM_7 = "alpha_room_7"
588
- ALPHA_ROOM_8 = "alpha_room_8"
589
- ALPHA_ROOM_9 = "alpha_room_9"
590
- ALPHA_ROOM_10 = "alpha_room_10"
591
- ALPHA_ROOM_11 = "alpha_room_11"
592
- ALPHA_ROOM_12 = "alpha_room_12"
593
- ALPHA_ROOM_13 = "alpha_room_13"
594
- ALPHA_ROOM_14 = "alpha_room_14"
595
- ALPHA_ROOM_15 = "alpha_room_15"
596
-
597
- """ Constants for the attribute keys """
598
- ATTR_FRIENDLY_NAME = "friendly_name"
599
- ATTR_VACUUM_BATTERY = "battery"
600
- ATTR_VACUUM_CHARGING = "charging"
601
- ATTR_VACUUM_POSITION = "vacuum_position"
602
- ATTR_VACUUM_TOPIC = "vacuum_topic"
603
- ATTR_VACUUM_STATUS = "vacuum_status"
604
- ATTR_JSON_DATA = "json_data"
605
- ATTR_VACUUM_JSON_ID = "vacuum_json_id"
606
- ATTR_CALIBRATION_POINTS = "calibration_points"
607
- ATTR_SNAPSHOT = "snapshot"
608
- ATTR_SNAPSHOT_PATH = "snapshot_path"
609
- ATTR_ROOMS = "rooms"
610
- ATTR_ZONES = "zones"
611
- ATTR_POINTS = "points"
612
- ATTR_OBSTACLES = "obstacles"
613
- ATTR_CAMERA_MODE = "camera_mode"
614
-
615
-
616
266
  class CameraModes:
617
- """Constants for the camera modes"""
267
+ """Constants for the camera modes."""
618
268
 
619
269
  MAP_VIEW = "map_view"
620
270
  OBSTACLE_VIEW = "obstacle_view"
@@ -669,3 +319,19 @@ class TrimsData:
669
319
  self.trim_down = 0
670
320
  self.trim_right = 0
671
321
  return asdict(self)
322
+
323
+
324
+ Color = Union[Tuple[int, int, int], Tuple[int, int, int, int]]
325
+ Colors = Dict[str, Color]
326
+ CalibrationPoints = list[dict[str, Any]]
327
+ RobotPosition: type[tuple[Any, Any, dict[str, int | float] | None]] = tuple[
328
+ Any, Any, dict[str, int | float] | None
329
+ ]
330
+ ChargerPosition = dict[str, Any]
331
+ RoomsProperties = dict[str, RoomProperty]
332
+ ImageSize = dict[str, int | list[int]]
333
+ Size = dict[str, int]
334
+ JsonType = Any # json.loads() return type is Any
335
+ PilPNG = Image.Image # Keep for backward compatibility
336
+ NumpyArray = np.ndarray
337
+ Point = Tuple[int, int]