Update: Battery Optimizer v3.4.0 mit allen Fixes und Features
This commit is contained in:
255
DIAGNOSE_LADE_PROBLEM.md
Normal file
255
DIAGNOSE_LADE_PROBLEM.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# Diagnose: Battery Charging nicht funktioniert
|
||||
|
||||
## Problem-Beschreibung
|
||||
Heute Nacht wurde nicht geladen, obwohl:
|
||||
- Der Speicher von INTERNAL auf REMOTE geschaltet wurde
|
||||
- `goodwe_manual_control` aktiviert wurde
|
||||
- Nach ein paar Sekunden wurde es wieder deaktiviert
|
||||
|
||||
## Wahrscheinliche Ursachen
|
||||
|
||||
### 1. Stündliche Ausführung stoppt das Laden
|
||||
**Problem**: Die Automation "Batterie Optimierung: Stündliche Ausführung" läuft **jede Stunde** um xx:05 und prüft den Schedule für die aktuelle Stunde.
|
||||
|
||||
**Verhalten**:
|
||||
- Stunde mit `action='charge'` → Laden wird aktiviert
|
||||
- Stunde mit `action='auto'` → Laden wird deaktiviert
|
||||
|
||||
**Szenario**:
|
||||
```
|
||||
23:05 → Schedule sagt "charge" → Laden startet
|
||||
00:05 → Schedule sagt "auto" → Laden stoppt
|
||||
```
|
||||
|
||||
**Mögliche Ursachen**:
|
||||
- Schedule enthält nur 1 Ladestunde statt mehrere
|
||||
- Zeitzone-Problem: Falsche Stunde wird geprüft
|
||||
- Tomorrow-Daten fehlen im Schedule
|
||||
|
||||
### 2. Schedule wurde nicht richtig erstellt
|
||||
**Problem**: Die tägliche Berechnung um 14:05 hat keinen sinnvollen Ladeplan erstellt.
|
||||
|
||||
**Mögliche Ursachen**:
|
||||
- Batterie war schon voll (kein Ladebedarf)
|
||||
- Tomorrow-Preise waren noch nicht verfügbar
|
||||
- Alle Nachtstunden waren nicht unter den N günstigsten
|
||||
|
||||
### 3. Zeitzone-Problem in execute_charging_schedule
|
||||
**Problem**: Der Vergleich zwischen aktueller Zeit und Schedule-Einträgen schlägt fehl.
|
||||
|
||||
Code in Zeile 593-606:
|
||||
```python
|
||||
entry_dt = datetime.fromisoformat(entry['datetime'])
|
||||
if entry_dt.tzinfo is None:
|
||||
entry_dt = entry_dt.replace(tzinfo=TIMEZONE)
|
||||
|
||||
entry_date = entry_dt.date()
|
||||
entry_hour = entry_dt.hour
|
||||
|
||||
if entry_date == current_date and entry_hour == current_hour:
|
||||
current_entry = entry
|
||||
```
|
||||
|
||||
**Mögliches Problem**: Die Datetimes im Schedule sind nicht korrekt timezone-aware.
|
||||
|
||||
## Debug-Schritte
|
||||
|
||||
### Schritt 1: Schedule prüfen
|
||||
Gehe zu **Developer Tools → States** und suche nach:
|
||||
```
|
||||
pyscript.battery_charging_schedule
|
||||
```
|
||||
|
||||
**Was zu prüfen**:
|
||||
1. Wann wurde `last_update` zuletzt aktualisiert?
|
||||
2. Wie viele `num_charges` sind geplant?
|
||||
3. Wie viele `num_charges_tomorrow` für die Nacht?
|
||||
4. Schau dir das `schedule` Array in den Attributen an
|
||||
|
||||
**Interpretation**:
|
||||
- `num_charges = 0` → Keine Ladungen geplant (Batterie war voll?)
|
||||
- `num_charges_tomorrow = 0` → Keine Nachtstunden geplant
|
||||
- `has_tomorrow_data = false` → Morgen-Preise fehlen
|
||||
|
||||
### Schritt 2: Home Assistant Logs prüfen
|
||||
Öffne die Home Assistant Logs und suche nach PyScript-Ausgaben:
|
||||
|
||||
**Für die tägliche Berechnung** (sollte um 14:05 laufen):
|
||||
```
|
||||
=== Batterie-Optimierung gestartet (v3.2 - FIXED Timezones) ===
|
||||
Konfiguration geladen: SOC 20-100%, Max 5000W
|
||||
Strompreise geladen: X Stunden (Tomorrow: true/false)
|
||||
Benötigte Ladestunden: X (bei 5000W pro Stunde)
|
||||
Top X günstigste Stunden ausgewählt
|
||||
```
|
||||
|
||||
**Für die stündliche Ausführung** (läuft jede Stunde um xx:05):
|
||||
```
|
||||
Suche Ladeplan für YYYY-MM-DD HH:00 Uhr (lokal)
|
||||
✓ Gefunden: YYYY-MM-DDTHH:00:00+01:00
|
||||
⚡ Stunde HH:00 [heute/morgen]: action=charge/auto, power=XW, price=X.XXct
|
||||
```
|
||||
|
||||
**Kritische Warnungen zu suchen**:
|
||||
```
|
||||
⚠ Keine Daten für YYYY-MM-DD HH:00
|
||||
Keine zukünftigen Preise verfügbar
|
||||
```
|
||||
|
||||
### Schritt 3: Zeitzone-Verifikation
|
||||
Führe manuell den Schedule aus und beobachte die Logs:
|
||||
|
||||
**Developer Tools → Services**:
|
||||
```yaml
|
||||
service: pyscript.execute_charging_schedule
|
||||
data: {}
|
||||
```
|
||||
|
||||
**Was zu beobachten**:
|
||||
1. Welche Zeit wird gesucht? "Suche Ladeplan für ..."
|
||||
2. Wird ein Eintrag gefunden? "✓ Gefunden: ..."
|
||||
3. Welche Aktion wird ausgeführt? "action=charge" oder "action=auto"
|
||||
|
||||
### Schritt 4: Manuellen Test durchführen
|
||||
Um zu verifizieren, dass das Laden grundsätzlich funktioniert:
|
||||
|
||||
**Developer Tools → Services**:
|
||||
```yaml
|
||||
service: input_number.set_value
|
||||
target:
|
||||
entity_id: input_number.charge_power_battery
|
||||
data:
|
||||
value: -5000
|
||||
```
|
||||
|
||||
Dann:
|
||||
```yaml
|
||||
service: input_boolean.turn_on
|
||||
target:
|
||||
entity_id: input_boolean.goodwe_manual_control
|
||||
```
|
||||
|
||||
**Erwartetes Verhalten**:
|
||||
1. ESS schaltet auf REMOTE
|
||||
2. Keep-Alive Automation startet (alle 30s)
|
||||
3. Batterie beginnt zu laden
|
||||
|
||||
**Wenn das funktioniert**: Problem liegt im Schedule/Optimizer
|
||||
**Wenn das nicht funktioniert**: Problem liegt in den Automations/OpenEMS
|
||||
|
||||
### Schritt 5: Schedule-Berechnung triggern
|
||||
Führe eine neue Berechnung aus:
|
||||
|
||||
**Developer Tools → Services**:
|
||||
```yaml
|
||||
service: pyscript.calculate_charging_schedule
|
||||
data: {}
|
||||
```
|
||||
|
||||
Dann prüfe:
|
||||
1. Die Logs für die Berechnung
|
||||
2. Den neuen Schedule in `pyscript.battery_charging_schedule`
|
||||
3. Ob Ladestunden für heute Nacht geplant sind
|
||||
|
||||
## Typische Probleme und Lösungen
|
||||
|
||||
### Problem A: "Keine Ladung nötig (Batterie voll)"
|
||||
**Symptom**: `num_charges = 0` im Schedule
|
||||
**Ursache**: `current_soc >= max_soc - reserve`
|
||||
**Lösung**: Normal, wenn Batterie voll ist. Warte bis SOC sinkt.
|
||||
|
||||
### Problem B: "Tomorrow-Daten fehlen"
|
||||
**Symptom**: `has_tomorrow_data = false`, nur wenige Stunden im Schedule
|
||||
**Ursache**: Berechnung lief vor 14:00, als Preise für morgen noch nicht verfügbar waren
|
||||
**Lösung**: Warte bis nach 14:00, dann läuft automatische Neuberechnung
|
||||
|
||||
### Problem C: "Zeitzone-Mismatch"
|
||||
**Symptom**: "⚠ Keine Daten für YYYY-MM-DD HH:00" obwohl Schedule existiert
|
||||
**Ursache**: Zeitvergleich matcht nicht
|
||||
**Lösung**: Siehe Code-Fix unten
|
||||
|
||||
### Problem D: "Keep-Alive Automation läuft nicht"
|
||||
**Symptom**: ESS ist auf REMOTE, aber keine Modbus-Befehle werden gesendet
|
||||
**Ursache**: Automation "Speicher manuell laden" ist deaktiviert oder fehlerhaft
|
||||
**Lösung**: Prüfe ob Automation aktiviert ist und läuft
|
||||
|
||||
## Code-Verbesserungsvorschläge
|
||||
|
||||
### Fix 1: Logging verbessern in execute_charging_schedule
|
||||
Füge mehr Debug-Output hinzu in Zeile 587:
|
||||
|
||||
```python
|
||||
log.info(f"=== Stündliche Ausführung gestartet ===")
|
||||
log.info(f"Lokale Zeit: {now.strftime('%Y-%m-%d %H:%M:%S %Z')}")
|
||||
log.info(f"Suche Ladeplan für {current_date} {current_hour}:00 Uhr (lokal)")
|
||||
log.info(f"Schedule hat {len(schedule)} Einträge")
|
||||
|
||||
# Zeige alle Schedule-Einträge für Debugging
|
||||
for i, entry in enumerate(schedule):
|
||||
entry_dt = datetime.fromisoformat(entry['datetime'])
|
||||
if entry_dt.tzinfo is None:
|
||||
entry_dt = entry_dt.replace(tzinfo=TIMEZONE)
|
||||
log.debug(f" [{i}] {entry_dt} → {entry['action']}")
|
||||
```
|
||||
|
||||
### Fix 2: Robusteres Datetime-Matching
|
||||
Ersetze den exakten Stunden-Match durch ein Zeitfenster (Zeile 603):
|
||||
|
||||
```python
|
||||
# Statt exaktem Match:
|
||||
if entry_date == current_date and entry_hour == current_hour:
|
||||
|
||||
# Verwende Zeitfenster (z.B. ±5 Minuten):
|
||||
entry_start = entry_dt.replace(minute=0, second=0)
|
||||
entry_end = entry_start + timedelta(hours=1)
|
||||
if entry_start <= now < entry_end:
|
||||
current_entry = entry
|
||||
log.info(f"✓ Match gefunden für Zeitfenster {entry_start} - {entry_end}")
|
||||
break
|
||||
```
|
||||
|
||||
### Fix 3: Fallback für fehlende Matches
|
||||
Nach der Schleife (Zeile 608):
|
||||
|
||||
```python
|
||||
if not current_entry:
|
||||
log.warning(f"⚠ Keine Daten für {current_date} {current_hour}:00")
|
||||
|
||||
# Debug: Zeige nächsten verfügbaren Eintrag
|
||||
future_entries = []
|
||||
for entry in schedule:
|
||||
entry_dt = datetime.fromisoformat(entry['datetime'])
|
||||
if entry_dt.tzinfo is None:
|
||||
entry_dt = entry_dt.replace(tzinfo=TIMEZONE)
|
||||
if entry_dt > now:
|
||||
future_entries.append((entry_dt, entry['action']))
|
||||
|
||||
if future_entries:
|
||||
next_entry = min(future_entries, key=lambda x: x[0])
|
||||
log.info(f"ℹ Nächster Schedule-Eintrag: {next_entry[0]} → {next_entry[1]}")
|
||||
else:
|
||||
log.warning("⚠ Keine zukünftigen Schedule-Einträge gefunden!")
|
||||
|
||||
return
|
||||
```
|
||||
|
||||
## Nächste Schritte
|
||||
|
||||
1. **JETZT**: Führe Debug-Schritte 1-3 aus und notiere die Ergebnisse
|
||||
2. **Prüfe**: Wie sieht der aktuelle Schedule aus?
|
||||
3. **Teste**: Funktioniert manuelles Laden (Schritt 4)?
|
||||
4. **Entscheide**: Basierend auf den Ergebnissen, welche Fix-Strategie anzuwenden ist
|
||||
|
||||
## Monitoring für die nächste Nacht
|
||||
|
||||
Um zu sehen, was heute Nacht passiert:
|
||||
|
||||
1. **Prüfe Schedule um 14:05**: Nach der täglichen Berechnung
|
||||
2. **Setze Benachrichtigung**: Für 23:05 (erste mögliche Ladestunde)
|
||||
3. **Überwache Logs**: Live während der Ladestunden
|
||||
4. **Prüfe OpenEMS**: Logs auf dem BeagleBone für Controller-Aktivität
|
||||
|
||||
```bash
|
||||
# Auf BeagleBone
|
||||
tail -f /var/log/openems/openems.log | grep -i "active.*power"
|
||||
```
|
||||
Reference in New Issue
Block a user