MQTT Anbindung / oder externe Daten per Push in Symcon bekommen

Hallo,

Ich hab schon gesucht ob es vielleicht eine MQTT Anbindung fuer Symcon gibt, aber das scheint nicht der Fall zu zein. Waere durchaus insteressant als zukuenftiges Modul einen MQTT Client als Modul zu inkludieren, der auf gewisse topics subscriben kann und dann die Inhalte direkt in Variablen uebernimmt. Dies scheint derzeit aber gar nicht moeglich zu sein.

Ich hab eine Menge an Daten von Selbstbau-Arduino-Funksensoren die ich nun gerne in Symcon bringen wuerde, allerdings hab ich keine sinnvolle Methode gefunden externe Daten per Push in Symcon reinzubringen. Da es keine direkte MQTT Anbindung gibt war meine erste Idee die Daten per python script als mqtt client subscriber ueber das IP Gateway an KNX zu senden; wodurch die dann auch in Symcon ankommen wuerden; allerdings stellt das auch eine nicht zu unterschaetzende Belastung des (ja doch sehr langsamen) KNX Bus dar.

Eine naechste, sehr holprige, Idee war ein User-PHP Script in Symcon aufzurufen, und dort die Variablen und Werte als GET Parameter zu uebergeben. Das waere relativ einfach umzusetzen, aber ist doch eher unschoen.

Es gibt sicher irgendeine elegantere Moeglichkeit externe Daten per Push (kein Polling durch Symcon) nach Symcon zu bekommen – freue mich ueber alle anderen Loesungsvorschlaege!

MQTT kenne ich zwar nicht, aber die Frage wie man Daten nach IPS bekommt; wurde die letzten Tage häufiger gestellt.
Native gibt es JSON-RPC und SOAP (veraltet); sowie noch min. zwei andere Wege (ohne jetzt jeden I/O-Socket in IPS zu zählen).
Doku zu JSON-RPC und SOAP:
Datenaustausch — IP-Symcon :: Automatisierungssoftware

Hier mal ein Auszug aus dem Forum:
JSON-RPC über Python
Befehl über LAN an IPS senden

Oder ganz banal mit einem ServerSocket, RegisterVariable und PHP-Script in IPS, welches ankommen Daten auf Port XYZ empfängt, zerlegt und in Variablen schreibt.
Michael

Hallo,

Danke erst mal an Obstler42 für die Eröffnung dieses Threads.

Aus meiner Sicht ist MQTT in jedem Fall interessant da es in vielen Beiträgen als das Protokoll des Internet of Things genannt wird.

Gibt es bei den Machern von IP Symcon hier entsprechende Überlegungen?

Grüße

Ich hab inzwischen mal an Anlehnung an das oben verlinkte Python script etwas selbst gebastelt per JSON-RPC SetValue Interface, und in mqttwarn integriert. Es funktioniert, allerdings sehr muehsam da man jede einzelne IPS Variable per id hardcoded hinzufuegen muss.

Aus meiner Sicht perfekt waere ein MQTT Interface in Symcon, wo man wie bei anderen Interfaces auch direkt topics <-> variablen hinzufuegen kann. Der Listener waere im Prinzip einfach ein mqtt subscriber fuer die relevanten topics, der das dann direkt in die entsprechende Variable schreibt – analog zu den bestehenden interfaces.

Erweiterbar waere das natuerlich auch noch in die andere Richtung per publishing, aber ein wirklich wichtiger, erster grosser Schritt waere ein Listener/Subscriber interface.

Open Source Beispiels-code zu dem Thema gibt es genug, zB auch beim mqtt interface von OpenHAB.

Ich wuerde mir hier wirklich eine zukuenftige direkte Integration in IPS wuenschen, und stehe auch zur Verfuegung fuer weitergehende Detaildiskussionen oder auch RealWorld tests, da bei mir mqtt sehr aktiv im Einsatz ist (fuer eine Vielzahl an arduino based funksensoren).

Gibt es vielleicht irgendwelche Updates zu dem Thema MQTT Integration bei Symcon?

Du kannst ja in deinem Script unbekannte Variablen direkt anlegen lassen statt sie hartcodiert zu hinterlegen. Das wäre dann so eine Art Automatismus, der dir vorschwebt. Ist nur doof für Leite mit einer kleinen Lizenz :wink:

IPS_VariableExists — IP-Symcon :: Automatisierungssoftware
IPS_CreateVariable — IP-Symcon :: Automatisierungssoftware

Gruß,

Toni

ja ich hab auch schon eine hackloesung ueber json/rpc api, aber da ist halt immer an mehreren Seiten Bastelarbeit notwendig.

Ein MQTT Listener/Configurator in Symcon waere die perfekte Anbindung fuer beliebige externe Devicedaten, und koennte dann einfach in Symcon die Adressen konfigurieren.

Es gibt eine API. Du darfst dich gern austoben. Alles was das Team damit programmiert kannst du auch damit programmieren. die JSON Schnittstelle ist ein guter Ansatz. Verwende ich auch am liebsten.

Toni

Hallo zusammen,
hab phpMQTT (bluerhinos/phpMQTT · GitHub) als publisher/subscriber und mosquitto MQTT (An Open Source MQTT v3.1 Broker) broker laufen.

Hier wird von einem 1-wire (DS18B20) die Temperatur „published“, Nachrichtenformat Base64 encoded JSON:

<?
require("\..\webfront\user\phpMQTT\phpMQTT.php");
define("MYSQL_DATE_FORMAT","Y-m-d H:i:s");

//test, soll noch weiter automatisiert werden
$arr['ts'] = date(MYSQL_DATE_FORMAT);
$arr['uuid'] = "44000005BFB92228"; // 1-wire device id, // todo: assign automatically by script/function
$arr['value'] = GetValue(15966);
$arr['unit'] = "degree celsius";
MQTTHelper::sendMQTTMessage("devices/onewire/tmex/".$arr['uuid'], json_encode($arr));

class MQTTHelper {
	/**
	 * -----------------------------------------------------------------------------
	 */
	public static function sendMQTTMessage($topic, $message) {
		$mqtt = new phpMQTT("whz-aiis-work", 1883, "SYMCON01"); //Change client name to something unique
   	if ($mqtt->connect()) {
			//$mqtt->publish($topic, base64_encode($message),0); // QoS 0
			$mqtt->publish($topic, $message,0);
			$mqtt->close();
		}
	}
}
?>

Und hier das Abonnement eines Topic:

<?
/**
 * IPS MQTT SUBSCRIBER
 */
require("\..\webfront\user\phpMQTT\phpMQTT.php");

ini_set('max_execution_time', 0); // in seconds, 0 ... infinite

subscribeMQTT('IPS/');


function subscribeMQTT($topic) {

	echo "Connecting to";
	$mqtt = new phpMQTT("whz-aiis-work", 1883, "phpMQTTSymconClient"); //Change client name to something unique
	if(!$mqtt->connect()){
		exit(1);
	}
	$topics[$topic] = array("qos"=>0, "function"=>"procmsg");
	$mqtt->subscribe($topics,0);
	echo "Subscribed to ";
	while($mqtt->proc()){
	}
	$mqtt->close();
}

/**
 * --------------------------------------------------------------------------
 *
 * --------------------------------------------------------------------------
 */
function procmsg($topic ,$msg){
	echo "[MQTTHelper] Msg Recieved: ".date("r")."
Topic:{$topic}
$msg
";
	SetValue(26366 /*[MQTT	opic]*/, $topic);
	SetValue(15655 /*[MQTT\message]*/, $msg);
	if(strcmp($topic,"IPS/")) {
		//handleIPSCMD($msg);
	}
}
?>


Nachteil dieser Lösung ist, dass mittels ini_set(‚max_execution_time‘, 0); für das Script die Laufzeitbeschränkung aufgehoben werden muss, damit es stets im Hintergrund seine Arbeit verrichtet. Alternativ könnte das Script periodisch gestartet werden, um bspw. Retained Messages oder QoS>0 empfangen zu können, dann kann die max_execution_time > 0 gesetzt bzw. beim Standardwert belassen werden.
Vielleicht hat noch jemand eine bessere Lösung!?

VG,
Marek :slight_smile:

Hallo Marek,

arbeite mich gerade in MQTT vor.
Hab aber noch meine Probleme.

Versuchsweise nutze ich diesen public Borker.
Mit dem (zu Versuchszwecken) ausgedünnten Script von Dir kann ich Massages einwandfrei verschicken.

<?
require("phpMQTT.php");

$topic="test1234/ersteMsg";
$msg="Hallo Welt!";

MQTTHelper::sendMQTTMessage($topic, $msg); 

class MQTTHelper {
    public static function sendMQTTMessage($topic, $message) {
        $mqtt = new phpMQTT("broker.mqttdashboard.com", 1883, "IPS"); 
       if ($mqtt->connect()) {
            $mqtt->publish($topic, $message,0);
            $mqtt->close();
        }
    }
}
?>

Im Dashboard von dem public Broker werden sie auch korrekt angezeigt.
MQTT_msg1.JPG
Mit dem Empfangen (subscribe) tue ich mich schwer und finde den Fehler nicht.
Auch hier dein (vereinfachtes) Script:

<?
require("phpMQTT.php");

ini_set('max_execution_time', 30); // in seconds, 0 ... infinite

subscribeMQTT('test1234/ersteMsg');

function subscribeMQTT($topic) {

    echo "Connecting to";
    $mqtt = new phpMQTT("broker.mqttdashboard.com", 1883, "IPS"); 
    if(!$mqtt->connect()){
        exit(1);
    }
    $topics[$topic] = array("qos"=>0, "function"=>"procmsg");
    $mqtt->subscribe($topics,0);
    echo "Subscribed to ";
    while($mqtt->proc()){
    }
    $mqtt->close();
}

function procmsg($topic ,$msg){
    echo "[MQTTHelper] Msg Recieved: ".date("r")."
Topic:{$topic}
$msg
";
      SetValue(59715 /*[Kann theoretisch weg\phpMQTT subscribe	opic]*/, $topic);
      SetValue(11622 /*[Kann theoretisch weg\phpMQTT subscribe\msg]*/, $msg);
    if(strcmp($topic,"BestimmtesEreignis")) {
        //handleIPSCMD($msg);
    }
}
?>

Entweder verschickt derpublic broker meine Message nicht weiter oder es liegt irgendwie an dem codieren des topic? Wenn ich als abonierte Topics z.B. alle zulasse (#) oder z.b. so was triviales „test“ empfängt er diverse von fremden versendern.
Wenn ich eine Massage über die publish-Funktion geannten Brokers verschicke geht es auch richtig. Sehe ich den Wald vor lauter Bäumen nicht?

Gruß
hardlog

Hallo hardlog,

hab die Scripte bei mir mal ausprobiert. Das Subscriber-Script hatte ich selbst noch nicht ausgiebig getestet, damit gibts noch große Probleme mit der Performance (ini_set(‚max_execution_time‘…)). Und was ich bisher selbst nicht besonders beachtet hatte: Die Lösung mit zwei separaten Scripten, die jeweils eine Client-Verbindung zum Broker aufbauen, ist nicht besonders optimal und hat einen Haken, wenn die gleiche Client-ID verwendet wird. Ich habs mal testweise mit unterschiedlichen ID`s versucht, da hat das publishen aus Skript 1 funktioniert und über Skript 2 (subscriber) kamen die Nachrichten wieder zurück und wurden einer Variable zugewiesen.
Das größte Manko ist aber, das beim Subscriben eingehende Nachrichten erst verarbeitet werden, wenn das Script beendet wird. Deshalb hab ich die MAX_EXECUTION_TIME auf 10 gesetzt und starte das Script per Timer alle 10 s immer wieder neu. Das funktioniert zwar, ist aber alles andere als schön.

Um die Funktionen (publish und subscribe) einzeln testen zu können hab ich übrigens das Tool „mqtt-spy“ (mqtt-spy) verwendet, welches ich sehr nützlich finde.

Und dann gibts auch seit ein paar Tagen für die neue Version 4.0 ein Modul „SymconMQTT“, das allerdings gegenwärtig nur publishen kann, das subscriben ist damit noch nicht möglich. Es basiert allerdings auf den oben verwendeten Scripten und ist auch nur mal zum Testen gedacht. Hier ist es zu finden:
mkretzschmar/SymconMQTT · GitHub

Damit lässt sich ein MQTTPublisher als I/O-Instanz einrichten und konfigurieren. In anderen Scripten kann diese Instanz dann auch verwendet werden

MQTT_Publish(12345, „Irgendein/Topic“, „Der Inhalt“, 0, false); // QoS = 0, Retained = false

Hoffe, diese Infos helfen irgendwie!?

Viele Grüße,
Marek

Hallo Marek,

das sieht doch sehr vielversprechend aus!
Hatte das Projekt erstmal zurück gestellt. Werde mich jetzt aber wieder damit befasse…

Gruß
hardlog

Nachtrag: demnächst ist mal noch eine Überarbeitung geplant, so dass als I/O Instanz ein „MQTT Broker“ benutzt wird (statt jetzt „MQTT Publisher“). MQTT Publisher wird als Gerät zur Verfügung stehen und kann dann als übergeordnete Instanz einen MQTT Broker verpasst bekommen. Ebenso wie das noch zu implementierende „MQTT Subscriber“ Gerät. Beim MQTT Subscriber ist noch die Frage, ob er sich nur auf einen Topic registrieren können soll oder auf mehrere.

Hat jemand Vorschläge, wie die Instanztypen besser/sinnvoller verwendet werden sollten?
[I/O] MQTT Broker <-- [Splitter] MQTT Publisher
[I/O] MQTT Broker <-- [Splitter] MQTT Subscriber
oder
[I/O] MQTT Broker <-- [Gerät] MQTT Publisher
[I/O] MQTT Broker <-- [Gerät] MQTT Subscriber
oder…?

Dann könnten jedem Broker mehrere Publisher und/oder Subscriber zugeordnet werden
1 MQTT Broker – 0…n MQTT Publisher
1 MQTT Broker – 0…n MQTT Subscriber

Oder ist das viel zu aufwendig? Würde ein Modul ‚MQTT Broker‘ (oder dann besser ‚MQTT Client‘ genannt), das alle Funktionen bereit stellt, sinnvoller sein? :confused:

Vielleicht hat ja jemand Ideen oder Vorschläge?

Hallo,
ich bin auch gerade dabei mit MQTT eigenen Sensoren zu integrieren (RaspPi 3 Basis). Von meiner warte denke ich ist der MQTTClient der richtige begriff, da ich davon ausgehen das du keinen tatsächlichen Broker implementieren willst.
Ich denke ein subscribe sollte auf mehrere Topics u.u. auch mit WildCard gehen und die darunter liegende Struktur auf basis der topic Struktur aufgebaut werden.

Der MQTT Broker läuft bei mir auf einem der PIs und wird dynamisch via DNS-SD bekannt gegeben und bei Ausfall des selbigen übernimmt ein anderer die Rolle.

Die Anbindung an IP-Symcon werde ich vermutlich über JSON RPC und einem Java Programm machen (allein schon wegen der DNS-SD Geschichte) und da ich für das Tracking auch ein paar KI Bibliotheken brauche. Ich werde aber denke ich die meisten Informationen dann 1:1 ins IP-Symcon spiegeln. Ich denke das ist dann auch etwas Eleganter, da für ein Subscribe nicht ständig ein Script am leben gehalten werden muss.

Bin grade noch am Programmieren, da aber pro Raum ungefähr die gleichen Daten anfallen (diverse Informationen zu den Präsenzmeldern (Ultraschall und PIR), Helligkeit, Erkannte BLE Geräte von Registrierten Benutzern mir RSSI (tracking), und die Sprachsteuerung/Ausgabe via Jasper (Open Source Raspberry PI Projekt))… Aktuell ist das meiste in Arbeit aber am werden.
Grüße
Marcus

Hallo,

ich finde die MQTT Anbindung an Symcon ist sehr Wichtig da es in Zukunft eher Mehr als Weniger Geräte geben wird die MQTT nutzen werden, auch ist die Installation eines Brokers (z. B. mosquitto) auf dem selben Raspi wo auch symcon läuft kein Thema wo dann alle Daten sowohl senden [Publishen] als auch von dort beziehen [Subscriben] können.

Wer will kann auch online broker nutzen oder seinen eigenen per ssl und Athentifizierung ins Internet schalten?!

Vielleicht haben ja die alten Hasen hier noch einige Infos und können uns „neulinge“ bei der Erstellung / Fertigstellung eines Moduls helfen?

Ich finde die Arbeit von Marek schon super, leider kann ich dabei noch nicht helfen weil ich symcon erst kennen und schätzen lerne…

hi,

da es ein paar private Anfragen zu meiner mqtt hack loesung gegeben hat, hier einfach oeffentlich.

Ich verwende mqttwarn https://github.com/jpmens/mqttwarn um gewisse topics per JSON RPC nach symcon zu pushen. Im Detail hierzu ein auszug aus der mqttwarn.ini:


[config:http]
timeout = 60
targets = {
                #method     #URL               # query params or None          # list auth
  'get1'    : [ "get",  "http://example.org?", { 'q': '{name}', 'isod' : '{_dtiso}', 'xx': 'yy' }, ('username', 'password') ],
  'post1'    : [ "post", "http://example.net", { 'q': '{name}', 'isod' : '{_dtiso}', 'xx': 'yy' }, None ],
  'postIPS'    : [ "post", "http://10.0.0.11:82/api/", None, ('username', 'passwd') ]
  }
...
[40/7/1]
targets = http:postIPS
template = ipsrpc.json
...

und im templates folder dann das entsprechende ninja json template:


{% set data = {
	'method': "SetValue",
	'params': [25963, topic +";"+ payload],
	'jsonrpc': "2.0",
	'id': '0'
    }
    %}
{{ data | jsonify }}

25963 ist eine string variable in symcon, die dann topic;payload/message per json geschrieben bekommt. Auf der Variable haengt per update event ein PHP script, dass die Werte dann in die jeweiligen richtigen symcon variablen schreibt:


<?
	// mqtt topic to symcon variable id mapping
	$idmap = [
	    "13/7/2" => 18846,
	    "13/7/5" => 10300,
	    "15/7/2" => 32631,
            // ....
	    "40/7/1" => 21172,
	];
	
	// get value from mqtt variable
	$var = GetValue(25963 /*[Sensor\Arduino\mqttString]*/);
	$varsplit = explode(";", $var);
	if(count($varsplit)>1)
	{
	   $topic=$varsplit[0];
	   $payload=$varsplit[1];
	   
	   $id=getIDforTopic($topic);
	   if($id > -1)
	      mqset($id, $payload);
	}
	
	function mqset($id, $value)
	{
	   $obj=IPS_GetVariable($id);
	   switch($obj["VariableType"])
	   {
	      case 0: // boolean
	         $val = filter_var($value, FILTER_VALIDATE_BOOLEAN);
	         SetValue($id, $val);
	         break;
	      case 1: // integer
	         $val = intval($value);
	         SetValue($id, $val);
	         break;
	      case 2: //  float
	         $val = floatval($value);
	         SetValue($id, $val);
	         break;
	      case 3: // string
	         SetValue($id, $value);
	         break;
	   }
	}
	
	function getIDforTopic($strTopic)
	{
	   global $idmap;
	   $return = -1;
	   if (array_key_exists($strTopic, $idmap))
		    $return = $idmap[$strTopic];
		return $return;
	}
?>

wie schon frueher gesagt, eigentlich ein schlimmer hack, aber funktioniert nun schon seit vielen Monaten mit ca. 10-15 variablen aus mqttstabil. ich verwende es hauptsaechlich fuer die selbst gebastelten wireless arduino/nodemcu sensoren die ihre werte automatisch direkt oder indirekt auf mqtt publishen, und von dort dann eben weiter nach symcon.

Wie viele andere auch wuerde ich mir natuerlich eine direkte Integration von mqtt in symcon wuenschen, aber so funktioniert es derzeit zumindest.

Hallo,

vielen Dank für die Anleitung!

Alternativ gibt es auch vom User Charykun ein Modul mit dem direkt in Symcon per MQTT Daten gesendet werden können, dies ist ein Test und noch nicht stabil, auch können damit keine Daten veröffentlicht werden und es muss / sollte für jeden MQTT Client ein eigenes Port benutzt werden.

Das Modul findet ihr hier:
https://github.com/Charykun/SymconMQTTBroker

Mir war Stabilitaet am wichtigsten, deswegen zu der beschriebenen Loesung gegriffen. mqttwarn laeuft seit Monaten auf einem RasPi ohne Probleme, und symcon-seitig ist das einfache PHP Script ebenso stabil. Etwas mehr Arbeit da alles doppelt (in mqttwarn und in symcon) angelegt werden muss, aber dafuer soweit problemfrei.

Hallo,

bei der Anleitung fehlt noch der Punkt das das http Modul auch von mqttwarn geladen werden muss also

; name the service providers you will be using.
launch    = file, log

auf

; name the service providers you will be using.
launch    = file, log, http

geändert werden muss.

Auch muss die Template datei wie angegeben den namen „ipsrpc.json“ im unterverzeichniss template haben.

Edit: → das Python paket python2.7-jinja2 muss auch noch Installiert sein, dann kann das Template auch die Daten Formatieren.

Und hilfreich ist auch die Information was die Wildcards sind.

pfad/+

bedeutet alles was unterhalb von pfad/ ist aber nur in dieser ebene

pfad/#

bedeutet das alles was unterhalb von pfad/ ist sogar weitere „Verzeichnisse“

Hallo,

ich habe das symcon script von obstler42 etwas angepasst so das topics Automatisch zerlegt und in Symcon angelegt werden.

Ich verwende in mqtt als topic einen Pfad ähnlich von Dateien wie:

devices/terminal/helligkeit

Eine Übergabe sieht folgendermaßen aus:

devices/terminal/helligkeit;319

und so sieht das in der console aus:

Hier noch das geänderte script:

<?
   //sample value: 	devices/terminal/helligkeit;319
	// mqtt topic to symcon variable id mapping
	$idParent = IPS_GetParent($_IPS['SELF']);

	// get value from mqtt variable
	$var = GetValue(53755 /*[Datenpunkte\MQTT\mqtt inbox]*/);
	$varsplit = explode(";", $var);
	
	if(count($varsplit)>1){
		$topics = explode("/", $varsplit[0]);
		$topic=$varsplit[0];
		$payload=$varsplit[1];
		//print_r($topics);

		$contType = 99;

		if (is_float($payload)){
			$contType = 2;
		}
		if (($contType == 99) and (is_numeric($payload))){
			$contType = 1;
		}
		if (($contType == 99) and (is_string($payload))){
			if ((strtolower($payload) == "true") or (strtolower($payload) == "false")){
				$contType = 0;
			} else {
				$contType = 3;
			}
		}
		$idf = $idParent;
		for($i = 0; $i < count($topics); $i++){
			if ($i == (count($topics) -1)){
	         $type = 2;
			} else {
				$type = 0;
			}
			$idf = checkIfElementExists($idf, $type, $topics[$i], $contType);
		}
		if (GetValue($idf) != $payload){
			mqset($idf, $payload);
		}
	}

function mqset($id, $value){
	$obj=IPS_GetVariable($id);
	switch($obj["VariableType"]){
		case 0: // boolean
			$val = filter_var($value, FILTER_VALIDATE_BOOLEAN);
			SetValue($id, $val);
			break;
		case 1: // integer
			$val = intval($value);
			SetValue($id, $val);
			break;
		case 2: //  float
			$val = floatval($value);
			SetValue($id, $val);
			break;
		case 3: // string
			SetValue($id, $value);
			break;
	}
}


function checkIfElementExists($varid, $type, $name, $contType ){
	$exists = false;
	if (IPS_HasChildren($varid)){
		// Wenn Childs da sind alle durchlaufen
		foreach (IPS_GetChildrenIDs($varid) as $key){
			if (IPS_GetObject($key)["ObjectType"] == $type){
				// Prüfen ob schon ein Event mit genau dem script existiert
				if (IPS_GetObject($key)["ObjectName"] == $name){
					// Sonst erstellen
					$exists = true;
					$id = $key;
				}
			}
		}
	}
	if (!$exists){
	   if($type == 2){
		   $id = IPS_CreateVariable($contType);
		} else {
		   $id = IPS_CreateCategory();
		}
		IPS_SetName($id, $name);
	   IPS_SetParent($id, $varid);
	} 
	return $id;
}
    
    
?>