Files
battery-charging-optimizer/openems/DIAGNOSE_LADE_PROBLEM.md
felix.zoesch 0fa03a566a feat: Major update - Battery Optimizer v3.4.0 with comprehensive fixes
## 🎯 Hauptänderungen

### Version 3.4.0 - SOC-Drift & Charging Capacity
-  Sicherheitspuffer (20-50% konfigurierbar) für untertägige SOC-Schwankungen
-  Monatliche automatische Batterie-Kalibrierung
- 🐛 SOC-Plausibilitäts-Check (filtert 65535% Spikes beim Modus-Wechsel)
- 🐛 Zeitabhängige API-Abfrage (vor/nach 14:00 Uhr)

### Neue Features
- 🔋 **Safety Buffer**: Kompensiert SOC-Drift und Eigenverbrauch
- 🔋 **Auto-Calibration**: Monatlicher Vollzyklus für SOC-Genauigkeit
- 🔋 **Spike Protection**: 4-fach Schutz gegen ungültige SOC-Werte
- 🔋 **Smart API**: Verhindert HTTP 500 Errors bei fehlenden Tomorrow-Preisen

### Dokumentation
- 📚 SOC_CALIBRATION_GUIDE.md - Umfassender Kalibrierungs-Guide
- 📚 FIX_CHARGING_CAPACITY.md - Sicherheitspuffer-Dokumentation
- 📚 FIX_SOC_SPIKE_PROBLEM.md - Spike-Protection-Lösung
- 📚 FIX_API_TIMING.md - Zeitabhängige API-Abfrage
- 📚 DIAGNOSE_LADE_PROBLEM.md - Debug-Guide

### Neue Dateien
- battery_calibration_automation.yaml - 4 Automations für Kalibrierung
- battery_calibration_input_helper.yaml - Input Helper Config
- battery_optimizer_input_helper_safety_buffer.yaml - Puffer Config
- debug_schedule.py - Umfassendes Debug-Script

### Scripts
- battery_charging_optimizer.py v3.4.0
- hastrom_flex_extended.py v1.1.0
- debug_schedule.py v1.0.0

### Fixes
- 🐛 SOC springt auf 65535% beim ESS-Modus-Wechsel → Debounce + Plausibilitäts-Check
- 🐛 API-HTTP-500 vor 14:00 → Zeitabhängige Abfrage
- 🐛 Batterie nicht bis 100% geladen → Sicherheitspuffer
- 🐛 SOC driftet ohne Vollzyklen → Automatische Kalibrierung

## 🚀 Installation

1. Input Helper erstellen (siehe battery_optimizer_input_helper_safety_buffer.yaml)
2. Automations installieren (siehe battery_calibration_automation.yaml)
3. Scripts aktualisieren (battery_charging_optimizer.py v3.4.0)
4. PyScript neu laden

## 📊 Verbesserungen

- Präzisere Ladeplanung durch Sicherheitspuffer
- Robustheit gegen SOC-Drift
- Keine API-Fehler mehr vor 14:00
- Hardware-Stopp bei 100% wird respektiert
- Bessere Batterie-Gesundheit durch regelmäßige Kalibrierung

🤖 Generated with Claude Code (claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 08:04:07 +01:00

8.0 KiB
Raw Blame History

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:

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:

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:

service: input_number.set_value
target:
  entity_id: input_number.charge_power_battery
data:
  value: -5000

Dann:

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:

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:

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):

# 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):

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
# Auf BeagleBone
tail -f /var/log/openems/openems.log | grep -i "active.*power"