diff --git a/Bat_EV_SDL/module.php b/Bat_EV_SDL/module.php index 284dd4c..fc1a712 100644 --- a/Bat_EV_SDL/module.php +++ b/Bat_EV_SDL/module.php @@ -57,186 +57,178 @@ 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 Fenster - $hours = 0.5; - - // Summe Batterie-Leistung - $sumBatPowerW = 0.0; - foreach ($batteries as $b) { - $p = (float)($b["powerbat"] ?? 0); - if ($p > 0) $sumBatPowerW += $p; - } - - // Wenn keine Leistung vorhanden → alles 0 - if ($sumBatPowerW <= 0.0) { - $this->WriteAllZero("sumBatPowerW=0"); - return; - } - - // SDL kann nicht größer als Gesamtleistung - if ($sdlTotalW > $sumBatPowerW) $sdlTotalW = $sumBatPowerW; - - // Totale - $totalCapKWh = 0.0; - $totalStoredKWh = 0.0; - - $sumSdlPowerW = 0.0; - $sumEvPowerW = 0.0; - - $sdlNeedTotalKWh = ($sdlTotalW / 1000.0) * $hours; - - $sumSdlAvailKWh = 0.0; // SDL-Energie, die wirklich geliefert werden kann - $sumEvKWh = 0.0; // Restenergie nach SDL-Reservierung - - // Power Limits - $pSdlChargeW = 0.0; - $pEvChargeW = 0.0; - - $pSdlDischargeW = 0.0; - $pEvDischargeW = 0.0; - - // Debug JSON - $calc = [ - "inputs" => [ - "SDL_Leistung_W" => $sdlTotalW, - "SumBatPower_W" => $sumBatPowerW, - "hours" => $hours - ], - "batteries" => [] - ]; - - foreach ($batteries as $idx => $b) { - $pBatW = max(0.0, (float)($b["powerbat"] ?? 0)); - $capKWh = max(0.0, (float)($b["capazity"] ?? 0)); - $socPct = (float)($b["soc"] ?? 0); - - if ($socPct < 0) $socPct = 0; - if ($socPct > 100) $socPct = 100; - - $totalCapKWh += $capKWh; - - // Energie im Akku - $storedKWh = $capKWh * ($socPct / 100.0); - $totalStoredKWh += $storedKWh; - - // SDL-Leistung anteilig verteilen - $sdlShareW = 0.0; - if ($pBatW > 0 && $sdlTotalW > 0) { - $sdlShareW = $sdlTotalW * ($pBatW / $sumBatPowerW); - } - // nicht über Batterie-Max - if ($sdlShareW > $pBatW) $sdlShareW = $pBatW; - - $evShareW = max(0.0, $pBatW - $sdlShareW); - - // Energiebedarf in diesem Zeitfenster - $sdlNeedKWh = ($sdlShareW / 1000.0) * $hours; - - // Wirklich verfügbare SDL-Energie - $sdlAvailKWh = min($storedKWh, $sdlNeedKWh); - - // EV Energie ist Rest - $evKWh = max(0.0, $storedKWh - $sdlAvailKWh); - - // Sammeln - $sumSdlPowerW += $sdlShareW; - $sumEvPowerW += $evShareW; - - $sumSdlAvailKWh += $sdlAvailKWh; - $sumEvKWh += $evKWh; - - // Laden (so wie du es willst) - $pSdlChargeW += $sdlShareW; - $pEvChargeW += $evShareW; - - // Entladen: energiebegrenzt für das Zeitfenster! - // max Leistung aus Energie über 0,5h: P = E/h *1000 - $maxWFromSdlEnergy = ($hours > 0) ? ($sdlAvailKWh / $hours) * 1000.0 : 0.0; - $maxWFromEvEnergy = ($hours > 0) ? ($evKWh / $hours) * 1000.0 : 0.0; - - $pSdlDischargeW += min($sdlShareW, $maxWFromSdlEnergy); - $pEvDischargeW += min($evShareW, $maxWFromEvEnergy); - - $calc["batteries"][] = [ - "idx" => $idx, - "typ" => (string)($b["typ"] ?? ("Bat " . ($idx + 1))), - "P_bat_W" => round($pBatW, 2), - "Cap_kWh" => round($capKWh, 4), - "SoC_pct" => round($socPct, 3), - "Stored_kWh" => round($storedKWh, 4), - - "SDL_share_W" => round($sdlShareW, 2), - "EV_share_W" => round($evShareW, 2), - - "SDL_need_kWh" => round($sdlNeedKWh, 4), - "SDL_avail_kWh" => round($sdlAvailKWh, 4), - "EV_kWh" => round($evKWh, 4), - - "SDL_discharge_max_W" => round(min($sdlShareW, $maxWFromSdlEnergy), 2), - "EV_discharge_max_W" => round(min($evShareW, $maxWFromEvEnergy), 2), - ]; - } - - // SDL% = verfügbare SDL Energie / benötigte SDL Energie - $sdlPosPct = 0.0; - if ($sdlNeedTotalKWh > 0.000001) { - $sdlPosPct = ($sumSdlAvailKWh / $sdlNeedTotalKWh) * 100.0; - } - - // EV%: - // Deine Skizze ist faktisch: EV verfügbar / EV max - // EV max interpretieren wir sinnvoll als: SumCap - SDL_need - // (wenn SDL_need größer ist, wird EV max 0) - $evMaxKWh = max(0.0, $totalCapKWh - $sdlNeedTotalKWh); - $evPosPct = 0.0; - if ($evMaxKWh > 0.000001) { - $evPosPct = ($sumEvKWh / $evMaxKWh) * 100.0; - } - - // Werte setzen - $this->SetIdentValue("SDL_Pos", round($sdlPosPct, 3)); - $this->SetIdentValue("SoC_EV", round($evPosPct, 3)); - - $this->SetIdentValue("P_SDL_max", round($sumSdlPowerW, 2)); - $this->SetIdentValue("P_SDL_laden", round($pSdlChargeW, 2)); - $this->SetIdentValue("P_SDL_entladen", round($pSdlDischargeW, 2)); - - $this->SetIdentValue("P_EV_max", round($sumEvPowerW, 2)); - $this->SetIdentValue("P_EV_laden", round($pEvChargeW, 2)); - $this->SetIdentValue("P_EV_entladen", round($pEvDischargeW, 2)); - - $calc["totals"] = [ - "totalCap_kWh" => round($totalCapKWh, 4), - "totalStored_kWh" => round($totalStoredKWh, 4), - "SDL_needTotal_kWh" => round($sdlNeedTotalKWh, 4), - "SDL_availTotal_kWh" => round($sumSdlAvailKWh, 4), - "EV_total_kWh" => round($sumEvKWh, 4), - "SDL_Pos_pct" => round($sdlPosPct, 3), - "EV_Pos_pct" => round($evPosPct, 3), - - "P_SDL_sum_W" => round($sumSdlPowerW, 2), - "P_EV_sum_W" => round($sumEvPowerW, 2), - "P_SDL_charge_W" => round($pSdlChargeW, 2), - "P_SDL_discharge_W" => round($pSdlDischargeW, 2), - "P_EV_charge_W" => round($pEvChargeW, 2), - "P_EV_discharge_W" => round($pEvDischargeW, 2), - ]; - - $this->SetIdentValue("CalcJSON", json_encode($calc, JSON_PRETTY_PRINT)); +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)); + $socPct = (float)($b["soc"] ?? 0); + + 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); + } + if ($sdlShareW > $pBatW) $sdlShareW = $pBatW; + + $evShareW = max(0.0, $pBatW - $sdlShareW); + + // in kW + $sdlShareKW = $sdlShareW / 1000.0; + $evShareKW = $evShareW / 1000.0; + + // under_grenze = SDL Energiebedarf in 30min + $underKWh = $sdlShareKW * $hours; + + // tatsächlich für SDL verfügbare Energie + $sdlAvailKWh = min($storedKWh, $underKWh); + + // EV Energie = Restenergie nach SDL (aber nicht negativ) + $upKWh = max(0.0, $storedKWh - $sdlAvailKWh); + + // --- 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; + + // Charge ist einfach die reservierte Leistung + $sdlChKW = $sdlShareKW; + $evChKW = $evShareKW; + + // Totals sammeln + $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_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), + ]; + } + + // 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)); +} + + private function WriteAllZero(string $reason): void { $this->SetIdentValue("SDL_Pos", 0.0);