-- =========================================================================== -- Wetter: Vorhersage und Archiv (Open-Meteo) -- -- Einspielen: -- mysql -h 127.0.0.1 -P 3310 -u -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;