diff --git a/CHANGELOG.md b/CHANGELOG.md
index 18a5c0a..544c6b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,21 @@
# Changelog
+## [0.8.6] - 2026-02-19
+
+### Added
+- **Paket-Log informativer Payload** (fixes #3): `bot.py` speichert nun vollständige
+ Paketdaten je Typ:
+ - `POSITION_APP`: lat/lon + Höhe, Geschwindigkeit, Satelliten
+ - `TELEMETRY_APP`: Akku, Spannung, Kanalauslastung, TX-Auslastung, Temperatur,
+ Luftfeuchtigkeit, Luftdruck (Environment Metrics)
+ - `NODEINFO_APP`: long_name, short_name + Hardware-Modell
+ - `ROUTING_APP`: Fehlercode (errorReason)
+ - `TRACEROUTE_APP`: Hop-Anzahl
+ - `NEIGHBORINFO_APP`: Anzahl Nachbarn
+- **Telemetrie-Unterdrückung eigener Node** (fixes #3): Telemetriepakete vom eigenen
+ Node (per `my_node_id` oder short_name `FTLW`) werden im Paket-Log nicht angezeigt
+ und gehen nicht in die Zählung ein.
+
## [0.8.5] - 2026-02-19
### Fixed
diff --git a/config.yaml b/config.yaml
index f7eae65..0826f7f 100644
--- a/config.yaml
+++ b/config.yaml
@@ -1,4 +1,4 @@
-version: "0.8.5"
+version: "0.8.6"
bot:
name: "MeshDD-Bot"
diff --git a/meshbot/bot.py b/meshbot/bot.py
index ad303d8..c3785c2 100644
--- a/meshbot/bot.py
+++ b/meshbot/bot.py
@@ -256,13 +256,43 @@ class MeshBot:
payload_summary = {"text": decoded.get("text", "")}
elif portnum == "POSITION_APP":
pos = decoded.get("position", {})
- payload_summary = {"lat": pos.get("latitude"), "lon": pos.get("longitude")}
+ payload_summary = {
+ "lat": pos.get("latitude"),
+ "lon": pos.get("longitude"),
+ "alt": pos.get("altitude"),
+ "speed": pos.get("groundSpeed"),
+ "sats": pos.get("satsInView"),
+ }
elif portnum == "TELEMETRY_APP":
- dm = decoded.get("telemetry", {}).get("deviceMetrics", {})
- payload_summary = {"battery": dm.get("batteryLevel"), "voltage": dm.get("voltage")}
+ telemetry = decoded.get("telemetry", {})
+ dm = telemetry.get("deviceMetrics", {})
+ em = telemetry.get("environmentMetrics", {})
+ payload_summary = {
+ "battery": dm.get("batteryLevel"),
+ "voltage": dm.get("voltage"),
+ "ch_util": dm.get("channelUtilization"),
+ "air_util": dm.get("airUtilTx"),
+ "temp": em.get("temperature"),
+ "humidity": em.get("relativeHumidity"),
+ "pressure": em.get("barometricPressure"),
+ }
elif portnum == "NODEINFO_APP":
u = decoded.get("user", {})
- payload_summary = {"long_name": u.get("longName"), "short_name": u.get("shortName")}
+ payload_summary = {
+ "long_name": u.get("longName"),
+ "short_name": u.get("shortName"),
+ "hw_model": u.get("hwModel"),
+ }
+ elif portnum == "ROUTING_APP":
+ routing = decoded.get("routing", {})
+ payload_summary = {"error": routing.get("errorReason")}
+ elif portnum == "TRACEROUTE_APP":
+ route = decoded.get("traceroute", {})
+ snodes = route.get("route", [])
+ payload_summary = {"hops": len(snodes), "route": snodes}
+ elif portnum == "NEIGHBORINFO_APP":
+ ni = decoded.get("neighborinfo", {})
+ payload_summary = {"count": len(ni.get("neighbors", []))}
pkt_record = await self.db.insert_packet(
str(from_id), str(to_id), portnum, channel,
diff --git a/static/js/packets.js b/static/js/packets.js
index 1e68dee..0d853de 100644
--- a/static/js/packets.js
+++ b/static/js/packets.js
@@ -6,6 +6,7 @@ const UNKNOWN_TYPE = '__unknown__';
let ws = null;
let nodes = {}; // node_id -> {long_name, short_name, ...}
let channels = {}; // ch_index -> channel name string
+let myNodeId = null;
let paused = false;
let activeFilter = 'all';
let pendingRows = [];
@@ -93,21 +94,56 @@ function portnumBadge(portnum) {
return `${escapeHtml(cfg.label)}`;
}
+function isSuppressed(pkt) {
+ if (pkt.portnum !== 'TELEMETRY_APP') return false;
+ if (myNodeId && pkt.from_id === myNodeId) return true;
+ const n = nodes[pkt.from_id];
+ return !!(n && n.short_name === 'FTLW');
+}
+
function fmtPayload(portnum, payloadStr) {
let p = {};
try { p = JSON.parse(payloadStr || '{}'); } catch { return ''; }
+
if (portnum === 'TEXT_MESSAGE_APP' && p.text)
return `${escapeHtml(p.text)}`;
- if (portnum === 'POSITION_APP' && p.lat != null)
- return `${p.lat?.toFixed(5)}, ${p.lon?.toFixed(5)}`;
+
+ if (portnum === 'POSITION_APP' && p.lat != null) {
+ const parts = [`${p.lat.toFixed(5)}, ${p.lon.toFixed(5)}`];
+ if (p.alt != null && p.alt !== 0) parts.push(`${p.alt} m`);
+ if (p.speed != null && p.speed !== 0) parts.push(`${p.speed} km/h`);
+ if (p.sats != null) parts.push(`${p.sats} Sat`);
+ return `${parts.join(' · ')}`;
+ }
+
if (portnum === 'TELEMETRY_APP') {
const parts = [];
- if (p.battery != null) parts.push(`🔋 ${p.battery}%`);
- if (p.voltage != null) parts.push(`${p.voltage?.toFixed(2)} V`);
+ if (p.battery != null) parts.push(`🔋 ${p.battery}%`);
+ if (p.voltage != null) parts.push(`${p.voltage.toFixed(2)} V`);
+ if (p.ch_util != null) parts.push(`CH ${p.ch_util.toFixed(1)}%`);
+ if (p.air_util != null) parts.push(`TX ${p.air_util.toFixed(1)}%`);
+ if (p.temp != null) parts.push(`🌡 ${p.temp.toFixed(1)} °C`);
+ if (p.humidity != null) parts.push(`💧 ${p.humidity.toFixed(0)}%`);
+ if (p.pressure != null) parts.push(`${p.pressure.toFixed(0)} hPa`);
return parts.length ? `${parts.join(' · ')}` : '';
}
- if (portnum === 'NODEINFO_APP' && (p.long_name || p.short_name))
- return `${escapeHtml(p.long_name || '')}${p.short_name ? ` [${escapeHtml(p.short_name)}]` : ''}`;
+
+ if (portnum === 'NODEINFO_APP' && (p.long_name || p.short_name)) {
+ let s = escapeHtml(p.long_name || '');
+ if (p.short_name) s += ` [${escapeHtml(p.short_name)}]`;
+ if (p.hw_model) s += ` ${escapeHtml(p.hw_model)}`;
+ return `${s}`;
+ }
+
+ if (portnum === 'ROUTING_APP' && p.error && p.error !== 'NONE')
+ return `${escapeHtml(p.error)}`;
+
+ if (portnum === 'TRACEROUTE_APP' && p.hops != null)
+ return `${p.hops} Hop${p.hops !== 1 ? 's' : ''}`;
+
+ if (portnum === 'NEIGHBORINFO_APP' && p.count != null)
+ return `${p.count} Nachbar${p.count !== 1 ? 'n' : ''}`;
+
return '';
}
@@ -187,6 +223,7 @@ function buildRow(pkt) {
}
function addRow(pkt, prepend = true) {
+ if (isSuppressed(pkt)) return;
knownTypes.add(typeKey(pkt.portnum));
const row = buildRow(pkt);
if (prepend) {
@@ -249,9 +286,13 @@ function handleMsg(msg) {
case 'channels':
channels = msg.data || {};
break;
+ case 'my_node_id':
+ myNodeId = msg.data;
+ break;
case 'initial_packets':
pktBody.innerHTML = '';
(msg.data || []).forEach(p => {
+ if (isSuppressed(p)) return;
knownTypes.add(typeKey(p.portnum));
pktBody.appendChild(buildRow(p));
});