diff --git a/Manager_v2/README.md b/Ansteuerung_Askoheat/README.md similarity index 100% rename from Manager_v2/README.md rename to Ansteuerung_Askoheat/README.md diff --git a/Ansteuerung_Askoheat/form.json b/Ansteuerung_Askoheat/form.json new file mode 100644 index 0000000..6069099 --- /dev/null +++ b/Ansteuerung_Askoheat/form.json @@ -0,0 +1,33 @@ +{ + "elements": [ + { + "type": "Label", + "caption": "Einstellungen für einstufigen Verbruacher Ein-Aus" + }, + + { + "type": "NumberSpinner", + "name": "IdleCounterMax", + "caption": "Zyklen zwischen zwei Leistungsänderungen (Multipliziert sich mit Interval)", + "suffix": "" + }, + { + "type": "NumberSpinner", + "name": "Interval", + "caption": "Intervall Neuberechnung der Werte Erst für spätere Versionen, aktuell auf 5 lassen!", + "suffix": "Sekunden" + }, + { + "type": "NumberSpinner", + "name": "BoilerLeistung", + "caption": "Maximalleitsung Askoheat", + "suffix": "" + }, + { + "type": "ValidationTextBox", + "name": "IP_Adresse", + "caption": "IP-Adresse Askoheat" + } + + ] +} diff --git a/Manager_v2/module.json b/Ansteuerung_Askoheat/module.json similarity index 66% rename from Manager_v2/module.json rename to Ansteuerung_Askoheat/module.json index 5d10fe9..225b24c 100644 --- a/Manager_v2/module.json +++ b/Ansteuerung_Askoheat/module.json @@ -1,6 +1,6 @@ { - "id": "{0998B49F-D79A-E588-94F2-C3EFC9652BB1}", - "name": "Manager_v2", + "id": "{C042B0F6-CD02-9843-8D21-31BC3049BC14}", + "name": "Ansteuerung_Askoheat", "type": 3, "vendor": "Belevo AG", "aliases": [], diff --git a/Ansteuerung_Askoheat/module.php b/Ansteuerung_Askoheat/module.php new file mode 100644 index 0000000..583296a --- /dev/null +++ b/Ansteuerung_Askoheat/module.php @@ -0,0 +1,328 @@ +RegisterPropertyInteger("BoilerLeistung", 4000); // Standardwert für Volllast + $this->RegisterPropertyString("IP_Adresse", ""); // Standardwert für Volllast + $this->RegisterPropertyInteger("Interval", 5); // Recheninterval + + // Variabeln für Kommunkation mit Manager + $this->RegisterVariableInteger("Sperre_Prio", "Sperre_Prio"); + $this->RegisterVariableInteger("PV_Prio", "PV_Prio"); + $this->RegisterVariableBoolean("Idle", "Idle", "", 0); + $this->RegisterVariableInteger("Aktuelle_Leistung", "Aktuelle_Leistung", "", 0); + $this->RegisterVariableFloat("Bezogene_Energie", "Bezogene_Energie", "", 0); + $this->RegisterVariableString("PowerSteps", "PowerSteps"); + $this->RegisterVariableInteger("Power", "Power"); + $this->RegisterVariableBoolean("Is_Peak_Shaving", "Is_Peak_Shaving"); + $this->RegisterVariableInteger("Leistung_Delta", "Leistung_Delta", "", 0); + $this->RegisterVariableFloat("Boilertemperatur", "Boilertemperatur", "", 0); + + // Hilfsvariabeln für Idle zustand + $this->RegisterPropertyInteger("IdleCounterMax", 2); + $this->RegisterVariableInteger("IdleCounter", "IdleCounter", "", 0); + $this->SetValue("IdleCounter", 0); + + // Initialisiere Idle + $this->SetValue("Idle", true); + + $this->RegisterTimer("Timer_Do_UserCalc_Verb",$this->ReadPropertyInteger("Interval")*1000,"IPS_RequestAction(" .$this->InstanceID .', "Do_UserCalc", "");'); + + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + $this->SetTimerInterval("Timer_Do_UserCalc_Verb",$this->ReadPropertyInteger("Interval")*1000); + + } + + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + + case "SetAktuelle_Leistung": + $this->SetValue("Power", (int)$Value); + break; + + case "GetCurrentData": + $this->SetValue("Is_Peak_Shaving", (bool)$Value); + break; + + case "ResetPowerSteps": + $this->ResetPowerSteps($Value); + break; + + case "Do_UserCalc": + $this->SetAktuelle_Leistung($this->GetValue("Power")); + $this->GetCurrentData($this->GetValue("Is_Peak_Shaving")); + break; + + default: + throw new Exception("Invalid Ident"); + } + } + + + + public function getNextTimeAndTemperature($zeitplan) { + $arr = json_decode($zeitplan, true); + if (empty($arr)) { + return null; + } + $currentTime = new DateTime(); + $nextEntry = null; + $minDiff = PHP_INT_MAX; + + foreach ($arr as $entry) { + $entryTime = DateTime::createFromFormat('H:i', $entry['Uhrzeit']); + if ($entryTime < $currentTime) { + $entryTime->modify('+1 day'); + } + $diff = $currentTime->diff($entryTime)->format('%r%a') * 24 * 60 + $currentTime->diff($entryTime)->format('%r%h') * 60 + $currentTime->diff($entryTime)->format('%r%i'); + if ($diff < $minDiff) { + $minDiff = $diff; + $nextEntry = $entry; + } + } + + return $nextEntry; + } + + public function calculateRemainingTime($nextTime) { + $currentTime = new DateTime(); + $nextDateTime = DateTime::createFromFormat('H:i', $nextTime); + if ($nextDateTime < $currentTime) { + $nextDateTime->modify('+1 day'); + } + $interval = $currentTime->diff($nextDateTime); + return $interval->h + ($interval->i / 60); + } + + public function calculateRequiredHeat($boilervolumen, $tempDiff) { + // Annahme: spezifische Wärmekapazität von Wasser = 4.186 J/g°C + // 1 Liter Wasser = 1000 Gramm + $specificHeatCapacity = 4.186; // J/g°C + $waterMass = $boilervolumen * 1000; // Gramm + return $specificHeatCapacity * $waterMass * $tempDiff; // Joules + } + + public function canBoilerReachTemperature($boilervolumen, $boilerTemper, $nextTemp, $remainingTime, $vollleistung) { + $tempDiff = $nextTemp - $boilerTemper; + $requiredHeat = $this->calculateRequiredHeat($boilervolumen, $tempDiff); + $availableHeat = $vollleistung * $remainingTime * 3600; // Leistung in Watt * Zeit in Sekunden + return $availableHeat >= $requiredHeat; + } + + // Methode zum Setzen des aktuellen Stromverbrauchs + public function SetAktuelle_Leistung(int $power) + { + // Schalte Kontakt Teillast und Vollast entsprechend der Power-Einstellung + if ($power == $this->ReadPropertyInteger("BoilerLeistungVolllast")) { + SetValue($this->ReadPropertyInteger("Kontakt_Volllast"), true); + SetValue($this->ReadPropertyInteger("Kontakt_Teillast"), false); + } elseif ( + $power == $this->ReadPropertyInteger("BoilerLeistungTeillast") + ) { + SetValue($this->ReadPropertyInteger("Kontakt_Volllast"), false); + SetValue($this->ReadPropertyInteger("Kontakt_Teillast"), true); + } else { + SetValue($this->ReadPropertyInteger("Kontakt_Volllast"), false); + SetValue($this->ReadPropertyInteger("Kontakt_Teillast"), false); + } + + // Prüfe auf Änderung der Power im Vergleich zur letzten Einstellung + $lastPower = GetValue($this->GetIDForIdent("Aktuelle_Leistung")); + if ($power != $lastPower) { + $this->SetValue("Idle", false); + $this->SetValue( + "IdleCounter", + $this->ReadPropertyInteger("IdleCounterMax") + ); + } + + // Setze die neue Aktuelle_Leistung + $this->SetValue("Aktuelle_Leistung", $power); + $this->SetValue("Bezogene_Energie", ($this->GetValue("Bezogene_Energie") + ($this->GetValue("Aktuelle_Leistung")*($this->ReadPropertyInteger("Interval")/3600)))); + // IdleCounter verarbeiten + $this->ProcessIdleCounter(); + } + + // Methode zum Abrufen der aktuellen Daten + public function GetCurrentData(bool $Peak) + { + $LegioCounter = $this->GetValue("LegioCounter"); + + + $boilertemperatur_glätten = $this->ReadPropertyBoolean("Boilertemperatur_glätten"); + + if ($boilertemperatur_glätten) { + // Wenn Glättung aktiviert ist, führe das Glätten durch + $boilerFuehlerPT1ID = $this->ReadPropertyInteger("Boilerfuehler_PT1"); + + if (IPS_VariableExists($boilerFuehlerPT1ID)) { + $boilerPT1 = GetValue($boilerFuehlerPT1ID); + } else { + $boilerPT1 = 0.0; // Standardwert + } + + $boilerTempID = $this->GetIDForIdent("Boilertemperatur"); + if (IPS_VariableExists($boilerTempID)) { + $boilerTemp = $this->GetValue("Boilertemperatur"); + } else { + $boilerTemp = 0.0; // Standardwert + } + + // PT + $time_constant= $this->ReadPropertyInteger("ZeitKonstante"); + $delta_t = 5; // Zeitdifferenz zwischen den Messungen (30 Sekunden) + $alpha = $delta_t / ($time_constant + $delta_t); + $newBoilerTemp = $boilerTemp + $alpha * ($boilerPT1 - $boilerTemp); + $this->SetValue("Boilertemperatur", $newBoilerTemp); + } else { + // Wenn Glättung nicht aktiviert ist, setze die Boilertemperatur direkt auf den Wert des Boilerfühlers + $boilerFuehlerPT1ID = $this->ReadPropertyInteger("Boilerfuehler_PT1"); + + if (IPS_VariableExists($boilerFuehlerPT1ID)) { + $boilerPT1 = GetValue($boilerFuehlerPT1ID); + } else { + $boilerPT1 = 0.0; // Standardwert + } + + // Setze Boilertemperatur direkt auf den Wert des Boilerfühlers + $this->SetValue("Boilertemperatur", $boilerPT1); + } + + + $boilerTemp = $this->GetValue("Boilertemperatur"); + $minTemp = $this->GetValue("Mindesttemperatur"); + $maxTemp = $this->GetValue("Maximaltemperatur"); + $LegioTemp = $this->GetValue("Legionellentemperatur"); + $teilLeistung = $this->ReadPropertyInteger("BoilerLeistungTeillast"); + $vollLeistung = $this->ReadPropertyInteger("BoilerLeistungVolllast"); + + + $nextEntry = $this->getNextTimeAndTemperature($this->ReadPropertyString("Zeitplan")); + if ($nextEntry !== null) { + $remainingTime = $this->calculateRemainingTime($nextEntry['Uhrzeit']); + $nextTemp = $nextEntry['Solltemperatur']; + + if (!$this->canBoilerReachTemperature($this->ReadPropertyInteger("Boilervolumen"), $boilerTemp, $nextTemp, $remainingTime, $vollLeistung)) { + $minTemp = $nextTemp; + } + } + + + + $AktuelleVollast = GetValue( + $this->ReadPropertyInteger("Kontakt_Volllast") + ); + $AktuelleTeillast = GetValue( + $this->ReadPropertyInteger("Kontakt_Teillast") + ); + + if ($boilerTemp > $LegioTemp) { + $LegioCounter = 0; + } else { + $LegioCounter = $LegioCounter + 1; + } + if ($LegioCounter > 69120) { + $maxTemp = $LegioTemp; + } + if ($LegioCounter > 120960 && $this->ist_nachts()) { + $minTemp = $LegioTemp; + } + if ($LegioCounter > 138240) { // Timeout für Legio wenn temperatur nicht erreicht werden kann, setze legionellenfunktion zurück + $LegioCounter = 0; + } + + $this->SetValue("LegioCounter", $LegioCounter); + + if ($Peak) { + if ($boilerTemp < $minTemp) { + $this->SetValue( "PowerSteps", json_encode([0, $teilLeistung, $vollLeistung]) ); + } elseif ( + $boilerTemp < $minTemp + 5 && + ($AktuelleVollast || $AktuelleTeillast) + ) { + $this->SetValue( + "PowerSteps", + json_encode([0, $teilLeistung, $vollLeistung]) + ); + } else { + $this->SetValue("PowerSteps", json_encode([0])); + } + } else { + if ($boilerTemp < $minTemp) { + $this->SetValue("PowerSteps", json_encode([$vollLeistung])); + } elseif ( + $boilerTemp < $minTemp + 5 && + ($AktuelleVollast || $AktuelleTeillast) + ) { + $this->SetValue("PowerSteps", json_encode([$vollLeistung])); + } elseif ($boilerTemp < $maxTemp - 5) { + $this->SetValue("PowerSteps", json_encode([0, $teilLeistung, $vollLeistung])); + } elseif ( $boilerTemp < $maxTemp && ($AktuelleVollast || $AktuelleTeillast) + ) { + $this->SetValue( "PowerSteps", json_encode([0, $teilLeistung, $vollLeistung])); + } else { + $this->SetValue("PowerSteps", json_encode([0])); + } + } + } + + private function ProcessIdleCounter() + { + // IdleCounter auslesen und verarbeiten + $idleCounter = $this->GetValue("IdleCounter"); + if ($idleCounter > 0) { + $this->SetValue("Idle", false); + $this->SetValue("IdleCounter", $idleCounter - 1); + } else { + $this->SetValue("Idle", true); + } + } + + private function CheckIdle($power) + { + $lastpower = GetValue("Aktuelle_Leistung"); + if ($lastpower != GetValue("Aktuelle_Leistung")) { + $this->SetValue("Idle", false); + $this->SetValue( + "IdleCounter", + $this->ReadPropertyInteger("IdleCounterMax") + ); + } + // IdleCounter auslesen und verarbeiten + $idleCounter = $this->GetValue("IdleCounter"); + if ($idleCounter > 0) { + $this->SetValue("Idle", false); + $this->SetValue("IdleCounter", $idleCounter - 1); + } else { + $this->SetValue("Idle", true); + } + } + + private function ist_nachts() + { + date_default_timezone_set("Europe/Berlin"); // Setze hier deine Zeitzone + + $aktuelle_zeit = strtotime(date("H:i")); // Aktuelle Zeit in Stunden und Minuten umwandeln + $start_nacht = strtotime("22:00"); // Startzeit der Nacht (22 Uhr) + $ende_nacht = strtotime("07:00"); // Endzeit der Nacht (7 Uhr) + + if ($aktuelle_zeit >= $start_nacht || $aktuelle_zeit < $ende_nacht) { + return true; + } else { + return false; + } + } +} + +?> diff --git a/HauptManager/module.php b/HauptManager/module.php index 2b06ffe..9f25774 100644 --- a/HauptManager/module.php +++ b/HauptManager/module.php @@ -111,7 +111,7 @@ class HauptManager extends IPSModule if(in_array(0, $user["PowerSteps"], true)){ // Addiere die aktuell bereits verwendete Leistung auf, um sie bei der verteilung zu berücksichtigen - $totalAktuelle_Leistung += ($user["Aktuelle_Leistung"]); + $totalAktuelle_Leistung += ($user["Aktuelle_Leistung"]- $user["Leistung_Delta"]); } } @@ -332,8 +332,8 @@ class HauptManager extends IPSModule $allSteps[] = [ "user" => $user["InstanceID"], "Writeback" => $user["Writeback"], - "step" => $step*-1, - "Leistung_Delta" => $user["Leistung_Delta"] + "step" => $step*-1 + //"Leistung_Delta" => $user["Leistung_Delta"] ]; } } diff --git a/Ladestation_v2/module.php b/Ladestation_v2/module.php index c036b08..ea97532 100644 --- a/Ladestation_v2/module.php +++ b/Ladestation_v2/module.php @@ -475,7 +475,9 @@ class Ladestation_v2 extends IPSModule $url = $baseUrl . "alw=0"; break; case 2: - $url = $baseUrl . "frc=1"; + //$url = $baseUrl . "frc=1"; + $url = $baseUrl . "ama=0"; + break; case 3: $url = $baseUrl . "0"; @@ -501,8 +503,8 @@ class Ladestation_v2 extends IPSModule $url2 = $baseUrl . "amp=$value"; break; case 2: - $url1 = $baseUrl . "frc=2"; - $url2 = $baseUrl . "amp=$value"; + //$url1 = $baseUrl . "frc=2"; + $url2 = $baseUrl . "ama=$value"; break; case 3: $url2 = $baseUrl . ($value * 1000); diff --git a/Manager/module.php b/Manager/module.php index 310a413..5b79bb3 100644 --- a/Manager/module.php +++ b/Manager/module.php @@ -144,7 +144,7 @@ class Manager extends IPSModule if(in_array(0, $powerSteps, true)){ // Addiere die aktuell bereits verwendete Leistung auf, um sie bei der verteilung zu berücksichtigen - $totalAktuelle_Leistung += ($Aktuelle_Leistung); + $totalAktuelle_Leistung += ($Aktuelle_Leistung-$delta); } diff --git a/Manager_v2/form.json b/Manager_v2/form.json deleted file mode 100644 index 84dadcb..0000000 --- a/Manager_v2/form.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "elements": [ - { - "type": "Label", - "caption": "Einstellungen Energiemanager" - }, - - { - "type": "NumberSpinner", - "name": "Peakleistung", - "caption": "Sollwertvorgabe für Peakshaving", - "suffix": "Watt" - }, - { - "type": "NumberSpinner", - "name": "Ueberschussleistung", - "caption": "Sollwertvorgabe für Solarladen", - "suffix": "Watt" - }, - { - "type": "SelectVariable", - "name": "Netzbezug", - "caption": "Variable mit dem zu regelnden Netzbezug" - }, - { - "type": "CheckBox", - "name": "HauptmanagerAktiv", - "caption": "Hauptmanager Aktiv" - }, - { - "type": "NumberSpinner", - "name": "ManagerID", - "caption": "Manager ID" - }, - { - "type": "SelectVariable", - "name": "DatenHoch", - "caption": "Daten Hoch" - }, - { - "type": "SelectVariable", - "name": "DatenZuruck", - "caption": "Daten Zurück" - }, - { - "type": "NumberSpinner", - "name": "Interval", - "caption": "Intervall Neuberechnung der Werte", - "suffix": "Sekunden" - }, - { - "type": "List", - "name": "Verbraucher_Liste", - "caption": "Verbraucher, die gemanagt werden sollen.", - "add": true, - "delete": true, - "sortable": true, - "columns": [ - { - "caption": "Energieverbraucher", - "name": "Verbraucher", - "width": "auto", - "add": 0, - "edit": { - "type": "SelectInstance", - "filter": "Verbraucher" - } - } - ] - } - ] -} diff --git a/Manager_v2/module.php b/Manager_v2/module.php deleted file mode 100644 index 7205c12..0000000 --- a/Manager_v2/module.php +++ /dev/null @@ -1,352 +0,0 @@ -RegisterPropertyInteger("Peakleistung", 0); - $this->RegisterPropertyInteger("Ueberschussleistung", 0); - $this->RegisterPropertyInteger("Netzbezug", 0); // Initialisierung mit 0 - $this->RegisterPropertyString("Verbraucher_Liste", "[]"); - $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 - - // 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": - if($this->ReadPropertyBoolean("HauptmanagerAktiv")==true ){ - - $data = json_decode(GetValue($this->ReadPropertyInteger("DatenZuruck")), true); - IPS_LogMessage("Manager", print_r($data)); - IPS_LogMessage("Manager", $data["timestamp"]); - - if (isset($data["timestamp"])) { - $timestamp = $data["timestamp"]; - $currentTime = time(); - IPS_LogMessage("Manager", ($currentTime - $timestamp)); - - if (($currentTime - $timestamp) < 3600) { - $this->DistributeEnergy_Extern(); - } - } else { - $this->DistributeEnergy(); - } - - }else{ - $this->DistributeEnergy(); - } - - break; - case "ApplyChanges": - $this->ApplyChanges(); - break; - default: - throw new Exception("Invalid Ident"); - } - } - - public function DistributeEnergy() - { - // Systemvariablen abrufen - $Netzbezug = GetValue($this->ReadPropertyInteger("Netzbezug")); - $Peakleistung = $this->ReadPropertyInteger("Peakleistung"); - $Ueberschussleistung = $this->ReadPropertyInteger("Ueberschussleistung"); - - // 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; - } - IPS_LogMessage("Manag anfang", $remainingPower); - - // Alle Energieverbraucher auslesen und dekodieren - $Verbraucher_Liste = json_decode($this->ReadPropertyString("Verbraucher_Liste"), true); - - if (empty($Verbraucher_Liste)) { - // Liste ist leer, daher nichts zu tun - IPS_LogMessage("Manager", "aufgerufen leere liste"); - - return; - } - - // 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); - IPS_LogMessage("Manager", "aufgerufen getcurrentdata"); - - } - } - - $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 - - // Fülle das Array mit allen entsprechenden Werten der Verbraucher ab - foreach ($Verbraucher_Liste as $user) { - if (!IPS_InstanceExists($user["Verbraucher"])) { - IPS_LogMessage("Manager", "aufgerufen komisch"); - continue; - } - $idle = GetValue(IPS_GetObjectIDByIdent("Idle", $user["Verbraucher"])); - $powerStepsJson = GetValue(IPS_GetObjectIDByIdent("PowerSteps", $user["Verbraucher"])); - $powerSteps = json_decode($powerStepsJson, true); - // Verbraucher-Daten zum gefilterten Array hinzufügen - $filteredVerbraucher[] = [ - "InstanceID" => $user["Verbraucher"], - "Aktuelle_Leistung" => GetValue(IPS_GetObjectIDByIdent("Power", $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" => $idle, - "PowerSteps" => $powerSteps, - "Leistung_Delta" => GetValue(IPS_GetObjectIDByIdent("Leistung_Delta", $user["Verbraucher"])) - ]; - - // Überprüfen, ob alle Benutzer Idle = true sind, wenn einer nicht ist, wird später verworfen... - if (!$idle) { - $allIdle = false; - IPS_LogMessage("Manager", "nciht idle"); - - } - if(in_array(0, $powerSteps, true)){ - - // Addiere die aktuell bereits verwendete Leistung auf, um sie bei der verteilung zu berücksichtigen - $totalAktuelle_Leistung += (GetValue(IPS_GetObjectIDByIdent("Power", $user["Verbraucher"])) + GetValue(IPS_GetObjectIDByIdent("Leistung_Delta", $user["Verbraucher"]))); - - } - - } - // Berücksichtigung der bereits verteilten Leistungen (nachher kann dafür wieder bei 0 begonnen werden zu verteilen) - $remainingPower += $totalAktuelle_Leistung; - - - // Abbrechen wenn es keine gefilterten User gibt - if (empty($filteredVerbraucher)) { - return; - } - - // Wenn nicht alle Benutzer Idle = true sind, rufe SetAktuelle_Leistung mit Aktuelle_Leistung Werten auf, (alle Verbraucher behalten die aktuelle Leistung) - if (!$allIdle) { - foreach ($filteredVerbraucher as $user) { - IPS_RequestAction($user["InstanceID"],"SetAktuelle_Leistung",$user["Aktuelle_Leistung"]); - IPS_LogMessage("Manager", "aufgerufen nicht alle idle"); - - } - return; - } - - // 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 = []; - $withoutZero = []; - // Verbraucher die nicht 0 Annhemen können, bekommen einfach den tiefsten wert - foreach ($samePriorityUsers as $entry) { - if (min($entry["PowerSteps"]) <= 0) { - $withZero[] = $entry; - } else { - $withoutZero[] = $entry; - } - } - // Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert - if (!empty($withoutZero)) { - foreach ($withoutZero as $entry) { - $instanceID = $entry["InstanceID"]; - $minPowerStep = min($entry["PowerSteps"]); - - IPS_RequestAction($instanceID,"SetAktuelle_Leistung",$minPowerStep); - //$remainingPower -= $entry["Aktuelle_Leistung"]; - } - } - - - // Nun die verteilen, die 0 erhalten können. - $samePriorityUsers = $withZero; - - $userEnergyProv = []; - $userEnergyProv = array_fill_keys(array_column($samePriorityUsers, "InstanceID"), 0); // Initialisierung für jeden Benutzer auf 0 setzen - - IPS_LogMessage("Manag", $remainingPower); - - $help_state = false; - if($remainingPower>=0){ - $help_state = true; - } - - // Alle Schritte der Benutzer in einem Array sammeln - $allSteps = []; - foreach ($samePriorityUsers as $user) { - foreach ($user["PowerSteps"] as $step) { - if($help_state==true){ - if($step>=0){ - $allSteps[] = [ - "user" => $user["InstanceID"], - "step" => $step - ]; - } - } - else{ - 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"]; - }); - - if($help_state==false){ - $remainingPower = $remainingPower *-1; - } - 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; - } - - } - if($help_state==false){ - $remainingPower = $remainingPower *-1; - } - // Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt - foreach ($userEnergyProv as $userInstanceID => $leistung) { - $minimalleitsung = min( - array_column( - array_filter($allSteps, function ($entry) use ( - $userInstanceID - ) { - return $entry["user"] == $userInstanceID; - }), - "step" - ) - ); - - // Jedem user den höheren der beiden werte aus minimalwert oder vergebenem zuteilen - $schreibleistung = max($leistung, $minimalleitsung) * ($help_state ? 1 : -1); - - // Methode SetAktuelle_Leistung für jeden Verbraucher mit der entsprechenden Energie aufrufen - if (IPS_InstanceExists($userInstanceID)) { - IPS_RequestAction($userInstanceID,"SetAktuelle_Leistung",$schreibleistung); - IPS_LogMessage("Manager", "aufgerufen setleistung else ".$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) { - // Verbraucher-Daten zum gefilterten Array hinzufügen - - - - - $filteredVerbraucher[] = [ - "InstanceID" => $user["Verbraucher"], - "Aktuelle_Leistung" => GetValue(IPS_GetObjectIDByIdent("Power", $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"])), - "PowerSteps" => json_decode(GetValue(IPS_GetObjectIDByIdent("PowerSteps", $user["Verbraucher"])), true), - "Leistung_Delta" => GetValue(IPS_GetObjectIDByIdent("Leistung_Delta", $user["Verbraucher"])), - "ParentManager" => $this->ReadPropertyInteger("ManagerID") - - ]; - } - $sendarray = []; - $sendarray = [ - "Users" => $filteredVerbraucher, - "Netzbezug" => $Netzbezug, - "Timestamp" => time() - ]; - - RequestAction($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"]); - } - - } - -} -?> diff --git a/Verbraucher_Sperrbar/README.md b/Verbraucher_Sperrbar/README.md new file mode 100644 index 0000000..b059e3a --- /dev/null +++ b/Verbraucher_Sperrbar/README.md @@ -0,0 +1,67 @@ +# Manager_1 +Beschreibung des Moduls. + +### Inhaltsverzeichnis + +1. [Funktionsumfang](#1-funktionsumfang) +2. [Voraussetzungen](#2-voraussetzungen) +3. [Software-Installation](#3-software-installation) +4. [Einrichten der Instanzen in IP-Symcon](#4-einrichten-der-instanzen-in-ip-symcon) +5. [Statusvariablen und Profile](#5-statusvariablen-und-profile) +6. [WebFront](#6-webfront) +7. [PHP-Befehlsreferenz](#7-php-befehlsreferenz) + +### 1. Funktionsumfang + +* + +### 2. Voraussetzungen + +- IP-Symcon ab Version 7.1 + +### 3. Software-Installation + +* Über den Module Store das 'Manager_1'-Modul installieren. +* Alternativ über das Module Control folgende URL hinzufügen + +### 4. Einrichten der Instanzen in IP-Symcon + + Unter 'Instanz hinzufügen' kann das 'Manager_1'-Modul mithilfe des Schnellfilters gefunden werden. + - Weitere Informationen zum Hinzufügen von Instanzen in der [Dokumentation der Instanzen](https://www.symcon.de/service/dokumentation/konzepte/instanzen/#Instanz_hinzufügen) + +__Konfigurationsseite__: + +Name | Beschreibung +-------- | ------------------ + | + | + +### 5. Statusvariablen und Profile + +Die Statusvariablen/Kategorien werden automatisch angelegt. Das Löschen einzelner kann zu Fehlfunktionen führen. + +#### Statusvariablen + +Name | Typ | Beschreibung +------ | ------- | ------------ + | | + | | + +#### Profile + +Name | Typ +------ | ------- + | + | + +### 6. WebFront + +Die Funktionalität, die das Modul im WebFront bietet. + +### 7. PHP-Befehlsreferenz + +`boolean GEF_BeispielFunktion(integer $InstanzID);` +Erklärung der Funktion. + +Beispiel: +`GEF_BeispielFunktion(12345);` \ No newline at end of file diff --git a/Verbraucher_Sperrbar/form.json b/Verbraucher_Sperrbar/form.json new file mode 100644 index 0000000..fc33c83 --- /dev/null +++ b/Verbraucher_Sperrbar/form.json @@ -0,0 +1,46 @@ +{ + "elements": [ + { + "type": "Label", + "caption": "Einstellungen für einstufigen Verbruacher Ein-Aus" + }, + + { + "type": "NumberSpinner", + "name": "IdleCounterMax", + "caption": "Zyklen zwischen zwei Leistungsänderungen (Multipliziert sich mit Interval)", + "suffix": " Zyklen" + }, + { + "type": "NumberSpinner", + "name": "Interval", + "caption": "Intervall Neuberechnung der Werte ", + "suffix": "Sekunden" + }, + { + "type": "NumberSpinner", + "name": "MaxSperrZeit", + "caption": "Maximale Sperrzeit Verbraucher pro Tag", + "suffix": "" + }, + { + "type": "NumberSpinner", + "name": "Zeit_Zwischen_Zustandswechseln", + "caption": "Mindestlaufzeit des Verbrauchers bei Lastschaltung", + "suffix": "" + }, + { + "type": "SelectVariable", + "name": "Leistung", + "caption": "Variable mit aktueller Leistung des Verbrauchers", + "suffix": "" + }, + { + "type": "SelectVariable", + "name": "Schaltkontakt1", + "caption": "Zu schaltenden Kontakt für Sperre", + "test": true + } + + ] +} diff --git a/Verbraucher_Sperrbar/module.json b/Verbraucher_Sperrbar/module.json new file mode 100644 index 0000000..12e41fa --- /dev/null +++ b/Verbraucher_Sperrbar/module.json @@ -0,0 +1,12 @@ +{ + "id": "{0A15319E-AC03-0E5A-33B4-A46BB9330B17}", + "name": "Verbraucher_Sperrbar", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/Verbraucher_Sperrbar/module.php b/Verbraucher_Sperrbar/module.php new file mode 100644 index 0000000..650243d --- /dev/null +++ b/Verbraucher_Sperrbar/module.php @@ -0,0 +1,229 @@ +RegisterPropertyInteger("Leistung", 0); // Standardwert für Volllast + $this->RegisterPropertyInteger("MaxSperrZeit", 4); + $this->RegisterPropertyInteger("Zeit_Zwischen_Zustandswechseln", 1); + $this->RegisterPropertyInteger("Schaltkontakt1", 0); + $this->RegisterPropertyInteger("Interval", 5); // Recheninterval + + // Verbraucherspezifische Variabeln + $this->RegisterVariableInteger("DailyOnTime", "DailyOnTime", "", 0); + $this->RegisterVariableBoolean("IsTimerActive", "IsTimerActive", "", 0); + + // Verbraucherspezifischer Timer + $this->SetValue("IsTimerActive", false); + $this->RegisterTimer("ZustandswechselTimer",0,"IPS_RequestAction(" .$this->InstanceID .', "ResetPowerSteps", "");'); + + // Variabeln für Kommunkation mit Manager + $this->RegisterVariableInteger("Sperre_Prio", "Sperre_Prio"); + $this->RegisterVariableInteger("PV_Prio", "PV_Prio"); + $this->RegisterVariableBoolean("Idle", "Idle", "", 0); + $this->RegisterVariableInteger("Aktuelle_Leistung", "Aktuelle_Leistung", "", 0); + $this->RegisterVariableFloat("Bezogene_Energie", "Bezogene_Energie", "", 0); + $this->RegisterVariableString("PowerSteps", "PowerSteps"); + $this->RegisterVariableInteger("Power", "Power"); + $this->RegisterVariableBoolean("Is_Peak_Shaving", "Is_Peak_Shaving"); + $this->RegisterVariableInteger("Leistung_Delta", "Leistung_Delta", "", 0); + + // Hilfsvariabeln für Idle zustand + $this->RegisterPropertyInteger("IdleCounterMax", 2); + $this->RegisterVariableInteger("IdleCounter", "IdleCounter", "", 0); + $this->SetValue("IdleCounter", 0); + + // Initialisiere Idle + $this->SetValue("Idle", true); + + $this->RegisterTimer("Timer_Do_UserCalc_Verb",$this->ReadPropertyInteger("Interval")*1000,"IPS_RequestAction(" .$this->InstanceID .', "Do_UserCalc", "");'); + + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + $this->SetTimerInterval("Timer_Do_UserCalc_Verb",$this->ReadPropertyInteger("Interval")*1000); + + } + + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + + case "SetAktuelle_Leistung": + $this->SetValue("Power", (int)$Value); + break; + + case "GetCurrentData": + $this->SetValue("Is_Peak_Shaving", (bool)$Value); + break; + + case "ResetPowerSteps": + $this->ResetPowerSteps($Value); + break; + + case "Do_UserCalc": + $this->SetAktuelle_Leistung($this->GetValue("Power")); + $this->GetCurrentData($this->GetValue("Is_Peak_Shaving")); + break; + + default: + throw new Exception("Invalid Ident"); + } + } + + + + + // Methode zum Setzen der PowerSteps und Timer starten + public function SetTimerOn() + { + // Timer setzen, der nach "Zeit_Zwischen_Zustandswechseln" abläuft + $zeitZwischenZustandswechseln = $this->ReadPropertyInteger( + "Zeit_Zwischen_Zustandswechseln" + ); + $this->SetTimerInterval( + "ZustandswechselTimer", + $zeitZwischenZustandswechseln * 60000 + ); // Timer in Millisekunden + IPS_LogMessage("Verbraucher", "In Set Methode"); + + // Timer-Status auf true setzen + $this->SetValue("IsTimerActive", true); + } + + // Methode zum Zurücksetzen von PowerSteps nach Ablauf des Timers + public function ResetPowerSteps() + { + // PowerSteps wieder auf den ursprünglichen Zustand setzen (wie vorherige Funktionalität) + $this->SetValue( + "PowerSteps", + json_encode([$this->GetValue("Aktuelle_Leistung")]) + ); + + // Timer stoppen + $this->SetTimerInterval("ZustandswechselTimer", 0); + + // Timer-Status auf false setzen + $this->SetValue("IsTimerActive", false); + } + // Methode zum Setzen des aktuellen Stromverbrauchs + public function SetAktuelle_Leistung(float $power) + { + $this->CheckIdle($power); + if ($this->GetValue("Aktuelle_Leistung") != $power) { + $this->SetTimerOn(); + } + $this->SetValue("Aktuelle_Leistung", $power); + $this->SetValue("Bezogene_Energie", ($this->GetValue("Bezogene_Energie") + ($this->GetValue("Aktuelle_Leistung")*($this->ReadPropertyInteger("Interval")/3600)))); + + $Leistung = $this->ReadPropertyInteger("Leistung"); + $schaltkontaktID = $this->ReadPropertyInteger("Schaltkontakt1"); + + if ($power == $Leistung) { + $schaltkontaktStatus = true; + } elseif ($power == 0) { + $schaltkontaktStatus = false; + } else { + // Keine Änderung, wenn power nicht 0 oder boilerLeistung entspricht + return; + } + + $currentStatus = GetValue($this->ReadPropertyInteger("Schaltkontakt1")); + + // Schaltkontaktstatus ändern + SetValue( + $this->ReadPropertyInteger("Schaltkontakt1"), + $schaltkontaktStatus + ); + + if ($schaltkontaktStatus) { + $this->SetValue("DailyOnTime", $this->GetValue("DailyOnTime") + 1); + } + } + + // Methode zum Abrufen der aktuellen Daten + public function GetCurrentData(bool $Peak) + { + $IstNacht = $this->GetValue("IstNacht"); + $NeuesIstNacht = $this->ist_nachts(); + + if ($IstNacht == true && $NeuesIstNacht == false) { + $this->SetValue("DailyOnTime", 0); + } + + $this->SetValue("IstNacht", $NeuesIstNacht); + + + + $DailyOnTime = $this->GetValue("DailyOnTime"); + $maxlaufzeit = $this->ReadPropertyInteger("Mindesttlaufzeit") * 60 / $this->ReadPropertyInteger("Interval"); + + // Überprüfen, ob der Timer aktiv ist + if ($this->GetValue("IsTimerActive")) { + // Timer ist aktiv, PowerSteps setzen + $this->SetValue( + "PowerSteps", + json_encode([$this->GetValue("Aktuelle_Leistung")]) + ); + } + // Wenn Maxlaufzeit nicht erreicht ist + elseif ($DailyOnTime < $maxlaufzeit) { + if ($Peak) { + $this->SetValue( + "PowerSteps", + json_encode([ + 0, + (int)GetValue($this->ReadPropertyInteger("Leistung")) + ]) + ); + } else { + $this->SetValue("PowerSteps",json_encode([(int)GetValue($this->ReadPropertyInteger("Leistung"))])); + } + } + // Andernfalls + else { + $this->SetValue("PowerSteps",json_encode([(int)GetValue($this->ReadPropertyInteger("Leistung"))])); + } + } + + public function CheckIdle($power) + { + $lastpower = GetValue($this->GetIDForIdent("Aktuelle_Leistung")); + if ($lastpower != $power) { + $this->SetValue("Idle", false); + $this->SetValue("IdleCounter",$this->ReadPropertyInteger("IdleCounterMax")); + } + // IdleCounter auslesen und verarbeiten + $idleCounter = $this->GetValue("IdleCounter"); + if ($idleCounter > 0) { + $this->SetValue("Idle", false); + $this->SetValue("IdleCounter", $idleCounter - 1); + } else { + $this->SetValue("Idle", true); + } + } + + private function ist_nachts() + { + date_default_timezone_set("Europe/Berlin"); // Setze hier deine Zeitzone + + $aktuelle_zeit = strtotime(date("H:i")); // Aktuelle Zeit in Stunden und Minuten umwandeln + $start_nacht = strtotime("24:00"); // Startzeit der Nacht (22 Uhr) + $ende_nacht = strtotime("07:00"); // Endzeit der Nacht (7 Uhr) + + if ($aktuelle_zeit >= $start_nacht || $aktuelle_zeit < $ende_nacht) { + return true; + } else { + return false; + } + } +} + +?>