2
2
Image Handler Module for Valetudo Re Vacuums.
3
3
It returns the PIL PNG image frame relative to the Map Data extrapolated from the vacuum json.
4
4
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
6
6
"""
7
7
8
8
from __future__ import annotations
9
9
10
- import json
11
10
import logging
12
11
import uuid
13
12
@@ -37,6 +36,8 @@ class ReImageHandler(object):
37
36
38
37
def __init__ (self , camera_shared ):
39
38
self .auto_crop = None # Auto crop flag
39
+ self .segment_data = None # Segment data
40
+ self .outlines = None # Outlines data
40
41
self .calibration_data = None # Calibration data
41
42
self .charger_pos = None # Charger position
42
43
self .crop_area = None # Crop area
@@ -153,7 +154,7 @@ async def auto_crop_and_trim_array(
153
154
del image_array
154
155
# Rotate the cropped image based on the given angle
155
156
if rotate == 90 :
156
- rotated = np .rot90 (trimmed , 1 )
157
+ rotated = np .rot90 (trimmed )
157
158
self .crop_area = [
158
159
self .trim_left ,
159
160
self .trim_up ,
@@ -180,96 +181,104 @@ async def auto_crop_and_trim_array(
180
181
_LOGGER .debug ("Trimmed image size: %s" , self .crop_img_size )
181
182
return rotated
182
183
183
- def extract_room_properties (
184
+ async def extract_room_properties (
184
185
self , json_data : JsonType , destinations : JsonType
185
186
) -> RoomsProperties :
186
187
"""Extract the room properties."""
187
188
unsorted_id = ImageData .get_rrm_segments_ids (json_data )
188
189
size_x , size_y = ImageData .get_rrm_image_size (json_data )
189
190
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
273
282
274
283
async def get_image_from_rrm (
275
284
self ,
@@ -367,10 +376,12 @@ async def get_image_from_rrm(
367
376
image_left = pos_left ,
368
377
)
369
378
# 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 :
374
385
room_color = self .shared .rooms_colors [room_id ]
375
386
# drawing floor
376
387
if pixels :
@@ -380,8 +391,9 @@ async def get_image_from_rrm(
380
391
# drawing segments floor
381
392
room_id = 0
382
393
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 :
385
397
room_color = self .shared .rooms_colors [room_id ]
386
398
rooms_list .append (room_color )
387
399
if (
@@ -573,7 +585,7 @@ async def get_rooms_attributes(
573
585
return self .room_propriety
574
586
if self .json_data and destinations :
575
587
_LOGGER .debug ("Checking for rooms data.." )
576
- self .room_propriety = self .extract_room_properties (
588
+ self .room_propriety = await self .extract_room_properties (
577
589
self .json_data , destinations
578
590
)
579
591
if self .room_propriety :
0 commit comments