feat: add CHANNEL_DATA_RECV (RESP_CODE 27) packet type and handler#88
Open
mwolter805 wants to merge 1 commit into
Open
feat: add CHANNEL_DATA_RECV (RESP_CODE 27) packet type and handler#88mwolter805 wants to merge 1 commit into
mwolter805 wants to merge 1 commit into
Conversation
Why: companion-radio firmware emits RESP_CODE_CHANNEL_DATA_RECV (27) for group-channel binary data (PAYLOAD_TYPE_GRP_DATA), shipped in companion-v1.15.0. The SDK had no PacketType value 27, no EventType, and no reader handler, so these frames hit the unknown-packet-type fallthrough and the payload was silently dropped. This adds PacketType.CHANNEL_DATA_RECV = 27 (the previously-skipped enum slot), EventType.CHANNEL_DATA_RECV, and a reader handler. The fixed 9-byte header (snr, reserved, channel_idx, path_len with the sentinel/hash-mode encoding) reuses CHANNEL_MSG_RECV_V3's framing; the typed tail decodes data_type (uint16 little-endian, widened from uint8 in firmware), data_len, and payload (hex string). An up-front length gate mirrors the defensive-read pattern used by the other handlers. data_type is exposed as an int (matching the txt_type convention) and payload as a hex string (matching RAW_DATA's convention for binary data of unknown encoding); attributes surface channel_idx and data_type so subscribers can filter without unpacking the payload. Tests: tests/unit/test_protocol_surface_gaps.py — 5 new tests (enum slot present, direct-path frame, route-flood path_len bit-split, under-minimum frame dropped, widened data_type round-trip). Full unit suite: 146 passed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds SDK support for the
CHANNEL_DATA_RECVpush frame (RESP_CODE_CHANNEL_DATA_RECV = 27), which the companion-radio firmware emits for group-channel binary data (PAYLOAD_TYPE_GRP_DATA— e.g. LPP-encoded telemetry or other binary blobs delivered over a shared channel). Before this change the SDK had noPacketTypevalue for 27 (the enum jumped fromALLOWED_REPEAT_FREQ = 26straight toDEFAULT_FLOOD_SCOPE = 28), noEventType, and no reader handler, so these frames hit the unknown-packet-type fallthrough inhandle_rxand the payload was silently dropped.What changed
packets.py: addPacketType.CHANNEL_DATA_RECV = 27in the previously-skipped enum slot.events.py: addEventType.CHANNEL_DATA_RECV = "channel_data".reader.py: add a handler that decodes the fixed 9-byte header (including the code byte) plus the variable payload, then dispatches anEventType.CHANNEL_DATA_RECVevent.tests/unit/test_protocol_surface_gaps.py: 5 new tests.Wire format
The firmware emits the frame from
MyMesh::onChannelDataRecv():RESP_CODE_CHANNEL_DATA_RECV(27)channel_idxpath_len(route-flood) or0xFF(direct)data_type(uint16, little-endian)data_lendata_lenbytes)The first six post-code bytes (SNR, reserved,
channel_idx,path_lenwith its sentinel/hash-mode encoding) are byte-for-byte identical in shape toCHANNEL_MSG_RECV_V3, so the decode of that prefix reuses the existing convention directly. The only genuinely new decode is the typeddata_type/data_len/ payload tail.Firmware history
The feature shipped across three firmware commits, all first contained in release companion-v1.15.0:
9b842786(2026-03-05) —feat: Add support for PAYLOAD_TYPE_GRP_DATA— initial feature.f25d7a88—fix: Align channel data framing— framing alignment.2f687691(2026-03-19) —fix: Widen grp data type— wideneddata_typefromuint8_ttouint16_t, which is whydata_typeis decoded as a 2-byte little-endian field (offsets 6–7) rather than a single byte.Design choices
These default to the SDK's existing conventions rather than introducing new patterns; happy to adjust any of them on review:
data_typeexposed as anint— mirrors thetxt_typeconvention inCHANNEL_MSG_RECV_V3. The raw integer preserves the on-wire value for any consumer that wants to dispatch on it directly; a parsed enum could be layered on later without a breaking change.data_lenexposed as anint— lets consumers validate payload integrity independent oflen(payload) // 2.payloadexposed as a hex string — mirrorsRAW_DATA's convention for binary data of unknown encoding. It is read as exactlydata_lenbytes (the firmware treatsdata_lenas authoritative) rather than "read remainder", so any trailer bytes are not folded into the payload.attributessurfaceschannel_idxanddata_type— mirrors howCHANNEL_MSG_RECV_V3surfaceschannel_idxandtxt_type, so subscribers can filter without unpacking the payload.len(data) < 9→ debug-log and return) — matches the defensive-read pattern used by the other handlers; protects against truncated frames.Tests
tests/unit/test_protocol_surface_gaps.pyadds:test_channel_data_recv_enum_exists—PacketType.CHANNEL_DATA_RECV == 27.test_channel_data_recv_direct_path_frame— realistic direct-path frame (path_len = 0xFF,data_type = 0x0123, 4-byte payload); asserts every decoded field includingpayload == "deadbeef".test_channel_data_recv_route_flood_path_len_bits—path_len = 0x42splits intohash_mode = 1,length = 2.test_channel_data_recv_under_minimum_frame_ignored— 8-byte frame returns early, no dispatch.test_channel_data_recv_widened_data_type—data_type = 0x0201confirms the high byte survives the 2-byte read; empty-payload tail.Full unit suite passes (no regressions).
Notes for the maintainer
subscribe_channel_data(channel_idx, callback)) was deliberately left out — consumers can use the event dispatcher directly. Happy to add it if you'd prefer the helper.v2.3.7and both touchreader.py, so whichever merges second will need a trivial rebase.