From 519e845eeb442640bfb245c93c6b5035fd393972 Mon Sep 17 00:00:00 2001 From: "belevo\\mh" Date: Thu, 22 Jan 2026 16:24:36 +0100 Subject: [PATCH] no message --- Bat_EV_SDL/module.php | 357 ++++++++++++++++++++---------------------- 1 file changed, 172 insertions(+), 185 deletions(-) diff --git a/Bat_EV_SDL/module.php b/Bat_EV_SDL/module.php index cd58223..8ec0080 100644 --- a/Bat_EV_SDL/module.php +++ b/Bat_EV_SDL/module.php @@ -86,222 +86,209 @@ class Bat_EV_SDL extends IPSModule * 4) Hauptlogik * =========================== */ public function Update() - { - if (!GetValue($this->GetIDForIdent("State"))) { - return; - } - - $batteries = json_decode($this->ReadPropertyString("Batteries"), true); - if (!is_array($batteries) || count($batteries) === 0) { - $this->SendDebug("Update", "Keine Batterien konfiguriert.", 0); - $this->WriteEmptyResults(); - return; - } - - $sdlPowerW = (int)$this->ReadPropertyInteger("SDL_Leistung"); - if ($sdlPowerW < 0) $sdlPowerW = 0; - - $reserveH = 0.5; // FIX: 30 Minuten - - // 1) Summe Max-Leistung aller Batterien - $sumBatPowerW = 0; - foreach ($batteries as $b) { - $p = (int)($b["powerbat"] ?? 0); - if ($p > 0) { - $sumBatPowerW += $p; + { + if (!GetValue($this->GetIDForIdent("State"))) { + return; } - } - if ($sumBatPowerW <= 0) { - $this->SendDebug("Update", "Summe powerbat ist 0 – bitte Leistungen setzen.", 0); - $this->WriteEmptyResults(); - return; - } + $batteries = json_decode($this->ReadPropertyString("Batteries"), true); + if (!is_array($batteries) || count($batteries) === 0) { + $this->SendDebug("Update", "Keine Batterien konfiguriert.", 0); + $this->WriteEmptyResults(); + return; + } - // Optional: SDL nicht größer als physikalisch möglich - if ($sdlPowerW > $sumBatPowerW) { - $this->SendDebug("Update", "SDL_Leistung ($sdlPowerW W) > SummeBatPower ($sumBatPowerW W) -> begrenze.", 0); - $sdlPowerW = $sumBatPowerW; - } + $sdlPowerW = (int)$this->ReadPropertyInteger("SDL_Leistung"); + if ($sdlPowerW < 0) $sdlPowerW = 0; - // Summen für Energiesicht - $sumSDLPowerW = 0.0; - $sumEVPowerW = 0.0; + $reserveH = 0.5; // FIX: 30 Minuten + $eps = 0.0001; - $sumSDLcapKWh = 0.0; // SDL-Topf Kapazität (Reserveenergie 30 min) - $sumEVcapKWh = 0.0; // EV-Topf Kapazität (Rest) + // 1) Summe Max-Leistung aller Batterien + $sumBatPowerW = 0; + foreach ($batteries as $b) { + $p = (int)($b["powerbat"] ?? 0); + if ($p > 0) $sumBatPowerW += $p; + } - $sumSDLkWh = 0.0; // aktueller Füllstand SDL - $sumEVkWh = 0.0; // aktueller Füllstand EV + if ($sumBatPowerW <= 0) { + $this->SendDebug("Update", "Summe powerbat ist 0 – bitte Leistungen setzen.", 0); + $this->WriteEmptyResults(); + return; + } - // Neue Leistungs-Maxwerte (wie dein Beispiel) - $P_EV_laden_W = 0.0; - $P_EV_entladen_W = 0.0; - $P_SDL_laden_W = 0.0; - $P_SDL_entladen_W = 0.0; + // SDL nicht größer als physikalisch möglich + if ($sdlPowerW > $sumBatPowerW) { + $this->SendDebug("Update", "SDL_Leistung ($sdlPowerW W) > SummeBatPower ($sumBatPowerW W) -> begrenze.", 0); + $sdlPowerW = $sumBatPowerW; + } - $eps = 0.0001; + // Summen (Leistung) + $P_SDL_max_W = 0.0; + $P_EV_max_W = 0.0; - $calc = []; + $P_SDL_laden_W = 0.0; + $P_SDL_entladen_W = 0.0; + $P_EV_laden_W = 0.0; + $P_EV_entladen_W = 0.0; - // 2) Pro Batterie rechnen - foreach ($batteries as $idx => $b) { + // Summen (Energie/Fenster) + $sumSDL_req_kWh = 0.0; // Summe Ereq (Reservebedarf 30 min) + $sumEVcap_kWh = 0.0; // Summe EV-Fensterkapazität = Sum(Cap - 2*Ereq) + $sumEV_kWh = 0.0; // Summe EV-Energie im Fenster + $sumSDL_ok_count = 0; // wie viele Batterien erfüllen beide Richtungen + $sumBat_count = 0; - $typ = (string)($b["typ"] ?? ("Bat#" . ($idx + 1))); - $pBatW = (int)($b["powerbat"] ?? 0); - $capKWh = (float)($b["capazity"] ?? 0); - $socVar = (int)($b["soc"] ?? 0); + $calc = []; - if ($pBatW <= 0 || $capKWh <= 0) { + foreach ($batteries as $idx => $b) { + + $typ = (string)($b["typ"] ?? ("Bat#" . ($idx + 1))); + $pBatW = (int)($b["powerbat"] ?? 0); + $capKWh = (float)($b["capazity"] ?? 0); + $socVar = (int)($b["soc"] ?? 0); + + if ($pBatW <= 0 || $capKWh <= 0) { + $calc[] = ["typ" => $typ, "skip" => "powerbat<=0 oder capacity<=0"]; + continue; + } + + $sumBat_count++; + + // SDL Anteil Leistung proportional + $pSDL_W_raw = ($sdlPowerW * $pBatW) / $sumBatPowerW; + $pSDL_W = min($pSDL_W_raw, (float)$pBatW); + if ($pSDL_W < 0) $pSDL_W = 0.0; + + // EV Anteil Leistung ist Rest + $pEV_W = max(0.0, $pBatW - $pSDL_W); + + $P_SDL_max_W += $pSDL_W; + $P_EV_max_W += $pEV_W; + + // Reservebedarf für 30 Minuten (kWh) + $Ereq_kWh = ($pSDL_W * $reserveH) / 1000.0; + $sumSDL_req_kWh += $Ereq_kWh; + + // Aktuelle Energie aus SoC + $socPct = $this->ReadSocPercent($socVar); + $E_kWh = ($socPct / 100.0) * $capKWh; + + // Reserviertes Fenster für SDL + $Emin = $Ereq_kWh; + $Emax = $capKWh - $Ereq_kWh; + + // EV-Fensterkapazität (kann 0 werden wenn Cap < 2*Ereq) + $EVcap = max(0.0, $Emax - $Emin); + $sumEVcap_kWh += $EVcap; + + // EV-Energie innerhalb Fenster: clamp(E - Emin, 0..EVcap) + $EV_kWh = 0.0; + if ($EVcap > $eps) { + $EV_kWh = min(max($E_kWh - $Emin, 0.0), $EVcap); + } + $sumEV_kWh += $EV_kWh; + + // SDL kann 30min ENTladen? + $canSDLdischarge = ($E_kWh + $eps) >= $Emin; + + // SDL kann 30min LADEN? + $canSDLcharge = (($capKWh - $E_kWh) + $eps) >= $Ereq_kWh; // equivalent E_kWh <= Emax + + // EV kann ENTladen? (nur wenn über Emin) + $canEVdischarge = ($E_kWh - $Emin) > $eps; + + // EV kann LADEN? (nur wenn unter Emax) + $canEVcharge = ($Emax - $E_kWh) > $eps; + + // Leistungsmaxima (dein Wunsch) + if ($canSDLdischarge) $P_SDL_entladen_W += $pSDL_W; + if ($canSDLcharge) $P_SDL_laden_W += $pSDL_W; + + if ($canEVdischarge) $P_EV_entladen_W += $pEV_W; + if ($canEVcharge) $P_EV_laden_W += $pEV_W; + + // SDL "OK" wenn beide Richtungen 30min gehen + $sdlOK = ($canSDLdischarge && $canSDLcharge); + if ($sdlOK) $sumSDL_ok_count++; + + // Debug / Detail $calc[] = [ "typ" => $typ, - "skip" => "powerbat<=0 oder capacity<=0" + "pBat_W" => $pBatW, + "cap_kWh" => round($capKWh, 3), + "soc_pct" => round($socPct, 2), + "E_kWh" => round($E_kWh, 3), + + "pSDL_W" => round($pSDL_W, 0), + "Ereq_kWh" => round($Ereq_kWh, 3), + "Emin_kWh" => round($Emin, 3), + "Emax_kWh" => round($Emax, 3), + + "pEV_W" => round($pEV_W, 0), + "EVcap_kWh" => round($EVcap, 3), + "EV_kWh" => round($EV_kWh, 3), + + "canSDLcharge_30min" => $canSDLcharge, + "canSDLdischarge_30min" => $canSDLdischarge, + "canEVcharge" => $canEVcharge, + "canEVdischarge" => $canEVdischarge, + "SDL_OK_both_directions" => $sdlOK ]; - continue; } - // SDL Anteil Leistung (W) proportional - $pSDL_W_raw = ($sdlPowerW * $pBatW) / $sumBatPowerW; + // SoC_EV = EV-Energie / EV-Fensterkapazität + $socEV = ($sumEVcap_kWh > $eps) ? ($sumEV_kWh / $sumEVcap_kWh) * 100.0 : 0.0; - // Physikalisch kappen: pro Batterie nicht mehr als pBatW - $pSDL_W = min($pSDL_W_raw, (float)$pBatW); - if ($pSDL_W < 0) $pSDL_W = 0.0; + // SoC_SDL als "OK-Quote" (wie viele Batterien schaffen beide Richtungen) + // Wenn du stattdessen "Leistungs-gewichtete" Quote willst, sag Bescheid. + $socSDL = ($sumBat_count > 0) ? ($sumSDL_ok_count / $sumBat_count) * 100.0 : 0.0; - // EV Anteil Leistung (W) ist Rest - $pEV_W = max(0.0, $pBatW - $pSDL_W); + // Setzen der Ergebnisvariablen + SetValue($this->GetIDForIdent("kWh_SDL"), round($sumSDL_req_kWh, 3)); + SetValue($this->GetIDForIdent("kWh_EV"), round($sumEV_kWh, 3)); - // SDL Reserveenergie für 30 Minuten (kWh) - $eSDLcap_kWh = ($pSDL_W * $reserveH) / 1000.0; + SetValue($this->GetIDForIdent("SoC_SDL"), round($socSDL, 2)); + SetValue($this->GetIDForIdent("SoC_EV"), round($socEV, 2)); - // EV-Topf ist Rest der Kapazität - $eEVcap_kWh = max(0.0, $capKWh - $eSDLcap_kWh); + SetValue($this->GetIDForIdent("P_SDL_max"), round($P_SDL_max_W, 0)); + SetValue($this->GetIDForIdent("P_EV_max"), round($P_EV_max_W, 0)); - // SoC lesen (0..100 oder 0..1 -> wird umgerechnet) - $socPct = $this->ReadSocPercent($socVar); + SetValue($this->GetIDForIdent("P_SDL_laden"), round($P_SDL_laden_W, 0)); + SetValue($this->GetIDForIdent("P_SDL_entladen"), round($P_SDL_entladen_W, 0)); + SetValue($this->GetIDForIdent("P_EV_laden"), round($P_EV_laden_W, 0)); + SetValue($this->GetIDForIdent("P_EV_entladen"), round($P_EV_entladen_W, 0)); - // Gesamtenergie in der Batterie (kWh) - $eTotal_kWh = ($socPct / 100.0) * $capKWh; + // JSON Debug / Buffer + $out = [ + "SDL_W" => $sdlPowerW, + "Reserve_h" => $reserveH, + "SumBatPower_W" => $sumBatPowerW, - // Aufteilung Energie: SDL zuerst füllen - $eSDL_kWh = min($eTotal_kWh, $eSDLcap_kWh); - $eEV_kWh = max(0.0, $eTotal_kWh - $eSDL_kWh); + "SumSDL_req_kWh" => round($sumSDL_req_kWh, 3), + "SumEVcap_kWh" => round($sumEVcap_kWh, 3), + "SumEV_kWh" => round($sumEV_kWh, 3), - // EV kann maximal eEVcap aufnehmen (sollte i.d.R. eh passen) - if ($eEV_kWh > $eEVcap_kWh) { - $eEV_kWh = $eEVcap_kWh; - } + "SoC_SDL_pct" => round($socSDL, 2), + "SoC_EV_pct" => round($socEV, 2), - // Summieren Energiesicht - $sumSDLPowerW += $pSDL_W; - $sumEVPowerW += $pEV_W; + "P_SDL_max_W" => round($P_SDL_max_W, 0), + "P_EV_max_W" => round($P_EV_max_W, 0), - $sumSDLcapKWh += $eSDLcap_kWh; - $sumEVcapKWh += $eEVcap_kWh; + "P_SDL_laden_W" => round($P_SDL_laden_W, 0), + "P_SDL_entladen_W" => round($P_SDL_entladen_W, 0), + "P_EV_laden_W" => round($P_EV_laden_W, 0), + "P_EV_entladen_W" => round($P_EV_entladen_W, 0), - $sumSDLkWh += $eSDL_kWh; - $sumEVkWh += $eEV_kWh; - - // 3) Maxwerte Leistung (dein gewünschtes Verhalten) - - // EV laden: nur wenn im EV-Topf noch Platz ist - if (($eEVcap_kWh - $eEV_kWh) > $eps) { - $P_EV_laden_W += $pEV_W; - } - - // EV entladen: nur wenn im EV-Topf Energie drin ist - if ($eEV_kWh > $eps) { - $P_EV_entladen_W += $pEV_W; - } - - // SDL laden: nur wenn im SDL-Topf noch Platz ist - if (($eSDLcap_kWh - $eSDL_kWh) > $eps) { - $P_SDL_laden_W += $pSDL_W; - } - - // SDL entladen: HART -> nur wenn SDL-Topf voll ist (30min Garantie) - if (($eSDL_kWh + $eps) >= $eSDLcap_kWh) { - $P_SDL_entladen_W += $pSDL_W; - } - - // Detail pro Batterie - $calc[] = [ - "typ" => $typ, - - "pBat_W" => $pBatW, - "cap_kWh" => round($capKWh, 3), - "soc_pct" => round($socPct, 2), - "eTotal_kWh" => round($eTotal_kWh, 3), - - "pSDL_W" => round($pSDL_W, 0), - "eSDLcap_kWh" => round($eSDLcap_kWh, 3), - "eSDL_kWh" => round($eSDL_kWh, 3), - - "eEVcap_kWh" => round($eEVcap_kWh, 3), - "eEV_kWh" => round($eEV_kWh, 3), - - "pEV_W" => round($pEV_W, 0), - - // hilfreiche Debug-Felder für deine Maxwerte-Logik - "canEVcharge" => (($eEVcap_kWh - $eEV_kWh) > $eps), - "canEVdischarge" => ($eEV_kWh > $eps), - "canSDLcharge" => (($eSDLcap_kWh - $eSDL_kWh) > $eps), - "canSDLdischarge_30min" => (($eSDL_kWh + $eps) >= $eSDLcap_kWh) + "batteries" => $calc ]; + + $json = json_encode($out, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + SetValue($this->GetIDForIdent("CalcJSON"), $json); + $this->SetBuffer("BatteryCalc", $json); + $this->SendDebug("Update", $json, 0); } - // 4) Gesamt-SoC für SDL/EV (bezogen auf jeweilige Topf-Kapazität) - $socSDL = ($sumSDLcapKWh > 0.0) ? ($sumSDLkWh / $sumSDLcapKWh) * 100.0 : 0.0; - $socEV = ($sumEVcapKWh > 0.0) ? ($sumEVkWh / $sumEVcapKWh) * 100.0 : 0.0; - - // 5) Setzen der Ergebnisvariablen (Energie + SoC) - SetValue($this->GetIDForIdent("kWh_SDL"), round($sumSDLkWh, 3)); - SetValue($this->GetIDForIdent("kWh_EV"), round($sumEVkWh, 3)); - - SetValue($this->GetIDForIdent("SoC_SDL"), round($socSDL, 2)); - SetValue($this->GetIDForIdent("SoC_EV"), round($socEV, 2)); - - // "statische" Power-Grenzen (ohne Füllstand) – bleiben nützlich - SetValue($this->GetIDForIdent("P_SDL_max"), round($sumSDLPowerW, 0)); - SetValue($this->GetIDForIdent("P_EV_max"), round($sumEVPowerW, 0)); - - // 6) Neue Power-Maxwerte (abhängig von Füllstand/Platz) - SetValue($this->GetIDForIdent("P_EV_laden"), round($P_EV_laden_W, 0)); - SetValue($this->GetIDForIdent("P_EV_entladen"), round($P_EV_entladen_W, 0)); - SetValue($this->GetIDForIdent("P_SDL_laden"), round($P_SDL_laden_W, 0)); - SetValue($this->GetIDForIdent("P_SDL_entladen"), round($P_SDL_entladen_W, 0)); - - // 7) Debug / Buffer - $out = [ - "SDL_W" => $sdlPowerW, - "Reserve_h" => $reserveH, - "SumBatPower_W" => $sumBatPowerW, - - "SumSDLcap_kWh" => round($sumSDLcapKWh, 3), - "SumEVcap_kWh" => round($sumEVcapKWh, 3), - - "SumSDL_kWh" => round($sumSDLkWh, 3), - "SumEV_kWh" => round($sumEVkWh, 3), - - "SoC_SDL_pct" => round($socSDL, 2), - "SoC_EV_pct" => round($socEV, 2), - - "P_SDL_max_W" => round($sumSDLPowerW, 0), - "P_EV_max_W" => round($sumEVPowerW, 0), - - "P_EV_laden_W" => round($P_EV_laden_W, 0), - "P_EV_entladen_W" => round($P_EV_entladen_W, 0), - "P_SDL_laden_W" => round($P_SDL_laden_W, 0), - "P_SDL_entladen_W" => round($P_SDL_entladen_W, 0), - - "batteries" => $calc - ]; - - $json = json_encode($out, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); - SetValue($this->GetIDForIdent("CalcJSON"), $json); - $this->SetBuffer("BatteryCalc", $json); - - $this->SendDebug("Update", $json, 0); - } /* ===========================