From 34a52c2d41f1f813a7e072c12787aed7a2f0473b Mon Sep 17 00:00:00 2001 From: "belevo\\mh" Date: Fri, 26 Jun 2026 12:52:55 +0200 Subject: [PATCH] no message --- MQTTPVSDL/README.md | 67 ++++++ MQTTPVSDL/form.json | 33 +++ MQTTPVSDL/module.json | 18 ++ MQTTPVSDL/module.php | 361 ++++++++++++++++++++++++++++++++ Verbraucher_1_Stufig/module.php | 11 +- 5 files changed, 481 insertions(+), 9 deletions(-) create mode 100644 MQTTPVSDL/README.md create mode 100644 MQTTPVSDL/form.json create mode 100644 MQTTPVSDL/module.json create mode 100644 MQTTPVSDL/module.php diff --git a/MQTTPVSDL/README.md b/MQTTPVSDL/README.md new file mode 100644 index 0000000..e3abe65 --- /dev/null +++ b/MQTTPVSDL/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/MQTTPVSDL/form.json b/MQTTPVSDL/form.json new file mode 100644 index 0000000..81bb211 --- /dev/null +++ b/MQTTPVSDL/form.json @@ -0,0 +1,33 @@ +{ + "elements": [ + { + "type": "ValidationTextBox", + "name": "TopicSuffix", + "caption": "Topic Suffix" + }, + { + "type": "SelectVariable", + "name": "ReqActionID", + "caption": "Dorssellelleistung" + }, + { + "type": "SelectVariable", + "name": "PowerProductionID", + "caption": "Aktuelle Leistung SDL-PV" + }, + { + "type": "NumberSpinner", + "name": "MaxPowerSetpoint", + "caption": "Max. Power Setpoint", + "minimum": 0, + "maximum": 100000 + } + ], + "actions": [ + { + "type": "Button", + "caption": "Batterie-Steuerung testen", + "onClick": "MBSDL_RunBatteryControl($id);" + } + ] +} \ No newline at end of file diff --git a/MQTTPVSDL/module.json b/MQTTPVSDL/module.json new file mode 100644 index 0000000..7334310 --- /dev/null +++ b/MQTTPVSDL/module.json @@ -0,0 +1,18 @@ +{ + "id": "{A04DDBFE-1943-8358-4C73-6EA3B96CCDAF}", + "name": "MQTTPVSDL", + "type": 3, + "vendor": "Belevo AG", + "aliases": [ + "MQTT PV SDL" + ], + "parentRequirements": [ + "{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}" + ], + "childRequirements": [], + "implemented": [ + "{7F7632D9-FA40-4F38-8DEA-C83CD4325A32}" + ], + "prefix": "MBSDL", + "url": "" +} \ No newline at end of file diff --git a/MQTTPVSDL/module.php b/MQTTPVSDL/module.php new file mode 100644 index 0000000..9b038ba --- /dev/null +++ b/MQTTPVSDL/module.php @@ -0,0 +1,361 @@ +RegisterPropertyString('TopicSuffix', ''); + + $this->RegisterPropertyInteger('ReqActionID', 0); + + $this->RegisterPropertyInteger('PowerProductionID', 0); + + $this->RegisterPropertyInteger('MaxPowerSetpoint', 10000); + + // Variablen + $this->RegisterVariableBoolean('IsReady', 'Is Ready', '', 10); + $this->RegisterVariableBoolean('IsRunning', 'Is Running', '', 20); + + + $this->RegisterVariableInteger('PowerSetpoint', 'Power Setpoint', '', 50); + $this->EnableAction('PowerSetpoint'); + + $this->RegisterVariableString('Strategy', 'Strategy', '', 60); + $this->EnableAction('Strategy'); + + $this->RegisterVariableString('LastReadResponse', 'Letzte Lese-Antwort', '', 70); + $this->RegisterVariableString('LastWriteResponse', 'Letzte Steuer-Antwort', '', 80); + + // MQTT Parent verbinden + $this->ConnectParent('{F7A0DD2E-7684-95C0-64C2-D2A9DC47577B}'); + + $this->RegisterTimer('PublishDelay', 0, 'MBSDL_DoDelayedPublish($_IPS["TARGET"]);'); + $this->SetBuffer('PublishTopic', ''); + $this->SetBuffer('PublishPayload', ''); + + $this->RegisterTimer('BatteryControlTimer', 3000, 'MBSDL_RunBatteryControl($_IPS["TARGET"]);'); + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + + $suffix = $this->ReadPropertyString('TopicSuffix'); + + if ($suffix == '') { + $this->SetStatus(IS_INACTIVE); + return; + } + + $filter = + '.*"Topic":"(feedback-request|remote-control-request)\/' + . preg_quote($suffix, '/') + . '".*'; + + $this->SetReceiveDataFilter($filter); + + $this->SetStatus(IS_ACTIVE); + } + + public function RequestAction($Ident, $Value) + { + switch ($Ident) { + + case 'PowerSetpoint': + + $max = $this->ReadPropertyInteger('MaxPowerSetpoint'); + + if ($Value > $max) { + $Value = $max; + } + + SetValue($this->GetIDForIdent('PowerSetpoint'), $Value); + + $this->RunBatteryControl(); + + break; + + case 'Strategy': + + SetValueString( + $this->GetIDForIdent('Strategy'), + (string)$Value + ); + + $this->RunBatteryControl(); + + break; + } + } + + public function RunBatteryControl() + { + $reqActionID = $this->ReadPropertyInteger('ReqActionID'); + $powerProductionID = $this->ReadPropertyInteger('PowerProductionID'); + + + + $strategy = GetValueString( + $this->GetIDForIdent('Strategy') + ); + + $setpoint = GetValue( + $this->GetIDForIdent('PowerSetpoint') + ); + + + } + + public function BuildReadResponse() + { + + + $powerProductionID = $this->ReadPropertyInteger( + 'PowerProductionID' + ); + + $data = [ + + 'power_production' => $this->IsValidVariable( + $powerProductionID + ) + ? GetValue($powerProductionID) + : 0, + + 'is_ready' => GetValue( + $this->GetIDForIdent('IsReady') + ), + + 'is_running' => GetValue( + $this->GetIDForIdent('IsRunning') + ) + ]; + + $json = json_encode( + $data, + JSON_PRETTY_PRINT + ); + + SetValueString( + $this->GetIDForIdent('LastReadResponse'), + $json + ); + + return $json; + } + + public function HandleRemoteControlJSON(string $payload) + { + if ($payload == '') { + return; + } + + $data = json_decode($payload, true); + + if (!is_array($data)) { + return; + } + + // ------------------------------------------------- + // Power Setpoint + // ------------------------------------------------- + + if (isset($data['power_setpoint'])) { + + $max = $this->ReadPropertyInteger( + 'MaxPowerSetpoint' + ); + + $val = (int)$data['power_setpoint']; + + if ($val > $max) { + $val = $max; + } + + if ($val < ($max * -1)) { + $val = $max * -1; + } + + SetValue( + $this->GetIDForIdent('PowerSetpoint'), + $val + ); + } + + // ------------------------------------------------- + // Strategy + // ------------------------------------------------- + + if (isset($data['strategy'])) { + + SetValueString( + $this->GetIDForIdent('Strategy'), + (string)$data['strategy'] + ); + } + + // ------------------------------------------------- + // Batterie-Regelung ausführen + // ------------------------------------------------- + + $this->RunBatteryControl(); + + // ------------------------------------------------- + // Antwort bauen + // ------------------------------------------------- + + $output = [ + + 'power_setpoint' => GetValue( + $this->GetIDForIdent('PowerSetpoint') + ), + + 'strategy' => GetValueString( + $this->GetIDForIdent('Strategy') + ) + ]; + + $json = json_encode( + $output, + JSON_PRETTY_PRINT + ); + + SetValueString( + $this->GetIDForIdent('LastWriteResponse'), + $json + ); + + return $json; + } + + public function ReceiveData($JSONString) + { + $data = json_decode($JSONString, true); + + if (!is_array($data)) { + return; + } + + if (($data['PacketType'] ?? 0) != 3) { + return; + } + + if (!isset($data['Topic'])) { + return; + } + + $topic = $data['Topic']; + + // Eigene Antworten ignorieren + if (strpos($topic, 'feedback-response/') === 0) { + return; + } + + if (strpos($topic, 'remote-control-response/') === 0) { + return; + } + + $this->SendDebug('ReceiveData', $JSONString, 0); + + $payload = $data['Payload'] ?? ''; + $suffix = $this->ReadPropertyString('TopicSuffix'); + + // ------------------------------------------------- + // Lesebefehl + // ------------------------------------------------- + + if ($topic === 'feedback-request/' . $suffix) { + + $json = $this->BuildReadResponse(); + + $this->QueuePublish( + 'feedback-response/' . $suffix, + $json + ); + + return; + } + + // ------------------------------------------------- + // Writebefehl + // ------------------------------------------------- + + if ($topic === 'remote-control-request/' . $suffix) { + + $json = $this->HandleRemoteControlJSON($payload); + + if ($json !== null) { + + $this->QueuePublish( + 'remote-control-response/' . $suffix, + $json + ); + } + + return; + } + } + + + private function PublishViaHelper(string $topic, string $payload) + { + $id = $this->ReadPropertyInteger('PublishInstanceID'); + + if ($id <= 0 || !IPS_InstanceExists($id)) { + $this->SendDebug('PublishViaHelper', 'Kein gültiges Publish-Modul gewählt', 0); + return; + } + + MBPUB_Publish($id, $topic, $payload); + } + + + private function QueuePublish(string $topic, string $payload) +{ + $this->SetBuffer('PublishTopic', $topic); + $this->SetBuffer('PublishPayload', $payload); + + $this->SetTimerInterval('PublishDelay', 500); +} + +public function DoDelayedPublish() +{ + $this->SetTimerInterval('PublishDelay', 0); + + $topic = $this->GetBuffer('PublishTopic'); + $payload = $this->GetBuffer('PublishPayload'); + + $this->SetBuffer('PublishTopic', ''); + $this->SetBuffer('PublishPayload', ''); + + if ($topic == '' || $payload == '') { + return; + } + + $this->SendDebug('Publish', $topic . ' => ' . $payload, 0); + + $this->SendDataToParent(json_encode([ + 'DataID' => '{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}', + 'PacketType' => 3, + 'QualityOfService' => 0, + 'Retain' => false, + 'Topic' => $topic, + 'Payload' => $payload + ])); +} + + private function IsValidVariable(int $id) + { + return ( + $id > 0 + && IPS_ObjectExists($id) + && IPS_GetObject($id)['ObjectType'] == 2 + ); + } +} + +?> \ No newline at end of file diff --git a/Verbraucher_1_Stufig/module.php b/Verbraucher_1_Stufig/module.php index 5838329..65eb204 100644 --- a/Verbraucher_1_Stufig/module.php +++ b/Verbraucher_1_Stufig/module.php @@ -174,11 +174,7 @@ class Verbraucher_1_Stufig extends IPSModule $Mindestlaufzeit = $this->ReadPropertyInteger("Mindesttlaufzeit") * 60 * 12; - if ( - $Peak && - $this->ReadPropertyInteger("Mindesttlaufzeit") <= 0 && - $this->ReadPropertyInteger("Zeit_Zwischen_Zustandswechseln") <= 0 - ) { + if ($Peak && $this->ReadPropertyInteger("Mindesttlaufzeit") <= 0 && $this->ReadPropertyInteger("Zeit_Zwischen_Zustandswechseln") <= 0) { $this->SetValue("PowerSteps", json_encode([0])); return; } @@ -196,10 +192,7 @@ class Verbraucher_1_Stufig extends IPSModule if ($Peak) { $this->SetValue( "PowerSteps", - json_encode([ - 0, - $this->ReadPropertyInteger("BoilerLeistung"), - ]) + json_encode([0,$this->ReadPropertyInteger("BoilerLeistung"),]) ); } else { $this->SetValue("PowerSteps",json_encode([$this->ReadPropertyInteger("BoilerLeistung")]));