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>
This commit is contained in:
@@ -22,6 +22,7 @@ Zusammenspiel mehrerer Prozesse entsteht. Sie haben eigene, ausführliche
|
||||
Dokumente unter [`doku/`](doku/README.md):
|
||||
**[Automatiken](doku/automatiken.md)**, **[Zeitleiste](doku/zeitleiste.md)**,
|
||||
**[Benachrichtigungen](doku/benachrichtigungen.md)**, **[Batterie](doku/byd.md)**,
|
||||
**[Meteogramm](doku/meteogramm.md)**,
|
||||
**[Solar-Übersicht](doku/uebersicht.md)**, **[Datenbanken](doku/datenbank.md)**
|
||||
und **[Einstellungsseite](doku/einstellungen.md)**.
|
||||
|
||||
@@ -191,12 +192,12 @@ genau die Topics, die auf den Kacheln stehen (`tileTopics()` in `rooms.php`).
|
||||
|
||||
| Seite (`?action=`) | Vorlage | Seitenskripte | holt per fetch | hört auf MQTT |
|
||||
|---|---|---|---|---|
|
||||
| `solar` (Vorgabe) | `solar.php` | `jahresstatistik.js`, `anlageKatalog.js`, `anlage.js`, `speicher.js`, `energieflussKatalog.js`, `energiefluss.js`, `bewaesserungAnzeige.js`, `ladefenster.js`, `bedienfenster.js`, `solarMQTT.js` | `speicher`, `getProdData`, `getConsData`, `getForecastData`, `getSunrise`, `getStats`, `carEG`, `carOG`, `heater`, `watering` | `solarManager/#`, `weatherStation/#`, `wattpilot/#`, `go-eCharger/#`, `Gartenwasser/#` |
|
||||
| `solar` (Vorgabe) | `solar.php` | `jahresstatistik.js`, `anlageKatalog.js`, `anlage.js`, `speicher.js`, `meteogrammKatalog.js`, `meteogramm.js`, `energieflussKatalog.js`, `energiefluss.js`, `bewaesserungAnzeige.js`, `ladefenster.js`, `bedienfenster.js`, `solarMQTT.js` | `speicher`, `meteogramm`, `getProdData`, `getConsData`, `getForecastData`, `getSunrise`, `getStats`, `carEG`, `carOG`, `heater`, `watering` | `solarManager/#`, `weatherStation/#`, `wattpilot/#`, `go-eCharger/#`, `Gartenwasser/#` |
|
||||
| `home` | `home.php` | `autoActionFuncs.js`, `zeitleiste.js`, `raumfenster.js`, `homeMQTT.js` | `room.php`, `AutoAction.php` | die Topics der Kacheln, `Raumtemp/#` |
|
||||
| `heat` | `heat.php` | `heatMQTT.js` | `getHeaterData`, `getWaterData`, `getSunrise` | `solarManager/#`, `weatherStation/#`, Wallbox-Topics |
|
||||
| `history` | `history.php` | `jahresstatistik.js`, `historyMQTT.js` | `energyHistory` (6 ×), `getStats` | — |
|
||||
| `skoda` | `skoda.php` | `skodaMQTT.js` | `skoda.php?was=…`, `skodaCmd.php`, `tile.php` | `solarManager/#` |
|
||||
| `weather` | `weather.php` | `weatherMQTT.js` | nichts — das Meteogramm ist ein fremdes Dokument im `<iframe>` (`js/meteogram.js`) | `weatherStation/#` |
|
||||
| `weather` | `weather.php` | `weatherMQTT.js` | nichts — hier steht noch das eingebettete Meteogramm von meteoblue (`js/meteogram.js`); das eigene gibt es bisher nur auf der Solar-Seite | `weatherStation/#` |
|
||||
| `settings` | `settings.php` | `settings.js`, `grundriss.js`, `energieflussKatalog.js`, `energiefluss.js`, `energieflussEinstellungen.js`, `benachrichtigungen.js` | `settings.php?action=…`, `push.php?action=…` | — |
|
||||
| `logs` | `logs.php` | `logs.js` | `logs.php?action=…` | — |
|
||||
| `einfuehrung` | `einfuehrung.php` | `einfuehrung.js` | nichts — Folien und Bilder stehen in der Seite | — |
|
||||
@@ -395,6 +396,18 @@ der Wechselrichter weiß. Die Nennenergie je Modul (2,76 kWh, HVM) steht als
|
||||
Konstante oben in `speicher.js` — die BMU meldet sie nicht. Wie die Werte
|
||||
aus der BMU herauskommen, steht in **[doku/byd.md](doku/byd.md)**.
|
||||
|
||||
**Karte „Wetter“** — `meteogramm.js` zeichnet aus `meteogrammKatalog.js` drei
|
||||
Felder mit gemeinsamer Zeitachse: Temperatur mit Wettersymbolen und
|
||||
Tag/Nacht-Schattierung, Niederschlag mit der Bewölkung als Höhenband,
|
||||
Wind mit Böen und Richtungspfeilen; dazu die Globalstrahlung auf der rechten
|
||||
Achse des ersten Feldes. Umschaltbar 2 / 5 / 7 Tage, am Handy mit 2 Tagen
|
||||
startend. **Links vom Jetzt-Strich stehen die gemessenen Werte der eigenen
|
||||
Station**, rechts die Vorhersage aus `ajax/meteogramm.php`
|
||||
(`weatherHours`/`weatherDays`, gefüllt von `gatherForecastData.py`). Das
|
||||
eingebettete Meteogramm von meteoblue ist über einen Umschalter weiter
|
||||
erreichbar und wird erst beim Hinschalten geladen. Einzelheiten in
|
||||
**[doku/meteogramm.md](doku/meteogramm.md)**.
|
||||
|
||||
**`skodaMQTT.js`** — Fahrzeugseite. Holt alles über `ajax/skoda.php?was=…`
|
||||
(live, ladungen, kurve, gesundheit, fahrten, strecke), zeichnet das Fahrzeug
|
||||
als SVG-Draufsicht mit Ladestand im Akku, Ladeknopf und Klimaknopf, und die
|
||||
@@ -456,6 +469,7 @@ klein: Diagramme füllen bzw. Logzeilen nachladen und einfärben.
|
||||
| `energyHistory.php` | `?series=prod\|cons&range=month\|year\|decade` | Verbrauchs-/Erzeugungsverlauf (Rohdaten oder Stundenarchiv) |
|
||||
| `getProdData` `getConsData` `getForecastData` `getHeaterData` `getWaterData` | `?FROM=&TO=` | Chart.js-Datensätze für die jeweilige Karte |
|
||||
| `getSunrise.php` | `?FROM=&TO=` | Sonnenauf- und -untergänge als Diagramm-Markierungen |
|
||||
| `meteogramm.php` | `?tage=2..7&rueck=0..72` | Vorhersage und gemessene Stunden zu einer Reihe verschmolzen, für das Meteogramm |
|
||||
| `skoda.php` | `?was=live\|ladungen\|kurve\|gesundheit\|fahrten\|strecke` | Fahrzeugauswertungen aus `solarLog.skoda` |
|
||||
| `skodaCmd.php` | `POST befehl=…` | Laden/Klima/Lüftung am Fahrzeug |
|
||||
| `tile.php` | `?z=&x=&y=` | Kartenkachel aus dem eigenen Zwischenspeicher, sonst einmalig von OSM |
|
||||
@@ -556,7 +570,9 @@ erDiagram
|
||||
| `skoda`, `skoda_raw` | Fahrzeugzustand im Verlauf | `gatherSkodaData.py` |
|
||||
| `skoda_ladepunkte` | Wallbox-Verlauf während einer Ladung, im Minutentakt, wird nicht ausgedünnt (`solarLog_skoda_ladepunkte.sql`) | `gatherSkodaData.py`; ältere Ladungen `skoda_ladepunkte_nachtragen.py` |
|
||||
| `byd`, `byd_zellen` | BYD-Speicher direkt aus der BMU: Ladestand, SOH, Temperaturen, Spreizung, Zähler alle 5 Minuten; alle 128 Zellspannungen und 64 Temperaturen alle 15 Minuten (`solarLog_byd.sql`) | `gatherBYDData.py` |
|
||||
| `weatherStation`, `weatherHours`, `weatherDays`, `daylight`, `simPower` | Wetter, Sonnenzeiten, Ertragsprognose | Wetterbrücke, Open-Meteo, Prognose |
|
||||
| `weatherStation` | die eigene Wetterstation, alle 5 Minuten | die Station selbst (`/volume1/web/weatherStation.php`, außerhalb der Repos) |
|
||||
| `weatherHours`, `weatherDays`, `weatherTilted`, `weatherForecastLog` | Wettervorhersage und Archiv; nichts wird gelöscht (`solarLog_weather.sql`, → [doku/meteogramm.md](doku/meteogramm.md)) | `gatherForecastData.py` (Open-Meteo), einmalig `wetterarchiv_nachtragen.py` |
|
||||
| `daylight`, `simPower` | Sonnenzeiten (nur bis morgen), Ertragsprognose | `solarManager.py`; Solcast über `gatherSolar.php` |
|
||||
| `gridCosts`, `gasCosts`, `fuelCosts` | Preiszeitreihen (Stichtag, kein Enddatum) | Einstellungsseite (`costs.php`) |
|
||||
|
||||
**`Logins`** — `users` (Passkeys) und `addUser` (Einmal-Links).
|
||||
|
||||
@@ -14,6 +14,7 @@ abzulesen sind.
|
||||
| [automatiken.md](automatiken.md) | Die Automatiken (AutoActions): Datenmodell, Editor, Runner, wie eine Bedingung wirklich ausgewertet wird, Verkettung, Sperren, Fehlerbilder |
|
||||
| [zeitleiste.md](zeitleiste.md) | Die Zeitleiste der Automatiken: wie der Server den Tag ausrechnet und wie der Browser ihn zeichnet |
|
||||
| [benachrichtigungen.md](benachrichtigungen.md) | Meldungen aus Automatiken: Web Push ohne App, E-Mail, das Gerät „Benachrichtigungen“ |
|
||||
| [meteogramm.md](meteogramm.md) | Das eigene Meteogramm: Vorhersage und eigene Messung in einem Bild, Katalog und Renderer, das Wetterarchiv |
|
||||
| [uebersicht.md](uebersicht.md) | Die Solar-Übersicht: Katalog und Renderer, wie aus Watt ein Ring, ein Fluss und ein Füllstand wird |
|
||||
| [byd.md](byd.md) | Die Batterie bis zur Zelle: das nachgebaute BMU-Protokoll, das Zeitfenster des Netzwerkmoduls, Byte-Lagen, Historie, Anzeige |
|
||||
| [datenbank.md](datenbank.md) | Die drei Schemata: wer was schreibt, wie Messreihen verdichtet werden, welche Tabelle man für welche Auswertung nimmt |
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
+9
-4
@@ -98,9 +98,11 @@ Stand heute rund 170 MB, verteilt auf wenige große Reihen:
|
||||
| `puffertemp` | ~247.000 | Puffertemperaturen | `solarManager.py` |
|
||||
| `wasser`, `zisterne` | ~300.000 | Wasserzähler und Zisterne | `gatherWaterData.py` |
|
||||
| `windrad`, `kiga_windrad` | ~255.000 | Windräder | `solarManager.py` |
|
||||
| `weatherStation`, `weatherHours`, `weatherDays` | ~220.000 | eigene Wetterstation, Vorhersage | Wetterbrücke, Open-Meteo |
|
||||
| `daylight` | 663 | Sonnenauf- und -untergang je Tag | Vorhersage-Job |
|
||||
| `simPower` | ~82.000 | Ertragsprognose | Prognose-Job |
|
||||
| `weatherStation` | ~195.000 | die eigene Wetterstation, alle 5 min | die Station selbst, über `/volume1/web/weatherStation.php` |
|
||||
| `weatherHours`, `weatherDays` | ~40.000 / 1.600 | Wettervorhersage **und Archiv**, stündlich bzw. täglich (→ [meteogramm.md](meteogramm.md)) | `gatherForecastData.py` (Open-Meteo) |
|
||||
| `weatherTilted`, `weatherForecastLog` | je nach Nutzung | Einstrahlung in Modulebene; die erste Vorhersage je Stunde | `gatherForecastData.py` |
|
||||
| `daylight` | 663 | Sonnenauf- und -untergang, nur bis morgen | `solarManager.py` (gerechnet, nicht abgerufen) |
|
||||
| `simPower` | ~82.000 | Ertragsprognose, 7 Tage voraus | Solcast, 4×/Tag über `/volume1/web/gatherSolar.php` |
|
||||
| `byd`, `byd_zellen` | 1.559 / 492 | BYD-Speicher: alle 5 min Ladestand, SOH, Temperaturen; alle 15 min 128 Zellspannungen und 64 Temperaturen (→ [byd.md](byd.md)) | `gatherBYDData.py` |
|
||||
| `skoda`, `skoda_raw`, `skoda_ladepunkte` | ~1.900 | Fahrzeugzustand, Rohantwort, Ladeverlauf im Minutentakt | `gatherSkodaData.py` |
|
||||
| `gridCosts`, `gasCosts`, `fuelCosts` | 13 | Preiszeitreihen | Einstellungsseite |
|
||||
@@ -146,7 +148,10 @@ Wichtig beim Auswerten:
|
||||
| `status` | zeigt, bis wohin aggregiert und ab wann ausgedünnt ist |
|
||||
|
||||
Ausgedünnt werden `EnergyFlow`, `Heater` und `weatherStation`; das Zielraster
|
||||
beträgt 15 Minuten. Was man beim Rechnen auf den Rohdaten wissen muss, steht
|
||||
beträgt 15 Minuten. **`weatherHours` und `weatherDays` stehen bewusst nicht
|
||||
dabei** — sie sind schon stündlich, und ihre Strahlungswerte sind die eine
|
||||
Hälfte des Datenpaares für eine künftige Ertragsprognose (die andere ist
|
||||
`EnergyFlow_hourly.pv_kwh`). Was man beim Rechnen auf den Rohdaten wissen muss, steht
|
||||
ausführlich im Kopf der SQL-Datei — die wichtigsten Punkte:
|
||||
|
||||
* **Alles sind Momentanleistungen**, gemittelt über rund fünf Minuten. kWh
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
# 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.
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||
```mermaid
|
||||
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.
|
||||
|
||||
```json
|
||||
{ "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:
|
||||
|
||||
```js
|
||||
{
|
||||
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 | 0–1000 W/m² (Strahlung) | Wettersymbole, Tag/Nacht, Tagesmin/-max |
|
||||
| Niederschlag und Bewölkung | mm/h | 0–14 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 0–2 km, mittel
|
||||
2–7 km, hoch 7–14 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` |
|
||||
Reference in New Issue
Block a user