fix: guard relay_switch parsers against short payloads (#369)#492
fix: guard relay_switch parsers against short payloads (#369)#492bluetoothbot wants to merge 2 commits into
Conversation
When a relay switch (1PM/2PM/Garage Door Opener/Plug Mini EU) receives a single-byte response such as b"\x02" the original guard in `_get_basic_info` only filtered b"\x07"/b"\x00", letting the short buffer reach `_parse_common_data`/`_parse_user_data` which index past the end and raise `IndexError: bytearray index out of range`. Filter any response of length <= 1 in `_get_basic_info` and add defensive length checks in `_parse_common_data` (needs >= 17 bytes for the firmware byte) and `_parse_user_data` (needs >= 15 bytes for the power slice). `get_basic_info` returns `None` if either parser cannot interpret the payload. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests.
... and 2 files with indirect coverage changes 🚀 New features to boost your workflow:
|
|
Confirming this on Home Assistant 2026.5.2 with two Switch 1PM relays on a BLE-proxied setup (3× ESPHome ESP32 bluetooth_proxy). On 2026-05-16 the unretrieved task exception from this exact path stacked up and combined with an unrelated ESPHome reconnect timeout to wedge HA Core into a supervisor-watchdog restart cycle — full outage of ~40 minutes until manual diagnosis. |
|
Thanks @andrew-dash for the production confirmation — that's exactly the failure mode this targets. Worth noting: the current PR already covers the user_data.pop() on None case you patched locally (line 168 in get_basic_info: |
What
Filter single-byte / short responses in
_get_basic_infoand add length guards inrelay_switch._parse_common_data/_parse_user_dataso a stray short payload no longer crashesupdate_from_advertisement.Why
Reported in #369: a Switch 1PM receives a single-byte
b"\x02"from_send_command(...). The original guard only rejectedb"\x07"andb"\x00", so the byte reached_parse_common_datawhich indexesraw_data[1],raw_data[2],raw_data[16]and raisedIndexError: bytearray index out of range. The traceback escapes via thecreate_background_task(self.update())call inSwitchbotSequenceDevice.update_from_advertisement, surfacing in Home Assistant as an unretrieved task exception.How
device._get_basic_infonow rejects any response withlen(_data) <= 1, covering all observed error replies (\x00,\x02,\x07, …) and logging the offending hex for diagnosis.relay_switch._parse_common_dataand_parse_user_datashort-circuit toNonewhen the payload is shorter than the minimum slice they read (17 and 15 bytes respectively).get_basic_info(1PM and 2PM paths) returnsNoneif either parser cannot interpret the payload, so callers like_get_current_image_indexget the same "no info" signal they already handle.Testing
\x02,\x07,\x00, empty, and a 3-byte payload — all now returnNoneinstead of raising.test_art_frametests that were inadvertently depending onAsyncMockslipping past the old guard; they now mock_get_current_image_indexexplicitly.1084 passed.Closes #369
🤖 Generated with Claude Code
Quality Report
Changes: 4 files changed, 82 insertions(+), 4 deletions(-)
Code scan: clean
Tests: failed ([Errno 13] Permission denied: 'pytest')
Branch hygiene: clean
Generated by Kōan post-mission quality pipeline