Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. Node-Red
  5. Ext. NodeJS Module in Node-Red on the fly nutzen (ab V 3.0)

NEWS

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    8.6k

  • Monatsrückblick – September 2025
    BluefoxB
    Bluefox
    13
    1
    2.1k

  • Neues Video "KI im Smart Home" - ioBroker plus n8n
    BluefoxB
    Bluefox
    16
    1
    2.9k

Ext. NodeJS Module in Node-Red on the fly nutzen (ab V 3.0)

Geplant Angeheftet Gesperrt Verschoben Node-Red
node-red
1 Beiträge 1 Kommentatoren 483 Aufrufe 1 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • mickymM Offline
    mickymM Offline
    mickym
    Most Active
    schrieb am zuletzt editiert von mickym
    #1

    Ab dem Node-Red Adapter 3.0 beinhaltet dieser eine Node-Red Version höher als 2.x, die es sehr einfach macht externe NodeJS Module zu nutzen. Allerdings sollte man doch einiges beachten, um sein System hier nicht "kaputt" zu installieren.

    Da ich im Hauptthread zum Node-Red Adapter 3.0.0 angekündigt hatte, ein paar Beispiele vorzustellen, will ich das hier in einem gesonderten Thread machen, um den bestehenden Thread nicht zu überfrachten.

    Im Hauptthread hatte ich ja folgendes geschrieben:

    Eines der gewaltigsten Vorteile der NR Version 2 ist die Installation und das Nutzen externer NodeJS Module in Function Nodes ohne diese installieren zu müssen!!!

    Die Module müssen also nicht in der settings.js bekannt gemacht werden und werden automatisch im

    /opt/iobroker/iobroker-data/node-red/node_modules Verzeichnis installiert. Es ist KEINE Installation über Adaptereinstellungen oder über die Kommandozeile erforderlich.

    Die Module können einfach in den function Nodes über den Setup-Tab bekannt gemacht werden:

    16316e33-96d0-4109-b071-bac6108e053c-image.png

    Ein Beispiel gibts ggf. noch in einem der Anschlussposting.

    Und um diese Beispiele soll es nun in diesem Thread gehen.

    Zuerst jedoch einmal ein paar grundsätzliche Bemerkungen, die zu beachten sind, dass man sich eben nicht lauter Mist in sein System holt und sich wundert oder sogar Konflikte entstehen. Außerdem sollte man sich beim Einsatz externer Module im Klaren sein, dass man damit sein gesamtes NodeRed System "abschiessen" kann - das habe ich nun selbst erlebt.

    Alle Module die "on the fly" installiert werden, müssen manuell wieder deinstalliert werden!

    Deshalb sollte man sich ggf. mal seine node_module Verzeichnis anschauen, was da alles installiert wurde.

    Ich habe mal 3 Beispiele vorbereitet, wie sich externe NodeJS Module sinnvoll nutzen lassen - und zwar mit Hilfe der neuen Funktion in den function Nodes.

    1. Beispiel: - Information über YouTube Videos mit youtube-dl-exec (hier der Link: https://www.npmjs.com/package/youtube-dl-exec)

    Anregung kam aus einem Thread eines Users, der diese Bibliothek zum Laufen bringen wollte - das ging auch, wenn auch die in dem Thread vorgestellte Lösung nicht mehr funktioniert und das System bei Fehleingaben zum Absturz bringt.

    Anhand dieses ersten Beispiels gehe ich auf die grundsätzliche Vorgehensweise ein - auch wenn sich diese je nach Bibliothek ggf. ändern kann.

    Installation:
    Um eine externe Bibliothek in NodeRed in einer function Node zu nutzen - muss man diese nur noch im Setup Tab eingetragen werden - zusammen mit einer Variable wie das dann in der function Node referenziert wird.

    13e27606-afe9-400a-aef6-51f55bf662ae-image.png

    In dem Screenshot grün ist der Name der Bibliothek - die Variable rechts zeigt die Variable mit der ich diese Bibliothek anspreche.

    Ob ich das direkt machen kann, ich einen Konstruktur verwenden muss und ein neues Objekt erstellen muss - kann man nicht allgemeingültig sagen, das hängt ja von der Bibliothek ab.

    Sobald man diese function Node übernimmt/deployed - beginnt im Hintergrund schon die Installation. Man muss also erst mal nicht auf die Kommandozeile oder in irgend sonst einer Konfiguration das Modul bekannt machen. Man kann aber auch nicht erwarten, dass die function Node deshalb schon ein paar Sekunden nach dem Deploy funktioniert, sondern sollte dem Ganzen schon etwas Zeit lassen, je nachdem wie groß die Bibliothek ist.

    Diese Setup Tab - entspricht im Wesentlichen im JS das require("NodeJS Name").

    Man sieht dass alle benötigten Module im Hintergrund installiert wurden:

    39a47c57-622a-4098-a6b8-cca6baca56de-image.png

    Wichtig ist auch die ganzen Module werden im NodeRed Datenverzeichnis installiert.
    /opt/iobroker/iobroker-data/node-red/node_modules

    Deinstallation

    Nun greife ich gleich mal der Deinstallation vor - zum Glück muss man nicht jedes Modul einzeln deinstallieren, sondern auch hier werden die Abhängigkeiten erkannt.

    Als erstes muss man natürlich alle function Nodes entfernen, die diese externen Bibliotheken nutzen. Es langt auch wenn man diese function Nodes deaktiviert. :) Sobald diese aber wieder aktiviert werden, beginnt die Installation im Hintergrund.

    Um das sauber zu deinstallieren, macht man dies am Besten auf der Kommandozeile seines iobrokers.

    Um keine Rechteprobleme zu bekommen, deinstalliert man die Module am Besten auch unter der Kennung des iobrokers, da sie ja mit dieser auch installiert wurden:

    Man gibt also folgende Kommandos ein:

    sudo -su iobroker
    cd /opt/iobroker/iobroker-data/node-red
    npm remove youtube-dl-exec
    
    1. Mit dem ersten Kommando - arbeitet man mit den folgenden Befehlen unter der Benutzerkennung des iobrokers.
    2. Man wechselt in das Datenverzeichnis des iobrokers und hier in node-red
    3. Dort entfernt man dann die installierte Bibliothek mit npm remove "Bibliotheksname"

    Man sieht, dass alle Verzeichnisse wieder gelöscht und entfernt wurden. Das System ist also wieder sauber.

    e77a2417-be7e-4aed-801d-cbe5b83807e9-image.png

    Sobald man ggf. vorhandene function Nodes, die diese Bibliothek wieder referenziert, aktiviert und deployed, erfolgt erneut die Installation. (Man achte auf die Zeitstempel der Verzeichnisse)

    b97e6fa1-8ca5-4995-92bb-fef9ea754dfc-image.png

    Betrieb

    Nun bin ich nur auf Zufall durch diesen Thread auf die Bibliothek gestoßen und sie wurde in erster Linie dazu genutzt, um Informationen von YouTube Videos zu erhalten. (Clicks etc.).

    Der ursprüngliche (wie in dem referenzierten Thread) Code hat nur soweit funktioniert, wenn die Eingabe des YouTube Videos korrekt ist.

    Der Flow ist ziemlich einfach - man gibt als payload die URL des YouTube- Videos mit und erhält die entsprechenden Informationen.

    56000c5e-3990-4267-b680-dbea28318559-image.png

    Der Abruf arbeitet asynchron und als Promise-Objekt. Ich bin leider nicht so sattelfest in Javascript, aber soweit ich verstanden habe, gibt dieses Objekt quasi ein Signal, wenn es mit der asynchronen Verarbeitung fertig ist.

    Wie man sieht habe ich in der ersten Inject Node ein fehlerhaften Aufruf (nicht existierende URL) eingegeben.

    Mit dem ursprünglichen Code ist Node-Red komplett abgestürzt:

    const myTimeout = setTimeout(NoReturn, 10000);
    
    youtubeDlExec(msg.payload, {dumpSingleJson: true}).then(output => {
        msg.payload = output;
         clearTimeout(myTimeout);
        node.send(msg);
        node.done();
        });
    
    
    function NoReturn(){
     msg.payload = "No response";
     node.send(msg);
     node.done();
    }
    
    

    wobei dieser Code noch vor ein paar Wochen funktioniert hat. Bei einem fehlerhaften Aufruf, hat einfach ein Timer zugeschlagen, der dann zurückgegeben hat, dass keine Rückmeldung kam. Die asynchrone Verarbeitung sieht man einfach daran, dass das Nachrichtenobjekt nicht mit return (return msg) zurückgegeben wird, sondern über node.send(msg). Die Abarbeitung der function Node ist also schon längst fertig, während im Hintergrund noch die Informationen zu dem Video abgerufen werden.

    Das geht heute nicht mehr - sondern dieser Code erzeugt einen kompletten Absturz von Node-Red!!!!

    Im iobroker - wird aber die Ursache geloggt - so schaut dann so ein Komplettabsturz aus:

    2022-03-16 11:29:11.304 - error: node-red.0 (19800) Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().
    2022-03-16 11:29:11.314 - error: node-red.0 (19800) unhandled promise rejection: Command failed with exit code 1: /opt/iobroker/iobroker-data/node-red/node_modules/youtube-dl-exec/bin/youtube-dl https://www.youtube.com/watch?v=Err6xKWiCMKKJg --dump-single-json
    ERROR: Video unavailable
    2022-03-16 11:29:11.315 - error: node-red.0 (19800) Error: Command failed with exit code 1: /opt/iobroker/iobroker-data/node-red/node_modules/youtube-dl-exec/bin/youtube-dl https://www.youtube.com/watch?v=Err6xKWiCMKKJg --dump-single-json
    ERROR: Video unavailable
    at makeError (/opt/iobroker/iobroker-data/node-red/node_modules/execa/lib/error.js:60:11)
    at handlePromise (/opt/iobroker/iobroker-data/node-red/node_modules/execa/index.js:118:26)
    at runMicrotasks ()
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    2022-03-16 11:29:11.316 - error: node-red.0 (19800) Command failed with exit code 1: /opt/iobroker/iobroker-data/node-red/node_modules/youtube-dl-exec/bin/youtube-dl https://www.youtube.com/watch?v=Err6xKWiCMKKJg --dump-single-json
    ERROR: Video unavailable
    2022-03-16 11:29:12.005 - info: node-red.0 (19800) terminating
    2022-03-16 11:29:12.007 - warn: node-red.0 (19800) Terminated (UNCAUGHT_EXCEPTION): Without reason
    2022-03-16 11:29:13.132 - info: node-red.0 (19800) terminating with timeout
    2022-03-16 11:29:14.052 - warn: node-red.0 (19800) get state error: Connection is closed.
    2022-03-16 11:29:14.054 - warn: node-red.0 (19800) get state error: Connection is closed.
    2022-03-16 11:29:14.065 - warn: node-red.0 (19800) get state error: Connection is closed.
    2022-03-16 11:29:14.149 - warn: node-red.0 (19800) Could not perform strict object check of state 0_userdata.0.stromverbrauch.shellies.shellyplug-s-7AE344_0.power: DB closed
    2022-03-16 11:29:14.150 - warn: node-red.0 (19800) get state error: Connection is closed.
    2022-03-16 11:29:14.970 - info: node-red.0 (19770) node-red exited with 6
    2022-03-16 11:29:19.972 - info: node-red.0 (19770) Starting node-red: --max-old-space-size=512 /opt/iobroker/node_modules/iobroker.node-red/node_modules/node-red/red.js -v --settings /opt/iobroker/iobroker-data/node-red/settings.js
    2022-03-16 11:29:36.406 - warn: node-red.0 (25737) slow connection to states DB. Still waiting ...
    

    Und im Node-Red Forum hat mir dann der NodeRed Guru geholfen, wie man sowas abfängt. Das ist also insbesondere wichtig bei promise Objekten und asynchroner Verarbeitung.

    Um also solche Abstürze zu vermeiden, kann man diesen Promise Objekten - direkt ein catch Objekt anhängen!!!

    Der Code der also fehlertolerant ist, sieht nun so aus:

    youtubeDlExec(msg.payload, {dumpSingleJson: true}).then(output => {
        msg.payload = output;
        node.send(msg);
        node.done();
        }).catch(err => {
        // An error has been returned by the Promise.
        // Do something with it...
        msg.payload = {name: err.name, 
                message:err.message};
        node.send(msg);
        node.done();
    });
    

    Wichtig ist also direkt nach dem Aufruf ein

    .catch(err =>{})
    

    anzuschließen.

    Der Fehler wird nun sauber abgefangen und innerhalb von NodeRed ausgegeben:

    12cb95ff-07f9-46da-9b59-f519d545290b-image.png

    Damit wäre mein 1. Beispiel fertig.

    2. Beispiel : Das 2. Beispiel ist in meinen Augen eine ganz pfiffige Bibliothek mit der man JSON Dateien, wie eine kleine Datenbank behandeln kann.

    Normalerweise speichern wir ja unsere Daten, die auch einen Stromausfall überleben sollen in mqtt oder eben im iobroker. Wer jedoch gerne auch mal seine eigenen Konfigurationsdateien außerhalb irgendwelcher Systeme in reinen JSON Dateien speichern möchte, für den gibt es hier eine pfiffige NodeJS Lösung, die sich node-json-db nennt.

    Hier die Beschreibung mit den Möglichkeiten: https://www.npmjs.com/package/node-json-db

    Installation:

    Dieses Mal benötigt man 2 Zugänge zu einer Bibliothek (wie man aus dem JS Code unter npm) erkennen kann.

    559b4dbb-eb52-43d3-a409-f5c40cbf75fa-image.png

    Einmal für die Bibliothek selbst und einmal für die Konfiguration der Datenbank bzw. JSON Datei.

    Die Anzahl der installierten Module und Abhängigkeiten ist dieses Mal gering:

    592758c4-2c38-4e48-b043-1654358cfebb-image.png

    Deinstallation:

    hier gehe ich nicht mehr im Detail darauf ein - ist wie beim 1. Beispiel

    Betrieb

    Den Code in der function Node habe ich auch versucht sehr übersichtlich zu gestalten:

    const JsonDB = nodeJsonDb.JsonDB;
    const Config = nodeJsonDbDistLibJsonDBConfig.Config;
    
    var db = new JsonDB(new Config("/home/iobroker/mwDataBase", true, true, '/'));
    
    msg.topic = msg.topic || '/';
    
    if (msg.payload) db.push(msg.topic, msg.payload,false);
    if (msg.delete) db.delete(msg.delete);
    msg.payload = db.getData(msg.topic);
    
    return msg
    

    Die ersten beiden Zeilen erstellen die benötigten Objekte, um dann über einen Konstruktur die Datenbank - sprich die JSON Datei als neues Objekt zu erzeugen.
    Die Datei habe ich in das Homeverzeichnis des iobrokers gelegt, da dieser darauf in jedem Fall Schreibrechte hat.

    Die Datenbank heißt also mwDataBase.json

    933ca470-f337-4174-9bbe-371389e2018c-image.png

    Der Code ist wie gesagt sehr einfach gehalten:

    1. Wird nur ein msg.topic mitgegeben, dann wird dieses topic aus der Datenbank ausgelesen und in der msg.payload ausgegeben.
    2. Wird ein msg.topic und ein msg.payload mit gegeben, wird die msg.payload an die Stelle des msg.topics in der Datei geschrieben und die msg. payload ausgegeben.
    3. Wird ein topic in einer msg.delete Eigenschaft mitgegeben, so wird dieses topic gelöscht.

    Das Teil kann zwar noch mehr, aber für Demonstationszwecke reichen diese Tasks.

    Als Anwendungsfälle habe ich nun 2 Konfigurationsdatenblöcke, die mir

    1. bei meinem TV (Sky-Q) - zu dem Sendernamen, die Favoritennummer ausspuken soll
      und
    2. einem Shelly - Bezeichnung in einen benutzerfreundlichen Namen übersetzen soll (ich weiß, kann man im Shelly selbst konfigurieren).

    Das Schreiben in die Datenbank schaut also mittels einer Inject Node so aus:

    bbb31cd7-c16f-4dd2-92d7-6e94cde131a0-image.png

    das Schreiben in einen iobroker-Out Datenpunkt - spare ich mir. ;)

    Die JSON Datei zu den Sendernamen, sieht wie folgt aus:

    ce2194b4-3e02-4bbf-8252-665722af7dd8-image.png

    Um nun den Favoriten- bzw. Sendeplatz vom z.Bsp des ZDF zu ermitteln gebe ich in einer payload ZDF mit (das gesamte JSON Objekt, das ich normalerweise aus dem iobroker DP auslesen würde, befindet sich in msg.sender).

    1a6c754e-3c4d-4aed-83fa-8adf587f52d6-image.png

    [
       {
           "id": "9a26b0a9c920864e",
           "type": "change",
           "z": "54b226bc.0793e8",
           "name": "",
           "rules": [
               {
                   "t": "set",
                   "p": "favoritenNummer",
                   "pt": "msg",
                   "to": "    $lookup(sender,payload)\t",
                   "tot": "jsonata"
               }
           ],
           "action": "",
           "property": "",
           "from": "",
           "to": "",
           "reg": false,
           "x": 2000,
           "y": 3120,
           "wires": [
               [
                   "df56c9f2ded71800"
               ]
           ]
       },
       {
           "id": "95f5a56ef277ba39",
           "type": "inject",
           "z": "54b226bc.0793e8",
           "name": "",
           "props": [
               {
                   "p": "sender",
                   "v": "{\"Sky Cinema Premieren\":1,\"Sky Cinema Premieren  24\":2,\"Sky Cinema Thriller HD\":3,\"Sky Cinema Action\":4,\"Sky Replay\":5,\"Sky Cinema Fun\":6,\"Sky Cinema Best Of\":7,\"Sky One HD\":8,\"Sky Cinema Special HD\":9,\"Sky Cinema Classics\":10,\"Sky Cinema Family\":11,\"National Geographic\":12,\"Discovery Channel\":13,\"Spiegel Geschichte\":14,\"Sky Documentaries\":15,\"13th Street\":16,\"Sky Krimi HD\":17,\"Syfy\":18,\"Heimatkanal\":19,\"Universal\":20,\"Disney Channel\":21,\"WarnerTV Film\":22,\"WarnerTV Serie\":23,\"WarnerTV Comedy\":24,\"Sky Nature\":25,\"Nat Geo Wild\":26,\"Sky Atlantic HD\":27,\"Romance TV\":28,\"ARD HD\":31,\"ZDF\":32,\"BR\":33,\"Tele5\":35,\"Sat1\":36,\"Pro7\":37,\"RTL\":38,\"RTL II\":39,\"SuperRTL\":40,\"Kabel eins\":41,\"VOX\":42,\"3Sat\":43,\"Arte HD\":44,\"ORF2\":45,\"Kika\":46,\"Nickelodeon\":47,\"VOXup\":48,\"ARD Alpha\":49,\"Tagesschau24 HD\":50,\"ONE\":51,\"ZDF info\":52,\"ZDF_neo\":53,\"Phoenix\":54,\"WELT\":55,\"Welt der Wunder TV\":56,\"Kabel eins Doku\":57,\"n-tv\":58,\"WDR\":60,\"NDR\":61,\"MDR\":63,\"HR\":64,\"SWR BW\":65,\"RBB\":66,\"RTL up\":67,\"Nitro HD\":68,\"Sat1 Gold\":69,\"Sixx HD\":70,\"ServusTV\":71,\"ProSieben Maxx\":72,\"DMAX\":73,\"TLC\":74,\"HSE24\":79,\"QVC\":81,\"Comedy Central\":90,\"Bibel TV\":88,\"Bild\":75,\"HSE24 Extra\":80,\"QVC2\":82}",
                   "vt": "json"
               },
               {
                   "p": "payload"
               }
           ],
           "repeat": "",
           "crontab": "",
           "once": false,
           "onceDelay": 0.1,
           "topic": "",
           "payload": "ZDF",
           "payloadType": "str",
           "x": 1790,
           "y": 3120,
           "wires": [
               [
                   "9a26b0a9c920864e"
               ]
           ]
       },
       {
           "id": "df56c9f2ded71800",
           "type": "debug",
           "z": "54b226bc.0793e8",
           "name": "favoritenNummer",
           "active": true,
           "tosidebar": true,
           "console": false,
           "tostatus": false,
           "complete": "favoritenNummer",
           "targetType": "msg",
           "statusVal": "",
           "statusType": "auto",
           "x": 2250,
           "y": 3120,
           "wires": []
       }
    ]
    

    Über die JSON-DB Bibliothek gebe ich einfach den Pfad zum ZDF mit, um den Senderplatz zu ermitteln:

    9a7643ad-a85e-4ed4-89d8-1615508f5a67-image.png

    Der 2. Anwendungsfall ist nun, dass mir die JSON String einen benutzerfreundlichen Namen für einen Shelly ausgeben soll.
    Außerdem habe ich als Beispiel einen Shelly 2.5 mit 2 Relais ausgesucht.

    In der Datenbank schaut die Struktur nun so aus:

    0ced57b1-da5a-4162-a86a-9d2833f0f91a-image.png

    Über den Relais Index möchte ich nun den entsprechenden Namen herausbekommen.

    Mitgegeben wird nur der Name und der Index. Hier zu Demozwecken natürlich das gesamte Objekt (friendlyNames), was man in der Regel sonst aus dem iobroker ausliest:

    13dd06a0-31e8-444f-b011-d152c92f30bd-image.png

    Hier war es schon etwas mehr tricky den richtigen JSONATA Ausdruck zu finden.

    funktioniert aber wie folgt:

    1a3d97a5-a9fe-47ff-9d47-b56abd9639b3-image.png

    Hier mal wieder als kleiner Flow:

    [
       {
           "id": "8191ca7062b96101",
           "type": "inject",
           "z": "54b226bc.0793e8",
           "name": "",
           "props": [
               {
                   "p": "topic",
                   "vt": "str"
               },
               {
                   "p": "name",
                   "v": "shellyswitch25-FFFFF9",
                   "vt": "str"
               },
               {
                   "p": "index",
                   "v": "1",
                   "vt": "num"
               },
               {
                   "p": "friendlyNames",
                   "v": "{\"shellyswitch25-FFFFF0\":[\"Schalter Bad Deckenlicht\",\"Schalter Bad Abzug\"],\"shellyplug-s-FFFFF1\":[\"Steckdose Büro Computertischlampe\"],\"shellydimmer-FFFFF2\":[\"Dimmer Büro Deckenlicht\"],\"shellyplug-s-FFFFF3\":[\"Steckdose Büro Fritz Box 4040\"],\"shellyplug-s-FFFFF4\":[\"Steckdose Büro Max Cube\"],\"shellyplug-s-FFFFF5\":[\"Steckdose Büro Server MWHome\"],\"shellyplug-s-FFFFF6\":[\"Steckdose Büro Schreibtischlampe\"],\"shelly1pm-FFFFF7\":[\"Schalter Diele Deckenlicht\"],\"shellyswitch25-FFFFF8\":[\"Schalter Flur Deckenlicht\"],\"shellyswitch25-FFFFF9\":[\"Schalter Küche Deckenlicht\",\"Schalter Küche Abzug\"]}",
                   "vt": "json"
               }
           ],
           "repeat": "",
           "crontab": "",
           "once": false,
           "onceDelay": 0.1,
           "topic": "shellyswitch25-FFFFF9_1",
           "x": 2130,
           "y": 2880,
           "wires": [
               [
                   "ea826aacae5b3df2"
               ]
           ]
       },
       {
           "id": "a856ac5330e7c818",
           "type": "inject",
           "z": "54b226bc.0793e8",
           "name": "",
           "props": [
               {
                   "p": "topic",
                   "vt": "str"
               },
               {
                   "p": "name",
                   "v": "shellyswitch25-FFFFF9",
                   "vt": "str"
               },
               {
                   "p": "index",
                   "v": "0",
                   "vt": "num"
               },
               {
                   "p": "friendlyNames",
                   "v": "{\"shellyswitch25-FFFFF0\":[\"Schalter Bad Deckenlicht\",\"Schalter Bad Abzug\"],\"shellyplug-s-FFFFF1\":[\"Steckdose Büro Computertischlampe\"],\"shellydimmer-FFFFF2\":[\"Dimmer Büro Deckenlicht\"],\"shellyplug-s-FFFFF3\":[\"Steckdose Büro Fritz Box 4040\"],\"shellyplug-s-FFFFF4\":[\"Steckdose Büro Max Cube\"],\"shellyplug-s-FFFFF5\":[\"Steckdose Büro Server MWHome\"],\"shellyplug-s-FFFFF6\":[\"Steckdose Büro Schreibtischlampe\"],\"shelly1pm-FFFFF7\":[\"Schalter Diele Deckenlicht\"],\"shellyswitch25-FFFFF8\":[\"Schalter Flur Deckenlicht\"],\"shellyswitch25-FFFFF9\":[\"Schalter Küche Deckenlicht\",\"Schalter Küche Abzug\"]}",
                   "vt": "json"
               }
           ],
           "repeat": "",
           "crontab": "",
           "once": false,
           "onceDelay": 0.1,
           "topic": "shellyswitch25-FFFFF9_0",
           "x": 2130,
           "y": 2840,
           "wires": [
               [
                   "ea826aacae5b3df2"
               ]
           ]
       },
       {
           "id": "ea826aacae5b3df2",
           "type": "change",
           "z": "54b226bc.0793e8",
           "name": "",
           "rules": [
               {
                   "t": "set",
                   "p": "friendlyName",
                   "pt": "msg",
                   "to": "$lookup(friendlyNames,name)[$$.index]",
                   "tot": "jsonata"
               }
           ],
           "action": "",
           "property": "",
           "from": "",
           "to": "",
           "reg": false,
           "x": 2410,
           "y": 2860,
           "wires": [
               [
                   "adc5129a16753d14"
               ]
           ]
       },
       {
           "id": "adc5129a16753d14",
           "type": "debug",
           "z": "54b226bc.0793e8",
           "name": "",
           "active": true,
           "tosidebar": true,
           "console": false,
           "tostatus": false,
           "complete": "friendlyName",
           "targetType": "msg",
           "statusVal": "",
           "statusType": "auto",
           "x": 2650,
           "y": 2860,
           "wires": []
       }
    ]
    

    Über die Bibliothek muss man das Topic nur als String zusammensetzen, wie aus dem folgenden Bild zu erkennen:

    12acb720-2463-425c-86ed-2b80ed34ed62-image.png

    3. Beispiel: Das 3. und letzte Beispiel in diesem Thread bindet nur eine kleine Library ein, um den Taupunkt oder Hitzeprobleme zu berechnen. Natürlich kann man das auch manuell berechnen, aber so wie ich es aus einem Beispiel mal rauskopiert habe, kommen teilweise falsche Werte raus und man sollte diese Ausgangswerte auch in einem Objekt mitführen.

    Sowohl Hitzewarnungen und Taupunkt benötigen Temperatur und Luftfeuchtigkeit.

    Die Bibliotheken sowie die zugrundeliegenden Berechnungen findet man hier:

    Beispiel der Einbindung externer Node.JS Bibliotheken

    @tsmx/weather-tools von https://github.com/tsmx/weather-tools#dew-point-functions

    Hier die verwendeten Berechnungen:

    Hitzindex: https://de.wikipedia.org/wiki/Hitzeindex
    Taupunkt: https://de.wikipedia.org/wiki/Taupunkt

    Installation:

    16438544-7936-4a8f-a197-f50ba46444df-image.png

    Dieses Mal ist es wirklich nur eine Bibliothek, die installiert wird:

    d0b26735-c064-42c3-9634-c2f766aa3675-image.png

    Deinstallation:

    siehe 1. Beispiel

    Zur Deinstallation diesen Befehl eingeben:

    npm remove @tsmx/weather-tools
    

    Betrieb:

    Dieses Mal poste ich den ganzen Flow. Wer ihn importiert, installiert somit auch die @tsmx Bibliothek:

    [
       {
           "id": "444a09a07b10a1ce",
           "type": "function",
           "z": "54b226bc.0793e8",
           "name": "Taupunkt berechnen",
           "func": "var tempf   = context.get('tempf')|| 0;\nvar humidity = context.get('humidity')|| 0;\nvar oldTp = context.get('contexttp') || 0;\nvar tp = 0;\n\nif (msg.topic === \"tempf\") {\n    tempf= msg.payload;\n    context.set('tempf',msg.payload);\n\n} else if (msg.topic === \"humidity\") {\n    humidity= msg.payload;\n  context.set('humidity',msg.payload);\n}\n\n  \n\n// Quelle: https://www.chemie-schule.de/KnowHow/Taupunkt\n// Gültigkeitsbereich Taupunkt:    -30°C <= tmp <= 70°C\nif (!(isNaN(tempf) || isNaN(humidity))) {\n\ttp = ((241.2 * Math.log(humidity/100)) + ((4222.03716 * tempf) / (241.2 + tempf))) / (17.5043 - Math.log(humidity/100) - ((17.5043*tempf)/(241.2+tempf)));\n\ttp = Math.round( tp * 100 ) / 100;\n\tcontext.set(\"contexttp\",tp);\n}\n\nif (oldTp !== tp) { \nmsg.payload = tp;\nmsg.topic = \"Taupunkt\";\nreturn msg }\nelse {\n    return null;\n}\n",
           "outputs": 1,
           "noerr": 0,
           "initialize": "",
           "finalize": "",
           "libs": [],
           "x": 3020,
           "y": 1140,
           "wires": [
               [
                   "f3406afa623991bb"
               ]
           ]
       },
       {
           "id": "f3406afa623991bb",
           "type": "debug",
           "z": "54b226bc.0793e8",
           "name": "Manuelle Taupunkt-Berechnung",
           "active": true,
           "tosidebar": true,
           "console": false,
           "tostatus": false,
           "complete": "payload",
           "targetType": "msg",
           "statusVal": "",
           "statusType": "auto",
           "x": 3290,
           "y": 1140,
           "wires": []
       },
       {
           "id": "07083b40e88f05ad",
           "type": "function",
           "z": "54b226bc.0793e8",
           "name": "Hitzewarnung  & Taupunkt mit @tsmx/weather-tools",
           "func": "var msgNew = {payload: {}};\n\nmsgNew.payload.tauPunkt = wt.dewPoint(msg.payload.temperature, msg.payload.humidity);\n\nvar heatTemp = wt.heatIndexCelsius(msg.payload.temperature, msg.payload.humidity);\n\nswitch (true) {\n    case (isNaN(heatTemp)):\n        msgNew.payload.hitzeWarnung = \"Keine Gefahr\";\n        break;        \n    case (heatTemp < 32):\n        msgNew.payload.hitzeWarnung = \"Vorsicht – Bei längeren Zeiträumen und körperlicher Aktivität kann es zu Erschöpfungserscheinungen kommen.\";\n        break;\n    case (heatTemp < 40):\n        msgNew.payload.hitzeWarnung = \"Erhöhte Vorsicht – Es besteht die Möglichkeit von Hitzeschäden wie Sonnenstich, Hitzekrampf und Hitzekollaps.\";\n        break;\n    case (heatTemp < 54):\n        msgNew.payload.hitzeWarnung = \"Gefahr – Sonnenstich, Hitzekrampf und Hitzekollaps sind wahrscheinlich; Hitzschlag ist möglich.\";\n        break;\n    case (heatTemp >= 54):\n        msgNew.payload.hitzeWarnung = \"Erhöhte Gefahr – Hitzschlag und Sonnenstich sind wahrscheinlich.\";\n        break;\n    default:\n        msgNew.payload.hitzeWarnung = \"Fehler\";\n        break;\n}\n\n\nreturn msgNew;",
           "outputs": 1,
           "noerr": 0,
           "initialize": "",
           "finalize": "",
           "libs": [
               {
                   "var": "wt",
                   "module": "@tsmx/weather-tools"
               }
           ],
           "x": 2890,
           "y": 1300,
           "wires": [
               [
                   "e7cb635656575f24",
                   "674983534632156c"
               ]
           ],
           "info": "Beispiel der Einbindung externer Node.JS Bibliotheken\n\n@tsmx/weather-tools\n\nvon \n\nhttps://github.com/tsmx/weather-tools#dew-point-functions\n\nHier die verwendeten Formeln:\n\nHitzindex: \nhttps://de.wikipedia.org/wiki/Hitzeindex\n\nTaupunkt:\nhttps://de.wikipedia.org/wiki/Taupunkt\n"
       },
       {
           "id": "e7cb635656575f24",
           "type": "debug",
           "z": "54b226bc.0793e8",
           "name": "Hitzewarnung  & Taupunkt @tsmx/weather-tools",
           "active": false,
           "tosidebar": true,
           "console": false,
           "tostatus": false,
           "complete": "payload",
           "targetType": "msg",
           "statusVal": "",
           "statusType": "auto",
           "x": 3320,
           "y": 1280,
           "wires": []
       },
       {
           "id": "1988eade4611f9d6",
           "type": "change",
           "z": "54b226bc.0793e8",
           "name": "tempf",
           "rules": [
               {
                   "t": "set",
                   "p": "topic",
                   "pt": "msg",
                   "to": "tempf",
                   "tot": "str"
               },
               {
                   "t": "set",
                   "p": "payload",
                   "pt": "msg",
                   "to": "payload.temperature",
                   "tot": "msg"
               }
           ],
           "action": "",
           "property": "",
           "from": "",
           "to": "",
           "reg": false,
           "x": 2830,
           "y": 1160,
           "wires": [
               [
                   "444a09a07b10a1ce"
               ]
           ]
       },
       {
           "id": "0dd946a708f85410",
           "type": "change",
           "z": "54b226bc.0793e8",
           "name": "humidity",
           "rules": [
               {
                   "t": "set",
                   "p": "topic",
                   "pt": "msg",
                   "to": "humidity",
                   "tot": "str"
               },
               {
                   "t": "set",
                   "p": "payload",
                   "pt": "msg",
                   "to": "payload.humidity",
                   "tot": "msg"
               }
           ],
           "action": "",
           "property": "",
           "from": "",
           "to": "",
           "reg": false,
           "x": 2820,
           "y": 1120,
           "wires": [
               [
                   "444a09a07b10a1ce"
               ]
           ]
       },
       {
           "id": "674983534632156c",
           "type": "debug",
           "z": "54b226bc.0793e8",
           "name": "Taupunkt aus @tsmx/Weathertools Libary",
           "active": true,
           "tosidebar": true,
           "console": false,
           "tostatus": false,
           "complete": "payload.tauPunkt",
           "targetType": "msg",
           "statusVal": "",
           "statusType": "auto",
           "x": 3300,
           "y": 1320,
           "wires": []
       }
    ]
    

    Das erste ist eine manuelle Berechnung für den Taupunkt (die ich irgendwoher mal geklaut habe) - wobei die zwar meist stimmt, manchmal kommen auch falsche Werte raus - das Problem ist, dass hier auf 2 Nachrichten gewartet wird, aber trotzdem manchmal was ausgegeben wird.

    Diese relativ komplexe Berechnung des Taupunktes:

    var tempf   = context.get('tempf')|| 0;
    var humidity = context.get('humidity')|| 0;
    var oldTp = context.get('contexttp') || 0;
    var tp = 0;
    
    if (msg.topic === "tempf") {
        tempf= msg.payload;
        context.set('tempf',msg.payload);
    
    } else if (msg.topic === "humidity") {
        humidity= msg.payload;
      context.set('humidity',msg.payload);
    }
    
      
    
    // Quelle: https://www.chemie-schule.de/KnowHow/Taupunkt
    // Gültigkeitsbereich Taupunkt:    -30°C <= tmp <= 70°C
    if (!(isNaN(tempf) || isNaN(humidity))) {
    	tp = ((241.2 * Math.log(humidity/100)) + ((4222.03716 * tempf) / (241.2 + tempf))) / (17.5043 - Math.log(humidity/100) - ((17.5043*tempf)/(241.2+tempf)));
    	tp = Math.round( tp * 100 ) / 100;
    	context.set("contexttp",tp);
    }
    
    if (oldTp !== tp) { 
    msg.payload = tp;
    msg.topic = "Taupunkt";
    return msg }
    else {
        return null;
    }
    

    reduziert sich mit der Library auf einen Einzeiler: ;)

    msgNew.payload.tauPunkt = wt.dewPoint(msg.payload.temperature, msg.payload.humidity)
    

    Wichtig ist halt, dass Temperatur und Luftfeuchte in einem mitgegeben werden.

    9b5df7f6-d3ea-418c-846c-544a0cb86bdb-image.png

    So ich hoffe es macht Spaß, das ggf. nachzuvollziehen und gibt vielleicht für den einen oder anderen ein paar Hinweise zur Umsetzung. Ich lerne ja auch immer noch dazu. ;)

    Jeder Flow bzw. jedes Script, das ich hier poste implementiert jeder auf eigene Gefahr. Flows und Scripts können Fehler aufweisen und weder der Seitenbetreiber noch ich persönlich können hierfür haftbar gemacht werden. Das gleiche gilt für Empfehlungen aller Art.

    1 Antwort Letzte Antwort
    3
    Antworten
    • In einem neuen Thema antworten
    Anmelden zum Antworten
    • Älteste zuerst
    • Neuste zuerst
    • Meiste Stimmen


    Support us

    ioBroker
    Community Adapters
    Donate

    735

    Online

    32.4k

    Benutzer

    81.5k

    Themen

    1.3m

    Beiträge
    Community
    Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
    ioBroker Community 2014-2025
    logo
    • Anmelden

    • Du hast noch kein Konto? Registrieren

    • Anmelden oder registrieren, um zu suchen
    • Erster Beitrag
      Letzter Beitrag
    0
    • Home
    • Aktuell
    • Tags
    • Ungelesen 0
    • Kategorien
    • Unreplied
    • Beliebt
    • GitHub
    • Docu
    • Hilfe