Skip to content

B01/Q7: fragmented live map responses (protocol 300 + 301) are not reassembled — map image freezes #1043

Open

Description

@jhigueras

Device / version

  • Roborock Q7 TF+ (roborock.vacuum.sc05), firmware 03.01.80 (unchanged since install)
  • python-roborock 7.12.0 (Home Assistant 2026.11.1); code path identical in 7.22.0

Problem

While cleaning, the Q7 streams live map frames over MQTT. When a frame is large it is
split into multiple messages sharing the same random/timestamp: non-final parts use
protocol 300, the final part uses 301 (MAP_RESPONSE).

The B01/Q7 map channel does not reassemble these:

  • _matches_map_response (roborock/devices/rpc/b01_q7_channel.py) only matches
    MAP_RESPONSE (301), so the leading 300 part is ignored.
  • subscribe_map_pushes / send_map_command decode each message standalone via
    decode_map_payload, which requires the base64-decoded length to be a multiple of 16.
    The trailing 301 fragment alone is not, so it raises:
    Unexpected encrypted B01 map payload length: 11188 (not a multiple of AES block size).

Result: live map updates stop; only the polled saved-map snapshot (~60 s) refreshes the
image, so the map appears frozen / laggy in Home Assistant.

Evidence (captured logs)

Two consecutive frames, same random=2716216127, timestamp=1794578237:

part protocol seq decoded bytes mod 16
1 300 1 48924 12
2 301 2 11188 4
sum 60112 = 16×3757 0

The concatenated payload is a valid AES-block multiple; the lone 301 fragment (11188 ≡ 4
mod 16) is exactly the logged error.

Relation to #1004

#1004 (merged) fixed this for V1: it added MAP_RESPONSE_PART = 300 and per-(random, timestamp) reassembly in v1_protocol.py. The B01/Q7 channel was not updated and still
decodes each message independently. This is the B01/Q7 equivalent of that bug.

Trigger (inference, not measured)

The full map is ~80152 base64 chars; fragment 1 is 65232 chars (~64 KiB). This suggests a
Roborock device/cloud per-message size cap near 64 KiB (MQTT itself allows far more). The
map payload likely grew past this cap as the cleaning path accumulated, which is why it
worked on prior days and broke without any firmware/HA change. The exact cap and the
"grew past it" timing are inferred; the reassembly gap above is proven from the logs.

Suggested fix

Port the V1 reassembly to the B01/Q7 map channel: accept protocol 300 as a map part,
buffer parts per (random, timestamp), and decode once the final 301 part and all
preceding seq numbers are present (handle out-of-order arrival, as #1004 does).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions