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:
@@ -3,11 +3,12 @@ Battery Charging Optimizer für OpenEMS + GoodWe
|
||||
Nutzt das bestehende manuelle Steuerungssystem
|
||||
|
||||
Speicherort: /config/pyscript/battery_charging_optimizer.py
|
||||
Version: 3.4.0 - NEW: Sicherheitspuffer (20%) für untertägige SOC-Schwankungen
|
||||
Konfigurierbar via input_number.battery_optimizer_safety_buffer
|
||||
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
|
||||
@@ -31,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':
|
||||
@@ -105,11 +106,9 @@ 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
|
||||
'safety_buffer': float(state.get('input_number.battery_optimizer_safety_buffer') or 20) / 100, # in %
|
||||
}
|
||||
|
||||
|
||||
@@ -290,36 +289,25 @@ 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 (berechnet): {available_capacity_wh/1000:.2f} kWh")
|
||||
|
||||
# ==========================================
|
||||
# Sicherheitspuffer: +X% für untertägige Schwankungen
|
||||
# ==========================================
|
||||
# Grund: SOC kann sich zwischen Planung (14:05) und Ladestart (z.B. 22:00) ändern
|
||||
# durch PV-Rest-Produktion, Eigenverbrauch, etc.
|
||||
safety_buffer = config['safety_buffer'] # z.B. 0.20 = 20%
|
||||
available_capacity_wh_with_buffer = available_capacity_wh * (1 + safety_buffer)
|
||||
|
||||
log.info(f"Verfügbare Ladekapazität (mit {safety_buffer*100:.0f}% Puffer): {available_capacity_wh_with_buffer/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
|
||||
# ==========================================
|
||||
max_charge_per_hour = config['max_charge_power'] # Wh pro Stunde
|
||||
needed_hours = int((available_capacity_wh_with_buffer + max_charge_per_hour - 1) / max_charge_per_hour) # Aufrunden
|
||||
needed_hours = int((available_capacity_wh + max_charge_per_hour - 1) / max_charge_per_hour) # Aufrunden
|
||||
|
||||
# Mindestens 1 Stunde, maximal verfügbare Stunden
|
||||
needed_hours = max(1, min(needed_hours, len(future_price_data)))
|
||||
|
||||
log.info(f"🎯 Benötigte Ladestunden: {needed_hours} (bei {max_charge_per_hour}W pro Stunde, inkl. Puffer)")
|
||||
log.info(f"🎯 Benötigte Ladestunden: {needed_hours} (bei {max_charge_per_hour}W pro Stunde)")
|
||||
|
||||
# ==========================================
|
||||
# RANKING: Erstelle Kandidaten-Liste
|
||||
|
||||
Reference in New Issue
Block a user