From e21f760e13f2edbc9de84fe87cbd0cb50623ad5f Mon Sep 17 00:00:00 2001 From: DanielHaefliger Date: Tue, 10 Feb 2026 13:21:43 +0100 Subject: [PATCH] no message --- VGT_Sub/form.json | 47 ++++--- VGT_Sub/module.json | 4 +- VGT_Sub/module.php | 302 ++++++++++++++++++++++---------------------- 3 files changed, 175 insertions(+), 178 deletions(-) diff --git a/VGT_Sub/form.json b/VGT_Sub/form.json index 16d2c46..3ecbe31 100644 --- a/VGT_Sub/form.json +++ b/VGT_Sub/form.json @@ -2,7 +2,16 @@ "elements": [ { "type": "Label", - "caption": "Hardware Quellen (Eingänge)" + "caption": "MQTT Konfiguration" + }, + { + "type": "ValidationTextBox", + "name": "MQTTBaseTopic", + "caption": "Base Topic (z.B. Test VGT_Steuerung/Komm)" + }, + { + "type": "Label", + "caption": "Hardware Verknüpfungen (Intern)" }, { "type": "SelectVariable", @@ -16,22 +25,6 @@ "caption": "Power Production", "validVariableTypes": [1, 2] }, - { - "type": "SelectVariable", - "name": "SourceMinSoC", - "caption": "Min SoC", - "validVariableTypes": [1, 2] - }, - { - "type": "SelectVariable", - "name": "SourceMaxSoC", - "caption": "Max SoC", - "validVariableTypes": [1, 2] - }, - { - "type": "Label", - "caption": "Status Flags" - }, { "type": "SelectVariable", "name": "SourceIsReady", @@ -44,9 +37,21 @@ "caption": "Is Running (Bool)", "validVariableTypes": [0] }, + { + "type": "SelectVariable", + "name": "SourceMinSoC", + "caption": "Min SoC", + "validVariableTypes": [1, 2] + }, + { + "type": "SelectVariable", + "name": "SourceMaxSoC", + "caption": "Max SoC", + "validVariableTypes": [1, 2] + }, { "type": "Label", - "caption": "Steuerung (Ausgang)" + "caption": "Steuerung (Output)" }, { "type": "SelectVariable", @@ -54,11 +59,5 @@ "caption": "Wechselrichter Sollwert (Ziel)", "validVariableTypes": [1, 2] } - ], - "actions": [ - { - "type": "Label", - "caption": "Hinweis: Änderungen an Variablen werden sofort übernommen." - } ] } \ No newline at end of file diff --git a/VGT_Sub/module.json b/VGT_Sub/module.json index 69aade5..366fd7c 100644 --- a/VGT_Sub/module.json +++ b/VGT_Sub/module.json @@ -6,9 +6,9 @@ "aliases": [ "VGT MQTT Device" ], - "parentRequirements": [], + "parentRequirements": ["{F66ADE63-8834-4178-8CA5-AE4465D2E252}"], "childRequirements": [], "implemented": [], "prefix": "VGT", - "version": "1.0" + "version": "1.1" } \ No newline at end of file diff --git a/VGT_Sub/module.php b/VGT_Sub/module.php index e443921..f4c3350 100644 --- a/VGT_Sub/module.php +++ b/VGT_Sub/module.php @@ -6,240 +6,238 @@ class VGT_Sub extends IPSModule { public function Create() { - // Diese Zeile nicht löschen. parent::Create(); // --------------------------------------------------------------------- - // 1. Eigenschaften registrieren (Links zu externer Hardware) + // 1. Eigenschaften: Hardware-Verknüpfungen & MQTT Einstellungen // --------------------------------------------------------------------- + $this->RegisterPropertyString('MQTTBaseTopic', 'Test VGT_Steuerung/Komm'); - // Externe Quellen (zum Lesen) - $this->RegisterPropertyInteger('SourceSoC', 0); // State of Charge - $this->RegisterPropertyInteger('SourcePowerProd', 0); // Power Production - $this->RegisterPropertyInteger('SourceIsReady', 0); // Is Ready (Bool) - $this->RegisterPropertyInteger('SourceIsRunning', 0); // Is Running (Bool) - $this->RegisterPropertyInteger('SourceMinSoC', 0); // Min SoC - $this->RegisterPropertyInteger('SourceMaxSoC', 0); // Max SoC + // Externe Hardware-Quellen (Lesen) + $this->RegisterPropertyInteger('SourceSoC', 0); + $this->RegisterPropertyInteger('SourcePowerProd', 0); + $this->RegisterPropertyInteger('SourceIsReady', 0); + $this->RegisterPropertyInteger('SourceIsRunning', 0); + $this->RegisterPropertyInteger('SourceMinSoC', 0); + $this->RegisterPropertyInteger('SourceMaxSoC', 0); - // Das Ziel (zum Schreiben/Steuern der Hardware) - $this->RegisterPropertyInteger('TargetControlVar', 0); // Haupt-RequestAction Ziel + // Hardware-Ziel (Schreiben) + $this->RegisterPropertyInteger('TargetControlVar', 0); // --------------------------------------------------------------------- - // 2. Interne Variablen erstellen + // 2. Variablen im Modul erstellen (zur Anzeige & Steuerung) // --------------------------------------------------------------------- - - // Die Steuerungsvariablen + // Diese Variablen spiegeln den aktuellen Zustand wider $this->RegisterVariableString('Strategy', 'Strategy', '', 10); $this->RegisterVariableInteger('PowerSetpoint', 'Power Setpoint', '', 20); - // Damit man sie im WebFront/Konsole ändern kann + // Visualisierung der gelesenen Hardware-Werte (optional, aber gut zur Kontrolle) + $this->RegisterVariableFloat('Visu_SoC', 'State of Charge', '~Intensity.100', 30); + $this->RegisterVariableFloat('Visu_Production', 'Power Production', '~Watt', 40); + $this->RegisterVariableBoolean('Visu_IsReady', 'Is Ready', '~Switch', 50); + $this->EnableAction('Strategy'); $this->EnableAction('PowerSetpoint'); - - // MQTT Schnittstelle (JSON Container) - // Input: Hier schreibt der MQTT Client rein - $this->RegisterVariableString('MQTT_Write_In', 'MQTT Write Command (Input)', '~TextBox', 30); - $this->RegisterVariableString('MQTT_Read_In', 'MQTT Read Command (Input)', '~TextBox', 50); - - // Output: Hier schreibt das Modul die Antwort rein (MQTT liest das) - $this->RegisterVariableString('MQTT_Write_Out', 'MQTT Write Response (Output)', '~TextBox', 40); - $this->RegisterVariableString('MQTT_Read_Out', 'MQTT Read Response (Output)', '~TextBox', 60); - - // Aktionen für Inputs aktivieren, damit Ereignisse darauf schreiben können - $this->EnableAction('MQTT_Write_In'); - $this->EnableAction('MQTT_Read_In'); } public function ApplyChanges() { - // Diese Zeile nicht löschen parent::ApplyChanges(); // --------------------------------------------------------------------- - // 3. Nachrichten registrieren (Events) + // 3. MQTT Filter setzen (Abonnement) // --------------------------------------------------------------------- + // Wir sagen dem Parent (MQTT Splitter): "Schick mir alles, was auf mein Topic kommt" + $topic = $this->ReadPropertyString('MQTTBaseTopic'); - // Wir wollen sofort reagieren, wenn sich der externe SoC ändert + // Regex Filter für ReceiveData: Alles was mit dem Topic beginnt + // Wir escapen Slash /, damit Regex funktioniert + $cleanTopic = preg_quote($topic, '/'); + // Filter: Topic muss matchen + $this->SetReceiveDataFilter(".*\"Topic\":\"" . $cleanTopic . "/.*\".*"); + + // --------------------------------------------------------------------- + // 4. Events registrieren (Hardware überwachen) + // --------------------------------------------------------------------- + // Wenn sich der externe SoC ändert, müssen wir die Logik ausführen $socID = $this->ReadPropertyInteger('SourceSoC'); if (IPS_VariableExists($socID)) { $this->RegisterMessage($socID, VM_UPDATE); } - - // Auch reagieren, wenn wir selbst Strategy oder Setpoint ändern $this->RegisterMessage($this->GetIDForIdent('Strategy'), VM_UPDATE); $this->RegisterMessage($this->GetIDForIdent('PowerSetpoint'), VM_UPDATE); } - // Empfängt Events (z.B. Änderung des SoC oder der Strategie) - public function MessageSink($TimeStamp, $SenderID, $Message, $Data) + // ------------------------------------------------------------------------- + // MQTT EMPFANG (Ersatz für Events auf MQTT-Variablen) + // ------------------------------------------------------------------------- + public function ReceiveData($JSONString) { - if ($Message == VM_UPDATE) { - $socID = $this->ReadPropertyInteger('SourceSoC'); - $stratID = $this->GetIDForIdent('Strategy'); - $setID = $this->GetIDForIdent('PowerSetpoint'); + $data = json_decode($JSONString); + + // UTF-8 Payload decodieren + $payload = utf8_decode($data->Payload); + $topic = $data->Topic; + + $baseTopic = $this->ReadPropertyString('MQTTBaseTopic'); - // Wenn sich eine der steuerungsrelevanten Variablen ändert, Logik ausführen - if ($SenderID == $socID || $SenderID == $stratID || $SenderID == $setID) { - $this->ExecuteControlLogic(); - } + // Prüfen: Ist es der Writebefehl? + if ($topic === $baseTopic . '/Writebefehl') { + $this->ProcessWriteCommand($payload); + } + // Prüfen: Ist es der Lesebefehl? + elseif ($topic === $baseTopic . '/Lesebefehl') { + $this->ProcessReadCommand(); } } - // Wird ausgeführt, wenn man Variable schaltet (WebFront, Skript oder MQTT-Event) + // ------------------------------------------------------------------------- + // LOGIK: Interne Verarbeitung + // ------------------------------------------------------------------------- + + // Hardware-Überwachung (MessageSink) bleibt gleich + public function MessageSink($TimeStamp, $SenderID, $Message, $Data) + { + if ($Message == VM_UPDATE) { + // Visualisierung aktualisieren (SoC im Modul anzeigen) + $socID = $this->ReadPropertyInteger('SourceSoC'); + if ($SenderID == $socID) { + $this->SetValue('Visu_SoC', $Data[0]); + } + + // Steuerungslogik immer ausführen wenn sich was ändert + $this->ExecuteControlLogic(); + } + } + + // Manuelles Schalten im WebFront public function RequestAction($Ident, $Value) { switch ($Ident) { case 'Strategy': case 'PowerSetpoint': $this->SetValue($Ident, $Value); - // Sofort Logik neu berechnen $this->ExecuteControlLogic(); break; - - case 'MQTT_Write_In': - // Wert setzen (optional, zur Ansicht) - $this->SetValue($Ident, $Value); - // JSON verarbeiten - $this->ProcessWriteCommand($Value); - break; - - case 'MQTT_Read_In': - // Wert setzen (optional) - $this->SetValue($Ident, $Value); - // Lesebefehl verarbeiten - $this->ProcessReadCommand(); - break; - - default: - throw new Exception("Invalid Ident"); } } // ------------------------------------------------------------------------- - // LOGIK: Batterie Steuern (Skript 1) + // CORE LOGIK // ------------------------------------------------------------------------- - private function ExecuteControlLogic() - { - $targetID = $this->ReadPropertyInteger('TargetControlVar'); - - // Sicherheitscheck: Existiert das Ziel? - if (!IPS_VariableExists($targetID)) return; - // Interne Werte laden - $strategy = $this->GetValue('Strategy'); - $setpoint = $this->GetValue('PowerSetpoint'); - - // SoC von extern holen - $socID = $this->ReadPropertyInteger('SourceSoC'); - $soc = IPS_VariableExists($socID) ? GetValue($socID) : 0; - - // Fall 1: STRATEGY = "activate" - if ($strategy == "activate") { - // Im Original: SetValue($idReqAction, ($setpoint)); - RequestAction($targetID, $setpoint); - return; - } - - // Fall 2: STRATEGY = "stop" - if ($strategy == "stop") { - RequestAction($targetID, 0); - return; - } - - // Fall 3: Alles andere - // Bei genau 50 -> auf 0 setzen - if ((int)$soc == 50) { - RequestAction($targetID, 0); - return; - } - - // Mode abhängig vom SoC - if ($soc < 50) { - // Laden (Positiver Betrag 2500) - RequestAction($targetID, abs(2500)); - } else { - // Entladen (Negativer Betrag -2500) - RequestAction($targetID, -2500); - } - } - - // ------------------------------------------------------------------------- - // LOGIK: Writebefehl + Antwort (Skript 2) - // ------------------------------------------------------------------------- private function ProcessWriteCommand($jsonInput) { if ($jsonInput == "") return; - $data = json_decode($jsonInput, true); if ($data === null) return; - // 1. power_setpoint verarbeiten + // 1. Variablen setzen if (isset($data['power_setpoint'])) { $val = $data['power_setpoint']; - - // Begrenzen auf -6000 bis +6000 - if ($val > 6000) { - $val = 6000; - } elseif ($val < -6000) { - $val = -6000; - } - + if ($val > 6000) $val = 6000; + elseif ($val < -6000) $val = -6000; $this->SetValue('PowerSetpoint', $val); } - // 2. strategy verarbeiten if (isset($data['strategy'])) { $this->SetValue('Strategy', $data['strategy']); } - // 3. Nach dem Schreiben Logik sofort ausführen, damit Hardware reagiert + // 2. Hardware sofort ansteuern $this->ExecuteControlLogic(); - // 4. Antwort generieren (Werte wieder lesen) + // 3. Antwort senden (an .../Writebefehl Antwort) $output = [ 'power_setpoint' => $this->GetValue('PowerSetpoint'), 'strategy' => $this->GetValue('Strategy') ]; - - $jsonOut = json_encode($output, JSON_PRETTY_PRINT); - // In die Output-Variable schreiben (MQTT liest diese) - $this->SetValue('MQTT_Write_Out', $jsonOut); + $this->SendMQTT($this->ReadPropertyString('MQTTBaseTopic') . '/Writebefehl Antwort', json_encode($output)); } - // ------------------------------------------------------------------------- - // LOGIK: Lesebefehl (Skript 3) - // ------------------------------------------------------------------------- private function ProcessReadCommand() { - // IDs aus Properties laden - $idPowerProduction = $this->ReadPropertyInteger('SourcePowerProd'); - $idIsReady = $this->ReadPropertyInteger('SourceIsReady'); - $idIsRunning = $this->ReadPropertyInteger('SourceIsRunning'); - $idStateOfCharge = $this->ReadPropertyInteger('SourceSoC'); - $idMinSOC = $this->ReadPropertyInteger('SourceMinSoC'); - $idMaxSOC = $this->ReadPropertyInteger('SourceMaxSoC'); + // Hardware Werte lesen + $data = $this->GatherHardwareData(); - // Hilfsfunktion: Wert holen oder 0/false wenn ID fehlt - $safeGet = function($id) { + // Lokale Visu-Variablen updaten (damit man im Modul sieht was passiert) + $this->SetValue('Visu_Production', $data['raw_prod']); // Hilfswert speichern + $this->SetValue('Visu_IsReady', $data['is_ready']); + + // JSON für MQTT + $mqttData = [ + 'power_production' => -1 * $data['raw_prod'], // Vorzeichen wie im Original + 'is_ready' => $data['is_ready'], + 'is_running' => $data['is_running'], + 'state_of_charge' => $data['soc'], + 'min_soc' => $data['min_soc'], + 'max_soc' => $data['max_soc'] + ]; + + // Senden an .../Lesebefehl Antwort + $this->SendMQTT($this->ReadPropertyString('MQTTBaseTopic') . '/Lesebefehl Antwort', json_encode($mqttData)); + } + + private function ExecuteControlLogic() + { + $targetID = $this->ReadPropertyInteger('TargetControlVar'); + if (!IPS_VariableExists($targetID)) return; + + $strategy = $this->GetValue('Strategy'); + $setpoint = $this->GetValue('PowerSetpoint'); + + // SoC holen (Live) + $socID = $this->ReadPropertyInteger('SourceSoC'); + $soc = IPS_VariableExists($socID) ? GetValue($socID) : 0; + + if ($strategy == "activate") { + RequestAction($targetID, $setpoint); + } elseif ($strategy == "stop") { + RequestAction($targetID, 0); + } else { + // Logic: soc == 50 -> 0 + if ((int)$soc == 50) { + RequestAction($targetID, 0); + } elseif ($soc < 50) { + RequestAction($targetID, 2500); + } else { + RequestAction($targetID, -2500); + } + } + } + + // Hilfsfunktion: Hardware Daten sammeln + private function GatherHardwareData() + { + $safeGet = function($propName) { + $id = $this->ReadPropertyInteger($propName); return IPS_VariableExists($id) ? GetValue($id) : 0; }; - // Daten zusammenstellen - $data = [ - // Achtung: Originalskript rechnete -1 * Wert - 'power_production' => -1 * $safeGet($idPowerProduction), - 'is_ready' => (bool)$safeGet($idIsReady), - 'is_running' => (bool)$safeGet($idIsRunning), - 'state_of_charge' => $safeGet($idStateOfCharge), - 'min_soc' => $safeGet($idMinSOC), - 'max_soc' => $safeGet($idMaxSOC) + return [ + 'raw_prod' => $safeGet('SourcePowerProd'), + 'is_ready' => (bool)$safeGet('SourceIsReady'), + 'is_running' => (bool)$safeGet('SourceIsRunning'), + 'soc' => $safeGet('SourceSoC'), + 'min_soc' => $safeGet('SourceMinSoC'), + 'max_soc' => $safeGet('SourceMaxSoC'), ]; + } - $json = json_encode($data, JSON_PRETTY_PRINT); + // Hilfsfunktion: MQTT Senden + protected function SendMQTT($Topic, $Payload) + { + $Data['DataID'] = '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}'; // MQTT Client/Splitter GUID + $Data['PacketType'] = 3; // Publish + $Data['QualityOfService'] = 0; + $Data['Retain'] = false; + $Data['Topic'] = $Topic; + $Data['Payload'] = $Payload; - // In Antwort-Variable schreiben - $this->SetValue('MQTT_Read_Out', $json); + $JSON = json_encode($Data); + // An Parent senden + $this->SendDataToParent($JSON); } } -?> +?> \ No newline at end of file