66
77import logging
88from collections .abc import Callable
9- from typing import Any , TypeVar
9+ from typing import TypeVar
1010
1111from roborock .containers import HomeDataDevice , NetworkInfo , RoborockBase , UserData
1212from roborock .exceptions import RoborockException
1313from roborock .mqtt .session import MqttParams , MqttSession
1414from roborock .protocols .v1_protocol import (
15- CommandType ,
16- ParamsType ,
1715 SecurityData ,
18- create_mqtt_payload_encoder ,
1916 create_security_data ,
20- decode_rpc_response ,
21- encode_local_payload ,
2217)
2318from roborock .roborock_message import RoborockMessage
2419from roborock .roborock_typing import RoborockCommand
2520
2621from .local_channel import LocalChannel , LocalSession , create_local_session
2722from .mqtt_channel import MqttChannel
23+ from .v1_rpc_channel import V1RpcChannel , create_combined_rpc_channel , create_mqtt_rpc_channel
2824
2925_LOGGER = logging .getLogger (__name__ )
3026
@@ -58,9 +54,10 @@ def __init__(
5854 """
5955 self ._device_uid = device_uid
6056 self ._mqtt_channel = mqtt_channel
61- self ._mqtt_payload_encoder = create_mqtt_payload_encoder ( security_data )
57+ self ._mqtt_rpc_channel = create_mqtt_rpc_channel ( mqtt_channel , security_data )
6258 self ._local_session = local_session
6359 self ._local_channel : LocalChannel | None = None
60+ self ._combined_rpc_channel : V1RpcChannel | None = None
6461 self ._mqtt_unsub : Callable [[], None ] | None = None
6562 self ._local_unsub : Callable [[], None ] | None = None
6663 self ._callback : Callable [[RoborockMessage ], None ] | None = None
@@ -76,6 +73,16 @@ def is_mqtt_connected(self) -> bool:
7673 """Return whether MQTT connection is available."""
7774 return self ._mqtt_unsub is not None
7875
76+ @property
77+ def rpc_channel (self ) -> V1RpcChannel :
78+ """Return the combined RPC channel prefers local with a fallback to MQTT."""
79+ return self ._combined_rpc_channel or self ._mqtt_rpc_channel
80+
81+ @property
82+ def mqtt_rpc_channel (self ) -> V1RpcChannel :
83+ """Return the MQTT RPC channel."""
84+ return self ._mqtt_rpc_channel
85+
7986 async def subscribe (self , callback : Callable [[RoborockMessage ], None ]) -> Callable [[], None ]:
8087 """Subscribe to all messages from the device.
8188
@@ -119,7 +126,9 @@ async def _get_networking_info(self) -> NetworkInfo:
119126 This is a cloud only command used to get the local device's IP address.
120127 """
121128 try :
122- return await self ._send_mqtt_decoded_command (RoborockCommand .GET_NETWORK_INFO , response_type = NetworkInfo )
129+ return await self ._mqtt_rpc_channel .send_command (
130+ RoborockCommand .GET_NETWORK_INFO , response_type = NetworkInfo
131+ )
123132 except RoborockException as e :
124133 raise RoborockException (f"Network info failed for device { self ._device_uid } " ) from e
125134
@@ -136,59 +145,9 @@ async def _local_connect(self) -> Callable[[], None]:
136145 except RoborockException as e :
137146 self ._local_channel = None
138147 raise RoborockException (f"Error connecting to local device { self ._device_uid } : { e } " ) from e
139-
148+ self . _combined_rpc_channel = create_combined_rpc_channel ( self . _local_channel , self . _mqtt_rpc_channel )
140149 return await self ._local_channel .subscribe (self ._on_local_message )
141150
142- async def send_decoded_command (
143- self ,
144- method : CommandType ,
145- * ,
146- response_type : type [_T ],
147- params : ParamsType = None ,
148- ) -> _T :
149- """Send a command using the best available transport.
150-
151- Will prefer local connection if available, falling back to MQTT.
152- """
153- connection = "local" if self .is_local_connected else "mqtt"
154- _LOGGER .debug ("Sending command (%s): %s, params=%s" , connection , method , params )
155- if self ._local_channel :
156- return await self ._send_local_decoded_command (method , response_type = response_type , params = params )
157- return await self ._send_mqtt_decoded_command (method , response_type = response_type , params = params )
158-
159- async def _send_mqtt_raw_command (self , method : CommandType , params : ParamsType | None = None ) -> dict [str , Any ]:
160- """Send a raw command and return a raw unparsed response."""
161- message = self ._mqtt_payload_encoder (method , params )
162- _LOGGER .debug ("Sending MQTT message for device %s: %s" , self ._device_uid , message )
163- response = await self ._mqtt_channel .send_command (message )
164- return decode_rpc_response (response )
165-
166- async def _send_mqtt_decoded_command (
167- self , method : CommandType , * , response_type : type [_T ], params : ParamsType | None = None
168- ) -> _T :
169- """Send a command over MQTT and decode the response."""
170- decoded_response = await self ._send_mqtt_raw_command (method , params )
171- return response_type .from_dict (decoded_response )
172-
173- async def _send_local_raw_command (self , method : CommandType , params : ParamsType | None = None ) -> dict [str , Any ]:
174- """Send a raw command over local connection."""
175- if not self ._local_channel :
176- raise RoborockException ("Local channel is not connected" )
177-
178- message = encode_local_payload (method , params )
179- _LOGGER .debug ("Sending local message for device %s: %s" , self ._device_uid , message )
180- response = await self ._local_channel .send_command (message )
181- return decode_rpc_response (response )
182-
183- async def _send_local_decoded_command (
184- self , method : CommandType , * , response_type : type [_T ], params : ParamsType | None = None
185- ) -> _T :
186- """Send a command over local connection and decode the response."""
187- if not self ._local_channel :
188- raise RoborockException ("Local channel is not connected" )
189- decoded_response = await self ._send_local_raw_command (method , params )
190- return response_type .from_dict (decoded_response )
191-
192151 def _on_mqtt_message (self , message : RoborockMessage ) -> None :
193152 """Handle incoming MQTT messages."""
194153 _LOGGER .debug ("V1Channel received MQTT message from device %s: %s" , self ._device_uid , message )
0 commit comments