From 536a6fd0cdf94fbd46eed0143c7e4a4a98978261 Mon Sep 17 00:00:00 2001 From: Micha Date: Sat, 20 Jun 2026 09:27:26 +0200 Subject: [PATCH] monitoring: Wetter-Tagesberichte in Grafana auffindbar machen - weather-report-history.json (ha-weather-report-history): Finder-Tabelle, eine Zeile pro Tag (Datum, Kurzbewertung, T min/max/Mittel, Regen, UV, Boee) mit Drilldown-Data-Link aufs Tagesbericht-Dashboard - weather-day-report.json: Zeitzone Europe/Berlin, Info-Panel zur Tagesauswahl, Nav-Dropdown zu den Wetter-Dashboards - monitoring/README: Abschnitt Wetter-Tagesberichte (finden, Datum waehlen, Quelle InfluxDB-SQL statt Markdown-Index, Deploy, Explore-Test) Co-Authored-By: Claude Opus 4.8 --- monitoring/README.md | 50 +++++++++++++++++ .../dashboards/weather-day-report.json | 19 ++++++- .../dashboards/weather-report-history.json | 55 +++++++++++++++++++ 3 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 monitoring/grafana/dashboards/weather-report-history.json diff --git a/monitoring/README.md b/monitoring/README.md index 530f2c9..d0fc562 100644 --- a/monitoring/README.md +++ b/monitoring/README.md @@ -74,6 +74,56 @@ INFLUXDB_BIND_IP=192.168.178.58 - Uptime Kuma ist entfernt; `Homelab / Availability`, Blackbox Exporter und Prometheus-Alerts sind der Zielzustand fuer HTTP-Verfuegbarkeit. - Dashboard-Zielbestand: `Homelab / Availability`, `Homelab / Containers + Logs`, `Homelab / Host Overview`, `Homelab / Family Status`, `Traefik Official Standalone Dashboard`. +## Wetter-Tagesberichte + +Die Ecowitt-/HA-Wetterdaten (DB `homeassistant`, Datasource `ha-weather-influx`) +werden in drei Grafana-Dashboards sichtbar (Ordner `Homelab`, Tag `weather`): + +- `Wetterarchiv KalliHome` (`ha-weather-archive`): Verlaeufe und Gauges ueber einen Zeitbereich. +- `Wetterbericht KalliHome` (`ha-weather-day-report`): ausfuehrlicher Tagesbericht (Markdown-Tabelle) fuer **einen** Tag. +- `Wetter-Tagesberichte KalliHome` (`ha-weather-report-history`): Index/Finder, **eine Zeile pro Tag** mit Kurzbewertung, T min/max/Mittel, Regen, UV und Boee. + +### Alte Tagesberichte finden und ein Datum waehlen + +1. Dashboard **`Wetter-Tagesberichte`** oeffnen (Standard: letzte 30 Tage, Europe/Berlin). + In der Tabelle **auf das Datum klicken** -> der ausfuehrliche Tagesbericht oeffnet + sich fuer genau diesen Tag. Weiter zurueck: Zeitbereich oben rechts vergroessern. +2. Alternativ im Dashboard **`Wetterbericht KalliHome`** direkt einen Tag waehlen: + Zeitbereich oben rechts -> *Absolute time range* -> z. B. From `2026-06-15 00:00:00`, + To `2026-06-16 00:00:00` -> **Apply**. Beide Dashboards haben Zeitzone + `Europe/Berlin`, die Eingaben gelten also in Berliner Lokalzeit; Standard ist + `gestern` (`now-1d/d` bis `now/d`). + +Grafana hat **keine echte Datepicker-Variable**: eine Textbox-Variable kann den +Zeitbereich nicht setzen, und ein `report_date`-Ansatz braeuchte zeitzonen-genaues +Tages-Bounding in InfluxDB-3-SQL. Deshalb ist der **Timepicker bei Dashboard-Zeitzone +`Europe/Berlin`** der exakte Weg fuer einen vollstaendigen Lokaltag, und die +Index-Tabelle der bequeme Klick-Selektor fuer alte Tage. Die Tabellen-Buckets liegen +auf UTC-Mitternacht (~01:00/02:00 Berlin); der verlinkte Tagesbericht zeigt das +gewaehlte Fenster vollstaendig. + +### Quelle: InfluxDB statt Markdown-Index + +Die Dashboards rendern direkt per SQL aus InfluxDB 3 Core - gleiche Quelle wie der +Markdown-Generator `services/posture-check/weather-day-report.py`. Damit ist jeder +zurueckliegende Tag reproduzierbar, solange die Rohdaten vorgehalten werden; ein +separater Markdown-Index ist bewusst nicht noetig. Die erzeugten Tagesberichte als +Datei (E-Mail/Offline-Archiv) liegen weiterhin unter +`/mnt/user/services/posture-check/daily-reports/homelab-day-YYYY-MM-DD.md`. + +### Deploy und Test + +Dashboards werden aus `monitoring/grafana/dashboards/` provisioniert (Verzeichnis- +Mount, Reload alle 5 Minuten, Ordner `Homelab`). Nach Push nach Gitea deployt Komodo +den `monitoring`-Stack; Grafana laedt die JSON-Dateien automatisch nach. Kein +Bootstrap-Importer noetig (der gilt nur fuer grafana.com-Dashboard-ID 17346). + +SQL der Index-Tabelle vor dem Verlassen einmal pruefen: Grafana -> *Explore* -> +Datasource `ha-weather-influx` -> die `rawSql` aus `weather-report-history.json` +einfuegen und einen Zeitbereich (z. B. letzte 7 Tage) waehlen. Erwartet: eine Zeile +pro Tag mit gefuellten Spalten. Falls `extract(epoch FROM ...)` auf dem InfluxDB-3- +Build nicht unterstuetzt wird, stattdessen `to_unixtime(...)` verwenden. + ## Alerting Prometheus wertet `monitoring/prometheus/alerts.yml` aus und sendet an `monitoring-alertmanager`. diff --git a/monitoring/grafana/dashboards/weather-day-report.json b/monitoring/grafana/dashboards/weather-day-report.json index 413f483..eab6750 100644 --- a/monitoring/grafana/dashboards/weather-day-report.json +++ b/monitoring/grafana/dashboards/weather-day-report.json @@ -2,19 +2,32 @@ "uid": "ha-weather-day-report", "title": "Wetterbericht KalliHome", "tags": ["weather", "ecowitt", "homeassistant", "report"], - "timezone": "browser", + "timezone": "Europe/Berlin", "schemaVersion": 39, - "version": 1, + "version": 2, "refresh": "", "time": { "from": "now-1d/d", "to": "now/d" }, "templating": { "list": [] }, "annotations": { "list": [] }, + "links": [ + { "asDropdown": true, "icon": "external link", "includeVars": false, "keepTime": false, "tags": ["weather"], "targetBlank": false, "title": "Wetter-Dashboards", "tooltip": "", "type": "dashboards", "url": "" } + ], "panels": [ + { + "id": 2, + "title": "", + "type": "text", + "gridPos": { "h": 4, "w": 24, "x": 0, "y": 0 }, + "options": { + "mode": "markdown", + "content": "## Tag auswählen\n\n**Standard:** gestern (ganzer Tag, Europe/Berlin).\n\n**Anderen Tag:** Zeitbereich oben rechts → *Absolute time range* → z. B. From `2026-06-15 00:00:00`, To `2026-06-16 00:00:00` → **Apply** (Zeiten gelten in Europe/Berlin).\n\n**Alle Tage als Liste:** [Wetter-Tagesberichte](/d/ha-weather-report-history) – dort jeden Tag per Klick öffnen." + } + }, { "id": 1, "title": "Tagesbericht", "type": "table", - "gridPos": { "h": 16, "w": 24, "x": 0, "y": 0 }, + "gridPos": { "h": 16, "w": 24, "x": 0, "y": 4 }, "datasource": { "type": "influxdb", "uid": "ha-weather-influx" }, "fieldConfig": { "defaults": { diff --git a/monitoring/grafana/dashboards/weather-report-history.json b/monitoring/grafana/dashboards/weather-report-history.json new file mode 100644 index 0000000..b48c2b4 --- /dev/null +++ b/monitoring/grafana/dashboards/weather-report-history.json @@ -0,0 +1,55 @@ +{ + "uid": "ha-weather-report-history", + "title": "Wetter-Tagesberichte KalliHome", + "tags": ["weather", "ecowitt", "homeassistant", "report"], + "timezone": "Europe/Berlin", + "schemaVersion": 39, + "version": 1, + "refresh": "", + "time": { "from": "now-30d/d", "to": "now/d" }, + "templating": { "list": [] }, + "annotations": { "list": [] }, + "links": [ + { "asDropdown": true, "icon": "external link", "includeVars": false, "keepTime": false, "tags": ["weather"], "targetBlank": false, "title": "Wetter-Dashboards", "tooltip": "", "type": "dashboards", "url": "" } + ], + "panels": [ + { + "id": 2, + "title": "", + "type": "text", + "gridPos": { "h": 4, "w": 24, "x": 0, "y": 0 }, + "options": { + "mode": "markdown", + "content": "## Wetter-Tagesberichte\n\nEine Zeile pro Tag im gewählten Zeitbereich (Standard: letzte 30 Tage, Europe/Berlin). **Auf das Datum klicken** öffnet den ausführlichen [Tagesbericht](/d/ha-weather-day-report) für genau diesen Tag. Zeitbereich oben rechts ändern, um weiter zurückzublättern.\n\nHinweis: Die Tagesgrenzen dieser Übersicht liegen auf UTC-Mitternacht (~01:00/02:00 Europe/Berlin); der verlinkte Tagesbericht zeigt das gewählte Tagesfenster vollständig an. Regen ist der Tages-Maximalwert von `gw3000a_daily_rain`." + } + }, + { + "id": 1, + "title": "Tagesübersicht", + "type": "table", + "gridPos": { "h": 20, "w": 24, "x": 0, "y": 4 }, + "datasource": { "type": "influxdb", "uid": "ha-weather-influx" }, + "fieldConfig": { + "defaults": { + "custom": { "align": "left", "cellOptions": { "type": "auto" }, "filterable": true, "inspect": false } + }, + "overrides": [ + { "matcher": { "id": "byName", "options": "from_ms" }, "properties": [ { "id": "custom.hidden", "value": true } ] }, + { "matcher": { "id": "byName", "options": "to_ms" }, "properties": [ { "id": "custom.hidden", "value": true } ] }, + { "matcher": { "id": "byName", "options": "Datum" }, "properties": [ { "id": "links", "value": [ { "title": "Tagesbericht öffnen", "url": "/d/ha-weather-day-report?from=${__data.fields.from_ms}&to=${__data.fields.to_ms}&timezone=Europe/Berlin", "targetBlank": true } ] } ] }, + { "matcher": { "id": "byName", "options": "Bewertung" }, "properties": [ { "id": "custom.cellOptions", "value": { "type": "color-text" } }, { "id": "mappings", "value": [ { "type": "value", "options": { "Sonnig, warm": { "color": "#ffa94d", "index": 0 }, "Warm": { "color": "#ffd43b", "index": 1 }, "Regnerisch": { "color": "#4dabf7", "index": 2 }, "Kalt": { "color": "#74c0fc", "index": 3 }, "Unauffaellig": { "color": "#adb5bd", "index": 4 } } } ] } ] } + ] + }, + "options": { "showHeader": true, "cellHeight": "sm", "footer": { "show": false, "reducer": ["sum"], "countRows": false }, "sortBy": [ { "displayName": "Datum", "desc": true } ] }, + "targets": [ + { + "refId": "A", + "datasource": { "type": "influxdb", "uid": "ha-weather-influx" }, + "rawQuery": true, + "format": "table", + "rawSql": "WITH t AS (SELECT date_bin(INTERVAL '1 day', time) AS d, min(value) AS tmin, max(value) AS tmax, avg(value) AS tavg FROM \"°C\" WHERE entity_id = 'gw3000a_outdoor_temperature' AND $__timeFilter(time) GROUP BY 1), r AS (SELECT date_bin(INTERVAL '1 day', time) AS d, max(value) AS rain FROM \"mm\" WHERE entity_id = 'gw3000a_daily_rain' AND $__timeFilter(time) GROUP BY 1), u AS (SELECT date_bin(INTERVAL '1 day', time) AS d, max(value) AS uvmax FROM \"UV index\" WHERE entity_id = 'gw3000a_uv_index' AND $__timeFilter(time) GROUP BY 1), s AS (SELECT date_bin(INTERVAL '1 day', time) AS d, max(value) AS smax FROM \"W/m²\" WHERE entity_id = 'gw3000a_solar_radiation' AND $__timeFilter(time) GROUP BY 1), wg AS (SELECT date_bin(INTERVAL '1 day', time) AS d, max(value) AS wmax FROM \"km/h\" WHERE entity_id = 'gw3000a_wind_gust' AND $__timeFilter(time) GROUP BY 1) SELECT cast(cast(t.d AS date) AS varchar) AS \"Datum\", CASE WHEN s.smax >= 700 AND u.uvmax >= 6 THEN 'Sonnig, warm' WHEN t.tmax >= 25 THEN 'Warm' WHEN coalesce(r.rain, 0) >= 5 THEN 'Regnerisch' WHEN t.tmax <= 5 THEN 'Kalt' ELSE 'Unauffaellig' END AS \"Bewertung\", round(t.tmin, 1) AS \"T min °C\", round(t.tmax, 1) AS \"T max °C\", round(t.tavg, 1) AS \"T Mittel °C\", round(coalesce(r.rain, 0), 1) AS \"Regen mm\", round(u.uvmax, 1) AS \"UV max\", round(wg.wmax, 1) AS \"Böe max km/h\", cast(extract(epoch FROM t.d) AS bigint) * 1000 AS from_ms, cast(extract(epoch FROM t.d + INTERVAL '1 day') AS bigint) * 1000 AS to_ms FROM t LEFT JOIN r ON r.d = t.d LEFT JOIN u ON u.d = t.d LEFT JOIN s ON s.d = t.d LEFT JOIN wg ON wg.d = t.d ORDER BY t.d DESC" + } + ] + } + ] +}