mirror of
https://github.com/meshcore-dev/meshcore_py.git
synced 2026-07-27 18:38:13 +00:00
feat: add CHANNEL_DATA_RECV packet type and handler
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 commit is contained in:
@@ -14,6 +14,7 @@ class EventType(Enum):
|
||||
SELF_INFO = "self_info"
|
||||
CONTACT_MSG_RECV = "contact_message"
|
||||
CHANNEL_MSG_RECV = "channel_message"
|
||||
CHANNEL_DATA_RECV = "channel_data"
|
||||
CURRENT_TIME = "time_update"
|
||||
NO_MORE_MSGS = "no_more_messages"
|
||||
CONTACT_URI = "contact_uri"
|
||||
|
||||
@@ -105,6 +105,7 @@ class PacketType(Enum):
|
||||
STATS = 24
|
||||
AUTOADD_CONFIG = 25
|
||||
ALLOWED_REPEAT_FREQ = 26
|
||||
CHANNEL_DATA_RECV = 27
|
||||
DEFAULT_FLOOD_SCOPE = 28
|
||||
|
||||
# Push notifications
|
||||
|
||||
@@ -332,6 +332,41 @@ class MessageReader:
|
||||
Event(EventType.CHANNEL_MSG_RECV, res, attributes)
|
||||
)
|
||||
|
||||
elif packet_type_value == PacketType.CHANNEL_DATA_RECV.value:
|
||||
# Group-channel binary data (PAYLOAD_TYPE_GRP_DATA), companion-v1.15.0+.
|
||||
# Fixed 9-byte header (including the code byte) + variable payload:
|
||||
# code(1) + snr(1) + reserved(2) + channel_idx(1)
|
||||
# + path_len(1) + data_type(2) + data_len(1) = 9 bytes
|
||||
# The first six post-code bytes share CHANNEL_MSG_RECV_V3's framing;
|
||||
# data_type is a 16-bit little-endian field (widened from uint8 in
|
||||
# firmware, so the high byte may be non-zero).
|
||||
if len(data) < 9:
|
||||
logger.debug(f"CHANNEL_DATA_RECV frame too short ({len(data)} bytes < 9), skipping parse")
|
||||
return
|
||||
res = {}
|
||||
res["SNR"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) / 4
|
||||
dbuf.read(2) # reserved
|
||||
res["channel_idx"] = dbuf.read(1)[0]
|
||||
plen = dbuf.read(1)[0]
|
||||
if plen == 255: # direct message
|
||||
res["path_hash_mode"] = -1
|
||||
res["path_len"] = plen
|
||||
else:
|
||||
res["path_hash_mode"] = plen >> 6
|
||||
res["path_len"] = plen & 0x3F
|
||||
res["data_type"] = int.from_bytes(dbuf.read(2), byteorder="little")
|
||||
res["data_len"] = dbuf.read(1)[0]
|
||||
res["payload"] = dbuf.read(res["data_len"]).hex()
|
||||
|
||||
attributes = {
|
||||
"channel_idx": res["channel_idx"],
|
||||
"data_type": res["data_type"],
|
||||
}
|
||||
|
||||
await self.dispatcher.dispatch(
|
||||
Event(EventType.CHANNEL_DATA_RECV, res, attributes)
|
||||
)
|
||||
|
||||
elif packet_type_value == PacketType.CURRENT_TIME.value:
|
||||
time_value = int.from_bytes(dbuf.read(4), byteorder="little")
|
||||
result = {"time": time_value}
|
||||
|
||||
Reference in New Issue
Block a user