diff --git a/haos-addon/config.yaml b/haos-addon/config.yaml index de72f41..19a6a6c 100644 --- a/haos-addon/config.yaml +++ b/haos-addon/config.yaml @@ -1,5 +1,5 @@ name: ShineBridge -version: "1.12.0" +version: "1.12.1" slug: shinebridge description: Energiemanagement für Growatt/Goodwe-Wechselrichter — PV-Überschussladen, Wallbox-EMS, Tarifvergleich, Zigbee2MQTT-Überschussgeräte url: https://gitea.bitfire.work/retr0/shinebridge diff --git a/haos-addon/src/main.py b/haos-addon/src/main.py index f56af89..1950f7b 100644 --- a/haos-addon/src/main.py +++ b/haos-addon/src/main.py @@ -511,6 +511,19 @@ def _restart_all(): # ── REST API ────────────────────────────────────────────────── +@app.get("/api/weather-forecast") +def api_weather_forecast(): + with State.lock: + lat = State.mqtt_cfg.get("site_lat") + lon = State.mqtt_cfg.get("site_lon") + # Beispiel-Basiswert: erstes konfigurierte Kathrein-Timeout, sonst 2h-Default + base = next( + (inv.get("ems_timeout") for inv in State.inverters_cfg if inv.get("ems_timeout")), + 2.0, + ) + return jsonify(weather_forecast.summary(lat, lon, float(base))) + + @app.get("/api/config") def api_get_config(): with State.lock: diff --git a/haos-addon/src/weather_forecast.py b/haos-addon/src/weather_forecast.py index 054e83e..fe203d9 100644 --- a/haos-addon/src/weather_forecast.py +++ b/haos-addon/src/weather_forecast.py @@ -84,3 +84,39 @@ def adjusted_timeout_h(base_timeout_h: float, lat: Optional[float], lon: Optiona # nicht sinnlos den vollen Timeout abwarten, aber nie unter 0.5h return max(base_timeout_h * 0.5, 0.5) return base_timeout_h + + +def summary(lat: Optional[float], lon: Optional[float], base_timeout_h: float = 2.0) -> dict: + """Für die UI: aktueller Prognose-Stand + Beispiel, wie sich ein Timeout + dadurch veraendern wuerde. Nicht fuers eigentliche EMS-Update genutzt + (das ruft adjusted_timeout_h direkt auf) — nur zur Anzeige/Diagnose.""" + if lat is None or lon is None: + return {"configured": False} + + hourly = get_hourly_ghi(lat, lon) + if not hourly: + return {"configured": True, "error": "Prognose aktuell nicht abrufbar"} + + now = time.time() + near = [ghi for ts, ghi in hourly if now <= ts < now + 3 * 3600] + horizon = [ghi for ts, ghi in hourly if now <= ts < now + 12 * 3600] + next_3h_max = round(max(near), 0) if near else 0 + horizon_12h_max = round(max(horizon), 0) if horizon else 0 + + adjusted = adjusted_timeout_h(base_timeout_h, lat, lon) + if adjusted > base_timeout_h: + verdict = "verlängert" + elif adjusted < base_timeout_h: + verdict = "verkürzt" + else: + verdict = "unverändert" + + return { + "configured": True, + "next_3h_max_ghi": next_3h_max, + "horizon_12h_max_ghi": horizon_12h_max, + "sun_threshold": SUN_THRESHOLD, + "example_base_h": base_timeout_h, + "example_adjusted_h": round(adjusted, 2), + "verdict": verdict, + } diff --git a/haos-addon/src/web/index.html b/haos-addon/src/web/index.html index 85a3cde..7ea8ceb 100644 --- a/haos-addon/src/web/index.html +++ b/haos-addon/src/web/index.html @@ -243,6 +243,7 @@ +
@@ -1252,6 +1253,7 @@ async function loadSettings() { document.getElementById("cfg-backup-retention").value = cfg.config_backup_retention_days ?? 14; document.getElementById("cfg-site-lat").value = cfg.site_lat ?? ""; document.getElementById("cfg-site-lon").value = cfg.site_lon ?? ""; + loadWeatherPreview(); } async function saveLocation() { @@ -1263,9 +1265,30 @@ async function saveLocation() { body: JSON.stringify({ site_lat: lat === "" ? null : lat, site_lon: lon === "" ? null : lon }), }); showToast("Gespeichert!", "ok"); + setTimeout(loadWeatherPreview, 500); } catch(e) { showToast("Fehler beim Speichern", "err"); } } +async function loadWeatherPreview() { + const el = document.getElementById("weather-preview"); + let data; + try { data = await fetchJSON(api("api/weather-forecast")); } + catch(e) { el.innerHTML = ""; return; } + + if (!data.configured) { + el.innerHTML = `Kein Standort gesetzt — Zwangslade-Timeout bleibt fest.`; + return; + } + if (data.error) { + el.innerHTML = `${esc(data.error)}`; + return; + } + const verdictColor = data.verdict === "verlängert" ? "var(--green)" : data.verdict === "verkürzt" ? "var(--orange, #ffa657)" : "var(--text-dim)"; + el.innerHTML = ` +
Solarstrahlung nächste 3h: ${data.next_3h_max_ghi} W/m² · nächste 12h: ${data.horizon_12h_max_ghi} W/m²
+
Beispiel bei ${data.example_base_h}h Basis-Timeout: ${data.example_adjusted_h}h (${data.verdict})
`; +} + async function savePrices() { const isSpot = document.getElementById("tariff-spot").checked; const body = {