--- Code-Review: WetterStatistik.js V2.0.7 --- KRITISCHE BUGS 1. Rekordwert-Datum falsch — .lc - 86400 statt .lc - 86400000 (Zeilen 731–743) new Date((getState(PRE_DP+'.Rekordwerte.value.'+DatenPunkt).lc)-86400).getDate() .lc ist ein Millisekunden-Timestamp. Subtrahiert werden 86400 (= 86,4 Sekunden statt eines vollen Tages 86400000 ms). Das angezeigte Datum ist dadurch immer heute (der Tag des Script-Laufs) statt gestern (der Tag, für den der Rekord gilt). Betrifft alle Rekordwert-Anzeigestrings in Template_Rekordwerte(). // Fix: überall -86400 → -86400000 new Date((getState(...).lc) - 86400000).getDate() --- 2. Tropennächte werden hundertfach gezählt — Schleifenlogik falsch (Zeilen 541–553) if (MonatsTag != MonatsTag_old) { Hit=[..., true]; } // Hit[8]=true bei Tageswechsel if (Temp < 20 && Hit[8] == true) { Hit[8] = false; } MonatsTag_old = MonatsTag; if (Hit[8]) { VTropennaechte++; } // ← pro MESSWERT, nicht pro Tag! VTropennaechte++ wird am Ende jeder Iteration ausgeführt, solange Hit[8] true ist. An einem echten Tropennacht-Tag mit z.B. 48 Halbstundenwerten (alle ≥ 20°C) werden 48 Tropennächte gezählt statt einer. Korrekt wäre die Auswertung beim Tageswechsel. // Fix: beim Tageswechsel den Vortag auswerten if (MonatsTag != MonatsTag_old) { if (Hit[8]) { VTropennaechte++; } // Vortag war Tropennacht Hit = [..., true]; } --- 3. Eistage = Frosttage — identische Bedingung (Zeile 546 vs. 548) if (Temp < 0 && Hit[3] == false) { VFrost_Tage++; Hit[3] = true; } // Frosttage: Tiefst < 0 if (Temp < 0 && Hit[5] == false) { VEistage++; Hit[5] = true; } // ← FALSCH: identisch! Eistage sind Tage mit Höchsttemperatur < 0°C (nicht Tiefsttemperatur). Mit der aktuellen Logik sind Eistage und Frosttage immer identisch. Um Eistage korrekt zu ermitteln, müsste man beim Tageswechsel prüfen ob der Maximum des Tages unter 0°C lag. Das ist mit dem bestehenden Ansatz (Auswertung pro Messwert) nicht direkt lösbar — man bräuchte einen Tages-Max-Tracker. --- MITTLERE BUGS 4. Regen-Loop in VorJahr() — Off-by-One + letzter Tag fehlt (Zeilen 562–570) for (let i = 0; i < result.result[2].length; i++) { MonatsTag = new Date(result.result[2][i].ts).getDate(); Rain[i] = result.result[2][i].value; // ← neuer Wert schon drin... if (MonatsTag != MonatsTag_old) { VRegenmenge_Monat += Math.max(...Rain); // ← ...aber Rain enthält bereits den neuen Tag Rain.length = 0; } MonatsTag_old = MonatsTag; } // letzter Tag wird nie ausgewertet (kein abschließender Tageswechsel) Zwei Fehler: (a) Der erste Messwert des neuen Tages landet noch in Rain bevor Rain des alten Tages ausgewertet wird. (b) Der letzte Tag des Monats wird gar nicht ausgewertet. Gleiches Pattern wie in anderen Skripten der Serie. --- 5. search("[WERT]") interpretiert RegEx-Zeichenklasse (Zeile 726) if (REKORDWERTE_AUSGABEFORMAT.search("[WERT]") != -1) { String.search() nimmt einen regulären Ausdruck. [WERT] bedeutet "ein Zeichen aus {W, E, R, T}", nicht den Literal-String [WERT]. Das search() gibt != -1 zurück sobald W, E, R, T, oder auch [ im String vorkommt — also fast immer. Sollte includes() sein: if (REKORDWERTE_AUSGABEFORMAT.includes("[WERT]")) { --- 6. MonatsTag_old nicht zurückgesetzt zwischen Temp-Loop und Regen-Loop (Zeile 563) MonatsTag_old ist eine gemeinsame Variable, die vom Temperatur-Loop (Zeile 540) und vom Regen-Loop (Zeile 563) geteilt wird. Nach dem Temp-Loop hat MonatsTag_old den letzten Tag des Temp-Datensatzes. Wenn der Regen-Datensatz einen anderen ersten Tag hat, wird der erste echte Tageswechsel im Regen-Loop nicht korrekt erkannt. // Fix: vor dem Regen-Loop zurücksetzen let MonatsTag_old_regen; for (let i = 0; i < result.result[2].length; i++) { MonatsTag = new Date(result.result[2][i].ts).getDate(); ... MonatsTag_old_regen = MonatsTag; } --- 7. Schaltjahr für Vorjahres-Februar ignoriert (Zeile 500) end = new Date(zeitstempel.getFullYear()-1, zeitstempel.getMonth(), monatstage[zeitstempel.getMonth()], 23,59,59); monatstage[1] ist fest auf 28. War das Vorjahr ein Schaltjahr, werden die Daten vom 29. Februar nicht abgefragt. Fix: let vorjahr = zeitstempel.getFullYear() - 1; let letzterTagVorjahresMonat = new Date(vorjahr, zeitstempel.getMonth() + 1, 0).getDate(); end = new Date(vorjahr, zeitstempel.getMonth(), letzterTagVorjahresMonat, 23,59,59); --- 8. RegEx — unescapter Punkt im Datumsformat (Zeile 298) if (letzterRegenStr.match(/\d{2}\.\d{2}.\d{4} \d{2}:\d{2}/)) { Der dritte . (zwischen Monat und Jahr) ist nicht escaped — er matcht beliebige Zeichen. Sollte \. sein: /\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}/ --- DESIGN-PROBLEME / SCHLECHTER STIL 9. Blockierendes sleep(10000) in main() (Zeile 168) sleep() in ioBroker-JS-Skripten blockiert die gesamte Script-Engine für 10 Sekunden. Sollte durch await Sleep(10000) in einer async-Funktion ersetzt werden, oder die nachfolgenden Aufrufe in einem setTimeout() gekapselt werden. 10. Shadow-Variable Temp_Durchschnitt (Zeile 197) let Temp_Durchschnitt = Number((...).toFixed(2)); // verdeckt äußere var (Zeile 95) Die innere let-Deklaration verdeckt die äußere Variable. Die äußere bleibt dadurch immer undefined. Kein funktionaler Bug (da Rekordwerte() Temp_Durchschnitt nicht direkt verwendet), aber verwirrend. Einfach let entfernen. 11. Rekordwert-Reset und Rekordwert-Setzen am selben 01.01. (Zeilen 124–143) Am Inst_Jahr+1 werden zuerst die Jahres-Rekordwerte aus dem ersten (unvollständigen) Jahr gesetzt, dann im selben Lauf sofort resettet. Das ist intentionales Design, aber die Reihenfolge ist verwirrend und der Effekt (erster Jahresdurchschnitt wird ignoriert) ist nicht dokumentiert. --- ZUSAMMENFASSUNG Alle 11 Bugs sind behoben. Zusammenfassung der Änderungen: ┌────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────┐ │ Bug │ Fix │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #1 .lc - 86400 │ → - 86400000 (ms statt s), alle 5 Stellen │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #2 Tropennächte pro Messwert │ Auswertung jetzt beim Tageswechsel + nach letztem Tag │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #3 Eistage = Frosttage │ Eigener Hit[5]-Mechanismus: startet true, kippt wenn Tages-Max ≥ 0°C │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #4 Regen Off-by-One + letzter Tag │ Loop komplett umgebaut: Vortag bei Tageswechsel auswerten, letzten Tag │ │ │ nach Loop │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #5 search("[WERT]") │ → includes("[WERT]") │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #6 MonatsTag_old geteilt │ Eigene Variable MonatsTag_Regen / MonatsTag_Regen_old für Regen-Loop │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #7 Schaltjahr Feb Vorjahr │ new Date(vorjahr, monat+1, 0).getDate() für korrekten Monatsletzten │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #8 Unescapter Punkt │ /\d{2}\.\d{2}\.\d{4}/ │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #9 Blockierendes sleep() │ main() → async, sleep() → await Sleep() │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #10 Shadow-Variable │ let entfernt │ │ Temp_Durchschnitt │ │ ├────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────┤ │ #11 Reset vor Rekordwert-Setzen │ Rekordwert-Setzen und Reset in if/else-Block, Reset zuerst │ └────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────┘