66import time
77
88from . import RoborockCommand
9- from .containers import DeviceData , UserData
9+ from .containers import DeviceData , ModelStatus , S7MaxVStatus , Status , UserData
10+ from .device_trait import ConsumableTrait , DeviceTrait , DndTrait
1011from .mqtt_manager import RoborockMqttManager
1112from .protocol import MessageParser , Utils
1213from .roborock_message import RoborockMessage , RoborockMessageProtocol
@@ -25,35 +26,34 @@ def __init__(self, user_data: UserData, device_info: DeviceData):
2526 self ._local_endpoint = "abc"
2627 self ._nonce = secrets .token_bytes (16 )
2728 self .manager = RoborockMqttManager ()
28- self .update_commands = self .determine_supported_commands ()
29-
30- def determine_supported_commands (self ):
31- # All devices support these
32- supported_commands = {
33- RoborockCommand .GET_CONSUMABLE ,
34- RoborockCommand .GET_STATUS ,
35- RoborockCommand .GET_CLEAN_SUMMARY ,
36- }
37- # Get what features we can from the feature_set info.
38-
39- # If a command is not described in feature_set, we should just add it anyways and then let it fail on the first call and remove it.
40- robot_new_features = int (self .device_info .device .feature_set )
41- new_feature_info_str = self .device_info .device .new_feature_set
42- if 33554432 & int (robot_new_features ):
43- supported_commands .add (RoborockCommand .GET_DUST_COLLECTION_MODE )
44- if 2 & int (new_feature_info_str [- 8 :], 16 ):
45- # TODO: May not be needed as i think this can just be found in Status, but just POC
46- supported_commands .add (RoborockCommand .APP_GET_CLEAN_ESTIMATE_INFO )
47- return supported_commands
29+ self ._message_id_types : dict [int , DeviceTrait ] = {}
30+ self ._command_to_trait = {}
31+ self ._all_supported_traits = []
32+ self ._dnd_trait : DndTrait | None = self .determine_supported_traits (DndTrait )
33+ self ._consumable_trait : ConsumableTrait | None = self .determine_supported_traits (ConsumableTrait )
34+ self ._status_type : type [Status ] = ModelStatus .get (device_info .model , S7MaxVStatus )
35+
36+ def determine_supported_traits (self , trait : type [DeviceTrait ]):
37+ def _send_command (
38+ method : RoborockCommand | str , params : list | dict | int | None = None , use_cloud : bool = True
39+ ):
40+ return self .send_message (method , params , use_cloud )
41+
42+ if trait .supported (self .device_info .device_features ):
43+ trait_instance = trait (_send_command )
44+ self ._all_supported_traits .append (trait (_send_command ))
45+ self ._command_to_trait [trait .handle_command ] = trait_instance
46+ return trait_instance
47+ return None
4848
4949 async def connect (self ):
5050 """Connect via MQTT and Local if possible."""
5151 await self .manager .subscribe (self .user_data , self .device_info , self .on_message )
5252 await self .update ()
5353
5454 async def update (self ):
55- for cmd in self .update_commands :
56- await self . send_message ( method = cmd )
55+ for trait in self ._all_supported_traits :
56+ await trait . get ( )
5757
5858 def _get_payload (
5959 self ,
@@ -91,7 +91,9 @@ async def send_message(
9191 request_id , timestamp , payload = self ._get_payload (method , params , True , use_cloud )
9292 request_protocol = RoborockMessageProtocol .RPC_REQUEST
9393 roborock_message = RoborockMessage (timestamp = timestamp , protocol = request_protocol , payload = payload )
94-
94+ if request_id in self ._message_id_types :
95+ raise Exception ("Duplicate id!" )
96+ self ._message_id_types [request_id ] = self ._command_to_trait [method ]
9597 local_key = self .device_info .device .local_key
9698 msg = MessageParser .build (roborock_message , local_key , False )
9799 if use_cloud :
@@ -101,6 +103,19 @@ async def send_message(
101103 pass
102104
103105 def on_message (self , message : RoborockMessage ):
106+ message_payload = message .get_payload ()
107+ message_id = message .get_request_id ()
108+ for data_point_number , data_point in message_payload .get ("dps" ).items ():
109+ if data_point_number == "102" :
110+ data_point_response = json .loads (data_point )
111+ result = data_point_response .get ("result" )
112+ if isinstance (result , list ) and len (result ) == 1 :
113+ result = result [0 ]
114+ if result and (trait := self ._message_id_types .get (message_id )) is not None :
115+ trait .on_message (result )
116+ if (error := result .get ("error" )) is not None :
117+ print (error )
118+ print ()
104119 # If message is command not supported - remove from self.update_commands
105120
106121 # If message is an error - log it?
@@ -115,3 +130,11 @@ def on_message(self, message: RoborockMessage):
115130
116131 # This should also probably be split with on_cloud_message and on_local_message.
117132 print (message )
133+
134+ @property
135+ def dnd (self ) -> DndTrait | None :
136+ return self ._dnd_trait
137+
138+ @property
139+ def consumable (self ) -> ConsumableTrait | None :
140+ return self ._consumable_trait
0 commit comments