Files
Symcon_Belevo_Energiemanage…/Bat_EV_SDL/module.php
2026-01-22 14:11:53 +01:00

286 lines
9.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
class Bat_EV_SDL extends IPSModule
{
/* ===========================
* 1) Setup
* =========================== */
public function Create()
{
parent::Create();
// ---- Properties (kommen aus form.json) ----
$this->RegisterPropertyString("Batteries", "[]"); // List JSON
$this->RegisterPropertyInteger("SDL_Leistung", 0); // W
// ---- Status / Steuerung ----
$this->RegisterVariableBoolean("State", "Aktiv", "~Switch", 1);
$this->EnableAction("State");
// ---- Ergebnisse (Gesamt) ----
$this->RegisterVariableFloat("kWh_SDL", "Energie SDL (kWh)", "", 10);
$this->RegisterVariableFloat("kWh_EV", "Energie EV (kWh)", "", 11);
$this->RegisterVariableFloat("SoC_SDL", "SoC SDL (%)", "", 12);
$this->RegisterVariableFloat("SoC_EV", "SoC EV (%)", "", 13);
$this->RegisterVariableFloat("P_SDL_max", "P SDL max (W)", "", 20);
$this->RegisterVariableFloat("P_EV_max", "P EV max (W)", "", 21);
// Optional: Voller JSON-Dump zum Debuggen/Weiterverarbeiten
$this->RegisterVariableString("CalcJSON", "Berechnung (JSON)", "", 99);
// ---- Timer ----
$this->RegisterTimer("UpdateTimer", 0, 'MyModule_Update($_IPS["TARGET"]);');
}
/* ===========================
* 2) ApplyChanges (Konsole speichern)
* =========================== */
public function ApplyChanges()
{
parent::ApplyChanges();
// z.B. alle 10s neu rechnen (0 = aus)
$this->SetTimerInterval("UpdateTimer", 10000);
// Sofort nach Speichern einmal rechnen
$this->Update();
}
/* ===========================
* 3) RequestAction (WebFront Klicks)
* =========================== */
public function RequestAction($Ident, $Value)
{
switch ($Ident) {
case "State":
SetValue($this->GetIDForIdent("State"), (bool)$Value);
if ((bool)$Value) {
$this->Update();
}
break;
default:
throw new Exception("Invalid Ident: " . $Ident);
}
}
/* ===========================
* 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");
$reserveH = 0.5; // FIX: SDL muss immer 30 Minuten laden/entladen können
// 1) Summe Max-Leistung aller Batterien
$sumBatPowerW = 0;
foreach ($batteries as $b) {
$p = (int)($b["powerbat"] ?? 0);
if ($p > 0) {
$sumBatPowerW += $p;
}
}
if ($sumBatPowerW <= 0) {
$this->SendDebug("Update", "Summe powerbat ist 0 bitte Leistungen setzen.", 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;
}
if ($sdlPowerW < 0) $sdlPowerW = 0;
// Summen
$sumSDLPowerW = 0.0;
$sumEVPowerW = 0.0;
$sumSDLcapKWh = 0.0; // SDL-Topf Kapazität (Reserveenergie)
$sumEVcapKWh = 0.0; // EV-Topf Kapazität (Rest)
$sumSDLkWh = 0.0; // aktueller Füllstand SDL
$sumEVkWh = 0.0; // aktueller Füllstand EV
$calc = [];
// 2) Pro Batterie rechnen
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;
}
// SDL Anteil Leistung (W) proportional
$pSDL_W_raw = ($sdlPowerW * $pBatW) / $sumBatPowerW;
// Physikalisch kappen: pro Batterie nicht mehr als pBatW
$pSDL_W = min($pSDL_W_raw, (float)$pBatW);
if ($pSDL_W < 0) $pSDL_W = 0.0;
// SDL Reserveenergie für 30 Minuten (kWh)
$eSDLcap_kWh = ($pSDL_W * $reserveH) / 1000.0;
// EV-Topf ist Rest der Kapazität
$eEVcap_kWh = max(0.0, $capKWh - $eSDLcap_kWh);
// SoC lesen (0..100 oder 0..1 -> wird umgerechnet)
$socPct = $this->ReadSocPercent($socVar);
// Gesamtenergie in der Batterie (kWh)
$eTotal_kWh = ($socPct / 100.0) * $capKWh;
// Aufteilung Energie: SDL zuerst füllen (damit SDL "sicher" ist)
$eSDL_kWh = min($eTotal_kWh, $eSDLcap_kWh);
$eEV_kWh = max(0.0, $eTotal_kWh - $eSDL_kWh);
// EV kann maximal eEVcap aufnehmen (sollte i.d.R. eh passen)
if ($eEV_kWh > $eEVcap_kWh) {
$eEV_kWh = $eEVcap_kWh;
}
// Power-Limits
$pEV_W = max(0.0, $pBatW - $pSDL_W);
// Summieren
$sumSDLPowerW += $pSDL_W;
$sumEVPowerW += $pEV_W;
$sumSDLcapKWh += $eSDLcap_kWh;
$sumEVcapKWh += $eEVcap_kWh;
$sumSDLkWh += $eSDL_kWh;
$sumEVkWh += $eEV_kWh;
// 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)
];
}
// 3) 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;
// 4) Setzen der Ergebnisvariablen
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));
SetValue($this->GetIDForIdent("P_SDL_max"), round($sumSDLPowerW, 0));
SetValue($this->GetIDForIdent("P_EV_max"), round($sumEVPowerW, 0));
// 5) 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),
"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);
}
/* ===========================
* 5) Helper
* =========================== */
/**
* SoC in Prozent (0..100).
* Akzeptiert 0..1 (wird *100) oder 0..100.
*/
private function ReadSocPercent(int $varId): float
{
if ($varId <= 0 || !IPS_VariableExists($varId)) {
return 0.0;
}
$v = GetValue($varId);
if (!is_numeric($v)) {
return 0.0;
}
$f = (float)$v;
// 0..1 -> Prozent
if ($f >= 0.0 && $f <= 1.0) {
$f *= 100.0;
}
// Clamp
if ($f < 0.0) $f = 0.0;
if ($f > 100.0) $f = 100.0;
return $f;
}
private function WriteEmptyResults()
{
SetValue($this->GetIDForIdent("kWh_SDL"), 0.0);
SetValue($this->GetIDForIdent("kWh_EV"), 0.0);
SetValue($this->GetIDForIdent("SoC_SDL"), 0.0);
SetValue($this->GetIDForIdent("SoC_EV"), 0.0);
SetValue($this->GetIDForIdent("P_SDL_max"), 0.0);
SetValue($this->GetIDForIdent("P_EV_max"), 0.0);
SetValue($this->GetIDForIdent("CalcJSON"), "{}");
$this->SetBuffer("BatteryCalc", "{}");
}
}