Skip to content

Commit acca342

Browse files
committed
🦎 q7: fetch map payload by map_id from map list
1 parent 0eb5720 commit acca342

File tree

3 files changed

+108
-6
lines changed

3 files changed

+108
-6
lines changed

roborock/devices/traits/b01/q7/map_content.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@
33
from __future__ import annotations
44

55
from dataclasses import dataclass
6+
from typing import Any
67

7-
from roborock.devices.rpc.b01_q7_channel import send_map_command
8-
from roborock.devices.transport.mqtt_channel import MqttChannel
8+
from roborock.devices.rpc.b01_q7_channel import send_decoded_command, send_map_command
99
from roborock.devices.traits import Trait
1010
from roborock.devices.traits.v1.map_content import MapContent
11-
from roborock.map.b01_map_parser import decode_b01_map_payload, parse_scmap_payload, render_map_png
11+
from roborock.devices.transport.mqtt_channel import MqttChannel
12+
from roborock.exceptions import RoborockException
13+
from roborock.map.b01_map_parser import (
14+
decode_b01_map_payload,
15+
parse_scmap_payload,
16+
render_map_png,
17+
)
1218
from roborock.protocols.b01_q7_protocol import Q7RequestMessage
1319
from roborock.roborock_typing import RoborockB01Q7Methods
1420

@@ -18,6 +24,23 @@ class B01MapContent(MapContent):
1824
"""B01 map content wrapper."""
1925

2026

27+
def _extract_current_map_id(map_list_response: dict[str, Any] | None) -> int | None:
28+
if not isinstance(map_list_response, dict):
29+
return None
30+
map_list = map_list_response.get("map_list")
31+
if not isinstance(map_list, list) or not map_list:
32+
return None
33+
34+
for entry in map_list:
35+
if isinstance(entry, dict) and entry.get("cur") and isinstance(entry.get("id"), int):
36+
return entry["id"]
37+
38+
first = map_list[0]
39+
if isinstance(first, dict) and isinstance(first.get("id"), int):
40+
return first["id"]
41+
return None
42+
43+
2144
class Q7MapContentTrait(B01MapContent, Trait):
2245
"""Fetch and parse map content from B01/Q7 devices."""
2346

@@ -29,9 +52,21 @@ def __init__(self, channel: MqttChannel, *, local_key: str, serial: str, model:
2952
self._model = model
3053

3154
async def refresh(self) -> B01MapContent:
55+
map_list_response = await send_decoded_command(
56+
self._channel,
57+
Q7RequestMessage(dps=10000, command=RoborockB01Q7Methods.GET_MAP_LIST, params={}),
58+
)
59+
map_id = _extract_current_map_id(map_list_response)
60+
if map_id is None:
61+
raise RoborockException(f"Unable to determine map_id from map list response: {map_list_response!r}")
62+
3263
raw_payload = await send_map_command(
3364
self._channel,
34-
Q7RequestMessage(dps=10000, command=RoborockB01Q7Methods.UPLOAD_BY_MAPTYPE, params={"maptype": 301}),
65+
Q7RequestMessage(
66+
dps=10000,
67+
command=RoborockB01Q7Methods.UPLOAD_BY_MAPID,
68+
params={"map_id": map_id},
69+
),
3570
)
3671
inflated = decode_b01_map_payload(
3772
raw_payload,

roborock/map/b01_map_parser.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import base64
66
import hashlib
77
import io
8-
import math
98
import zlib
109
from dataclasses import dataclass
1110

tests/devices/traits/b01/q7/test_init.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
from roborock.devices.rpc.b01_q7_channel import send_decoded_command
1717
from roborock.devices.traits.b01.q7 import Q7PropertiesApi
1818
from roborock.exceptions import RoborockException
19+
from roborock.map.b01_map_parser import B01MapData
1920
from roborock.protocols.b01_q7_protocol import B01_VERSION, Q7RequestMessage
20-
from roborock.roborock_message import RoborockB01Props, RoborockMessageProtocol
21+
from roborock.roborock_message import RoborockB01Props, RoborockMessage, RoborockMessageProtocol
2122
from tests.fixtures.channel_fixtures import FakeChannel
2223

2324
from . import B01MessageBuilder
@@ -257,3 +258,70 @@ async def test_q7_api_find_me(q7_api: Q7PropertiesApi, fake_channel: FakeChannel
257258
payload_data = json.loads(unpad(message.payload, AES.block_size))
258259
assert payload_data["dps"]["10000"]["method"] == "service.find_device"
259260
assert payload_data["dps"]["10000"]["params"] == {}
261+
262+
263+
async def test_q7_api_clean_segments(
264+
q7_api: Q7PropertiesApi, fake_channel: FakeChannel, message_builder: B01MessageBuilder
265+
):
266+
"""Test room/segment cleaning helper for Q7."""
267+
fake_channel.response_queue.append(message_builder.build({"result": "ok"}))
268+
await q7_api.clean_segments([10, 11])
269+
270+
assert len(fake_channel.published_messages) == 1
271+
message = fake_channel.published_messages[0]
272+
payload_data = json.loads(unpad(message.payload, AES.block_size))
273+
assert payload_data["dps"]["10000"]["method"] == "service.set_room_clean"
274+
assert payload_data["dps"]["10000"]["params"] == {
275+
"clean_type": CleanTaskTypeMapping.ROOM.code,
276+
"ctrl_value": SCDeviceCleanParam.START.code,
277+
"room_ids": [10, 11],
278+
}
279+
280+
281+
async def test_q7_map_content_refresh_from_map_response(
282+
q7_api: Q7PropertiesApi,
283+
fake_channel: FakeChannel,
284+
message_builder: B01MessageBuilder,
285+
monkeypatch: pytest.MonkeyPatch,
286+
):
287+
"""Test Q7 map content refresh wiring through map list + MAP_RESPONSE payload path."""
288+
289+
fake_channel.response_queue.append(message_builder.build({"map_list": [{"id": 1772093512, "cur": True}]}))
290+
fake_channel.response_queue.append(
291+
RoborockMessage(
292+
protocol=RoborockMessageProtocol.MAP_RESPONSE,
293+
payload=b"raw-map-payload",
294+
version=b"B01",
295+
seq=message_builder.seq + 1,
296+
)
297+
)
298+
299+
monkeypatch.setattr(
300+
"roborock.devices.traits.b01.q7.map_content.decode_b01_map_payload",
301+
lambda raw_payload, **kwargs: b"inflated-payload",
302+
)
303+
monkeypatch.setattr(
304+
"roborock.devices.traits.b01.q7.map_content.parse_scmap_payload",
305+
lambda payload: B01MapData(size_x=1, size_y=1, map_data=b"\x01"),
306+
)
307+
monkeypatch.setattr(
308+
"roborock.devices.traits.b01.q7.map_content.render_map_png",
309+
lambda parsed: b"\x89PNG-test",
310+
)
311+
312+
result = await q7_api.map_content.refresh()
313+
314+
assert result.image_content == b"\x89PNG-test"
315+
assert result.raw_api_response == b"raw-map-payload"
316+
317+
assert len(fake_channel.published_messages) == 2
318+
319+
first = fake_channel.published_messages[0]
320+
first_payload = json.loads(unpad(first.payload, AES.block_size))
321+
assert first_payload["dps"]["10000"]["method"] == "service.get_map_list"
322+
assert first_payload["dps"]["10000"]["params"] == {}
323+
324+
second = fake_channel.published_messages[1]
325+
second_payload = json.loads(unpad(second.payload, AES.block_size))
326+
assert second_payload["dps"]["10000"]["method"] == "service.upload_by_mapid"
327+
assert second_payload["dps"]["10000"]["params"] == {"map_id": 1772093512}

0 commit comments

Comments
 (0)