diff --git a/Bat_EV_SDL/module.php b/Bat_EV_SDL/module.php index 7dbb2d7..8ec28af 100644 --- a/Bat_EV_SDL/module.php +++ b/Bat_EV_SDL/module.php @@ -66,162 +66,183 @@ class Bat_EV_SDL extends IPSModule } $batteries = json_decode($this->ReadPropertyString("Batteries"), true); - if (!is_array($batteries) || count($batteries) === 0) { + if (!is_array($batteries)) { + $batteries = []; + } + + $sdlTotalW = (float)$this->ReadPropertyInteger("SDL_Leistung"); // W + if ($sdlTotalW < 0) $sdlTotalW = 0; + + // Zeitscheibe in Stunden (0.5h hattest du vorher – jetzt 1h fix) + $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) { + // Keine Batterie-Leistung konfiguriert -> nichts zu tun + $this->SetValueFloat("SDL_Pos", 0.0); + $this->SetValueFloat("SoC_EV", 0.0); + $this->SetValueFloat("P_SDL_max", 0.0); + $this->SetValueFloat("P_SDL_laden", 0.0); + $this->SetValueFloat("P_SDL_entladen", 0.0); + $this->SetValueFloat("P_EV_max", 0.0); + $this->SetValueFloat("P_EV_laden", 0.0); + $this->SetValueFloat("P_EV_entladen", 0.0); + $this->SetValueString("CalcJSON", json_encode(["error" => "sumBatPowerW=0"], JSON_PRETTY_PRINT)); return; } - $sdlPowerW = max(0, (int)$this->ReadPropertyInteger("SDL_Leistung")); - $reserveH = 0.5; // 30 Minuten - $eps = 0.0001; - - // Summe Batterie-Maxleistungen - $sumBatPowerW = 0; - foreach ($batteries as $b) { - $p = (int)($b["powerbat"] ?? 0); - if ($p > 0) $sumBatPowerW += $p; + // Begrenzung: SDL kann nicht mehr ziehen als gesamt verfügbar ist + if ($sdlTotalW > $sumBatPowerW) { + $sdlTotalW = $sumBatPowerW; } - if ($sumBatPowerW <= 0) return; - // SDL begrenzen - if ($sdlPowerW > $sumBatPowerW) $sdlPowerW = $sumBatPowerW; + $calc = [ + "inputs" => [ + "SDL_Leistung_W" => $sdlTotalW, + "SumBatPower_W" => $sumBatPowerW, + "hours" => $hours + ], + "batteries" => [] + ]; - // Summen Leistung - $P_SDL_max = 0.0; - $P_EV_max = 0.0; + // Totale + $totalStoredKWh = 0.0; - $P_SDL_laden = 0.0; - $P_SDL_entladen = 0.0; - $P_EV_laden = 0.0; - $P_EV_entladen = 0.0; + $sumSdlPowerW = 0.0; + $sumEvPowerW = 0.0; - // Summen Energie für Prozentberechnung (deine Logik) - $sumCap_kWh = 0.0; + $sumSdlEnergyAvailKWh = 0.0; // verfügbare SDL-Energie innerhalb der Zeitscheibe + $sumEvEnergyKWh = 0.0; // Energie nach SDL-Reservierung (EV „Restenergie“) - $sumSDLreq_kWh = 0.0; // Sum(E_req) - $sumSDLavail_kWh = 0.0; // Sum(min(E_ist, E_req)) + $pSdlChargeW = 0.0; + $pSdlDischargeW = 0.0; + $pEvChargeW = 0.0; + $pEvDischargeW = 0.0; - $sumEVavail_kWh = 0.0; // Sum(max(E_ist - E_req, 0)) + foreach ($batteries as $idx => $b) { + $pBatW = (float)($b["powerbat"] ?? 0); + $capKWh = (float)($b["capazity"] ?? 0); + $socPct = (float)($b["soc"] ?? 0); - $calc = []; + if ($pBatW < 0) $pBatW = 0; + if ($capKWh < 0) $capKWh = 0; + if ($socPct < 0) $socPct = 0; + if ($socPct > 100) $socPct = 100; - foreach ($batteries as $b) { + // Energie im Akku + $storedKWh = $capKWh * ($socPct / 100.0); + $totalStoredKWh += $storedKWh; - $typ = (string)($b["typ"] ?? ""); - $pBat = (int)($b["powerbat"] ?? 0); - $cap = (float)($b["capazity"] ?? 0); - if ($pBat <= 0 || $cap <= 0) continue; + // Anteilige SDL-Leistung (nach Leistungsklasse verteilt) + $sdlShareW = 0.0; + if ($pBatW > 0 && $sdlTotalW > 0) { + $sdlShareW = $sdlTotalW * ($pBatW / $sumBatPowerW); + } - $sumCap_kWh += $cap; + // Physikalisch: nicht über Batterie-Max + if ($sdlShareW > $pBatW) $sdlShareW = $pBatW; - $socPct = $this->ReadSocPercent((int)($b["soc"] ?? 0)); - $E_ist = $cap * $socPct / 100.0; // kWh + // EV-Leistung ist der Rest + $evShareW = max(0.0, $pBatW - $sdlShareW); - // SDL Anteil Leistung proportional - $pSDL = min(($sdlPowerW * $pBat) / $sumBatPowerW, (float)$pBat); - if ($pSDL < 0) $pSDL = 0.0; + // Energie-Anforderung für SDL innerhalb der Zeitscheibe + // (ohne 0,5h: 1h => kWh = kW * 1h) + $sdlNeedKWh = ($sdlShareW / 1000.0) * $hours; - $pEV = max(0.0, $pBat - $pSDL); + // Verfügbare SDL-Energie kann nicht größer sein als gespeicherte Energie + $sdlAvailKWh = min($storedKWh, $sdlNeedKWh); - $P_SDL_max += $pSDL; - $P_EV_max += $pEV; + // EV-Energie ist der Rest im Tank nach SDL-Reservierung + $evEnergyKWh = max(0.0, $storedKWh - $sdlAvailKWh); - // E_req für 30 min SDL (kWh) - $E_req = ($pSDL * $reserveH) / 1000.0; + // Totale sammeln + $sumSdlPowerW += $sdlShareW; + $sumEvPowerW += $evShareW; - $sumSDLreq_kWh += $E_req; + $sumSdlEnergyAvailKWh += $sdlAvailKWh; + $sumEvEnergyKWh += $evEnergyKWh; - // SDL verfügbar nach deiner Skizze: min(E_ist, E_req) - $SDL_avail = min($E_ist, $E_req); - $sumSDLavail_kWh += $SDL_avail; + // Lade-/Entladegrenzen (einfach & robust) + // Laden: geht immer bis zur Leistungsgrenze + $pSdlChargeW += $sdlShareW; + $pEvChargeW += $evShareW; - // EV verfügbar nach deiner Skizze: max(E_ist - E_req, 0) - $EV_avail = max($E_ist - $E_req, 0.0); - $sumEVavail_kWh += $EV_avail; + // Entladen: nur wenn überhaupt Energie drin ist + if ($storedKWh > 0.0001) { + $pSdlDischargeW += $sdlShareW; + $pEvDischargeW += $evShareW; + } - // Leistungsmaxima (Richtung) nach Reservierungslogik - // SDL entladen geht nur, wenn genug Energie für 30min vorhanden - $canSDLdis = ($E_ist + $eps) >= $E_req; + $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 laden geht nur, wenn genug Platz für 30min vorhanden - $canSDLch = (($cap - $E_ist) + $eps) >= $E_req; + "SDL_share_W" => round($sdlShareW, 2), + "EV_share_W" => round($evShareW, 2), - if ($canSDLdis) $P_SDL_entladen += $pSDL; - if ($canSDLch) $P_SDL_laden += $pSDL; - - // EV entladen geht nur, wenn Energie über SDL-Reserve liegt - if (($E_ist - $E_req) > $eps) $P_EV_entladen += $pEV; - - // EV laden (einfach): solange noch Platz ist - if (($cap - $E_ist) > $eps) $P_EV_laden += $pEV; - - $calc[] = [ - "typ" => $typ, - "pBat_W" => $pBat, - "cap_kWh" => round($cap, 3), - "soc_pct" => round($socPct, 2), - "E_ist_kWh" => round($E_ist, 3), - - "pSDL_W" => round($pSDL, 0), - "E_req_kWh" => round($E_req, 3), - "SDL_avail_kWh" => round($SDL_avail, 3), - - "pEV_W" => round($pEV, 0), - "EV_avail_kWh" => round($EV_avail, 3), - - "canSDLcharge" => $canSDLch, - "canSDLdischarge" => $canSDLdis + "SDL_need_kWh" => round($sdlNeedKWh, 4), + "SDL_avail_kWh" => round($sdlAvailKWh, 4), + "EV_energy_kWh" => round($evEnergyKWh, 4), ]; } - // EV max Energie = SumCap - SumSDLreq (deine 77 - 16 = 61) - $EV_max_kWh = max(0.0, $sumCap_kWh - $sumSDLreq_kWh); + // SDL% = verfügbare SDL-Energie / benötigte SDL-Energie + $sdlNeedTotalKWh = ($sdlTotalW / 1000.0) * $hours; + $sdlPosPct = 0.0; + if ($sdlNeedTotalKWh > 0.000001) { + $sdlPosPct = ($sumSdlEnergyAvailKWh / $sdlNeedTotalKWh) * 100.0; + } - // SDL% und EV% (deine Darstellung) - $SDL_pct = ($sumSDLreq_kWh > $eps) ? ($sumSDLavail_kWh / $sumSDLreq_kWh) * 100.0 : 0.0; - $EV_pct = ($EV_max_kWh > $eps) ? ($sumEVavail_kWh / $EV_max_kWh) * 100.0 : 0.0; + // EV%: wie viel Energie bleibt nach SDL-Deckung, bezogen auf "max EV Energie" + // Max EV Energie = totalStoredKWh - (SDL_needTotalKWh - SDL_availTotalKWh) + // (also: was theoretisch nach dem Versuch SDL zu liefern übrig bleiben kann) + $evMaxKWh = $totalStoredKWh - max(0.0, $sdlNeedTotalKWh - $sumSdlEnergyAvailKWh); + $evPosPct = 0.0; + if ($evMaxKWh > 0.000001) { + $evPosPct = ($sumEvEnergyKWh / $evMaxKWh) * 100.0; + } - // Setzen Variablen - SetValue($this->GetIDForIdent("SDL_Pos"), round($SDL_pct, 2)); - SetValue($this->GetIDForIdent("SoC_EV"), round($EV_pct, 2)); + // Variablen setzen + $this->SetValueFloat("SDL_Pos", round($sdlPosPct, 3)); + $this->SetValueFloat("SoC_EV", round($evPosPct, 3)); - SetValue($this->GetIDForIdent("P_SDL_max"), round($P_SDL_max, 0)); - SetValue($this->GetIDForIdent("P_SDL_laden"), round($P_SDL_laden, 0)); - SetValue($this->GetIDForIdent("P_SDL_entladen"), round($P_SDL_entladen, 0)); + $this->SetValueFloat("P_SDL_max", round($sumSdlPowerW, 2)); + $this->SetValueFloat("P_SDL_laden", round($pSdlChargeW, 2)); + $this->SetValueFloat("P_SDL_entladen", round($pSdlDischargeW, 2)); - SetValue($this->GetIDForIdent("P_EV_max"), round($P_EV_max, 0)); - SetValue($this->GetIDForIdent("P_EV_laden"), round($P_EV_laden, 0)); - SetValue($this->GetIDForIdent("P_EV_entladen"), round($P_EV_entladen, 0)); + $this->SetValueFloat("P_EV_max", round($sumEvPowerW, 2)); + $this->SetValueFloat("P_EV_laden", round($pEvChargeW, 2)); + $this->SetValueFloat("P_EV_entladen", round($pEvDischargeW, 2)); - // Debug JSON mit Summen - $out = [ - "SDL_W" => $sdlPowerW, - "SumBatPower_W" => $sumBatPowerW, - "Reserve_h" => $reserveH, + $calc["totals"] = [ + "totalStored_kWh" => round($totalStoredKWh, 4), + "SDL_needTotal_kWh" => round($sdlNeedTotalKWh, 4), + "SDL_availTotal_kWh" => round($sumSdlEnergyAvailKWh, 4), + "EV_energyTotal_kWh" => round($sumEvEnergyKWh, 4), + "SDL_Pos_pct" => round($sdlPosPct, 3), + "EV_Pos_pct" => round($evPosPct, 3), - "SumCap_kWh" => round($sumCap_kWh, 3), - - "SumSDLreq_kWh" => round($sumSDLreq_kWh, 3), - "SumSDLavail_kWh" => round($sumSDLavail_kWh, 3), - "SDL_pct" => round($SDL_pct, 2), - - "EV_max_kWh" => round($EV_max_kWh, 3), - "SumEVavail_kWh" => round($sumEVavail_kWh, 3), - "EV_pct" => round($EV_pct, 2), - - "P_SDL_max_W" => round($P_SDL_max, 0), - "P_SDL_laden_W" => round($P_SDL_laden, 0), - "P_SDL_entladen_W" => round($P_SDL_entladen, 0), - - "P_EV_max_W" => round($P_EV_max, 0), - "P_EV_laden_W" => round($P_EV_laden, 0), - "P_EV_entladen_W" => round($P_EV_entladen, 0), - - "batteries" => $calc + "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), ]; - SetValue($this->GetIDForIdent("CalcJSON"), json_encode($out, JSON_PRETTY_PRINT)); + $this->SetValueString("CalcJSON", json_encode($calc, JSON_PRETTY_PRINT)); } + private function ReadSocPercent(int $varId): float { if ($varId <= 0 || !IPS_VariableExists($varId)) return 0.0;