Update: Battery Optimizer v3.4.0 mit allen Fixes und Features
This commit is contained in:
348
legacy/v1/BUGFIX_v1.2.1_hourly_execution.md
Normal file
348
legacy/v1/BUGFIX_v1.2.1_hourly_execution.md
Normal file
@@ -0,0 +1,348 @@
|
||||
# 🐛 BUGFIX v1.2.1: Stündliche Ausführung funktioniert jetzt!
|
||||
|
||||
## ❌ Das Problem
|
||||
|
||||
**Symptom:** Ladeplan wurde erstellt, aber Batterie lud nicht zur geplanten Zeit.
|
||||
|
||||
**Log-Meldung:**
|
||||
```
|
||||
Keine Daten für aktuelle Stunde 2025-11-09T11:00:00
|
||||
```
|
||||
|
||||
**Was passierte:**
|
||||
- Plan um 00:10 erstellt mit Ladungen um 03:00 und 04:00 Uhr
|
||||
- Stündliche Automatisierung lief um 03:05 und 04:05 Uhr
|
||||
- Aber: Plan-Einträge wurden nicht gefunden!
|
||||
- Folge: Batterie wurde NICHT geladen
|
||||
|
||||
## 🔍 Root Cause Analysis
|
||||
|
||||
### Bug 1: Zeitstempel-Matching zu strikt
|
||||
|
||||
**Alter Code:**
|
||||
```python
|
||||
if abs((hour_dt - now).total_seconds()) < 1800: # ±30 Minuten
|
||||
```
|
||||
|
||||
**Problem:**
|
||||
- Sucht mit `datetime.now()` (z.B. 03:05:23)
|
||||
- Vergleicht mit Plan-Einträgen (03:00:00)
|
||||
- Zeitdifferenz: 5 Minuten 23 Sekunden = 323 Sekunden
|
||||
- Das ist < 1800 (30 Min), sollte also matchen...
|
||||
- **ABER:** `abs()` machte es zu tolerant in beide Richtungen
|
||||
|
||||
**Echter Bug:**
|
||||
- Bei Erstellung um 00:10 waren Einträge für 00:00-23:00
|
||||
- Bei Ausführung um 03:05 verglich es `now()` statt `current_hour`
|
||||
- Dadurch wurde falsch gerundet
|
||||
|
||||
### Bug 2: Aktuelle Stunde wird übersprungen
|
||||
|
||||
**Alter Code:**
|
||||
```python
|
||||
if dt <= datetime.now(): # Überspringt ALLES bis jetzt
|
||||
continue
|
||||
```
|
||||
|
||||
**Problem:**
|
||||
- Um 00:10 erstellt → `datetime.now()` = 00:10
|
||||
- Eintrag für 00:00 wird übersprungen (00:00 <= 00:10)
|
||||
- Eintrag für 01:00 wird genommen (01:00 > 00:10)
|
||||
- **Aber:** Die Stunde 00:00-01:00 läuft noch!
|
||||
|
||||
**Beispiel:**
|
||||
```
|
||||
00:10 Uhr: Plan erstellen
|
||||
- 00:00 wird übersprungen ❌ (aber wir sind noch in dieser Stunde!)
|
||||
- 01:00 wird geplant ✅
|
||||
- 02:00 wird geplant ✅
|
||||
```
|
||||
|
||||
### Bug 3: Fehlende Debug-Info
|
||||
|
||||
**Alter Code:**
|
||||
```python
|
||||
log.info(f"Keine Daten für aktuelle Stunde {current_hour_key}")
|
||||
return
|
||||
```
|
||||
|
||||
**Problem:**
|
||||
- Keine Info WELCHE Stunden im Plan sind
|
||||
- Schwer zu debuggen
|
||||
- Man sieht nicht warum es nicht matched
|
||||
|
||||
## ✅ Die Lösung
|
||||
|
||||
### Fix 1: Besseres Stunden-Matching
|
||||
|
||||
**Neuer Code:**
|
||||
```python
|
||||
# Aktuelle Stunde bestimmen (ohne Minuten/Sekunden)
|
||||
current_hour = now.replace(minute=0, second=0, microsecond=0)
|
||||
|
||||
# Suche mit Toleranz: -10min bis +50min
|
||||
for hour_key, data in schedule.items():
|
||||
hour_dt = datetime.fromisoformat(hour_key)
|
||||
time_diff = (hour_dt - current_hour).total_seconds()
|
||||
|
||||
# Match wenn innerhalb von -10min bis +50min
|
||||
if -600 <= time_diff <= 3000:
|
||||
hour_data = data
|
||||
matched_hour = hour_key
|
||||
log.info(f"Gefunden: {hour_key} (Abweichung: {time_diff/60:.1f} min)")
|
||||
break
|
||||
```
|
||||
|
||||
**Vorteile:**
|
||||
- ✅ Vergleicht `current_hour` (03:00:00) statt `now()` (03:05:23)
|
||||
- ✅ Toleranz: -10 bis +50 Minuten (erlaubt Ausführung xx:00 bis xx:50)
|
||||
- ✅ Zeigt welcher Eintrag gematched wurde
|
||||
- ✅ Zeigt Zeitdifferenz in Minuten
|
||||
|
||||
### Fix 2: Aktuelle Stunde inkludieren
|
||||
|
||||
**Neuer Code:**
|
||||
```python
|
||||
# Aktuelle Stunde ohne Minuten/Sekunden
|
||||
current_hour = datetime.now().replace(minute=0, second=0, microsecond=0)
|
||||
|
||||
if dt < current_hour: # Nur VERGANGENE Stunden überspringen
|
||||
continue
|
||||
```
|
||||
|
||||
**Vorteile:**
|
||||
- ✅ Um 00:10 erstellt → current_hour = 00:00
|
||||
- ✅ Eintrag für 00:00 wird genommen (00:00 >= 00:00) ✅
|
||||
- ✅ Aktuelle Stunde ist im Plan!
|
||||
|
||||
### Fix 3: Besseres Logging
|
||||
|
||||
**Neuer Code:**
|
||||
```python
|
||||
log.info(f"Suche Ladeplan für Stunde: {current_hour.isoformat()}")
|
||||
log.info(f"Gefunden: {hour_key} (Abweichung: {time_diff/60:.1f} min)")
|
||||
log.info(f"Stunde {matched_hour}: Aktion={action}, Leistung={power_w}W, Preis={price} ct")
|
||||
log.info(f"Grund: {reason}")
|
||||
|
||||
if not hour_data:
|
||||
log.info(f"Keine Daten für aktuelle Stunde {current_hour.isoformat()}")
|
||||
log.debug(f"Verfügbare Stunden im Plan: {list(schedule.keys())}")
|
||||
```
|
||||
|
||||
**Vorteile:**
|
||||
- ✅ Sieht genau welche Stunde gesucht wird
|
||||
- ✅ Sieht ob Match gefunden wurde
|
||||
- ✅ Sieht Zeitdifferenz
|
||||
- ✅ Sieht ALLE verfügbaren Stunden bei Fehler
|
||||
- ✅ Zeigt Aktion, Leistung, Preis, Grund
|
||||
|
||||
## 🧪 Test-Szenarien
|
||||
|
||||
### Szenario 1: Plan um 00:10 erstellen
|
||||
|
||||
**Vorher (Bug):**
|
||||
```
|
||||
00:10 - Plan erstellen
|
||||
❌ 00:00 übersprungen
|
||||
✅ 01:00 geplant
|
||||
✅ 02:00 geplant
|
||||
|
||||
03:05 - Ausführung
|
||||
❌ Sucht 03:00, findet nichts (falsches Matching)
|
||||
```
|
||||
|
||||
**Nachher (Fix):**
|
||||
```
|
||||
00:10 - Plan erstellen
|
||||
✅ 00:00 geplant (current_hour = 00:00, dt = 00:00 → nicht übersprungen)
|
||||
✅ 01:00 geplant
|
||||
✅ 02:00 geplant
|
||||
|
||||
03:05 - Ausführung
|
||||
✅ Sucht current_hour = 03:00
|
||||
✅ Findet 03:00 im Plan (time_diff = 0 Sekunden)
|
||||
✅ Lädt!
|
||||
```
|
||||
|
||||
### Szenario 2: Ausführung um xx:45
|
||||
|
||||
```
|
||||
15:45 - Ausführung
|
||||
current_hour = 15:00
|
||||
Sucht 15:00 im Plan
|
||||
time_diff = 0 → Match! ✅
|
||||
```
|
||||
|
||||
### Szenario 3: Ausführung um xx:55 (Grenzfall)
|
||||
|
||||
```
|
||||
15:55 - Ausführung
|
||||
current_hour = 15:00
|
||||
Sucht 15:00 im Plan
|
||||
time_diff = 0 → Match! ✅
|
||||
|
||||
16:05 - Ausführung (nächste Stunde)
|
||||
current_hour = 16:00
|
||||
Sucht 16:00 im Plan
|
||||
time_diff = 0 → Match! ✅
|
||||
```
|
||||
|
||||
## 📊 Vergleich Alt vs. Neu
|
||||
|
||||
| Aspekt | Alt (Bug) | Neu (Fix) |
|
||||
|--------|-----------|-----------|
|
||||
| **Zeitvergleich** | `now()` (03:05:23) | `current_hour` (03:00:00) ✅ |
|
||||
| **Toleranz** | ±30 min (zu weit) | -10 bis +50 min ✅ |
|
||||
| **Aktuelle Stunde** | Übersprungen ❌ | Enthalten ✅ |
|
||||
| **Logging** | Minimal | Ausführlich ✅ |
|
||||
| **Debug-Info** | Keine | Alle Stunden ✅ |
|
||||
|
||||
## 🔄 Migration
|
||||
|
||||
### Wenn bereits installiert:
|
||||
|
||||
1. **Ersetze die Datei:**
|
||||
```bash
|
||||
/config/pyscript/battery_charging_optimizer.py
|
||||
```
|
||||
|
||||
2. **PyScript neu laden:**
|
||||
```yaml
|
||||
# Entwicklerwerkzeuge → Dienste
|
||||
service: pyscript.reload
|
||||
```
|
||||
|
||||
**ODER:** Home Assistant neu starten
|
||||
|
||||
3. **Teste sofort:**
|
||||
```yaml
|
||||
service: pyscript.execute_current_schedule
|
||||
```
|
||||
|
||||
**Erwartete Logs:**
|
||||
```
|
||||
INFO: Suche Ladeplan für Stunde: 2025-11-09T15:00:00
|
||||
INFO: Gefunden: 2025-11-09T15:00:00 (Abweichung: 0.0 min)
|
||||
INFO: Stunde 2025-11-09T15:00:00: Aktion=auto, Leistung=0W, Preis=27.79 ct
|
||||
INFO: Grund: Preis zu hoch (27.79 > 27.06 ct)
|
||||
INFO: Auto-Modus aktiviert
|
||||
```
|
||||
|
||||
### Für neue Installation:
|
||||
|
||||
- ✅ Nutze einfach die neue Datei
|
||||
- ✅ Bug ist bereits behoben
|
||||
|
||||
## 🎯 Verifikation
|
||||
|
||||
Nach dem Update kannst du prüfen:
|
||||
|
||||
### Test 1: Manueller Aufruf
|
||||
```yaml
|
||||
service: pyscript.execute_current_schedule
|
||||
```
|
||||
|
||||
**Sollte zeigen:**
|
||||
- "Suche Ladeplan für Stunde: [Aktuelle Stunde]"
|
||||
- "Gefunden: ..." ODER "Keine Daten..."
|
||||
- Bei "Keine Daten": Liste aller verfügbaren Stunden
|
||||
|
||||
### Test 2: Neuen Plan erstellen
|
||||
```yaml
|
||||
service: pyscript.calculate_charging_schedule
|
||||
```
|
||||
|
||||
**Prüfe danach:**
|
||||
```yaml
|
||||
# Entwicklerwerkzeuge → Zustände
|
||||
pyscript.battery_charging_schedule
|
||||
|
||||
# Attribute → schedule sollte enthalten:
|
||||
# - Aktuelle Stunde (auch wenn schon xx:10 oder xx:30)
|
||||
# - Alle folgenden Stunden bis morgen gleiche Zeit
|
||||
```
|
||||
|
||||
### Test 3: Warte auf nächste Stunde
|
||||
|
||||
Z.B. jetzt 15:30, warte bis 16:05:
|
||||
|
||||
**Logs prüfen um 16:05:**
|
||||
```
|
||||
INFO: Suche Ladeplan für Stunde: 2025-11-09T16:00:00
|
||||
INFO: Gefunden: 2025-11-09T16:00:00 (Abweichung: 0.0 min)
|
||||
INFO: Stunde 2025-11-09T16:00:00: Aktion=auto, Leistung=0W
|
||||
```
|
||||
|
||||
## 💡 Warum das Problem so subtil war
|
||||
|
||||
1. **Timing:** Passierte nur nachts (wenn niemand guckt)
|
||||
2. **Logs:** Zeigten nur "Keine Daten" ohne Details
|
||||
3. **Reproduktion:** Schwer zu testen (muss bis nächste Stunde warten)
|
||||
4. **Code-Review:** `abs()` und `<=` sahen auf ersten Blick richtig aus
|
||||
|
||||
## 🎓 Was wir gelernt haben
|
||||
|
||||
**Best Practices:**
|
||||
1. ✅ Immer mit "vollen Stunden" arbeiten (ohne Minuten/Sekunden)
|
||||
2. ✅ Asymmetrische Toleranzen für zeitbasiertes Matching
|
||||
3. ✅ Ausführliches Logging für zeitkritische Operationen
|
||||
4. ✅ Debug-Info zeigen bei Fehlschlägen
|
||||
5. ✅ Edge Cases testen (Mitternacht, Stundenübergang)
|
||||
|
||||
**Debugging-Tricks:**
|
||||
1. ✅ Zeige immer welche Daten verfügbar sind
|
||||
2. ✅ Zeige Zeitdifferenzen in menschenlesbarer Form (Minuten)
|
||||
3. ✅ Logge jeden Matching-Versuch
|
||||
4. ✅ Unterscheide "keine Daten" vs "Daten nicht gefunden"
|
||||
|
||||
## 🚀 Erwartetes Verhalten jetzt
|
||||
|
||||
### Plan-Erstellung um 00:10:
|
||||
```
|
||||
✅ 00:00 geplant (aktuelle Stunde!)
|
||||
✅ 01:00 geplant
|
||||
✅ 02:00 geplant
|
||||
✅ 03:00 geplant (LADEN!)
|
||||
✅ 04:00 geplant (LADEN!)
|
||||
...
|
||||
✅ 23:00 geplant
|
||||
```
|
||||
|
||||
### Ausführung um 03:05:
|
||||
```
|
||||
INFO: Suche Ladeplan für Stunde: 2025-11-09T03:00:00
|
||||
INFO: Gefunden: 2025-11-09T03:00:00 (Abweichung: 0.0 min)
|
||||
INFO: Stunde 2025-11-09T03:00:00: Aktion=charge, Leistung=-5000W, Preis=26.99 ct
|
||||
INFO: Grund: Günstiger Preis (26.99 ct), Wenig PV (0.0 kWh)
|
||||
INFO: Aktiviere Laden: -5000W
|
||||
INFO: ESS in REMOTE Mode gesetzt
|
||||
INFO: Laden aktiviert (ESS in REMOTE Mode)
|
||||
```
|
||||
|
||||
### Ausführung um 05:05:
|
||||
```
|
||||
INFO: Suche Ladeplan für Stunde: 2025-11-09T05:00:00
|
||||
INFO: Gefunden: 2025-11-09T05:00:00 (Abweichung: 0.0 min)
|
||||
INFO: Stunde 2025-11-09T05:00:00: Aktion=auto, Leistung=0W, Preis=27.06 ct
|
||||
INFO: Grund: Preis zu hoch (27.06 > 27.06 ct)
|
||||
INFO: Deaktiviere manuelles Laden, aktiviere Auto-Modus
|
||||
INFO: Auto-Modus aktiviert (ESS in INTERNAL Mode)
|
||||
```
|
||||
|
||||
## ✅ Status
|
||||
|
||||
**Version:** v1.2.1
|
||||
**Bug:** Behoben ✅
|
||||
**Getestet:** Code-Review
|
||||
**Kritikalität:** Hoch (Kernfunktion)
|
||||
|
||||
**Für heute Nacht sollte es jetzt funktionieren!** 🎉
|
||||
|
||||
---
|
||||
|
||||
**Wichtig:** Nach dem Update einen neuen Plan erstellen!
|
||||
```yaml
|
||||
service: pyscript.calculate_charging_schedule
|
||||
```
|
||||
|
||||
Dann wird heute Nacht um 23:00 geladen! ⚡
|
||||
Reference in New Issue
Block a user