no message
This commit is contained in:
@@ -67,22 +67,13 @@
|
|||||||
}
|
}
|
||||||
,
|
,
|
||||||
{
|
{
|
||||||
"caption":"Register Laden Modus",
|
"caption": "Register Laden/Entladen Modus",
|
||||||
"name":"register_laden_modus",
|
"name": "register_ladenentladen_modus",
|
||||||
"width":"200px",
|
"width": "260px",
|
||||||
"add":0,
|
"add": 0,
|
||||||
"edit":{
|
"edit": {
|
||||||
"type":"SelectVariable"
|
"type": "SelectVariable"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"caption":"Register Entladen Modus",
|
|
||||||
"name":"register_entladen_modus",
|
|
||||||
"width":"200px",
|
|
||||||
"add":0,
|
|
||||||
"edit":{
|
|
||||||
"type":"SelectVariable"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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");
|
$calcJsonId = $this->GetIDForIdent("CalcJSON");
|
||||||
$calc = json_decode((string)GetValue($calcJsonId), true);
|
// Fallback, falls Variable leer ist oder nicht existiert
|
||||||
|
if (!IPS_VariableExists($calcJsonId)) {
|
||||||
if (!is_array($calc) || empty($calc["batteries"])) {
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
$rawJson = (string)GetValue($calcJsonId);
|
||||||
|
if (empty($rawJson)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$calc = json_decode($rawJson, true);
|
||||||
|
$batteries = $calc['batteries'] ?? [];
|
||||||
|
|
||||||
foreach ($calc["batteries"] as $bat) {
|
// ---------------------------------------------------------
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
$idx = (int)$bat["idx"];
|
if (abs($targetPower) < 0.01) {
|
||||||
$typ = (string)$bat["typ"];
|
return $result; // Nichts zu tun
|
||||||
|
}
|
||||||
|
|
||||||
$evSoc = (float)$bat["EV_SOC"];
|
$isCharge = ($targetPower > 0);
|
||||||
$sdlSoc = (float)$bat["SDL_SOC"];
|
$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';
|
||||||
|
|
||||||
$sdlChargeKW = (float)$bat["SDL_Charge_kW"];
|
// 1. Batterien vorbereiten und gruppieren nach gerundetem SoC
|
||||||
$sdlDischargeKW = (float)$bat["SDL_Discharge_kW"];
|
$groups = [];
|
||||||
$evChargeKW = (float)$bat["EV_Charge_kW"];
|
foreach ($batteries as $bat) {
|
||||||
$evDischargeKW = (float)$bat["EV_Discharge_kW"];
|
$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;
|
||||||
|
|
||||||
/*
|
|
||||||
// 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
|
|
||||||
];
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function WriteBatteryPowerSetpoints(array $distribution): void
|
private function WriteBatteryPowerSetpoints(array $distribution): void
|
||||||
{
|
{
|
||||||
// Batteries-Config laden (enthält register_laden / register_entladen)
|
|
||||||
$batteriesCfg = json_decode($this->ReadPropertyString("Batteries"), true);
|
$batteriesCfg = json_decode($this->ReadPropertyString("Batteries"), true);
|
||||||
if (!is_array($batteriesCfg) || empty($batteriesCfg)) {
|
if (!is_array($batteriesCfg) || empty($batteriesCfg)) {
|
||||||
return;
|
return;
|
||||||
@@ -511,7 +599,6 @@ class Bat_EV_SDL_V2 extends IPSModule
|
|||||||
|
|
||||||
foreach ($distribution as $d) {
|
foreach ($distribution as $d) {
|
||||||
|
|
||||||
// --- Batterie bestimmen ---
|
|
||||||
$idx = (int)($d["idx"] ?? -1);
|
$idx = (int)($d["idx"] ?? -1);
|
||||||
if ($idx < 0 || !isset($batteriesCfg[$idx])) {
|
if ($idx < 0 || !isset($batteriesCfg[$idx])) {
|
||||||
continue;
|
continue;
|
||||||
@@ -519,105 +606,107 @@ class Bat_EV_SDL_V2 extends IPSModule
|
|||||||
|
|
||||||
$cfg = $batteriesCfg[$idx];
|
$cfg = $batteriesCfg[$idx];
|
||||||
|
|
||||||
// typ aus distribution bevorzugen, sonst aus Config
|
|
||||||
$typ = (string)($d["typ"] ?? ($cfg["typ"] ?? ("Bat " . ($idx + 1))));
|
$typ = (string)($d["typ"] ?? ($cfg["typ"] ?? ("Bat " . ($idx + 1))));
|
||||||
|
|
||||||
// --- Sollwerte (Watt) ---
|
// ✅ immer positiv
|
||||||
$chargeW = max(0.0, (float)($d["chargeW"] ?? 0.0));
|
$chargeW = max(0.0, (float)($d["chargeW"] ?? 0.0));
|
||||||
$dischargeW = max(0.0, (float)($d["dischargeW"] ?? 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) {
|
if ($chargeW > 0.0 && $dischargeW > 0.0) {
|
||||||
$this->SendDebug(
|
$this->SendDebug("WriteBatteryPowerSetpoints", "WARN: both >0 for $typ (idx=$idx) -> set 0", 0);
|
||||||
"WriteBatteryPowerSetpoints",
|
|
||||||
"WARN: charge & discharge gleichzeitig > 0 bei $typ (idx=$idx) -> setze beides 0",
|
|
||||||
0
|
|
||||||
);
|
|
||||||
$chargeW = 0.0;
|
$chargeW = 0.0;
|
||||||
$dischargeW = 0.0;
|
$dischargeW = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Vendor-/Default-Handling über Register schreiben ---
|
$this->WriteByVendorRegistersSingleMode($typ, $cfg, $chargeW, $dischargeW);
|
||||||
$this->WriteByVendorRegisters($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
|
private function WriteByVendorRegistersSingleMode(string $typ, array $cfg, float $chargeW, float $dischargeW): void
|
||||||
{
|
{
|
||||||
$typLower = mb_strtolower($typ);
|
$t = mb_strtolower($typ);
|
||||||
|
|
||||||
// Register aus Formular
|
// Leistungs-Variablen
|
||||||
$regCharge = (int)($cfg["register_laden"] ?? 0);
|
$varPowerCharge = (int)($cfg["powerbat_laden"] ?? 0);
|
||||||
$regDisch = (int)($cfg["register_entladen"] ?? 0);
|
$varPowerDisch = (int)($cfg["powerbat_entladen"] ?? 0);
|
||||||
|
|
||||||
// -----------------------------
|
// ✅ EIN Modus-Register
|
||||||
// GOODWE → ein Register, signed
|
$varMode = (int)($cfg["register_ladenentladen_modus"] ?? 0);
|
||||||
// -----------------------------
|
|
||||||
if (strpos($typLower, "goodwe") !== false) {
|
|
||||||
|
|
||||||
if ($regCharge > 0 && IPS_VariableExists($regCharge)) {
|
// sichere Writer
|
||||||
$signedW = 0.0;
|
$setInt = function(int $varId, int $value): void {
|
||||||
|
if ($varId > 0 && IPS_VariableExists($varId)) {
|
||||||
if ($chargeW > 0.0) {
|
SetValue($varId, $value);
|
||||||
$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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
// --------------------------------
|
$setW = function(int $varId, float $w): void {
|
||||||
// SOLAREDGE → getrennte Register
|
if ($varId > 0 && IPS_VariableExists($varId)) {
|
||||||
// --------------------------------
|
SetValue($varId, (int)round(max(0.0, $w), 0)); // ✅ niemals negativ
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// --------------------------------
|
// Moduscodes je Typ
|
||||||
// DEFAULT → getrennt, fest: 1 / 2
|
$modeCharge = 1; $modeDisch = 2; // Default
|
||||||
// --------------------------------
|
if (strpos($t, "goodwe") !== false) {
|
||||||
// Laden = Register 1, Entladen = Register 2
|
$modeCharge = 11; $modeDisch = 12;
|
||||||
|
} elseif (strpos($t, "solaredge") !== false) {
|
||||||
if (IPS_VariableExists(1)) {
|
$modeCharge = 3; $modeDisch = 4;
|
||||||
SetValue(1, (int)round($chargeW, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IPS_VariableExists(2)) {
|
|
||||||
SetValue(2, (int)round($dischargeW, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->SendDebug(
|
|
||||||
"WriteByVendorRegisters",
|
|
||||||
"DEFAULT ($typ): charge={$chargeW}W -> Reg1, discharge={$dischargeW}W -> Reg2",
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// Laden
|
||||||
|
// =========================
|
||||||
|
if ($chargeW > 0.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 + Default: zwei Leistungsregister
|
||||||
|
$setW($varPowerCharge, $chargeW);
|
||||||
|
$setW($varPowerDisch, 0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// Entladen
|
||||||
|
// =========================
|
||||||
|
if ($dischargeW > 0.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SolarEdge + Default: Entladen in powerbat_entladen
|
||||||
|
$setW($varPowerDisch, $dischargeW);
|
||||||
|
$setW($varPowerCharge, 0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// Stop / Neutral
|
||||||
|
// =========================
|
||||||
|
$setW($varPowerCharge, 0.0);
|
||||||
|
$setW($varPowerDisch, 0.0);
|
||||||
|
// optional: Modus nicht anfassen oder auf 0 setzen:
|
||||||
|
// $setInt($varMode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|||||||
Reference in New Issue
Block a user