1313import struct
1414import time
1515from random import randint
16- from typing import Any , Callable , Coroutine , Optional , Type , TypeVar
16+ from typing import Any , Callable , Coroutine , Optional , Type , TypeVar , final
1717
1818import aiohttp
1919
2020from .code_mappings import RoborockDockTypeCode
21+ from .command_cache import AttributeCache , CacheableAttribute , CommandType , parse_method
2122from .containers import (
2223 ChildLockStatus ,
2324 CleanRecord ,
5657from .roborock_future import RoborockFuture
5758from .roborock_message import RoborockDataProtocol , RoborockMessage
5859from .roborock_typing import DeviceProp , DockSummary , RoborockCommand
59- from .util import fallback_cache , unpack_list
60+ from .util import unpack_list
6061
6162_LOGGER = logging .getLogger (__name__ )
6263KEEPALIVE = 60
@@ -103,8 +104,9 @@ def __init__(self, endpoint: str, device_info: DeviceData) -> None:
103104 self ._last_disconnection = self .time_func ()
104105 self .keep_alive = KEEPALIVE
105106 self ._diagnostic_data : dict [str , dict [str , Any ]] = {}
106- self .dnd_timer : DnDTimer | None = None
107- self .valley_timer : ValleyElectricityTimer | None = None
107+ self .cache : dict [CacheableAttribute , AttributeCache ] = {
108+ cacheable_attribute : AttributeCache (cacheable_attribute ) for cacheable_attribute in CacheableAttribute
109+ }
108110
109111 def __del__ (self ) -> None :
110112 self .sync_disconnect ()
@@ -240,38 +242,51 @@ def _get_payload(
240242 )
241243 return request_id , timestamp , payload
242244
245+ async def _send_command (
246+ self ,
247+ method : RoborockCommand ,
248+ params : Optional [list | dict ] = None ,
249+ ):
250+ raise NotImplementedError
251+
252+ @final
243253 async def send_command (
244254 self ,
245255 method : RoborockCommand ,
246256 params : Optional [list | dict ] = None ,
247257 return_type : Optional [Type [RT ]] = None ,
248258 ) -> RT :
249- raise NotImplementedError
259+ parsed_method = parse_method (method )
260+ if parsed_method is not None and parsed_method .type == CommandType .GET :
261+ cache = self .cache [parsed_method .attribute ].value
262+ if cache is not None :
263+ if return_type :
264+ return return_type .from_dict (cache )
265+ return cache
266+
267+ response = await self ._send_command (method , params )
268+
269+ if parsed_method is not None :
270+ if parsed_method .type == CommandType .SET :
271+ self .cache [parsed_method .attribute ].evict ()
272+ elif parsed_method .type == CommandType .GET :
273+ self .cache [parsed_method .attribute ].load (response )
274+ if return_type :
275+ return return_type .from_dict (response )
276+ return response
250277
251- @fallback_cache
252278 async def get_status (self ) -> Status | None :
253279 _cls : Type [Status ] = ModelStatus .get (
254280 self .device_info .model , S7MaxVStatus
255281 ) # Default to S7 MAXV if we don't have the data
256282 return await self .send_command (RoborockCommand .GET_STATUS , return_type = _cls )
257283
258- @fallback_cache
259284 async def get_dnd_timer (self ) -> DnDTimer | None :
260- result = await self .send_command (RoborockCommand .GET_DND_TIMER , return_type = DnDTimer )
261- if result is not None :
262- self .dnd_timer = result
263- return result
285+ return await self .send_command (RoborockCommand .GET_DND_TIMER , return_type = DnDTimer )
264286
265- @fallback_cache
266287 async def get_valley_electricity_timer (self ) -> ValleyElectricityTimer | None :
267- result = await self .send_command (
268- RoborockCommand .GET_VALLEY_ELECTRICITY_TIMER , return_type = ValleyElectricityTimer
269- )
270- if result is not None :
271- self .valley_timer = result
272- return result
288+ return await self .send_command (RoborockCommand .GET_VALLEY_ELECTRICITY_TIMER , return_type = ValleyElectricityTimer )
273289
274- @fallback_cache
275290 async def get_clean_summary (self ) -> CleanSummary | None :
276291 clean_summary : dict | list | int = await self .send_command (RoborockCommand .GET_CLEAN_SUMMARY )
277292 if isinstance (clean_summary , dict ):
@@ -288,27 +303,21 @@ async def get_clean_summary(self) -> CleanSummary | None:
288303 return CleanSummary (clean_time = clean_summary )
289304 return None
290305
291- @fallback_cache
292306 async def get_clean_record (self , record_id : int ) -> CleanRecord | None :
293307 return await self .send_command (RoborockCommand .GET_CLEAN_RECORD , [record_id ], return_type = CleanRecord )
294308
295- @fallback_cache
296309 async def get_consumable (self ) -> Consumable | None :
297310 return await self .send_command (RoborockCommand .GET_CONSUMABLE , return_type = Consumable )
298311
299- @fallback_cache
300312 async def get_wash_towel_mode (self ) -> WashTowelMode | None :
301313 return await self .send_command (RoborockCommand .GET_WASH_TOWEL_MODE , return_type = WashTowelMode )
302314
303- @fallback_cache
304315 async def get_dust_collection_mode (self ) -> DustCollectionMode | None :
305316 return await self .send_command (RoborockCommand .GET_DUST_COLLECTION_MODE , return_type = DustCollectionMode )
306317
307- @fallback_cache
308318 async def get_smart_wash_params (self ) -> SmartWashParams | None :
309319 return await self .send_command (RoborockCommand .GET_SMART_WASH_PARAMS , return_type = SmartWashParams )
310320
311- @fallback_cache
312321 async def get_dock_summary (self , dock_type : RoborockDockTypeCode ) -> DockSummary | None :
313322 """Gets the status summary from the dock with the methods available for a given dock.
314323
@@ -330,7 +339,6 @@ async def get_dock_summary(self, dock_type: RoborockDockTypeCode) -> DockSummary
330339 )
331340 return DockSummary (dust_collection_mode , wash_towel_mode , smart_wash_params )
332341
333- @fallback_cache
334342 async def get_prop (self ) -> DeviceProp | None :
335343 """Gets device general properties."""
336344 [status , clean_summary , consumable ] = await asyncio .gather (
@@ -356,15 +364,12 @@ async def get_prop(self) -> DeviceProp | None:
356364 )
357365 return None
358366
359- @fallback_cache
360367 async def get_multi_maps_list (self ) -> MultiMapsList | None :
361368 return await self .send_command (RoborockCommand .GET_MULTI_MAPS_LIST , return_type = MultiMapsList )
362369
363- @fallback_cache
364370 async def get_networking (self ) -> NetworkInfo | None :
365371 return await self .send_command (RoborockCommand .GET_NETWORK_INFO , return_type = NetworkInfo )
366372
367- @fallback_cache
368373 async def get_room_mapping (self ) -> list [RoomMapping ] | None :
369374 """Gets the mapping from segment id -> iot id. Only works on local api."""
370375 mapping : list = await self .send_command (RoborockCommand .GET_ROOM_MAPPING )
@@ -375,17 +380,14 @@ async def get_room_mapping(self) -> list[RoomMapping] | None:
375380 ]
376381 return None
377382
378- @fallback_cache
379383 async def get_child_lock_status (self ) -> ChildLockStatus | None :
380384 """Gets current child lock status."""
381385 return await self .send_command (RoborockCommand .GET_CHILD_LOCK_STATUS , return_type = ChildLockStatus )
382386
383- @fallback_cache
384387 async def get_flow_led_status (self ) -> FlowLedStatus | None :
385388 """Gets current flow led status."""
386389 return await self .send_command (RoborockCommand .GET_FLOW_LED_STATUS , return_type = FlowLedStatus )
387390
388- @fallback_cache
389391 async def get_sound_volume (self ) -> int | None :
390392 """Gets current volume level."""
391393 return await self .send_command (RoborockCommand .GET_SOUND_VOLUME )
0 commit comments