55
55
)
56
56
from .protocol import Utils
57
57
from .roborock_future import RoborockFuture
58
- from .roborock_message import RoborockDataProtocol , RoborockMessage , RoborockMessageProtocol
58
+ from .roborock_message import (
59
+ ROBOROCK_DATA_CONSUMABLE_PROTOCOL ,
60
+ ROBOROCK_DATA_STATUS_PROTOCOL ,
61
+ RoborockDataProtocol ,
62
+ RoborockMessage ,
63
+ RoborockMessageProtocol ,
64
+ )
59
65
from .roborock_typing import DeviceProp , DockSummary , RoborockCommand
60
66
from .util import RepeatableTask , get_running_loop_or_create_one , unpack_list
61
67
@@ -124,18 +130,23 @@ def stop(self):
124
130
self .task .cancel ()
125
131
126
132
async def update_value (self , params ):
133
+ if self .attribute .set_command is None :
134
+ raise RoborockException (f"{ self .attribute .attribute } have no set command" )
127
135
response = await self .api ._send_command (self .attribute .set_command , params )
128
136
await self ._async_value ()
129
137
return response
130
138
131
139
async def close_value (self ):
132
140
if self .attribute .close_command is None :
133
- raise RoborockException (f"{ self .attribute .attribute } is not closeable " )
141
+ raise RoborockException (f"{ self .attribute .attribute } have no close command " )
134
142
response = await self .api ._send_command (self .attribute .close_command )
135
143
await self ._async_value ()
136
144
return response
137
145
138
146
147
+ device_cache : dict [str , dict [CacheableAttribute , AttributeCache ]] = {}
148
+
149
+
139
150
class RoborockClient :
140
151
def __init__ (self , endpoint : str , device_info : DeviceData ) -> None :
141
152
self .event_loop = get_running_loop_or_create_one ()
@@ -147,9 +158,15 @@ def __init__(self, endpoint: str, device_info: DeviceData) -> None:
147
158
self ._last_disconnection = self .time_func ()
148
159
self .keep_alive = KEEPALIVE
149
160
self ._diagnostic_data : dict [str , dict [str , Any ]] = {}
150
- self .cache : dict [CacheableAttribute , AttributeCache ] = {
151
- cacheable_attribute : AttributeCache (attr , self ) for cacheable_attribute , attr in create_cache_map ().items ()
152
- }
161
+ cache = device_cache .get (device_info .device .duid )
162
+ if not cache :
163
+ cache = {
164
+ cacheable_attribute : AttributeCache (attr , self )
165
+ for cacheable_attribute , attr in create_cache_map ().items ()
166
+ }
167
+ device_cache [device_info .device .duid ] = cache
168
+ self .cache = cache
169
+ self ._listeners : list [Callable [[CacheableAttribute , RoborockBase ], None ]] = []
153
170
154
171
def __del__ (self ) -> None :
155
172
self .release ()
@@ -212,6 +229,30 @@ def on_message_received(self, messages: list[RoborockMessage]) -> None:
212
229
if isinstance (result , list ) and len (result ) == 1 :
213
230
result = result [0 ]
214
231
queue .resolve ((result , None ))
232
+ else :
233
+ try :
234
+ data_protocol = RoborockDataProtocol (int (data_point_number ))
235
+ _LOGGER .debug (f"Got device update for { data_protocol .name } : { data_point } " )
236
+ if data_protocol in ROBOROCK_DATA_STATUS_PROTOCOL :
237
+ _cls : Type [Status ] = ModelStatus .get (
238
+ self .device_info .model , S7MaxVStatus
239
+ ) # Default to S7 MAXV if we don't have the data
240
+ value = self .cache [CacheableAttribute .status ].value
241
+ value [data_protocol .name ] = data_point
242
+ status = _cls .from_dict (value )
243
+ for listener in self ._listeners :
244
+ listener (CacheableAttribute .status , status )
245
+ elif data_protocol in ROBOROCK_DATA_CONSUMABLE_PROTOCOL :
246
+ value = self .cache [CacheableAttribute .consumable ].value
247
+ value [data_protocol .name ] = data_point
248
+ consumable = Consumable .from_dict (value )
249
+ for listener in self ._listeners :
250
+ listener (CacheableAttribute .consumable , consumable )
251
+ return
252
+ except ValueError :
253
+ pass
254
+ dps = {data_point_number : data_point }
255
+ _LOGGER .debug (f"Got unknown data point { dps } " )
215
256
elif data .payload and protocol == RoborockMessageProtocol .MAP_RESPONSE :
216
257
payload = data .payload [0 :24 ]
217
258
[endpoint , _ , request_id , _ ] = struct .unpack ("<8s8sH6s" , payload )
@@ -223,8 +264,6 @@ def on_message_received(self, messages: list[RoborockMessage]) -> None:
223
264
if isinstance (decompressed , list ):
224
265
decompressed = decompressed [0 ]
225
266
queue .resolve ((decompressed , None ))
226
- elif data .payload and protocol in RoborockDataProtocol :
227
- _LOGGER .debug (f"Got device update for { RoborockDataProtocol (protocol ).name } : { data .payload !r} " )
228
267
else :
229
268
queue = self ._waiting_queue .get (data .seq )
230
269
if queue :
@@ -329,13 +368,15 @@ async def get_status(self) -> Status | None:
329
368
_cls : Type [Status ] = ModelStatus .get (
330
369
self .device_info .model , S7MaxVStatus
331
370
) # Default to S7 MAXV if we don't have the data
332
- return await self .send_command ( RoborockCommand . GET_STATUS , return_type = _cls )
371
+ return _cls . from_dict ( await self .cache [ CacheableAttribute . status ]. async_value () )
333
372
334
373
async def get_dnd_timer (self ) -> DnDTimer | None :
335
- return await self .send_command ( RoborockCommand . GET_DND_TIMER , return_type = DnDTimer )
374
+ return DnDTimer . from_dict ( await self .cache [ CacheableAttribute . dnd_timer ]. async_value () )
336
375
337
376
async def get_valley_electricity_timer (self ) -> ValleyElectricityTimer | None :
338
- return await self .send_command (RoborockCommand .GET_VALLEY_ELECTRICITY_TIMER , return_type = ValleyElectricityTimer )
377
+ return ValleyElectricityTimer .from_dict (
378
+ await self .cache [CacheableAttribute .valley_electricity_timer ].async_value ()
379
+ )
339
380
340
381
async def get_clean_summary (self ) -> CleanSummary | None :
341
382
clean_summary : dict | list | int = await self .send_command (RoborockCommand .GET_CLEAN_SUMMARY )
@@ -357,16 +398,16 @@ async def get_clean_record(self, record_id: int) -> CleanRecord | None:
357
398
return await self .send_command (RoborockCommand .GET_CLEAN_RECORD , [record_id ], return_type = CleanRecord )
358
399
359
400
async def get_consumable (self ) -> Consumable | None :
360
- return await self .send_command ( RoborockCommand . GET_CONSUMABLE , return_type = Consumable )
401
+ return Consumable . from_dict ( await self .cache [ CacheableAttribute . consumable ]. async_value () )
361
402
362
403
async def get_wash_towel_mode (self ) -> WashTowelMode | None :
363
- return await self .send_command ( RoborockCommand . GET_WASH_TOWEL_MODE , return_type = WashTowelMode )
404
+ return WashTowelMode . from_dict ( await self .cache [ CacheableAttribute . wash_towel_mode ]. async_value () )
364
405
365
406
async def get_dust_collection_mode (self ) -> DustCollectionMode | None :
366
- return await self .send_command ( RoborockCommand . GET_DUST_COLLECTION_MODE , return_type = DustCollectionMode )
407
+ return DustCollectionMode . from_dict ( await self .cache [ CacheableAttribute . dust_collection_mode ]. async_value () )
367
408
368
409
async def get_smart_wash_params (self ) -> SmartWashParams | None :
369
- return await self .send_command ( RoborockCommand . GET_SMART_WASH_PARAMS , return_type = SmartWashParams )
410
+ return SmartWashParams . from_dict ( await self .cache [ CacheableAttribute . smart_wash_params ]. async_value () )
370
411
371
412
async def get_dock_summary (self , dock_type : RoborockDockTypeCode ) -> DockSummary | None :
372
413
"""Gets the status summary from the dock with the methods available for a given dock.
@@ -432,15 +473,18 @@ async def get_room_mapping(self) -> list[RoomMapping] | None:
432
473
433
474
async def get_child_lock_status (self ) -> ChildLockStatus | None :
434
475
"""Gets current child lock status."""
435
- return await self .send_command ( RoborockCommand . GET_CHILD_LOCK_STATUS , return_type = ChildLockStatus )
476
+ return ChildLockStatus . from_dict ( await self .cache [ CacheableAttribute . child_lock_status ]. async_value () )
436
477
437
478
async def get_flow_led_status (self ) -> FlowLedStatus | None :
438
479
"""Gets current flow led status."""
439
- return await self .send_command ( RoborockCommand . GET_FLOW_LED_STATUS , return_type = FlowLedStatus )
480
+ return FlowLedStatus . from_dict ( await self .cache [ CacheableAttribute . flow_led_status ]. async_value () )
440
481
441
482
async def get_sound_volume (self ) -> int | None :
442
483
"""Gets current volume level."""
443
- return await self .send_command (RoborockCommand .GET_SOUND_VOLUME )
484
+ return await self .cache [CacheableAttribute .sound_volume ].async_value ()
485
+
486
+ def add_listener (self , listener : Callable ):
487
+ self ._listeners .append (listener )
444
488
445
489
446
490
class RoborockApiClient :
0 commit comments