mirror of
https://github.com/l5yth/potato-mesh.git
synced 2026-03-28 17:42:48 +01:00
104 lines
3.7 KiB
Python
104 lines
3.7 KiB
Python
import json, os, sqlite3, time, threading
|
|
from pathlib import Path
|
|
|
|
try: # meshtastic is optional for tests
|
|
from meshtastic.serial_interface import SerialInterface
|
|
from meshtastic.mesh_interface import MeshInterface
|
|
except ModuleNotFoundError: # pragma: no cover - imported lazily for hardware usage
|
|
SerialInterface = None # type: ignore
|
|
MeshInterface = None # type: ignore
|
|
|
|
DB = os.environ.get("MESH_DB", "nodes.db")
|
|
|
|
schema = Path(__file__).with_name("nodes.sql").read_text()
|
|
conn = sqlite3.connect(DB, check_same_thread=False)
|
|
conn.executescript(schema)
|
|
conn.commit()
|
|
|
|
|
|
def _get(obj, key, default=None):
|
|
"""Return value for key/attribute from dicts or objects."""
|
|
if isinstance(obj, dict):
|
|
return obj.get(key, default)
|
|
return getattr(obj, key, default)
|
|
|
|
|
|
def upsert_node(node_id, n):
|
|
user = _get(n, "user") or {}
|
|
met = _get(n, "deviceMetrics") or {}
|
|
pos = _get(n, "position") or {}
|
|
lh = _get(n, "lastHeard")
|
|
row = (
|
|
node_id,
|
|
_get(n, "num"),
|
|
_get(user, "shortName"),
|
|
_get(user, "longName"),
|
|
_get(user, "macaddr"),
|
|
_get(user, "hwModel") or _get(n, "hwModel"),
|
|
_get(user, "role"),
|
|
_get(user, "publicKey"),
|
|
_get(user, "isUnmessagable"),
|
|
_get(n, "isFavorite"),
|
|
_get(n, "hopsAway"),
|
|
_get(n, "snr"),
|
|
lh,
|
|
lh,
|
|
_get(met, "batteryLevel"),
|
|
_get(met, "voltage"),
|
|
_get(met, "channelUtilization"),
|
|
_get(met, "airUtilTx"),
|
|
_get(met, "uptimeSeconds"),
|
|
_get(pos, "time"),
|
|
_get(pos, "locationSource"),
|
|
_get(pos, "latitude"),
|
|
_get(pos, "longitude"),
|
|
_get(pos, "altitude"),
|
|
)
|
|
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,first_heard,battery_level,voltage,channel_utilization,air_util_tx,uptime_seconds,
|
|
position_time,location_source,latitude,longitude,altitude)
|
|
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
|
|
""",
|
|
row,
|
|
)
|
|
|
|
|
|
def load_nodes_from_file(path: str | Path):
|
|
"""Populate the database from a nodes.json file."""
|
|
nodes = json.loads(Path(path).read_text())
|
|
for node_id, node in nodes.items():
|
|
upsert_node(node_id, node)
|
|
conn.commit()
|
|
|
|
|
|
def main():
|
|
if SerialInterface is None:
|
|
raise RuntimeError("meshtastic library not installed")
|
|
iface = SerialInterface(
|
|
# or whatever serial interface it is
|
|
devPath="/dev/ttyACM0"
|
|
)
|
|
print("Nodes ingestor running. Ctrl+C to stop.")
|
|
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(30)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|