added more diagnostics
This commit is contained in:
@@ -14,6 +14,7 @@ import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import paho.mqtt.client as mqtt
|
||||
import requests
|
||||
@@ -62,6 +63,36 @@ CHANNEL_KEY = expand_psk(CHANNEL_PSK)
|
||||
# Matches "T1:20.8", "H2:54.4", "P:1013" etc. (unit suffix is ignored here).
|
||||
PAIR_RE = re.compile(r"([A-Za-z][A-Za-z0-9_]*)\s*:\s*(-?\d+(?:\.\d+)?)")
|
||||
|
||||
# Built-in per-node diagnostic sensors derived from packet metadata.
|
||||
DIAGNOSTICS = [
|
||||
{
|
||||
"key": "last_seen",
|
||||
"name": "Last Seen",
|
||||
"device_class": "timestamp",
|
||||
"icon": "mdi:clock-outline",
|
||||
},
|
||||
{
|
||||
"key": "rssi",
|
||||
"name": "RSSI",
|
||||
"device_class": "signal_strength",
|
||||
"unit": "dBm",
|
||||
"state_class": "measurement",
|
||||
},
|
||||
{
|
||||
"key": "snr",
|
||||
"name": "SNR",
|
||||
"unit": "dB",
|
||||
"state_class": "measurement",
|
||||
"icon": "mdi:signal",
|
||||
},
|
||||
{
|
||||
"key": "hops_away",
|
||||
"name": "Hops Away",
|
||||
"state_class": "measurement",
|
||||
"icon": "mdi:transit-connection-variant",
|
||||
},
|
||||
]
|
||||
|
||||
# Set once the HA broker client is connected.
|
||||
ha_client = None
|
||||
|
||||
@@ -158,10 +189,62 @@ def publish_discovery():
|
||||
json.dumps(cfg),
|
||||
retain=True,
|
||||
)
|
||||
log(f"[ha] published discovery for last_message + {len(SENSORS)} sensors")
|
||||
|
||||
for d in DIAGNOSTICS:
|
||||
key = d["key"]
|
||||
cfg = {
|
||||
"name": d["name"],
|
||||
"unique_id": f"{DEVICE_UID}_{key}",
|
||||
"state_topic": f"{BASE_TOPIC}/{key}",
|
||||
"availability_topic": AVAIL_TOPIC,
|
||||
"entity_category": "diagnostic",
|
||||
"device": device_block(),
|
||||
}
|
||||
if d.get("unit"):
|
||||
cfg["unit_of_measurement"] = d["unit"]
|
||||
if d.get("device_class"):
|
||||
cfg["device_class"] = d["device_class"]
|
||||
if d.get("state_class"):
|
||||
cfg["state_class"] = d["state_class"]
|
||||
if d.get("icon"):
|
||||
cfg["icon"] = d["icon"]
|
||||
ha_client.publish(
|
||||
f"{DISCOVERY_PREFIX}/sensor/{DEVICE_UID}/{key}/config",
|
||||
json.dumps(cfg),
|
||||
retain=True,
|
||||
)
|
||||
|
||||
log(
|
||||
f"[ha] published discovery for last_message + {len(SENSORS)} sensors "
|
||||
f"+ {len(DIAGNOSTICS)} diagnostics"
|
||||
)
|
||||
|
||||
|
||||
# --- Message handling --------------------------------------------------------
|
||||
def publish_metadata(mp):
|
||||
"""Publish link-quality diagnostics from a received MeshPacket."""
|
||||
# Last seen: prefer the gateway's rx_time, fall back to now.
|
||||
ts = (
|
||||
datetime.fromtimestamp(mp.rx_time, tz=timezone.utc)
|
||||
if mp.rx_time
|
||||
else datetime.now(timezone.utc)
|
||||
)
|
||||
ha_client.publish(f"{BASE_TOPIC}/last_seen", ts.isoformat(), retain=True)
|
||||
|
||||
# RSSI/SNR are 0 for packets the gateway originated itself — only publish
|
||||
# genuine over-the-air measurements.
|
||||
if mp.rx_rssi != 0:
|
||||
ha_client.publish(f"{BASE_TOPIC}/rssi", mp.rx_rssi, retain=True)
|
||||
ha_client.publish(f"{BASE_TOPIC}/snr", round(mp.rx_snr, 2), retain=True)
|
||||
|
||||
# hop_start is the original TTL; hops taken = hop_start - hop_limit.
|
||||
# hop_start == 0 means older firmware that didn't report it.
|
||||
if mp.hop_start:
|
||||
ha_client.publish(
|
||||
f"{BASE_TOPIC}/hops_away", mp.hop_start - mp.hop_limit, retain=True
|
||||
)
|
||||
|
||||
|
||||
def handle_text(text, from_id, to_id):
|
||||
# HA sensor states are limited to 255 chars.
|
||||
ha_client.publish(f"{BASE_TOPIC}/last_message", text[:255], retain=True)
|
||||
@@ -212,6 +295,9 @@ def on_src_message(client, userdata, msg):
|
||||
mp = env.packet
|
||||
from_id = getattr(mp, "from")
|
||||
|
||||
# Link diagnostics + last-seen update for every packet on the channel.
|
||||
publish_metadata(mp)
|
||||
|
||||
if mp.HasField("decoded"):
|
||||
data = mp.decoded
|
||||
elif mp.HasField("encrypted"):
|
||||
|
||||
Reference in New Issue
Block a user