NEWS
NodeRed - SendTo Proxy mit JS
-
Neuer Thread - um die fehlende Funktion der sendTo Funktionalität an diverse Adapter auszugleichen, habe ich ein kleines Javascript verfasst.
https://github.com/ioBroker/ioBroker.node-red/issues/291
Dazu legt man einen Datenpunkt unter 0_userdata.0 an und bekommt das Ergebnis entweder in dem gleichen Datenpunkt oder in unterschiedlichen Datenpunkten.
So kann man für verschiedene Abfragen immer den gleichen Eingangs (Query) Datenpunkt nehmen, aber auch unterschiedliche Ausgangspunkte.
-
So nun im Detail - hier das Javascript, dass man importieren muss und das stellvertretend für NodeRed - die Nachricht an den entsprechenden Adapter schickt:
EDIT - so noch ein paar kleine Nacharbeiten notwendig - da man ja nur Strings durch die Gegend schicken darf und keine Objekte:
Manche Adapter liefern ja auch gar nichts zurück.
So hier nun das Javascript - was natürlich immer aktiv bleiben muss.
/** * Zweck: Setzt für einen NodeRed Flow - SendTo Kommandos an verschiedene Adapter ab * Datum: 17.08.2022 * Autor: @mickym */ // In die Variable exchangeWithNodeRed den Pfad des Datenpunktes auswählen, der als Trigger verwendet wird /********************************************************************************************************* * In den Datenpunkt muss ein Objekt von Node Red mit folgenden Eigenschaften übergeben werden: * { "adapter": <Adapterinstanz oder Adapter z.Bsp mqtt.0, sql.0>, "command": <Kommando - hängt vom jeweilige Adapter ab - bei sql zum Beispiel query, bei manchen Adaptern auch nicht erforderlich>, "message": <Nachricht, die an den Adapter geschickt wird>, "result": <optional: Pfad zum Datenpunkt, wo das Ergebnis als String abgespeichert wird, ansonsten wird der Trigger als Ausgabepunkt verwendet> } * * */ var exchangeWithNodeRed = "0_userdata.0.NodeRed.sendTo"; on({id: exchangeWithNodeRed, change: 'any', ack: false},function (obj) { // console.log(obj.state.val); var val = JSON.parse(obj.state.val); var returnResult; // console.log("adapter: " + val.adapter); // console.log("command: " + val.command); // console.log("message: " + val.message); // console.log("result: " + val.result); if (val.result === undefined) returnResult = exchangeWithNodeRed; else returnResult =val.result; if (val.command === undefined){ // console.log("no command"); sendTo(val.adapter,val.message,function (result) { if (result.error) setState(returnResult,result.error,true); else setState(returnResult,JSON.stringify(result.result),true); }) } else { // console.log("command: " + val.command); sendTo(val.adapter, val.command, val.message, function (result) { if (result.error) setState(returnResult,result.error,true); else setState(returnResult,JSON.stringify(result.result),true); }); } });
Wie gesagt ich habe immer den gleichen Datenpunkt für die sendTo Funktion verwendet und ggf. unterschiedliche Rückgabe Punkte muss aber nicht sein.
Schauen wir uns mal an, wie man so über den MQTT-Adapter Nachrichten published:
Herzstück für alle Abfragen zu einem Adapter ist die Change-Node, daraus wird dann das Objekt gemacht, dass als JSON zu dem Austauschdatenpunkt geschickt wird.:
Hier nun eine Beispiel mit Rückgabe anhand des SQL-Adapters:
Hier die SQL Abfrage:
und hier die Change-Node:
einmal direkt das Ergebnis geholt oder einen neuen Flow triggern lassen:
Hier mal alles zum Import:
-
Es empfiehlt sich im Übrigen den Datenpunkt zum Austausch als Typ JSON zu definieren:
Damit kann man das Script natürlich dann ohne NodeRed schon mal vortesten.
-
@mickym Sehr elegante Lösung
Ich habe Beispiele wo auch
message
leer sein kann. Dann rennt das Script auf Fehler. Habs mal so gelöst (Zeile 43):/** * Zweck: Setzt für einen NodeRed Flow - SendTo Kommandos an verschiedene Adapter ab * Datum: 17.08.2022 * Autor: @mickym */ // In die Variable exchangeWithNodeRed den Pfad des Datenpunktes auswählen, der als Trigger verwendet wird /********************************************************************************************************* * In den Datenpunkt muss ein Objekt von Node Red mit folgenden Eigenschaften übergeben werden: * { "adapter": <Adapterinstanz oder Adapter z.Bsp mqtt.0, sql.0>, "command": <Kommando - hängt vom jeweilige Adapter ab - bei sql zum Beispiel query, bei manchen Adaptern auch nicht erforderlich>, "message": <Nachricht, die an den Adapter geschickt wird>, "result": <optional: Pfad zum Datenpunkt, wo das Ergebnis als String abgespeichert wird, ansonsten wird der Trigger als Ausgabepunkt verwendet> } * * */ const exchangeWithNodeRed = '0_userdata.0.NodeRed.sendTo'; on({id: exchangeWithNodeRed, change: 'any', ack: false}, function(obj) { console.log(obj.state.val); const val = JSON.parse(obj.state.val); let returnResult; //console.log('adapter: ' + val.adapter); //console.log('command: ' + val.command); //console.log('message: ' + val.message); //console.log('result: ' + val.result); if (!val.result) returnResult = exchangeWithNodeRed; else returnResult = val.result; if (!val.command){ //console.log('no command'); sendTo(val.adapter, val.message, function(result) { if (result.error) setState(returnResult, result.error, true); else setState(returnResult, JSON.stringify(result.result), true); }); } else { //console.log('command: ' + val.command); sendTo(val.adapter, val.command, val.message? val.message : {}, function(result) { if (result.error) setState(returnResult, result.error, true); else setState(returnResult, JSON.stringify(result.result), true); }); } });
-
@sineos Aha - Muss dass dann ein leeres Objekt sein oder kann es auch ein leerer String sein? - nur halt nicht undefined nehme ich mal an.
-
@mickym Das ist wohl ein bisschen abhängig davon, was der Adapter erwartet. Die homematic
rssiInfo
Methode erwartet ein Objekt. -
@sineos Dann würde ich aber nicht den JS Code ändern, sondern belassen und aus NodeRed ein leeres Objekt als message mitgeben.
-
@mickym Stimmt, auch ne Möglichkeit. Sogar die flexiblere.