Skip to content

Commit a315470

Browse files
authored
Corrections for Rand256 and updating the routines #229 from sca075/220-addig-on-off-for-map-trims
Corrections for Rand256 and updating the routines.
2 parents f6dbf7e + f7cbef8 commit a315470

File tree

6 files changed

+125
-110
lines changed

6 files changed

+125
-110
lines changed

custom_components/mqtt_vacuum_camera/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
"documentation": "https://github.com/sca075/mqtt_vacuum_camera",
88
"iot_class": "local_polling",
99
"issue_tracker": "https://github.com/sca075/mqtt_vacuum_camera/issues",
10-
"requirements": ["pillow==10.4.0", "numpy"],
11-
"version": "2024.08.0b2"
10+
"requirements": ["pillow", "numpy"],
11+
"version": "2024.08.0b3"
1212
}

custom_components/mqtt_vacuum_camera/utils/img_data.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
ImageData is part of the Image_Handler
44
used functions to search data in the json
55
provided for the creation of the new camera frame
6-
Version: v2024.06.1
6+
Version: v2024.08.0
77
"""
88

99
from __future__ import annotations
@@ -173,7 +173,7 @@ def find_virtual_walls_recursive(obj):
173173
return virtual_walls
174174

175175
@staticmethod
176-
async def get_rooms_coordinates(
176+
async def async_get_rooms_coordinates(
177177
pixels: list, pixel_size: int = 5, rand: bool = False
178178
) -> tuple:
179179
"""
@@ -463,10 +463,11 @@ def get_rrm_walls(json_data: JsonType) -> list:
463463
return img.get("pixels", {}).get("walls", [])
464464

465465
@staticmethod
466-
def get_rrm_segments(
466+
async def async_get_rrm_segments(
467467
json_data, size_x, size_y, pos_top, pos_left, out_lines: bool = False
468468
):
469469
"""Get the segments data from the json."""
470+
470471
img = ImageData.get_rrm_image(json_data)
471472
seg_data = img.get("segments", {})
472473
seg_ids = seg_data.get("id")
@@ -485,12 +486,11 @@ def get_rrm_segments(
485486
)
486487
)
487488
if out_lines:
488-
outlines.append(
489-
ImageData.get_rooms_coordinates(
490-
pixels=segments[count_seg], rand=True
491-
)
492-
)
493-
count_seg += 1
489+
room_coords = await ImageData.async_get_rooms_coordinates(
490+
pixels=segments[count_seg],
491+
rand=True)
492+
outlines.append(room_coords)
493+
count_seg += 1
494494
if count_seg > 0:
495495
if out_lines:
496496
return segments, outlines

custom_components/mqtt_vacuum_camera/valetudo/MQTT/connector.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Version: v2024.08.0b0
2+
Version: v2024.08.0
33
- Removed the PNG decode, the json is extracted from map-data instead of map-data-hass.
44
- Tested no influence on the camera performance.
55
- Added gzip library used in Valetudo RE data compression.
@@ -170,7 +170,7 @@ async def is_disconnect_vacuum(self) -> None:
170170
Disconnect the vacuum detected.
171171
Generate a Warning message if the vacuum is disconnected.
172172
"""
173-
if self._mqtt_vac_connect_state == "disconnected":
173+
if self._mqtt_vac_connect_state == "disconnected" or self._mqtt_vac_connect_state == "lost":
174174
_LOGGER.debug(
175175
f"{self._mqtt_topic}: Vacuum Disconnected from MQTT, waiting for connection."
176176
)
@@ -248,6 +248,9 @@ async def rand256_handle_destinations(self, msg) -> None:
248248
self._payload = msg.payload
249249
tmp_data = await self.async_decode_mqtt_payload(msg)
250250
self._rrm_destinations = tmp_data
251+
if 'rooms' in tmp_data:
252+
rooms_data = {str(room['id']): room['name'].strip('#') for room in tmp_data['rooms']}
253+
await RoomStore().async_set_rooms_data(self._file_name, rooms_data)
251254
_LOGGER.info(
252255
f"{self._file_name}: Received vacuum destinations: {self._rrm_destinations}"
253256
)

custom_components/mqtt_vacuum_camera/valetudo/hypfer/image_handler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Hypfer Image Handler Class.
33
It returns the PIL PNG image frame relative to the Map Data extrapolated from the vacuum json.
44
It also returns calibration, rooms data to the card and other images information to the camera.
5-
Version: 2024.07.2
5+
Version: 2024.08.0
66
"""
77

88
from __future__ import annotations
@@ -270,7 +270,7 @@ async def async_extract_room_properties(self, json_data):
270270
compressed_pixels = layer.get("compressedPixels", [])
271271
pixels = self.data.sublist(compressed_pixels, 3)
272272
# Calculate x and y min/max from compressed pixels
273-
x_min, y_min, x_max, y_max = await self.data.get_rooms_coordinates(
273+
x_min, y_min, x_max, y_max = await self.data.async_get_rooms_coordinates(
274274
pixels, pixel_size
275275
)
276276
corners = [

custom_components/mqtt_vacuum_camera/valetudo/rand256/image_handler.py

Lines changed: 106 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22
Image Handler Module for Valetudo Re Vacuums.
33
It returns the PIL PNG image frame relative to the Map Data extrapolated from the vacuum json.
44
It also returns calibration, rooms data to the card and other images information to the camera.
5-
Version: v2024.04.3
5+
Version: v2024.08.0
66
"""
77

88
from __future__ import annotations
99

10-
import json
1110
import logging
1211
import uuid
1312

@@ -37,6 +36,8 @@ class ReImageHandler(object):
3736

3837
def __init__(self, camera_shared):
3938
self.auto_crop = None # Auto crop flag
39+
self.segment_data = None # Segment data
40+
self.outlines = None # Outlines data
4041
self.calibration_data = None # Calibration data
4142
self.charger_pos = None # Charger position
4243
self.crop_area = None # Crop area
@@ -153,7 +154,7 @@ async def auto_crop_and_trim_array(
153154
del image_array
154155
# Rotate the cropped image based on the given angle
155156
if rotate == 90:
156-
rotated = np.rot90(trimmed, 1)
157+
rotated = np.rot90(trimmed)
157158
self.crop_area = [
158159
self.trim_left,
159160
self.trim_up,
@@ -180,96 +181,104 @@ async def auto_crop_and_trim_array(
180181
_LOGGER.debug("Trimmed image size: %s", self.crop_img_size)
181182
return rotated
182183

183-
def extract_room_properties(
184+
async def extract_room_properties(
184185
self, json_data: JsonType, destinations: JsonType
185186
) -> RoomsProperties:
186187
"""Extract the room properties."""
187188
unsorted_id = ImageData.get_rrm_segments_ids(json_data)
188189
size_x, size_y = ImageData.get_rrm_image_size(json_data)
189190
top, left = ImageData.get_rrm_image_position(json_data)
190-
dummy_segments, outlines = ImageData.get_rrm_segments(
191-
json_data, size_x, size_y, top, left, True
192-
)
193-
del dummy_segments # free memory
194-
dest_json = json.loads(destinations)
195-
room_data = dict(dest_json).get("rooms", [])
196-
zones_data = dict(dest_json).get("zones", [])
197-
points_data = dict(dest_json).get("spots", [])
198-
room_id_to_data = {room["id"]: room for room in room_data}
199-
self.rooms_pos = []
200-
room_properties = {}
201-
zone_properties = {}
202-
point_properties = {}
203-
for id_x, room_id in enumerate(unsorted_id):
204-
if room_id in room_id_to_data:
205-
room_info = room_id_to_data[room_id]
206-
name = room_info.get("name")
207-
# Calculate x and y min/max from outlines
208-
x_min = outlines[id_x][0][0]
209-
x_max = outlines[id_x][1][0]
210-
y_min = outlines[id_x][0][1]
211-
y_max = outlines[id_x][1][1]
212-
corners = [
213-
(x_min, y_min),
214-
(x_max, y_min),
215-
(x_max, y_max),
216-
(x_min, y_max),
217-
]
218-
# rand256 vacuums accept int(room_id) or str(name)
219-
# the card will soon support int(room_id) but the camera will send name
220-
# this avoids the manual change of the values in the card.
221-
self.rooms_pos.append(
222-
{
223-
"name": name,
224-
"corners": corners,
225-
}
226-
)
227-
room_properties[int(room_id)] = {
228-
"number": int(room_id),
229-
"outline": corners,
230-
"name": name,
231-
"x": (x_min + x_max) // 2,
232-
"y": (y_min + y_max) // 2,
233-
}
234-
id_count = 1
235-
for zone in zones_data:
236-
zone_name = zone.get("name")
237-
coordinates = zone.get("coordinates")
238-
if coordinates and len(coordinates) > 0:
239-
coordinates[0].pop()
240-
x1, y1, x2, y2 = coordinates[0]
241-
zone_properties[zone_name] = {
242-
"zones": coordinates,
243-
"name": zone_name,
244-
"x": ((x1 + x2) // 2),
245-
"y": ((y1 + y2) // 2),
246-
}
247-
id_count += 1
248-
id_count = 1
249-
for point in points_data:
250-
point_name = point.get("name")
251-
coordinates = point.get("coordinates")
252-
if coordinates and len(coordinates) > 0:
253-
coordinates = point.get("coordinates")
254-
x1, y1 = coordinates
255-
point_properties[id_count] = {
256-
"position": coordinates,
257-
"name": point_name,
258-
"x": x1,
259-
"y": y1,
260-
}
261-
id_count += 1
262-
if room_properties != {}:
263-
if zone_properties != {}:
264-
_LOGGER.debug("Rooms and Zones, data extracted!")
265-
else:
266-
_LOGGER.debug("Rooms, data extracted!")
267-
elif zone_properties != {}:
268-
_LOGGER.debug("Zones, data extracted!")
269-
else:
270-
self.rooms_pos = None
271-
_LOGGER.debug("Rooms and Zones data not available!")
272-
return room_properties, zone_properties, point_properties
191+
if not self.segment_data or not self.outlines:
192+
self.segment_data, self.outlines = await ImageData.async_get_rrm_segments(
193+
json_data, size_x, size_y, top, left, True
194+
)
195+
try:
196+
dest_json = destinations
197+
room_data = dict(dest_json).get("rooms", [])
198+
zones_data = dict(dest_json).get("zones", [])
199+
points_data = dict(dest_json).get("spots", [])
200+
room_id_to_data = {room["id"]: room for room in room_data}
201+
self.rooms_pos = []
202+
room_properties = {}
203+
zone_properties = {}
204+
point_properties = {}
205+
if self.outlines:
206+
for id_x, room_id in enumerate(unsorted_id):
207+
if room_id in room_id_to_data:
208+
room_info = room_id_to_data[room_id]
209+
name = room_info.get("name")
210+
# Calculate x and y min/max from outlines
211+
x_min = self.outlines[id_x][0][0]
212+
x_max = self.outlines[id_x][1][0]
213+
y_min = self.outlines[id_x][0][1]
214+
y_max = self.outlines[id_x][1][1]
215+
corners = [
216+
(x_min, y_min),
217+
(x_max, y_min),
218+
(x_max, y_max),
219+
(x_min, y_max),
220+
]
221+
# rand256 vacuums accept int(room_id) or str(name)
222+
# the card will soon support int(room_id) but the camera will send name
223+
# this avoids the manual change of the values in the card.
224+
self.rooms_pos.append(
225+
{
226+
"name": name,
227+
"corners": corners,
228+
}
229+
)
230+
room_properties[int(room_id)] = {
231+
"number": int(room_id),
232+
"outline": corners,
233+
"name": name,
234+
"x": (x_min + x_max) // 2,
235+
"y": (y_min + y_max) // 2,
236+
}
237+
id_count = 1
238+
for zone in zones_data:
239+
zone_name = zone.get("name")
240+
coordinates = zone.get("coordinates")
241+
if coordinates and len(coordinates) > 0:
242+
coordinates[0].pop()
243+
x1, y1, x2, y2 = coordinates[0]
244+
zone_properties[zone_name] = {
245+
"zones": coordinates,
246+
"name": zone_name,
247+
"x": ((x1 + x2) // 2),
248+
"y": ((y1 + y2) // 2),
249+
}
250+
id_count += 1
251+
id_count = 1
252+
for point in points_data:
253+
point_name = point.get("name")
254+
coordinates = point.get("coordinates")
255+
if coordinates and len(coordinates) > 0:
256+
coordinates = point.get("coordinates")
257+
x1, y1 = coordinates
258+
point_properties[id_count] = {
259+
"position": coordinates,
260+
"name": point_name,
261+
"x": x1,
262+
"y": y1,
263+
}
264+
id_count += 1
265+
if room_properties != {}:
266+
if zone_properties != {}:
267+
_LOGGER.debug("Rooms and Zones, data extracted!")
268+
else:
269+
_LOGGER.debug("Rooms, data extracted!")
270+
elif zone_properties != {}:
271+
_LOGGER.debug("Zones, data extracted!")
272+
else:
273+
self.rooms_pos = None
274+
_LOGGER.debug("Rooms and Zones data not available!")
275+
return room_properties, zone_properties, point_properties
276+
except Exception as e:
277+
_LOGGER.warning(
278+
f"{self.file_name} : Error in extract_room_properties: {e}",
279+
exc_info=True,
280+
)
281+
return None, None, None
273282

274283
async def get_image_from_rrm(
275284
self,
@@ -367,10 +376,12 @@ async def get_image_from_rrm(
367376
image_left=pos_left,
368377
)
369378
# checking if there are segments too (sorted pixels in the raw data).
370-
segments = self.data.get_rrm_segments(
371-
m_json, size_x, size_y, pos_top, pos_left
372-
)
373-
if (segments and pixels) or pixels:
379+
if not self.segment_data:
380+
self.segment_data, self.outlines = await self.data.async_get_rrm_segments(
381+
m_json, size_x, size_y, pos_top, pos_left, True
382+
)
383+
384+
if (self.segment_data and pixels) or pixels:
374385
room_color = self.shared.rooms_colors[room_id]
375386
# drawing floor
376387
if pixels:
@@ -380,8 +391,9 @@ async def get_image_from_rrm(
380391
# drawing segments floor
381392
room_id = 0
382393
rooms_list = [color_wall]
383-
if segments:
384-
for pixels in segments:
394+
if self.segment_data:
395+
_LOGGER.info(self.file_name + ": Drawing segments ")
396+
for pixels in self.segment_data:
385397
room_color = self.shared.rooms_colors[room_id]
386398
rooms_list.append(room_color)
387399
if (
@@ -573,7 +585,7 @@ async def get_rooms_attributes(
573585
return self.room_propriety
574586
if self.json_data and destinations:
575587
_LOGGER.debug("Checking for rooms data..")
576-
self.room_propriety = self.extract_room_properties(
588+
self.room_propriety = await self.extract_room_properties(
577589
self.json_data, destinations
578590
)
579591
if self.room_propriety:

hacs.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
"name": "MQTT Vacuum Camera",
33
"zip_release": true,
44
"render_readme": true,
5-
"homeassistant": "2024.8.0b0",
5+
"homeassistant": "2024.7.0b0",
66
"filename": "mqtt_vacuum_camera.zip"
77
}

0 commit comments

Comments
 (0)