commit 1a43a5d5a0442ed9c3f806ed0cf533c72df80393 Author: Daniel Häfliger Date: Thu Nov 28 13:42:11 2024 +0100 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6079a58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# ---> CakePHP +# CakePHP 3 + +/vendor/* +/config/app.php + +/tmp/cache/models/* +!/tmp/cache/models/empty +/tmp/cache/persistent/* +!/tmp/cache/persistent/empty +/tmp/cache/views/* +!/tmp/cache/views/empty +/tmp/sessions/* +!/tmp/sessions/empty +/tmp/tests/* +!/tmp/tests/empty + +/logs/* +!/logs/empty + +# CakePHP 2 + +/app/tmp/* +/app/Config/core.php +/app/Config/database.php +/vendors/* + +# Module für vorlagen, Testzwecke... + +/User_Template +/Wechselrichter \ No newline at end of file diff --git a/Belevo_Server_Kommunikation/README.md b/Belevo_Server_Kommunikation/README.md new file mode 100644 index 0000000..b059e3a --- /dev/null +++ b/Belevo_Server_Kommunikation/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/Belevo_Server_Kommunikation/form.json b/Belevo_Server_Kommunikation/form.json new file mode 100644 index 0000000..1f2b53f --- /dev/null +++ b/Belevo_Server_Kommunikation/form.json @@ -0,0 +1,67 @@ +{ + "elements":[ + { + "type":"Label", + "caption":"Influx Punkte" + }, + { + "type":"Select", + "name":"InfluxJaNein", + "caption":"Influx Aufzeichnen Ja oder Nein", + "options":[ + { + "caption":"Ja", + "value":true + }, + { + "caption":"Nein", + "value":false + } + ] + }, + { + "type":"ValidationTextBox", + "name":"Anlagenummer", + "caption":"Influxadresse", + "suffix":"", + "validate":"^An_\\d{4}$" + }, + { + "type":"ValidationTextBox", + "name":"Gerätenummer", + "caption":"Gerätenummer" + }, + { + "type":"ValidationTextBox", + "name":"Ortschaft", + "caption":"Ortschaft" + }, + { + "type":"List", + "name":"ZusatzVariablen", + "caption":"Zusätzliche Datenpunkte für Influxaufzeichnung", + "add":true, + "delete":true, + "columns":[ + { + "caption":"Influx Name", + "name":"Variablenname", + "width":"200px", + "add":"", + "edit":{ + "type":"ValidationTextBox" + } + }, + { + "caption":"Datenpunkt", + "name":"Variable", + "width":"300px", + "add":0, + "edit":{ + "type":"SelectVariable" + } + } + ] + } + ] + } \ No newline at end of file diff --git a/Belevo_Server_Kommunikation/module.json b/Belevo_Server_Kommunikation/module.json new file mode 100644 index 0000000..3c37d4c --- /dev/null +++ b/Belevo_Server_Kommunikation/module.json @@ -0,0 +1,12 @@ +{ + "id": "{9449616A-D571-8D4A-E917-A3DD338F00A6}", + "name": "Belevo_Server_Kommunikation", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/Belevo_Server_Kommunikation/module.php b/Belevo_Server_Kommunikation/module.php new file mode 100644 index 0000000..2ece353 --- /dev/null +++ b/Belevo_Server_Kommunikation/module.php @@ -0,0 +1,271 @@ +RegisterPropertyString( + "BaseURL", + "https://brain.belevo.ch/storedata" + ); + $this->RegisterPropertyString("Anlagenummer", "0"); + $this->RegisterPropertyBoolean("InfluxJaNein", false); + + // JSON-String für Zusatzvariablen + $this->RegisterPropertyString("ZusatzVariablen", json_encode([])); // Bezeichnung der Liste + $this->RegisterPropertyString("Variable", "0"); // Datenpunkt kann im Syncom ausgewählt werden + $this->RegisterPropertyString("Variablenname", "0"); // Name für Influxaufzeichnung + $this->RegisterPropertyString("Gerätenummer", "0"); + $this->RegisterPropertyString("Ortschaft", "0"); + + $this->RegisterVariableInteger( + "Wolkenwarscheinlichkeit", + "Wolkenwarscheinlichkeit" + ); + $this->RegisterVariableInteger("Temperatur", "Temperatur"); + + // Timer registrieren + $this->RegisterTimer( + "Timer_Influx", + 0, + "IPS_RequestAction(" . $this->InstanceID . ', "GetAction", "");' + ); + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + + // Holen Sie sich die Einstellung, ob Influx aktiviert ist + $InfluxJaNein = $this->ReadPropertyBoolean("InfluxJaNein"); + + if ($InfluxJaNein) { + // Timer auf 5 Minuten setzen + $this->SetTimerInterval("Timer_Influx", 300000); // Alle 5 Minuten -> 5*60*1000=300000 + IPS_LogMessage("Belevo_Server_Kommunikation", "Influx Ja"); + } else { + // Timer stoppen + $this->SetTimerInterval("Timer_Influx", 0); + IPS_LogMessage("Belevo_Server_Kommunikation", "Influx Nein"); + } + } + + public function getWetter($bn, $pw, $loc) + { + // URL mit Parametern zusammenstellen + $url = + "https://brain.belevo.ch/v2wetter?loc=" . + urlencode($loc) . + "&nr=" . + urlencode($bn); + + // HTTP-Anfrage-Header einstellen + $headers = ["id: $pw"]; + + // cURL-Initialisierung + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + // HTTP-Anfrage ausführen und Antwort erhalten + $response = curl_exec($ch); + curl_close($ch); + + // Antwort in ein Array dekodieren + $data = json_decode($response, true); + + // Überprüfen, ob die benötigten Felder vorhanden sind und zurückgeben + if ( + isset($data["forecast"]["forecastday"][0]["hour"][5]["temp_c"]) && + isset($data["forecast"]["forecastday"][0]["hour"][8]["cloud"]) + ) { + return [ + "temp" => + $data["forecast"]["forecastday"][0]["hour"][5]["temp_c"], + "cloud" => + $data["forecast"]["forecastday"][0]["hour"][8]["cloud"], + ]; + } + + // Fehlerbehandlung + return null; + } + + private function ProcessZusatzVariablen() + { + // Abrufen der ZusatzVariablen-Liste + $zusatzVariablen = json_decode( + $this->ReadPropertyString("ZusatzVariablen"), + true + ); // JSON decodieren + + // Array für die Ausgabe erstellen + $output = []; + + // Verarbeitung der Variablen + if (!empty($zusatzVariablen)) { + foreach ($zusatzVariablen as $variable) { + // Überprüfen, ob der Variablenname gesetzt ist + if (isset($variable["Variablenname"])) { + $variablenname = $variable["Variablenname"]; + } else { + IPS_LogMessage( + "Belevo_Server_Kommunikation", + "Variablenname nicht gesetzt für die Variable: " . + json_encode($variable) + ); + continue; // Mit der nächsten Variable fortfahren + } + + $variableID = $variable["Variable"]; + + // Überprüfen, ob die Variable existiert + if (IPS_VariableExists($variableID)) { + $wert = GetValue($variableID); // Den aktuellen Wert der Zusatzvariable abrufen + + // Wert dem Variablenname zuweisen + $output[$variablenname] = $wert; + } else { + IPS_LogMessage( + "Belevo_Server_Kommunikation", + "Variable mit ID $variableID existiert nicht." + ); + } + } + } + + // Wenn gewünscht, kannst du das JSON zurückgeben oder speichern + return json_encode($output); + } + + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + case "GetAction": + $this->GetAction(); + break; + default: + throw new Exception("Invalid action"); + } + } + + public function GetAction() + { + $output = $this->ProcessZusatzVariablen(); + $json = $this->MakeJson($output); + // Verarbeite die Zusatzvariablen + + $baseURL = $this->ReadPropertyString("BaseURL"); + + $anlagenummer = $this->ReadPropertyString("Anlagenummer"); + + $answer = $this->getWetter( + $anlagenummer, + $this->ReadPropertyString("Gerätenummer"), + $this->ReadPropertyString("Ortschaft") + ); + + //$this->SetValue("Temperatur", $answer['temp']); + //$this->SetValue("Wolkenwarscheinlichkeit", $answer['cloud']); + if (isset($answer["temp"])) { + $this->SetValue("Temperatur", $answer["temp"]); + } else { + IPS_LogMessage( + "Belevo_Server_Kommunikation", + "Temperatur-Wert ist nicht vorhanden." + ); + } + + if (isset($answer["cloud"])) { + $this->SetValue("Wolkenwarscheinlichkeit", $answer["cloud"]); + } else { + IPS_LogMessage( + "Belevo_Server_Kommunikation", + "Wolkenwarscheinlichkeit-Wert ist nicht vorhanden." + ); + } + + if (!empty($json)) { + // Kombiniere die URL mit der Anlagenummer + $fullURL = $baseURL; + $this->SendJsonToInfluxDB($fullURL, $json); + } else { + IPS_LogMessage( + "Belevo_Server_Kommunikation", + "Keine Aufzeichnung im Influx: Anlagenummer oder JSON-Daten fehlen" + ); + } + } + + public function MakeJson($json) + { + $an_nummer = $this->ReadPropertyString("Anlagenummer"); // Anlagenummer lesen + $InfluxJaNein = $this->ReadPropertyBoolean("InfluxJaNein"); + + // Werte in ein Array packen + $influxData = [ + "Value" => [ + "InfluxAllowed" => $InfluxJaNein, // Setze InfluxAllowed + ], + "Parameter" => [ + "Influxadr" => $an_nummer, + ], + "Tracker" => $json, // Initialisiere Tracker als leeres Array + ]; + + // Array in JSON konvertieren + $json = json_encode($influxData); + + // JSON zurückgeben oder weiterverarbeiten + return $json; + } + + private function SendJsonToInfluxDB($url, $jsonData) + { + $pw = $this->ReadPropertyString("Gerätenummer"); + if (empty($pw)) { + IPS_LogMessage( + "Belevo_Server_Kommunikation", + "Fehler: Gerätenummer ist leer." + ); + return; + } + + // cURL Initialisieren + $curl = curl_init(); + // Optionen für cURL-Request definieren + curl_setopt_array($curl, [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, // POST-Methode verwenden + CURLOPT_HTTPHEADER => [ + "Content-Type: application/json", // Header für JSON-Daten setzen + "Accept: application/json", + "id:" . $pw, + ], + CURLOPT_POSTFIELDS => $jsonData, // JSON-Daten als POST-Feld senden + ]); + + // cURL-Request ausführen + $result = curl_exec($curl); + + $error = curl_error($curl); + $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + // cURL beenden + curl_close($curl); + + if ($error) { + IPS_LogMessage( + "Belevo_Server_Kommunikation", + "Fehler beim Senden an Influx: " . $error + ); + } + } +} + +?> diff --git a/Boiler_2_Stufig_Mit_Fueler/README.md b/Boiler_2_Stufig_Mit_Fueler/README.md new file mode 100644 index 0000000..b059e3a --- /dev/null +++ b/Boiler_2_Stufig_Mit_Fueler/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/Boiler_2_Stufig_Mit_Fueler/form.json b/Boiler_2_Stufig_Mit_Fueler/form.json new file mode 100644 index 0000000..d9d8436 --- /dev/null +++ b/Boiler_2_Stufig_Mit_Fueler/form.json @@ -0,0 +1,45 @@ +{ + "elements": [ + { + "type": "Label", + "caption": "Konfiguration der nötigen Schaltkontakte und Nennleistungen" + }, + { + "type": "NumberSpinner", + "name": "IdleCounterMax", + "caption": "Zyklen zwischen zwei Leisutungsänderungen", + "suffix": "" + }, + { + "type": "NumberSpinner", + "name": "BoilerLeistungTeillast", + "caption": "Leistug Teillast", + "suffix": "" + }, + { + "type": "NumberSpinner", + "name": "BoilerLeistungVolllast", + "caption": "Leistug Vollast", + "suffix": "" + }, + { + "type": "SelectVariable", + "name": "Boilertemperatur", + "caption": "Variable für Boilertemperatur", + "test": true + }, + { + "type": "SelectVariable", + "name": "Kontakt_Teillast", + "caption": "Schaltkontakt Teillast", + "test": true + }, + { + "type": "SelectVariable", + "name": "Kontakt_Volllast", + "caption": "Schaltkontakt Volllast", + "test": true + } + + ] +} diff --git a/Boiler_2_Stufig_Mit_Fueler/module.json b/Boiler_2_Stufig_Mit_Fueler/module.json new file mode 100644 index 0000000..bb2a98d --- /dev/null +++ b/Boiler_2_Stufig_Mit_Fueler/module.json @@ -0,0 +1,12 @@ +{ + "id": "{64ECCE9F-7757-0ABA-0B81-501108A25565}", + "name": "Boiler_2_Stufig_Mit_Fueler", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/Boiler_2_Stufig_Mit_Fueler/module.php b/Boiler_2_Stufig_Mit_Fueler/module.php new file mode 100644 index 0000000..7da6ab9 --- /dev/null +++ b/Boiler_2_Stufig_Mit_Fueler/module.php @@ -0,0 +1,221 @@ +RegisterVariableInteger("LockPrio", "LockPrio"); + $this->RegisterVariableInteger("UserPrio", "UserPrio"); + + // Energiehandling + $this->RegisterVariableBoolean("Idle", "Idle", "", 0); + $this->RegisterVariableInteger("CurrentPower", "CurrentPower", "", 0); + $this->RegisterVariableFloat("UsedEnergy", "UsedEnergy", "", 0); + $this->RegisterVariableString("PowerSteps", "PowerSteps"); // PowerSteps-Variable registrieren + + // Trägheit system + $this->RegisterPropertyInteger("IdleCounterMax", 2); + $this->RegisterVariableInteger("IdleCounter", "IdleCounter", "", 0); + $this->SetValue("IdleCounter", 0); + + // Boiler spezifische Properties + $this->RegisterPropertyInteger("BoilerLeistungTeillast", 3000); + $this->RegisterPropertyInteger("BoilerLeistungVolllast", 6000); + $this->RegisterPropertyInteger("Boilertemperatur", 0); + + // Boiler spezifische Variablen + $this->RegisterVariableInteger("Boilermintemp","Boilermintemp","",45); + $this->RegisterVariableInteger("Boilermaxtemp","Boilermaxtemp","",60); + $this->RegisterVariableInteger("Boilerlegiotemp","Boilerlegiotemp","",65); + $this->RegisterVariableInteger("LegioCounter", "LegioCounter", "", 0); + + // Schaltkontakte + $this->RegisterPropertyInteger("Kontakt_Teillast", 0); + $this->RegisterPropertyInteger("Kontakt_Volllast", 0); + + // Initialisieren + $this->SetValue("Idle", true); + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + } + + // Aktionen verarbeiten + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + case "SetCurrentPower": + $this->SetCurrentPower($Value); + break; + case "GetCurrentData": + return $this->GetCurrentData($Value); + default: + throw new Exception("Invalid Ident"); + } + } + + // Methode zum Setzen des aktuellen Stromverbrauchs + public function SetCurrentPower(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("CurrentPower")); + if ($power != $lastPower) { + $this->SetValue("Idle", false); + $this->SetValue( + "IdleCounter", + $this->ReadPropertyInteger("IdleCounterMax") + ); + } + + // Setze die neue CurrentPower + $this->SetValue("CurrentPower", $power); + + // IdleCounter verarbeiten + $this->ProcessIdleCounter(); + } + + // Methode zum Abrufen der aktuellen Daten + public function GetCurrentData(bool $Peak) + { + $LegioCounter = $this->GetValue("LegioCounter"); + + $boilerTemp = GetValue($this->ReadPropertyInteger("Boilertemperatur")); + $minTemp = $this->GetValue("Boilermintemp"); + $maxTemp = $this->GetValue("Boilermaxtemp"); + $LegioTemp = $this->GetValue("Boilerlegiotemp"); + $teilLeistung = $this->ReadPropertyInteger("BoilerLeistungTeillast"); + $vollLeistung = $this->ReadPropertyInteger("BoilerLeistungVolllast"); + + $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; + } + + $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("CurrentPower"); + if ($lastpower != GetValue("CurrentPower")) { + $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/CC100_HW/README.md b/CC100_HW/README.md new file mode 100644 index 0000000..b059e3a --- /dev/null +++ b/CC100_HW/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/CC100_HW/module.json b/CC100_HW/module.json new file mode 100644 index 0000000..af02547 --- /dev/null +++ b/CC100_HW/module.json @@ -0,0 +1,12 @@ +{ + "id": "{21F800F4-A506-62C9-CF26-9CA152629C22}", + "name": "CC100_HW", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/CC100_HW/module.php b/CC100_HW/module.php new file mode 100644 index 0000000..f0b15c3 --- /dev/null +++ b/CC100_HW/module.php @@ -0,0 +1,205 @@ +RegisterPropertyString("FilePath", $this->DigOutPID); + $this->RegisterVariableBoolean("Bit1", "DO 1", "~Switch", 1); + $this->RegisterVariableBoolean("Bit2", "DO 2", "~Switch", 2); + $this->RegisterVariableBoolean("Bit3", "DO 3", "~Switch", 3); + $this->RegisterVariableBoolean("Bit4", "DO 4", "~Switch", 4); + + $this->RegisterVariableBoolean("DI1", "DI1"); + $this->RegisterVariableBoolean("DI2", "DI2"); + $this->RegisterVariableBoolean("DI3", "DI3"); + $this->RegisterVariableBoolean("DI4", "DI4"); + $this->RegisterVariableBoolean("DI5", "DI5"); + $this->RegisterVariableBoolean("DI6", "DI6"); + $this->RegisterVariableBoolean("DI7", "DI7"); + $this->RegisterVariableBoolean("DI8", "DI8"); + + $this->RegisterVariableFloat('PT1', 'PT1 Temperatur', '~Temperature', 5); + $this->RegisterVariableFloat('PT2', 'PT2 Temperatur', '~Temperature', 6); + $this->EnableAction("Bit1"); + $this->EnableAction("Bit2"); + $this->EnableAction("Bit3"); + $this->EnableAction("Bit4"); + + // Timer für PT1 und PT2 einrichten + // Timer für PT1 und PT2 einrichten + $this->RegisterTimer("ReadPTValues", 30000, 'IPS_RequestAction(' . $this->InstanceID . ', "DOUT_ReadPTValues", "");'); + $this->RegisterTimer("WriteBits", 2000, 'IPS_RequestAction(' . $this->InstanceID . ', "UpdateFile", "");'); + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + // Timer aktivieren + $this->SetTimerInterval("ReadPTValues", 30000); + } + + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + case "DOUT_ReadPTValues": + $this->DOUT_ReadPTValues(); + break; + case "UpdateFile": + $this->UpdateFile(); + break; + default: + throw new Exception("Invalid Ident"); + } + } + + private function UpdateFile() + { + $starttime = microtime(true) * 2000; + $this->TryWriteFile($starttime); + } + + private function readAndConvertToBools($filePath) + { + // Inhalt der Datei auslesen + $content = @file_get_contents($filePath); + + if ($content === false) { + throw new Exception("Fehler beim Lesen der Datei $filePath."); + } + + // Die gelesene Zahl bereinigen (Leerzeichen oder Zeilenumbrüche entfernen) + $number = trim($content); + + // Sicherstellen, dass die Zahl gültig ist + if (!ctype_digit($number)) { + throw new Exception( + "Der Inhalt der Datei ist keine gültige Zahl: $number" + ); + } + + // Die Zahl in einen Integer umwandeln + $intValue = (int) $number; + + // Sicherstellen, dass die Zahl zwischen 0 und 255 liegt + if ($intValue < 0 || $intValue > 255) { + throw new Exception( + "Die Zahl muss zwischen 0 und 255 liegen: $intValue" + ); + } + + // In einen 8-Bit-Binärstring umwandeln + $binaryString = str_pad(decbin($intValue), 8, "0", STR_PAD_LEFT); + + // Binärwerte in ein Array von booleschen Werten umwandeln + $boolValues = array_map( + fn($bit) => $bit === "1", + str_split($binaryString) + ); + + $this->SetValue("DI1", $boolValues[7]); + $this->SetValue("DI2", $boolValues[6]); + $this->SetValue("DI3", $boolValues[5]); + $this->SetValue("DI4", $boolValues[4]); + $this->SetValue("DI5", $boolValues[3]); + $this->SetValue("DI6", $boolValues[2]); + $this->SetValue("DI7", $boolValues[1]); + $this->SetValue("DI8", $boolValues[0]); + return $boolValues; + } + + private function TryWriteFile($starttime) + { + $this->readAndConvertToBools($DIN_FILE); + + $currentTime = microtime(true) * 1000; + if ($currentTime - $starttime <= $this->maxTime) { + $file = $this->ReadPropertyString("FilePath"); + if (is_writable($file)) { + $bit1 = GetValueBoolean($this->GetIDForIdent("Bit1")); + $bit2 = GetValueBoolean($this->GetIDForIdent("Bit2")); + $bit3 = GetValueBoolean($this->GetIDForIdent("Bit3")); + $bit4 = GetValueBoolean($this->GetIDForIdent("Bit4")); + + $value = + ($bit4 ? 8 : 0) + + ($bit3 ? 4 : 0) + + ($bit2 ? 2 : 0) + + ($bit1 ? 1 : 0); + + if (@file_put_contents($file, $value) === false) { + usleep($this->waitingTime * 1000); + $this->TryWriteFile($starttime); + } + } else { + + usleep($this->waitingTime * 1000); + $this->TryWriteFile($starttime); + } + } else { + IPS_LogMessage( + "DOUTModule", + "Fehler: Schreibvorgang für Datei $file hat zu lange gedauert." + ); + } + } + + public function DOUT_ReadPTValues() + { + $pt1Value = $this->ReadPTValue($this->PT1PID); + $pt2Value = $this->ReadPTValue($this->PT2PID); + + SetValue($this->GetIDForIdent("PT1"), $pt1Value); + SetValue($this->GetIDForIdent("PT2"), $pt2Value); + } + + private function ReadPTValue($file) + { + if (file_exists($file)) { + $data = @file_get_contents($file); + if ($data !== false) { + $data = intval($data); + $scale = 0; + if ($data >= 600 && $data < 3600) { + $scale = 37; + } elseif ($data >= 3600 && $data < 6700) { + $scale = 43; + } elseif ($data >= 6700 && $data < 9750) { + $scale = 45; + } elseif ($data >= 9750 && $data < 12740) { + $scale = 49; + } elseif ($data >= 12740 && $data < 15700) { + $scale = 50.6; + } elseif ($data >= 15700 && $data < 21000) { + $scale = 52.4; + } elseif ($data > 21000) { + $scale = 53.7; + } + + $numb = $data / $scale - 200; + return round($numb, 1); + } else { + IPS_LogMessage( + "DOUTModule", + "Fehler: Datei $file konnte nicht gelesen werden." + ); + } + } else { + IPS_LogMessage( + "DOUTModule", + "Fehler: Datei $file existiert nicht." + ); + } + return 0; + } +} + +?> diff --git a/Ladestation_Universal/README.md b/Ladestation_Universal/README.md new file mode 100644 index 0000000..b059e3a --- /dev/null +++ b/Ladestation_Universal/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/Ladestation_Universal/form.json b/Ladestation_Universal/form.json new file mode 100644 index 0000000..269236a --- /dev/null +++ b/Ladestation_Universal/form.json @@ -0,0 +1,41 @@ +{ + "elements": [ + { + "type": "ValidationTextBox", + "name": "IP_Adresse", + "caption": "IP-Adresse Go-E", + "suffix": "" + }, + { + "type": "NumberSpinner", + "name": "MinLeistung", + "caption": "Mindestleistung Ladestation" + }, + { + "type": "NumberSpinner", + "name": "MaxLeistung", + "caption": "Maximlalleistung Ladestation" + }, + { + "type": "NumberSpinner", + "name": "MinLeistung_1ph", + "caption": "Mindestleistung Ladestation 1-Phasig" + }, + { + "type": "NumberSpinner", + "name": "MaxLeistung_1ph", + "caption": "Maximlalleistung Ladestation 1-Phasig" + }, + { + "type": "NumberSpinner", + "name": "IdleCounterMax", + "caption": "Zyklen zwischen zwei Leisutungsänderungen", + "suffix": "" + }, + + { + "type": "Label", + "caption": "Aktuell wird nur Go-E ladestation zu testzwecken unterstützt!" + } + ] +} diff --git a/Ladestation_Universal/module.json b/Ladestation_Universal/module.json new file mode 100644 index 0000000..f0512fe --- /dev/null +++ b/Ladestation_Universal/module.json @@ -0,0 +1,12 @@ +{ + "id": "{C684C38F-8C0D-CCE0-4533-11769F314B2D}", + "name": "Ladestation_Universal", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/Ladestation_Universal/module.php b/Ladestation_Universal/module.php new file mode 100644 index 0000000..465917a --- /dev/null +++ b/Ladestation_Universal/module.php @@ -0,0 +1,497 @@ +RegisterVariableInteger("LockPrio", "LockPrio"); + $this->RegisterVariableInteger("UserPrio", "UserPrio"); + + // Energiehandling + $this->RegisterVariableBoolean("Idle", "Idle", "", 0); + $this->RegisterVariableInteger("CurrentPower", "CurrentPower", "", 0); + $this->RegisterVariableFloat("UsedEnergy", "UsedEnergy", "", 0); + $this->RegisterVariableString("PowerSteps", "PowerSteps"); // PowerSteps-Variable registrieren + + // Trägheit + $this->RegisterPropertyInteger("IdleCounterMax", 2); + $this->RegisterVariableInteger("IdleCounter", "IdleCounter", "", 0); + $this->SetValue("IdleCounter", 0); + + // Properties registrieren + $this->RegisterPropertyInteger("MinLeistung", 3600); + $this->RegisterPropertyInteger("MaxLeistung", 11000); + + $this->RegisterPropertyInteger("MinLeistung_1ph", 1400); + $this->RegisterPropertyInteger("MaxLeistung_1ph", 7800); + $this->RegisterPropertyString("IP_Adresse", "0.0.0.0"); + + $this->RegisterVariableBoolean("Ladebereit", "Ladebereit", "~Switch", 11); + $this->RegisterVariableBoolean("Solarladen", "Solarladen", "~Switch", 11); + $this->RegisterVariableInteger("Fahrzeugstatus", "Fahrzeugstatus", "", 0); + $this->RegisterVariableInteger("Lademodus", "Lademodus", "", 0); + $this->RegisterVariableFloat("Ladeleistung_Effektiv", "Ladeleistung_Effektiv", "", 0); + + $this->RegisterVariableBoolean("Peak", "Peak", "", 0); + + $this->RegisterVariableInteger("Ladestrom", "Ladestrom"); + $this->RegisterVariableInteger("Ladeleistung", "Ladeleistung"); + + // Initialisieren + $this->SetValue("Idle", true); + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + // Zusätzliche Anpassungen nach Bedarf + } + + // Aktionen verarbeiten + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + case "SetCurrentPower": + $this->SetCurrentPower($Value); + break; + case "GetCurrentData": + $powerSteps = $this->GetCurrentData($Value); + return $powerSteps; + default: + throw new Exception("Invalid Ident"); + } + } + + public function SetCurrentPower(int $power) + { + $internalPower = GetValue($this->GetIDForIdent("CurrentPower")); + // Aktuelle Leistungsvorgabe setzen + SetValue($this->GetIDForIdent("CurrentPower"), $power); + if ($power != $internalPower) { + // Setze die interne Leistungsvorgabe + + // Idle für 4 Zyklen auf false setzen + SetValue($this->GetIDForIdent("Idle"), false); + SetValue( + $this->GetIDForIdent("IdleCounter"), + $this->ReadPropertyInteger("IdleCounterMax") + ); + } else { + // IdleCallCounter herunterzählen, wenn power == interne Leistungsvorgabe + $idleCounter = GetValue($this->GetIDForIdent("IdleCounter")); + if ($idleCounter > 0) { + $idleCounter--; + SetValue($this->GetIDForIdent("IdleCounter"), $idleCounter); + if ($idleCounter == 0) { + SetValue($this->GetIDForIdent("Idle"), true); + } + } + } + + // Ladeleistung setzen + $peak = GetValue($this->GetIDForIdent("Peak")); + $solarladen = GetValue($this->GetIDForIdent("Solarladen")); + $Ladebereit = GetValue($this->GetIDForIdent("Ladebereit")); + if (!$Ladebereit) { + $this->sendPowerToStation(0); + SetValue($this->GetIDForIdent("Ladeleistung"), 0); + SetValue($this->GetIDForIdent("Ladestrom"), 0); + } elseif (!$peak && !$solarladen) { + // Wenn weder Peak noch Solarladen aktiv sind, setze Ladeleistung auf MaxLeistung + SetValue( + $this->GetIDForIdent("Ladeleistung"), + $this->ReadPropertyInteger("MaxLeistung") + ); + + if (GetValue($this->GetIDForIdent("Lademodus")) == 0) { + SetValue( + $this->GetIDForIdent("Ladestrom"), + $this->ReadPropertyInteger("MaxLeistung_1ph") / 240 + ); + $this->sendPowerToStation( + $this->ReadPropertyInteger("MaxLeistung_1ph") + ); + } elseif (GetValue($this->GetIDForIdent("Lademodus")) == 1) { + SetValue( + $this->GetIDForIdent("Ladestrom"), + $this->ReadPropertyInteger("MaxLeistung") / 400 / sqrt(3) + ); + $this->sendPowerToStation( + $this->ReadPropertyInteger("MaxLeistung") + ); + } + } else { + // Ansonsten setze Ladeleistung auf die aktuelle Leistungsvorgabe (CurrentPower) + SetValue($this->GetIDForIdent("Ladeleistung"), $power); + + if (GetValue($this->GetIDForIdent("Lademodus")) == 0) { + SetValue($this->GetIDForIdent("Ladestrom"), $power / 240); + } elseif (GetValue($this->GetIDForIdent("Lademodus")) == 1) { + SetValue( + $this->GetIDForIdent("Ladestrom"), + $power / 400 / sqrt(3) + ); + } + + $this->sendPowerToStation($power); + } + } + + // Methode zum Abrufen der aktuellen Daten + public function GetCurrentData(bool $Peak) + { + // Aktuelle Properties abrufen + $ladebereit = GetValue($this->GetIDForIdent("Ladebereit")); + $solarladen = GetValue($this->GetIDForIdent("Solarladen")); + + if (GetValue($this->GetIDForIdent("Lademodus")) == 0) { + $minLeistung = $this->ReadPropertyInteger("MinLeistung_1ph"); + $maxLeistung = $this->ReadPropertyInteger("MaxLeistung_1ph"); + } elseif (GetValue($this->GetIDForIdent("Lademodus")) == 1) { + $minLeistung = $this->ReadPropertyInteger("MinLeistung"); + $maxLeistung = $this->ReadPropertyInteger("MaxLeistung"); + } + + $ch = curl_init(); + + // Setze die URL + curl_setopt( + $ch, + CURLOPT_URL, + "http://" . $this->ReadPropertyString("IP_Adresse") . "/api/status" + ); + + // Setze die Option, die Antwort als String zurückzugeben + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + + // Führe die Anfrage aus und speichere die Antwort + $response = curl_exec($ch); + + // Schließe cURL + curl_close($ch); + + // Überprüfe, ob die Antwort nicht leer ist + if ($response) { + // Dekodiere die JSON-Antwort + $data = json_decode($response, true); + + // Überprüfe, ob das JSON-Dekodieren erfolgreich war und der Schlüssel "car" existiert + if (json_last_error() === JSON_ERROR_NONE && isset($data["car"])) { + // Speichere den Wert von "car" in der Variable $status + + SetValue( + $this->GetIDForIdent("Ladeleistung_Effektiv"), + $data["nrg"][11] + ); + + SetValue($this->GetIDForIdent("Fahrzeugstatus"), $data["car"]); + + if ($data["nrg"][6] > 1 && $data["car"] == 2) { + SetValue($this->GetIDForIdent("Lademodus"), 1); + } elseif ($data["nrg"][6] <= 1 && $data["car"] == 2) { + SetValue($this->GetIDForIdent("Lademodus"), 0); + } + } + } + + // Peak-Wert speichern + $this->SetValue("Peak", $Peak); + + // Array für die Powersteps initialisieren + $powerSteps = [0]; + + if (GetValue($this->GetIDForIdent("Fahrzeugstatus")) != 1) { + // Konfiguration des powerSteps-Arrays basierend auf den Properties + if (!$ladebereit) { + $powerSteps = [0]; + } elseif (!$Peak && !$solarladen) { + $powerSteps = [$maxLeistung]; + } elseif (!$Peak && $solarladen) { + $powerSteps = array_merge( + $powerSteps, + $this->getRangeLimits( + $minLeistung, + $maxLeistung, + GetValue($this->GetIDForIdent("Lademodus")) + ) + ); + } elseif ($solarladen && $Peak) { + $powerSteps = [0]; + } else { + $powerSteps += $this->getRangeLimits( + $minLeistung, + $maxLeistung, + GetValue($this->GetIDForIdent("Lademodus")) + ); + } + } + // PowerSteps in der RegisterVariable speichern + SetValue($this->GetIDForIdent("PowerSteps"), json_encode($powerSteps)); + + // Rückgabe der Powersteps + return $powerSteps; + } + + public function sendPowerToStation($value) + { + // Base URL + $baseUrl = + "http://" . $this->ReadPropertyString("IP_Adresse") . "/api/set?"; + IPS_LogMessage("Ladestation", "Aufgerufene ip" . $baseUrl); + $value = $this->convertPowerToCurrent( + $value, + GetValue($this->GetIDForIdent("Lademodus")) + ); + // Initialize a cURL session + $ch = curl_init(); + + // If value is 0, make a single request + if ($value == 0) { + $url = $baseUrl . "frc=1"; + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $response = curl_exec($ch); + curl_close($ch); + return $response; + } + + // For value between 1 and 32, make two requests + elseif ($value >= 1 && $value <= 32) { + // First request + $url1 = $baseUrl . "frc=2"; + curl_setopt($ch, CURLOPT_URL, $url1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $response1 = curl_exec($ch); + + // Check for errors + if (curl_errno($ch)) { + curl_close($ch); + return "Error:" . curl_error($ch); + } + + // Second request + $url2 = $baseUrl . "amp=$value"; + curl_setopt($ch, CURLOPT_URL, $url2); + $response2 = curl_exec($ch); + + // Check for errors + if (curl_errno($ch)) { + curl_close($ch); + return "Error:" . curl_error($ch); + } + + // Close cURL session + curl_close($ch); + + // Return responses + return [$response1, $response2]; + } + + // If value is out of range + else { + return "Invalid value. Must be between 0 and 32."; + } + } + + public function convertPowerToCurrent($value, $Lademodus) + { + if ($Lademodus == 1) { + if ($value == 0) { + return 0; + } elseif ($value == 4150) { + return 6; + } elseif ($value == 4850) { + return 7; + } elseif ($value == 5550) { + return 8; + } elseif ($value == 6250) { + return 9; + } elseif ($value == 6950) { + return 10; + } elseif ($value == 7600) { + return 11; + } elseif ($value == 8300) { + return 12; + } elseif ($value == 9000) { + return 13; + } elseif ($value == 9700) { + return 14; + } elseif ($value == 10300) { + return 15; + } elseif ($value == 11000) { + return 16; + } elseif ($value == 11750) { + return 17; + } elseif ($value == 12450) { + return 18; + } elseif ($value == 13150) { + return 19; + } elseif ($value == 13850) { + return 20; + } elseif ($value == 14550) { + return 21; + } elseif ($value == 15250) { + return 22; + } elseif ($value == 15900) { + return 23; + } elseif ($value == 16600) { + return 24; + } elseif ($value == 17300) { + return 25; + } elseif ($value == 18000) { + return 26; + } elseif ($value == 18700) { + return 27; + } elseif ($value == 19400) { + return 28; + } elseif ($value == 20100) { + return 29; + } elseif ($value == 20800) { + return 30; + } elseif ($value == 21500) { + return 31; + } elseif ($value >= 22000) { + return 32; + } + return 0; + } elseif ($Lademodus == 0) { + if ($value == 0) { + return 0; + } elseif ($value == 1450) { + return 6; + } elseif ($value == 1690) { + return 7; + } elseif ($value == 1930) { + return 8; + } elseif ($value == 2170) { + return 9; + } elseif ($value == 2410) { + return 10; + } elseif ($value == 2650) { + return 11; + } elseif ($value == 2890) { + return 12; + } elseif ($value == 3130) { + return 13; + } elseif ($value == 3370) { + return 14; + } elseif ($value == 3610) { + return 15; + } elseif ($value == 3850) { + return 16; + } elseif ($value == 4090) { + return 17; + } elseif ($value == 4330) { + return 18; + } elseif ($value == 4570) { + return 19; + } elseif ($value == 4810) { + return 20; + } elseif ($value == 5050) { + return 21; + } elseif ($value == 5290) { + return 22; + } elseif ($value == 5530) { + return 23; + } elseif ($value == 5770) { + return 24; + } elseif ($value == 6010) { + return 25; + } elseif ($value == 6250) { + return 26; + } elseif ($value == 6490) { + return 27; + } elseif ($value == 6730) { + return 28; + } elseif ($value == 6970) { + return 29; + } elseif ($value == 7210) { + return 30; + } elseif ($value == 7450) { + return 31; + } elseif ($value >= 7690) { + return 32; + } + return 0; + } + } + + public function getRangeLimits($min, $max, $Lademodus) + { + if ($Lademodus == 1) { + $limits = [ + 4150, + 4850, + 5550, + 6250, + 6950, + 7600, + 8300, + 9000, + 9700, + 10300, + 11000, + 11750, + 12450, + 13150, + 13850, + 14550, + 15250, + 15900, + 16600, + 17300, + 18000, + 18700, + 19400, + 20100, + 20800, + 21500, + 22000, + ]; + } elseif ($Lademodus == 0) { + $limits = [ + 1450, + 1690, + 1930, + 2170, + 2410, + 2650, + 2890, + 3130, + 3370, + 3610, + 3850, + 4090, + 4330, + 4570, + 4810, + 5050, + 5290, + 5530, + 5770, + 6010, + 6250, + 6490, + 6730, + 6970, + 7210, + 7450, + 7690, + ]; + } + + $result = []; + + foreach ($limits as $limit) { + if ($limit >= $min && $limit <= $max) { + $result[] = $limit; + } + } + + return $result; + } +} +?> diff --git a/Manager/README.md b/Manager/README.md new file mode 100644 index 0000000..b059e3a --- /dev/null +++ b/Manager/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/Manager/form.json b/Manager/form.json new file mode 100644 index 0000000..23706ae --- /dev/null +++ b/Manager/form.json @@ -0,0 +1,46 @@ +{ + "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": "List", + "name": "EnergyUserList", + "caption": "Verbraucher, die gemanagt werden sollen.", + "add": true, + "delete": true, + "sortable": true, + "columns": [ + { + "caption": "Energieverbraucher", + "name": "EnergyUser", + "width": "auto", + "add": 0, + "edit": { + "type": "SelectInstance", + "filter": "EnergyUser" + } + } + ] + } + ] +} diff --git a/Manager/module.json b/Manager/module.json new file mode 100644 index 0000000..304e436 --- /dev/null +++ b/Manager/module.json @@ -0,0 +1,12 @@ +{ + "id": "{F1F645F9-7F78-2843-2728-5D1708A4F835}", + "name": "Manager", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/Manager/module.php b/Manager/module.php new file mode 100644 index 0000000..3d07434 --- /dev/null +++ b/Manager/module.php @@ -0,0 +1,273 @@ +RegisterPropertyInteger("Peakleistung", 0); + $this->RegisterPropertyInteger("Ueberschussleistung", 0); + $this->RegisterPropertyInteger("Netzbezug", 0); // Initialisierung mit 0 + $this->RegisterPropertyString("EnergyUserList", "[]"); + + // Timer registrieren + $this->RegisterTimer( + "Timer_DistributeEnergy", + 5000, + "IPS_RequestAction(" . + $this->InstanceID . + ', "DistributeEnergy", "");' + ); + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + + // Zusätzliche Konfigurationslogik hier hinzufügen + $energyUserList = $this->ReadPropertyString("EnergyUserList"); + } + + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + case "DistributeEnergy": + $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 + if ($Netzbezug < ($Peakleistung - $Ueberschussleistung) / 2) { + $remainingPower = -1 * (-1 * $Ueberschussleistung + $Netzbezug); + $getCurrentDataParam = false; + } else { + $remainingPower = $Peakleistung - $Netzbezug; + $getCurrentDataParam = true; + } + + // EnergyUserList auslesen und dekodieren + $energyUserListJSON = $this->ReadPropertyString("EnergyUserList"); + $energyUserList = json_decode($energyUserListJSON, true); + + if (empty($energyUserList)) { + // Liste ist leer, daher nichts zu tun + return; + } + + // Schleife, um IPS_RequestAction für alle Benutzer auszuführen + foreach ($energyUserList as $user) { + if (IPS_InstanceExists($user["EnergyUser"])) { + IPS_RequestAction( + $user["EnergyUser"], + "GetCurrentData", + $getCurrentDataParam + ); + } + } + + $filteredEnergyUsers = []; + $allIdle = true; // Variable zur Überprüfung, ob alle Benutzer Idle = true sind + $totalCurrentPower = 0; // Variable zur Summierung der CurrentPower Werte + + foreach ($energyUserList as $user) { + if (!IPS_InstanceExists($user["EnergyUser"])) { + continue; + } + + // Werte direkt von der EnergyUser-Instanz abrufen + $currentPower = GetValue(IPS_GetObjectIDByIdent("CurrentPower", $user["EnergyUser"])); + $usedEnergy = GetValue(IPS_GetObjectIDByIdent("UsedEnergy", $user["EnergyUser"])); + $userPrio = GetValue(IPS_GetObjectIDByIdent("UserPrio", $user["EnergyUser"])); + $lockPrio = GetValue(IPS_GetObjectIDByIdent("LockPrio", $user["EnergyUser"])); + $idle = GetValue(IPS_GetObjectIDByIdent("Idle", $user["EnergyUser"])); + $powerStepsJson = GetValue(IPS_GetObjectIDByIdent("PowerSteps", $user["EnergyUser"])); + $powerSteps = json_decode($powerStepsJson, true); + + // EnergyUser-Daten zum gefilterten Array hinzufügen + $filteredEnergyUsers[] = [ + "EnergyUser" => $user["EnergyUser"], + "InstanceID" => $user["EnergyUser"], + "CurrentPower" => $currentPower, + "UsedEnergy" => $usedEnergy, + "UserPrio" => $userPrio, + "LockPrio" => $lockPrio, + "Idle" => $idle, + "PowerSteps" => $powerSteps, // PowerSteps direkt hier hinzufügen + ]; + + // Überprüfen, ob alle Benutzer Idle = true sind + if (!$idle) { + $allIdle = false; + } + + // Add the totalCurrentPower to the remainingPower + $totalCurrentPower += $currentPower; + } + $remainingPower += $totalCurrentPower; + + if (empty($filteredEnergyUsers)) { + return; + } + + // Wenn nicht alle Benutzer Idle = true sind, rufe SetCurrentPower mit CurrentPower Werten auf + if (!$allIdle) { + foreach ($filteredEnergyUsers as $user) { + IPS_RequestAction( + $user["InstanceID"], + "SetCurrentPower", + $user["CurrentPower"] + ); + } + return; + } + + // Sortiere die EnergyUser nach Priorität basierend auf dem Parameter + usort($filteredEnergyUsers, function ($a, $b) use ( + $getCurrentDataParam + ) { + $primaryKey = $getCurrentDataParam ? "LockPrio" : "UserPrio"; + + if ($a[$primaryKey] == $b[$primaryKey]) { + return $a["UsedEnergy"] <=> $b["UsedEnergy"]; + } + + return $a[$primaryKey] <=> $b[$primaryKey]; + }); + + // Primärschlüssel für die Priorität basierend auf dem Parameter auswählen + $priorityKey = $getCurrentDataParam ? "LockPrio" : "UserPrio"; + + // Schleife durch alle Prioritäten + $priorities = array_unique( + array_column($filteredEnergyUsers, $priorityKey) + ); + $groupedUsers = []; + + foreach ($priorities as $priority) { + $groupedUsers[$priority] = array_filter( + $filteredEnergyUsers, + function ($user) use ($priority, $priorityKey) { + return $user[$priorityKey] == $priority; + } + ); + } + // Jetzt kannst du die Benutzer gruppenweise verarbeiten + foreach ($groupedUsers as $priority => $users) { + // EnergyUser mit gleicher Priorität sammeln + $samePriorityUsers = isset($groupedUsers[$priority]) + ? $groupedUsers[$priority] + : []; + + // Wenn keine EnergyUser 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 (in_array(0, $entry["PowerSteps"])) { + $withZero[] = $entry; + } else { + $withoutZero[] = $entry; + } + } + // Methode für alle im withoutZero-Array aufrufen + if (!empty($withoutZero)) { + foreach ($withoutZero as $entry) { + $instanceID = $entry["InstanceID"]; + $minPowerStep = min($entry["PowerSteps"]); + + // Simulierte Methode (Debug-Ausgabe) + IPS_RequestAction( + $instanceID, + "SetCurrentPower", + $minPowerStep + ); + $remainingPower -= $entry["CurrentPower"]; + } + } + $samePriorityUsers = $withZero; + // Array für die verteilte Energie pro User erstellen + $userEnergyProv = []; + $userEnergyProv = array_fill_keys( + array_column($samePriorityUsers, "InstanceID"), + 0 + ); // Initialisierung für jeden Benutzer auf 0 setzen + + // Alle Schritte der Benutzer in einem Array sammeln + $allSteps = []; + foreach ($samePriorityUsers as $user) { + foreach ($user["PowerSteps"] as $step) { + $allSteps[] = [ + "user" => $user["InstanceID"], + "step" => $step, + ]; + } + } + + // Sortiere die Schritte nach Größe + usort($allSteps, function ($a, $b) { + return $a["step"] <=> $b["step"]; + }); + + // 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]) { + // Aktualisiere die verbleibende Energie und die bereitgestellte Energie für den Benutzer + $remainingPower -= $powerstep - $userEnergyProv[$user]; + $userEnergyProv[$user] = $powerstep; + } //else { + // Wenn nicht genug Energie vorhanden ist, setze die verbleibende Energie + //} + } + + // Energie für jeden EnergyUser verbrauchen und loggen + foreach ($userEnergyProv as $userInstanceID => $energy) { + $weui = min( + array_column( + array_filter($allSteps, function ($entry) use ( + $userInstanceID + ) { + return $entry["user"] == $userInstanceID; + }), + "step" + ) + ); + + $energy = max($energy, $weui); // Testzeile + + // Methode SetCurrentPower für jeden EnergyUser aufrufen + if (IPS_InstanceExists($userInstanceID)) { + IPS_RequestAction( + $userInstanceID, + "SetCurrentPower", + $energy + ); // Annahme: SetCurrentPower wird über eine Aktionsanfrage ausgeführt + } + } + } + } +} +?> diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d0656d --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Energiemanager_Symconmodule_Demo + +Module zum Managen und Optimieren von Solarstrom und Lastmanagement innerhalb eines Symconservers \ No newline at end of file diff --git a/Verbraucher_1_Stufig/README.md b/Verbraucher_1_Stufig/README.md new file mode 100644 index 0000000..b059e3a --- /dev/null +++ b/Verbraucher_1_Stufig/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_1_Stufig/form.json b/Verbraucher_1_Stufig/form.json new file mode 100644 index 0000000..8b4b348 --- /dev/null +++ b/Verbraucher_1_Stufig/form.json @@ -0,0 +1,40 @@ +{ + "elements": [ + { + "type": "Label", + "caption": "Einstellungen für einstufigen Verbruacher Ein-Aus" + }, + + { + "type": "NumberSpinner", + "name": "IdleCounterMax", + "caption": "Zyklen zwischen zwei Leisutungsänderungen", + "suffix": "" + }, + { + "type": "NumberSpinner", + "name": "Mindesttlaufzeit", + "caption": "Mindestlaufzeit des Verbruachers wärend eines Tages", + "suffix": "" + }, + { + "type": "NumberSpinner", + "name": "Zeit_Zwischen_Zustandswechseln", + "caption": "Mindestlaufzeit des Verbrauchers bei Lastschaltung", + "suffix": "" + }, + { + "type": "NumberSpinner", + "name": "BoilerLeistung", + "caption": "Leistung des Verbrauchers", + "suffix": "" + }, + { + "type": "SelectVariable", + "name": "Schaltkontakt1", + "caption": "Zu schaltenden Kontakt", + "test": true + } + + ] +} diff --git a/Verbraucher_1_Stufig/module.json b/Verbraucher_1_Stufig/module.json new file mode 100644 index 0000000..634bbe3 --- /dev/null +++ b/Verbraucher_1_Stufig/module.json @@ -0,0 +1,12 @@ +{ + "id": "{5A156BE6-30FE-55AC-A832-7F80BF043C8C}", + "name": "Verbraucher_1_Stufig", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/Verbraucher_1_Stufig/module.php b/Verbraucher_1_Stufig/module.php new file mode 100644 index 0000000..979cd59 --- /dev/null +++ b/Verbraucher_1_Stufig/module.php @@ -0,0 +1,219 @@ +RegisterVariableInteger("LockPrio", "LockPrio"); + $this->RegisterVariableInteger("UserPrio", "UserPrio"); + + // Energiehandling + $this->RegisterVariableBoolean("Idle", "Idle", "", 0); + $this->RegisterVariableBoolean("IstNacht", "IstNacht", "", 0); + $this->RegisterVariableInteger("CurrentPower", "CurrentPower", "", 0); + $this->RegisterVariableInteger("DailyOnTime", "DailyOnTime", "", 0); + $this->RegisterVariableFloat("UsedEnergy", "UsedEnergy", "", 0); + $this->RegisterVariableString("PowerSteps", "PowerSteps"); // PowerSteps-Variable registrieren + + // Neue Variable für den Timer-Status + $this->RegisterVariableBoolean("IsTimerActive", "IsTimerActive", "", 0); + $this->SetValue("IsTimerActive", false); + + // Trägheit + $this->RegisterPropertyInteger("IdleCounterMax", 4); + $this->RegisterVariableInteger("IdleCounter", "IdleCounter", "", 0); + $this->SetValue("IdleCounter", 0); + + $this->RegisterPropertyInteger("BoilerLeistung", 6000); // Standardwert für Volllast + $this->RegisterPropertyInteger("Mindesttlaufzeit", 4); // Standardwert für Volllast + $this->RegisterPropertyInteger("Zeit_Zwischen_Zustandswechseln", 1); + $this->RegisterPropertyInteger("Schaltkontakt1", 0); + + // Timer für Zeit_Zwischen_Zustandswechseln + $this->RegisterTimer( + "ZustandswechselTimer", + 0, + "IPS_RequestAction(" . + $this->InstanceID . + ', "ResetPowerSteps", "");' + ); + + //Initialisieren + $this->SetValue("Idle", true); + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + } + + // Aktionen verarbeiten + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + case "SetCurrentPower": + $this->SetCurrentPower($Value); + break; + case "GetCurrentData": + return $this->GetCurrentData($Value); + break; + case "ResetPowerSteps": + $this->ResetPowerSteps($Value); + 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("CurrentPower")]) + ); + + // Timer stoppen + $this->SetTimerInterval("ZustandswechselTimer", 0); + + // Timer-Status auf false setzen + $this->SetValue("IsTimerActive", false); + } + // Methode zum Setzen des aktuellen Stromverbrauchs + public function SetCurrentPower(float $power) + { + $this->CheckIdle($power); + if ($this->GetValue("CurrentPower") != $power) { + $this->SetTimerOn(); + } + $this->SetValue("CurrentPower", $power); + $boilerLeistung = $this->ReadPropertyInteger("BoilerLeistung"); + $schaltkontaktID = $this->ReadPropertyInteger("Schaltkontakt1"); + + if ($power == $boilerLeistung) { + $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"); + $Mindestlaufzeit = + $this->ReadPropertyInteger("Mindesttlaufzeit") * 60 * 12; + + // Überprüfen, ob der Timer aktiv ist + if ($this->GetValue("IsTimerActive")) { + // Timer ist aktiv, PowerSteps setzen + $this->SetValue( + "PowerSteps", + json_encode([$this->GetValue("CurrentPower")]) + ); + } + // Wenn Nacht und Mindestlaufzeit nicht erreicht ist + elseif ($NeuesIstNacht && $DailyOnTime < $Mindestlaufzeit) { + if ($Peak) { + $this->SetValue( + "PowerSteps", + json_encode([ + 0, + $this->ReadPropertyInteger("BoilerLeistung"), + ]) + ); + } else { + $this->SetValue("PowerSteps",json_encode([$this->ReadPropertyInteger("BoilerLeistung")])); + } + } + // Andernfalls + else { + if ($Peak) { + $this->SetValue("PowerSteps", json_encode([0])); + } else { + $this->SetValue("PowerSteps",json_encode([0,$this->ReadPropertyInteger("BoilerLeistung"),]) + ); + } + } + } + + public function CheckIdle($power) + { + $lastpower = GetValue($this->GetIDForIdent("CurrentPower")); + 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("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/WP_Steuerung/form.json b/WP_Steuerung/form.json new file mode 100644 index 0000000..a86a354 --- /dev/null +++ b/WP_Steuerung/form.json @@ -0,0 +1,71 @@ +{ + "elements": [ + { + "type": "Label", + "caption": "Einstellungen für Wärmepumpe" + }, + + { + "type": "NumberSpinner", + "name": "Wolkenschwellwert", + "caption": "Wolkenschwellwert für Sperre", + "suffix": "" + }, + { + "type": "SelectVariable", + "name": "Wolkenwert", + "caption": "Variable mit der Wolkenprognose", + "test": true + }, + { + "type": "SelectVariable", + "name": "Aussentemperatur", + "caption": "Variable mit der Aussentemperatur", + "test": true + }, + { + "type": "NumberSpinner", + "name": "WP_Leistung", + "caption": "Leistung Wärmepumpe", + "suffix": "" + }, + { + "type": "SelectVariable", + "name": "Referenzzeit", + "caption": "Referenzzeit für Sonnenaufgang (von Standort Modul)", + "test": true + }, + { + "type": "SelectVariable", + "name": "Sperrkontakt", + "caption": "Zu schaltenden Kontakt für WP-Sperre", + "test": true + }, + { + "type": "SelectVariable", + "name": "Kontakt_Erhoeung", + "caption": "Zu schaltenden Kontakt für WP-Erhöhung", + "test": true + }, + + { + "type": "CheckBox", + "name": "Schwellwert_Anwenden", + "caption": "Schwellwert zum Entsperren Aktiv", + "test": true + }, + { + "type": "NumberSpinner", + "name": "Schwellwert", + "caption": "Warmwassertemperatur auf welche der Schwellwert angewendet wird.", + "test": true + }, + { + "type": "SelectVariable", + "name": "WW_Temp", + "caption": "Variable mit der Aktuellen Warmwassertemperatur", + "test": true + } + + ] +} diff --git a/WP_Steuerung/module.json b/WP_Steuerung/module.json new file mode 100644 index 0000000..6bde917 --- /dev/null +++ b/WP_Steuerung/module.json @@ -0,0 +1,12 @@ +{ + "id": "{9DAC29C7-F381-F2A4-7CE2-F391C4FBAF38}", + "name": "WP_Steuerung", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/WP_Steuerung/module.php b/WP_Steuerung/module.php new file mode 100644 index 0000000..fb87125 --- /dev/null +++ b/WP_Steuerung/module.php @@ -0,0 +1,289 @@ +RegisterVariableInteger("LockPrio", "LockPrio"); + $this->RegisterVariableInteger("UserPrio", "UserPrio"); + + // Energiehandling + $this->RegisterVariableBoolean("Idle", "Idle", "", true); + $this->RegisterVariableInteger("CurrentPower", "CurrentPower", "", 0); + $this->RegisterVariableBoolean("Sperrzeit", "Sperrzeit", "", false); + $this->RegisterVariableFloat("UsedEnergy", "UsedEnergy", "", 0); + $this->RegisterVariableString("PowerSteps", "PowerSteps"); // PowerSteps-Variable registrieren + + // Trägheit + $this->RegisterPropertyInteger("IdleCounterMax", 4); + $this->RegisterVariableInteger("IdleCounter", "IdleCounter", "", 0); + $this->SetValue("IdleCounter", 0); + + $this->RegisterVariableInteger("Mindestlaufzeit", "IdleCounter", "", 0); + + $this->RegisterVariableInteger("Zustand_WP", "Zustand_WP", "", 1); + + $this->RegisterVariableInteger("WP_Laufzeit_Zahler", "WP_Laufzeit_Zahler", "", 20*12); + $this->RegisterVariableBoolean("LetzterPeakwert", "LetzterPeakwert", "", false); + $this->RegisterVariableBoolean("LetzterPeakwert_Aenderung", "LetzterPeakwert_Aenderung", "", false); + + $this->RegisterPropertyInteger("WP_Leistung", 6000); + $this->RegisterPropertyInteger("Wolkenschwellwert", 60); + $this->RegisterPropertyInteger("Wolkenwert", 0); + $this->RegisterPropertyInteger("Aussentemperatur", 0); + $this->RegisterPropertyInteger("Referenzzeit", 0); + $this->RegisterPropertyInteger("Sperrkontakt", 0); + $this->RegisterPropertyInteger("Kontakt_Erhoeung", 0); + $this->RegisterPropertyBoolean("Schwellwert_Anwenden", false); + $this->RegisterPropertyInteger("Schwellwert", 0); + $this->RegisterPropertyInteger("WW_Temp", 1); + + //Initialisieren + $this->SetValue("Idle", true); + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + } + + // Aktionen verarbeiten + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + case "SetCurrentPower": + $this->SetCurrentPower($Value); + break; + case "GetCurrentData": + return $this->GetCurrentData($Value); + break; + case "ResetPowerSteps": + $this->ResetPowerSteps($Value); + break; + default: + throw new Exception("Invalid Ident"); + } + } + + // Methode zum Setzen des aktuellen Stromverbrauchs + public function SetCurrentPower(float $power) + { + $sperrzei_abs = + ((60 * + 60 * + (GetValue($this->ReadPropertyInteger("Aussentemperatur")) + + 10)) / + 25) * + 6; + + if ($sperrzei_abs > 6 * 60 * 60) { + $sperrzei_abs = 6 * 60 * 60; + } + if ( + $this->ReadPropertyInteger("Wolkenschwellwert") < + GetValue($this->ReadPropertyInteger("Wolkenwert")) || + $sperrzei_abs < 0 + ) { + $sperrzei_abs = 0; + } + $sperrzeit = (GetValue($this->ReadPropertyInteger("Referenzzeit")) % 86400) - $sperrzei_abs; + $entsperrzeit = GetValue($this->ReadPropertyInteger("Referenzzeit")) % 86400; + $aktuellezeit = time() % 86400; + + $this->CheckIdle($power); + + $its_lock_time = $this->GetValue("Sperrzeit"); + if ( + $aktuellezeit > $sperrzeit && + $aktuellezeit < $entsperrzeit && + $this->GetValue("WP_Laufzeit_Zahler") > 240 + ) { + $this->SetValue("Sperrzeit", true); + } elseif ($this->GetValue("WP_Laufzeit_Zahler") > 240) { + $this->SetValue("Sperrzeit", false); + } + + $newlocktime = $this->GetValue("Sperrzeit"); + + if ( + $newlocktime == true && + $its_lock_time == false && + $this->GetValue("WP_Laufzeit_Zahler") > 240 + ) { + $this->SetValue("WP_Laufzeit_Zahler", 0); + SetValue($this->ReadPropertyInteger("Sperrkontakt"), true); + } elseif ( + $newlocktime == false && + $its_lock_time == true && + $this->GetValue("WP_Laufzeit_Zahler") > 240 + ) { + $this->SetValue("WP_Laufzeit_Zahler", 0); + SetValue($this->ReadPropertyInteger("Sperrkontakt"), false); + } elseif ( + $this->GetValue("CurrentPower") != $power || + ($this->GetValue("WP_Laufzeit_Zahler") > 240 && + $this->GetValue("LetzterPeakwert") != + $this->GetValue("LetzterPeakwert_Aenderung")) + ) { + $this->SetValue("WP_Laufzeit_Zahler", 0); + + $LastPeak = $this->GetValue("LetzterPeakwert"); + $this->SetValue("LetzterPeakwert_Aenderung", $LastPeak); + $state = $this->GetValue("Zustand_WP"); + + if ($state == 0) { + $this->SetValue("Zustand_WP", 1); + SetValue($this->ReadPropertyInteger("Sperrkontakt"), false); + SetValue($this->ReadPropertyInteger("Kontakt_Erhoeung"), false); + } elseif ($state == 1) { + // Normalbetrieb + if ( + $LastPeak && + $power == $this->ReadPropertyInteger("WP_Leistung") + ) { + $this->SetValue("Zustand_WP", 1); + $this->SetValue( + "CurrentPower", + $this->ReadPropertyInteger("WP_Leistung") + ); + } elseif ($LastPeak && $power == 0) { + $this->SetValue("Zustand_WP", 2); + $this->SetValue("CurrentPower", 0); + } elseif ( + $LastPeak == false && + $power == $this->ReadPropertyInteger("WP_Leistung") + ) { + $this->SetValue("Zustand_WP", 3); + $this->SetValue( + "CurrentPower", + $this->ReadPropertyInteger("WP_Leistung") + ); + } elseif ($LastPeak == false && $power == 0) { + $this->SetValue("Zustand_WP", 1); + $this->SetValue("CurrentPower", 0); + } + } elseif ($state == 2) { + // Sperre + if ( + $LastPeak && + $power == $this->ReadPropertyInteger("WP_Leistung") + ) { + $this->SetValue("Zustand_WP", 1); + $this->SetValue( + "CurrentPower", + $this->ReadPropertyInteger("WP_Leistung") + ); + } elseif ($LastPeak && $power == 0) { + $this->SetValue("Zustand_WP", 2); + $this->SetValue("CurrentPower", 0); + } elseif ( + $LastPeak == false && + $power == $this->ReadPropertyInteger("WP_Leistung") + ) { + $this->SetValue("Zustand_WP", 3); + $this->SetValue( + "CurrentPower", + $this->ReadPropertyInteger("WP_Leistung") + ); + } elseif ($LastPeak == false && $power == 0) { + $this->SetValue("Zustand_WP", 1); + $this->SetValue("CurrentPower", 0); + } + } elseif ($state == 3) { + // Erhöht + if ( + $LastPeak && + $power == $this->ReadPropertyInteger("WP_Leistung") + ) { + $this->SetValue("Zustand_WP", 1); + $this->SetValue( + "CurrentPower", + $this->ReadPropertyInteger("WP_Leistung") + ); + } elseif ($LastPeak && $power == 0) { + $this->SetValue("Zustand_WP", 2); + $this->SetValue("CurrentPower", 0); + } elseif ( + $LastPeak == false && + $power == $this->ReadPropertyInteger("WP_Leistung") + ) { + $this->SetValue("Zustand_WP", 3); + $this->SetValue( + "CurrentPower", + $this->ReadPropertyInteger("WP_Leistung") + ); + } elseif ($LastPeak == false && $power == 0) { + $this->SetValue("Zustand_WP", 1); + $this->SetValue("CurrentPower", 0); + } + } else { + $this->SetValue("Zustand_WP", 1); + } + + $newState = $this->GetValue("Zustand_WP"); + + if ($newState == 0) { + SetValue($this->ReadPropertyInteger("Sperrkontakt"), false); + SetValue($this->ReadPropertyInteger("Kontakt_Erhoeung"), false); + } elseif ($newState == 1) { + SetValue($this->ReadPropertyInteger("Sperrkontakt"), false); + SetValue($this->ReadPropertyInteger("Kontakt_Erhoeung"), false); + } elseif ($newState == 2) { + SetValue($this->ReadPropertyInteger("Sperrkontakt"), true); + SetValue($this->ReadPropertyInteger("Kontakt_Erhoeung"), false); + } elseif ($newState == 3) { + SetValue($this->ReadPropertyInteger("Sperrkontakt"), false); + SetValue($this->ReadPropertyInteger("Kontakt_Erhoeung"), true); + } else { + SetValue($this->ReadPropertyInteger("Sperrkontakt"), false); + SetValue($this->ReadPropertyInteger("Kontakt_Erhoeung"), false); + } + } else { + $newCount = $this->GetValue("WP_Laufzeit_Zahler"); + $this->SetValue("WP_Laufzeit_Zahler", $newCount + 1); + } + } + + // Methode zum Abrufen der aktuellen Daten + public function GetCurrentData(bool $Peak) + { + // tagessperrzeit berechnen + + $this->SetValue("LetzterPeakwert", $Peak); + + if($this->GetValue("WP_Laufzeit_Zahler")<(20*12)){ + + $this->SetValue("PowerSteps", json_encode([$this->GetValue("CurrentPower")])); + + }elseif($this->ReadPropertyBoolean("Schwellwert_Anwenden")==true && ($this->ReadPropertyInteger("Schwellwert")>GetValue($this->ReadPropertyInteger("WW_Temp"))) ){ + + $this->SetValue("PowerSteps", json_encode([$this->ReadPropertyInteger("WP_Leistung")])); + } + else{ + $this->SetValue("PowerSteps", json_encode([0, $this->ReadPropertyInteger("WP_Leistung")])); + } + } + + public function CheckIdle($power) + { + $lastpower = GetValue($this->GetIDForIdent("CurrentPower")); + 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); + } + } +} +?> diff --git a/library.json b/library.json new file mode 100644 index 0000000..3ca86f3 --- /dev/null +++ b/library.json @@ -0,0 +1,12 @@ +{ + "id": "{89CAF37C-8E6E-5312-8195-6DA8AB7E5E70}", + "name": "Manager", + "author": "Daniel Haefliger", + "url": "https://git.belevo.ch/dh/Energiemanager_Symconmodule_Demo", + "compatibility": { + "version": "7.1" + }, + "version": "0.156", + "build": 0, + "date": 0 +} \ No newline at end of file