NEWS
Node-red eigene ioBroker Function Node sinnvoll?
-
Hi zusammen,
mein kleines Projekt zum Üben, der Fritzbox Flow, ist mittlerweile aus dem Preview Status raus und wird die Tage veröffentlicht. Es gibt davor noch einiges damit zu tun.
Ein Erkenntnis habe ich daraus. Mit KISS (Keep It Simple Stupid) hat das nichts mehr zu tun. Die Herausforderung in Node-red Proekten ist für mich, die "doppelte Variablen Vorhaltung". D.h. da ich nicht an die Werte in ioBroker rankomme, habe ich das meiste noch einmal als globale Node-red variablen angelegt. Wie man damit eine persistente Konfiguration realisieren will, hat sich mir noch nicht erschlossen.
Was fehlt ist die Möglichkeit auf ioBroker Objekte aktiv zugreifen zu können.
Derzeit erhält man die Werte nur bei Änderung in ioBroker über eine ioBroker input Node. Man braucht aber häufig beim Deploy eines Flows die Objekte, wie sie gerade in ioBroker abgelegt sind.
Die Erkenntnis daraus ist für mich bisher:
Entweder man nutzt ioBroker nur für kleinere "Programme" und hauptsächlich zur Datenverarbeitung (Daten aus einen Webservice abholen -> an ioBroker -> dort per Javascript verarbeiten (Hauptprogramm)) oder es muss eine dritte ioBroker Node her, aus der Kategorie "function Nodes".
Wenn ich nicht ganz falsch liege, sind die beiden ioBroker Nodes (input und Output) innerhalb dieses Projektes geschrieben worden:
https://github.com/ioBroker/ioBroker.no … ster/nodes
ioBroker.js:
/** * Copyright 2013,2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ module.exports = function(RED) { "use strict"; var util = require("util"); var utils = require(__dirname + '/../lib/utils'); //var redis = require("redis"); var hashFieldRE = /^([^=]+)=(.*)$/; // Get the redis address var settings = require(process.env.NODE_RED_HOME+"/red/red").settings; var instance = settings.get("iobrokerInstance") || 0; var config = settings.get("iobrokerConfig"); if (typeof config == 'string') { config = JSON.parse(config); } try { var adapter = utils.adapter({name: 'node-red', instance: instance, config: config}); } catch(e) { console.log(e); } var nodes = []; var ready = false; adapter.on("ready", function () { ready = true; adapter.subscribeForeignStates('*'); while (nodes.length) { var node = nodes.pop(); if (node instanceof IOBrokerInNode) adapter.on('stateChange', node.stateChange); node.status({fill:"green",shape:"dot",text:"connected"}); } }); // name is like system.state, pattern is like "*.state" or "*" or "*system*" function getRegex(pattern) { if (!pattern || pattern == '*') return null; if (pattern.indexOf('*') == -1) return null; if (pattern[pattern.length - 1] != '*') pattern = pattern + '/r>; if (pattern[0] != '*') pattern = '^' + pattern; pattern = pattern.replace(/\*/g, '[a-zA-Z0-9.\s]'); return new RegExp(pattern); } function IOBrokerInNode(n) { var node = this; RED.nodes.createNode(node,n); node.topic = (n.topic || '*').replace(/\//g, '.'); // If no adapter prefix, add own adapter prefix if (node.topic && node.topic.indexOf('.') == -1) { node.topic = adapter.namespace + '.' + node.topic; } node.regex = getRegex(this.topic); node.payloadType = n.payloadType; if (node.topic) { var id = node.topic; // If no wildchars and belongs to this adapter if (id.indexOf('*') == -1 && (id.match("^node-red\." + instance) || id.indexOf('.') != -1)) { adapter.getObject(id, function (err, obj) { if (!obj) { if (adapter.log) { adapter.log.warn('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id)); } else { console.log(('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id))); } // Create object adapter.setObject(id, { common: { name: id, role: 'info' }, native: {}, type: 'state' }, function (err, obj) { adapter.setState(id, undefined); }); } }); } } if (ready) { node.status({fill:"green",shape:"dot",text:"connected"}); } else { node.status({fill:"red",shape:"ring",text:"disconnected"},true); } node.stateChange = function(topic, obj) { if (node.regex) { if (!node.regex.exec(topic)) return; } else if (node.topic != '*' && node.topic != topic) { return; } node.send({ topic: topic.replace(/\./g, '/'), payload: (node.payloadType == 'object') ? obj : (obj.val === null || obj.val === undefined) ? '' : obj.val.toString(), acknowledged:obj.ack, timestamp: obj.ts, lastchange: obj.lc, from: obj.from }); }; node.on('close', function() { adapter.removeListener('stateChange', node.stateChange); }); if (ready) { adapter.on('stateChange', node.stateChange); } else { nodes.push(node); } } RED.nodes.registerType("ioBroker in",IOBrokerInNode); function IOBrokerOutNode(n) { var node = this; RED.nodes.createNode(node,n); node.topic = n.topic; node.ack = (n.ack === "true" || n.ack === true); node.autoCreate = (n.autoCreate === "true" || n.autoCreate === true); node.regex = new RegExp("^node-red\." + instance); // Create variable if not exists if (node.autoCreate && node.topic) { var id = node.topic.replace(/\//g, '.'); // If no wildchars and belongs to this adapter if (id.indexOf('*') == -1 && (node.regex.exec(id) || id.indexOf('.') != -1)) { adapter.getObject(node.topic, function (err, obj) { if (!obj) { if (adapter.log) { adapter.log.warn('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id)); } else { console.log('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id)); } // Create object adapter.setObject(id, { common: { name: id, role: 'info' }, native: {}, type: 'state' }, function (err, obj) { adapter.setState(id, undefined); node.idChecked = true; }); } else { node.idChecked = true; } }); } } if (ready) { node.status({fill:"green",shape:"dot",text:"connected"}); } else { node.status({fill:"red",shape:"ring",text:"disconnected"},true); } function setState(id, val, ack) { if (id == node.topic && node.idChecked) { if (val != '__create__') { adapter.setState(id, {val: val, ack: ack}); } } else { adapter.getObject(id, function (err, obj) { if (!obj) { if (!node.autoCreate) { if (adapter.log) { adapter.log.warn('State "' + id + '" does not exist in the ioBroker.'); } else { console.log('State "' + id + '" does not exist in the ioBroker.'); } return; } if (adapter.log) { adapter.log.warn('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id)); } else { console.log('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id)); } // Create object adapter.setObject(id, { common: { name: id, role: 'info' }, native: {}, type: 'state' }, function (err, obj) { if (val != '__create__') { adapter.setState(id, {val: val, ack: ack}); } else { adapter.setState(id, {val: undefined, ack: ack}); } }); } else { if (val != '__create__') { adapter.setState(id, {val: val, ack: ack}); } } }); } } node.on("input", function(msg) { var id = node.topic || msg.topic; if (id) { id = id.replace(/\//g, '.'); // If not this adapter state if (!node.regex.exec(id) && id.indexOf('.') != -1) { // Check if state exists adapter.getForeignState(id, function (err, state) { if (!err && state) { adapter.setForeignState(id, {val: msg.payload, ack: node.ack}); } else { if (adapter.log) { adapter.log.warn('State "' + id + '" does not exist in the ioBroker'); } else { console.log('State "' + id + '" does not exist in the ioBroker') } } }); } else { if (id.indexOf('*') != -1) { if (adapter.log) { adapter.log.warn('Invalid topic name "' + id + '" for ioBroker'); } else { console.log('Invalid topic name "' + id + '" for ioBroker'); } } else { setState(id, msg.payload, node.ack); } } } else { node.warn("No key or topic set"); } }); if (!ready) { nodes.push(node); } //node.on("close", function() { // // }); } RED.nodes.registerType("ioBroker out",IOBrokerOutNode); }
Wie man Nodes selbst entwickelt wird auf der Node-red Seite beschrieben:
http://nodered.org/docs/creating-nodes/first-node.html
Jetzt ist es doch theoretisch denkbar, eine eigene dritte ioBroker Node dazu zupacken, die einen Input hat, um aus den Flow angetriggert zu werden, dann ein ioBroker Objekt ausliest und dieses dann an einem Output ausgibt, oder?
Ich hab einen kurzen Blick in den Javascriptcode der vorhandenen beiden Nodes geworfen und es schnell sein lassen. Regex, wohin das Auge sieht
Und die Kurzformen, z.B. von if Anweisungen sind auch nicht meins.
Jetzt meine Fragen dazu:
-
ist so eine Node sinnvoll und kann diese über eine Erweiterung von ioBroker.js erstellt werden?
-
habe ich eine Möglichkeit übersehen, persistente Informationen zwischen ioBroker und einem Flow zu nutzen?
(Werte, die über VIS geändert werden und auf die ich nach einem Deploy zugreifen kann?)
-
kann man komplexe ioBroker Programme mit Node-red entwickeln oder besser als Datensammler und den Rest im Javascript Adapter?
-
wer ist fitter in JS und kann sich an einer ioBroker function node versuchen? :mrgreen:
-
-
` > Jetzt meine Fragen dazu:
- ist so eine Node sinnvoll und kann diese über eine Erweiterung von ioBroker.js erstellt werden?
Ich habe erstaunt beobachtet, wie du die komplexe JavaScript Formel in node-red implementierst. Nun, IMHO, node-red ist dafür schlecht geeignet. Es ist viel besser und einfacher einen ioBroker-Adapter zu schreiben. Da hast du die Variablen und JavaScript ohne Ende.
> - habe ich eine Möglichkeit übersehen, persistente Informationen zwischen ioBroker und einem Flow zu nutzen?
(Werte, die über VIS geändert werden und auf die ich nach einem Deploy zugreifen kann?) `
Es gibt tatsächlich keine Möglichkeit die States aus ioBroker direkt zu lesen. node-red ist als "stateless" gedacht.> - kann man komplexe ioBroker Programme mit Node-red entwickeln oder besser als Datensammler und den Rest im Javascript Adapter?
Lieber nicht. Node-red nur um was einfaches auszuprobieren oder ein Prototyp zu bauen.> - wer ist fitter in JS und kann sich an einer ioBroker function node versuchen? ;-) ;-) ;-) :mrgreen:
Wenn du solche komplexe flows schreiben kannst, dann bist du schon fit genug. Schreibe doch ioBroker.fritzbox :!: :idea:
Ich werde helfen. 8-) Du stellst die Fragen ich beantworte die nach und nach.
- ist so eine Node sinnvoll und kann diese über eine Erweiterung von ioBroker.js erstellt werden?
-
Guten Morgen Bluefox,
danke für die Rückmeldung!
Die Erkenntnis hätte ich früher haben müssen, dass größere "Programme" nicht der Anwendungsfall für Node-red sind
Es ist aber super für den Einstieg in die Programmierung. Die Flows ermöglichen eine gute Übersicht, wo es im Programm gerade lang geht und die Debug-Nodes sind einfach genial.
Ich werde jetzt den Fritzbox-Flow fertigstellen. Das wird noch 2 bis 3 Wochen dauern. Es kommen trotz der simplen Sache, immer neue Ideen hinzu.
Als echte Anwendung brauch das, in Zusammenhang mit der Fritzbox, kein Mensch. Es st halt ein Hobby
Während ich meinen Eintrag oben schrieb, bin ich schon zu dem Entschluss gekommen, dass ich danach den Fritzbox Flow in den Javascript Adapter als Script portiere. Das werde ich nun überspringen.
Nach Deiner Antwort sieht mein neuer Plan nun so aus:
1.) den Flow fertig stellen (ca. 2-3 Wochen)
2.) mich an einem Adapter versuchen
Mal sehen, ob ich damit zurechtkomme. Der Call-Generator dürfte schon einmal eine große Hilfe sein.
Eine Hürde wird sein, den tcp Stream zu erhalten.
Das wird "klein, bunt und hässlich" :mrgreen:
Schöner Code wird dabei nicht rauskommen. Und es wird etwas dauern. Aber man braucht ja Ziele
Konzentriert man sich bei einem Adapter auf das Wesentliche (die Anbindung und das Liefern der Daten)? Oder ist das auch der Ort, um "mehr" zu bieten? KISS scheint noch nicht ganz mein Ding zu sein…
-
Guten Morgen Bluefox,
Es ist aber super für den Einstieg in die Programmierung. Die Flows ermöglichen eine gute Übersicht, wo es im Programm gerade lang geht und die Debug-Nodes sind einfach genial. `
Na. ja…. Du hast noch WebStorm-Debug nicht gesehen. :lol: Dann wirst du denken, dass node-red-debug primitiv ist.Ich werde jetzt den Fritzbox-Flow fertigstellen. Das wird noch 2 bis 3 Wochen dauern. Es kommen trotz der simplen Sache, immer neue Ideen hinzu.
Als echte Anwendung brauch das, in Zusammenhang mit der Fritzbox, kein Mensch. Es st halt ein Hobby `
Hauptsache konstant daran arbeiten. Ich bin schon an ioBroker fast 1 Jahr und vorher CCU.IO 2 Jahre…. Auch nicht gedacht.Während ich meinen Eintrag oben schrieb, bin ich schon zu dem Entschluss gekommen, dass ich danach den Fritzbox Flow in den Javascript Adapter als Script portiere. Das werde ich nun überspringen. `
Ich hoffe, du hast richtig verstanden, dass ich nicht iobroker.Javascript gemeint habe. Obwohl ein JS Script ist auch gut auf dem Weg zur VollkommenheitNach Deiner Antwort sieht mein neuer Plan nun so aus:
1.) den Flow fertig stellen (ca. 2-3 Wochen)
2.) mich an einem Adapter versuchen `
JA :!: Gib uns iobroker.fritzbox (mit Phonebook import )Mal sehen, ob ich damit zurechtkomme. Der Call-Generator dürfte schon einmal eine große Hilfe sein.
Eine Hürde wird sein, den tcp Stream zu erhalten. `
Kannst du das Problem mit TCP beschreiben?Das wird "klein, bunt und hässlich" :mrgreen:
Schöner Code wird dabei nicht rauskommen. Und es wird etwas dauern. Aber man braucht ja Ziele `
So lange es funktioniert, keine wird den Code sogar anschauen… So ist das leben.Konzentriert man sich bei einem Adapter auf das Wesentliche (die Anbindung und das Liefern der Daten)? Oder ist das auch der Ort, um "mehr" zu bieten? `
Es ist auf jeden Fall Platz für alles. Hauptsache es funktioniert und stabil. -
Hier sind die Fritzbox Schnittstellen dokumentiert
http://avm.de/service/schnittstellen/
Vielleicht hilft es dir weiter…
-
Danke Dir
Das wird dann nicht weiterhelfen, da es nur um eine einfache tcp Kommunikation geht. Es ist derzeit auch noch gar kein Thema. Es ging mir nur darum, dass ich mir ansehen muss, wie man in Javascript, auf einen tcp Stream lauscht. In Node-red ist es nur ein tcp Node und ein Auslesen des Buffers. Wird halt die erste "Hürde" sein, wenn ich Teil 2 (Adapter) des Projekts anfange. Aber alles zu seiner Zeit…
Der Import des Fritzbox Telefonbuchs stelle ich mir auch noch einmal schwieriger vor (die Datei einlesen, xml paren), als in Node-red. In Node-red ist es ein Copy Paste des XML-Telefonbuchs in ein template-node, dann ein xml-node und die Daten stehen als JSON zur Verfügung (zwar in einer "blöden" Struktur...).
@Bluefox: zu "Kannst du das Problem mit TCP beschreiben?" -> das meinte ich eigentlich. Es ist gar kein Problem. Wenn ich damit anfange wird es nur Punkte geben, die in Node-red simpel sind und die im Javascript erst einmal ein Hürde für mich darstellen (tcp stream, xml parsen, ...) werden. Sehe das aber sportlich und wenn es so weit ist und ich daran verzweifele, wird es bestimmt ein Post von mir geben
Danke Euch noch einmal!
-
Danke Dir
Das wird dann nicht weiterhelfen, da es nur um eine einfache tcp Kommunikation geht. Es ist derzeit auch noch gar kein Thema. Es ging mir nur darum, dass ich mir ansehen muss, wie man in Javascript, auf einen tcp Stream lauscht. In Node-red ist es nur ein tcp Node und ein Auslesen des Buffers. Wird halt die erste "Hürde" sein, wenn ich Teil 2 (Adapter) des Projekts anfange. Aber alles zu seiner Zeit…
Der Import des Fritzbox Telefonbuchs stelle ich mir auch noch einmal schwieriger vor (die Datei einlesen, xml paren), als in Node-red. In Node-red ist es ein Copy Paste des XML-Telefonbuchs in ein template-node, dann ein xml-node und die Daten stehen als JSON zur Verfügung (zwar in einer "blöden" Struktur...).
@Bluefox: zu "Kannst du das Problem mit TCP beschreiben?" -> das meinte ich eigentlich. Es ist gar kein Problem. Wenn ich damit anfange wird es nur Punkte geben, die in Node-red simpel sind und die im Javascript erst einmal ein Hürde für mich darstellen (tcp stream, xml parsen, ...) werden. Sehe das aber sportlich und wenn es so weit ist und ich daran verzweifele, wird es bestimmt ein Post von mir geben
Danke Euch noch einmal! `
Es ist alles super einfach. Z.B. TCP connectionvar net = require('net'); // Connect to fritzbox. var socketBox = net.connect({port: 1012, host: '192.168.1.1'}, function() { adapter.log.info("adapter fritzBox connected to fritz"); }); // Listen for data socketBox.on('data', function (data) { adapter.log.info("adapter fritzBox data: " + data.toString()); });
Du kannst hier abschauen https://github.com/hobbyquaker/ccu.io/b … ritzBox.js
Zeile 265.
Xml Parsen: (http://forum.iobroker.org/viewtopic.php ... 6401#p6397)
var xml2js = require('xml2js'); var options = { explicitArray: false, mergeAttrs: true }; var parser = new xml2js.Parser(options); parser.parseString(data, function (err, result) { if (err) { console.log("Fehler: "+err); } else { console.log("Result: " + JSON.stringify(result, null, 2)); } });