From 5aa44ad8a2c291faaba76cbaff6d2ee08ec27bab Mon Sep 17 00:00:00 2001 From: DanielHaefliger Date: Tue, 10 Feb 2026 11:56:21 +0100 Subject: [PATCH] no message --- VGT_Sub/form.json | 63 ++++++++- VGT_Sub/module.json | 8 +- VGT_Sub/module.php | 310 ++++++++++++++++++++++++++++++-------------- 3 files changed, 275 insertions(+), 106 deletions(-) diff --git a/VGT_Sub/form.json b/VGT_Sub/form.json index 9f5911e..16d2c46 100644 --- a/VGT_Sub/form.json +++ b/VGT_Sub/form.json @@ -1,9 +1,64 @@ { "elements": [ { - "type": "ValidationTextBox", - "name": "DeviceID", - "caption": "Device ID" + "type": "Label", + "caption": "Hardware Quellen (Eingänge)" + }, + { + "type": "SelectVariable", + "name": "SourceSoC", + "caption": "State of Charge (SoC)", + "validVariableTypes": [1, 2] + }, + { + "type": "SelectVariable", + "name": "SourcePowerProd", + "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", + "caption": "Is Ready (Bool)", + "validVariableTypes": [0] + }, + { + "type": "SelectVariable", + "name": "SourceIsRunning", + "caption": "Is Running (Bool)", + "validVariableTypes": [0] + }, + { + "type": "Label", + "caption": "Steuerung (Ausgang)" + }, + { + "type": "SelectVariable", + "name": "TargetControlVar", + "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 14d84f7..69aade5 100644 --- a/VGT_Sub/module.json +++ b/VGT_Sub/module.json @@ -3,10 +3,12 @@ "name": "VGT_Sub", "type": 3, "vendor": "Belevo", - "aliases": ["VGT MQTT Device"], - "prefix": "VGT", + "aliases": [ + "VGT MQTT Device" + ], "parentRequirements": [], "childRequirements": [], "implemented": [], + "prefix": "VGT", "version": "1.0" -} +} \ No newline at end of file diff --git a/VGT_Sub/module.php b/VGT_Sub/module.php index 9d1d03c..e443921 100644 --- a/VGT_Sub/module.php +++ b/VGT_Sub/module.php @@ -6,128 +6,240 @@ class VGT_Sub extends IPSModule { public function Create() { + // Diese Zeile nicht löschen. parent::Create(); - // DeviceID - $this->RegisterPropertyString('DeviceID', ''); + // --------------------------------------------------------------------- + // 1. Eigenschaften registrieren (Links zu externer Hardware) + // --------------------------------------------------------------------- + + // 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 + + // Das Ziel (zum Schreiben/Steuern der Hardware) + $this->RegisterPropertyInteger('TargetControlVar', 0); // Haupt-RequestAction Ziel - // Eingehende MQTT-Nachrichten - $this->RegisterVariableString("InputJSON", "MQTT Input", "", 1); - $this->EnableAction("InputJSON"); - - // Ausgehende MQTT-Nachrichten - $this->RegisterVariableString("OutputJSON", "MQTT Output", "", 2); - - // Statusvariablen - $this->RegisterVariableFloat('PowerProduction', 'Power Production', '', 10); - $this->EnableAction('PowerProduction'); - - $this->RegisterVariableFloat('StateOfCharge', 'State of Charge', '', 11); - $this->EnableAction('StateOfCharge'); - - $this->RegisterVariableBoolean('IsRunning', 'Is Running', '', 12); - $this->EnableAction('IsRunning'); - - $this->RegisterVariableBoolean('IsReady', 'Is Ready', '', 13); - $this->EnableAction('IsReady'); - - $this->RegisterVariableFloat('MinSoc', 'Min SoC', '', 14); - $this->EnableAction('MinSoc'); - - $this->RegisterVariableFloat('MaxSoc', 'Max SoC', '', 15); - $this->EnableAction('MaxSoc'); - - // Remote-Control + // --------------------------------------------------------------------- + // 2. Interne Variablen erstellen + // --------------------------------------------------------------------- + + // Die Steuerungsvariablen + $this->RegisterVariableString('Strategy', 'Strategy', '', 10); $this->RegisterVariableInteger('PowerSetpoint', 'Power Setpoint', '', 20); - IPS_SetVariableCustomAction($this->GetIDForIdent('PowerSetpoint'), 0); + + // Damit man sie im WebFront/Konsole ändern kann + $this->EnableAction('Strategy'); + $this->EnableAction('PowerSetpoint'); - $this->RegisterVariableString('Strategy', 'Strategy', '', 21); - IPS_SetVariableCustomAction($this->GetIDForIdent('Strategy'), 0); + // 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) + // --------------------------------------------------------------------- + + // Wir wollen sofort reagieren, wenn sich der externe SoC ändert + $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) + { + if ($Message == VM_UPDATE) { + $socID = $this->ReadPropertyInteger('SourceSoC'); + $stratID = $this->GetIDForIdent('Strategy'); + $setID = $this->GetIDForIdent('PowerSetpoint'); + + // Wenn sich eine der steuerungsrelevanten Variablen ändert, Logik ausführen + if ($SenderID == $socID || $SenderID == $stratID || $SenderID == $setID) { + $this->ExecuteControlLogic(); + } + } + } + + // Wird ausgeführt, wenn man Variable schaltet (WebFront, Skript oder MQTT-Event) public function RequestAction($Ident, $Value) { - if ($Ident === "InputJSON") { - SetValueString($this->GetIDForIdent("InputJSON"), $Value); - $this->ProcessIncoming($Value); - return; - } + switch ($Ident) { + case 'Strategy': + case 'PowerSetpoint': + $this->SetValue($Ident, $Value); + // Sofort Logik neu berechnen + $this->ExecuteControlLogic(); + break; - SetValue($this->GetIDForIdent($Ident), $Value); - } + case 'MQTT_Write_In': + // Wert setzen (optional, zur Ansicht) + $this->SetValue($Ident, $Value); + // JSON verarbeiten + $this->ProcessWriteCommand($Value); + break; - private function ProcessIncoming($jsonString) - { - $data = @json_decode($jsonString, true); - if (!is_array($data)) { - return; - } + case 'MQTT_Read_In': + // Wert setzen (optional) + $this->SetValue($Ident, $Value); + // Lesebefehl verarbeiten + $this->ProcessReadCommand(); + break; - if (!isset($data['topic']) || !isset($data['payload'])) { - return; - } - - $topic = $data['topic']; - $payload = $data['payload']; - - $device = $this->ReadPropertyString('DeviceID'); - - // Feedback Request - if ($topic === "feedback-request/$device") { - $this->SendFeedback($device); - return; - } - - // Remote Control Request - if ($topic === "remote-control-request/$device") { - $this->ProcessRemoteControl($device, $payload); - return; + default: + throw new Exception("Invalid Ident"); } } - private function SendFeedback($device) + // ------------------------------------------------------------------------- + // LOGIK: Batterie Steuern (Skript 1) + // ------------------------------------------------------------------------- + private function ExecuteControlLogic() { - $response = [ - "power_production" => GetValue($this->GetIDForIdent('PowerProduction')), - "state_of_charge" => GetValue($this->GetIDForIdent('StateOfCharge')), - "is_running" => GetValue($this->GetIDForIdent('IsRunning')), - "is_ready" => GetValue($this->GetIDForIdent('IsReady')), - "min_soc" => GetValue($this->GetIDForIdent('MinSoc')), - "max_soc" => GetValue($this->GetIDForIdent('MaxSoc')) + $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 + if (isset($data['power_setpoint'])) { + $val = $data['power_setpoint']; + + // Begrenzen auf -6000 bis +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 + $this->ExecuteControlLogic(); + + // 4. Antwort generieren (Werte wieder lesen) + $output = [ + 'power_setpoint' => $this->GetValue('PowerSetpoint'), + 'strategy' => $this->GetValue('Strategy') ]; - SetValueString( - $this->GetIDForIdent("OutputJSON"), - json_encode([ - "topic" => "feedback-response/$device", - "payload" => json_encode($response) - ]) - ); + $jsonOut = json_encode($output, JSON_PRETTY_PRINT); + + // In die Output-Variable schreiben (MQTT liest diese) + $this->SetValue('MQTT_Write_Out', $jsonOut); } - private function ProcessRemoteControl($device, $payload) + // ------------------------------------------------------------------------- + // LOGIK: Lesebefehl (Skript 3) + // ------------------------------------------------------------------------- + private function ProcessReadCommand() { - $json = @json_decode($payload, true); - if (!is_array($json)) { - return; - } + // 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'); - if (isset($json['power_setpoint'])) { - SetValue($this->GetIDForIdent('PowerSetpoint'), (int)$json['power_setpoint']); - } + // Hilfsfunktion: Wert holen oder 0/false wenn ID fehlt + $safeGet = function($id) { + return IPS_VariableExists($id) ? GetValue($id) : 0; + }; - if (isset($json['strategy'])) { - SetValueString($this->GetIDForIdent('Strategy'), (string)$json['strategy']); - } + // 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) + ]; - SetValueString( - $this->GetIDForIdent("OutputJSON"), - json_encode([ - "topic" => "remote-control-response/$device", - "payload" => json_encode($json) - ]) - ); + $json = json_encode($data, JSON_PRETTY_PRINT); + + // In Antwort-Variable schreiben + $this->SetValue('MQTT_Read_Out', $json); } } - -?> \ No newline at end of file +?>