Skip to content

Commit 8e0f92b

Browse files
authored
Merge pull request #14 from sca075/dev
Centralize shared in the library.
2 parents cd4213c + eb8c243 commit 8e0f92b

File tree

6 files changed

+162
-106
lines changed

6 files changed

+162
-106
lines changed

.github/workflows/release.yaml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ jobs:
88
build-and-publish-pypi:
99
name: Build and publish release to PyPI
1010
runs-on: ubuntu-latest
11+
permissions:
12+
id-token: write
13+
contents: read
14+
environment:
15+
name: valetudo_map_parser_pypi
16+
1117
steps:
1218
# Step 1: Checkout the repository
1319
- uses: actions/checkout@v5
@@ -46,9 +52,8 @@ jobs:
4652
run: poetry build --no-interaction
4753

4854
# Step 8: Publish to PyPI
49-
- name: Publish to PyPi
50-
env:
51-
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
52-
run: |
53-
poetry config pypi-token.pypi "${PYPI_TOKEN}"
54-
poetry publish --no-interaction
55+
- name: Publish to PyPI (Trusted Publishing)
56+
uses: pypa/gh-action-pypi-publish@release/v1
57+
with:
58+
skip-existing: true
59+
packages-dir: dist/

SCR/valetudo_map_parser/config/shared.py

Lines changed: 80 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
Class Camera Shared.
33
Keep the data between the modules.
4-
Version: v0.1.10
4+
Version: v0.1.12
55
"""
66

77
import asyncio
@@ -54,129 +54,112 @@ class CameraShared:
5454
"""
5555

5656
def __init__(self, file_name):
57-
self.camera_mode: str = CameraModes.MAP_VIEW # Camera mode
58-
self.frame_number: int = 0 # camera Frame number
59-
self.destinations: list = [] # MQTT rand destinations
60-
self.rand256_active_zone: list = [] # Active zone for rand256
61-
self.rand256_zone_coordinates: list = [] # Active zone coordinates for rand256
62-
self.is_rand: bool = False # MQTT rand data
63-
self._new_mqtt_message = False # New MQTT message
64-
# Initialize last_image with default gray image (250x150 minimum)
65-
self.last_image = Image.new(
66-
"RGBA", (250, 150), (128, 128, 128, 255)
67-
) # Gray default image
68-
self.new_image: PilPNG | None = None # New image received
69-
self.binary_image: bytes | None = None # Current image in binary format
70-
self.image_last_updated: float = 0.0 # Last image update time
71-
self.image_format = "image/pil" # Image format
72-
self.image_size = None # Image size
73-
self.robot_size = None # Robot size
74-
self.image_auto_zoom: bool = False # Auto zoom image
75-
self.image_zoom_lock_ratio: bool = True # Zoom lock ratio
76-
self.image_ref_height: int = 0 # Image reference height
77-
self.image_ref_width: int = 0 # Image reference width
78-
self.image_aspect_ratio: str = "None" # Change Image aspect ratio
79-
self.image_grab = True # Grab image from MQTT
80-
self.image_rotate: int = 0 # Rotate image
81-
self.drawing_limit: float = 0.0 # Drawing CPU limit
82-
self.current_room = None # Current room of rhe vacuum
83-
self.user_colors = Colors # User base colors
84-
self.rooms_colors = Colors # Rooms colors
85-
self.vacuum_battery = 0 # Vacuum battery state
86-
self.vacuum_connection = False # Vacuum connection state
87-
self.vacuum_state = None # Vacuum state
88-
self.charger_position = None # Vacuum Charger position
89-
self.show_vacuum_state = None # Show vacuum state on the map
57+
self.camera_mode: str = CameraModes.MAP_VIEW
58+
self.frame_number: int = 0
59+
self.destinations: list = []
60+
self.rand256_active_zone: list = []
61+
self.rand256_zone_coordinates: list = []
62+
self.is_rand: bool = False
63+
self._new_mqtt_message = False
64+
self.last_image = Image.new("RGBA", (250, 150), (128, 128, 128, 255))
65+
self.new_image: PilPNG | None = None
66+
self.binary_image: bytes | None = None
67+
self.image_last_updated: float = 0.0
68+
self.image_format = "image/pil"
69+
self.image_size = None
70+
self.robot_size = None
71+
self.image_auto_zoom: bool = False
72+
self.image_zoom_lock_ratio: bool = True
73+
self.image_ref_height: int = 0
74+
self.image_ref_width: int = 0
75+
self.image_aspect_ratio: str = "None"
76+
self.image_grab = True
77+
self.image_rotate: int = 0
78+
self.drawing_limit: float = 0.0
79+
self.current_room = None
80+
self.user_colors = Colors
81+
self.rooms_colors = Colors
82+
self.vacuum_battery = 0
83+
self.vacuum_connection = False
84+
self.vacuum_state = None
85+
self.charger_position = None
86+
self.show_vacuum_state = None
9087
self.vacuum_status_font: str = (
91-
"custom_components/mqtt_vacuum_camera/utils/fonts/FiraSans.ttf" # Font
88+
"custom_components/mqtt_vacuum_camera/utils/fonts/FiraSans.ttf"
9289
)
93-
self.vacuum_status_size: int = 50 # Vacuum status size
94-
self.vacuum_status_position: bool = True # Vacuum status text image top
95-
self.snapshot_take = False # Take snapshot
96-
self.vacuum_error = None # Vacuum error
97-
self.vacuum_api = None # Vacuum API
98-
self.vacuum_ips = None # Vacuum IPs
99-
self.vac_json_id = None # Vacuum json id
100-
self.margins = "100" # Image margins
101-
self.obstacles_data = None # Obstacles data
102-
self.obstacles_pos = None # Obstacles position
103-
self.offset_top = 0 # Image offset top
104-
self.offset_down = 0 # Image offset down
105-
self.offset_left = 0 # Image offset left
106-
self.offset_right = 0 # Image offset right
107-
self.export_svg = False # Export SVG
108-
self.svg_path = None # SVG Export path
109-
self.enable_snapshots = False # Enable snapshots
110-
self.file_name = file_name # vacuum friendly name as File name
111-
self.attr_calibration_points = None # Calibration points of the image
112-
self.map_rooms = None # Rooms data from the vacuum
113-
self.map_pred_zones = None # Predefined zones data
114-
self.map_pred_points = None # Predefined points data
115-
self.map_new_path = None # New path data
116-
self.map_old_path = None # Old path data
117-
self.user_language = None # User language
90+
self.vacuum_status_size: int = 50
91+
self.vacuum_status_position: bool = True
92+
self.snapshot_take = False
93+
self.vacuum_error = None
94+
self.vacuum_api = None
95+
self.vacuum_ips = None
96+
self.vac_json_id = None
97+
self.margins = "100"
98+
self.obstacles_data = None
99+
self.obstacles_pos = None
100+
self.offset_top = 0
101+
self.offset_down = 0
102+
self.offset_left = 0
103+
self.offset_right = 0
104+
self.export_svg = False
105+
self.svg_path = None
106+
self.enable_snapshots = False
107+
self.file_name = file_name
108+
self.attr_calibration_points = None
109+
self.map_rooms = None
110+
self.map_pred_zones = None
111+
self.map_pred_points = None
112+
self.map_new_path = None
113+
self.map_old_path = None
114+
self.user_language = None
118115
self.trim_crop_data = None
119-
self.trims = TrimsData.from_dict(DEFAULT_VALUES["trims_data"]) # Trims data
116+
self.trims = TrimsData.from_dict(DEFAULT_VALUES["trims_data"])
120117
self.skip_room_ids: List[str] = []
121-
self.device_info = None # Store the device_info
118+
self.device_info = None
122119

123120
def vacuum_bat_charged(self) -> bool:
124121
"""Check if the vacuum is charging."""
125122
return (self.vacuum_state == "docked") and (int(self.vacuum_battery) < 100)
126123

127124
@staticmethod
128125
def _compose_obstacle_links(vacuum_host_ip: str, obstacles: list) -> list | None:
129-
"""
130-
Compose JSON with obstacle details including the image link.
131-
"""
126+
"""Compose JSON with obstacle details including the image link."""
132127
obstacle_links = []
133128
if not obstacles or not vacuum_host_ip:
134129
return None
135130

136131
for obstacle in obstacles:
137-
# Extract obstacle details
138132
label = obstacle.get("label", "")
139133
points = obstacle.get("points", {})
140134
image_id = obstacle.get("id", "None")
141135

142136
if label and points and image_id and vacuum_host_ip:
143-
# Append formatted obstacle data
144137
if image_id != "None":
145-
# Compose the link
146138
image_link = (
147139
f"http://{vacuum_host_ip}"
148140
f"/api/v2/robot/capabilities/ObstacleImagesCapability/img/{image_id}"
149141
)
150142
obstacle_links.append(
151-
{
152-
"point": points,
153-
"label": label,
154-
"link": image_link,
155-
}
143+
{"point": points, "label": label, "link": image_link}
156144
)
157145
else:
158-
obstacle_links.append(
159-
{
160-
"point": points,
161-
"label": label,
162-
}
163-
)
146+
obstacle_links.append({"point": points, "label": label})
164147
return obstacle_links
165148

166149
def update_user_colors(self, user_colors):
167-
"""Update the user colors."""
150+
"""Update user colors palette"""
168151
self.user_colors = user_colors
169152

170153
def get_user_colors(self):
171-
"""Get the user colors."""
154+
"""Return user colors"""
172155
return self.user_colors
173156

174157
def update_rooms_colors(self, user_colors):
175158
"""Update the rooms colors."""
176159
self.rooms_colors = user_colors
177160

178161
def get_rooms_colors(self):
179-
"""Get the rooms colors."""
162+
"""Return rooms colors"""
180163
return self.rooms_colors
181164

182165
def reset_trims(self) -> dict:
@@ -185,7 +168,7 @@ def reset_trims(self) -> dict:
185168
return self.trims
186169

187170
async def batch_update(self, **kwargs):
188-
"""Batch update multiple attributes."""
171+
"""Update the data of Shared in Batch"""
189172
for key, value in kwargs.items():
190173
setattr(self, key, value)
191174

@@ -210,24 +193,32 @@ def generate_attributes(self) -> dict:
210193
)
211194
attrs[ATTR_OBSTACLES] = self.obstacles_data
212195

213-
if self.enable_snapshots:
214-
attrs[ATTR_SNAPSHOT] = self.snapshot_take
215-
else:
216-
attrs[ATTR_SNAPSHOT] = False
196+
attrs[ATTR_SNAPSHOT] = self.snapshot_take if self.enable_snapshots else False
217197

218-
# Add dynamic shared attributes if they are available
219198
shared_attrs = {
220199
ATTR_ROOMS: self.map_rooms,
221200
ATTR_ZONES: self.map_pred_zones,
222201
ATTR_POINTS: self.map_pred_points,
223202
}
224-
225203
for key, value in shared_attrs.items():
226204
if value is not None:
227205
attrs[key] = value
228206

229207
return attrs
230208

209+
def to_dict(self) -> dict:
210+
"""Return a dictionary with image and attributes data."""
211+
return {
212+
"image": {
213+
"binary": self.binary_image,
214+
"pil_image_size": self.new_image.size,
215+
"size": self.new_image.size if self.new_image else None,
216+
"format": self.image_format,
217+
"updated": self.image_last_updated,
218+
},
219+
"attributes": self.generate_attributes(),
220+
}
221+
231222

232223
class CameraSharedManager:
233224
"""Camera Shared Manager class."""

SCR/valetudo_map_parser/config/types.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import logging
99
import threading
1010
from dataclasses import asdict, dataclass
11-
from typing import Any, Dict, Optional, Tuple, TypedDict, Union
11+
from typing import Any, Dict, Optional, Tuple, TypedDict, Union, List, NotRequired
1212

1313
import numpy as np
1414
from PIL import Image
@@ -18,6 +18,23 @@
1818

1919
LOGGER = logging.getLogger(__package__)
2020

21+
class Spot(TypedDict):
22+
name: str
23+
coordinates: List[int] # [x, y]
24+
25+
class Zone(TypedDict):
26+
name: str
27+
coordinates: List[List[int]] # [[x1, y1, x2, y2, repeats], ...]
28+
29+
class Room(TypedDict):
30+
name: str
31+
id: int
32+
33+
class Destinations(TypedDict, total=False):
34+
spots: NotRequired[Optional[List[Spot]]]
35+
zones: NotRequired[Optional[List[Zone]]]
36+
rooms: NotRequired[Optional[List[Room]]]
37+
updated: NotRequired[Optional[int]]
2138

2239
class RoomProperty(TypedDict):
2340
number: int

0 commit comments

Comments
 (0)