RegisterPropertyInteger("Peakleistung", 0); $this->RegisterPropertyInteger("Ueberschussleistung", 0); $this->RegisterPropertyInteger("Netzbezug", 0); // Initialisierung mit 0 $this->RegisterPropertyString("Verbraucher_Liste", "[]"); $this->RegisterPropertyBoolean("UmschaltpunktStatisch", false); // Initialisierung mit 0 $this->RegisterPropertyInteger("Umschalt_Solarladen", 100); // Initialisierung mit 0 $this->RegisterPropertyInteger("Umschalt_Peakshaving", 10000); // Initialisierung mit 0 $this->RegisterPropertyBoolean("HauptmanagerAktiv", false); // Initialisierung mit 0 $this->RegisterPropertyInteger("ManagerID", 0); // Initialisierung mit 0 $this->RegisterPropertyInteger("DatenHoch", 0); // Initialisierung mit 0 $this->RegisterPropertyInteger("DatenZuruck", 0); // Initialisierung mit 0 $this->RegisterPropertyInteger("Interval", 2); // Recheninterval $this->RegisterVariableBoolean("Is_Peak_Shaving", false); // Timer registrieren $this->RegisterTimer( "Timer_DistributeEnergy", $this->ReadPropertyInteger("Interval") * 1000, "IPS_RequestAction(" . $this->InstanceID . ', "DistributeEnergy", "");' ); } public function ApplyChanges() { parent::ApplyChanges(); $this->SetTimerInterval( "Timer_DistributeEnergy", $this->ReadPropertyInteger("Interval") * 1000 ); } public function RequestAction($Ident, $Value) { switch ($Ident) { case "DistributeEnergy": // Fallunterscheidung, ist der Manager stand-alone oder in einem verbund mit Hauptmanager? if ($this->ReadPropertyBoolean("HauptmanagerAktiv") == true) { // Wenn im Verbund, parse die Verbraucherdaten und sende sie dem Hauptmanager, sofern dieser aktiv ist $data = json_decode( GetValue($this->ReadPropertyInteger("DatenZuruck")), true ); if (isset($data["Timestamp"])) { $timestamp = $data["Timestamp"]; $currentTime = time(); if ($currentTime - $timestamp < 3600) { $this->DistributeEnergy_Extern(); } else { $sendarray = [ "Netzbezug" => GetValue( $this->ReadPropertyInteger("Netzbezug") ), "Timestamp" => time(), ]; SetValue( $this->ReadPropertyInteger("DatenHoch"), json_encode($sendarray) ); $this->DistributeEnergy(); } } else { $sendarray = [ "Netzbezug" => GetValue( $this->ReadPropertyInteger("Netzbezug") ), "Timestamp" => time(), ]; SetValue( $this->ReadPropertyInteger("DatenHoch"), json_encode($sendarray) ); $this->DistributeEnergy(); } } else { // wenn stand-alone, rufe einfach die Methode zum verteilen auf $this->DistributeEnergy(); } break; case "ApplyChanges": $this->ApplyChanges(); break; default: throw new Exception("Invalid Ident"); } } public function DistributeEnergy() { // Alle Energieverbraucher auslesen und dekodieren $Verbraucher_Liste = json_decode( $this->ReadPropertyString("Verbraucher_Liste"), true ); if (empty($Verbraucher_Liste)) { // Wenn keine Verbruacher angemeldet sind abbrechen return; } // Hilfsvariabeln initialiseren $filteredVerbraucher = []; // Array das später mit allen Verbrauchsdaten der Energieverbraucher gefüllt wird $allIdle = true; // Variable zur Überprüfung, ob alle Benutzer Idle = true sind $totalAktuelle_Leistung = 0; // Variable zur Summierung der Aktuelle_Leistung Werte $helpvar_offset_peakermitteln = 0; // Variable zur Summierung der Aktuelle_Leistung Werte // Fülle das Array mit allen entsprechenden Werten der Verbraucher ab foreach ($Verbraucher_Liste as $user) { // Werte direkt von der Verbraucher-Instanz abrufen $Aktuelle_Leistung = GetValue( IPS_GetObjectIDByIdent("Power", $user["Verbraucher"]) ); // Aktuelle Leistung durch Power ersetzt $Bezogene_Energie = GetValue( IPS_GetObjectIDByIdent("Bezogene_Energie", $user["Verbraucher"]) ); $PV_Prio = GetValue( IPS_GetObjectIDByIdent("PV_Prio", $user["Verbraucher"]) ); $Sperre_Prio = GetValue( IPS_GetObjectIDByIdent("Sperre_Prio", $user["Verbraucher"]) ); $idle = GetValue( IPS_GetObjectIDByIdent("Idle", $user["Verbraucher"]) ); $powerStepsJson = GetValue( IPS_GetObjectIDByIdent("PowerSteps", $user["Verbraucher"]) ); $powerSteps = json_decode($powerStepsJson, true); $delta = GetValue( IPS_GetObjectIDByIdent("Leistung_Delta", $user["Verbraucher"]) ); // Verbraucher-Daten zum gefilterten Array hinzufügen $filteredVerbraucher[] = [ "Verbraucher" => $user["Verbraucher"], "InstanceID" => $user["Verbraucher"], "Aktuelle_Leistung" => $Aktuelle_Leistung, "Bezogene_Energie" => $Bezogene_Energie, "PV_Prio" => $PV_Prio, "Sperre_Prio" => $Sperre_Prio, "Idle" => $idle, "PowerSteps" => $powerSteps, "Leistung_Delta" => $delta, ]; // Überprüfen, ob alle Benutzer Idle = true sind (bereit für eine Leistungsänderung), wenn einer nicht ist, wird später verworfen... if (!$idle) { $allIdle = false; } // Addiere die aktuell bereits verwendete Leistung auf, um sie bei der verteilung zu berücksichtigen $totalAktuelle_Leistung += $Aktuelle_Leistung - $delta; } // Variante der Leistungsregelung (Überschuss, Peak) ermitteln und aktuellen Netzbezug bereitstellen $Netzbezug = GetValue($this->ReadPropertyInteger("Netzbezug")); $Peakleistung = $this->ReadPropertyInteger("Peakleistung"); $Ueberschussleistung = $this->ReadPropertyInteger( "Ueberschussleistung" ); // Dynamische ermittlung der Betriebsart if ($this->ReadPropertyBoolean("UmschaltpunktStatisch") == false) { // Fallunterscheidung ob auf Solarladen oder Peakshaving gerregelt wird. if ($Netzbezug < ($Peakleistung + $Ueberschussleistung) / 2) { $remainingPower = -1 * (-1 * $Ueberschussleistung + $Netzbezug); $Is_Peak_Shaving = false; } else { $remainingPower = $Peakleistung - $Netzbezug; $Is_Peak_Shaving = true; } } // Statische ermittlung der Betriebsart else { if ( $Netzbezug < $this->ReadPropertyInteger("Umschalt_Solarladen") ) { $remainingPower = -1 * (-1 * $Ueberschussleistung + $Netzbezug); $Is_Peak_Shaving = false; } elseif ( $Netzbezug > $this->ReadPropertyInteger("Umschalt_Peakshaving") ) { $remainingPower = $Peakleistung - $Netzbezug; $Is_Peak_Shaving = true; } elseif ($this->GetValue("Is_Peak_Shaving") == false) { $remainingPower = -1 * (-1 * $Ueberschussleistung + $Netzbezug); $Is_Peak_Shaving = false; } elseif ($this->GetValue("Is_Peak_Shaving") == true) { $remainingPower = $Peakleistung - $Netzbezug; $Is_Peak_Shaving = true; } } // Aktuelle bereits verwendete Leistung auf die verbleibende Leistung als Offset summieren. $remainingPower += $totalAktuelle_Leistung; // Frage alle Energieverbraucher ab, was sie für Leistungen benötigen könnten foreach ($Verbraucher_Liste as $user) { if (IPS_InstanceExists($user["Verbraucher"])) { IPS_RequestAction( $user["Verbraucher"], "GetCurrentData", $Is_Peak_Shaving ); } } // Abbrechen wenn es keine Verbruacher gibt, die verfügbar sind if (empty($filteredVerbraucher)) { return; } // Wenn nicht alle Benutzer Idle = true sind, oder sich der zustand von Is_Peak_shaving gerade verändert hat rufe SetAktuelle_Leistung mit Aktuelle_Leistung Werten auf, (alle Verbraucher behalten die aktuelle Leistung) if ( !$allIdle || $Is_Peak_Shaving != $this->GetValue("Is_Peak_Shaving") ) { foreach ($filteredVerbraucher as $user) { IPS_RequestAction( $user["InstanceID"], "SetAktuelle_Leistung", $user["Aktuelle_Leistung"] ); } $this->SetValue("Is_Peak_Shaving", $Is_Peak_Shaving); return; } $this->SetValue("Is_Peak_Shaving", $Is_Peak_Shaving); // Sortiere die Verbruacher nach Priorität entweder der PV_Prio oder der Peak Prio usort($filteredVerbraucher, function ($a, $b) use ($Is_Peak_Shaving) { $primaryKey = $Is_Peak_Shaving ? "Sperre_Prio" : "PV_Prio"; // Wenn die Prio geleich ist, sortiere danach welcher verbraucher bisher am wenigsten Energie bekommen hat. if ($a[$primaryKey] == $b[$primaryKey]) { return round($a["Bezogene_Energie"] / 2000) <=> round($b["Bezogene_Energie"] / 2000); } return $a[$primaryKey] <=> $b[$primaryKey]; }); // Primärschlüssel für die Priorität basierend auf dem Parameter auswählen (für sortierung in gruppen anschliessend) $priorityKey = $Is_Peak_Shaving ? "Sperre_Prio" : "PV_Prio"; // Schleife durch alle Prioritäten $priorities = array_unique( array_column($filteredVerbraucher, $priorityKey) ); $groupedUsers = []; foreach ($priorities as $priority) { $groupedUsers[$priority] = array_filter( $filteredVerbraucher, function ($user) use ($priority, $priorityKey) { return $user[$priorityKey] == $priority; } ); } // Jetzt werden die energien pro gruppe verteilt (Immer alle pro prio in einer gruppe miteinander) foreach ($groupedUsers as $priority => $users) { // Verbraucher mit gleicher Priorität sammeln $samePriorityUsers = isset($groupedUsers[$priority]) ? $groupedUsers[$priority] : []; // Wenn keine Verbraucher mit gleicher Priorität vorhanden sind, überspringen if (empty($samePriorityUsers)) { continue; } $withZero = []; $withoutZeroHigh = []; $withoutZeroLow = []; // Sammle alle Verbraucher die nicht null als Verbruach annehmen können. (inkl. sortierung in positive und Negative) foreach ($samePriorityUsers as $entry) { $withZero[] = $entry; if (min($entry["PowerSteps"]) > 0) { $withoutZeroHigh[] = $entry; } if (max($entry["PowerSteps"]) < 0) { $withoutZeroLow[] = $entry; } } // positiven Verbrauchern die nicht 0 annhemen können bereits den minimalwert zuteilen if (!empty($withoutZeroHigh)) { foreach ($withoutZeroHigh as $entry) { $instanceID = $entry["InstanceID"]; $minPowerStep = min($entry["PowerSteps"]); } } // negativen Verbrauchern die nicht 0 annhemen können bereits den minimalwert zuteilen if (!empty($withoutZeroLow)) { foreach ($withoutZeroLow as $entry) { $instanceID = $entry["InstanceID"]; $minPowerStep = max($entry["PowerSteps"]); } } // Nun die Verbrucher verteilen, die auch 0 erhalten können. $samePriorityUsers = $withZero; $userEnergyProv = []; foreach ($samePriorityUsers as $entry) { $instanceID = $entry["InstanceID"]; // Schutz: falls PowerSteps nicht gesetzt oder kein Array ist $steps = isset($entry["PowerSteps"]) && is_array($entry["PowerSteps"]) ? $entry["PowerSteps"] : [0]; // Wenn 0 als möglicher Step vorhanden ist -> 0 setzen $minStep = min($steps); $maxStep = max($steps); if (in_array(0, $steps, true)) { $userEnergyProv[$instanceID] = 0; continue; } elseif ($minStep > 0) { // nur positive Schritte -> minimalen positiven Wert einsetzen $userEnergyProv[$instanceID] = $minStep; // entspricht deinem vorherigen Verhalten: remainingPower reduzieren $remainingPower -= $minStep; } elseif ($maxStep < 0) { // nur negative Schritte -> maximalen (am wenigsten negativen) Wert einsetzen $userEnergyProv[$instanceID] = $maxStep; // entspricht deinem vorherigen Verhalten: remainingPower erhöhen (maxStep ist negativ) $remainingPower += $maxStep; } else { // Mixed (positive & negative) aber kein 0 -> Fallback 0 (ändere wenn nötig) $userEnergyProv[$instanceID] = 0; } } // Beginne mit aktiver Verteilung if ($remainingPower >= 0) { // Alle Schritte der Benutzer in einem Array sammeln $allSteps = []; foreach ($samePriorityUsers as $user) { foreach ($user["PowerSteps"] as $step) { if ($step >= 0) { $allSteps[] = [ "user" => $user["InstanceID"], //"Leistung_Delta" => $user["Leistung_Delta"], "step" => $step, ]; } } } // Sortiere die Schritte nach Größe usort($allSteps, function ($a, $b) { return $a["step"] <=> $b["step"]; }); //if remaining power >0 // Iteriere durch alle Schritte foreach ($allSteps as $entry) { $user = $entry["user"]; $powerstep = $entry["step"]; // Überprüfe, ob noch genügend verbleibende Energie für den nächsten Schritt vorhanden ist if ( $remainingPower >= $powerstep - $userEnergyProv[$user] ) { // oder bedingung testen // Aktualisiere die verbleibende Energie und die bereitgestellte Energie für den Benutzer $remainingPower -= $powerstep - $userEnergyProv[$user]; $userEnergyProv[$user] = $powerstep; } } // Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt foreach ($userEnergyProv as $userInstanceID => $leistung) { // Innerhalb der Schleife: alle nicht-negativen Leistungen sammeln $positiveValues = array_filter( array_column( $samePriorityUsers, "PowerSteps", "InstanceID" )[$userInstanceID], function ($l) { return $l >= 0; } ); // Falls keine Werte ≥ 0 vorhanden sind, auf 0 zurückfallen $fallbackMinimum = empty($positiveValues) ? max( array_column( $samePriorityUsers, "PowerSteps", "InstanceID" )[$userInstanceID] ) : min($positiveValues); // minimalleistung = dieser Fallback $minimalleistung = $fallbackMinimum; //den höheren Wert wählen und für IPS negativieren if (abs($leistung) > abs($minimalleistung)) { $schreibleistung = $leistung; } else { $schreibleistung = $minimalleistung; } // Die Veteilten Leistungen auf die Verbruacher schreiben if (IPS_InstanceExists($userInstanceID)) { IPS_RequestAction( $userInstanceID, "SetAktuelle_Leistung", $schreibleistung ); } } } else { // Alle Schritte der Benutzer in einem Array sammeln $allSteps = []; foreach ($samePriorityUsers as $user) { foreach ($user["PowerSteps"] as $step) { if ($step <= 0) { $allSteps[] = [ "user" => $user["InstanceID"], "step" => -1 * $step, ]; } } } // Sortiere die Schritte nach Größe usort($allSteps, function ($a, $b) { return $a["step"] <=> $b["step"]; }); $remainingPower = $remainingPower * -1; // Iteriere durch alle Schritte foreach ($allSteps as $entry) { $user = $entry["user"]; $powerstep = $entry["step"]; // Überprüfe, ob noch genügend verbleibende Energie für den nächsten Schritt vorhanden ist if ( $remainingPower >= $powerstep - $userEnergyProv[$user] ) { // oder bedingung testen // Aktualisiere die verbleibende Energie und die bereitgestellte Energie für den Benutzer $remainingPower -= $powerstep - $userEnergyProv[$user]; $userEnergyProv[$user] = $powerstep; } } $remainingPower = $remainingPower * -1; // Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt foreach ($userEnergyProv as $userInstanceID => $leistung) { // Innerhalb der Schleife: alle nicht-negativen Leistungen sammeln $positiveValues = array_filter( array_column( $samePriorityUsers, "PowerSteps", "InstanceID" )[$userInstanceID], function ($l) { return $l <= 0; } ); // Falls keine Werte ≥ 0 vorhanden sind, auf 0 zurückfallen $fallbackMinimum = empty($positiveValues) ? min( array_column( $samePriorityUsers, "PowerSteps", "InstanceID" )[$userInstanceID] ) : max($positiveValues); // minimalleistung = dieser Fallback $minimalleistung = $fallbackMinimum; // den höheren Wert wählen und negieren if (abs($leistung) > abs($minimalleistung)) { $schreibleistung = $leistung * -1; } else { $schreibleistung = $minimalleistung; } // Die Veteilten Leistungen auf die Verbruacher schreiben if (IPS_InstanceExists($userInstanceID)) { IPS_RequestAction( $userInstanceID, "SetAktuelle_Leistung", $schreibleistung ); } } } } } public function DistributeEnergy_Extern() { // Systemvariablen abrufen $Netzbezug = GetValue($this->ReadPropertyInteger("Netzbezug")); $Verbraucher_Liste = json_decode( $this->ReadPropertyString("Verbraucher_Liste"), true ); $filteredVerbraucher = []; // Fülle das Array mit allen entsprechenden Werten der Verbraucher ab foreach ($Verbraucher_Liste as $user) { // Werte direkt von der Verbraucher-Instanz abrufen $Aktuelle_Leistung = GetValue( IPS_GetObjectIDByIdent( "Aktuelle_Leistung", $user["Verbraucher"] ) ); $Bezogene_Energie = GetValue( IPS_GetObjectIDByIdent("Bezogene_Energie", $user["Verbraucher"]) ); $PV_Prio = GetValue( IPS_GetObjectIDByIdent("PV_Prio", $user["Verbraucher"]) ); $Sperre_Prio = GetValue( IPS_GetObjectIDByIdent("Sperre_Prio", $user["Verbraucher"]) ); $idle = GetValue( IPS_GetObjectIDByIdent("Idle", $user["Verbraucher"]) ); $powerStepsJson = GetValue( IPS_GetObjectIDByIdent("PowerSteps", $user["Verbraucher"]) ); $delta = GetValue( IPS_GetObjectIDByIdent("Leistung_Delta", $user["Verbraucher"]) ); $powerSteps = json_decode($powerStepsJson, true); // Verbraucher-Daten zum gefilterten Array hinzufügen $filteredVerbraucher[] = [ "Verbraucher" => $user["Verbraucher"], "InstanceID" => $user["Verbraucher"], "Aktuelle_Leistung" => $Aktuelle_Leistung, "Bezogene_Energie" => $Bezogene_Energie, "PV_Prio" => $PV_Prio, "Sperre_Prio" => $Sperre_Prio, "Idle" => $idle, "PowerSteps" => $powerSteps, "Leistung_Delta" => $delta, "ParentManager" => $this->ReadPropertyInteger("ManagerID"), ]; } $sendarray = []; $sendarray = [ "User" => $filteredVerbraucher, "Netzbezug" => $Netzbezug, "Timestamp" => time(), ]; SetValue( $this->ReadPropertyInteger("DatenHoch"), json_encode($sendarray) ); $answerArray = json_decode( GetValue($this->ReadPropertyInteger("DatenZuruck")), true ); foreach ($answerArray["User"] as $user) { IPS_RequestAction( $user["InstanceID"], "GetCurrentData", $answerArray["Is_Peak_Shaving"] ); IPS_RequestAction( $user["InstanceID"], "SetAktuelle_Leistung", $user["Set_Leistung"] ); } } } ?>