data: add mesh node data ingestor python daemon

This commit is contained in:
l5y
2025-09-13 08:39:00 +02:00
parent 1d1bbb1a12
commit aaf77988e9
5 changed files with 127 additions and 0 deletions
+6
View File
@@ -1,2 +1,8 @@
# potato-mesh
a simple node dashboard for berlin mediumfast
### data
uses python meshtastic library to ingest mesh data into an sqlite3 database locally
run `nodes.sh` in `data/` to keep updating node records.
+3
View File
@@ -0,0 +1,3 @@
*.db
*.db-wal
*.db-shm
+80
View File
@@ -0,0 +1,80 @@
import json, sqlite3, time, threading
from pathlib import Path
from meshtastic.serial_interface import SerialInterface
from meshtastic.mesh_interface import MeshInterface
DB = "nodes.db"
schema = Path("nodes.sql").read_text()
conn = sqlite3.connect(DB, check_same_thread=False)
conn.executescript(schema)
conn.commit()
def upsert_node(node_id, n):
user = (n.get("user") or {})
met = (n.get("deviceMetrics") or {})
pos = (n.get("position") or {})
row = (
node_id,
n.get("num"),
user.get("shortName"),
user.get("longName"),
user.get("macaddr"),
user.get("hwModel") or n.get("hwModel"),
user.get("role"),
user.get("publicKey"),
user.get("isUnmessagable"),
n.get("isFavorite"),
n.get("hopsAway"),
n.get("snr"),
n.get("lastHeard"),
met.get("batteryLevel"),
met.get("voltage"),
met.get("channelUtilization"),
met.get("airUtilTx"),
met.get("uptimeSeconds"),
pos.get("time"),
pos.get("locationSource"),
pos.get("latitude"),
pos.get("longitude"),
pos.get("altitude"),
json.dumps(n, ensure_ascii=False)
)
conn.execute("""
INSERT INTO nodes(node_id,num,short_name,long_name,macaddr,hw_model,role,public_key,is_unmessagable,is_favorite,
hops_away,snr,last_heard,battery_level,voltage,channel_utilization,air_util_tx,uptime_seconds,
position_time,location_source,latitude,longitude,altitude,node_json)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
ON CONFLICT(node_id) DO UPDATE SET
num=excluded.num, short_name=excluded.short_name, long_name=excluded.long_name, macaddr=excluded.macaddr,
hw_model=excluded.hw_model, role=excluded.role, public_key=excluded.public_key, is_unmessagable=excluded.is_unmessagable,
is_favorite=excluded.is_favorite, hops_away=excluded.hops_away, snr=excluded.snr, last_heard=excluded.last_heard,
battery_level=excluded.battery_level, voltage=excluded.voltage, channel_utilization=excluded.channel_utilization,
air_util_tx=excluded.air_util_tx, uptime_seconds=excluded.uptime_seconds, position_time=excluded.position_time,
location_source=excluded.location_source, latitude=excluded.latitude, longitude=excluded.longitude,
altitude=excluded.altitude, node_json=excluded.node_json
""", row)
def snapshot_nodes_periodically(iface: MeshInterface, every_sec=30):
time.sleep(5) # let the library sync initial node DB
while True:
try:
for node_id, n in (getattr(iface, "nodes", {}) or {}).items():
upsert_node(node_id, n)
conn.commit()
except Exception as e:
print("node snapshot error:", e)
time.sleep(every_sec)
def main():
iface = SerialInterface(devPath="/dev/ttyACM0")
# optional: also update on any incoming nodeinfo packets via onReceive if you want
threading.Thread(target=snapshot_nodes_periodically, args=(iface, 30), daemon=True).start()
print("Nodes ingestor running. Ctrl+C to stop.")
try:
while True: time.sleep(60)
except KeyboardInterrupt:
pass
if __name__ == "__main__":
main()
Executable
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
python -m venv .venv && source .venv/bin/activate
pip install meshtastic
python nodes.py
+33
View File
@@ -0,0 +1,33 @@
-- nodes.sql
PRAGMA journal_mode=WAL;
CREATE TABLE IF NOT EXISTS nodes (
node_id TEXT PRIMARY KEY, -- e.g. "!0c63e027"
num INTEGER, -- numeric node number
short_name TEXT,
long_name TEXT,
macaddr TEXT,
hw_model TEXT,nodes
role TEXT,
public_key TEXT,
is_unmessagable BOOLEAN,
is_favorite BOOLEAN,
hops_away INTEGER,
snr REAL,
last_heard INTEGER, -- unix seconds
battery_level REAL,
voltage REAL,
channel_utilization REAL,
air_util_tx REAL,
uptime_seconds INTEGER,
position_time INTEGER,
location_source TEXT,
latitude REAL,
longitude REAL,
altitude REAL,
node_json TEXT NOT NULL -- full original node object for debugging
);
CREATE INDEX IF NOT EXISTS idx_nodes_last_heard ON nodes(last_heard);
CREATE INDEX IF NOT EXISTS idx_nodes_hw_model ON nodes(hw_model);
CREATE INDEX IF NOT EXISTS idx_nodes_latlon ON nodes(latitude, longitude);