@@ -51,6 +51,7 @@ class OperatingModes(enum.IntEnum):
51
51
@enum .unique
52
52
class Properties (enum .Enum ):
53
53
"""Useful properties"""
54
+ AREAS_TO_CLEAN = "Areas_To_Clean"
54
55
BATTERY_CAPACITY = "Battery_Capacity"
55
56
CHARGING_STATUS = "Charging_Status"
56
57
CLEAN_COMPLETE = "CleanComplete"
@@ -66,6 +67,7 @@ class Properties(enum.Enum):
66
67
RECHARGE_RESUME = "Recharge_Resume"
67
68
RECHARGING_TO_RESUME = "Recharging_To_Resume"
68
69
ROBOT_FIRMWARE_VERSION = "Robot_Firmware_Version"
70
+ ROBOT_ROOM_LIST = "Robot_Room_List"
69
71
RSSI = "RSSI"
70
72
71
73
@@ -341,16 +343,67 @@ async def async_get_file_property(self, property_name: PropertyName) -> bytes:
341
343
def _encode_room_list (self , rooms : List [str ]):
342
344
"""Base64 encode the list of rooms to clean"""
343
345
if not rooms :
344
- raise ValueError ('Room list must not be empty' )
345
- if len (rooms ) > 3 :
346
- raise ValueError ('At most three rooms may be given' )
347
- # These are a mystery to me, but they seem constant
348
- header = b'\x80 \x01 \x0b \xca \x02 '
349
- footer = b'\x1a \x08 155B43C4'
346
+ # By default, clean all rooms
347
+ return '*'
348
+
349
+ room_list = self ._get_device_room_list ()
350
+ _LOGGER .debug (f'Room list identifier is: { room_list ["identifier" ]} ' )
351
+
352
+ # Header explained:
353
+ # 0x80: Control character - some mode selection
354
+ # 0x01: Start of Heading Character
355
+ # 0x0B: Use Line Tabulation (entries separated by newlines)
356
+ # 0xca: Control character - purpose unknown
357
+ # 0x02: Start of text (indicates start of room list)
358
+ header = '\x80 \x01 \x0b \xca \x02 '
359
+
360
+ # For each room in the list:
361
+ # - Insert a byte representing the length of the room name string
362
+ # - Add the room name
363
+ # - Join with newlines (presumably because of the 0x0B in the header)
364
+ rooms_enc = "\n " .join ([chr (len (room )) + room for room in rooms ])
365
+
366
+ # The footer starts with control character 0x1A
367
+ # Then add the length indicator for the room list identifier
368
+ # Then add the room list identifier
369
+ footer = '\x1a ' + chr (len (room_list ['identifier' ])) + room_list ['identifier' ]
370
+
371
+ # Now that we've computed the room list and footer and know their lengths, finish building the header
372
+ # This character denotes the length of the remaining input
373
+ header += chr (0
374
+ + 1 # Add one for a newline following the length specifier
375
+ + len (rooms_enc )
376
+ + len (footer )
377
+ )
378
+ header += '\n ' # This is the newline reference above
379
+
380
+ # Finally, join and base64 encode the parts
381
+ return base64 .b64encode (
382
+ # First encode the string as latin_1 to get the right endianness
383
+ (header + rooms_enc + footer ).encode ('latin_1' )
384
+ # Then return as a utf8 string for ease of handling
385
+ ).decode ('utf8' )
386
+
387
+ def _get_device_room_list (self ):
388
+ """Gets the list of known rooms from the device, including the map identifier"""
389
+ room_list = self .get_property_value (Properties .ROBOT_ROOM_LIST )
390
+ split = room_list .split (':' )
391
+ return {
392
+ # The room list is preceded by an identifier, which I believe identifies the list of rooms with the
393
+ # onboard map in the robot
394
+ 'identifier' : split [0 ],
395
+ 'rooms' : split [1 :],
396
+ }
397
+
398
+ def get_room_list (self ) -> List [str ]:
399
+ """Gets the list of rooms known by the device"""
400
+ return self ._get_device_room_list ()['rooms' ]
350
401
351
402
def clean_rooms (self , rooms : List [str ]) -> None :
352
403
payload = self ._encode_room_list (rooms )
353
- raise NotImplementedError
404
+ _LOGGER .debug ('Room list payload: ' + payload )
405
+ self .set_property_value (Properties .AREAS_TO_CLEAN , payload )
406
+ self .set_operating_mode (OperatingModes .START )
354
407
355
408
356
409
class SharkPropertiesView (abc .Mapping ):
0 commit comments