mirror of
https://github.com/rightup/pyMC_Repeater.git
synced 2026-03-28 17:43:06 +01:00
add disallowed packet types configuration for LetsMesh and implement filtering in storage collector
This commit is contained in:
@@ -144,7 +144,24 @@ letsmesh:
|
||||
enabled: true
|
||||
iata_code: "test" # e.g., "SFO", "LHR", "test"
|
||||
broker_index: 0 # Which LetsMesh broker (0=EU, 1=US West)
|
||||
status_interval: 60
|
||||
status_interval: 60
|
||||
|
||||
# Block specific packet types from being published to LetsMesh
|
||||
# If not specified or empty list, all types are published
|
||||
# Available types: REQ, RESPONSE, TXT_MSG, ACK, ADVERT, GRP_TXT,
|
||||
# GRP_DATA, ANON_REQ, PATH, TRACE, RAW_CUSTOM
|
||||
disallowed_packet_types: []
|
||||
# - REQ # Don't publish requests
|
||||
# - RESPONSE # Don't publish responses
|
||||
# - TXT_MSG # Don't publish text messages
|
||||
# - ACK # Don't publish acknowledgments
|
||||
# - ADVERT # Don't publish advertisements
|
||||
# - GRP_TXT # Don't publish group text messages
|
||||
# - GRP_DATA # Don't publish group data
|
||||
# - ANON_REQ # Don't publish anonymous requests
|
||||
# - PATH # Don't publish path packets
|
||||
# - TRACE # Don't publish trace packets
|
||||
# - RAW_CUSTOM # Don't publish custom raw packets
|
||||
|
||||
logging:
|
||||
# Log level: DEBUG, INFO, WARNING, ERROR
|
||||
|
||||
@@ -29,13 +29,22 @@ def get_node_info(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
|
||||
letsmesh_config = config.get("letsmesh", {})
|
||||
|
||||
from pymc_core.protocol.utils import PAYLOAD_TYPES
|
||||
|
||||
disallowed_types = letsmesh_config.get("disallowed_packet_types", [])
|
||||
type_name_map = {name: code for code, name in PAYLOAD_TYPES.items()}
|
||||
|
||||
disallowed_hex = [type_name_map.get(name.upper(), None) for name in disallowed_types]
|
||||
disallowed_hex = [val for val in disallowed_hex if val is not None] # Filter out invalid names
|
||||
|
||||
return {
|
||||
"node_name": node_name,
|
||||
"radio_config": radio_config_str,
|
||||
"iata_code": letsmesh_config.get("iata_code", "TEST"),
|
||||
"broker_index": letsmesh_config.get("broker_index", 0),
|
||||
"status_interval": letsmesh_config.get("status_interval", 60),
|
||||
"model": letsmesh_config.get("model", "PyMC-Repeater")
|
||||
"model": letsmesh_config.get("model", "PyMC-Repeater"),
|
||||
"disallowed_packet_types": disallowed_hex
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import base64
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
from datetime import datetime, timedelta, UTC
|
||||
from dataclasses import dataclass, asdict
|
||||
from nacl.signing import SigningKey
|
||||
from typing import Callable, Optional
|
||||
from .. import __version__
|
||||
@@ -301,3 +302,80 @@ class MeshCoreToMqttJwtPusher:
|
||||
result = self.client.publish(topic, message, retain=retain)
|
||||
logging.debug(f"Published to {topic}: {message}")
|
||||
return result
|
||||
|
||||
|
||||
# ====================================================================
|
||||
# LetsMesh Packet Data Class
|
||||
# ====================================================================
|
||||
|
||||
@dataclass
|
||||
class LetsMeshPacket:
|
||||
"""
|
||||
Data class for LetsMesh packet format.
|
||||
Converts internal packet_record format to LetsMesh publish format.
|
||||
"""
|
||||
origin: str
|
||||
origin_id: str
|
||||
timestamp: str
|
||||
type: str
|
||||
direction: str
|
||||
time: str
|
||||
date: str
|
||||
len: str
|
||||
packet_type: str
|
||||
route: str
|
||||
payload_len: str
|
||||
raw: str
|
||||
SNR: str
|
||||
RSSI: str
|
||||
score: str
|
||||
duration: str
|
||||
hash: str
|
||||
|
||||
@classmethod
|
||||
def from_packet_record(cls, packet_record: dict, origin: str, origin_id: str) -> Optional['LetsMeshPacket']:
|
||||
"""
|
||||
Create LetsMeshPacket from internal packet_record format.
|
||||
|
||||
Args:
|
||||
packet_record: Internal packet record dictionary
|
||||
origin: Node name
|
||||
origin_id: Public key of the node
|
||||
|
||||
Returns:
|
||||
LetsMeshPacket instance or None if raw_packet is missing
|
||||
"""
|
||||
if "raw_packet" not in packet_record or not packet_record["raw_packet"]:
|
||||
return None
|
||||
|
||||
# Extract timestamp and format date/time
|
||||
timestamp = packet_record.get("timestamp", 0)
|
||||
dt = datetime.fromtimestamp(timestamp)
|
||||
|
||||
# Format route type (1=Flood->F, 2=Direct->D, etc)
|
||||
route_map = {1: "F", 2: "D"}
|
||||
route = route_map.get(packet_record.get("route", 0), str(packet_record.get("route", 0)))
|
||||
|
||||
return cls(
|
||||
origin=origin,
|
||||
origin_id=origin_id,
|
||||
timestamp=dt.isoformat(),
|
||||
type="PACKET",
|
||||
direction="rx",
|
||||
time=dt.strftime("%H:%M:%S"),
|
||||
date=dt.strftime("%-d/%-m/%Y"),
|
||||
len=str(len(packet_record["raw_packet"]) // 2),
|
||||
packet_type=str(packet_record.get("type", 0)),
|
||||
route=route,
|
||||
payload_len=str(packet_record.get("payload_length", 0)),
|
||||
raw=packet_record["raw_packet"],
|
||||
SNR=str(packet_record.get("snr", 0)),
|
||||
RSSI=str(packet_record.get("rssi", 0)),
|
||||
score=str(int(packet_record.get("score", 0) * 1000)),
|
||||
duration="0",
|
||||
hash=packet_record.get("packet_hash", "")
|
||||
)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert to dictionary for JSON serialization"""
|
||||
return asdict(self)
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Optional, Dict, Any
|
||||
from .sqlite_handler import SQLiteHandler
|
||||
from .rrdtool_handler import RRDToolHandler
|
||||
from .mqtt_handler import MQTTHandler
|
||||
from .letsmesh_handler import MeshCoreToMqttJwtPusher
|
||||
from .letsmesh_handler import MeshCoreToMqttJwtPusher, LetsMeshPacket
|
||||
|
||||
|
||||
logger = logging.getLogger("StorageCollector")
|
||||
@@ -42,10 +42,23 @@ class StorageCollector:
|
||||
stats_provider=self._get_live_stats
|
||||
)
|
||||
self.letsmesh_handler.connect()
|
||||
|
||||
# Get disallowed packet types from config
|
||||
from ..config import get_node_info
|
||||
node_info = get_node_info(config)
|
||||
self.disallowed_packet_types = set(node_info["disallowed_packet_types"])
|
||||
|
||||
logger.info(f"LetsMesh handler initialized with public key: {public_key_hex[:16]}...")
|
||||
if self.disallowed_packet_types:
|
||||
logger.info(f"Disallowed packet types: {sorted(self.disallowed_packet_types)}")
|
||||
else:
|
||||
logger.info("All packet types allowed")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize LetsMesh handler: {e}")
|
||||
self.letsmesh_handler = None
|
||||
self.disallowed_packet_types = set()
|
||||
else:
|
||||
self.disallowed_packet_types = set()
|
||||
|
||||
def _get_live_stats(self) -> dict:
|
||||
"""Get live stats from RepeaterHandler"""
|
||||
@@ -75,40 +88,28 @@ class StorageCollector:
|
||||
# Publish to LetsMesh if enabled
|
||||
if self.letsmesh_handler:
|
||||
try:
|
||||
# Format packet data for LetsMesh publish_packet
|
||||
if "raw_packet" in packet_record and packet_record["raw_packet"]:
|
||||
# Extract timestamp and format date/time
|
||||
timestamp = packet_record.get("timestamp", time.time())
|
||||
dt = datetime.fromtimestamp(timestamp)
|
||||
# Check if packet has type field
|
||||
if "type" not in packet_record:
|
||||
logger.error("Cannot publish to LetsMesh: packet_record missing 'type' field")
|
||||
return
|
||||
|
||||
packet_type = packet_record["type"]
|
||||
|
||||
if packet_type in self.disallowed_packet_types:
|
||||
logger.debug(f"Skipped publishing packet type 0x{packet_type:02X} (in disallowed types)")
|
||||
return
|
||||
|
||||
# Create LetsMesh packet from record
|
||||
node_name = self.config.get("repeater", {}).get("node_name", "Unknown")
|
||||
letsmesh_packet = LetsMeshPacket.from_packet_record(
|
||||
packet_record,
|
||||
origin=node_name,
|
||||
origin_id=self.letsmesh_handler.public_key
|
||||
)
|
||||
|
||||
if letsmesh_packet:
|
||||
self.letsmesh_handler.publish_packet(letsmesh_packet.to_dict())
|
||||
|
||||
# Get node name from config
|
||||
node_name = self.config.get("repeater", {}).get("node_name", "Unknown")
|
||||
|
||||
# Format route type (1=Flood->F, 2=Direct->D, etc)
|
||||
route_map = {1: "F", 2: "D"}
|
||||
route = route_map.get(packet_record.get("route", 0), str(packet_record.get("route", 0)))
|
||||
|
||||
letsmesh_packet = {
|
||||
"origin": node_name,
|
||||
"origin_id": self.letsmesh_handler.public_key,
|
||||
"timestamp": dt.isoformat(),
|
||||
"type": "PACKET",
|
||||
"direction": "rx",
|
||||
"time": dt.strftime("%H:%M:%S"),
|
||||
"date": dt.strftime("%-d/%-m/%Y"),
|
||||
"len": str(len(packet_record["raw_packet"]) // 2), # Raw packet length in bytes
|
||||
"packet_type": str(packet_record.get("type", 0)),
|
||||
"route": route,
|
||||
"payload_len": str(packet_record.get("payload_length", 0)),
|
||||
"raw": packet_record["raw_packet"],
|
||||
"SNR": str(packet_record.get("snr", 0)),
|
||||
"RSSI": str(packet_record.get("rssi", 0)),
|
||||
"score": str(int(packet_record.get("score", 0) * 1000)), # Convert to integer score
|
||||
"duration": "0", # Not available in our packet record
|
||||
"hash": packet_record.get("packet_hash", "")
|
||||
}
|
||||
|
||||
self.letsmesh_handler.publish_packet(letsmesh_packet)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to publish packet to LetsMesh: {e}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user