From 72ce4ec0944f5bfddb5273dfd04edb2d5b04cc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fliger?= Date: Mon, 7 Apr 2025 15:18:24 +0200 Subject: [PATCH] =?UTF-8?q?version=202=20f=C3=BCr=20manager=20und=20ladest?= =?UTF-8?q?ation=20erstellt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Batterie/module.php | 116 --------- Ladestation_v2/README.md | 67 ++++++ Ladestation_v2/form.json | 91 ++++++++ Ladestation_v2/module.json | 12 + Ladestation_v2/module.php | 468 +++++++++++++++++++++++++++++++++++++ Manager_v2/README.md | 67 ++++++ Manager_v2/form.json | 72 ++++++ Manager_v2/module.json | 12 + Manager_v2/module.php | 342 +++++++++++++++++++++++++++ 9 files changed, 1131 insertions(+), 116 deletions(-) create mode 100644 Ladestation_v2/README.md create mode 100644 Ladestation_v2/form.json create mode 100644 Ladestation_v2/module.json create mode 100644 Ladestation_v2/module.php create mode 100644 Manager_v2/README.md create mode 100644 Manager_v2/form.json create mode 100644 Manager_v2/module.json create mode 100644 Manager_v2/module.php diff --git a/Batterie/module.php b/Batterie/module.php index 6713823..418aaaf 100644 --- a/Batterie/module.php +++ b/Batterie/module.php @@ -182,7 +182,6 @@ public function RequestAction($Ident, $Value) // IdleCounter verarbeiten $this->ProcessIdleCounter(); - $this->SetValue("Leistung_Delta", GetValue($this->ReadPropertyInteger("Batterieleistung_Effektiv"))-$power); } @@ -286,121 +285,6 @@ public function RequestAction($Ident, $Value) } - - - - // $inhystere = $this->GetValue("Hysterese"); - // $inhystere_netz = $this->GetValue("Hysterese_Netz"); - - /*$batterieManagement = $this->ReadPropertyInteger("Batteriemanagement"); - if ($batterieManagement == 1) { - $dummy_array[] = 0; - return $this->SetValue("PowerSteps", json_encode($dummy_array)); - } - - $batterieladezustandID = $this->ReadPropertyInteger("Batterieladezustand"); - if ($batterieladezustandID > 0) { - $batterieladezustand = GetValue($batterieladezustandID); - } else { - IPS_LogMessage("Batterie", "Fehler: Ungültige Batterieladezustand-ID."); - return; - } - if ($Peak) { - if ($batterieladezustand >= 5 + $aufdasnachladen) { - $this->SetValue("Hysterese", false); - $this->SetValue("Hysterese_Netz", true); - } - - if ($batterieladezustand < $aufdasnachladen) { - $this->SetValue("Hysterese_Netz", false); - } - - if ($batterieladezustand <= $minimumentladen) { - $filtered_powersteps = array_filter($array_powersteps, function ($value) { - return $value >= 0; - }); - $filtered_powersteps_laden = array_values($filtered_powersteps); - $this->SetValue("PowerSteps", json_encode($filtered_powersteps_laden)); - $this->SetValue("Hysterese", true); - IPS_LogMessage("Batterie", "Peak: unter minimum"); - - } elseif ($batterieladezustand > $minimumentladen) { - - if ($inhystere == true && $inhystere_netz == false) { - $filtered_powersteps = array_filter($array_powersteps, function ($value) { - return $value >= 0; - }); - $filtered_powersteps_laden = array_values($filtered_powersteps); - $this->SetValue("PowerSteps", json_encode($filtered_powersteps_laden)); - IPS_LogMessage("Batterie", ": peak: Hysterese"); - IPS_LogMessage("Debug", "peak: Hysterese Hysterese_Netz: " . ($inhystere_netz ? "true" : "false")); - IPS_LogMessage("Debug", "peak: Hysterese Hysterese: " . ($inhystere ? "true" : "false")); - - } elseif ($inhystere_netz == true && $inhystere == false) { - $netzbezug = GetValue($this->ReadPropertyInteger("Netzbezug")); - if (abs($netzbezug) > $maxleistung) { - $dummy_array[] = $maxleistung * (-1); - } else { - $dummy_array[] = $netzbezug; - } - $this->SetValue("PowerSteps", json_encode($dummy_array)); - IPS_LogMessage("Batterie", "Peak: netzbezug, keine hysterese"); - IPS_LogMessage("Debug", "Peak: netzbezug, keine hysterese Hysterese_Netz: " . ($inhystere_netz ? "true" : "false")); - IPS_LogMessage("Debug", "Peak: netzbezug, keine hysterese Hysterese: " . ($inhystere ? "true" : "false")); - - } elseif ($inhystere_netz == false && $inhystere == false) { - $this->SetValue("PowerSteps", json_encode($array_powersteps)); - IPS_LogMessage("Batterie", "Peak: alle Werte, keine hysterese"); - - } else { - $dummy_array[] = 0; - $this->SetValue("PowerSteps", json_encode($dummy_array)); - IPS_LogMessage("Batterie", ": peak: Kein Fall"); - } - } - - - } else { // Solar - - if ($batterieladezustand >= 5 + $aufdasnachladen) { - $this->SetValue("Hysterese", false); - $this->SetValue("Hysterese_Netz", true); - } - - if ($batterieladezustand < $aufdasnachladen) { - $this->SetValue("Hysterese", true); - $this->SetValue("Hysterese_Netz", false); - $nachladen = $this->ReadPropertyInteger("MaxNachladen"); - $dummy_array[] = $nachladen; - IPS_LogMessage("Batterie", ": MaxNachladen " . json_encode($dummy_array)); - $this->SetValue("PowerSteps", json_encode($dummy_array)); - - } elseif ($batterieladezustand >= $aufdasnachladen) { - if ($inhystere == true) { - $filtered_powersteps = array_filter($array_powersteps, function ($value) { - return $value >= 0; - }); - $filtered_powersteps_laden = array_values($filtered_powersteps); - $this->SetValue("PowerSteps", json_encode($filtered_powersteps_laden)); - IPS_LogMessage("Batterie", ": solar: Hysterese"); - } else { - $this->SetValue("PowerSteps", json_encode($array_powersteps)); - IPS_LogMessage("Batterie", ": solar: Keine Hysterese"); - } - - } else { - $dummy_array[] = 0; - $this->SetValue("PowerSteps", json_encode($dummy_array)); - } - - if ($batterieladezustand >= 100) { - $filtered_powersteps_entladen = array_filter($array_powersteps, function ($value) { - return $value <= 0; - }); - $this->SetValue("PowerSteps", json_encode($filtered_powersteps_entladen)); - IPS_LogMessage("Batterie", ": Solar über 100%" ); - } - }*/ } diff --git a/Ladestation_v2/README.md b/Ladestation_v2/README.md new file mode 100644 index 0000000..b059e3a --- /dev/null +++ b/Ladestation_v2/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_v2/form.json b/Ladestation_v2/form.json new file mode 100644 index 0000000..73c4ac3 --- /dev/null +++ b/Ladestation_v2/form.json @@ -0,0 +1,91 @@ +{ + "elements": [ + { + "type": "Select", + "name": "Ladestation", + "caption": "Ladestation", + "options": [ + { + "caption": "Go-E Wallbox (Alte Version)", + "value": 1 + }, + { + "caption": "Go-E Germini / Germini Flex", + "value": 2 + }, + { + "caption": "Smart-Me Pico", + "value": 3 + }, + { + "caption": "Dummy Station (Ohne Ansteuerung)", + "value": 4 + }, + { + "caption": "Easee", + "value": 5 + } + ] + }, + { + "type": "NumberSpinner", + "name": "MinLeistung", + "caption": "Mindestleistung Ladestation" + }, + { + "type": "NumberSpinner", + "name": "MaxLeistung", + "caption": "Maximalleistung Ladestation" + }, + { + "type": "NumberSpinner", + "name": "MinLeistung_1ph", + "caption": "Mindestleistung Ladestation 1-Phasig" + }, + { + "type": "NumberSpinner", + "name": "MaxLeistung_1ph", + "caption": "Maximalleistung Ladestation 1-Phasig" + }, + { + "type": "NumberSpinner", + "name": "IdleCounterMax", + "caption": "Zyklen zwischen zwei Leistungsänderungen (Multipliziert sich mit Interval)", + "suffix": "" + }, + { + "type": "NumberSpinner", + "name": "Interval", + "caption": "Intervall Neuberechnung der Werte", + "suffix": "Sekunden" + }, + { + "type": "ValidationTextBox", + "name": "IP_Adresse", + "caption": "IP-Adresse Ladestation" + }, + { + "type": "ValidationTextBox", + "name": "ID", + "caption": "ID" + + }, + { + "type": "ValidationTextBox", + "name": "Seriennummer", + "caption": "Seriennummer" + + }, + { + "type": "ValidationTextBox", + "name": "Username", + "caption": "Username" + + }, + { "type": "PasswordTextBox", + "name": "Password", + "caption": "Passwort" + } + + ] +} \ No newline at end of file diff --git a/Ladestation_v2/module.json b/Ladestation_v2/module.json new file mode 100644 index 0000000..100a622 --- /dev/null +++ b/Ladestation_v2/module.json @@ -0,0 +1,12 @@ +{ + "id": "{51E121F0-E2E9-D0C2-C8BD-92601B143EB8}", + "name": "Ladestation_v2", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/Ladestation_v2/module.php b/Ladestation_v2/module.php new file mode 100644 index 0000000..bdcf6e9 --- /dev/null +++ b/Ladestation_v2/module.php @@ -0,0 +1,468 @@ +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->RegisterPropertyInteger("Ladestation", 2); + $this->RegisterPropertyString("ID", ""); + $this->RegisterPropertyString("Seriennummer", ""); + $this->RegisterPropertyString("Username", ""); + $this->RegisterPropertyString("Password", ""); + $this->RegisterPropertyInteger("Interval", 5); // Recheninterval + + + // Ladestationspezifische Variabeln + $this->RegisterVariableFloat("Max_Current", 0); + $this->RegisterVariableBoolean("Is_1_ph", "Is_1_ph", "", false); + $this->RegisterVariableBoolean("Car_Detected", false); + $this->RegisterPropertyInteger("Pending_Counter", "Pending_Counter", "", 0); + $this->RegisterVariableBoolean("Car_is_full", "Car_is_full", "", false); + + $this->RegisterVariableBoolean("Ladebereit", "Ladebereit", "~Switch", 1); + $this->RegisterVariableBoolean("Solarladen", "Solarladen", "~Switch", 0); + $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"); + + // 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->RegisterVariableInteger("Power_Count", "Power_Count", "", 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_EVC",$this->ReadPropertyInteger("Interval")*1000,"IPS_RequestAction(" .$this->InstanceID .', "Do_UserCalc", "");'); + + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + $this->SetTimerInterval("Timer_Do_UserCalc_EVC",$this->ReadPropertyInteger("Interval")*1000); + + // Zusätzliche Anpassungen nach Bedarf + } + + 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 "Do_UserCalc": + + + if($this->Detect_Car($this->ReadPropertyInteger("Ladestation"))){ + $this->SetAktuelle_Leistung($this->GetValue("Power")); + $this->GetCurrentData($this->GetValue("Is_Peak_Shaving")); + } + else{ + SetValue($this->GetIDForIdent("PowerSteps"), json_encode([0])); + $this->SetValue("Power", 0); + + } + break; + + default: + throw new Exception("Invalid Ident"); + } + } + + + public function Detect_Car(int $carType) + { + + if($this->GetValue("Car_Detected")){ + if($this->Get_Car_Status($carType)==true){ + $this->SetValue("Car_Detected", true); + }else{ + $this->SetValue("Car_Detected", false); + } + } + else{ + if($this->Get_Car_Status($carType)==true){ + + $this->sendPowerToStation(32); + + $counter = $this->GetValue("Pending_Counter"); + + if($counter>(20/($this->ReadPropertyInteger("Interval")*1000))){ + + $this->SetValue("Pending_Counter", 0); + if($this->GetValue("Ladeleistung_Effektiv")>7500){ + $this->SetValue("Is_1_ph", false); + + } + else{ + $this->SetValue("Is_1_ph", true); + } + + $this->Calc_Max_Current($this->GetValue("Is_1_ph")); + $this->SetValue("Car_Detected", true); + + + } + else{ + $this->SetValue("Pending_Counter", $counter+1); + $this->SetValue("Car_Detected", false); + + } + + + }else{ + $this->SetValue("Car_Detected", false); + } + } + return $this->GetValue("Car_Detected"); + + } + + public function Get_Car_Status(int $carType) + { + $plug_state = false; + + switch ($carType) { + case 1: + curl_setopt($ch, CURLOPT_URL, "http://" . $this->ReadPropertyString("IP_Adresse") . "/mqtt?payload="); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($ch); + curl_close($ch); + $data = json_decode($response, true); + + if (json_last_error() === JSON_ERROR_NONE && isset($data["car"])) { + if ($data["car"] != 1) { + $plug_state = true; + } + $this->SetValue("Ladeleistung_Effektiv", $data["nrg"][11]*10); + $this->SetValue("Fahrzeugstatus", $data["car"]); + } + break; + + case 2: + curl_setopt($ch, CURLOPT_URL, "http://" . $this->ReadPropertyString("IP_Adresse") . "/api/status"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($ch); + curl_close($ch); + $data = json_decode($response, true); + + if (json_last_error() === JSON_ERROR_NONE && isset($data["car"])) { + if ($data["car"] != 1) { + $plug_state = true; + } + $this->SetValue("Ladeleistung_Effektiv", $data["nrg"][11]); + $this->SetValue("Fahrzeugstatus", $data["car"]); + } + break; + + case 3: + curl_setopt($ch, CURLOPT_URL, "https://api.smart-me.com/pico/charging/" . $this->ReadPropertyString("ID")); + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_setopt($ch, CURLOPT_USERPWD, $this->ReadPropertyString("Username") . ":" . $this->ReadPropertyString("Password")); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($ch); + curl_close($ch); + $data = json_decode($response, true); + + if (json_last_error() === JSON_ERROR_NONE && isset($data["State"])) { + if ($data["State"] != 1) { + $plug_state = true; + } + $this->SetValue("Ladeleistung_Effektiv", round($data["ActiveChargingPower"]*1000)); + $this->SetValue("Fahrzeugstatus", $data["State"]); + + } + break; + + case 4: + $plug_state = true; + break; + + case 5: + echo "Fall 5"; + break; + + default: + $plug_state = false; + $this->SetValue("Fahrzeugstatus", -1); + break; + } + + $this->SetValue("Car_detected", $plug_state); + + return $plug_state; + } + + public function Calc_Max_Current(bool $is_1_ph) + { + $maxCurrent = 32; + if($is_1_ph){ + $maxCurrent = 1.1 * ($this->GetValue("Ladeleistung_Effektiv") / 230); + } + else{ + $maxCurrent = 1.1 * ($this->GetValue("Ladeleistung_Effektiv") / (1.71*400)); + } + if($maxCurrent>32){ + $maxCurrent = 32; + } + $this->SetValue("Max_Current", $maxCurrent); + } + + public function Get_Current_From_Power(bool $is_1_ph, int $power) + { + if ($is_1_ph) { + $current = round($power / 230); + } else { + $current = round($power / (400 * 1.71)); + } + + return $current; + } + + public function Get_Array_From_Current(bool $is_1_ph, float $current) + { + $resultArray = []; + + for ($i = 6; $i <= $current; $i++) { + if ($is_1_ph) { + $resultArray[] = $i * 230; + } else { + $resultArray[] = $i * 400 * 1.71; + } + } + + return $resultArray; + } + + + public function SetAktuelle_Leistung(int $power) + { + + $internalPower = GetValue($this->GetIDForIdent("Aktuelle_Leistung")); + // Aktuelle Leistungsvorgabe setzen + SetValue($this->GetIDForIdent("Aktuelle_Leistung"), $power); + $this->SetValue("Bezogene_Energie", ($this->GetValue("Bezogene_Energie") + ($this->GetValue("Aktuelle_Leistung")*($this->ReadPropertyInteger("Interval")/3600)))); + + 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); + + } elseif (!$peak && !$solarladen) { + // Wenn weder Peak noch Solarladen aktiv sind, setze Ladeleistung auf MaxLeistung + $this->sendPowerToStation(Get_Current_From_Power($this->GetValue("Is_1_ph"), $this->GetValue("Max_Current"))); + + } else { + // Ansonsten setze Ladeleistung auf die aktuelle Leistungsvorgabe (Aktuelle_Leistung) + $this->sendPowerToStation(Get_Current_From_Power($this->GetValue("Is_1_ph"), $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")); + + + // Peak-Wert speichern + $this->SetValue("Peak", $Peak); + + // Array für die Powersteps initialisieren + $powerSteps = []; + + // Konfiguration des powerSteps-Arrays basierend auf den Properties + if (!$ladebereit) { + $powerSteps = [0]; + } elseif (!$Peak && !$solarladen) { + $powerSteps = [$max($this->Get_Array_From_Current("Max_Current"))]; + } elseif (!$Peak && $solarladen) { + $powerSteps = $this->Get_Array_From_Current("Max_Current"); + } elseif ($solarladen && $Peak) { + $powerSteps = [0]; + } else { + $powerSteps = [0]; + } + + // PowerSteps in der RegisterVariable speichern + SetValue($this->GetIDForIdent("PowerSteps"), json_encode($powerSteps)); + // Rückgabe der Powersteps + $counter = $this->GetValue("Pending_Counter"); + + if(max($powerSteps)==$this->GetValue("Aktuelle_Leistung")){ + if(max($powerSteps)>(1.11*$this->GetValue("Ladeleistung_Effektiv"))){ + $this->SetValue("Pending_Counter", $counter+1); + + } + else{ + + $this->SetValue("Pending_Counter", 0); + + } + + if($counter>(20/($this->ReadPropertyInteger("Interval")*1000))){ + + $this->SetValue("Pending_Counter", 0); + $this->Calc_Max_Current($this->GetValue("Is_1_ph")); + } + } + + + return $powerSteps; + } + + public function sendPowerToStation(float $value) + { + $baseUrl = ""; + $stationType = $this->ReadPropertyInteger("Ladestation"); + + switch ($stationType) { + case 1: + $baseUrl = "http://" . $this->ReadPropertyString("IP_Adresse") . "/mqtt?payload="; + break; + case 2: + $baseUrl = "http://" . $this->ReadPropertyString("IP_Adresse") . "/api/set?"; + break; + case 3: + $baseUrl = "https://api.smart-me.com/pico/loadmanagementgroup/current/" . $this->ReadPropertyString("Seriennummer") . "?current="; + break; + case 4: + // Nichts zu tun für Dummy station + return; + } + + $value = $this->convertPowerToCurrent($value, GetValue($this->GetIDForIdent("Lademodus"))); + $ch = curl_init(); + IPS_LogMessage("value", "nach konvertierung" . $value); + + if ($value == 0) { + switch ($stationType) { + case 1: + $url = $baseUrl . "alw=0"; + break; + case 2: + $url = $baseUrl . "frc=1"; + break; + case 3: + $url = $baseUrl . "0"; + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_setopt($ch, CURLOPT_USERPWD, $this->ReadPropertyString("Username") . ":" . $this->ReadPropertyString("Password")); + break; + case 4: + // Nichts zu tun für Dummy station + return; + default: + return "Invalid station type."; + } + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $response = curl_exec($ch); + curl_close($ch); + return $response; + } elseif ($value >= 1 && $value <= 32) { + switch ($stationType) { + case 1: + $url1 = $baseUrl . "alw=1"; + $url2 = $baseUrl . "amp=$value"; + break; + case 2: + $url1 = $baseUrl . "frc=2"; + $url2 = $baseUrl . "amp=$value"; + break; + case 3: + $url2 = $baseUrl . ($value * 1000); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_setopt($ch, CURLOPT_USERPWD, $this->ReadPropertyString("Username") . ":" . $this->ReadPropertyString("Password")); + break; + case 4: + // Nichts zu tun für Dummy station + return; + default: + return "Invalid station type."; + } + + if (isset($url1)) { + curl_setopt($ch, CURLOPT_URL, $url1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $response1 = curl_exec($ch); + if (curl_errno($ch)) { + curl_close($ch); + return "Error:" . curl_error($ch); + } + } + + curl_setopt($ch, CURLOPT_URL, $url2); + $response2 = curl_exec($ch); + if (curl_errno($ch)) { + curl_close($ch); + return "Error:" . curl_error($ch); + } + curl_close($ch); + return; + } else { + return "Invalid value. Must be between 0 and 32."; + } + } + + +} +?> diff --git a/Manager_v2/README.md b/Manager_v2/README.md new file mode 100644 index 0000000..b059e3a --- /dev/null +++ b/Manager_v2/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_v2/form.json b/Manager_v2/form.json new file mode 100644 index 0000000..84dadcb --- /dev/null +++ b/Manager_v2/form.json @@ -0,0 +1,72 @@ +{ + "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.json b/Manager_v2/module.json new file mode 100644 index 0000000..5d10fe9 --- /dev/null +++ b/Manager_v2/module.json @@ -0,0 +1,12 @@ +{ + "id": "{0998B49F-D79A-E588-94F2-C3EFC9652BB1}", + "name": "Manager_v2", + "type": 3, + "vendor": "Belevo AG", + "aliases": [], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "GEF", + "url": "" +} \ No newline at end of file diff --git a/Manager_v2/module.php b/Manager_v2/module.php new file mode 100644 index 0000000..337e6a4 --- /dev/null +++ b/Manager_v2/module.php @@ -0,0 +1,342 @@ +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; + } + + // 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"])) + ]; + + // Ü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 += ($Aktuelle_Leistung + 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"]); + } + + } + +} +?>