Update: Battery Optimizer v3.5.0 - Volle Ladung bis 100% SOC

## Hauptänderungen

### Removed
- Sicherheitspuffer (20%) entfernt - führte zu unvollständiger Ladung
- Reservekapazität (2 kWh) entfernt - Hardware hat eigene Puffer
- Problem: Mehr Ladestunden geplant als nötig, aber tatsächliche Ladung begrenzt
- Folge: Batterie erreichte nie 100% SOC

### Changed
- Standardwert max_charge_power: 5000W → 8000W (+60%)
- Standardwert price_threshold: 28ct → 25ct/kWh
- Ladelogik vereinfacht: Direkte Berechnung ohne Puffer

### Fixed
- Batterie lädt jetzt vollständig bis 100% SOC
- Genauere Ladestunden-Berechnung
- Bessere Kapazitätsnutzung: Volle Leistung in allen Stunden

## Projekt-Aufräumarbeiten

### Archiviert
- Bugfix-Dokumentationen → archive/
  - BUGFIX_TIMEZONE_v3.2.md
  - DIAGNOSE_LADE_PROBLEM.md
  - FIX_API_TIMING.md
  - FIX_CHARGING_CAPACITY.md
  - FIX_SOC_SPIKE_PROBLEM.md
  - FIX_SOC_SPIKE_REMOTE_MODE.md
  - SOC_CALIBRATION_GUIDE.md

### Entfernt
- docs/ (Duplikate)
- debug_log.txt, debug_schedule.py

### Neu
- UPGRADE_TO_v3.5.0.md - Detaillierter Upgrade-Guide
- PROJECT_SUMMARY_v3.5.0.md - Technische Zusammenfassung
- pyscripts/ aktualisiert auf v3.5.0

## Migration

1. Backup erstellen
2. Neue Skripte nach /config/pyscript/ kopieren
3. PyScript neu laden
4. Input Helper anpassen (8000W, 25ct)
5. Test durchführen

Details: siehe UPGRADE_TO_v3.5.0.md

---

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Felix Zösch
2025-12-28 16:49:58 +01:00
parent d2a41aad2d
commit 43f1f3c93c
18 changed files with 485 additions and 1089 deletions

View File

@@ -3,9 +3,12 @@ Battery Charging Optimizer für OpenEMS + GoodWe
Nutzt das bestehende manuelle Steuerungssystem
Speicherort: /config/pyscript/battery_charging_optimizer.py
Version: 3.3.1 - FIXED: SOC-Plausibilitäts-Check (filtert 65535% Spikes beim Modus-Wechsel)
Setzt charge_power_battery als negativen Wert (Laden = negativ)
Nutzt bestehende Automations für ESS-Modus und Keep-Alive
Version: 3.5.0 - REMOVED: Sicherheitspuffer und Reservekapazität (Hardware hat eigene Puffer)
Batterie lädt jetzt bis 100% SOC
CHANGED: Standardwerte - Preisschwelle 25ct, Ladeleistung 8000W
FIXED: SOC-Plausibilitäts-Check (filtert 65535% Spikes beim Modus-Wechsel)
Setzt charge_power_battery als negativen Wert (Laden = negativ)
Nutzt bestehende Automations für ESS-Modus und Keep-Alive
"""
import json
@@ -29,7 +32,7 @@ def calculate_charging_schedule():
Nutzt Ranking-Methode: Wählt die N günstigsten Stunden aus
"""
log.info("=== Batterie-Optimierung gestartet (v3.2 - FIXED Timezones) ===")
log.info("=== Batterie-Optimierung gestartet (v3.5.0 - Volle Ladung bis 100%) ===")
# Prüfe ob Optimierung aktiviert ist
if state.get('input_boolean.battery_optimizer_enabled') != 'on':
@@ -103,9 +106,8 @@ def load_configuration():
'battery_capacity': float(state.get('input_number.battery_capacity_kwh') or 10) * 1000, # in Wh
'min_soc': float(state.get('input_number.battery_optimizer_min_soc') or 20),
'max_soc': float(state.get('input_number.battery_optimizer_max_soc') or 100),
'max_charge_power': float(state.get('input_number.battery_optimizer_max_charge_power') or 5000),
'price_threshold': float(state.get('input_number.battery_optimizer_price_threshold') or 28),
'reserve_capacity': float(state.get('input_number.battery_optimizer_reserve_capacity') or 2) * 1000, # in Wh
'max_charge_power': float(state.get('input_number.battery_optimizer_max_charge_power') or 8000),
'price_threshold': float(state.get('input_number.battery_optimizer_price_threshold') or 25),
'pv_threshold': float(state.get('input_number.battery_optimizer_pv_threshold') or 500), # in Wh
}
@@ -287,15 +289,14 @@ def optimize_charging(price_data, pv_forecast, current_soc, config):
avg_price = sum(all_prices) / len(all_prices)
log.info(f"Preise: Min={min_price:.2f}, Max={max_price:.2f}, Avg={avg_price:.2f} ct/kWh")
# Verfügbare Ladekapazität berechnen
# Verfügbare Ladekapazität berechnen (OHNE Reserven - Hardware hat eigene Puffer)
available_capacity_wh = (config['max_soc'] - current_soc) / 100 * config['battery_capacity']
available_capacity_wh -= config['reserve_capacity']
if available_capacity_wh <= 0:
log.info("Batterie ist voll oder Reserve erreicht - keine Ladung nötig")
log.info("Batterie ist bereits voll - keine Ladung nötig")
return create_auto_only_schedule(future_price_data)
log.info(f"Verfügbare Ladekapazität: {available_capacity_wh/1000:.2f} kWh")
log.info(f"Verfügbare Ladekapazität: {available_capacity_wh/1000:.2f} kWh (bis {config['max_soc']}% SOC)")
# ==========================================
# Berechne benötigte Ladestunden
@@ -628,6 +629,13 @@ def execute_charging_schedule():
log.info(f" Grund: {reason}")
if action == 'charge':
# Prüfe aktuellen SOC beim Ladestart
current_soc_now = float(state.get('sensor.esssoc') or 50)
if current_soc_now > 100 or current_soc_now < 0:
log.warning(f"⚠ Ungültiger SOC beim Ladestart: {current_soc_now}%. Verwende trotzdem geplante Leistung.")
else:
log.info(f"📊 SOC beim Ladestart: {current_soc_now}%")
log.info(f"🔋 AKTIVIERE LADEN mit {abs(power_w)}W")
# Setze Ziel-Leistung als NEGATIVEN Wert (Laden = negativ)