From 97c72a3eb7914bf06e5d74eb5c53c35d897d4a16 Mon Sep 17 00:00:00 2001 From: Patrick Skillen Date: Tue, 2 Jun 2026 12:54:28 +0100 Subject: [PATCH] feat(meshcore): upload rx_log_data TEXT_MSG and PATH as raw Part of meshflow-api#385. Enables API twin-merge of path_hashes onto channel_text observations for message Heard. --- docs/MESHCORE.md | 3 ++- src/meshcore/serializers.py | 6 ++++-- test/meshcore/test_serializers.py | 24 ++++++++++++++++++++---- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/docs/MESHCORE.md b/docs/MESHCORE.md index 721a6a3..3216e9f 100644 --- a/docs/MESHCORE.md +++ b/docs/MESHCORE.md @@ -50,7 +50,8 @@ Traceroute commands remain Meshtastic-only; MC feeders ignore `traceroute` WS me | `advertisement` | Yes (`payload_type: advert`) | Identity only (`public_key`) | | **`rx_log_data`** + `payload_typename: ADVERT` | Yes | **`adv_lat` / `adv_lon` / `adv_name`** (map coordinates in Meshflow UI) | | `contact_message`, `channel_message` | Yes (text) | N/A | -| `rx_log_data` (TEXT_MSG, PATH, …) | No (`MeshCoreSkipUpload`) | N/A | +| `rx_log_data` + `TEXT_MSG` or `PATH` | Yes (`payload_type: raw`) | Path + `pkt_hash` for API twin-merge to channel messages | +| `rx_log_data` (REQ, CONTROL, …) | No (`MeshCoreSkipUpload`) | N/A | Map coordinates in the Meshflow UI require **bot** [meshflow-bot#102](https://github.com/pskillen/meshflow-bot/issues/102) and **API** [meshflow-api#330](https://github.com/pskillen/meshflow-api/issues/330) / [#298](https://github.com/pskillen/meshflow-api/issues/298) deployed on feeders. diff --git a/src/meshcore/serializers.py b/src/meshcore/serializers.py index 9c708d4..cc9aeb4 100644 --- a/src/meshcore/serializers.py +++ b/src/meshcore/serializers.py @@ -11,7 +11,7 @@ logger = logging.getLogger(__name__) -UPLOADABLE_PAYLOAD_TYPES = frozenset({"advert", "channel_text", "contact_text"}) +UPLOADABLE_PAYLOAD_TYPES = frozenset({"advert", "channel_text", "contact_text", "raw"}) def _json_safe(value: Any) -> Any: @@ -140,7 +140,9 @@ def _build_from_envelope(envelope: dict[str, Any]) -> dict[str, Any]: "adv_lat": payload.get("adv_lat"), "adv_lon": payload.get("adv_lon"), } - raise MeshCoreSkipUpload(f"rx_log_data {typename} not uploaded in Phase 1") + if typename in ("TEXT_MSG", "PATH"): + return {**base, "payload_type": "raw"} + raise MeshCoreSkipUpload(f"rx_log_data {typename} not uploaded") raise MeshCoreSkipUpload(f"event_type {event_type!r} not uploaded in Phase 1") diff --git a/test/meshcore/test_serializers.py b/test/meshcore/test_serializers.py index c6170d4..21578ba 100644 --- a/test/meshcore/test_serializers.py +++ b/test/meshcore/test_serializers.py @@ -8,7 +8,7 @@ import pytest from src.data_classes import MeshNode -from src.meshcore.serializers import MeshCorePacketSerializer, MeshCoreSkipUpload +from src.meshcore.serializers import MeshCorePacketSerializer DOCS = Path(__file__).resolve().parents[2] / "docs" / "meshcore_packets" @@ -72,7 +72,7 @@ def test_serialise_contact_message() -> None: assert out["from_pubkey_prefix"] == "e563a2e933ce" -def test_skip_rx_log_text() -> None: +def test_serialise_rx_log_text_msg() -> None: dump = _load("rx_log_data/20260506_205845_837997.json") raw = { "meshcore": True, @@ -80,8 +80,24 @@ def test_skip_rx_log_text() -> None: "payload": dump["payload"], "attributes": {}, } - with pytest.raises(MeshCoreSkipUpload): - MeshCorePacketSerializer().serialise_raw_packet(raw) + out = MeshCorePacketSerializer().serialise_raw_packet(raw) + assert out["payload_type"] == "raw" + assert out["event_type"] == "rx_log_data" + assert out["pkt_hash"] == dump["payload"]["pkt_hash"] + + +def test_serialise_rx_log_path() -> None: + dump = _load("rx_log_data/20260506_211515_351329.json") + raw = { + "meshcore": True, + "type": "rx_log_data", + "payload": dump["payload"], + "attributes": dump.get("attributes", {}), + } + out = MeshCorePacketSerializer().serialise_raw_packet(raw) + assert out["payload_type"] == "raw" + assert out["path_hashes"] == ["f3bcf1"] + assert out["path_hash_size"] == 3 def test_rx_log_data_serialises_bytes_in_raw_payload() -> None: