no message

This commit is contained in:
belevo\mh
2026-01-27 15:15:48 +01:00
parent a5ea4c75ce
commit 76464b66cf
2 changed files with 212 additions and 132 deletions

View File

@@ -67,21 +67,12 @@
}
,
{
"caption":"Register Laden Modus",
"name":"register_laden_modus",
"width":"200px",
"add":0,
"edit":{
"type":"SelectVariable"
}
},
{
"caption":"Register Entladen Modus",
"name":"register_entladen_modus",
"width":"200px",
"add":0,
"edit":{
"type":"SelectVariable"
"caption": "Register Laden/Entladen Modus",
"name": "register_ladenentladen_modus",
"width": "260px",
"add": 0,
"edit": {
"type": "SelectVariable"
}
}

View File

@@ -456,54 +456,142 @@ class Bat_EV_SDL_V2 extends IPSModule
private function CalculateBatteryDistribution(float $pEvW, float $pSdlW): array
private function CalculateBatteryDistribution(float $pEvW, float $pSdlW): array
{
// Holt das berechnete Json aus, also das Debug json, wo in der Konsole angezeigt wird
$calcJsonId = $this->GetIDForIdent("CalcJSON");
$calc = json_decode((string)GetValue($calcJsonId), true);
if (!is_array($calc) || empty($calc["batteries"])) {
// Fallback, falls Variable leer ist oder nicht existiert
if (!IPS_VariableExists($calcJsonId)) {
return [];
}
$rawJson = (string)GetValue($calcJsonId);
if (empty($rawJson)) {
return [];
}
$calc = json_decode($rawJson, true);
$batteries = $calc['batteries'] ?? [];
foreach ($calc["batteries"] as $bat) {
$idx = (int)$bat["idx"];
$typ = (string)$bat["typ"];
$evSoc = (float)$bat["EV_SOC"];
$sdlSoc = (float)$bat["SDL_SOC"];
$sdlChargeKW = (float)$bat["SDL_Charge_kW"];
$sdlDischargeKW = (float)$bat["SDL_Discharge_kW"];
$evChargeKW = (float)$bat["EV_Charge_kW"];
$evDischargeKW = (float)$bat["EV_Discharge_kW"];
// ---------------------------------------------------------
// Hilfsfunktion: Verteilungslogik (Closure)
// ---------------------------------------------------------
$distributePower = function(float $targetPower, string $mode) use ($batteries): array {
// Initialisierung des Ergebnis-Arrays (Key = idx, Value = zugewiesene Watt)
$result = [];
foreach ($batteries as $bat) {
$result[$bat['idx']] = 0.0;
}
if (abs($targetPower) < 0.01) {
return $result; // Nichts zu tun
}
$isCharge = ($targetPower > 0);
$absPower = abs($targetPower);
// Relevante Keys basierend auf Modus (EV oder SDL) und Richtung (Laden/Entladen)
$socKey = ($mode === 'EV') ? 'EV_SOC' : 'SDL_SOC';
// Achtung: JSON Limits sind in kW, wir rechnen in Watt -> * 1000
$limitKey = $isCharge ? $mode . '_Charge_kW' : $mode . '_Discharge_kW';
/*
// Return sieht so aus:
return :[
"idx" => 0,
"typ":"1",
"chargeW" => 0.0,
"dischargeW" => 3000.0
],
[
"idx" => 1,
"typ":"2",
"chargeW" => 0.0,
"dischargeW" => 1000.0
// 1. Batterien vorbereiten und gruppieren nach gerundetem SoC
$groups = [];
foreach ($batteries as $bat) {
$soc = (int)round($bat[$socKey]); // Auf ganze Zahl runden
$maxW = ((float)$bat[$limitKey]) * 1000.0; // kW in Watt umrechnen
$groups[$soc][] = [
'idx' => $bat['idx'],
'maxW' => $maxW
];
*/
}
// 2. Sortieren der Gruppen
// Laden: Wenig SoC zuerst (ASC) -> leere füllen
// Entladen: Viel SoC zuerst (DESC) -> volle leeren
if ($isCharge) {
ksort($groups);
} else {
krsort($groups);
}
// 3. Verteilung
$remainingNeeded = $absPower;
foreach ($groups as $soc => $groupBatteries) {
if ($remainingNeeded <= 0.01) break;
// Gesamte verfügbare Leistung in dieser SoC-Gruppe ermitteln
$groupTotalCapacity = 0.0;
foreach ($groupBatteries as $gb) {
$groupTotalCapacity += $gb['maxW'];
}
// Wie viel können wir dieser Gruppe zuteilen?
// Entweder alles was die Gruppe kann, oder den Restbedarf
$powerForThisGroup = min($remainingNeeded, $groupTotalCapacity);
// Proportionale Aufteilung innerhalb der Gruppe
// Falls Gruppe Kapazität 0 hat (Defekt/Voll), verhindern wir DivByZero
if ($groupTotalCapacity > 0) {
$ratio = $powerForThisGroup / $groupTotalCapacity;
foreach ($groupBatteries as $gb) {
$assigned = $gb['maxW'] * $ratio;
$result[$gb['idx']] = $assigned;
}
}
$remainingNeeded -= $powerForThisGroup;
}
// Wenn wir entladen, müssen die Werte negativ sein
if (!$isCharge) {
foreach ($result as $idx => $val) {
$result[$idx] = -$val;
}
}
return $result;
};
// ---------------------------------------------------------
// Hauptablauf
// ---------------------------------------------------------
// 1. Berechnung für EV und SDL getrennt durchführen
$evDistribution = $distributePower($pEvW, 'EV');
$sdlDistribution = $distributePower($pSdlW, 'SDL');
// 2. Ergebnisse zusammenführen und Output formatieren
$finalOutput = [];
foreach ($batteries as $bat) {
$idx = $bat['idx'];
// Summe der beiden Anforderungen (kann sich gegenseitig aufheben)
$valEv = $evDistribution[$idx] ?? 0.0;
$valSdl = $sdlDistribution[$idx] ?? 0.0;
$totalW = $valEv + $valSdl;
// Aufteilen in Charge / Discharge für das Return-Format
$chargeW = 0.0;
$dischargeW = 0.0;
if ($totalW > 0) {
$chargeW = abs($totalW);
} else {
$dischargeW = abs($totalW);
}
// JSON Objekt erstellen
$finalOutput[] = [
"idx" => $idx,
"typ" => (string)$bat['typ'], // Typ als String beibehalten
"chargeW" => round($chargeW, 0), // Optional: runden für sauberes JSON
"dischargeW" => round($dischargeW, 0)
];
}
return $finalOutput;
}
private function WriteBatteryPowerSetpoints(array $distribution): void
{
// Batteries-Config laden (enthält register_laden / register_entladen)
$batteriesCfg = json_decode($this->ReadPropertyString("Batteries"), true);
if (!is_array($batteriesCfg) || empty($batteriesCfg)) {
return;
@@ -511,7 +599,6 @@ class Bat_EV_SDL_V2 extends IPSModule
foreach ($distribution as $d) {
// --- Batterie bestimmen ---
$idx = (int)($d["idx"] ?? -1);
if ($idx < 0 || !isset($batteriesCfg[$idx])) {
continue;
@@ -519,104 +606,106 @@ class Bat_EV_SDL_V2 extends IPSModule
$cfg = $batteriesCfg[$idx];
// typ aus distribution bevorzugen, sonst aus Config
$typ = (string)($d["typ"] ?? ($cfg["typ"] ?? ("Bat " . ($idx + 1))));
// --- Sollwerte (Watt) ---
// ✅ immer positiv
$chargeW = max(0.0, (float)($d["chargeW"] ?? 0.0));
$dischargeW = max(0.0, (float)($d["dischargeW"] ?? 0.0));
// Sicherheitsnetz: nie gleichzeitig laden & entladen
// nie gleichzeitig
if ($chargeW > 0.0 && $dischargeW > 0.0) {
$this->SendDebug(
"WriteBatteryPowerSetpoints",
"WARN: charge & discharge gleichzeitig > 0 bei $typ (idx=$idx) -> setze beides 0",
0
);
$this->SendDebug("WriteBatteryPowerSetpoints", "WARN: both >0 for $typ (idx=$idx) -> set 0", 0);
$chargeW = 0.0;
$dischargeW = 0.0;
}
// --- Vendor-/Default-Handling über Register schreiben ---
$this->WriteByVendorRegisters($typ, $cfg, $chargeW, $dischargeW);
$this->WriteByVendorRegistersSingleMode($typ, $cfg, $chargeW, $dischargeW);
// Optionales Debug
$this->SendDebug(
"Setpoints",
"$typ (idx=$idx): charge={$chargeW}W discharge={$dischargeW}W",
0
);
$this->SendDebug("Setpoints", "$typ (idx=$idx) charge={$chargeW}W discharge={$dischargeW}W", 0);
}
}
private function WriteByVendorRegisters(string $typ, array $cfg, float $chargeW, float $dischargeW): void
{
$typLower = mb_strtolower($typ);
private function WriteByVendorRegistersSingleMode(string $typ, array $cfg, float $chargeW, float $dischargeW): void
{
$t = mb_strtolower($typ);
// Register aus Formular
$regCharge = (int)($cfg["register_laden"] ?? 0);
$regDisch = (int)($cfg["register_entladen"] ?? 0);
// Leistungs-Variablen
$varPowerCharge = (int)($cfg["powerbat_laden"] ?? 0);
$varPowerDisch = (int)($cfg["powerbat_entladen"] ?? 0);
// -----------------------------
// GOODWE → ein Register, signed
// -----------------------------
if (strpos($typLower, "goodwe") !== false) {
// ✅ EIN Modus-Register
$varMode = (int)($cfg["register_ladenentladen_modus"] ?? 0);
if ($regCharge > 0 && IPS_VariableExists($regCharge)) {
$signedW = 0.0;
// sichere Writer
$setInt = function(int $varId, int $value): void {
if ($varId > 0 && IPS_VariableExists($varId)) {
SetValue($varId, $value);
}
};
$setW = function(int $varId, float $w): void {
if ($varId > 0 && IPS_VariableExists($varId)) {
SetValue($varId, (int)round(max(0.0, $w), 0)); // ✅ niemals negativ
}
};
// Moduscodes je Typ
$modeCharge = 1; $modeDisch = 2; // Default
if (strpos($t, "goodwe") !== false) {
$modeCharge = 11; $modeDisch = 12;
} elseif (strpos($t, "solaredge") !== false) {
$modeCharge = 3; $modeDisch = 4;
}
// =========================
// Laden
// =========================
if ($chargeW > 0.0) {
$signedW = $chargeW; // Laden → +
} elseif ($dischargeW > 0.0) {
$signedW = -1.0 * $dischargeW; // Entladen → -
}
SetValue($regCharge, (int)round($signedW, 0));
}
// optional: zweites Register auf 0 halten
if ($regDisch > 0 && IPS_VariableExists($regDisch)) {
SetValue($regDisch, 0);
}
// Modus setzen (ein Register)
$setInt($varMode, $modeCharge);
// GoodWe: nur powerbat_laden
if (strpos($t, "goodwe") !== false) {
$setW($varPowerCharge, $chargeW);
$setW($varPowerDisch, 0.0);
return;
}
// --------------------------------
// SOLAREDGE → getrennte Register
// --------------------------------
if (strpos($typLower, "solaredge") !== false) {
if ($regCharge > 0 && IPS_VariableExists($regCharge)) {
SetValue($regCharge, (int)round($chargeW, 0));
}
if ($regDisch > 0 && IPS_VariableExists($regDisch)) {
SetValue($regDisch, (int)round($dischargeW, 0));
}
// SolarEdge + Default: zwei Leistungsregister
$setW($varPowerCharge, $chargeW);
$setW($varPowerDisch, 0.0);
return;
}
// --------------------------------
// DEFAULT → getrennt, fest: 1 / 2
// --------------------------------
// Laden = Register 1, Entladen = Register 2
// =========================
// Entladen
// =========================
if ($dischargeW > 0.0) {
if (IPS_VariableExists(1)) {
SetValue(1, (int)round($chargeW, 0));
// Modus setzen (ein Register)
$setInt($varMode, $modeDisch);
// GoodWe: Entladen nutzt trotzdem powerbat_laden (immer positiv)
if (strpos($t, "goodwe") !== false) {
$setW($varPowerCharge, $dischargeW);
$setW($varPowerDisch, 0.0);
return;
}
if (IPS_VariableExists(2)) {
SetValue(2, (int)round($dischargeW, 0));
// SolarEdge + Default: Entladen in powerbat_entladen
$setW($varPowerDisch, $dischargeW);
$setW($varPowerCharge, 0.0);
return;
}
$this->SendDebug(
"WriteByVendorRegisters",
"DEFAULT ($typ): charge={$chargeW}W -> Reg1, discharge={$dischargeW}W -> Reg2",
0
);
}
// =========================
// Stop / Neutral
// =========================
$setW($varPowerCharge, 0.0);
$setW($varPowerDisch, 0.0);
// optional: Modus nicht anfassen oder auf 0 setzen:
// $setInt($varMode, 0);
}
}