Skip to content

Commit b954c9c

Browse files
chore: fix cloud_api.py
1 parent 9760605 commit b954c9c

File tree

4 files changed

+28
-39
lines changed

4 files changed

+28
-39
lines changed

roborock/api.py

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -92,22 +92,21 @@ async def request(
9292

9393
class RoborockClient:
9494

95-
def __init__(self, endpoint: str, device_localkey: dict[str, str], prefixed=False) -> None:
95+
def __init__(self, endpoint: str, device_localkey: dict[str, str]) -> None:
9696
self.device_localkey = device_localkey
9797
self._seq = 1
9898
self._random = 4711
9999
self._id_counter = 10000
100100
self._salt = "TXdfu$jyZ#TZHsg4"
101-
self._endpoint = base64.b64encode(md5bin(endpoint)[8:14]).decode()
101+
self._endpoint = endpoint
102102
self._nonce = secrets.token_bytes(16)
103-
self._prefixed = prefixed
104103
self._waiting_queue: dict[int, RoborockQueue] = {}
105104
self._status_listeners: list[Callable[[str, str], None]] = []
106105

107106
def _decode_msg(self, msg: bytes, local_key: str) -> dict[str, Any]:
108-
if self._prefixed:
107+
if msg[4:7] == "1.0".encode():
109108
msg = msg[4:]
110-
if msg[0:3] != "1.0".encode():
109+
elif msg[0:3] != "1.0".encode():
111110
raise RoborockException(f"Unknown protocol version {msg[0:3]}")
112111
if len(msg) == 17:
113112
[version, _seq, _random, timestamp, protocol] = struct.unpack(
@@ -118,31 +117,27 @@ def _decode_msg(self, msg: bytes, local_key: str) -> dict[str, Any]:
118117
"timestamp": timestamp,
119118
"protocol": protocol,
120119
}
121-
# crc32 = binascii.crc32(msg[0: len(msg) - 4])
122120
[version, _seq, _random, timestamp, protocol, payload_len] = struct.unpack(
123121
"!3sIIIHH", msg[0:19]
124122
)
125-
if not payload_len:
126-
return {
127-
"version": version,
128-
"timestamp": timestamp,
129-
"protocol": protocol,
130-
}
131-
[payload, expected_crc32] = struct.unpack_from(f"!{payload_len}sI", msg, 19)
132-
# if crc32 != expected_crc32:
133-
# raise RoborockException(f"Wrong CRC32 {crc32}, expected {expected_crc32}")
123+
extra_len = len(msg) - 23 - payload_len
124+
[payload, expected_crc32, extra] = struct.unpack_from(f"!{payload_len}sI{extra_len}s", msg, 19)
125+
if not extra_len:
126+
crc32 = binascii.crc32(msg[0: 19 + payload_len])
127+
if crc32 != expected_crc32:
128+
raise RoborockException(f"Wrong CRC32 {crc32}, expected {expected_crc32}")
134129

135130
aes_key = md5bin(encode_timestamp(timestamp) + local_key + self._salt)
136131
decipher = AES.new(aes_key, AES.MODE_ECB)
137-
decrypted_payload = unpad(decipher.decrypt(payload), AES.block_size)
132+
decrypted_payload = unpad(decipher.decrypt(payload), AES.block_size) if payload else extra
138133
return {
139134
"version": version,
140135
"timestamp": timestamp,
141136
"protocol": protocol,
142-
"payload": decrypted_payload,
137+
"payload": decrypted_payload
143138
}
144139

145-
def _encode_msg(self, device_id, protocol, timestamp, payload, prefix='') -> bytes:
140+
def _encode_msg(self, device_id, protocol, timestamp, payload, prefix=None) -> bytes:
146141
local_key = self.device_localkey[device_id]
147142
aes_key = md5bin(encode_timestamp(timestamp) + local_key + self._salt)
148143
cipher = AES.new(aes_key, AES.MODE_ECB)
@@ -157,13 +152,13 @@ def _encode_msg(self, device_id, protocol, timestamp, payload, prefix='') -> byt
157152
encrypted_len,
158153
encrypted
159154
]
160-
if self._prefixed:
155+
if prefix:
161156
values = [prefix] + values
162157
msg = struct.pack(
163-
f"!{'I' if self._prefixed else ''}3sIIIHH{encrypted_len}s",
158+
f"!{'I' if prefix else ''}3sIIIHH{encrypted_len}s",
164159
*values
165160
)
166-
crc32 = binascii.crc32(msg[4:] if self._prefixed else msg)
161+
crc32 = binascii.crc32(msg[4:] if prefix else msg)
167162
msg += struct.pack("!I", crc32)
168163
return msg
169164

@@ -258,7 +253,7 @@ def _get_payload(
258253
if secured:
259254
inner["security"] = {
260255
"endpoint": self._endpoint,
261-
"nonce": "39344139463753454b4851444f4a4442",
256+
"nonce": self._nonce.hex().upper(),
262257
}
263258
payload = bytes(
264259
json.dumps(

roborock/cloud_api.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ class RoborockMqttClient(RoborockClient, mqtt.Client):
3636

3737
def __init__(self, user_data: UserData, device_localkey: dict[str, str]) -> None:
3838
rriot = user_data.rriot
39-
self._mqtt_user = rriot.user
40-
RoborockClient.__init__(self, rriot.endpoint, device_localkey)
39+
endpoint = base64.b64encode(md5bin(rriot.endpoint)[8:14]).decode()
40+
RoborockClient.__init__(self, endpoint, device_localkey)
4141
mqtt.Client.__init__(self, protocol=mqtt.MQTTv5)
42+
self._mqtt_user = rriot.user
4243
self._hashed_user = md5hex(self._mqtt_user + ":" + rriot.endpoint)[2:10]
4344
url = urlparse(rriot.reference.mqtt)
4445
self._mqtt_host = url.hostname
@@ -94,7 +95,7 @@ async def on_message(self, _client, _, msg, __=None) -> None:
9495
async with self._mutex:
9596
self._last_device_msg_in = mqtt.time_func()
9697
device_id = msg.topic.split("/").pop()
97-
super().on_message(device_id, msg)
98+
await super().on_message(device_id, msg.payload)
9899

99100
@run_in_executor()
100101
async def on_disconnect(self, _client: mqtt.Client, _, rc, __=None) -> None:

roborock/local_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323

2424
class RoborockLocalClient(RoborockClient):
2525

26-
def __init__(self, ip: str, endpoint: str, device_localkey: dict[str, str]):
27-
super().__init__(endpoint, device_localkey, True)
26+
def __init__(self, ip: str, device_localkey: dict[str, str]):
27+
super().__init__("abc", device_localkey)
2828
self.device_listener: dict[str, RoborockSocketListener] = {
2929
device_id: RoborockSocketListener(ip, device_id, self.on_message)
3030
for device_id in device_localkey

roborock/offline/offline.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
import logging
33

44
from roborock.local_api import RoborockLocalClient
5-
from roborock.typing import RoborockCommand
65

7-
local_ip = "192.168.1.232"
8-
local_key = "nXTBj42ej5WxQopO"
9-
endpoint = "abc"
6+
local_ip = "<local_ip>"
7+
local_key = "<local_key>"
108

119

1210
async def main():
@@ -15,17 +13,12 @@ async def main():
1513
}
1614
logging.basicConfig(**logging_config)
1715
device_id = "1r9W0cAmDZ2COuVekgRhKA"
18-
client = RoborockLocalClient(local_ip, endpoint, {
16+
client = RoborockLocalClient(local_ip, {
1917
"1r9W0cAmDZ2COuVekgRhKA": local_key
2018
})
21-
# print(client._decode_msg(bytes.fromhex('000000c7312e300000cf6a0000bff56424b659000400b0772b8ddc5645f5751d58d4a9805c37cc7c1989a5c76ca69130ce6672cbf27d231d9fb0a85ddb8eb2a999cc1f630f18f05f5425d6a291319c88da0851a3811f2646b0b5a34bd26a4f83c118d48340302383c9c9ee3b0063f3ad5a773f606ffd0358fdb573afa774859bfc993eea8b2695f3a5af5938a2af03b6be6b559f4bc9601ca65617838efdbc8ac5e30b8e7bcc06218f3cc5eb279263c44de63b506bb7953baf9288963ee987270d01b1b9481df058b2f161'), local_key))
2219
await client.async_connect()
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)
20+
props = await client.get_prop(device_id)
21+
print(props)
2922

3023

3124
if __name__ == "__main__":

0 commit comments

Comments
 (0)