no message

This commit is contained in:
2026-02-10 13:21:43 +01:00
parent 5aa44ad8a2
commit e21f760e13
3 changed files with 175 additions and 178 deletions

View File

@@ -2,7 +2,16 @@
"elements": [ "elements": [
{ {
"type": "Label", "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", "type": "SelectVariable",
@@ -16,22 +25,6 @@
"caption": "Power Production", "caption": "Power Production",
"validVariableTypes": [1, 2] "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", "type": "SelectVariable",
"name": "SourceIsReady", "name": "SourceIsReady",
@@ -44,9 +37,21 @@
"caption": "Is Running (Bool)", "caption": "Is Running (Bool)",
"validVariableTypes": [0] "validVariableTypes": [0]
}, },
{
"type": "SelectVariable",
"name": "SourceMinSoC",
"caption": "Min SoC",
"validVariableTypes": [1, 2]
},
{
"type": "SelectVariable",
"name": "SourceMaxSoC",
"caption": "Max SoC",
"validVariableTypes": [1, 2]
},
{ {
"type": "Label", "type": "Label",
"caption": "Steuerung (Ausgang)" "caption": "Steuerung (Output)"
}, },
{ {
"type": "SelectVariable", "type": "SelectVariable",
@@ -54,11 +59,5 @@
"caption": "Wechselrichter Sollwert (Ziel)", "caption": "Wechselrichter Sollwert (Ziel)",
"validVariableTypes": [1, 2] "validVariableTypes": [1, 2]
} }
],
"actions": [
{
"type": "Label",
"caption": "Hinweis: Änderungen an Variablen werden sofort übernommen."
}
] ]
} }

View File

@@ -6,9 +6,9 @@
"aliases": [ "aliases": [
"VGT MQTT Device" "VGT MQTT Device"
], ],
"parentRequirements": [], "parentRequirements": ["{F66ADE63-8834-4178-8CA5-AE4465D2E252}"],
"childRequirements": [], "childRequirements": [],
"implemented": [], "implemented": [],
"prefix": "VGT", "prefix": "VGT",
"version": "1.0" "version": "1.1"
} }

View File

@@ -6,240 +6,238 @@ class VGT_Sub extends IPSModule
{ {
public function Create() public function Create()
{ {
// Diese Zeile nicht löschen.
parent::Create(); 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) // Externe Hardware-Quellen (Lesen)
$this->RegisterPropertyInteger('SourceSoC', 0); // State of Charge $this->RegisterPropertyInteger('SourceSoC', 0);
$this->RegisterPropertyInteger('SourcePowerProd', 0); // Power Production $this->RegisterPropertyInteger('SourcePowerProd', 0);
$this->RegisterPropertyInteger('SourceIsReady', 0); // Is Ready (Bool) $this->RegisterPropertyInteger('SourceIsReady', 0);
$this->RegisterPropertyInteger('SourceIsRunning', 0); // Is Running (Bool) $this->RegisterPropertyInteger('SourceIsRunning', 0);
$this->RegisterPropertyInteger('SourceMinSoC', 0); // Min SoC $this->RegisterPropertyInteger('SourceMinSoC', 0);
$this->RegisterPropertyInteger('SourceMaxSoC', 0); // Max SoC $this->RegisterPropertyInteger('SourceMaxSoC', 0);
// Das Ziel (zum Schreiben/Steuern der Hardware) // Hardware-Ziel (Schreiben)
$this->RegisterPropertyInteger('TargetControlVar', 0); // Haupt-RequestAction Ziel $this->RegisterPropertyInteger('TargetControlVar', 0);
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// 2. Interne Variablen erstellen // 2. Variablen im Modul erstellen (zur Anzeige & Steuerung)
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Diese Variablen spiegeln den aktuellen Zustand wider
// Die Steuerungsvariablen
$this->RegisterVariableString('Strategy', 'Strategy', '', 10); $this->RegisterVariableString('Strategy', 'Strategy', '', 10);
$this->RegisterVariableInteger('PowerSetpoint', 'Power Setpoint', '', 20); $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('Strategy');
$this->EnableAction('PowerSetpoint'); $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() public function ApplyChanges()
{ {
// Diese Zeile nicht löschen
parent::ApplyChanges(); 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'); $socID = $this->ReadPropertyInteger('SourceSoC');
if (IPS_VariableExists($socID)) { if (IPS_VariableExists($socID)) {
$this->RegisterMessage($socID, VM_UPDATE); $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('Strategy'), VM_UPDATE);
$this->RegisterMessage($this->GetIDForIdent('PowerSetpoint'), 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) { $data = json_decode($JSONString);
$socID = $this->ReadPropertyInteger('SourceSoC');
$stratID = $this->GetIDForIdent('Strategy'); // UTF-8 Payload decodieren
$setID = $this->GetIDForIdent('PowerSetpoint'); $payload = utf8_decode($data->Payload);
$topic = $data->Topic;
$baseTopic = $this->ReadPropertyString('MQTTBaseTopic');
// Wenn sich eine der steuerungsrelevanten Variablen ändert, Logik ausführen // Prüfen: Ist es der Writebefehl?
if ($SenderID == $socID || $SenderID == $stratID || $SenderID == $setID) { if ($topic === $baseTopic . '/Writebefehl') {
$this->ExecuteControlLogic(); $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) public function RequestAction($Ident, $Value)
{ {
switch ($Ident) { switch ($Ident) {
case 'Strategy': case 'Strategy':
case 'PowerSetpoint': case 'PowerSetpoint':
$this->SetValue($Ident, $Value); $this->SetValue($Ident, $Value);
// Sofort Logik neu berechnen
$this->ExecuteControlLogic(); $this->ExecuteControlLogic();
break; 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) private function ProcessWriteCommand($jsonInput)
{ {
if ($jsonInput == "") return; if ($jsonInput == "") return;
$data = json_decode($jsonInput, true); $data = json_decode($jsonInput, true);
if ($data === null) return; if ($data === null) return;
// 1. power_setpoint verarbeiten // 1. Variablen setzen
if (isset($data['power_setpoint'])) { if (isset($data['power_setpoint'])) {
$val = $data['power_setpoint']; $val = $data['power_setpoint'];
if ($val > 6000) $val = 6000;
// Begrenzen auf -6000 bis +6000 elseif ($val < -6000) $val = -6000;
if ($val > 6000) {
$val = 6000;
} elseif ($val < -6000) {
$val = -6000;
}
$this->SetValue('PowerSetpoint', $val); $this->SetValue('PowerSetpoint', $val);
} }
// 2. strategy verarbeiten
if (isset($data['strategy'])) { if (isset($data['strategy'])) {
$this->SetValue('Strategy', $data['strategy']); $this->SetValue('Strategy', $data['strategy']);
} }
// 3. Nach dem Schreiben Logik sofort ausführen, damit Hardware reagiert // 2. Hardware sofort ansteuern
$this->ExecuteControlLogic(); $this->ExecuteControlLogic();
// 4. Antwort generieren (Werte wieder lesen) // 3. Antwort senden (an .../Writebefehl Antwort)
$output = [ $output = [
'power_setpoint' => $this->GetValue('PowerSetpoint'), 'power_setpoint' => $this->GetValue('PowerSetpoint'),
'strategy' => $this->GetValue('Strategy') 'strategy' => $this->GetValue('Strategy')
]; ];
$jsonOut = json_encode($output, JSON_PRETTY_PRINT);
// In die Output-Variable schreiben (MQTT liest diese) $this->SendMQTT($this->ReadPropertyString('MQTTBaseTopic') . '/Writebefehl Antwort', json_encode($output));
$this->SetValue('MQTT_Write_Out', $jsonOut);
} }
// -------------------------------------------------------------------------
// LOGIK: Lesebefehl (Skript 3)
// -------------------------------------------------------------------------
private function ProcessReadCommand() private function ProcessReadCommand()
{ {
// IDs aus Properties laden // Hardware Werte lesen
$idPowerProduction = $this->ReadPropertyInteger('SourcePowerProd'); $data = $this->GatherHardwareData();
$idIsReady = $this->ReadPropertyInteger('SourceIsReady');
$idIsRunning = $this->ReadPropertyInteger('SourceIsRunning');
$idStateOfCharge = $this->ReadPropertyInteger('SourceSoC');
$idMinSOC = $this->ReadPropertyInteger('SourceMinSoC');
$idMaxSOC = $this->ReadPropertyInteger('SourceMaxSoC');
// Hilfsfunktion: Wert holen oder 0/false wenn ID fehlt // Lokale Visu-Variablen updaten (damit man im Modul sieht was passiert)
$safeGet = function($id) { $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; return IPS_VariableExists($id) ? GetValue($id) : 0;
}; };
// Daten zusammenstellen return [
$data = [ 'raw_prod' => $safeGet('SourcePowerProd'),
// Achtung: Originalskript rechnete -1 * Wert 'is_ready' => (bool)$safeGet('SourceIsReady'),
'power_production' => -1 * $safeGet($idPowerProduction), 'is_running' => (bool)$safeGet('SourceIsRunning'),
'is_ready' => (bool)$safeGet($idIsReady), 'soc' => $safeGet('SourceSoC'),
'is_running' => (bool)$safeGet($idIsRunning), 'min_soc' => $safeGet('SourceMinSoC'),
'state_of_charge' => $safeGet($idStateOfCharge), 'max_soc' => $safeGet('SourceMaxSoC'),
'min_soc' => $safeGet($idMinSOC),
'max_soc' => $safeGet($idMaxSOC)
]; ];
}
$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 $JSON = json_encode($Data);
$this->SetValue('MQTT_Read_Out', $json); // An Parent senden
$this->SendDataToParent($JSON);
} }
} }
?> ?>