Skip to content

Commit 12bf756

Browse files
fix: removing local_api.py nonworking commands from api.py
1 parent 29bdb45 commit 12bf756

File tree

4 files changed

+122
-81
lines changed

4 files changed

+122
-81
lines changed

roborock/api.py

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
RoborockCommand.GET_MAP_V1,
5252
]
5353

54+
5455
def md5hex(message: str) -> str:
5556
md5 = hashlib.md5()
5657
md5.update(message.encode())
@@ -107,7 +108,7 @@ def _decode_msg(self, msg: bytes, local_key: str) -> dict[str, Any]:
107108
if self._prefixed:
108109
msg = msg[4:]
109110
if msg[0:3] != "1.0".encode():
110-
raise RoborockException("Unknown protocol version")
111+
raise RoborockException(f"Unknown protocol version {msg[0:3]}")
111112
if len(msg) == 17:
112113
[version, _seq, _random, timestamp, protocol] = struct.unpack(
113114
"!3sIIIH", msg[0:17]
@@ -281,53 +282,75 @@ async def get_status(self, device_id: str) -> Status:
281282
return Status(status)
282283

283284
async def get_dnd_timer(self, device_id: str) -> DNDTimer:
284-
dnd_timer = await self.send_command(device_id, RoborockCommand.GET_DND_TIMER)
285-
if isinstance(dnd_timer, dict):
286-
return DNDTimer(dnd_timer)
285+
try:
286+
dnd_timer = await self.send_command(device_id, RoborockCommand.GET_DND_TIMER)
287+
if isinstance(dnd_timer, dict):
288+
return DNDTimer(dnd_timer)
289+
except RoborockTimeout as e:
290+
_LOGGER.error(e)
287291

288292
async def get_clean_summary(self, device_id: str) -> CleanSummary:
289-
clean_summary = await self.send_command(
290-
device_id, RoborockCommand.GET_CLEAN_SUMMARY
291-
)
292-
if isinstance(clean_summary, dict):
293-
return CleanSummary(clean_summary)
294-
elif isinstance(clean_summary, bytes):
295-
return CleanSummary({"clean_time": clean_summary})
293+
try:
294+
clean_summary = await self.send_command(
295+
device_id, RoborockCommand.GET_CLEAN_SUMMARY
296+
)
297+
if isinstance(clean_summary, dict):
298+
return CleanSummary(clean_summary)
299+
elif isinstance(clean_summary, bytes):
300+
return CleanSummary({"clean_time": clean_summary})
301+
except RoborockTimeout as e:
302+
_LOGGER.error(e)
296303

297304
async def get_clean_record(self, device_id: str, record_id: int) -> CleanRecord:
298-
clean_record = await self.send_command(
299-
device_id, RoborockCommand.GET_CLEAN_RECORD, [record_id]
300-
)
301-
if isinstance(clean_record, dict):
302-
return CleanRecord(clean_record)
305+
try:
306+
clean_record = await self.send_command(
307+
device_id, RoborockCommand.GET_CLEAN_RECORD, [record_id]
308+
)
309+
if isinstance(clean_record, dict):
310+
return CleanRecord(clean_record)
311+
except RoborockTimeout as e:
312+
_LOGGER.error(e)
303313

304314
async def get_consumable(self, device_id: str) -> Consumable:
305-
consumable = await self.send_command(device_id, RoborockCommand.GET_CONSUMABLE)
306-
if isinstance(consumable, dict):
307-
return Consumable(consumable)
315+
try:
316+
consumable = await self.send_command(device_id, RoborockCommand.GET_CONSUMABLE)
317+
if isinstance(consumable, dict):
318+
return Consumable(consumable)
319+
except RoborockTimeout as e:
320+
_LOGGER.error(e)
308321

309322
async def get_washing_mode(self, device_id: str) -> RoborockDockWashingModeType:
310-
washing_mode = await self.send_command(device_id, RoborockCommand.GET_WASH_TOWEL_MODE)
311-
return WASH_MODE_MAP.get(washing_mode['wash_mode'])
323+
try:
324+
washing_mode = await self.send_command(device_id, RoborockCommand.GET_WASH_TOWEL_MODE)
325+
return WASH_MODE_MAP.get(washing_mode['wash_mode'])
326+
except RoborockTimeout as e:
327+
_LOGGER.error(e)
312328

313329
async def get_dust_collection_mode(self, device_id: str) -> RoborockDockDustCollectionType:
314-
dust_collection = await self.send_command(device_id, RoborockCommand.GET_DUST_COLLECTION_MODE)
315-
return DUST_COLLECTION_MAP.get(dust_collection['mode'])
330+
try:
331+
dust_collection = await self.send_command(device_id, RoborockCommand.GET_DUST_COLLECTION_MODE)
332+
return DUST_COLLECTION_MAP.get(dust_collection['mode'])
333+
except RoborockTimeout as e:
334+
_LOGGER.error(e)
316335

317336
async def get_mop_wash_mode(self, device_id: str) -> SmartWashParameters:
318-
mop_wash_mode = await self.send_command(device_id, RoborockCommand.GET_SMART_WASH_PARAMS)
319-
if isinstance(mop_wash_mode, dict):
320-
return SmartWashParameters(mop_wash_mode)
337+
try:
338+
mop_wash_mode = await self.send_command(device_id, RoborockCommand.GET_SMART_WASH_PARAMS)
339+
if isinstance(mop_wash_mode, dict):
340+
return SmartWashParameters(mop_wash_mode)
341+
except RoborockTimeout as e:
342+
_LOGGER.error(e)
321343

322344
async def get_dock_summary(self, device_id: str, dock_type: RoborockDockType) -> RoborockDockSummary:
323-
collection_mode = await self.get_dust_collection_mode(device_id)
324-
mop_wash = None
325-
washing_mode = None
326-
if dock_type == RoborockDockType.EMPTY_WASH_FILL_DOCK:
327-
[mop_wash, washing_mode] = await asyncio.gather(
328-
*[self.get_mop_wash_mode(device_id), self.get_washing_mode(device_id)])
345+
try:
346+
commands = [self.get_dust_collection_mode(device_id)]
347+
if dock_type == RoborockDockType.EMPTY_WASH_FILL_DOCK:
348+
commands += [self.get_mop_wash_mode(device_id), self.get_washing_mode(device_id)]
349+
[collection_mode, mop_wash, washing_mode] = (list(await asyncio.gather(*commands)) + [None, None])[:3]
329350

330-
return RoborockDockSummary(collection_mode, washing_mode, mop_wash)
351+
return RoborockDockSummary(collection_mode, washing_mode, mop_wash)
352+
except RoborockTimeout as e:
353+
_LOGGER.error(e)
331354

332355
async def get_prop(self, device_id: str) -> RoborockDeviceProp:
333356
[status, dnd_timer, clean_summary, consumable] = await asyncio.gather(
@@ -338,17 +361,9 @@ async def get_prop(self, device_id: str) -> RoborockDeviceProp:
338361
self.get_consumable(device_id),
339362
]
340363
)
341-
last_clean_record = None
342-
# if clean_summary and clean_summary.records and len(clean_summary.records) > 0:
343-
# last_clean_record = await self.get_clean_record(
344-
# device_id, clean_summary.records[0]
345-
# )
346-
dock_summary = None
347-
if status and status.dock_type != RoborockDockType.NO_DOCK:
348-
dock_summary = await self.get_dock_summary(device_id, status.dock_type)
349364
if any([status, dnd_timer, clean_summary, consumable]):
350365
return RoborockDeviceProp(
351-
status, dnd_timer, clean_summary, consumable, last_clean_record, dock_summary
366+
status, dnd_timer, clean_summary, consumable
352367
)
353368

354369
async def get_multi_maps_list(self, device_id) -> MultiMapsList:

roborock/cloud_api.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from urllib.parse import urlparse
1010

1111
import paho.mqtt.client as mqtt
12+
from roborock.code_mappings import RoborockDockType
1213

1314
from roborock.api import md5hex, md5bin, RoborockClient, SPECIAL_COMMANDS
1415
from roborock.exceptions import (
@@ -21,7 +22,7 @@
2122
)
2223
from .roborock_queue import RoborockQueue
2324
from .typing import (
24-
RoborockCommand,
25+
RoborockCommand, RoborockDeviceProp,
2526
)
2627
from .util import run_in_executor
2728

@@ -205,3 +206,17 @@ async def send_command(
205206
else:
206207
_LOGGER.debug(f"id={request_id} Response from {method}: {response}")
207208
return response
209+
210+
async def get_prop(self, device_id: str) -> RoborockDeviceProp:
211+
device_prop = await super().get_prop(device_id)
212+
last_clean_record = None
213+
if device_prop.clean_summary and device_prop.clean_summary.records and len(device_prop.clean_summary.records) > 0:
214+
last_clean_record = await self.get_clean_record(
215+
device_id, device_prop.clean_summary.records[0]
216+
)
217+
device_prop.last_clean_record = last_clean_record
218+
dock_summary = None
219+
if device_prop.status and device_prop.status.dock_type != RoborockDockType.NO_DOCK:
220+
dock_summary = await self.get_dock_summary(device_id, device_prop.status.dock_type)
221+
device_prop.dock_summary = dock_summary
222+
return device_prop

roborock/local_api.py

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def __init__(self, ip: str, endpoint: str, device_localkey: dict[str, str]):
2929
device_id: RoborockSocketListener(ip, device_id, self.on_message)
3030
for device_id in device_localkey
3131
}
32+
self._mutex = Lock()
3233

3334
async def async_connect(self):
3435
await asyncio.gather(*[
@@ -39,28 +40,25 @@ async def async_connect(self):
3940
async def send_command(
4041
self, device_id: str, method: RoborockCommand, params: list = None
4142
):
42-
secured = True if method in SPECIAL_COMMANDS else False
43-
request_id, timestamp, payload = self._get_payload(method, params, secured)
44-
_LOGGER.debug(f"id={request_id} Requesting method {method} with {params}")
45-
prefix = secured_prefix if method in SPECIAL_COMMANDS else get_prefix
46-
protocol = 4
47-
msg = self._encode_msg(device_id, protocol, timestamp, payload, prefix)
48-
_LOGGER.debug(f"Requesting with prefix {prefix} and payload {payload}")
49-
# Send the command to the Roborock device
50-
listener = self.device_listener.get(device_id)
51-
await listener.send_message(msg, self.device_localkey.get(device_id))
52-
(response, err) = await self._async_response(request_id, 4)
53-
if err:
54-
raise CommandVacuumError(method, err) from err
55-
_LOGGER.debug(f"id={request_id} Response from {method}: {response}")
56-
return response
43+
async with self._mutex:
44+
secured = True if method in SPECIAL_COMMANDS else False
45+
request_id, timestamp, payload = self._get_payload(method, params, secured)
46+
_LOGGER.debug(f"id={request_id} Requesting method {method} with {params}")
47+
prefix = secured_prefix if method in SPECIAL_COMMANDS else get_prefix
48+
protocol = 4
49+
msg = self._encode_msg(device_id, protocol, timestamp, payload, prefix)
50+
# Send the command to the Roborock device
51+
listener = self.device_listener.get(device_id)
52+
await listener.send_message(msg)
53+
(response, err) = await self._async_response(request_id, 4)
54+
if err:
55+
raise CommandVacuumError(method, err) from err
56+
_LOGGER.debug(f"id={request_id} Response from {method}: {response}")
57+
return response
5758

5859
class RoborockSocket(socket.socket):
5960
_closed = None
6061

61-
def __init__(self, *args, **kwargs):
62-
super().__init__(*args, **kwargs)
63-
6462
@property
6563
def is_closed(self):
6664
return self._closed
@@ -78,34 +76,37 @@ def __init__(self, ip: str, device_id: str, on_message: Callable[[str, bytes], C
7876
self.on_message = on_message
7977
self.timeout = timeout
8078
self.is_connected = False
81-
self._lock = Lock()
79+
self._mutex = Lock()
8280

8381
async def _main_coro(self):
8482
while not self.socket.is_closed:
8583
try:
8684
message = await self.loop.sock_recv(self.socket, 4096)
87-
accepted = await self.on_message(self.device_id, message)
88-
if accepted:
89-
self._lock.release() if self._lock.locked() else None
85+
try:
86+
await self.on_message(self.device_id, message)
87+
except Exception as e:
88+
_LOGGER.exception(e)
9089
except Exception as e:
9190
_LOGGER.exception(e)
92-
self.is_connected = False
93-
await self.connect()
91+
self.socket.close()
9492

9593
async def connect(self):
96-
async with async_timeout.timeout(self.timeout):
97-
await self.loop.sock_connect(self.socket, (self.ip, 58867))
98-
self.is_connected = True
99-
self.loop.create_task(self._main_coro())
100-
101-
async def send_message(self, data: bytes, local_key: str):
94+
async with self._mutex:
95+
if not self.is_connected or self.socket.is_closed:
96+
async with async_timeout.timeout(self.timeout):
97+
_LOGGER.info(f"Connecting to {self.ip}")
98+
await self.loop.sock_connect(self.socket, (self.ip, 58867))
99+
self.is_connected = True
100+
self.loop.create_task(self._main_coro())
101+
102+
async def send_message(self, data: bytes):
102103
response = {}
104+
await self.connect()
103105
try:
104-
async with async_timeout.timeout(self.timeout):
105-
await self._lock.acquire()
106-
await self.loop.sock_sendall(self.socket, data)
106+
async with self._mutex:
107+
async with async_timeout.timeout(self.timeout):
108+
await self.loop.sock_sendall(self.socket, data)
107109
except (asyncio.TimeoutError, asyncio.CancelledError):
108-
self._lock.release() if self._lock.locked() else None
109110
raise RoborockTimeout(
110111
f"Timeout after {self.timeout} seconds waiting for response"
111112
) from None

roborock/offline/offline.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
import asyncio
2+
import logging
23

34
from roborock.local_api import RoborockLocalClient
45
from roborock.typing import RoborockCommand
56

6-
local_ip = "<device_ip>"
7-
local_key = "<device_localkey>"
7+
local_ip = "192.168.1.232"
8+
local_key = "nXTBj42ej5WxQopO"
89
endpoint = "abc"
910

1011

1112
async def main():
13+
logging_config = {
14+
"level": logging.DEBUG
15+
}
16+
logging.basicConfig(**logging_config)
1217
device_id = "1r9W0cAmDZ2COuVekgRhKA"
1318
client = RoborockLocalClient(local_ip, endpoint, {
1419
"1r9W0cAmDZ2COuVekgRhKA": local_key
1520
})
21+
# print(client._decode_msg(bytes.fromhex('000000c7312e300000cf6a0000bff56424b659000400b0772b8ddc5645f5751d58d4a9805c37cc7c1989a5c76ca69130ce6672cbf27d231d9fb0a85ddb8eb2a999cc1f630f18f05f5425d6a291319c88da0851a3811f2646b0b5a34bd26a4f83c118d48340302383c9c9ee3b0063f3ad5a773f606ffd0358fdb573afa774859bfc993eea8b2695f3a5af5938a2af03b6be6b559f4bc9601ca65617838efdbc8ac5e30b8e7bcc06218f3cc5eb279263c44de63b506bb7953baf9288963ee987270d01b1b9481df058b2f161'), local_key))
1622
await client.async_connect()
17-
response = await client.send_command(device_id, RoborockCommand.GET_STATUS)
18-
print(response)
23+
dnd_timer = await client.get_clean_record(device_id, 1680313530)
24+
print(dnd_timer)
25+
# clean_summary = await client.get_clean_summary(device_id)
26+
# print(clean_summary)
27+
# consumable = await client.get_consumable(device_id)
28+
# print(consumable)
1929

2030

2131
if __name__ == "__main__":

0 commit comments

Comments
 (0)