Update: Battery Optimizer v3.4.0 mit allen Fixes und Features

This commit is contained in:
felix.zoesch
2025-12-12 08:20:19 +01:00
commit d2a41aad2d
78 changed files with 18053 additions and 0 deletions

214
hastrom_flex_extended.py Normal file
View File

@@ -0,0 +1,214 @@
# /homeassistant/pyscript/hastrom_flex_extended.py
# Version: 1.1.0 - FIXED: Zeitabhängige API-Abfrage
# VOR 14:00: Nur heute (verhindert HTTP 500 Error)
# AB 14:00: Heute + morgen
import requests, json
import datetime
from zoneinfo import ZoneInfo
# Konstante für Timezone
TIMEZONE = ZoneInfo("Europe/Berlin")
def get_local_now():
"""Gibt die aktuelle Zeit in lokaler Timezone zurück (Europe/Berlin)"""
return datetime.datetime.now(TIMEZONE)
@service
def getprices_extended():
"""
Erweiterte Version von haStrom FLEX PRO Preisabfrage mit Tomorrow-Support.
Erstellt neue Sensoren: sensor.hastrom_flex_ext und sensor.hastrom_flex_pro_ext
FIXED: Proper timezone handling - alle Datetimes in Europe/Berlin
"""
now = get_local_now()
today = now.strftime("%Y%m%d")
tomorrow_date = now + datetime.timedelta(days=1)
tomorrow = tomorrow_date.strftime("%Y%m%d")
hr = int(now.strftime("%H"))
# ==========================================
# Zeitabhängige API-Abfrage
# ==========================================
# VOR 14:00: Nur heute abfragen (Tomorrow-Preise noch nicht verfügbar)
# AB 14:00: Heute + morgen abfragen
if hr < 14:
end_date = today
log.info(f"Lade Preise nur für {today} (vor 14:00 - Tomorrow nicht verfügbar)")
else:
end_date = tomorrow
log.info(f"Lade Preise für {today} bis {tomorrow} (ab 14:00 - Tomorrow verfügbar)")
log.info(f"Lokale Zeit: {now.strftime('%Y-%m-%d %H:%M:%S %Z')}")
# ==========================================
# API-Call für heute (+ morgen ab 14:00) - FLEX PRO
# ==========================================
url = f"http://eex.stwhas.de/api/spotprices/flexpro?start_date={today}&end_date={end_date}"
try:
response = task.executor(requests.get, url, timeout=10)
# Check HTTP status
if response.status_code != 200:
log.error(f"❌ API-Fehler: HTTP {response.status_code}")
log.error(f"URL: {url}")
log.error(f"Response: {response.text[:200]}")
return
# Try to parse JSON
try:
data = response.json()
except ValueError as json_err:
log.error(f"❌ JSON Parse-Fehler: {json_err}")
log.error(f"Response Text: {response.text[:200]}")
return
# Check if data structure is valid
if 'data' not in data:
log.error(f"❌ API-Response hat kein 'data' Feld")
log.error(f"Response keys: {list(data.keys())}")
return
log.info(f"✓ API-Abfrage erfolgreich: {len(data.get('data', []))} Datenpunkte")
except Exception as e:
log.error(f"❌ Fehler beim Abrufen der Strompreise: {e}")
log.error(f"URL: {url}")
return
# ==========================================
# Verarbeite Daten mit TIMEZONE AWARENESS
# ==========================================
today_date = now.date()
tomorrow_date_obj = tomorrow_date.date()
# Sammle Daten
price_list_today = []
datetime_list_today = []
price_list_tomorrow = []
datetime_list_tomorrow = []
current_price = None
for item in data["data"]:
# FIXED: Parse timestamps und lokalisiere nach Europe/Berlin
start_dt_naive = datetime.datetime.strptime(item["start_timestamp"], "%Y-%m-%d %H:%M:%S")
end_dt_naive = datetime.datetime.strptime(item["end_timestamp"], "%Y-%m-%d %H:%M:%S")
# Timestamps from API are in local time (Europe/Berlin), so we add timezone info
start_dt = start_dt_naive.replace(tzinfo=TIMEZONE)
end_dt = end_dt_naive.replace(tzinfo=TIMEZONE)
# FLEX PRO Preis: t_price_has_pro_incl_vat
price = item["t_price_has_pro_incl_vat"]
timestamp = item["start_timestamp"]
# FIXED: Aktueller Preis - vergleiche timezone-aware datetimes
if start_dt <= now < end_dt:
current_price = price
# Sortiere nach Datum
if start_dt.date() == today_date:
price_list_today.append(price)
datetime_list_today.append(timestamp)
elif start_dt.date() == tomorrow_date_obj:
price_list_tomorrow.append(price)
datetime_list_tomorrow.append(timestamp)
# ==========================================
# UPDATE: sensor.hastrom_flex_ext
# ==========================================
if current_price is not None:
state.set("sensor.hastrom_flex_ext", value=float(current_price))
# Tariff info (angepasst für FLEX PRO)
if "tariff_info_flex_pro" in data:
for key, value in data["tariff_info_flex_pro"].items():
state.setattr(f"sensor.hastrom_flex_ext.{key}", value)
# Prices
state.setattr("sensor.hastrom_flex_ext.prices_today", price_list_today)
state.setattr("sensor.hastrom_flex_ext.datetime_today", datetime_list_today)
state.setattr("sensor.hastrom_flex_ext.prices_tomorrow", price_list_tomorrow)
state.setattr("sensor.hastrom_flex_ext.datetime_tomorrow", datetime_list_tomorrow)
# Status
state.setattr("sensor.hastrom_flex_ext.tomorrow_available", len(price_list_tomorrow) > 0)
state.setattr("sensor.hastrom_flex_ext.tomorrow_count", len(price_list_tomorrow))
state.setattr("sensor.hastrom_flex_ext.last_update", now.strftime("%Y-%m-%d %H:%M:%S"))
# ==========================================
# UPDATE: sensor.hastrom_flex_pro_ext
# ==========================================
if current_price is not None:
state.set("sensor.hastrom_flex_pro_ext", value=float(current_price))
# Tariff info
if "tariff_info_flex_pro" in data:
for key, value in data["tariff_info_flex_pro"].items():
state.setattr(f"sensor.hastrom_flex_pro_ext.{key}", value)
# Prices
state.setattr("sensor.hastrom_flex_pro_ext.prices_today", price_list_today)
state.setattr("sensor.hastrom_flex_pro_ext.datetime_today", datetime_list_today)
state.setattr("sensor.hastrom_flex_pro_ext.prices_tomorrow", price_list_tomorrow)
state.setattr("sensor.hastrom_flex_pro_ext.datetime_tomorrow", datetime_list_tomorrow)
# Status
state.setattr("sensor.hastrom_flex_pro_ext.tomorrow_available", len(price_list_tomorrow) > 0)
state.setattr("sensor.hastrom_flex_pro_ext.tomorrow_count", len(price_list_tomorrow))
state.setattr("sensor.hastrom_flex_pro_ext.last_update", now.strftime("%Y-%m-%d %H:%M:%S"))
# ==========================================
# Logging & Debug
# ==========================================
tomorrow_expected = hr >= 14
tomorrow_available = len(price_list_tomorrow) > 0
log.info(f"📊 haStrom FLEX PRO Extended - Preise aktualisiert:")
log.info(f" ├─ Heute: {len(price_list_today)} Stunden")
if tomorrow_expected:
if tomorrow_available:
log.info(f" └─ Morgen: {len(price_list_tomorrow)} Stunden ✓ verfügbar (nach 14:00)")
else:
log.warning(f" └─ Morgen: {len(price_list_tomorrow)} Stunden ⚠ NICHT verfügbar (sollte verfügbar sein nach 14:00!)")
else:
log.info(f" └─ Morgen: {len(price_list_tomorrow)} Stunden (noch nicht erwartet vor 14:00)")
if price_list_today:
min_today = min(price_list_today)
max_today = max(price_list_today)
avg_today = sum(price_list_today) / len(price_list_today)
log.info(f" 📈 Heute: Min={min_today:.2f}, Max={max_today:.2f}, Avg={avg_today:.2f} ct/kWh")
if price_list_tomorrow:
min_tomorrow = min(price_list_tomorrow)
max_tomorrow = max(price_list_tomorrow)
avg_tomorrow = sum(price_list_tomorrow) / len(price_list_tomorrow)
log.info(f" 📈 Morgen: Min={min_tomorrow:.2f}, Max={max_tomorrow:.2f}, Avg={avg_tomorrow:.2f} ct/kWh")
# ==========================================
# Automatische Aktualisierung
# ==========================================
@time_trigger("cron(0 * * * *)") # Jede volle Stunde
def update_prices_hourly():
"""Aktualisiere Preise jede Stunde"""
pyscript.getprices_extended()
@time_trigger("cron(5 14 * * *)") # Täglich um 14:05
def update_prices_afternoon():
"""Extra Update um 14:05 wenn Preise für morgen verfügbar werden"""
log.info("=== TRIGGER: 14:05 Update für Tomorrow-Preise ===")
pyscript.getprices_extended()
@time_trigger("cron(5 0 * * *)") # Um Mitternacht
def update_prices_midnight():
"""Update um Mitternacht für neuen Tag"""
log.info("=== TRIGGER: Midnight Update ===")
pyscript.getprices_extended()