diff --git a/Int_VGT/form.json b/Int_VGT/form.json index 9f5911e..f628176 100644 --- a/Int_VGT/form.json +++ b/Int_VGT/form.json @@ -1,9 +1,50 @@ { "elements": [ { - "type": "ValidationTextBox", - "name": "DeviceID", - "caption": "Device ID" + "type": "Label", + "caption": "MQTT Einstellungen" + }, + { + "type": "Select", + "name": "MQTTServer", + "caption": "MQTT Server", + "options": [ + { + "caption": "Automatisch (Parent)", + "value": "auto" + } + ] + }, + { + "type": "CheckBox", + "name": "Debug", + "caption": "Debug Log aktivieren" + }, + { + "type": "Label", + "caption": "Erkannte Shelly Geräte" + }, + { + "type": "List", + "name": "Devices", + "columns": [ + { + "caption": "Geräte-ID", + "name": "id", + "width": "200px" + }, + { + "caption": "Typ", + "name": "type", + "width": "150px" + } + ] + }, + { + "type": "Button", + "caption": "Geräte neu scannen", + "onClick": "ShellyScanDevices();" } - ] + ], + "actions": [] } diff --git a/Int_VGT/libs/ShellyParser.php b/Int_VGT/libs/ShellyParser.php new file mode 100644 index 0000000..c542b86 --- /dev/null +++ b/Int_VGT/libs/ShellyParser.php @@ -0,0 +1,45 @@ + $val) { + + if ($key === "input") { + $mapped["input"] = (bool)$val; + } + + if ($key === "output") { + $mapped["output"] = (bool)$val; + } + + if ($key === "temperature" || $key === "temp") { + $mapped["temperature"] = (float)$val; + } + + // weitere Shelly-Geräte können später hier ergänzt werden + } + + return $mapped; + } +} diff --git a/Symcon_Publish_to_Shelly_MQTT/README.md b/Shelly_Parser_MQTT/README.md similarity index 100% rename from Symcon_Publish_to_Shelly_MQTT/README.md rename to Shelly_Parser_MQTT/README.md diff --git a/Shelly_Parser_MQTT/form.json b/Shelly_Parser_MQTT/form.json new file mode 100644 index 0000000..9f5911e --- /dev/null +++ b/Shelly_Parser_MQTT/form.json @@ -0,0 +1,9 @@ +{ + "elements": [ + { + "type": "ValidationTextBox", + "name": "DeviceID", + "caption": "Device ID" + } + ] +} diff --git a/Shelly_Parser_MQTT/module.json b/Shelly_Parser_MQTT/module.json new file mode 100644 index 0000000..d742db4 --- /dev/null +++ b/Shelly_Parser_MQTT/module.json @@ -0,0 +1,17 @@ +{ + "id": "{ED0ED0AC-3843-0888-DF27-FA45435BCEF3}", + "name": "Shelly_Parser_MQTT", + "type": 3, + "vendor": "Belevo", + "aliases": [ + "Shelly_Parser_MQTT-Gateway" + ], + "prefix": "Shelly_Parser_MQTT", + "parentRequirements": [ + "{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}" + ], + "childRequirements": [], + "implemented": [ + "{7F7632D9-FA40-4F38-8DEA-C83CD4325A32}" + ] +} diff --git a/Shelly_Parser_MQTT/module.php b/Shelly_Parser_MQTT/module.php new file mode 100644 index 0000000..0bcb182 --- /dev/null +++ b/Shelly_Parser_MQTT/module.php @@ -0,0 +1,207 @@ +ConnectParent('{C6D2AEB3-6E1F-4B2E-8E69-3A1A00246850}'); + $this->Subscribe("#"); + + $this->RegisterPropertyString("Devices", "[]"); + $this->RegisterPropertyBoolean("Debug", false); + } + + + public function ApplyChanges() + { + parent::ApplyChanges(); + $this->ConnectParent('{C6D2AEB3-6E1F-4B2E-8E69-3A1A00246850}'); + $this->Subscribe("#"); + } + + + /* ------------------------------------------------------------------ + * MQTT SUBSCRIBE + * ------------------------------------------------------------------*/ + private function Subscribe(string $topic): void + { + $packet = [ + "PacketType" => 8, + "QualityOfService" => 0, + "Retain" => false, + "Topic" => $topic, + "Payload" => "" + ]; + + $this->SendDataToParent(json_encode([ + "DataID" => "{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}" + ] + $packet)); + } + + + /* ------------------------------------------------------------------ + * MQTT PUBLISH + * ------------------------------------------------------------------*/ + private function Publish(string $topic, string $payload): void + { + $packet = [ + "PacketType" => 3, + "QualityOfService" => 0, + "Retain" => false, + "Topic" => $topic, + "Payload" => $payload + ]; + + $this->SendDataToParent(json_encode([ + "DataID" => "{043EA491-0325-4ADD-8FC2-A30C8EEB4D3F}" + ] + $packet)); + } + + + /* ------------------------------------------------------------------ + * RequestAction → Schalten des Outputs + * ------------------------------------------------------------------*/ + public function RequestAction($Ident, $Value) + { + $iid = IPS_GetParent($this->InstanceID); + + $this->SetValue($Ident, $Value); + + // output? + if ($Ident === "output") { + + $deviceID = IPS_GetName($iid); + + $topic = $deviceID . "/rpc/Switch.Set"; + $payload = json_encode([ + "id" => 0, + "on" => (bool)$Value + ]); + + $this->Publish($topic, $payload); + } + } + + + /* ------------------------------------------------------------------ + * RECEIVE MQTT + * ------------------------------------------------------------------*/ + public function ReceiveData($JSONString) + { + $data = json_decode($JSONString, true); + + $topic = $data["Topic"] ?? ""; + $payload = $data["Payload"] ?? ""; + + if ($topic === "") { + return; + } + + $parts = explode("/", $topic); + $deviceID = $parts[0] ?? ""; + + if ($deviceID === "") { + return; + } + + // ONLINE + if (isset($parts[1]) && $parts[1] === "online") { + $this->HandleOnline($deviceID, $payload); + return; + } + + // RPC + if ((($parts[1] ?? "") === "events") && (($parts[2] ?? "") === "rpc")) { + $this->HandleRPC($deviceID, $payload); + return; + } + } + + + /* ------------------------------------------------------------------ + * ONLINE + * ------------------------------------------------------------------*/ + private function HandleOnline(string $deviceID, string $payload) + { + $iid = $this->GetOrCreateDeviceInstance($deviceID); + + $onlineID = @$this->GetIDForIdentEx("online", $iid); + if (!$onlineID) { + $this->RegisterVariableBoolean("online", "Online"); + } + + $this->SetValue("online", $payload === "true"); + } + + + /* ------------------------------------------------------------------ + * RPC + * ------------------------------------------------------------------*/ + private function HandleRPC(string $deviceID, string $payload) + { + $json = json_decode($payload, true); + if (!is_array($json)) { + return; + } + + $src = $json["src"] ?? ""; + if (!str_starts_with($src, "shelly")) { + return; + } + + // Modell ermitteln + $type = ShellyParser::ExtractType($src); + + $iid = $this->GetOrCreateDeviceInstance($deviceID); + + // Typ speichern + $this->RegisterVariableString("type", "Geräte-Typ"); + $this->SetValue("type", $type); + + // Parameter extrahieren + $mapped = ShellyParser::MapParams($json["params"] ?? []); + + // Variablen anlegen + if (isset($mapped["input"])) { + $this->RegisterVariableBoolean("input", "Input"); + $this->SetValue("input", $mapped["input"]); + } + + if (isset($mapped["output"])) { + $this->RegisterVariableBoolean("output", "Output"); + $this->EnableAction("output"); + $this->SetValue("output", $mapped["output"]); + } + + if (isset($mapped["temperature"])) { + $this->RegisterVariableFloat("temperature", "Temperatur"); + $this->SetValue("temperature", $mapped["temperature"]); + } + } + + + /* ------------------------------------------------------------------ + * Helper → Child-Instanzen erzeugen + * ------------------------------------------------------------------*/ + private function GetOrCreateDeviceInstance(string $deviceID): int + { + foreach (IPS_GetInstanceListByModuleID("{ED0ED0AC-3843-0888-DF27-FA45435BCEF3}") as $id) { + if (IPS_GetName($id) === $deviceID) { + return $id; + } + } + + // neue Instanz + $iid = IPS_CreateInstance("{ED0ED0AC-3843-0888-DF27-FA45435BCEF3}"); + IPS_SetName($iid, $deviceID); + IPS_SetParent($iid, $this->InstanceID); + + return $iid; + } +} diff --git a/Symcon_Publish_to_Shelly_MQTT/form.json b/Symcon_Publish_to_Shelly_MQTT/form.json deleted file mode 100644 index 6e4e50d..0000000 --- a/Symcon_Publish_to_Shelly_MQTT/form.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "elements": [ - { - "type": "ValidationTextBox", - "name": "broker_address", - "caption": "Broker-Adresse" - }, - { - "type": "NumberSpinner", - "name": "broker_port", - "caption": "Broker-Port" - }, - { - "type": "ValidationTextBox", - "name": "username", - "caption": "Benutzername" - }, - { - "type": "PasswordTextBox", - "name": "password", - "caption": "Passwort" - }, - { - "type": "ValidationTextBox", - "name": "Topic", - "caption": "MQTT Topic" - }, - { - "type": "ValidationTextBox", - "name": "client_id", - "caption": "Klient Id" - }, - { - "type": "ValidationTextBox", - "name": "mqtt_instance_id", - "caption": "MQTT Id" - }, - { - "type": "NumberSpinner", - "name": "msg_id", - "caption": "Message ID" - }, - { - "type": "ValidationTextBox", - "name": "src", - "caption": "Source" - }, - { - "type": "ValidationTextBox", - "name": "method", - "caption": "Method" - }, - { - "type": "SelectVariable", - "name": "switch_bool", - "caption": "Variable mit dem zu regelnden Shelly Kontakt", - "validVariableTypes": [0] - } - - ] -} diff --git a/Symcon_Publish_to_Shelly_MQTT/module.json b/Symcon_Publish_to_Shelly_MQTT/module.json deleted file mode 100644 index e8676c4..0000000 --- a/Symcon_Publish_to_Shelly_MQTT/module.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "id": "{F6020D23-AA8C-34C5-D92F-9034BEFBBCD8}", - "name": "Symcon_Publish_to_Shelly_MQTT", - "type": 3, - "vendor": "Belevo AG", - "aliases": [], - "parentRequirements": [], - "childRequirements": [], - "implemented": [], - "prefix": "GEF", - "url": "" -} \ No newline at end of file diff --git a/Symcon_Publish_to_Shelly_MQTT/module.php b/Symcon_Publish_to_Shelly_MQTT/module.php deleted file mode 100644 index a4f2b73..0000000 --- a/Symcon_Publish_to_Shelly_MQTT/module.php +++ /dev/null @@ -1,90 +0,0 @@ -RegisterPropertyString("broker_address", ""); - $this->RegisterPropertyInteger("broker_port", 1883); - $this->RegisterPropertyString("username", ""); - $this->RegisterPropertyString("password", ""); - $this->RegisterPropertyString("Topic", ""); - $this->RegisterPropertyString("client_id", ""); - $this->RegisterPropertyInteger("mqtt_instance_id", 0); - // Nachricht-Payload-Parameter als Properties - $this->RegisterPropertyInteger("msg_id", 100); - $this->RegisterPropertyString("src", "user1"); - $this->RegisterPropertyString("method", "Switch.Set"); - $this->RegisterPropertyInteger("switch_bool", 0); // ID der Bool-Variable - - - $this->RegisterTimer("Timer_Influx",5000,"IPS_RequestAction(" . $this->InstanceID . ', "GetAction", "");'); - } - - public function ApplyChanges() - { - parent::ApplyChanges(); - - } - public function RequestAction($Ident, $Value) - { - IPS_LogMessage("ShellySwitchSender", "RequestAction gestartet"); - switch ($Ident) { - case "GetAction": - $this->GetAction(); - break; - default: - throw new Exception("Invalid action"); - } - } - - - public function GetAction() - { - IPS_LogMessage("ShellySwitchSender", "GetAction gestartet"); - - $mqttInstanceID = $this->ReadPropertyInteger("mqtt_instance_id"); - $topic = $this->ReadPropertyString("Topic"); - - $msg_id = $this->ReadPropertyInteger("msg_id"); - $src = $this->ReadPropertyString("src"); - $method = $this->ReadPropertyString("method"); - $boolVarID = $this->ReadPropertyInteger("switch_bool"); - - if (!IPS_VariableExists($boolVarID)) { - IPS_LogMessage("ShellySwitchSender", "FEHLER: Bool-Variable mit ID $boolVarID existiert nicht."); - return; - } - - $onValue = GetValueBoolean($boolVarID); - - $payload = [ - "id" => 0, - "src" => $src, - "method" => $method, - "params" => [ - "id" => $msg_id, - "on" => $onValue - ] - ]; - - $jsonPayload = json_encode($payload); - - IPS_LogMessage("ShellySwitchSender", "MQTT Payload: $jsonPayload"); - - if (!IPS_InstanceExists($mqttInstanceID)) { - IPS_LogMessage("ShellySwitchSender", "FEHLER: MQTT-Instanz-ID $mqttInstanceID existiert nicht."); - return; - } - - // ✅ RICHTIG: Direkt senden über MQTTClient_SendMessage - MQTTClient_SendMessage($mqttInstanceID, $topic, $jsonPayload, 0, false); - IPS_LogMessage("ShellySwitchSender", "Nachricht erfolgreich gesendet"); - } - - - -}