diff --git a/meshai/mesh_data_store.py b/meshai/mesh_data_store.py index 4b8ba1e..51436a7 100644 --- a/meshai/mesh_data_store.py +++ b/meshai/mesh_data_store.py @@ -458,12 +458,20 @@ class MeshDataStore: - Deliverability (source overlap) Does NOT rebuild the full node model. """ - # Recount packets per node from all sources + # Recount packets per node from all sources (deduped by packet ID) packet_counts: dict[int, int] = {} packets_by_type: dict[int, dict[str, int]] = {} + seen_packet_ids: set = set() for name, source in self._sources.items(): for pkt in (source.packets if hasattr(source, 'packets') else []): + # Dedup: skip packets already seen from another source + pkt_id = pkt.get("packet_id") or pkt.get("id") + if pkt_id is not None: + if pkt_id in seen_packet_ids: + continue + seen_packet_ids.add(pkt_id) + from_node = pkt.get("from_node") or pkt.get("from_node_id") or pkt.get("from") if from_node is None: continue @@ -478,7 +486,7 @@ class MeshDataStore: packet_counts[from_node] = packet_counts.get(from_node, 0) + 1 - portnum = pkt.get("portnum") or pkt.get("portnum_name") or pkt.get("type") or "UNKNOWN" + portnum = str(pkt.get("portnum") or pkt.get("portnum_name") or pkt.get("type") or "UNKNOWN") if from_node not in packets_by_type: packets_by_type[from_node] = {} packets_by_type[from_node][portnum] = packets_by_type[from_node].get(portnum, 0) + 1 diff --git a/meshai/mesh_reporter.py b/meshai/mesh_reporter.py index 93f48d3..6943d02 100644 --- a/meshai/mesh_reporter.py +++ b/meshai/mesh_reporter.py @@ -18,6 +18,7 @@ logger = logging.getLogger(__name__) # Portnum display names (from Meshtastic protobufs) PORTNUM_DISPLAY = { + # String names (from some APIs) "TEXT_MESSAGE_APP": "Text", "POSITION_APP": "Position", "NODEINFO_APP": "NodeInfo", @@ -35,6 +36,29 @@ PORTNUM_DISPLAY = { "REMOTE_HARDWARE_APP": "RemoteHW", "ATAK_PLUGIN": "ATAK", "ATAK_FORWARDER": "ATAK", + # Numeric port numbers (from other APIs) + "0": "Unknown", + "1": "Text", + "3": "Position", + "4": "NodeInfo", + "5": "Routing", + "6": "Admin", + "7": "Waypoint", + "8": "Audio", + "33": "Reply", + "34": "IP Tunnel", + "64": "Serial", + "65": "Store&Fwd", + "66": "RangeTest", + "67": "Telemetry", + "68": "Sensor", + "69": "PaxCounter", + "70": "Traceroute", + "71": "Neighbors", + "72": "ATAK", + "73": "MapReport", + "256": "Private", + "257": "ATAK Fwd", } def _clean_portnum(portnum) -> str: diff --git a/meshai/router.py b/meshai/router.py index 39058d1..9f11148 100644 --- a/meshai/router.py +++ b/meshai/router.py @@ -113,6 +113,11 @@ RESPONSE STYLE: - ABSOLUTELY NO markdown. No asterisks, no bold, no bullet points with * or -, no numbered lists with 1. 2. 3. Just plain text sentences. - NEVER say "Want me to keep going?" — the message system adds this automatically when needed. If you say it yourself, users see it twice. - When explaining "X/Y gateways" (like 7/7), explain that it means the node is visible to X out of Y data sources (Meshview and MeshMonitor instances that monitor the mesh). It does NOT mean infrastructure routers or regional gateways. +- When reporting packet types, ALWAYS use the name (Position, NodeInfo, Telemetry) not the number. +- Normal position interval: 15-30 minutes (48-96 packets/day). 400+ Position packets in 24h means aggressive position interval, wasting airtime. Tell the user. +- Normal NodeInfo: every 2-3 hours (8-12/day). 50+ is excessive. +- Normal NeighborInfo: every 6 hours (4/day). 20+ is aggressive. +- If a node has high packet volume, explain WHAT the packets are and WHETHER the rate is abnormal compared to normal intervals. QUESTION TYPES: - "How's the mesh?" -> Lead with composite score. Highlight 1-2 biggest issues by name. Summarize each region briefly.