From 3b14b90e124f3fb6559ed31a79b229ba0bf4fa26 Mon Sep 17 00:00:00 2001 From: "belevo\\mh" Date: Tue, 9 Jun 2026 15:16:16 +0200 Subject: [PATCH] =?UTF-8?q?=20=20=20=20=20=20=20=20if=20($Peak=20&&=20$net?= =?UTF-8?q?zbezug=20>=200)=20{=20=20=20=20=20=20=20=20=20=20=20=20=20$netz?= =?UTF-8?q?bezug=20=3D=20-min($netzbezug,=20$maxentladeleistung)=20+=20$ba?= =?UTF-8?q?t=5Fleistung;=20=20=20=20=20=20=20=20=20}=20hinzgef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Batterie/form.json | 5 + Batterie/module.php | 337 +++++++++++++++++++++++--------------------- 2 files changed, 183 insertions(+), 159 deletions(-) diff --git a/Batterie/form.json b/Batterie/form.json index b95e24d..659af31 100644 --- a/Batterie/form.json +++ b/Batterie/form.json @@ -85,6 +85,11 @@ "type": "SelectVariable", "name": "Netzbezug", "caption": "Variable mit dem zu regelnden Netzbezug" + }, + { + "type": "SelectVariable", + "name": "AktuelleBatterieleistung", + "caption": "Variable mit der aktueller Batterieleistung" }, { "type": "Label", diff --git a/Batterie/module.php b/Batterie/module.php index 4f045fd..0ebec1b 100644 --- a/Batterie/module.php +++ b/Batterie/module.php @@ -15,27 +15,25 @@ class Batterie extends IPSModule $this->RegisterPropertyInteger("Batteriemanagement", 1); $this->RegisterPropertyInteger("Batterietyp", 1); $this->RegisterPropertyInteger("MaxNachladen",0); - $this->RegisterPropertyInteger("Netzbezug", 0); // Initialisierung mit 0 + $this->RegisterPropertyInteger("Netzbezug", 0); + $this->RegisterPropertyInteger("AktuelleBatterieleistung", 0);// Initialisierung mit 0 $this->RegisterPropertyInteger("Interval", 2); // Recheninterval - - - // Variabeln für Kommunkation mit Manager $this->RegisterVariableInteger("Batteriemanagement_Variabel","Batteriemanagement_Variabel", "",0); - $this->RegisterVariableInteger("Laden_Entladen","Laden_Entladen", "",3); - $this->RegisterVariableInteger("Aktuelle_Leistung", "Aktuelle_Leistung", "", 0); - $this->RegisterVariableString("PowerSteps", "PowerSteps"); - $this->RegisterVariableBoolean("Idle", "Idle", "", 0); - $this->RegisterVariableInteger("Sperre_Prio", "Sperre_Prio"); - $this->RegisterVariableInteger("PV_Prio", "PV_Prio"); - $this->RegisterVariableInteger("Power", "Power"); - $this->RegisterVariableBoolean("Is_Peak_Shaving", "Is_Peak_Shaving"); - $this->RegisterVariableInteger("Leistung_Delta", "Leistung_Delta", "", 0); - + $this->RegisterVariableInteger("Laden_Entladen","Laden_Entladen", "",3); $this->RegisterVariableBoolean("Hysterese", "Hysterese","",false); - - $this->RegisterVariableFloat("Bezogene_Energie", "Bezogene_Energie", "", 0); + + // Variabeln für Kommunkation mit Manager + $this->RegisterVariableInteger("Aktuelle_Leistung", "Aktuelle zugeteilte Leistung", "", 0); + $this->RegisterVariableString("PowerSteps", "Leistungsschritte"); + $this->RegisterVariableBoolean("Idle", "Idle", "", 0); + $this->RegisterVariableInteger("Sperre_Prio", "Priorität Peakshaving"); + $this->RegisterVariableInteger("PV_Prio", "Priorität PV"); + $this->RegisterVariableInteger("Power", "Leistung Batterie"); + $this->RegisterVariableBoolean("Is_Peak_Shaving", "Peakshavingmodus"); + $this->RegisterVariableInteger("Leistung_Delta", "Leistung Delta", "", 0); + $this->RegisterVariableFloat("Bezogene_Energie", "Bezogene Energie", "", 0); // Hilfsvariabeln für Idle zustand $this->RegisterPropertyInteger("IdleCounterMax", 2); @@ -43,8 +41,7 @@ class Batterie extends IPSModule $this->SetValue("IdleCounter", 0); // Initialisiere Idle - $this->SetValue("Idle", true); - + $this->RegisterTimer("Timer_Do_UserCalc_Battery",$this->ReadPropertyInteger("Interval")*1000,"IPS_RequestAction(" .$this->InstanceID .', "Do_UserCalc", "");'); @@ -60,37 +57,42 @@ class Batterie extends IPSModule $batterietyp = $this->ReadPropertyInteger("Batterietyp"); + // Batterietypabhängige Variabeln Initialisieren switch ($batterietyp) { - case 1: // Goodwe - $this->MaintainVariable("Goodwe_EntLadeleistung", "Goodwe_EntLadeleistung", VARIABLETYPE_INTEGER, "", 10, true); + case 0: // Herstellerunabhängig + + $this->MaintainVariable("Goodwe_EntLadeleistung", "Goodwe_EntLadeleistung", VARIABLETYPE_FLOAT, "", 10, false); + $this->MaintainVariable("Ladeleistung", "Ladeleistung", VARIABLETYPE_FLOAT, "", 11, false); + $this->MaintainVariable("Entladeleistung", "Entladeleistung", VARIABLETYPE_FLOAT, "", 13, false); + break; + + case 1: // Goodwe + $this->MaintainVariable("Goodwe_EntLadeleistung", "Goodwe_EntLadeleistung", VARIABLETYPE_FLOAT, "", 10, true); $this->MaintainVariable("Ladeleistung", "Ladeleistung", VARIABLETYPE_FLOAT, "", 11, false); $this->MaintainVariable("Entladeleistung", "Entladeleistung", VARIABLETYPE_FLOAT, "", 13, false); - $this->MaintainVariable("Laden_Entladen", "Laden_Entladen", VARIABLETYPE_INTEGER, "", 12, true); break; case 2: // Solaredge - $this->MaintainVariable("Goodwe_EntLadeleistung", "Goodwe_EntLadeleistung", VARIABLETYPE_INTEGER, "", 10, false); + $this->MaintainVariable("Goodwe_EntLadeleistung", "Goodwe_EntLadeleistung", VARIABLETYPE_FLOAT, "", 10, false); $this->MaintainVariable("Ladeleistung", "Ladeleistung", VARIABLETYPE_FLOAT, "", 11, true); $this->MaintainVariable("Entladeleistung", "Entladeleistung", VARIABLETYPE_FLOAT, "", 13, true); - $this->MaintainVariable("Laden_Entladen", "Laden_Entladen", VARIABLETYPE_INTEGER, "", 12, true); break; case 3: // SiG Energy - $this->MaintainVariable("Goodwe_EntLadeleistung", "Goodwe_EntLadeleistung", VARIABLETYPE_INTEGER, "", 10, false); + $this->MaintainVariable("Goodwe_EntLadeleistung", "Goodwe_EntLadeleistung", VARIABLETYPE_FLOAT, "", 10, false); $this->MaintainVariable("Ladeleistung", "Ladeleistung", VARIABLETYPE_FLOAT, "", 11, true); $this->MaintainVariable("Entladeleistung", "Entladeleistung", VARIABLETYPE_FLOAT, "", 13, true); - $this->MaintainVariable("Laden_Entladen", "Laden_Entladen", VARIABLETYPE_INTEGER, "", 12, true); break; default: - // Sicherheit: alles weg - $this->MaintainVariable("Goodwe_EntLadeleistung", "Goodwe_EntLadeleistung", VARIABLETYPE_INTEGER, "", 10, false); + // Default, werden alle ausgeschaltet + $this->MaintainVariable("Goodwe_EntLadeleistung", "Goodwe_EntLadeleistung", VARIABLETYPE_FLOAT, "", 10, false); $this->MaintainVariable("Ladeleistung", "Ladeleistung", VARIABLETYPE_FLOAT, "", 11, false); - $this->MaintainVariable("Laden_Entladen", "Laden_Entladen", VARIABLETYPE_INTEGER, "", 12, false); $this->MaintainVariable("Entladeleistung", "Entladeleistung", VARIABLETYPE_FLOAT, "", 13, false); break; } - + + // $maxBatVar = $this->ReadPropertyInteger("MaxBatterieleistung"); $maxNachVar = $this->ReadPropertyInteger("MaxNachladen"); @@ -104,99 +106,6 @@ class Batterie extends IPSModule } - public function MessageSink($TimeStamp, $SenderID, $Message, $Data) -{ - if ($Message !== VM_UPDATE) { - return; - } - - $maxBatVar = $this->ReadPropertyInteger("MaxBatterieleistung"); - $maxNachVar = $this->ReadPropertyInteger("MaxNachladen"); - - if ($SenderID === $maxBatVar || $SenderID === $maxNachVar) { - // PowerSteps sofort neu berechnen (mit aktuellem Peak-Status) - $this->GetCurrentData($this->GetValue("Is_Peak_Shaving")); - } -} - - - -private function GeneratePowerSteps($additionalValue) -{ - - $maxleistung_raw = GetValue($this->ReadPropertyInteger("MaxBatterieleistung")); - $nachladen_raw = GetValue($this->ReadPropertyInteger("MaxNachladen")); - - $stepSize = 250; // Grobe Schrittgröße - $stepSizeSmall = 50; // Feine Schrittgröße - - // Grenzen auf 50er abrunden (floor) - $maxleistung = (int)(floor($maxleistung_raw / $stepSizeSmall) * $stepSizeSmall); - $minleistung = (int)(-floor($nachladen_raw / $stepSizeSmall) * $stepSizeSmall); // negativ! - - // Sicherheitscheck: falls Werte komisch sind - if ($maxleistung < 0) $maxleistung = 0; - if ($minleistung > 0) $minleistung = 0; - - // Grundarray: von min bis max in 250er Schritten - $neg = ($minleistung < 0) ? range($minleistung, 0, $stepSize) : [0]; - $pos = range(0, $maxleistung, $stepSize); - - $array_powersteps = array_values(array_unique(array_merge($neg, $pos))); - sort($array_powersteps, SORT_NUMERIC); - - // Zusätzlichen Wert auf 50er abrunden (floor, nicht round!) - // (wichtig: floor bei negativen Zahlen geht "weiter runter", daher extra Logik) - $closestValue = (int)(floor($additionalValue / $stepSizeSmall) * $stepSizeSmall); - - // Clamp in den Bereich - if ($closestValue < $minleistung) $closestValue = $minleistung; - if ($closestValue > $maxleistung) $closestValue = $maxleistung; - - // Prüfen ob der Wert im Array existiert (bei 250er Raster oft NICHT) - $index = array_search($closestValue, $array_powersteps, true); - - // Wenn nicht vorhanden: an der richtigen Stelle einsortieren - if ($index === false) { - $index = 0; - $count = count($array_powersteps); - while ($index < $count && $array_powersteps[$index] < $closestValue) { - $index++; - } - // $index ist jetzt Einfügeposition - } - - // Feine Werte um closestValue herum (±4 * 50) - $newValues = []; - for ($i = -4; $i <= 4; $i++) { - $v = $closestValue + ($i * $stepSizeSmall); - if ($v >= $minleistung && $v <= $maxleistung) { - $newValues[] = $v; - } - } - - // Duplikate vermeiden (falls schon Werte vorhanden sind) - $newValues = array_values(array_unique($newValues)); - - // Wenn closestValue exakt im Grundarray war: diesen einen ersetzen - // sonst: feinwerte einfach an der Einfügestelle einfügen - if (array_search($closestValue, $array_powersteps, true) !== false) { - $existingIndex = array_search($closestValue, $array_powersteps, true); - array_splice($array_powersteps, $existingIndex, 1, $newValues); - } else { - array_splice($array_powersteps, $index, 0, $newValues); - } - - // Am Ende sortieren + Duplikate killen (sicher ist sicher) - $array_powersteps = array_values(array_unique($array_powersteps)); - sort($array_powersteps, SORT_NUMERIC); - - return $array_powersteps; -} // Ende Array Steps - - - - public function RequestAction($Ident, $Value) { @@ -218,32 +127,55 @@ public function RequestAction($Ident, $Value) break; default: - throw new Exception("Invalid Ident"); + break; + } } + // Nach aufruf bei anpassung Batteriewerte, sofort Getcurrentdata aufrufen. + public function MessageSink($TimeStamp, $SenderID, $Message, $Data) +{ + if ($Message !== VM_UPDATE) { + return; + } + + $maxBatVar = $this->ReadPropertyInteger("MaxBatterieleistung"); + $maxNachVar = $this->ReadPropertyInteger("MaxNachladen"); + + if ($SenderID === $maxBatVar || $SenderID === $maxNachVar) { + // PowerSteps sofort neu berechnen (mit aktuellem Peak-Status) + $this->GetCurrentData($this->GetValue("Is_Peak_Shaving")); + } +} + + + + // Aktuelle Leistung setzen lassen, der Manager vorgibt public function SetAktuelle_Leistung(int $power) { - $batterietyp = $this->ReadPropertyInteger("Batterietyp"); $batterieManagement = $this->ReadPropertyInteger("Batteriemanagement"); + //Fallunterscheidung, soll Enelix überhaupt steuern? + + // Wechselrichter steuert // Goodwe, Solaredge WR Modus if ($batterieManagement == 1 && ($batterietyp == 1 || $batterietyp == 2)) { $this->SetValue("Entladeleistung", 0); $this->SetValue("Ladeleistung", 0); $this->SetValue("Batteriemanagement_Variabel", 1); return; - //Sig Energy WR Modus - } elseif ($batterieManagement == 1 && $batterietyp == 3) { + //Sig Energy, Herstellerunabhänig im WR Modus + } elseif ($batterieManagement == 1 && ($batterietyp == 0 || $batterietyp == 3)) { $this->SetValue("Entladeleistung", 0); $this->SetValue("Ladeleistung", 0); $this->SetValue("Batteriemanagement_Variabel", 0); return; + // Enelix Steuert // Sig Energy Symcon Modus - } elseif ($batterieManagement == 2 && $batterietyp == 3) { + } elseif ($batterieManagement == 2 && ($batterietyp == 0 || $batterietyp == 3)) { $this->SetValue("Batteriemanagement_Variabel", 1); //Solaredge Symcon Modus @@ -251,12 +183,37 @@ public function RequestAction($Ident, $Value) $this->SetValue("Batteriemanagement_Variabel", 4); } - + // Variabeln entsprechend gewältem Batterietyp setzen $batterietyp = $this->ReadPropertyInteger("Batterietyp"); - if ($batterietyp == 1) {//Goodwe - $this->SetValue("Entladeleistung", 0); - $this->SetValue("Ladeleistung", 0); - //-----------------------Gooodwee-------------------------------------// + if ($batterietyp == 0) {//Herstellerunabhängig + + if($this->GetValue("Is_Peak_Shaving")==true){ + + if ($power >= 0) { + $this->SetValue("Ladeleistung", $power); + $this->SetValue("Entladeleistung", 0); + $this->SetValue("Laden_Entladen", 0); + } else { + $this->SetValue("Entladeleistung", abs($power)); + $this->SetValue("Ladeleistung", 0); + $this->SetValue("Laden_Entladen", 1); + } + + }else{ + if ($power >= 0) { + $this->SetValue("Ladeleistung", $power); + $this->SetValue("Entladeleistung", 0); + $this->SetValue("Laden_Entladen", 0); + } else { + $this->SetValue("Entladeleistung", abs($power)); + $this->SetValue("Ladeleistung", 0); + $this->SetValue("Laden_Entladen", 1); + } + } + } + elseif ($batterietyp == 1) {//Goodwe + //$this->SetValue("Entladeleistung", 0); -> ausskommentiert am 01.06.2026 wegen evt vielen Fehlerlogs + //$this->SetValue("Ladeleistung", 0); if($this->GetValue("Is_Peak_Shaving")==true){ if ($power >= 0) { @@ -281,8 +238,6 @@ public function RequestAction($Ident, $Value) }elseif ($batterietyp == 2) {//Solaredge - //-----------------------Solaredge-------------------------------------// - $this->SetValue("Goodwe_EntLadeleistung",0); if($this->GetValue("Is_Peak_Shaving")==true){ if ($power >= 0) { @@ -308,8 +263,6 @@ public function RequestAction($Ident, $Value) } } elseif ($batterietyp == 3) {//Sig Energy - //-----------------------Sig Energy-------------------------------------// - $this->SetValue("Goodwe_EntLadeleistung",0); if($this->GetValue("Is_Peak_Shaving")==true){ if ($power >= 0) { @@ -335,16 +288,7 @@ public function RequestAction($Ident, $Value) } } - - - - - - - - - - + // Prüfe auf Änderung der Leistung im Vergleich zur letzten Einstellung @@ -370,12 +314,12 @@ public function RequestAction($Ident, $Value) public function GetCurrentData(bool $Peak) { - IPS_LogMessage("Batterie", "Currentdata"); - - $array_powersteps = $this->GeneratePowerSteps($this->GetValue("Aktuelle_Leistung")); + // Aktuelle Daten ausarbeiten und Berechnen + $array_powersteps = $this->GeneratePowerSteps(); $aufdasnachladen = $this->ReadPropertyInteger("AufdasNachladen"); $minimumentladen = $this->ReadPropertyInteger("MinimumEntladen"); - $maxleistung = GetValue($this->ReadPropertyInteger("MaxBatterieleistung")); + $maxleistung = GetValue($this->ReadPropertyInteger("MaxBatterieleistung"));//Ladeleistung + $maxentladeleistung = GetValue($this->ReadPropertyInteger("MaxNachladen"));//Entladeleistung $dummy_array = []; $batterieladezustand = GetValue($this->ReadPropertyInteger("Batterieladezustand")); $filtered_powersteps_entladen = []; @@ -384,9 +328,13 @@ public function RequestAction($Ident, $Value) return $this->SetValue("PowerSteps", json_encode($dummy_array)); } + $netzbezug = GetValue($this->ReadPropertyInteger("Netzbezug")); - if (abs($netzbezug) > $maxleistung) { - $netzbezug = $maxleistung * (-1); + $bat_leistung = GetValue($this->ReadPropertyInteger("AktuelleBatterieleistung")); + + + if ($Peak && $netzbezug > 0) { + $netzbezug = -min($netzbezug, $maxentladeleistung) + $bat_leistung; } if($batterieladezustand>(2+$aufdasnachladen)){ @@ -400,7 +348,6 @@ public function RequestAction($Ident, $Value) $hyst = $this->GetValue("Hysterese"); if($Peak){ - IPS_LogMessage("Batterie", "Im if teil"); if($batterieladezustand>$aufdasnachladen && $hyst==false){ @@ -430,11 +377,9 @@ public function RequestAction($Ident, $Value) } }else{ - IPS_LogMessage("Batterie", "Im else teil"); if($batterieladezustand>99){ - IPS_LogMessage("Batterie", "im 1"); $filtered_powersteps = array_filter($array_powersteps, function ($value) { return $value <= 0; @@ -445,7 +390,6 @@ public function RequestAction($Ident, $Value) }elseif($batterieladezustand>$aufdasnachladen && $hyst==false){ $this->SetValue("PowerSteps", json_encode($array_powersteps)); - IPS_LogMessage("Batterie", "im 2"); }elseif($batterieladezustand>=$aufdasnachladen && $hyst==true){ @@ -459,9 +403,9 @@ public function RequestAction($Ident, $Value) }elseif($batterieladezustand<$aufdasnachladen){ - $dummy_array[] = GetValue($this->ReadPropertyInteger("MaxNachladen")); + //$dummy_array[] = GetValue($this->ReadPropertyInteger("MaxNachladen")); + $dummy_array[] = GetValue($this->ReadPropertyInteger("MaxBatterieleistung")); $this->SetValue("PowerSteps", json_encode($dummy_array)); - IPS_LogMessage("Batterie", "im 3"); } @@ -470,10 +414,85 @@ public function RequestAction($Ident, $Value) } + // Ab hier Hilfsfunktionen + +private function GeneratePowerSteps() +{ + // Hilfsfunktion um die Möglichen Powersteps zu generieren + $maxleistung_raw = GetValue($this->ReadPropertyInteger("MaxBatterieleistung")); + $nachladen_raw = GetValue($this->ReadPropertyInteger("MaxNachladen")); + + $stepSize = 250; // Grobe Schrittgröße + $stepSizeSmall = 50; // Feine Schrittgröße + + // Grenzen auf 50er abrunden (floor) + $maxleistung = (int)(floor($maxleistung_raw / $stepSizeSmall) * $stepSizeSmall); + $minleistung = (int)(-floor($nachladen_raw / $stepSizeSmall) * $stepSizeSmall); // negativ! + + // Sicherheitscheck: falls Werte komisch sind + if ($maxleistung < 0) $maxleistung = 0; + if ($minleistung > 0) $minleistung = 0; + + // Grundarray: von min bis max in 250er Schritten + $neg = ($minleistung < 0) ? range($minleistung, 0, $stepSize) : [0]; + $pos = range(0, $maxleistung, $stepSize); + + $array_powersteps = array_values(array_unique(array_merge($neg, $pos))); + sort($array_powersteps, SORT_NUMERIC); + + $additionalValue = 0; + // Zusätzlichen Wert auf 50er abrunden + $closestValue = (int)(floor($additionalValue / $stepSizeSmall) * $stepSizeSmall); + + // Clamp in den Bereich + if ($closestValue < $minleistung) $closestValue = $minleistung; + if ($closestValue > $maxleistung) $closestValue = $maxleistung; + + // Prüfen ob der Wert im Array existiert (bei 250er Raster oft NICHT) + $index = array_search($closestValue, $array_powersteps, true); + + // Wenn nicht vorhanden: an der richtigen Stelle einsortieren + if ($index === false) { + $index = 0; + $count = count($array_powersteps); + while ($index < $count && $array_powersteps[$index] < $closestValue) { + $index++; + } + } + + // Feine Werte um closestValue herum (±4 * 50) + $newValues = []; + for ($i = -4; $i <= 4; $i++) { + $v = $closestValue + ($i * $stepSizeSmall); + if ($v >= $minleistung && $v <= $maxleistung) { + $newValues[] = $v; + } + } + + // Duplikate vermeiden (falls schon Werte vorhanden sind) + $newValues = array_values(array_unique($newValues)); + + // Wenn closestValue exakt im Grundarray war: diesen einen ersetzen + // sonst: feinwerte einfach an der Einfügestelle einfügen + if (array_search($closestValue, $array_powersteps, true) !== false) { + $existingIndex = array_search($closestValue, $array_powersteps, true); + array_splice($array_powersteps, $existingIndex, 1, $newValues); + } else { + array_splice($array_powersteps, $index, 0, $newValues); + } + + // Am Ende sortieren + $array_powersteps = array_values(array_unique($array_powersteps)); + sort($array_powersteps, SORT_NUMERIC); + + return $array_powersteps; +} + + private function CheckIdle($power) - { + { // Hilfsfunktion zum Prüfen ob die Batterie schon wieder Leistung verändern kann $lastpower = GetValue("Aktuelle_Leistung"); if ($lastpower != GetValue("Aktuelle_Leistung")) { $this->SetValue("Idle", false);