diff --git a/Bat_EV_SDL/module.php b/Bat_EV_SDL/module.php index 8404d0c..68e25a9 100644 --- a/Bat_EV_SDL/module.php +++ b/Bat_EV_SDL/module.php @@ -57,182 +57,193 @@ class Bat_EV_SDL extends IPSModule throw new Exception("Invalid Ident: " . $Ident); } -public function Update() -{ - if (!GetValue($this->GetIDForIdent("State"))) { - return; - } - - $batteries = json_decode($this->ReadPropertyString("Batteries"), true); - if (!is_array($batteries)) $batteries = []; - - $sdlTotalW = (float)$this->ReadPropertyInteger("SDL_Leistung"); // W - if ($sdlTotalW < 0) $sdlTotalW = 0; - - // 30 Minuten - $hours = 0.5; - - // Summe Batterie-Maxleistungen - $sumBatPowerW = 0.0; - foreach ($batteries as $b) { - $p = (float)($b["powerbat"] ?? 0); - if ($p > 0) $sumBatPowerW += $p; - } - if ($sumBatPowerW <= 0.0) { - $this->WriteAllZero("sumBatPowerW=0"); - return; - } - - // SDL nicht größer als Summe Batterieleistung - if ($sdlTotalW > $sumBatPowerW) $sdlTotalW = $sumBatPowerW; - - // Totals - $totalCapKWh = 0.0; - $totalStoredKWh = 0.0; - - $sdlNeedTotalKWh = ($sdlTotalW / 1000.0) * $hours; - - $sumSdlAvailKWh = 0.0; // wirklich lieferbar (energiebegrenzt) - $sumEvKWh = 0.0; // Restenergie nach SDL - - $sumSdlChargeKW = 0.0; - $sumEvChargeKW = 0.0; - $sumSdlDisKW = 0.0; - $sumEvDisKW = 0.0; - - $calc = [ - "batteries" => [], - "total" => [] - ]; - - foreach ($batteries as $idx => $b) { - $pBatW = max(0.0, (float)($b["powerbat"] ?? 0)); - $capKWh = max(0.0, (float)($b["capazity"] ?? 0)); - $socVarId = (int)($b["soc"] ?? 0); - $socPct = $this->ReadSocPercent($socVarId); - - if ($socPct < 0) $socPct = 0; - if ($socPct > 100) $socPct = 100; - - $totalCapKWh += $capKWh; - - // gespeicherte Energie - $storedKWh = $capKWh * ($socPct / 100.0); - $totalStoredKWh += $storedKWh; - - // SDL-Leistung anteilig verteilen - $sdlShareW = 0.0; - if ($pBatW > 0 && $sdlTotalW > 0) { - $sdlShareW = $sdlTotalW * ($pBatW / $sumBatPowerW); + public function Update() + { + if (!GetValue($this->GetIDForIdent("State"))) { + return; } - if ($sdlShareW > $pBatW) $sdlShareW = $pBatW; - $evShareW = max(0.0, $pBatW - $sdlShareW); + $batteries = json_decode($this->ReadPropertyString("Batteries"), true); + if (!is_array($batteries)) { + $batteries = []; + } - // in kW - $sdlShareKW = $sdlShareW / 1000.0; - $evShareKW = $evShareW / 1000.0; + $sdlTotalW = (float)$this->ReadPropertyInteger("SDL_Leistung"); // W + if ($sdlTotalW < 0) $sdlTotalW = 0; - // under_grenze = SDL Energiebedarf in 30min - $underKWh = $sdlShareKW * $hours; + // 30 Minuten Fenster + $hours = 0.5; - // tatsächlich für SDL verfügbare Energie - $sdlAvailKWh = min($storedKWh, $underKWh); + // Summe Batterie-Maxleistungen + $sumBatPowerW = 0.0; + foreach ($batteries as $b) { + $p = (float)($b["powerbat"] ?? 0); + if ($p > 0) $sumBatPowerW += $p; + } - // EV Energie = Restenergie nach SDL (aber nicht negativ) - $upKWh = max(0.0, $storedKWh - $sdlAvailKWh); + if ($sumBatPowerW <= 0.0) { + $this->WriteAllZero("sumBatPowerW=0"); + return; + } - // --- RUNTERREGELN (Discharge) --- - // Max kW aus Energie in 30min: P = E / hours - $sdlDisKW = ($hours > 0) ? min($sdlShareKW, $sdlAvailKWh / $hours) : 0.0; - $evDisKW = ($hours > 0) ? min($evShareKW, $upKWh / $hours) : 0.0; + // SDL nicht größer als Summe Batterieleistung + if ($sdlTotalW > $sumBatPowerW) $sdlTotalW = $sumBatPowerW; - // Charge ist einfach die reservierte Leistung - $sdlChKW = $sdlShareKW; - $evChKW = $evShareKW; + // Totals + $totalCapKWh = 0.0; + $totalStoredKWh = 0.0; - // Totals sammeln - $sumSdlAvailKWh += $sdlAvailKWh; - $sumEvKWh += $upKWh; + $sdlNeedTotalKWh = ($sdlTotalW / 1000.0) * $hours; - $sumSdlChargeKW += $sdlChKW; - $sumEvChargeKW += $evChKW; - $sumSdlDisKW += $sdlDisKW; - $sumEvDisKW += $evDisKW; + // Energie totals + $sumSdlAvailKWh = 0.0; // tatsächlich lieferbare SDL-Energie (energiebegrenzt) + $sumEvKWh = 0.0; // Restenergie nach SDL - $calc["batteries"][] = [ - "idx" => $idx, - "typ" => (string)($b["typ"] ?? ("Bat " . ($idx + 1))), + // Leistung totals (kW) + $sumSdlChargeKW = 0.0; + $sumEvChargeKW = 0.0; + $sumSdlDisKW = 0.0; + $sumEvDisKW = 0.0; - "P_bat_W" => round($pBatW, 0), - "Cap_kWh" => round($capKWh, 3), - "SoC_pct" => round($socPct, 3), - - "SDL_Power_kW" => round($sdlShareKW, 3), - "EV_Power_kW" => round($evShareKW, 3), - - "under_grenze_kWh" => round($underKWh, 3), - "up_grenze_kWh" => round($upKWh, 3), - - "SDL_Charge_kW" => round($sdlChKW, 3), - "SDL_Discharge_kW" => round($sdlDisKW, 3), - - "EV_Charge_kW" => round($evChKW, 3), - "EV_Discharge_kW" => round($evDisKW, 3), - - "SoC_varId" => $socVarId, - "SoC_pct" => round($socPct, 3), - "Stored_kWh"=> round($storedKWh, 3), + $calc = [ + "inputs" => [ + "SDL_Leistung_W" => round($sdlTotalW, 0), + "SumBatPower_W" => round($sumBatPowerW, 0), + "hours" => $hours + ], + "batteries" => [], + "total" => [] ]; + + foreach ($batteries as $idx => $b) { + $pBatW = max(0.0, (float)($b["powerbat"] ?? 0)); + $capKWh = max(0.0, (float)($b["capazity"] ?? 0)); + + // soc ist SelectVariable -> Variable-ID + $socVarId = (int)($b["soc"] ?? 0); + $socPct = $this->ReadSocPercent($socVarId); + + $totalCapKWh += $capKWh; + + // gespeicherte Energie + $storedKWh = $capKWh * ($socPct / 100.0); + $totalStoredKWh += $storedKWh; + + // SDL-Leistung anteilig verteilen + $sdlShareW = 0.0; + if ($pBatW > 0 && $sdlTotalW > 0) { + $sdlShareW = $sdlTotalW * ($pBatW / $sumBatPowerW); + } + if ($sdlShareW > $pBatW) $sdlShareW = $pBatW; + + $evShareW = max(0.0, $pBatW - $sdlShareW); + + // in kW + $sdlShareKW = $sdlShareW / 1000.0; + $evShareKW = $evShareW / 1000.0; + + // under_grenze_kWh = SDL Energiebedarf in 30min + $underKWh = $sdlShareKW * $hours; + + // tatsächlich für SDL verfügbare Energie (energiebegrenzt) + $sdlAvailKWh = min($storedKWh, $underKWh); + + // up_grenze_kWh = EV Energie (Restenergie nach SDL) + $upKWh = max(0.0, $storedKWh - $sdlAvailKWh); + + // Runterregeln Discharge anhand Energie im Zeitfenster + // Pmax(kW) = E(kWh) / hours + $sdlDisKW = ($hours > 0) ? min($sdlShareKW, $sdlAvailKWh / $hours) : 0.0; + $evDisKW = ($hours > 0) ? min($evShareKW, $upKWh / $hours) : 0.0; + + // Charge = reservierte Leistungsanteile + $sdlChKW = $sdlShareKW; + $evChKW = $evShareKW; + + // Totals + $sumSdlAvailKWh += $sdlAvailKWh; + $sumEvKWh += $upKWh; + + $sumSdlChargeKW += $sdlChKW; + $sumEvChargeKW += $evChKW; + $sumSdlDisKW += $sdlDisKW; + $sumEvDisKW += $evDisKW; + + $calc["batteries"][] = [ + "idx" => $idx, + "typ" => (string)($b["typ"] ?? ("Bat " . ($idx + 1))), + + "P_bat_W" => round($pBatW, 0), + "Cap_kWh" => round($capKWh, 3), + + "SoC_varId" => $socVarId, + "SoC_pct" => round($socPct, 3), + "Stored_kWh"=> round($storedKWh, 3), + + "SDL_Power_kW" => round($sdlShareKW, 3), + "EV_Power_kW" => round($evShareKW, 3), + + "under_grenze_kWh" => round($underKWh, 3), + "up_grenze_kWh" => round($upKWh, 3), + + "SDL_Charge_kW" => round($sdlChKW, 3), + "SDL_Discharge_kW" => round($sdlDisKW, 3), + + "EV_Charge_kW" => round($evChKW, 3), + "EV_Discharge_kW" => round($evDisKW, 3), + ]; + } + + // SDL% = lieferbare SDL Energie / benötigte SDL Energie + $sdlPosPct = 0.0; + if ($sdlNeedTotalKWh > 0.000001) { + $sdlPosPct = ($sumSdlAvailKWh / $sdlNeedTotalKWh) * 100.0; + } + + // EV%: Anteil der EV-Restenergie bezogen auf "max EV Energie" im gespeicherten Zustand. + // Max EV Energie (im Speicher) = totalStored - SDL_needTotal (aber nicht <0) + $evMaxKWh = max(0.0, $totalStoredKWh - $sdlNeedTotalKWh); + $evPosPct = 0.0; + if ($evMaxKWh > 0.000001) { + $evPosPct = ($sumEvKWh / $evMaxKWh) * 100.0; + } + + // Variablen setzen + $this->SetIdentValue("SDL_Pos", round($sdlPosPct, 3)); + $this->SetIdentValue("SoC_EV", round($evPosPct, 3)); + + // W-Variablen + $this->SetIdentValue("P_SDL_max", round($sumSdlChargeKW * 1000.0, 0)); + $this->SetIdentValue("P_SDL_laden", round($sumSdlChargeKW * 1000.0, 0)); + $this->SetIdentValue("P_SDL_entladen", round($sumSdlDisKW * 1000.0, 0)); + + $this->SetIdentValue("P_EV_max", round($sumEvChargeKW * 1000.0, 0)); + $this->SetIdentValue("P_EV_laden", round($sumEvChargeKW * 1000.0, 0)); + $this->SetIdentValue("P_EV_entladen", round($sumEvDisKW * 1000.0, 0)); + + $calc["total"] = [ + "SDL_SoC_pct" => round($sdlPosPct, 3), + "EV_SoC_pct" => round($evPosPct, 3), + + "SDL_Charge_kW" => round($sumSdlChargeKW, 3), + "SDL_Discharge_kW" => round($sumSdlDisKW, 3), + + "EV_Charge_kW" => round($sumEvChargeKW, 3), + "EV_Discharge_kW" => round($sumEvDisKW, 3), + + "SDL_needTotal_kWh" => round($sdlNeedTotalKWh, 3), + "SDL_availTotal_kWh" => round($sumSdlAvailKWh, 3), + "EV_total_kWh" => round($sumEvKWh, 3), + + "totalCap_kWh" => round($totalCapKWh, 3), + "totalStored_kWh" => round($totalStoredKWh, 3), + "EV_max_kWh" => round($evMaxKWh, 3) + ]; + + $this->SetIdentValue("CalcJSON", json_encode($calc, JSON_PRETTY_PRINT)); } - // SDL% = lieferbare SDL Energie / benötigte SDL Energie - $sdlPosPct = 0.0; - if ($sdlNeedTotalKWh > 0.000001) { - $sdlPosPct = ($sumSdlAvailKWh / $sdlNeedTotalKWh) * 100.0; - } - - // EV%: ich nehme "EV verfügbar / (SumCap - SDL_need)" wie wir zuletzt hatten - // Wenn du lieber EV% auf Basis "SumStored - SDL_avail" willst, sag Bescheid. - $evMaxKWh = max(0.0, $totalCapKWh - $sdlNeedTotalKWh); - $evPosPct = 0.0; - if ($evMaxKWh > 0.000001) { - $evPosPct = ($sumEvKWh / $evMaxKWh) * 100.0; - } - - // Variablen setzen - $this->SetIdentValue("SDL_Pos", round($sdlPosPct, 3)); - $this->SetIdentValue("SoC_EV", round($evPosPct, 3)); - - // W-Variablen im Objekt: wir geben die totals in W aus - $this->SetIdentValue("P_SDL_max", round($sumSdlChargeKW * 1000.0, 0)); - $this->SetIdentValue("P_SDL_laden", round($sumSdlChargeKW * 1000.0, 0)); - $this->SetIdentValue("P_SDL_entladen", round($sumSdlDisKW * 1000.0, 0)); - - $this->SetIdentValue("P_EV_max", round($sumEvChargeKW * 1000.0, 0)); - $this->SetIdentValue("P_EV_laden", round($sumEvChargeKW * 1000.0, 0)); - $this->SetIdentValue("P_EV_entladen", round($sumEvDisKW * 1000.0, 0)); - - $calc["total"] = [ - "SDL_SoC_pct" => round($sdlPosPct, 3), - "EV_SoC_pct" => round($evPosPct, 3), - - "SDL_Charge_kW" => round($sumSdlChargeKW, 3), - "SDL_Discharge_kW" => round($sumSdlDisKW, 3), - - "EV_Charge_kW" => round($sumEvChargeKW, 3), - "EV_Discharge_kW" => round($sumEvDisKW, 3), - - "SDL_needTotal_kWh" => round($sdlNeedTotalKWh, 3), - "SDL_availTotal_kWh" => round($sumSdlAvailKWh, 3), - "EV_total_kWh" => round($sumEvKWh, 3), - "totalCap_kWh" => round($totalCapKWh, 3), - "totalStored_kWh" => round($totalStoredKWh, 3) - ]; - - $this->SetIdentValue("CalcJSON", json_encode($calc, JSON_PRETTY_PRINT)); -} - + // ---------------- Helpers ---------------- private function WriteAllZero(string $reason): void { @@ -259,4 +270,33 @@ public function Update() } SetValue($id, $value); } + + private function ReadSocPercent(int $varId): float + { + // Falls jemand statt Variable direkt 0..100 einträgt + if ($varId >= 0 && $varId <= 100) { + return (float)$varId; + } + + if ($varId <= 0 || !IPS_VariableExists($varId)) { + return 0.0; + } + + $v = GetValue($varId); + if (!is_numeric($v)) { + return 0.0; + } + + $soc = (float)$v; + + // 0..1 -> 0..100 + if ($soc >= 0.0 && $soc <= 1.0) { + $soc *= 100.0; + } + + if ($soc < 0.0) $soc = 0.0; + if ($soc > 100.0) $soc = 100.0; + + return $soc; + } }