Eigenes Meteogramm statt des eingebetteten von meteoblue
Das Widget von meteoblue war ein fremdes Dokument im iframe: es passte farblich nicht, liess sich nicht aendern, wurde auf schmalen Schirmen als Ganzes heruntergerechnet und konnte die eigenen Messwerte nicht zeigen. Genau das kann das neue Modul - links vom Jetzt-Strich steht, was die Wetterstation gemessen hat, rechts die Vorhersage. Aufgebaut wie die uebrigen Anzeige-Module: meteogrammKatalog.js sagt, WAS gezeigt wird, meteogramm.js nur, WIE. Der Renderer kennt kein Feld und keine Spur beim Namen. Eine weitere Spur ist damit ein Objekt im Katalog und keine Codeaenderung. Ein einziges SVG ueber alle drei Felder, damit Zeitachse, Nachtstreifen, Jetzt-Linie und Zeiger nur einmal existieren und ueberall gleich sitzen. Zwei Entscheidungen, die man sonst sucht: Das Wolkenband zeichnet je Schicht ein Rechteck mit waagerechtem Farbverlauf. Ein Rechteck je Stunde war der erste Versuch und sah aus wie Fernsehrauschen - zweihundert harte Kanten nebeneinander. Open-Meteo kennt ohnehin nur drei Schichten statt eines Hoehenprofils. Die Strahlung teilt sich das Feld mit der Temperatur, statt ein viertes zu bekommen: gleicher Tagesrhythmus, gleiche Nacht-Schattierung, und ein viertes Feld machte das Ganze am Handy hoeher als das externe Widget. Die alte Einbindung bleibt vorerst und ist umschaltbar. mountMeteogram() laeuft jetzt erst beim Umschalten - wer meteoblue nie ansieht, laedt es auch nicht mehr. Die Karte heisst "Wetter" statt "Forecast": darunter steht bereits eine zweite Karte dieses Namens. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
-- ===========================================================================
|
||||
-- Wetter: Vorhersage und Archiv (Open-Meteo)
|
||||
--
|
||||
-- Einspielen:
|
||||
-- mysql -h 127.0.0.1 -P 3310 -u <user> -p solarLog < solarLog_weather.sql
|
||||
--
|
||||
-- Geschrieben von SolarManager/gatherForecastData.py (laufend) und einmalig
|
||||
-- von SolarManager/wetterarchiv_nachtragen.py. Gelesen von ajax/meteogramm.php.
|
||||
--
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Warum die alten Tabellen nicht erweitert, sondern neu aufgebaut werden
|
||||
--
|
||||
-- weatherHours und weatherDays hatten die Form der OpenWeatherMap-One-Call-
|
||||
-- Antwort: weatherStr, icon ("04n"), pop, uvi, feelsLike, moonrise, mooncycle,
|
||||
-- Wind in m/s, und Bewoelkung als ein einziger Prozentwert. Geschrieben hat
|
||||
-- sie /volume1/web/gatherWeather.php - ausserhalb beider Repositorien, von
|
||||
-- keiner Aufgabe mehr gestartet, seit dem 27.10.2024 tot, weil die kostenlose
|
||||
-- One-Call-2.5-API abgeschaltet wurde. Gelesen hat sie zuletzt niemand: im
|
||||
-- ganzen Web-Repository gibt es keinen einzigen SELECT darauf.
|
||||
--
|
||||
-- Von siebzehn beziehungsweise neunundzwanzig Spalten waeren keine fuenf
|
||||
-- uebriggeblieben. Ein ALTER TABLE haette die Ruine stehengelassen.
|
||||
--
|
||||
-- Die Namen bleiben trotzdem: "stuendliches Wetter" und "Wetter je Tag"
|
||||
-- beschreiben den neuen Inhalt genauso gut, die Dokumentation bleibt gueltig,
|
||||
-- und zwei aehnlich heissende Tabellen nebeneinander verwirren mehr, als ein
|
||||
-- neuer Name nuetzt. Die alten werden vorher umbenannt, nicht geloescht - die
|
||||
-- 25 MB eilen nicht, ein DROP ist nicht rueckholbar. Nach ein paar Wochen
|
||||
-- Ruhe duerfen weatherHours_alt und weatherDays_alt weg.
|
||||
--
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Warum nichts geloescht wird
|
||||
--
|
||||
-- Diese Tabellen sind nicht nur Anzeigevorrat, sondern ein Archiv. Aus den
|
||||
-- Strahlungswerten der Vergangenheit soll spaeter eine eigene Ertragsprognose
|
||||
-- gerechnet werden: EnergyFlow_hourly.pv_kwh liefert den gemessenen Ertrag je
|
||||
-- Stunde seit dem 02.04.2022, hier steht die zugehoerige Einstrahlung. Wer
|
||||
-- alte Zeilen wegraeumt, wirft genau die Haelfte des Datenpaares weg.
|
||||
--
|
||||
-- Der Platz spricht nicht dagegen: 8.760 Zeilen im Jahr, rund 1,5 MB. Ein
|
||||
-- einziger Tag EnergyFlow ist groesser. Die Tabellen kommen deshalb auch
|
||||
-- nicht in THIN_TABLES des Rollup-Jobs - sie sind schon stuendlich, da gibt
|
||||
-- es nichts auszuduennen.
|
||||
--
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Wie eine Stunde erwachsen wird
|
||||
--
|
||||
-- Der Schluessel ist der Zeitpunkt, nicht eine laufende Nummer, und
|
||||
-- geschrieben wird mit REPLACE INTO. Dieselbe Stunde kommt alle zwanzig
|
||||
-- Minuten erneut herein und wird dabei genauer: erst als Vorhersage von
|
||||
-- uebermorgen, zuletzt als Analyse des vergangenen Tages. Angehaengt wird
|
||||
-- nichts, ueberschrieben alles.
|
||||
--
|
||||
-- Damit man einer Zeile trotzdem ansieht, woher sie stammt, tragen beide
|
||||
-- Tabellen zwei Spalten mit: "abgerufen" sagt, wann sie zuletzt geschrieben
|
||||
-- wurde, "ist_vorhersage" sagt, ob die Stunde zu diesem Zeitpunkt noch in der
|
||||
-- Zukunft lag. Eine Zeile mit ist_vorhersage = 0 ist nachgerechnet und damit
|
||||
-- die belastbare Haelfte fuer eine Korrelation.
|
||||
--
|
||||
-- Was dabei verlorenginge, faengt weatherForecastLog auf: dort landet je
|
||||
-- Stunde die *erste* Vorhersage und wird nie wieder angefasst (INSERT
|
||||
-- IGNORE). Damit laesst sich spaeter beantworten, wie gut eine Vorhersage von
|
||||
-- vor drei Tagen war, ohne dafuer erst jahrelang zu sammeln.
|
||||
--
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Bewoelkung und Strahlung
|
||||
--
|
||||
-- Open-Meteo kennt kein stufenloses Hoehenprofil der Bewoelkung, sondern drei
|
||||
-- Schichten. Daraus zeichnet das Meteogramm drei Baender - 0-2, 2-7 und
|
||||
-- 7-14 km -, deren Graustufe der Bedeckungsgrad ist. Mehr Hoehenaufloesung
|
||||
-- gibt die Quelle nicht her; das ist eine Vereinfachung gegenueber dem
|
||||
-- Vorbild und keine Nachlaessigkeit.
|
||||
--
|
||||
-- Die Strahlung steht hier ausrichtungsfrei: global auf die Waagerechte
|
||||
-- (strahlung), sowie ihre Zerlegung in direkt und diffus. Was ein geneigtes
|
||||
-- Dach davon abbekommt, haengt an Neigung und Azimut und steht deshalb in
|
||||
-- weatherTilted - eine Zeile je Stunde und Flaeche. Eine zusaetzliche Flaeche
|
||||
-- ist damit eine Zeile in der config.ini und keine Spalte hier.
|
||||
-- ===========================================================================
|
||||
|
||||
-- Erst beiseite, dann neu. IF EXISTS (MariaDB ab 10.5) fuer den Fall, dass es
|
||||
-- die Altlast nie gab.
|
||||
--
|
||||
-- Diese beiden Zeilen gelten genau einmal: laeuft das Skript ein zweites Mal,
|
||||
-- bricht es hier mit "weatherHours_alt already exists" ab - dann ist die
|
||||
-- Umbenennung laengst passiert, und man laesst die beiden Zeilen weg. Das ist
|
||||
-- ein verstaendlicher Abbruch und lieber so als eine Automatik, die im
|
||||
-- Zweifel die frisch gefuellte Tabelle beiseiteschiebt.
|
||||
RENAME TABLE IF EXISTS `weatherHours` TO `weatherHours_alt`;
|
||||
RENAME TABLE IF EXISTS `weatherDays` TO `weatherDays_alt`;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `weatherHours` (
|
||||
`datetime` datetime NOT NULL COMMENT 'Ortszeit, volle Stunde',
|
||||
`temp` decimal(4,1) DEFAULT NULL COMMENT 'degC in 2 m (temperature_2m)',
|
||||
`gefuehlt` decimal(4,1) DEFAULT NULL COMMENT 'degC gefuehlt (apparent_temperature)',
|
||||
`feuchte` tinyint(3) unsigned DEFAULT NULL COMMENT '% rel. (relativehumidity_2m)',
|
||||
`taupunkt` decimal(4,1) DEFAULT NULL COMMENT 'degC (dewpoint_2m)',
|
||||
`druck` smallint(5) unsigned DEFAULT NULL COMMENT 'hPa auf Meereshoehe (pressure_msl) - so wie weatherStation.qff, damit beide vergleichbar sind',
|
||||
`regen` decimal(4,1) DEFAULT NULL COMMENT 'mm in dieser Stunde (precipitation)',
|
||||
`regen_wkt` tinyint(3) unsigned DEFAULT NULL COMMENT '% Wahrscheinlichkeit (precipitation_probability)',
|
||||
`schnee` decimal(4,1) DEFAULT NULL COMMENT 'cm in dieser Stunde (snowfall)',
|
||||
`wettercode` tinyint(3) unsigned DEFAULT NULL COMMENT 'WMO 0..99 (weathercode) - bestimmt das Symbol',
|
||||
`wolken_tief` tinyint(3) unsigned DEFAULT NULL COMMENT '% Bedeckung 0-2 km (cloudcover_low)',
|
||||
`wolken_mittel` tinyint(3) unsigned DEFAULT NULL COMMENT '% Bedeckung 2-7 km (cloudcover_mid)',
|
||||
`wolken_hoch` tinyint(3) unsigned DEFAULT NULL COMMENT '% Bedeckung 7-14 km (cloudcover_high)',
|
||||
`wind` decimal(4,1) DEFAULT NULL COMMENT 'km/h in 10 m, Mittel der Stunde (windspeed_10m)',
|
||||
`boe` decimal(4,1) DEFAULT NULL COMMENT 'km/h, Spitze der Stunde (windgusts_10m)',
|
||||
`richtung` smallint(5) unsigned DEFAULT NULL COMMENT 'Grad, woher der Wind kommt (winddirection_10m)',
|
||||
`strahlung` smallint(5) unsigned DEFAULT NULL COMMENT 'W/m2 global auf die Waagerechte (shortwave_radiation)',
|
||||
`direkt` smallint(5) unsigned DEFAULT NULL COMMENT 'W/m2 direkter Anteil (direct_radiation)',
|
||||
`diffus` smallint(5) unsigned DEFAULT NULL COMMENT 'W/m2 diffuser Anteil (diffuse_radiation)',
|
||||
`tag` tinyint(1) NOT NULL DEFAULT 1 COMMENT '1 = Tag, 0 = Nacht (is_day) - entscheidet Sonne oder Mond im Symbol',
|
||||
`ist_vorhersage` tinyint(1) NOT NULL DEFAULT 1 COMMENT '1 = beim Schreiben lag die Stunde noch in der Zukunft, 0 = nachgerechnet',
|
||||
`abgerufen` datetime NOT NULL COMMENT 'wann diese Zeile zuletzt geschrieben wurde',
|
||||
PRIMARY KEY (`datetime`),
|
||||
KEY `ist_vorhersage` (`ist_vorhersage`, `datetime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `weatherDays` (
|
||||
`date` date NOT NULL COMMENT 'Ortszeit',
|
||||
`wettercode` tinyint(3) unsigned DEFAULT NULL COMMENT 'WMO, praegendes Wetter des Tages',
|
||||
`temp_min` decimal(4,1) DEFAULT NULL COMMENT 'degC (temperature_2m_min)',
|
||||
`temp_max` decimal(4,1) DEFAULT NULL COMMENT 'degC (temperature_2m_max)',
|
||||
`sonnenauf` time DEFAULT NULL COMMENT 'Ortszeit - Quelle der Tag/Nacht-Schattierung, denn daylight reicht nur bis morgen',
|
||||
`sonnenunter` time DEFAULT NULL COMMENT 'Ortszeit',
|
||||
`regen_summe` decimal(5,1) DEFAULT NULL COMMENT 'mm (precipitation_sum)',
|
||||
`regen_wkt` tinyint(3) unsigned DEFAULT NULL COMMENT '% (precipitation_probability_max)',
|
||||
`wind_max` decimal(4,1) DEFAULT NULL COMMENT 'km/h (windspeed_10m_max)',
|
||||
`boe_max` decimal(4,1) DEFAULT NULL COMMENT 'km/h (windgusts_10m_max)',
|
||||
`strahlung_summe` decimal(6,2) DEFAULT NULL COMMENT 'MJ/m2 am Tag (shortwave_radiation_sum) - Vergleichsgroesse zur Tagesernte',
|
||||
`ist_vorhersage` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'wie bei weatherHours',
|
||||
`abgerufen` datetime NOT NULL,
|
||||
PRIMARY KEY (`date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
|
||||
-- Einstrahlung in Modulebene, je Flaeche eine Zeile.
|
||||
--
|
||||
-- Eine eigene Tabelle und keine Spalten in weatherHours: die Anlage hat drei
|
||||
-- Flaechen mit verschiedener Neigung und Ausrichtung (Dach, Veranda,
|
||||
-- Carport), und eine vierte soll keine Schemaaenderung kosten. Welche
|
||||
-- Flaechen es gibt, sagt allein die config.ini des Sammlers.
|
||||
--
|
||||
-- Gefuellt wird erst, wenn dort Neigung und Azimut eingetragen sind. Ohne
|
||||
-- Eintrag bleibt die Tabelle leer, und das Meteogramm merkt nichts davon -
|
||||
-- es zeichnet die waagerechte Strahlung aus weatherHours. Nachtragen laesst
|
||||
-- sich das jederzeit: die Archiv-API liefert auch die geneigte Einstrahlung
|
||||
-- rueckwirkend.
|
||||
CREATE TABLE IF NOT EXISTS `weatherTilted` (
|
||||
`datetime` datetime NOT NULL COMMENT 'Ortszeit, volle Stunde - zeigt auf weatherHours',
|
||||
`flaeche` varchar(20) NOT NULL COMMENT 'Kuerzel wie im Anlagenkatalog: dach, veranda, carport',
|
||||
`strahlung` smallint(5) unsigned DEFAULT NULL COMMENT 'W/m2 in Modulebene (global_tilted_irradiance)',
|
||||
PRIMARY KEY (`datetime`, `flaeche`),
|
||||
KEY `flaeche` (`flaeche`, `datetime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
|
||||
-- Die erste Vorhersage je Stunde, unveraenderlich.
|
||||
--
|
||||
-- weatherHours haelt immer den neuesten Stand - das ist fuer die Anzeige
|
||||
-- richtig und fuer die Frage "wie gut war die Vorhersage?" unbrauchbar, weil
|
||||
-- die Vorhersage dort laengst von der Analyse ueberschrieben wurde. Hier
|
||||
-- landet deshalb, was zuerst ueber eine Stunde gesagt wurde, mit dem
|
||||
-- Zeitpunkt der Aussage. INSERT IGNORE, nie REPLACE.
|
||||
--
|
||||
-- Nur die drei Groessen, um die es dabei geht. Eine vollstaendige Kopie waere
|
||||
-- der doppelte Platz fuer eine Frage, die sich an Temperatur, Strahlung und
|
||||
-- Regen entscheidet.
|
||||
CREATE TABLE IF NOT EXISTS `weatherForecastLog` (
|
||||
`datetime` datetime NOT NULL COMMENT 'die vorhergesagte Stunde, Ortszeit',
|
||||
`abgerufen` datetime NOT NULL COMMENT 'wann diese Aussage gemacht wurde - die Differenz ist der Vorlauf',
|
||||
`temp` decimal(4,1) DEFAULT NULL COMMENT 'degC',
|
||||
`strahlung` smallint(5) unsigned DEFAULT NULL COMMENT 'W/m2 waagerecht',
|
||||
`regen` decimal(4,1) DEFAULT NULL COMMENT 'mm',
|
||||
PRIMARY KEY (`datetime`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
Reference in New Issue
Block a user