Update: Battery Optimizer v3.4.0 mit allen Fixes und Features
This commit is contained in:
285
debug_schedule.py
Normal file
285
debug_schedule.py
Normal file
@@ -0,0 +1,285 @@
|
||||
"""
|
||||
Debug Script für Battery Charging Optimizer
|
||||
Speicherort: /config/pyscript/debug_schedule.py
|
||||
|
||||
Verwendung in Home Assistant Developer Tools → Services:
|
||||
service: pyscript.debug_schedule
|
||||
data: {}
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
TIMEZONE = ZoneInfo("Europe/Berlin")
|
||||
|
||||
def get_local_now():
|
||||
"""Gibt die aktuelle Zeit in lokaler Timezone zurück"""
|
||||
return datetime.now(TIMEZONE)
|
||||
|
||||
|
||||
@service
|
||||
def debug_schedule():
|
||||
"""
|
||||
Gibt detaillierte Debug-Informationen über den aktuellen Schedule aus
|
||||
"""
|
||||
|
||||
log.info("=" * 60)
|
||||
log.info("=== DEBUG: Battery Charging Schedule ===")
|
||||
log.info("=" * 60)
|
||||
|
||||
now = get_local_now()
|
||||
log.info(f"Aktuelle Zeit: {now.strftime('%Y-%m-%d %H:%M:%S %Z')}")
|
||||
|
||||
# 1. Prüfe Optimizer-Status
|
||||
log.info("")
|
||||
log.info("--- 1. Optimizer Status ---")
|
||||
optimizer_enabled = state.get('input_boolean.battery_optimizer_enabled')
|
||||
manual_override = state.get('input_boolean.battery_optimizer_manual_override')
|
||||
manual_control = state.get('input_boolean.goodwe_manual_control')
|
||||
|
||||
log.info(f"Optimizer enabled: {optimizer_enabled}")
|
||||
log.info(f"Manual override: {manual_override}")
|
||||
log.info(f"Manual control active: {manual_control}")
|
||||
|
||||
if optimizer_enabled != 'on':
|
||||
log.warning("⚠ Optimizer ist DEAKTIVIERT!")
|
||||
|
||||
if manual_override == 'on':
|
||||
log.warning("⚠ Manual override ist AKTIV - Ausführung wird übersprungen!")
|
||||
|
||||
# 2. Prüfe Schedule State
|
||||
log.info("")
|
||||
log.info("--- 2. Schedule State ---")
|
||||
|
||||
schedule_attr = state.getattr('pyscript.battery_charging_schedule')
|
||||
|
||||
if not schedule_attr:
|
||||
log.error("❌ Kein Schedule vorhanden!")
|
||||
log.info("Führe aus: service: pyscript.calculate_charging_schedule")
|
||||
return
|
||||
|
||||
log.info(f"Schedule Value: {state.get('pyscript.battery_charging_schedule')}")
|
||||
log.info(f"Last Update: {schedule_attr.get('last_update', 'N/A')}")
|
||||
log.info(f"Anzahl Stunden im Plan: {schedule_attr.get('num_hours', 0)}")
|
||||
log.info(f"Anzahl Ladungen gesamt: {schedule_attr.get('num_charges', 0)}")
|
||||
log.info(f"Anzahl Ladungen morgen: {schedule_attr.get('num_charges_tomorrow', 0)}")
|
||||
log.info(f"Gesamt-Energie: {schedule_attr.get('total_energy_kwh', 0)} kWh")
|
||||
log.info(f"Durchschnittspreis: {schedule_attr.get('avg_charge_price', 0):.2f} ct/kWh")
|
||||
log.info(f"Tomorrow-Daten vorhanden: {schedule_attr.get('has_tomorrow_data', False)}")
|
||||
|
||||
first_charge = schedule_attr.get('first_charge_time')
|
||||
first_charge_tomorrow = schedule_attr.get('first_charge_tomorrow')
|
||||
|
||||
if first_charge:
|
||||
log.info(f"Erste Ladung: {first_charge}")
|
||||
else:
|
||||
log.warning("⚠ Keine Ladungen geplant!")
|
||||
|
||||
if first_charge_tomorrow:
|
||||
log.info(f"Erste Ladung morgen: {first_charge_tomorrow}")
|
||||
|
||||
# 3. Prüfe Schedule-Einträge
|
||||
log.info("")
|
||||
log.info("--- 3. Schedule Details ---")
|
||||
|
||||
schedule = schedule_attr.get('schedule', [])
|
||||
|
||||
if not schedule:
|
||||
log.error("❌ Schedule Array ist leer!")
|
||||
return
|
||||
|
||||
current_hour = now.hour
|
||||
current_date = now.date()
|
||||
|
||||
# Finde aktuellen Eintrag
|
||||
current_entry = None
|
||||
current_index = None
|
||||
|
||||
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)
|
||||
|
||||
entry_date = entry_dt.date()
|
||||
entry_hour = entry_dt.hour
|
||||
|
||||
if entry_date == current_date and entry_hour == current_hour:
|
||||
current_entry = entry
|
||||
current_index = i
|
||||
break
|
||||
|
||||
if current_entry:
|
||||
log.info(f"✓ Aktueller Eintrag gefunden (Index {current_index}):")
|
||||
log.info(f" Zeit: {current_entry['datetime']}")
|
||||
log.info(f" Aktion: {current_entry['action']}")
|
||||
log.info(f" Leistung: {current_entry['power_w']}W")
|
||||
log.info(f" Preis: {current_entry['price']:.2f} ct/kWh")
|
||||
log.info(f" Grund: {current_entry.get('reason', 'N/A')}")
|
||||
|
||||
if current_entry['action'] == 'charge':
|
||||
log.info(" → ✓ Sollte LADEN")
|
||||
else:
|
||||
log.info(" → ℹ Sollte AUTO-Modus")
|
||||
else:
|
||||
log.warning(f"⚠ Kein Eintrag für {current_date} {current_hour}:00 gefunden!")
|
||||
|
||||
# 4. Zeige nächste Stunden
|
||||
log.info("")
|
||||
log.info("--- 4. Nächste 24 Stunden ---")
|
||||
|
||||
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)
|
||||
|
||||
# Sortiere nach Zeit
|
||||
future_entries.sort(key=lambda x: datetime.fromisoformat(x['datetime']))
|
||||
|
||||
# Zeige erste 24
|
||||
for entry in future_entries[:24]:
|
||||
entry_dt = datetime.fromisoformat(entry['datetime'])
|
||||
if entry_dt.tzinfo is None:
|
||||
entry_dt = entry_dt.replace(tzinfo=TIMEZONE)
|
||||
|
||||
action_symbol = "🔋" if entry['action'] == 'charge' else "🔄"
|
||||
day_str = "morgen" if entry.get('is_tomorrow', False) else "heute"
|
||||
|
||||
log.info(
|
||||
f"{action_symbol} {entry_dt.strftime('%H:%M')} ({day_str}): "
|
||||
f"{entry['action']:6s} | "
|
||||
f"{entry['power_w']:6d}W | "
|
||||
f"{entry['price']:5.2f}ct | "
|
||||
f"{entry.get('reason', '')}"
|
||||
)
|
||||
|
||||
# 5. Prüfe Ladungen in den nächsten 12 Stunden
|
||||
log.info("")
|
||||
log.info("--- 5. Geplante Ladungen (nächste 12h) ---")
|
||||
|
||||
upcoming_charges = []
|
||||
for entry in future_entries[:12]:
|
||||
if entry['action'] == 'charge':
|
||||
upcoming_charges.append(entry)
|
||||
|
||||
if upcoming_charges:
|
||||
log.info(f"✓ {len(upcoming_charges)} Ladungen geplant:")
|
||||
for entry in upcoming_charges:
|
||||
entry_dt = datetime.fromisoformat(entry['datetime'])
|
||||
if entry_dt.tzinfo is None:
|
||||
entry_dt = entry_dt.replace(tzinfo=TIMEZONE)
|
||||
day_str = "morgen" if entry.get('is_tomorrow', False) else "heute"
|
||||
log.info(
|
||||
f" 🔋 {entry_dt.strftime('%H:%M')} ({day_str}): "
|
||||
f"{abs(entry['power_w'])}W @ {entry['price']:.2f}ct"
|
||||
)
|
||||
else:
|
||||
log.warning("⚠ Keine Ladungen in den nächsten 12 Stunden geplant!")
|
||||
|
||||
# 6. Prüfe Batterie-Status
|
||||
log.info("")
|
||||
log.info("--- 6. Batterie Status ---")
|
||||
|
||||
soc = float(state.get('sensor.esssoc') or 0)
|
||||
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)
|
||||
capacity = float(state.get('input_number.battery_capacity_kwh') or 10)
|
||||
charge_power = float(state.get('input_number.charge_power_battery') or 0)
|
||||
|
||||
log.info(f"Aktueller SOC: {soc}%")
|
||||
log.info(f"SOC-Bereich: {min_soc}% - {max_soc}%")
|
||||
log.info(f"Batterie-Kapazität: {capacity} kWh")
|
||||
log.info(f"Ziel-Ladeleistung: {charge_power}W")
|
||||
|
||||
available_capacity = (max_soc - soc) / 100 * capacity
|
||||
log.info(f"Verfügbare Ladekapazität: {available_capacity:.2f} kWh")
|
||||
|
||||
if soc >= max_soc:
|
||||
log.info("ℹ Batterie ist voll - keine Ladung nötig")
|
||||
elif soc < min_soc:
|
||||
log.warning(f"⚠ SOC unter Minimum ({min_soc}%)!")
|
||||
|
||||
# 7. Prüfe Preis-Sensoren
|
||||
log.info("")
|
||||
log.info("--- 7. Strompreis-Sensoren ---")
|
||||
|
||||
# Extended Sensor
|
||||
price_ext_state = state.get('sensor.hastrom_flex_pro_ext')
|
||||
price_ext_attr = state.getattr('sensor.hastrom_flex_pro_ext')
|
||||
|
||||
if price_ext_attr:
|
||||
log.info("✓ Extended Sensor verfügbar:")
|
||||
prices_today = price_ext_attr.get('prices_today', [])
|
||||
prices_tomorrow = price_ext_attr.get('prices_tomorrow', [])
|
||||
tomorrow_available = price_ext_attr.get('tomorrow_available', False)
|
||||
|
||||
log.info(f" Preise heute: {len(prices_today)} Stunden")
|
||||
log.info(f" Preise morgen: {len(prices_tomorrow)} Stunden")
|
||||
log.info(f" Tomorrow verfügbar: {tomorrow_available}")
|
||||
|
||||
if prices_today:
|
||||
min_price_today = min(prices_today)
|
||||
max_price_today = max(prices_today)
|
||||
log.info(f" Preisspanne heute: {min_price_today:.2f} - {max_price_today:.2f} ct/kWh")
|
||||
|
||||
if not tomorrow_available and now.hour >= 14:
|
||||
log.warning("⚠ Tomorrow-Preise sollten verfügbar sein (nach 14:00), sind es aber nicht!")
|
||||
else:
|
||||
log.warning("⚠ Extended Sensor nicht verfügbar")
|
||||
|
||||
# 8. Prüfe Automations
|
||||
log.info("")
|
||||
log.info("--- 8. Automation Status ---")
|
||||
|
||||
automation_hourly = state.get('automation.batterie_optimierung_stundliche_ausfuhrung')
|
||||
automation_daily = state.get('automation.batterie_optimierung_tagliche_planung')
|
||||
automation_keepalive = state.get('automation.automation_speicher_manuell_laden')
|
||||
|
||||
log.info(f"Stündliche Ausführung: {automation_hourly if automation_hourly else 'NICHT GEFUNDEN'}")
|
||||
log.info(f"Tägliche Planung: {automation_daily if automation_daily else 'NICHT GEFUNDEN'}")
|
||||
log.info(f"Keep-Alive (Manuell Laden): {automation_keepalive if automation_keepalive else 'NICHT GEFUNDEN'}")
|
||||
|
||||
if automation_keepalive and automation_keepalive == 'off':
|
||||
if manual_control == 'on':
|
||||
log.error("❌ PROBLEM: Manual Control ist ON, aber Keep-Alive Automation ist OFF!")
|
||||
log.info(" → Keine Modbus-Befehle werden gesendet!")
|
||||
|
||||
# 9. Zusammenfassung
|
||||
log.info("")
|
||||
log.info("=" * 60)
|
||||
log.info("=== ZUSAMMENFASSUNG ===")
|
||||
log.info("=" * 60)
|
||||
|
||||
issues = []
|
||||
|
||||
if optimizer_enabled != 'on':
|
||||
issues.append("Optimizer ist deaktiviert")
|
||||
|
||||
if not schedule:
|
||||
issues.append("Kein Schedule vorhanden")
|
||||
|
||||
if schedule_attr.get('num_charges', 0) == 0:
|
||||
issues.append("Keine Ladungen geplant")
|
||||
|
||||
if not schedule_attr.get('has_tomorrow_data', False) and now.hour >= 14:
|
||||
issues.append("Tomorrow-Daten fehlen (sollten nach 14:00 da sein)")
|
||||
|
||||
if not current_entry:
|
||||
issues.append(f"Kein Schedule-Eintrag für aktuelle Stunde ({current_hour}:00)")
|
||||
|
||||
if manual_control == 'on' and automation_keepalive == 'off':
|
||||
issues.append("Keep-Alive Automation ist deaktiviert trotz Manual Control")
|
||||
|
||||
if issues:
|
||||
log.warning("⚠ PROBLEME GEFUNDEN:")
|
||||
for issue in issues:
|
||||
log.warning(f" - {issue}")
|
||||
else:
|
||||
log.info("✓ Keine offensichtlichen Probleme gefunden")
|
||||
|
||||
log.info("")
|
||||
log.info("Debug-Report abgeschlossen")
|
||||
log.info("=" * 60)
|
||||
Reference in New Issue
Block a user