Describe the bug
On the Roborock Saros 20 (roborock.vacuum.a288, an L01-protocol device), all V1 traits work — status, get_maps, and rooms all refresh correctly — but the map image can never be decoded. The robot does answer GET_MAP_V1, but the returned map frame is in the new L01/GCM-framed format that the current V1 map decoder cannot handle, so no image.* entity is ever produced (this is what surfaces in Home Assistant as a permanently missing Roborock map for Saros 20).
This looks related to, but distinct from, #532 (that report is a Saros Z70 hitting decrypt_cbc → "Padding is incorrect"; here the device negotiates L01 and the payload is not classic CBC at all).
Roborock Model
Roborock Saros 20 — roborock.vacuum.a288
Python-roborock Version
Reproduced on 2.19.0 and current 6.2.1 (also 7.2.3). Behavior is the same across all three.
What works
Using create_device_manager(...) (6.x), the device connects locally (is_local_connected == True) over its local server (TCP port 58867) speaking protocol L01:
status → OK
maps / get_maps → OK (returns the correct single map "First Floor" with the full, correctly-named room list)
rooms → OK
Only map_content.refresh() (GET_MAP_V1) fails to yield an image.
Observed map-fetch behavior
- Local (L01) channel:
GET_MAP_V1 returns only an empty proto=3 ack — no protocol=301 map frame is ever sent locally.
- Cloud MQTT channel:
GET_MAP_V1 returns exactly one protocol=301 frame per request (verified causal: 0 frames arrive when no request is sent; 1 arrives per request). The frame is ~7080 bytes and is high-entropy end-to-end (~7.97/8.0 across the entire payload) — there is no plaintext header region. The session SecurityData.nonce does not decrypt it at any offset (0–63), under AES-CBC or AES-ECB, or with key variants (nonce, md5(nonce), reversed). No gzip/zlib stream is present anywhere in the payload.
So the map response is being delivered, but in an encrypted L01 framing the V1 map decoder doesn't understand.
Secondary bug: uncaught UnicodeDecodeError in the map decoder
While reproducing, the map-response decoder crashes on these frames:
roborock/protocols/v1_protocol.py::create_map_response_decoder → _decode_map_response calls endpoint.decode() on the leading bytes of the payload. For these L01 map frames the "endpoint" region is binary, raising UnicodeDecodeError. That exception is not caught in roborock/devices/rpc/v1_channel.py::find_response (which only catches RoborockException), so the response callback dies, the future never resolves, and the caller just times out (~10s) with no useful error. Even before full L01 map support lands, catching/handling this would turn a silent timeout into a clear failure.
Expected Behavior
GET_MAP_V1 on a288 / L01 devices should decode the returned map frame (or at least surface a specific, non-timeout error) so the image.* map entity can be produced.
Offer
I have captured raw 301 map frames plus the exact per-session SecurityData (nonce + endpoint) for this device and am happy to share samples / test candidate decoders to help wire up L01 map decoding.
Describe the bug
On the Roborock Saros 20 (
roborock.vacuum.a288, an L01-protocol device), all V1 traits work —status,get_maps, androomsall refresh correctly — but the map image can never be decoded. The robot does answerGET_MAP_V1, but the returned map frame is in the new L01/GCM-framed format that the current V1 map decoder cannot handle, so noimage.*entity is ever produced (this is what surfaces in Home Assistant as a permanently missing Roborock map for Saros 20).This looks related to, but distinct from, #532 (that report is a Saros Z70 hitting
decrypt_cbc→ "Padding is incorrect"; here the device negotiates L01 and the payload is not classic CBC at all).Roborock Model
Roborock Saros 20 —
roborock.vacuum.a288Python-roborock Version
Reproduced on
2.19.0and current6.2.1(also7.2.3). Behavior is the same across all three.What works
Using
create_device_manager(...)(6.x), the device connects locally (is_local_connected == True) over its local server (TCP port58867) speaking protocol L01:status→ OKmaps/get_maps→ OK (returns the correct single map "First Floor" with the full, correctly-named room list)rooms→ OKOnly
map_content.refresh()(GET_MAP_V1) fails to yield an image.Observed map-fetch behavior
GET_MAP_V1returns only an emptyproto=3ack — noprotocol=301map frame is ever sent locally.GET_MAP_V1returns exactly oneprotocol=301frame per request (verified causal: 0 frames arrive when no request is sent; 1 arrives per request). The frame is ~7080 bytes and is high-entropy end-to-end (~7.97/8.0 across the entire payload) — there is no plaintext header region. The sessionSecurityData.noncedoes not decrypt it at any offset (0–63), under AES-CBC or AES-ECB, or with key variants (nonce,md5(nonce), reversed). No gzip/zlib stream is present anywhere in the payload.So the map response is being delivered, but in an encrypted L01 framing the V1 map decoder doesn't understand.
Secondary bug: uncaught
UnicodeDecodeErrorin the map decoderWhile reproducing, the map-response decoder crashes on these frames:
roborock/protocols/v1_protocol.py::create_map_response_decoder→_decode_map_responsecallsendpoint.decode()on the leading bytes of the payload. For these L01 map frames the "endpoint" region is binary, raisingUnicodeDecodeError. That exception is not caught inroborock/devices/rpc/v1_channel.py::find_response(which only catchesRoborockException), so the response callback dies, the future never resolves, and the caller just times out (~10s) with no useful error. Even before full L01 map support lands, catching/handling this would turn a silent timeout into a clear failure.Expected Behavior
GET_MAP_V1on a288 / L01 devices should decode the returned map frame (or at least surface a specific, non-timeout error) so theimage.*map entity can be produced.Offer
I have captured raw 301 map frames plus the exact per-session
SecurityData(nonce + endpoint) for this device and am happy to share samples / test candidate decoders to help wire up L01 map decoding.