Files
Smart-Dashboard/doku/meteogramm.md
T
adminandClaude Opus 5 b6ff7c6ad9 Doku: das Meteogramm beschrieben, falsche Quellenangabe richtiggestellt
doku/meteogramm.md: der Weg der Daten, welche Groessen links vom Jetzt-Strich
gemessen und welche vorhergesagt sind, die vier Tabellen samt der Regel, dass
nichts geloescht wird, das Tupel-Schema des Endpunkts, das Katalog-Vokabular
mit dem Dreizeiler "so kommt eine Spur dazu", und die beiden Entscheidungen
beim Zeichnen (ein SVG ueber alle Felder, Wolkenband als Farbverlauf statt
als Raster).

Dazu zwei Fallen, die sonst niemand wiederfindet: die Archiv-Schnittstelle
von Open-Meteo rechnet alle Zeiten mit dem heute gueltigen Zeitzonenversatz
um, und die Spaltenlage des Endpunkts steht an zwei Stellen und muss
zueinander passen.

Richtiggestellt: doku/datenbank.md und README.md nannten Open-Meteo als
Quelle von weatherHours/weatherDays. Das war nie so - es war OpenWeatherMap,
und seit dem 27.10.2024 gar nichts mehr. Beide Zeilen warfen ausserdem
Messung und Vorhersage in einen Topf; sie sind jetzt getrennt, mit dem
richtigen Schreiber je Tabelle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 14:07:07 +02:00

13 KiB
Raw Blame History

Das Meteogramm

Die Karte „Wetter“ auf der Solar-Seite: drei Felder mit gemeinsamer Zeitachse — Temperatur mit Wettersymbolen, Niederschlag mit Bewölkung nach Höhe, Wind mit Böen. Wie die Übersicht ist sie ein Katalog plus ein Renderer, und genau diese Trennung macht sie erweiterbar.

Das Meteogramm

Bis September 2026 stand hier ein Widget von meteoblue in einem <iframe>. Es passte farblich nicht zum Rest, ließ sich nicht ändern, wurde auf schmalen Schirmen als Ganzes heruntergerechnet — und konnte das eine nicht, was diese Anlage auszeichnet: zeigen, was die eigene Wetterstation gemessen hat. Links vom Jetzt-Strich steht deshalb die Messung, rechts die Vorhersage.

Datei Aufgabe
SolarManager/gatherForecastData.py holt die Vorhersage von Open-Meteo, alle 20 Minuten
SolarManager/wetterarchiv_nachtragen.py füllt das Archiv rückwirkend (einmalig)
solarLog_weather.sql die vier Tabellen samt Begründung
ajax/meteogramm.php verschmilzt Vorhersage und Messung zu einer Reihe
js/solar/meteogrammKatalog.js was gezeigt wird: Felder, Spuren, Bänder, Symbole
js/solar/meteogramm.js wie gezeichnet wird. Kennt keine Spur beim Namen
js/solar/solarMQTT.js setzt es auf und schaltet zwischen eigenem und meteoblue um

1. Der Weg der Daten

flowchart LR
  OM["Open-Meteo<br/><small>Vorhersage + ERA5-Archiv</small>"] --> G["gatherForecastData.py<br/><small>alle 20 Min.</small>"]
  G --> WH[("weatherHours<br/>weatherDays")]
  G --> FL[("weatherForecastLog<br/><small>erste Aussage je Stunde</small>")]
  ST["Wetterstation"] --> WS[("weatherStation<br/><small>alle 5 Min.</small>")]
  ST -->|MQTT| B
  WH --> A["ajax/meteogramm.php"]
  WS --> A
  A --> B["meteogramm.js<br/><small>+ meteogrammKatalog.js</small>"]

Zwei Quellen, ein Bild. Der Endpunkt legt beide übereinander — aber nicht vollständig, und das ist der Kern:

Größe links vom Jetzt-Strich rechts davon
Temperatur, Wind, Böe, Richtung, Feuchte, Druck gemessen (weatherStation) Vorhersage
Niederschlag, Bewölkung, Strahlung, Wettercode Vorhersage, nachgerechnet Vorhersage

Die Station hat weder Regenmesser noch Pyranometer noch Wolkenkamera. Für diese vier Größen bleibt es deshalb auch in der Vergangenheit bei Open-Meteo — dort allerdings bei der Analyse, nicht mehr bei der Vorhersage. Genau dafür holt der Sammler zwei Tage Vergangenheit mit.


2. Die Tabellen

Tabelle Inhalt Wächst um
weatherHours eine Zeile je Stunde, 2 bis +7 Tage, dauerhaft aufbewahrt 8.760 Zeilen/Jahr, ~1,5 MB
weatherDays eine Zeile je Tag: Sonnenzeiten, Min/Max, Tagessummen 365 Zeilen/Jahr
weatherTilted Einstrahlung in Modulebene, je Stunde und Fläche nur wenn Winkel konfiguriert
weatherForecastLog die erste Vorhersage je Stunde, unveränderlich 8.760 Zeilen/Jahr

Wie eine Stunde erwachsen wird. Schlüssel ist der Zeitpunkt, geschrieben wird mit REPLACE INTO: dieselbe Stunde kommt alle zwanzig Minuten wieder und wird dabei genauer — erst Vorhersage für übermorgen, zuletzt Analyse des vergangenen Tages. Woran man das sieht, steht in zwei Spalten: abgerufen sagt, wann zuletzt geschrieben wurde, ist_vorhersage, ob die Stunde dabei noch in der Zukunft lag.

Warum nichts gelöscht wird. Die Tabellen sind zugleich ein Archiv. Aus der Einstrahlung und EnergyFlow_hourly.pv_kwh (gemessener Ertrag je Stunde seit dem 02.04.2022) soll eine eigene Ertragsprognose entstehen — wer alte Zeilen wegräumt, wirft die Hälfte jedes Datenpaares weg. Deshalb stehen sie auch nicht in THIN_TABLES des Rollup-Jobs.

Warum weatherForecastLog extra. weatherHours hält immer den neuesten Stand — für die Anzeige richtig, für die Frage „wie gut war die Vorhersage?“ unbrauchbar, weil die Vorhersage dort längst von der Analyse überschrieben wurde. Hier landet per INSERT IGNORE, was zuerst über eine Stunde gesagt wurde. Die Differenz zu datetime ist der Vorlauf.

Eine Falle beim Nachtragen: Die Archiv-Schnittstelle rechnet alle Zeiten mit dem heute gültigen Zeitzonenversatz um — die Antwort für einen Dezembertag meldet selbst utc_offset_seconds: 7200. Sonnenaufgang am 21.12. käme so als 09:04 statt 08:04. Bei Stundenwerten fällt das nicht ins Gewicht (sie hängen an ihrem Zeitstempel), bei einer Uhrzeit schon. wetterarchiv_nachtragen.py schreibt Sonnenzeiten deshalb ausdrücklich nicht; gebraucht werden sie nur für die sichtbaren Tage, und die schreibt der Sammler richtig.

Was vorher da war

weatherHours und weatherDays gab es schon — in der Form der OpenWeatherMap-One-Call-2.5-API (weatherStr, icon, pop, uvi, moonrise, Wind in m/s, Bewölkung als ein Prozentwert). Ihr Schreiber liegt außerhalb beider Repos unter /volume1/web/gatherWeather.php, wurde von keiner Aufgabe mehr gestartet, und die API ist Ende 2024 abgeschaltet worden: letzte Zeile 27.10.2024. Gelesen hat sie zuletzt niemand.

Von 17 bzw. 29 Spalten wären keine fünf übriggeblieben, also wurden sie neu aufgebaut statt erweitert. Die alten liegen als weatherHours_alt und weatherDays_alt daneben und dürfen nach ein paar Wochen Ruhe weg.


3. Der Endpunkt

ajax/meteogramm.php?tage=5&rueck=24      tage 2…7, rueck 0…72 Stunden

Antwort wie im Haus üblich: Unix-Sekunden, Messreihen als Tupel statt Objekte — bei 200 Stunden mit 16 Feldern spart das ein Vielfaches.

{ "von":, "bis":, "jetzt":, "stand":,
  "stunden": [[t, gemessen, temp, regen, schnee, wind, boe, richtung,
               strahlung, wtief, wmittel, whoch, code, feuchte, druck, regenWkt], ],
  "tage":    [[t0, tmin, tmax, code, sonnenauf, sonnenunter], ] }

Die Spaltenlage steht zweimal: im Kopfkommentar des Endpunkts und als SPALTEN im Katalog. Beide müssen zueinander passen — neue Größen hängen deshalb hinten an, dann bleiben die Stellen bestehender Spuren gültig.

Drei Feinheiten, die man beim Lesen der SQL sonst übersieht:

  • MAX(gustSpeed), aber AVG(windSpeed). Open-Meteos Böe ist die Spitze der Stunde. Mit einem Mittelwert auf der Messseite spränge die Böenlinie genau am Jetzt-Strich.
  • Die Windrichtung wird über die Vektorsumme gemittelt (ATAN2(AVG(SIN(…)), AVG(COS(…)))). Der arithmetische Mittelwert aus 350° und 10° wäre 180° — die Gegenrichtung.
  • Sonnenzeiten kommen aus weatherDays, nicht aus daylight. daylight reicht nur bis morgen, das Meteogramm bis zu sieben Tage.

4. Katalog und Renderer

Der Renderer kennt kein Feld, keine Spur und keine Farbe beim Namen. Er liest nur, was der Katalog deklariert.

So kommt eine Spur dazu

  1. Spalte in ajax/meteogramm.php hinten anhängen
  2. Schlüssel in SPALTEN hinten anhängen
  3. Eintrag in spuren des passenden Feldes

Sonst nichts:

{
  id: "wind", name: "Wind", spalte: "wind",
  achse: "links", art: "linie", farbe: FARBE.wind, breit: 2.2,
  legende: true,
  format: w => komma(w, 1) + " km/h",
},

Ein Feld ist ein waagerechter Streifen mit eigener senkrechter Achse; alle teilen sich die Zeitachse. Es hat links/rechts (Achsen), nacht (Schattierung), symbole, pfeile, baender und spuren. Eine Spur hat art (linie · flaeche · balken), farbe, format() und die Filter nurGemessen / nurVorhersage.

Datenfarben stehen im Katalog, Strukturfarben im Stylesheet — dieselbe Regel wie beim Energiefluss.

Die drei Felder

Feld Links Rechts Dazu
Temperatur °C 01000 W/m² (Strahlung) Wettersymbole, Tag/Nacht, Tagesmin/-max
Niederschlag und Bewölkung mm/h 014 km (Wolkenband) Tag/Nacht
Wind km/h Richtungspfeile

Warum die Strahlung kein eigenes Feld hat: gleicher Tagesrhythmus, gleiche Nacht-Schattierung wie die Temperatur — und ein viertes Feld machte das Modul am Handy höher als das externe Widget, also genau das Gegenteil des Ziels. Über den Katalog ist es ein Zweizeiler, sie später auszulagern.

Zwei Entscheidungen beim Zeichnen

Ein einziges SVG über alle drei Felder. Dadurch gibt es genau eine X-Skala, und Nachtstreifen, Tagesgrenzen, Jetzt-Linie und der Zeiger laufen ohne Kunstgriff durch alle Felder. Jedes Feld ist ein <g transform= "translate(0, oben)"> mit eigener Y-Abbildung. Auch der Tooltip gibt es deshalb nur einmal — er zeigt alle Spuren aller Felder zu einer Stunde.

Das Wolkenband ist ein Farbverlauf, kein Raster.

je Stunde ein Rechteck          je Schicht ein Rechteck mit Verlauf
▐▌▐▌▌▐▐▌▌▐▌▐▐▌▌▐▌▐▌▐▐▌          ▓▓▒▒░░▒▒▓▓▓▒░░░▒▒▓▓▓▒
zweihundert harte Kanten        dieselben Zahlen, weich verbunden

Der erste Versuch zeichnete je Stunde und Schicht ein Rechteck und sah aus wie Fernsehrauschen. Der Verlauf setzt einen Haltepunkt je Stunde und blendet dazwischen über — Bewölkung springt ohnehin nicht zur vollen Stunde um.

Und eine Vereinfachung, die man kennen sollte: Open-Meteo liefert kein stufenloses Höhenprofil, sondern drei Schichten (tief 02 km, mittel 27 km, hoch 714 km). Mehr Auflösung hat die Quelle nicht.

Am Handy

Unter 520 px schalten die Feldhöhen auf schmal (120/100/92 statt 150/130/115) — zusammen rund 345 px statt der 500 px des eingebetteten Widgets. Außerdem startet das Modul dort mit zwei Tagen statt fünf: auf 358 nutzbaren Pixeln blieben für fünf Tage drei Pixel je Stunde.

Wettersymbole und Windpfeile dünnen sich selbst aus: beide verdoppeln ihre Schrittweite, bis der Abstand mindestAbstand erreicht. Das ist die eine Regel, die am Handy alles rettet.

touch-action: pan-y auf dem SVG ist Pflicht — sonst fängt der Tooltip die Wischgeste ab und die Seite lässt sich nicht mehr scrollen.


5. Umschalter und Einbau

Die Karte trägt zwei Flächen: id="meteogramm" (zwei m, das eigene Modul) und id="meteogram" (ein m, das Ziel des meteoblue-Rahmens — so bleibt mountMeteogram() in common.js unverändert). Umgeschaltet wird über data-mtg-quelle, nicht über data-enf-ansicht: zeigeAnsicht() in solarMQTT.js greift mit document.querySelectorAll seitenweit zu und würde sonst die Realtime-Karte mitschalten.

mountMeteogram() läuft jetzt erst beim ersten Umschalten auf meteoblue. Wer es nie ansieht, lädt das fremde Dokument auch nicht mehr — nebenbei der größte Ladezeitgewinn dieser Karte.

Die Karte heißt „Wetter“ und nicht mehr „Forecast“: direkt darunter steht bereits eine zweite Karte dieses Namens, die aus simPower die PV-Ernte vorhersagt.


6. Fehlerbilder

Beobachtung Wahrscheinliche Ursache
„Noch keine Vorhersage vorhanden“ gatherForecastData.py läuft nicht — ps aux | grep gatherForecast, dann forecastOutput.log
Fuß färbt sich gelb, „Stand“ liegt Stunden zurück Sammler läuft, kommt aber nicht an Open-Meteo heran
Links vom Jetzt-Strich fehlt die gemessene Kurve weatherStation bekommt nichts mehr — das schreibt die Station selbst über /volume1/web/weatherStation.php, kein Skript in den Repos
Bewölkung fehlt in der Vergangenheit past_days in der config.ini steht auf 0
Wolkenband bleibt leer Die Zeilen stammen aus dem Nachtragen und haben keine Schichtwerte — Archiv und laufende Vorhersage liefern dieselben Felder, prüfen mit SELECT wolken_tief … WHERE datetime = …
Symbole überlappen mindestAbstand im Katalog zu klein für diese Breite

7. Wo fange ich an, wenn ich …

Vorhaben Ort
… eine weitere Spur zeigen drei Zeilen, siehe Abschnitt 4
… die Farben ändern FARBE in meteogrammKatalog.js
… ein Feld höher machen HOEHEN im Katalog, je Breite getrennt
… einen weiteren Zeitraum anbieten zeitraeume im Katalog — der Endpunkt klemmt auf 2…7 Tage
… ein anderes Wettersymbol WETTER[code] im Katalog; Codepunkte stehen in css/bootstrap-icons.min.css
… öfter abrufen intervall_minuten in [vorhersage] der config.ini — öfter als stündlich rechnet Open-Meteo nicht
… die Einstrahlung in Modulebene flaechen = dach:30:10, … in [vorhersage]; füllt weatherTilted, rückwirkend über wetterarchiv_nachtragen.py
… das Meteogramm auch auf der Wetterseite restricted/weather.php bekommt dieselben zwei Flächen, index.php die zwei Skripte, weatherMQTT.js den Aufruf
… meteoblue endgültig loswerden Umschalter aus solar.php, "meteogram" => false in index.php, js/meteogram.js und mountMeteogram() in common.js