Grafana Dashboards aufgeteilt und ADS-B Windrose gebaut

Mein Grafana-Dashboard war eine einzige riesige Seite mit allem drin: Server-Stats, Fritzbox, Heizung, Strom, PV - alles in collapsed Rows. Das war nervig zu navigieren. Also hab ich einen Nachmittag investiert und das Ding in 5 spezialisierte Dashboards aufgeteilt. Dabei ist mir aufgefallen dass die Brennerstarts meiner Heizung verdächtig hoch sind, aber das ist eine andere Geschichte.

Dashboard-Umbau

Das monolithische Dashboard war unübersichtlich geworden. Wenn ich nach Heizungs-Daten suchen wollte musste ich erstmal 5 collapsed Rows aufklappen und scrollen. Also hab ich mir einen Nachmittag Zeit genommen und das Ganze in thematische Dashboards aufgeteilt.

Rausgekommen sind 5 Dashboards: Vault Server mit CPU, RAM, ZFS und Disk I/O. Internet & Fritzbox mit WAN Throughput und WLAN Clients. Heizung mit Kessel, Zigbee Thermometern und Öl-Verbrauch. Stromverbrauch mit Grid und Tagesbilanz. PV Anlage mit Einspeisung und Strang-Daten. Einige Panels hab ich cross-referenced, Stromverbrauch ist sowohl im Heizungs- als auch im PV-Dashboard relevant.

Grafana läuft als Docker-Container hinter Traefik auf grafana.malura.org. Das Umbau-Scripting hab ich über die Grafana HTTP API gemacht - Service Account Token erstellt, Dashboard-JSON via curl hochgeladen. Spart Zeit wenn man viele Dashboards umzieht.

[[img:4]]

ADS-B Windrose: Empfangsrichtung visualisieren

Mein ADS-B Feeder läuft seit ein paar Tagen und loggt Flugzeug-Daten in InfluxDB. Das Dashboard hatte schon Flugzeug-Tracking, aber ich wollte sehen aus welcher Richtung ich die meisten Signale empfange. Das operato-windrose-panel Plugin braucht exakt die Felder wind_direction und wind_speed. Blöd nur dass das track-Feld die Flugrichtung zeigt, nicht die Empfangsrichtung.

Also hab ich die Bearing-Berechnung direkt in meinen Python-Logger (adsb_influx.py) eingebaut:

from math import radians, degrees, atan2, sin, cos

def calculate_bearing(lat1, lon1, lat2, lon2):
dLon = radians(lon2 - lon1)
lat1, lat2 = radians(lat1), radians(lat2)

x = sin(dLon) * cos(lat2)
y = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)

bearing = degrees(atan2(x, y))
return (bearing + 360) % 360

# Receiver-Position aus Config
receiver_lat, receiver_lon = MY_LAT, MY_LON

bearing = calculate_bearing(receiver_lat, receiver_lon, aircraft_lat, aircraft_lon)

Neue InfluxDB-Felder: bearing (0-360°) und distance_km. Query für die Windrose:

SELECT "bearing" AS "wind_direction", "distance_km" AS "wind_speed"
FROM "adsb_aircraft"
WHERE $timeFilter

Funktioniert. Die meisten Flugzeuge kommen aus Süd-West.

[[img:3]]

[[img:1]]

[[img:2]]

Learnings: Was nicht funktionierte

Barchart-Paneltyp crasht in Grafana 13 mit Zeitreihen -> timeseries mit drawStyle: "bars" verwenden

Plotly-Plugin (ae3e-plotly-panel) hat kryptische Syntax -> native Panels sind robuster

Dynamic Text Plugin crasht bei Handlebars mit Umlauten -> Stat-Panels im Grid-Layout nutzen

filterByValue-Transformation filterte manchmal ALLE Daten weg -> Split-Queries als Alternative

Am Ende hab ich jetzt 5 Dashboards statt 1, ADS-B mit Windrose, und ein paar Plugin-Enttäuschungen. Dafür läuft jetzt alles sauber über die API und ist reproduzierbar.