diff --git a/analysis/mlsentiment/locales/de/mlsentiment.json b/analysis/mlsentiment/locales/de/mlsentiment.json
new file mode 100644
index 00000000..5931483e
--- /dev/null
+++ b/analysis/mlsentiment/locales/de/mlsentiment.json
@@ -0,0 +1,8 @@
+{
+ "mlsentiment": {
+ "sentiment": "sentiment",
+ "label": {
+ "language": "Sprache"
+ }
+ }
+}
diff --git a/analysis/mlsentiment/locales/en-US/mlsentiment.html b/analysis/mlsentiment/locales/en-US/mlsentiment.html
new file mode 100644
index 00000000..8e9f5c9d
--- /dev/null
+++ b/analysis/mlsentiment/locales/en-US/mlsentiment.html
@@ -0,0 +1,29 @@
+
+
diff --git a/analysis/mlsentiment/locales/ja/mlsentiment.html b/analysis/mlsentiment/locales/ja/mlsentiment.html
index b55a6e74..ae7b5fa7 100644
--- a/analysis/mlsentiment/locales/ja/mlsentiment.html
+++ b/analysis/mlsentiment/locales/ja/mlsentiment.html
@@ -1,5 +1,5 @@
-
-
-
-
diff --git a/function/PID/locales/en-US/pidcontrol.html b/function/PID/locales/en-US/pidcontrol.html
new file mode 100644
index 00000000..f1222218
--- /dev/null
+++ b/function/PID/locales/en-US/pidcontrol.html
@@ -0,0 +1,8 @@
+
+
diff --git a/function/PID/pidcontrol.html b/function/PID/pidcontrol.html
index 06116a32..3ab0d646 100644
--- a/function/PID/pidcontrol.html
+++ b/function/PID/pidcontrol.html
@@ -1,5 +1,5 @@
-
-
-
-
-
diff --git a/function/datagenerator/package.json b/function/datagenerator/package.json
index 3dc05d16..16339dd9 100644
--- a/function/datagenerator/package.json
+++ b/function/datagenerator/package.json
@@ -1,6 +1,6 @@
{
"name" : "node-red-node-data-generator",
- "version" : "0.1.1",
+ "version" : "0.2.0",
"description" : "A Node-RED node to create a string of dummy data values from a template. Useful for test-cases.",
"dependencies" : {
"dummy-json": "^2.0.0"
diff --git a/function/random/locales/de/random.json b/function/random/locales/de/random.json
new file mode 100644
index 00000000..197c13d9
--- /dev/null
+++ b/function/random/locales/de/random.json
@@ -0,0 +1,13 @@
+{
+ "random": {
+ "label": {
+ "generate": "Generiere",
+ "wholeNumber": "eine Ganzzahl (integer)",
+ "realNumber": "eine reelle Zahl (floating point)",
+ "from": "Von",
+ "lowestNumber": "kleinste Zahl",
+ "to": "Bis",
+ "highestNumber": "größte Zahl"
+ }
+ }
+}
diff --git a/function/random/package.json b/function/random/package.json
index 2f2f4dc0..24946b69 100644
--- a/function/random/package.json
+++ b/function/random/package.json
@@ -1,6 +1,6 @@
{
"name" : "node-red-node-random",
- "version" : "0.3.1",
+ "version" : "0.4.0",
"description" : "A Node-RED node that when triggered generates a random number between two values.",
"dependencies" : {
},
diff --git a/function/rbe/locales/de/rbe.html b/function/rbe/locales/de/rbe.html
new file mode 100644
index 00000000..99fcd318
--- /dev/null
+++ b/function/rbe/locales/de/rbe.html
@@ -0,0 +1,40 @@
+
diff --git a/function/rbe/locales/de/rbe.json b/function/rbe/locales/de/rbe.json
new file mode 100644
index 00000000..dcdded6b
--- /dev/null
+++ b/function/rbe/locales/de/rbe.json
@@ -0,0 +1,29 @@
+{
+ "rbe": {
+ "rbe": "rbe",
+ "label": {
+ "func": "Modus",
+ "init": "Sende Anfangswert",
+ "start": "Startwert",
+ "name": "Name",
+ "septopics": "Modus für jedes msg.topic separat anwenden"
+ },
+ "placeholder":{
+ "bandgap": "z.B. 10 oder 5%",
+ "start": "Leer lassen, um erste empfangenen Daten zu nutzen"
+ },
+ "opts": {
+ "rbe": "Blockieren bis Wertänderung",
+ "rbei": "Blockieren bis Wertänderung (Anfangswert ignorieren)",
+ "deadband": "Blockieren bis Wertänderung ist größer als",
+ "deadbandEq": "Blockieren bis Wertänderung ist größer-gleich",
+ "narrowband": "Blockieren wenn Wertänderung ist größer als",
+ "narrowbandEq": "Blockieren wenn Wertänderung ist größer-gleich",
+ "in": "verglichen mit letzten Eingangswert",
+ "out": "verglichen mit letzten gültigen Ausgangswert"
+ },
+ "warn": {
+ "nonumber": "Keine Zahl gefunden in den Nutzdaten (Payload)"
+ }
+ }
+}
diff --git a/function/rbe/locales/en-US/rbe.html b/function/rbe/locales/en-US/rbe.html
index 44d2068d..0bca2cf0 100644
--- a/function/rbe/locales/en-US/rbe.html
+++ b/function/rbe/locales/en-US/rbe.html
@@ -1,18 +1,19 @@
diff --git a/function/rbe/locales/en-US/rbe.json b/function/rbe/locales/en-US/rbe.json
index 8adbb62a..0a493e3a 100644
--- a/function/rbe/locales/en-US/rbe.json
+++ b/function/rbe/locales/en-US/rbe.json
@@ -5,7 +5,8 @@
"func": "Mode",
"init": "Send initial value",
"start": "Start value",
- "name": "Name"
+ "name": "Name",
+ "septopics": "Apply mode for each msg.topic separately"
},
"placeholder":{
"bandgap": "e.g. 10 or 5%",
diff --git a/function/rbe/package.json b/function/rbe/package.json
index c66f41ee..eb11db4d 100644
--- a/function/rbe/package.json
+++ b/function/rbe/package.json
@@ -1,6 +1,6 @@
{
"name" : "node-red-node-rbe",
- "version" : "0.2.9",
+ "version" : "0.5.0",
"description" : "A Node-RED node that provides report-by-exception (RBE) and deadband capabilities.",
"dependencies" : {
},
diff --git a/function/rbe/rbe.html b/function/rbe/rbe.html
index 70cad4c5..58a5a9be 100644
--- a/function/rbe/rbe.html
+++ b/function/rbe/rbe.html
@@ -27,6 +27,11 @@
+
+
+
+
+
@@ -43,6 +48,7 @@
gap: {value:"",validate:RED.validators.regex(/^(\d*[.]*\d*|)(%|)$/)},
start: {value:""},
inout: {value:"out"},
+ septopics: {value:true},
property: {value:"payload",required:true}
},
inputs:1,
@@ -59,6 +65,9 @@
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
+ if (this.septopics === undefined) {
+ $("#node-input-septopics").prop('checked', true);
+ }
$("#node-input-property").typedInput({default:'msg',types:['msg']});
//$( "#node-input-gap" ).spinner({min:0});
if ($("#node-input-inout").val() === null) {
diff --git a/function/rbe/rbe.js b/function/rbe/rbe.js
index dc98496a..c5885101 100644
--- a/function/rbe/rbe.js
+++ b/function/rbe/rbe.js
@@ -14,20 +14,25 @@ module.exports = function(RED) {
}
this.g = this.gap;
this.property = n.property||"payload";
+ this.septopics = true;
+ if (n.septopics !== undefined && n.septopics === false) {
+ this.septopics = false;
+ }
var node = this;
node.previous = {};
this.on("input",function(msg) {
if (msg.hasOwnProperty("reset")) {
- if (msg.hasOwnProperty("topic") && (typeof msg.topic === "string") && (msg.topic !== "")) {
+ if (node.septopics && msg.hasOwnProperty("topic") && (typeof msg.topic === "string") && (msg.topic !== "")) {
delete node.previous[msg.topic];
}
else { node.previous = {}; }
}
var value = RED.util.getMessageProperty(msg,node.property);
if (value !== undefined) {
- var t = msg.topic || "_no_topic";
+ var t = "_no_topic";
+ if (node.septopics) { t = msg.topic || t; }
if ((this.func === "rbe") || (this.func === "rbei")) {
var doSend = (this.func !== "rbei") || (node.previous.hasOwnProperty(t)) || false;
if (typeof(value) === "object") {
diff --git a/function/smooth/17-smooth.html b/function/smooth/17-smooth.html
index 75103f05..81643a1b 100644
--- a/function/smooth/17-smooth.html
+++ b/function/smooth/17-smooth.html
@@ -45,16 +45,6 @@
Tip: This node ONLY works with numbers.
-
-
diff --git a/hardware/Arduino/35-arduino.html b/hardware/Arduino/35-arduino.html
index b492fa28..dd509759 100644
--- a/hardware/Arduino/35-arduino.html
+++ b/hardware/Arduino/35-arduino.html
@@ -24,14 +24,6 @@
-
-
-
-
+
+
diff --git a/hardware/PiFace/37-rpi-piface.html b/hardware/PiFace/37-rpi-piface.html
index 9a2f7a59..e42989c0 100644
--- a/hardware/PiFace/37-rpi-piface.html
+++ b/hardware/PiFace/37-rpi-piface.html
@@ -1,5 +1,5 @@
-
-
-
-
diff --git a/hardware/PiLcd/locales/de/pilcd.json b/hardware/PiLcd/locales/de/pilcd.json
new file mode 100644
index 00000000..b980cdc2
--- /dev/null
+++ b/hardware/PiLcd/locales/de/pilcd.json
@@ -0,0 +1,19 @@
+{
+ "pilcd": {
+ "label": {
+ "pins": "Pins"
+ },
+ "tip": {
+ "tip": "
Tipp : Bei Pins muss eine Komma-getrennte Liste von 6 GPIO-Anschlusspin-Nummern eingetragen werden, die mit RS, E, D4, D5, D6 und D7 des LCD verbunden sind."
+ },
+ "status": {
+ "not-available": "Nicht verfügbar",
+ "na": "Nicht anwendbar: __value__"
+ },
+ "errors": {
+ "ignorenode": "Raspberry-Pi-spezifische Nodes inaktiv gesetzt",
+ "libnotfound": "RPi.GPIO-Python-Bibliothek nicht gefunden",
+ "needtobeexecutable": "__command__ muss ausführbar sein"
+ }
+ }
+}
diff --git a/hardware/PiLcd/locales/en-US/pilcd.html b/hardware/PiLcd/locales/en-US/pilcd.html
index 966815b6..0211a4da 100644
--- a/hardware/PiLcd/locales/en-US/pilcd.html
+++ b/hardware/PiLcd/locales/en-US/pilcd.html
@@ -1,5 +1,11 @@
-
+
+
+
+
diff --git a/hardware/blinkstick/blinkstick.js b/hardware/blinkstick/blinkstick.js
new file mode 100644
index 00000000..389dd605
--- /dev/null
+++ b/hardware/blinkstick/blinkstick.js
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2013 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 blinkstick = require("blinkstick");
+
+ Object.size = function(obj) {
+ var size = 0;
+ for (var key in obj) { if (obj.hasOwnProperty(key)) { size++; } }
+ return size;
+ };
+
+ function BlinkStick(n) {
+ RED.nodes.createNode(this,n);
+ var p1 = /^\#[A-Fa-f0-9]{6}$/
+ var p2 = /[0-9]+,[0-9]+,[0-9]+/
+ this.led = blinkstick.findFirst(); // maybe try findAll() (one day)
+ var node = this;
+
+ this.on("input", function(msg) {
+ if (msg != null) {
+ if (Object.size(node.led) !== 0) {
+ try {
+ if (p2.test(msg.payload)) {
+ var rgb = msg.payload.split(",");
+ node.led.setColor(parseInt(rgb[0])&255, parseInt(rgb[1])&255, parseInt(rgb[2])&255);
+ }
+ else {
+ node.led.setColor(msg.payload.toLowerCase().replace(/\s+/g,''));
+ }
+ }
+ catch (err) {
+ node.warn("BlinkStick missing ?");
+ node.led = blinkstick.findFirst();
+ }
+ }
+ else {
+ //node.warn("No BlinkStick found");
+ node.led = blinkstick.findFirst();
+ }
+ }
+ });
+ if (Object.size(node.led) === 0) {
+ node.error("No BlinkStick found");
+ }
+
+ }
+
+ RED.nodes.registerType("blinkstick",BlinkStick);
+}
diff --git a/hardware/blinkstick/package.json b/hardware/blinkstick/package.json
index a7c2c344..6b43e404 100644
--- a/hardware/blinkstick/package.json
+++ b/hardware/blinkstick/package.json
@@ -1,9 +1,9 @@
{
"name" : "node-red-node-blinkstick",
- "version" : "0.1.16",
+ "version" : "0.1.7",
"description" : "A Node-RED node to control a Blinkstick",
"dependencies" : {
- "blinkstick" : "1.1.3"
+ "blinkstick" : "1.2.0"
},
"repository" : {
"type":"git",
diff --git a/hardware/pigpiod/locales/de/pi-gpiod.html b/hardware/pigpiod/locales/de/pi-gpiod.html
new file mode 100644
index 00000000..d9962ded
--- /dev/null
+++ b/hardware/pigpiod/locales/de/pi-gpiod.html
@@ -0,0 +1,38 @@
+
+
+
+
diff --git a/hardware/pigpiod/locales/de/pi-gpiod.json b/hardware/pigpiod/locales/de/pi-gpiod.json
new file mode 100644
index 00000000..912ac260
--- /dev/null
+++ b/hardware/pigpiod/locales/de/pi-gpiod.json
@@ -0,0 +1,59 @@
+{
+ "pi-gpiod": {
+ "label": {
+ "gpiopin": "GPIO",
+ "selectpin": "Pin-Auswahl",
+ "host": "Host",
+ "resistor": "Widerstand",
+ "readinitial": "Initalzustand bei Übernahme/Neustart lesen",
+ "type": "Typ",
+ "initpin": "Pin-Zustand initialisieren",
+ "debounce": "Entprellung",
+ "limits": "Limits",
+ "min": "min.",
+ "max": "max.",
+ "freq": "Frequenz"
+ },
+ "place": {
+ "host": "Lokale oder entfernte IP",
+ "port": "Port"
+ },
+ "resistor": {
+ "none": "Kein",
+ "pullup": "Pull-Up",
+ "pulldown": "Pull-Down"
+ },
+ "digout": "Digitaler Ausgang",
+ "pwmout": "PWM-Ausgang",
+ "servo": "Servo-Ausgang",
+ "initpin0": "Initalzustand low (0)",
+ "initpin1": "Initalzustand high (1)",
+ "pinname": "Pin",
+ "tip": {
+ "pin": "
Pins in Verwendung : ",
+ "in": "Nur digitaler Eingang unterstützt - Eingangszustand muss 0 oder 1 sein",
+ "dig": "Digitaler Ausgang - Node-Eingangswert muss 0 oder 1 sein",
+ "pwm": "PWM-Ausgang - Node-Eingangswert muss zwischen 0 und 100 sein",
+ "ser": "Servo-Ausgang - Node-Eingangswert muss zwischen 0 und 100 sein. 50 ist die Mitte.
Min. muss mindestens 500 µs und Max. muss 2500 µs oder weniger sein.",
+ "dual": "Blaue Pins sind mehrfach verwendbar. Es ist sicherzustellen, dass keine andere Verwendung aktiviert ist, um sie als GPIO zu verwenden."
+ },
+ "types": {
+ "digout": "Digitaler Ausgang",
+ "input": "Eingang",
+ "pullup": "Eingang mit Pull-Up",
+ "pulldown": "Eingang mit Pull-Down",
+ "pwmout": "PWM-Ausgang",
+ "servo": "Servo-Ausgang"
+ },
+ "status": {
+ "stopped": "Gestoppt",
+ "closed": "Geschlossen",
+ "not-running": "Nicht aktiv"
+ },
+ "errors": {
+ "invalidpin": "Ungültiger GPIO-Pin",
+ "invalidinput": "Ungültige Eingabe",
+ "error": "FEHLER: __error__"
+ }
+ }
+}
diff --git a/hardware/pigpiod/locales/en-US/pi-gpiod.html b/hardware/pigpiod/locales/en-US/pi-gpiod.html
new file mode 100644
index 00000000..84b017b2
--- /dev/null
+++ b/hardware/pigpiod/locales/en-US/pi-gpiod.html
@@ -0,0 +1,36 @@
+
+
+
+
diff --git a/hardware/pigpiod/locales/en-US/pi-gpiod.json b/hardware/pigpiod/locales/en-US/pi-gpiod.json
index 5eaa33e6..047560e5 100644
--- a/hardware/pigpiod/locales/en-US/pi-gpiod.json
+++ b/hardware/pigpiod/locales/en-US/pi-gpiod.json
@@ -11,7 +11,8 @@
"debounce": "Debounce",
"limits": "Limits",
"min": "min",
- "max": "max"
+ "max": "max",
+ "freq": "Frequency"
},
"place": {
"host": "local or remote ip",
@@ -33,7 +34,8 @@
"in": "Only Digital Input is supported - input must be 0 or 1.",
"dig": "Digital output - input must be 0 or 1.",
"pwm": "PWM output - input must be between 0 to 100.",
- "ser": "Servo output - input must be between 0 to 100. 50 is centre.
Min must be 500uS or more, Max must be 2500uS or less."
+ "ser": "Servo output - input must be between 0 to 100. 50 is centre.
min. must be 500 us or more, max. must be 2500 us or less.",
+ "dual": "Pins marked in blue are dual use. Make sure they are not enabled for their other use before using as GPIO."
},
"types": {
"digout": "digital output",
diff --git a/hardware/pigpiod/package.json b/hardware/pigpiod/package.json
index 9310b69e..0c07689e 100644
--- a/hardware/pigpiod/package.json
+++ b/hardware/pigpiod/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-pi-gpiod",
- "version": "0.1.1",
+ "version": "0.4.0",
"description": "A node-red node for PiGPIOd",
"dependencies" : {
"js-pigpio": "*"
diff --git a/hardware/pigpiod/pi-gpiod.html b/hardware/pigpiod/pi-gpiod.html
index 7f19ce79..113028a5 100644
--- a/hardware/pigpiod/pi-gpiod.html
+++ b/hardware/pigpiod/pi-gpiod.html
@@ -164,26 +164,9 @@
- Pins marked in blue are dual use. Make sure they are not enabled for
- their other use before using as GPIO.
+
-
-
-
-
+
+
diff --git a/hardware/sensehat/package.json b/hardware/sensehat/package.json
index d893dfa8..7babe47e 100644
--- a/hardware/sensehat/package.json
+++ b/hardware/sensehat/package.json
@@ -1,6 +1,6 @@
{
"name" : "node-red-node-pi-sense-hat",
- "version" : "0.0.18",
+ "version" : "0.1.0",
"description" : "A Node-RED node to interact with a Raspberry Pi Sense HAT",
"repository" : {
"type":"git",
diff --git a/hardware/sensehat/sensehat.html b/hardware/sensehat/sensehat.html
index 41c42dd6..8de10721 100644
--- a/hardware/sensehat/sensehat.html
+++ b/hardware/sensehat/sensehat.html
@@ -1,132 +1,33 @@
-
-
-
-
-
-
@@ -66,7 +65,8 @@
});
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/io/serialport/package.json b/io/serialport/package.json
index aefb7123..caa00dd3 100644
--- a/io/serialport/package.json
+++ b/io/serialport/package.json
@@ -1,9 +1,9 @@
{
"name" : "node-red-node-serialport",
- "version" : "0.11.1",
+ "version" : "0.13.0",
"description" : "Node-RED nodes to talk to serial ports",
"dependencies" : {
- "serialport" : "^9.0.2"
+ "serialport" : "^9.0.7"
},
"repository" : {
"type":"git",
diff --git a/io/stomp/18-stomp.html b/io/stomp/18-stomp.html
index 4aab1258..e643807c 100644
--- a/io/stomp/18-stomp.html
+++ b/io/stomp/18-stomp.html
@@ -1,5 +1,5 @@
-
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/social/email/locales/de/61-email.json b/social/email/locales/de/61-email.json
new file mode 100644
index 00000000..575774f0
--- /dev/null
+++ b/social/email/locales/de/61-email.json
@@ -0,0 +1,71 @@
+{
+ "email": {
+ "email": "email",
+ "label": {
+ "getmail":"Mailempfang",
+ "auto": "automatisch",
+ "trigger": "wenn getriggert",
+ "to": "An",
+ "server": "Server",
+ "port": "Port",
+ "useSecureConnection": "Sichere Verbindung verwenden",
+ "userid": "Benutzer-ID",
+ "password": "Passwort",
+ "repeat": "alle",
+ "seconds": "Sekunden",
+ "folder": "Verzeichnis",
+ "protocol": "Protokoll",
+ "useSSL": "SSL",
+ "useTLS": "TLS",
+ "disposition": "Behandlung",
+ "none": "Keine",
+ "read": "Gelesen markieren",
+ "delete": "Löschen",
+ "criteria": "Kriterium",
+ "criteriaFromMsg": "Gesetzt durch msg.criteria",
+ "all": "Alle",
+ "answered": "Beantwortet",
+ "flagged": "Markiert",
+ "seen": "Gesichtet",
+ "unanswered": "Unbeantwortet",
+ "unflagged": "Unmarkiert",
+ "unseen": "Ungesehen",
+ "autotls": "STARTTLS?",
+ "never": "nie",
+ "required": "wenn erforderlich",
+ "always": "immer"
+ },
+ "default-message": "__description__\n\nDatei von Node-RED ist angehängt: __filename__",
+ "tip": {
+ "cred": "Hinweis : Berechtigungen von globaler emailkeys.js-Datei kopiert",
+ "recent": "Tipp: Es wird nur die letzte E-Mail abgerufen",
+ "mta": "Hinweis : Um Ports unter 1024 zu verwenden könnten höhere (root) Rechte benötigt werden. Siehe Hilfe-Seitenleiste."
+ },
+ "status": {
+ "messagesent": "Nachricht gesendet: __response__",
+ "fetching": "Rufe ab",
+ "foldererror": "Fehler bei Verzeichnisabruf",
+ "messageerror": "Fehler bei Nachrichtenabruf",
+ "message": "Nachricht #__number__",
+ "newemail": "Neue E-Mail empfangen: __topic__",
+ "duplicate": "Duplikat nicht gesendet: __topic__",
+ "inboxzero": "Posteingang leer",
+ "sending": "Sende",
+ "sendfail": "Senden fehlgeschlagen",
+ "parseerror": "Analyse der Nachricht fehlgeschlagen",
+ "connecterror": "Verbindungsfehler"
+ },
+ "errors": {
+ "nouserid": "Kein E-Mail-Benutzer-ID angegeben",
+ "nopassword": "Kein E-Mail-Passwort angegeben",
+ "nocredentials": "Keine E-Mail-Berechtigungen gefunden. Siehe Info-Anzeige.",
+ "nosmtptransport": "Kein SMTP-Transport. Siehe Info-Anzeige.",
+ "nopayload": "Keine sendbaren Nutzdaten (Payload)",
+ "fetchfail": "Verzeichnisabruf fehlgeschlagen: __folder__",
+ "parsefail": "Analyse der Nachricht fehlgeschlagen",
+ "messageerror": "Nachrichtenabruf fehlgeschlagen: __error__",
+ "refreshtoolarge": "Abrufintervall ist zu groß. Max. Limit sind 2147483 Sekunden.",
+ "invalidattachment": "Ungültiger Anhang-Inhalt. Es muss ein String oder ein Buffer sein."
+ }
+ }
+}
diff --git a/social/email/locales/en-US/61-email.html b/social/email/locales/en-US/61-email.html
index 3806b8de..959058f1 100644
--- a/social/email/locales/en-US/61-email.html
+++ b/social/email/locales/en-US/61-email.html
@@ -6,22 +6,24 @@
msg.inReplyTo
, msg.references
, msg.headers
, or msg.priority
properties.
You may optionally set msg.from
in the payload which will override the userid
default value.
- GMail users
- If you are accessing GMail you may need to either enable an application password ,
+
Gmail users
+ If you are accessing Gmail you may need to either enable an application password ,
or enable less secure access via your Google account settings.
Details
- The payload can be html format.
+ The payload can be html format. You may supply a separate plaintext version using msg.plaintext
.
+ If you don't and msg.payload
contains html, it will also be used for the plaintext.
+ msg.plaintext
will be ignored if msg.payload
doesn't contain html.
If the payload is a binary buffer then it will be converted to an attachment.
The filename should be set using msg.filename
. Optionally msg.description
can be added for the body text.
Alternatively you may provide msg.attachments
which should contain an array of one or
more attachments in nodemailer format.
If required by your recipient you may also pass in a msg.envelope
object, typically containing extra from and to properties.
If you have own signed certificates, Nodemailer can complain about that and refuse sending the message. In this case you can try switching off TLS.
- Note: uses SMTP with SSL to port 465.
+ Note : uses SMTP with SSL to port 465.
diff --git a/social/feedparser/locales/de/32-feedparse.json b/social/feedparser/locales/de/32-feedparse.json
new file mode 100644
index 00000000..4bbe5591
--- /dev/null
+++ b/social/feedparser/locales/de/32-feedparse.json
@@ -0,0 +1,15 @@
+{
+ "feedparse": {
+ "feedparse": "feedparser",
+ "label": {
+ "feedurl": "Feed-URL",
+ "refresh": "Aktualisierung",
+ "minutes": "Minuten"
+ },
+ "errors": {
+ "badstatuscode": "Fehler - Fehlerhafter Statuscode",
+ "invalidurl": "Ungültige URL",
+ "invalidinterval": "Aktualisierungsintervall zu groß"
+ }
+ }
+}
diff --git a/social/feedparser/locales/en-US/32-feedparse.html b/social/feedparser/locales/en-US/32-feedparse.html
index 87189b64..0f27140d 100644
--- a/social/feedparser/locales/en-US/32-feedparse.html
+++ b/social/feedparser/locales/en-US/32-feedparse.html
@@ -1,7 +1,15 @@
diff --git a/social/feedparser/package.json b/social/feedparser/package.json
index 5e658d02..34c69077 100644
--- a/social/feedparser/package.json
+++ b/social/feedparser/package.json
@@ -1,10 +1,10 @@
{
"name": "node-red-node-feedparser",
- "version": "0.1.16",
+ "version": "0.2.1",
"description": "A Node-RED node to get RSS Atom feeds.",
"dependencies": {
"feedparser": "^2.2.10",
- "request": "^2.88.0"
+ "request": "^2.88.2"
},
"repository": {
"type": "git",
diff --git a/social/irc/91-irc.html b/social/irc/91-irc.html
index 2849914d..beba8041 100644
--- a/social/irc/91-irc.html
+++ b/social/irc/91-irc.html
@@ -1,5 +1,5 @@
-
-
-
-
-
-
-
-
-
+
+
diff --git a/social/irc/package.json b/social/irc/package.json
index 5fcc70ca..dcb7a518 100644
--- a/social/irc/package.json
+++ b/social/irc/package.json
@@ -1,9 +1,9 @@
{
"name" : "node-red-node-irc",
- "version" : "0.0.8",
+ "version" : "0.1.0",
"description" : "A Node-RED node to talk to an IRC server",
"dependencies" : {
- "irc" : "~0.4.0"
+ "irc" : "^0.5.2"
},
"repository" : {
"type":"git",
diff --git a/social/notify/57-notify.html b/social/notify/57-notify.html
index fe3244a2..db576917 100644
--- a/social/notify/57-notify.html
+++ b/social/notify/57-notify.html
@@ -10,13 +10,6 @@
-
-
diff --git a/social/pushover/57-pushover.html b/social/pushover/57-pushover.html
index dabbb4cc..942f2285 100644
--- a/social/pushover/57-pushover.html
+++ b/social/pushover/57-pushover.html
@@ -82,7 +82,10 @@
msg.attachment
: attach an image (Buffer or file path)
msg.url
: to add a web address
msg.url_title
: to add a url title if not already set in the properties
+ msg.html
: set to true or 1 if message is HTML formatted, see the supported tags
msg.sound
: set the notification sound, see the available options
+ msg.retry
: set retry interval for Emergency priority (2) messages, see details
+ msg.expire
: set retry duration for Emergency priority (2) messages, see details
Uses Pushover. See this link for more details.
diff --git a/social/pushover/57-pushover.js b/social/pushover/57-pushover.js
index 947528c1..2a197712 100644
--- a/social/pushover/57-pushover.js
+++ b/social/pushover/57-pushover.js
@@ -37,12 +37,30 @@ module.exports = function(RED) {
var sound = node.sound || msg.sound || null;
var url = node.url || msg.url || null;
var url_title = node.url_title || msg.url_title || null;
- var html = node.html || false;
+ var html = node.html || msg.html || false;
var attachment = msg.attachment || null;
+ var retry = msg.retry || 30;
+ var expire = msg.expire || 600;
if (isNaN(pri)) {pri=0;}
if (pri > 2) {pri = 2;}
if (pri < -2) {pri = -2;}
- if (!msg.payload) { msg.payload = ""; }
+ if (isNaN(retry)) {
+ retry = 30;
+ node.warn("No valid number for retry found, using default 30s retry interval");
+ }
+ if (isNaN(expire)) {
+ expire = 600;
+ node.warn("No valid number for expire time found, using default 600s retry duration");
+ }
+ if (retry < 30) {
+ retry = 30;
+ node.warn("Retry interval too low, using minimal 30s retry interval");
+ }
+ if (expire > 10800) {
+ expire = 10800;
+ node.warn("Expire time too high, using maximum setting of 10800s (3 hours) retry duration");
+ }
+ if (typeof msg.payload === 'undefined') { msg.payload = "(undefined msg.payload)"; }
if (typeof(msg.payload) === 'object') {
msg.payload = JSON.stringify(msg.payload);
}
@@ -52,8 +70,8 @@ module.exports = function(RED) {
message: msg.payload,
title: title,
priority: pri,
- retry: 30,
- expire: 600,
+ retry: retry,
+ expire: expire,
html: html
};
if (dev) { pushmsg.device = dev; }
diff --git a/social/pushover/README.md b/social/pushover/README.md
index fd948db6..55acef20 100644
--- a/social/pushover/README.md
+++ b/social/pushover/README.md
@@ -24,7 +24,10 @@ Optionally uses `msg.topic` to set the configuration, if not already set in the
- `msg.attachment`: to specify an image to attach to message (path as a string or Buffer containing image)
- `msg.url`: to add a web address
- `msg.url_title`: to add a url title
+ - `msg.html`: set to true or 1 if message is HTML formatted, see the [supported tags](https://pushover.net/api#html)
- `msg.sound`: to set the alert sound, see the [available options](https://pushover.net/api#sounds)
+ - `msg.retry`: to set retry interval for Emergency priority (2) messages, see [priority](https://pushover.net/api#priority)
+ - `msg.expire`: to set retry duration for Emergency priority (2) messages, see [priority](https://pushover.net/api#priority)
The User-key and API-token are stored in a separate credentials file.
diff --git a/social/pushover/package.json b/social/pushover/package.json
index 6b39a6dc..d0aa3a3b 100644
--- a/social/pushover/package.json
+++ b/social/pushover/package.json
@@ -1,6 +1,6 @@
{
"name" : "node-red-node-pushover",
- "version" : "0.0.20",
+ "version" : "0.0.24",
"description" : "A Node-RED node to send alerts via Pushover",
"dependencies" : {
"pushover-notifications" : "^1.2.2"
diff --git a/social/twitter/27-twitter.html b/social/twitter/27-twitter.html
index 3d72a66c..57ea833b 100644
--- a/social/twitter/27-twitter.html
+++ b/social/twitter/27-twitter.html
@@ -1,4 +1,4 @@
-
-
-
@@ -69,6 +90,10 @@
Is a Chat Room ?
+
+ Password
+
+
Name
@@ -79,7 +104,6 @@
@@ -95,6 +119,9 @@
join: {value:false},
sendObject: {value:false}
},
+ credentials: {
+ password: {type:"password"}
+ },
inputs:1,
outputs:0,
icon: "xmpp.png",
@@ -104,6 +131,16 @@
},
labelStyle: function() {
return (this.name)?"node_label_italic":"";
+ },
+ oneditprepare: function() {
+ $('#node-input-join').change(function() {
+ if ($("#node-input-join").is(':checked') && $("#node-input-to").val() && $("#node-input-to").val().indexOf(':') === -1) { $("#node-room-pwd").show(); }
+ else { $("#node-room-pwd").hide(); }
+ });
+ $('#node-input-to').change(function() {
+ if ($("#node-input-join").is(':checked') && $("#node-input-to").val() && $("#node-input-to").val().indexOf(':') === -1) { $("#node-room-pwd").show(); }
+ else { $("#node-room-pwd").hide(); }
+ });
}
});
@@ -124,7 +161,7 @@
- Password
+ Password
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index 59e0ac36..6326c7a8 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -15,13 +15,13 @@ module.exports = function(RED) {
if ("undefined" === typeof n.server || n.server === "") {
this.server = n.user.split('@')[1];
}
- else{
+ else {
this.server = n.server;
}
if ("undefined" === typeof n.port || n.port === "") {
this.port = 5222;
}
- else{
+ else {
this.port = parseInt(n.port);
}
@@ -50,6 +50,8 @@ module.exports = function(RED) {
this.connected = false;
// store the nodes that have us as config so we know when to tear it all down.
this.users = {};
+ // store the chatrooms (MUC) that we've joined (sent "presence" XML to) already
+ this.MUCs = {};
// helper variable, because "this" changes definition inside a callback
var that = this;
@@ -57,7 +59,7 @@ module.exports = function(RED) {
this.register = function(xmppThat) {
if (RED.settings.verbose || LOGITALL) {that.log("registering "+xmppThat.id); }
that.users[xmppThat.id] = xmppThat;
- // So we could start the connection here, but we already have the logic in the thats.
+ // So we could start the connection here, but we already have the logic in the nodes that takes care of that.
// if (Object.keys(that.users).length === 1) {
// this.client.start();
// }
@@ -73,7 +75,8 @@ module.exports = function(RED) {
if (Object.keys(that.users).length === 0) {
if (that.client && that.client.connected) {
return that.client.stop(done);
- } else {
+ }
+ else {
return done();
}
}
@@ -82,6 +85,7 @@ module.exports = function(RED) {
// store the last node to use us, in case we get an error back
this.lastUsed = undefined;
+
// function for a node to tell us it has just sent a message to our server
// so we know which node to blame if it all goes Pete Tong
this.used = function(xmppThat) {
@@ -89,11 +93,11 @@ module.exports = function(RED) {
that.lastUsed = xmppThat;
}
-
// Some errors come back as a message :-(
// this means we need to figure out which node might have sent it
// we also deal with subscriptions (i.e. presence information) here
- this.client.on('stanza', async (stanza) =>{
+ this.client.on('stanza', async (stanza) => {
+ //console.log("STANZA",stanza.toString())
if (stanza.is('message')) {
if (stanza.attrs.type == 'error') {
if (RED.settings.verbose || LOGITALL) {
@@ -103,23 +107,27 @@ module.exports = function(RED) {
var err = stanza.getChild('error');
if (err) {
var textObj = err.getChild('text');
- var text = "node-red:common.status.error";
- if ("undefined" !== typeof textObj) {
+ var text = "error";
+ if (typeof textObj !== "undefined") {
text = textObj.getText();
}
- else{
- textObj = err.getChild('code');
- if ("undefined" !== typeof textObj) {
- text = textObj.getText();
+ else {
+ textObj = err.getAttr('code');
+ if (typeof textObj !== "undefined") {
+ text = textObj;
}
}
- if (RED.settings.verbose || LOGITALL) {that.log("Culprit: "+that.lastUsed); }
- if ("undefined" !== typeof that.lastUsed) {
- that.lastUsed.status({fill:"red",shape:"ring",text:text});
- that.lastUsed.warn(text);
+ if (RED.settings.verbose || LOGITALL) {that.log("Culprit: "+that.lastUsed.id); }
+ if (typeof that.lastUsed !== "undefined") {
+ that.lastUsed.status({fill:"yellow",shape:"dot",text:"warning. "+text});
+ that.lastUsed.warn("Warning. "+text);
+ if (that.lastUsed.join) {
+ // it was trying to MUC things up
+ clearMUC(that);
+ }
}
if (RED.settings.verbose || LOGITALL) {
- that.log("We did wrong: "+text);
+ that.log("We did wrong: Error "+text);
that.log(stanza);
}
@@ -144,6 +152,24 @@ module.exports = function(RED) {
that.log("Was told we've "+stanza.attrs.type+" from "+stanza.attrs.from+" but we don't really care");
}
}
+ if (stanza.attrs.to && stanza.attrs.to.indexOf(that.jid) !== -1) {
+ var _x = stanza.getChild("x")
+ if (_x !== undefined) {
+ var _stat = _x.getChildren("status");
+ for (var i = 0; i<_stat.length; i++) {
+ if (_stat[i].attrs.code == 201) {
+ if (RED.settings.verbose || LOGITALL) {that.log("created new room"); }
+ var stanza = xml('iq',
+ {type:'set', id:that.id, from:that.jid, to:stanza.attrs.from.split('/')[0]},
+ xml('query', 'http://jabber.org/protocol/muc#owner',
+ xml('x', {xmlns:'jabber:x:data', type:'submit'})
+ )
+ );
+ that.client.send(stanza);
+ }
+ }
+ }
+ }
}
else if (stanza.is('iq')) {
if (RED.settings.verbose || LOGITALL) {that.log("got an iq query"); }
@@ -154,17 +180,9 @@ module.exports = function(RED) {
that.lastUsed.warn(stanza.getChild('error'));
}
}
- else if (stanza.attrs.type === 'result') {
- // AM To-Do check for 'bind' result with our current jid
- var query = stanza.getChild('query');
- if (RED.settings.verbose || LOGITALL) {that.log("result!"); }
- if (RED.settings.verbose || LOGITALL) {that.log(query); }
-
- }
}
});
-
// We shouldn't have any errors here that the input/output nodes can't handle
// if you need to see everything though; uncomment this block
// this.client.on('error', err => {
@@ -189,8 +207,12 @@ module.exports = function(RED) {
// gets called when the node is destroyed, e.g. if N-R is being stopped.
this.on("close", async done => {
- if (that.client.connected) {
- await that.client.send(xml('presence', {type: 'unavailable'}));
+ const rooms = Object.keys(this.MUCs)
+ for (const room of rooms) {
+ await that.client.send(xml('presence', {to:room, type:'unavailable'}));
+ }
+ if (that.connected) {
+ await that.client.send(xml('presence', {type:'unavailable'}));
try{
if (RED.settings.verbose || LOGITALL) {
that.log("Calling stop() after close, status is "+that.client.status);
@@ -211,6 +233,103 @@ module.exports = function(RED) {
}
});
+ function getItems(thing,id,xmpp) {
+ // Now try to get a list of all items/conference rooms available on this server
+ var stanza = xml('iq',
+ {type:'get', id:id, to:thing},
+ xml('query', 'http://jabber.org/protocol/disco#items')
+ );
+ xmpp.send(stanza);
+ }
+
+ function joinMUC(node, xmpp, name) {
+ // the presence with the muc x element signifies we want to join the muc
+ // if we want to support passwords, we need to add that as a child of the x element
+ // (third argument to the x/muc/children )
+ // We also turn off chat history (maxstanzas 0) because that's not what this node is about.
+ // Yes, there's a race condition, but it's not a huge problem to send two messages
+ // so we don't care.
+ var mu = name.split("/")[0];
+ if (mu in node.serverConfig.MUCs) {
+ if (RED.settings.verbose || LOGITALL) { node.log("already joined MUC "+name); }
+ }
+ else {
+ var stanza = xml('presence',
+ {"to":name},
+ xml("x",'http://jabber.org/protocol/muc',
+ xml("history", {maxstanzas:0, seconds:1}) // We don't want any history
+ )
+ );
+ if (node.hasOwnProperty("credentials") && node.credentials.hasOwnProperty("password")) {
+ stanza = xml('presence',
+ {"to":name},
+ xml("x",'http://jabber.org/protocol/muc',
+ xml("history", {maxstanzas:0, seconds:1}), // We don't want any history
+ xml("password", {}, node.credentials.password) // Add the password
+ )
+ );
+ }
+ node.serverConfig.used(node);
+ node.serverConfig.MUCs[mu] = "joined";
+ if (RED.settings.verbose || LOGITALL) { node.log("JOINED "+mu); }
+ xmpp.send(stanza);
+ }
+ }
+
+ function clearMUC(config) {
+ //something has happened, so clear out our presence indicators
+ if (RED.settings.verbose || LOGITALL) {
+ config.log("cleared all MUC membership");
+ }
+ config.MUCs = {};
+ }
+
+ // separated out since we want the same functionality from both in and out nodes
+ function errorHandler(node, err){
+ if (!node.quiet) {
+ node.quiet = true;
+ // if the error has a "stanza" then we've probably done something wrong and the
+ // server is unhappy with us
+ if (err.hasOwnProperty("stanza")) {
+ if (err.stanza.name === 'stream:error') { node.error("stream:error - bad login id/pwd ?",err); }
+ else { node.error(err.stanza.name,err); }
+ node.status({fill:"red",shape:"ring",text:"bad login"});
+ }
+ // The error might be a string
+ else if (err == "TimeoutError") {
+ // OK, this happens with OpenFire, suppress it, but invalidate MUC membership as it will need to be re-established.
+ clearMUC(node.serverConfig);
+ node.status({fill:"grey",shape:"dot",text:"TimeoutError"});
+ node.log("Timed out! ",err);
+ // node.status({fill:"red",shape:"ring",text:"XMPP timeout"});
+ }
+ else if (err === "XMPP authentication failure") {
+ node.error(err,err);
+ node.status({fill:"red",shape:"ring",text:"XMPP authentication failure"});
+ }
+ // or it might have a name that tells us what's wrong
+ else if (err.name === "SASLError") {
+ node.error("Authorization error! "+err.condition,err);
+ node.status({fill:"red",shape:"ring",text:"XMPP authorization failure"});
+ }
+ // or it might have the errno set.
+ else if (err.errno === "ETIMEDOUT") {
+ node.error("Timeout connecting to server",err);
+ node.status({fill:"red",shape:"ring",text:"timeout"});
+ }
+ else if (err.errno === "ENOTFOUND") {
+ node.error("Server doesn't exist "+node.serverConfig.server,err);
+ node.status({fill:"red",shape:"ring",text:"bad address"});
+ }
+ // nothing we've seen before!
+ else {
+ node.error("Unknown error: "+err,err);
+ node.status({fill:"red",shape:"ring",text:"error"});
+ }
+ }
+ }
+
+
function XmppInNode(n) {
RED.nodes.createNode(this,n);
this.server = n.server;
@@ -219,9 +338,27 @@ module.exports = function(RED) {
this.join = n.join || false;
this.sendAll = n.sendObject;
// Yes, it's called "from", don't ask me why; I don't know why
- this.from = n.to || "";
+ // (because it's where you are asking to get messages from...)
+ this.from = ((n.to || "").split(':')).map(s => s.trim());
+ this.quiet = false;
+ this.subject = {};
+ // MUC == Multi-User-Chat == chatroom
+ //this.muc = this.join && (this.from !== "")
var node = this;
+ var joinrooms = function() {
+ if (node.from[0] === "") {
+ // try to get list of all rooms and join them all.
+ getItems(node.serverConfig.server, node.serverConfig.id, xmpp);
+ }
+ else {
+ // if we want to use a chatroom, we need to tell the server we want to join it
+ for (var i=0; i {
- node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
- if ((node.join) && (node.from !== "")) {
- var to = node.from+'/'+node.nick;
- // the presence with the muc x element signifies we want to join the muc
- // if we want to support passwords, we need to add that as a child of the x element
- // (third argument to the x/muc/children )
- // We also turn off chat history (maxstanzas 0) because that's not what this node is about.
- var stanza = xml('presence',
- {"to": to},
- xml("x",'http://jabber.org/protocol/muc'),
- { maxstanzas:0, seconds:1 }
- );
- node.serverConfig.used(node);
- xmpp.send(stanza).catch(error => {node.warn("Got error when sending presence: "+error)});
+ node.quiet = false;
+ node.status({fill:"green",shape:"dot",text:"connected"});
+ if (node.join) {
+ node.jointick = setInterval(function() { joinrooms(); }, 60000);
+ joinrooms();
}
});
xmpp.on('connecting', async address => {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ if (!node.quiet) {
+ node.status({fill:"grey",shape:"dot",text:"connecting"});
+ }
});
xmpp.on('connect', async address => {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connected"});
+ node.status({fill:"grey",shape:"dot",text:"connected"});
});
xmpp.on('opening', async address => {
node.status({fill:"grey",shape:"dot",text:"opening"});
@@ -271,7 +409,7 @@ module.exports = function(RED) {
node.status({fill:"grey",shape:"dot",text:"closing"});
});
xmpp.on('close', async address => {
- node.status({fill:"grey",shape:"dot",text:"closed"});
+ node.status({fill:"grey",shape:"ring",text:"closed"});
});
xmpp.on('disconnecting', async address => {
node.status({fill:"grey",shape:"dot",text:"disconnecting"});
@@ -281,57 +419,29 @@ module.exports = function(RED) {
// Should we listen on other's status (chatstate) or a chatroom state (groupbuddy)?
xmpp.on('error', err => {
if (RED.settings.verbose || LOGITALL) { node.log("XMPP Error: "+err); }
- if (err.hasOwnProperty("stanza")) {
- if (err.stanza.name === 'stream:error') { node.error("stream:error - bad login id/pwd ?",err); }
- else { node.error(err.stanza.name,err); }
- node.status({fill:"red",shape:"ring",text:"bad login"});
- }
- else {
- if (err.errno === "ETIMEDOUT") {
- node.error("Timeout connecting to server",err);
- node.status({fill:"red",shape:"ring",text:"timeout"});
- }
- if (err.errno === "ENOTFOUND") {
- node.error("Server doesn't exist "+xmpp.options.service,err);
- node.status({fill:"red",shape:"ring",text:"bad address"});
- }
- else if (err === "XMPP authentication failure") {
- node.error("Authentication failure! "+err,err);
- node.status({fill:"red",shape:"ring",text:"XMPP authentication failure"});
- }
- else if (err.name === "SASLError") {
- node.error("Authorization error! "+err.condition,err);
- node.status({fill:"red",shape:"ring",text:"XMPP authorization failure"});
- }
- else if (err == "TimeoutError") {
- // Suppress it!
- node.warn("Timed out! ");
- node.status({fill:"grey",shape:"dot",text:"opening"});
- //node.status({fill:"red",shape:"ring",text:"XMPP timeout"});
- }
- else {
- node.error(err,err);
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
- }
- }
+ errorHandler(node, err);
});
// Meat of it, a stanza object contains chat messages (and other things)
- xmpp.on('stanza', async (stanza) =>{
- // node.log("Received stanza");
- if (RED.settings.verbose || LOGITALL) {node.log(stanza); }
+ xmpp.on('stanza', async (stanza) => {
+ if (RED.settings.verbose || LOGITALL) { node.log(stanza); }
if (stanza.is('message')) {
+ var subj = stanza.getChild("subject");
+ if (subj) {
+ subj = subj.getText();
+ if (subj.trim() !== "") { node.subject[stanza.attrs.from.split('/')[0]] = subj; }
+ }
if (stanza.attrs.type == 'chat') {
var body = stanza.getChild('body');
if (body) {
- var msg = { payload:body.getText() };
+ var msg = { payload:body.getText(), subject:node.subject[stanza.attrs.from.split('/')[0]] };
var ids = stanza.attrs.from.split('/');
if (ids[1].length !== 36) {
msg.topic = stanza.attrs.from
}
else { msg.topic = ids[0]; }
- // if (RED.settings.verbose || LOGITALL) {node.log("Received a message from "+stanza.attrs.from); }
- if (!node.join && ((node.from === "") || (node.from === stanza.attrs.to))) {
+ // if (RED.settings.verbose || LOGITALL) { node.log("Received a message from "+stanza.attrs.from); }
+ if (!node.join && ((node.from[0] === "") || (node.from.includes(stanza.attrs.to)))) {
node.send([msg,null]);
}
}
@@ -340,42 +450,74 @@ module.exports = function(RED) {
const parts = stanza.attrs.from.split("/");
var conference = parts[0];
var from = parts[1];
+ var msg = { topic:from, room:conference, subject:node.subject[stanza.attrs.from.split('/')[0]] };
var body = stanza.getChild('body');
- var payload = "";
- if ("undefined" !== typeof body) {
- payload = body.getText();
- }
- var msg = { topic:from, payload:payload, room:conference };
- if (stanza.attrs.from != node.nick) {
- if ((node.join) && (node.from === conference)) {
+ if (typeof body !== "undefined") {
+ msg.payload = body.getText();
+ //if (from && stanza.attrs.from != node.nick && from != node.nick) {
+ if (from && node.join && (node.from[0] === "" || node.from.includes(conference))) {
node.send([msg,null]);
}
}
+ //}
}
}
else if (stanza.is('presence')) {
if (['subscribe','subscribed','unsubscribe','unsubscribed'].indexOf(stanza.attrs.type) > -1) {
// this isn't for us, let the config node deal with it.
-
}
- else{
+ else {
+ if (stanza.attrs.type === 'error') {
+ var error = stanza.getChild('error');
+ if (error.attrs.code) {
+ var reas = "";
+ try {
+ reas = error.toString().split('><')[1].split(" xml")[0].trim();
+ if (reas == "registration-required") { reas = "membership-required"; }
+ }
+ catch(e) {}
+ var msg = {
+ topic:stanza.attrs.from,
+ payload: {
+ code:error.attrs.code,
+ status:"error",
+ reason:reas,
+ name:node.serverConfig.MUCs[stanza.attrs.from.split('/')[0]]
+ }
+ };
+ node.send([null,msg]);
+ node.status({fill:"red",shape:"ring",text:"error : "+error.attrs.code+", "+error.attrs.type+", "+reas});
+ node.error(error.attrs.type+" error. "+error.attrs.code+" "+reas,msg);
+ }
+ }
+
+ var state = stanza.getChild('show');
+ if (state) { state = state.getText(); }
+ else { state = "available"; }
var statusText="";
if (stanza.attrs.type === 'unavailable') {
// the user might not exist, but the server doesn't tell us that!
statusText = "offline";
+ state = "offline";
}
var status = stanza.getChild('status');
- if ("undefined" !== typeof status) {
+ if (typeof status !== "undefined") {
statusText = status.getText();
}
// right, do we care if there's no status?
if (statusText !== "") {
var from = stanza.attrs.from;
- var state = stanza.attrs.show;
- var msg = {topic:from, payload: {presence:state, status:statusText} };
+ var msg = {
+ topic:from,
+ payload: {
+ presence:state,
+ status:statusText,
+ name:node.serverConfig.MUCs[stanza.attrs.from.split('/')[0]]
+ }
+ };
node.send([null,msg]);
}
- else{
+ else {
if (RED.settings.verbose || LOGITALL) {
node.log("not propagating blank status");
node.log(stanza);
@@ -383,6 +525,49 @@ module.exports = function(RED) {
}
}
}
+ else if (stanza.attrs.type === 'result') {
+ // AM To-Do check for 'bind' result with our current jid
+ var query = stanza.getChild('query');
+ if (RED.settings.verbose || LOGITALL) { this.log("result!"); }
+ if (RED.settings.verbose || LOGITALL) { this.log(query); }
+
+ // handle query for list of rooms available
+ if (query && query.attrs.hasOwnProperty("xmlns") && query.attrs["xmlns"] === "http://jabber.org/protocol/disco#items") {
+ var _items = stanza.getChild('query').getChildren('item');
+ for (var i = 0; i<_items.length; i++) {
+ if ( _items[i].attrs.jid.indexOf('@') === -1 ) {
+ // if no @ in jid then it's probably the root or the room server so ask again
+ getItems(_items[i].attrs.jid,this.serverConfig.jid,xmpp);
+ }
+ else {
+ var name = _items[i].attrs.jid+'/'+node.serverConfig.username;
+ if (!(name in node.serverConfig.MUCs)) {
+ if (RED.settings.verbose || LOGITALL) { node.log("Need to Join room:"+name); }
+ joinMUC(node, xmpp, name);
+ node.serverConfig.MUCs[name.split('/')[0]] = _items[i].attrs.name.split('/')[0];
+ }
+ else {
+ if (RED.settings.verbose || LOGITALL) { node.log("Already joined:"+name); }
+ }
+ }
+ }
+ }
+ if (query && query.attrs.hasOwnProperty("xmlns") && query.attrs["xmlns"] === "http://jabber.org/protocol/disco#info") {
+ var fe = [];
+ var _items = stanza.getChild('query').getChildren('feature');
+ for (var i = 0; i<_items.length; i++) {
+ fe.push(_items[i].attrs);
+ }
+ var id = []
+ var _idents = stanza.getChild('query').getChildren('identity');
+ for (var i = 0; i<_idents.length; i++) {
+ id.push(_idents[i].attrs);
+ }
+ var from = stanza.attrs.from;
+ var msg = {topic:from, payload: { identity:id, features:fe} };
+ node.send([null,msg]);
+ }
+ }
});
// xmpp.on('subscribe', from => {
@@ -394,31 +579,38 @@ module.exports = function(RED) {
// Now actually make the connection
try {
if (xmpp.status === "online") {
- node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ node.status({fill:"green",shape:"dot",text:"connected"});
}
- else{
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ else {
+ node.status({fill:"grey",shape:"dot",text:"connecting"});
if (xmpp.status === "offline") {
if (RED.settings.verbose || LOGITALL) {
node.log("starting xmpp client");
}
- xmpp.start().catch(error => {node.warn("Got error on start: "+error); node.warn("XMPP Status is now: "+xmpp.status)});
+ xmpp.start().catch(error => {
+ node.warn("Got error on start: "+error);
+ node.warn("XMPP Status is now: "+xmpp.status)
+ });
}
}
}
catch(e) {
node.error("Bad xmpp configuration; service: "+xmpp.options.service+" jid: "+node.serverConfig.jid);
- node.warn(e);
node.warn(e.stack);
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
+ node.status({fill:"red",shape:"ring",text:"disconnected"});
}
node.on("close", function(removed, done) {
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
+ if (node.jointick) { clearInterval(node.jointick); }
+ node.status({fill:"grey",shape:"ring",text:"disconnected"});
node.serverConfig.deregister(node, done);
});
}
- RED.nodes.registerType("xmpp in",XmppInNode);
+ RED.nodes.registerType("xmpp in",XmppInNode,{
+ credentials: {
+ password: {type: "password"}
+ }
+ });
function XmppOutNode(n) {
@@ -429,6 +621,9 @@ module.exports = function(RED) {
this.join = n.join || false;
this.sendAll = n.sendObject;
this.to = n.to || "";
+ this.quiet = false;
+ // MUC == Multi-User-Chat == chatroom
+ this.muc = this.join && (this.to !== "")
var node = this;
var xmpp = this.serverConfig.client;
@@ -446,30 +641,31 @@ module.exports = function(RED) {
disconnect: Socket is disconnected
*/
+ // if we're already connected, then do the actions now, otherwise register a callback
+ // if (xmpp.status === "online") {
+ // node.status({fill:"green",shape:"dot",text:"connected"});
+ // if (node.muc){
+ // // if we want to use a chatroom, we need to tell the server we want to join it
+ // joinMUC(node, xmpp, node.from+'/'+node.nick);
+ // }
+ // }
+ // sod it, register it anyway, that way things will work better on a reconnect:
xmpp.on('online', function(data) {
- node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
- if ((node.join) && (node.to !== "")) {
- // disable chat history
- var to = node.to+'/'+node.nick;
- // the presence with the muc x element signifies we want to join the muc
- // if we want to support passwords, we need to add that as a child of the x element
- // (third argument to the x/muc/children )
- // We also turn off chat history (maxstanzas 0) because that's not what this node is about.
- var stanza = xml('presence',
- {"to": to},
- xml("x",'http://jabber.org/protocol/muc'),
- { maxstanzas:0, seconds:1 }
- );
- node.serverConfig.used(node);
- xmpp.send(stanza);
+ node.quiet = false;
+ node.status({fill:"green",shape:"dot",text:"connected"});
+ if (node.muc) {
+ // if we want to use a chatroom, we need to tell the server we want to join it
+ joinMUC(node, xmpp, node.from+'/'+node.nick);
}
});
xmpp.on('connecting', async address => {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ if (!node.quiet) {
+ node.status({fill:"grey",shape:"dot",text:"connecting"});
+ }
});
xmpp.on('connect', async address => {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connected"});
+ node.status({fill:"grey",shape:"dot",text:"connected"});
});
xmpp.on('opening', async address => {
node.status({fill:"grey",shape:"dot",text:"opening"});
@@ -490,33 +686,30 @@ module.exports = function(RED) {
xmpp.on('error', function(err) {
if (RED.settings.verbose || LOGITALL) { node.log(err); }
- if (err.hasOwnProperty("stanza")) {
- if (err.stanza.name === 'stream:error') { node.error("stream:error - bad login id/pwd ?",err); }
- else { node.error(err.stanza.name,err); }
- node.status({fill:"red",shape:"ring",text:"bad login"});
- }
- else {
- if (err.errno === "ETIMEDOUT") {
- node.error("Timeout connecting to server",err);
- node.status({fill:"red",shape:"ring",text:"timeout"});
- }
- else if (err.errno === "ENOTFOUND") {
- node.error("Server doesn't exist "+xmpp.options.service,err);
- node.status({fill:"red",shape:"ring",text:"bad address"});
- }
- else if (err === "XMPP authentication failure") {
- node.error(err,err);
- node.status({fill:"red",shape:"ring",text:"XMPP authentication failure"});
- }
- else if (err == "TimeoutError") {
- // OK, this happens with OpenFire, suppress it.
- node.status({fill:"grey",shape:"dot",text:"opening"});
- node.log("Timed out! ",err);
- // node.status({fill:"red",shape:"ring",text:"XMPP timeout"});
- }
- else {
- node.error("Unknown error: "+err,err);
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
+ errorHandler(node, err)
+ });
+
+ xmpp.on('stanza', async (stanza) => {
+ if (stanza.attrs.type === 'error') {
+ var error = stanza.getChild('error');
+ if (error.attrs.code) {
+ var reas = "";
+ try {
+ reas = error.toString().split('><')[1].split(" xml")[0].trim();
+ if (reas == "registration-required") { reas = "membership-required"; }
+ }
+ catch(e) {}
+ var msg = {
+ topic:stanza.attrs.from,
+ payload: {
+ code:error.attrs.code,
+ status:"error",
+ reason:reas,
+ name:node.serverConfig.MUCs[stanza.attrs.from.split('/')[0]]
+ }
+ };
+ node.status({fill:"red",shape:"ring",text:"error : "+error.attrs.code+", "+error.attrs.type+", "+reas});
+ node.error(error.attrs.type+" error. "+error.attrs.code+" "+reas,msg);
}
}
});
@@ -527,25 +720,24 @@ module.exports = function(RED) {
if (xmpp.status === "online") {
node.status({fill:"green",shape:"dot",text:"online"});
}
- else{
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ else {
+ node.status({fill:"grey",shape:"dot",text:"connecting"});
if (xmpp.status === "offline") {
xmpp.start().catch(error => {
node.error("Bad xmpp configuration; service: "+xmpp.options.service+" jid: "+node.serverConfig.jid);
node.warn(error);
node.warn(error.stack);
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
+ node.status({fill:"red",shape:"ring",text:"error"});
});
}
}
// Let's get down to business and actually send a message
node.on("input", function(msg) {
+ var to = node.to || msg.topic || "";
if (msg.presence) {
if (['away', 'dnd', 'xa', 'chat'].indexOf(msg.presence) > -1 ) {
- var stanza = xml('presence',
- {"show":msg.presence},
- xml('status',{},msg.payload));
+ var stanza = xml('presence', {"show":msg.presence}, xml('status', {}, msg.payload));
node.serverConfig.used(node);
xmpp.send(stanza);
}
@@ -553,35 +745,56 @@ module.exports = function(RED) {
}
else if (msg.command) {
if (msg.command === "subscribe") {
- var stanza = xml('presence',
- {type:'subscribe', to: msg.payload});
+ var stanza = xml('presence', {type:'subscribe', to:msg.payload});
node.serverConfig.used(node);
xmpp.send(stanza);
}
else if (msg.command === "get") {
- var to = node.to || msg.topic || "";
var stanza = xml('iq',
- {type:'get', id:node.id, to: to},
+ {type:'get', id:node.id, to:to},
xml('query', 'http://jabber.org/protocol/muc#admin',
- xml('item',{affiliation:msg.payload})));
+ xml('item', {affiliation:msg.payload})
+ )
+ );
node.serverConfig.used(node);
- if (RED.settings.verbose || LOGITALL) {node.log("sending stanza "+stanza.toString()); }
+ if (RED.settings.verbose || LOGITALL) { node.log("sending stanza "+stanza.toString()); }
+ xmpp.send(stanza);
+ }
+ else if (msg.command === "info") {
+ var stanza = xml('iq',
+ {type:'get', id:node.id, to:to},
+ xml('query', 'http://jabber.org/protocol/disco#info')
+ );
+ node.serverConfig.used(node);
+ if (RED.settings.verbose || LOGITALL) { node.log("sending stanza "+stanza.toString()); }
xmpp.send(stanza);
}
-
}
else {
- var to = node.to || msg.topic || "";
if (to !== "") {
var message;
- var type = node.join? "groupchat":"chat";
+ var type = "chat";
+ if (node.join) {
+ // we want to connect to groupchat / chatroom / MUC
+ type = "groupchat";
+ // joinMUC will do nothing if we're already joined
+ joinMUC(node, xmpp, to+'/'+node.nick);
+ }
+ if (msg.subject) {
+ var stanza = xml(
+ "message",
+ { type:type, to:to, from:node.serverConfig.jid },
+ xml("subject", {}, msg.subject.toString())
+ );
+ node.serverConfig.used(node);
+ xmpp.send(stanza);
+ }
if (node.sendAll) {
message = xml(
"message",
{ type: type, to: to },
xml("body", {}, JSON.stringify(msg))
);
-
}
else if (msg.payload) {
if (typeof(msg.payload) === "object") {
@@ -599,17 +812,23 @@ module.exports = function(RED) {
);
}
}
- node.serverConfig.used(node);
- xmpp.send(message);
+ if (message) {
+ node.serverConfig.used(node);
+ xmpp.send(message);
+ }
}
}
});
node.on("close", function(removed, done) {
- if (RED.settings.verbose || LOGITALL) {node.log("Closing"); }
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
+ if (RED.settings.verbose || LOGITALL) { node.log("Closing"); }
+ node.status({fill:"grey",shape:"ring",text:"disconnected"});
node.serverConfig.deregister(node, done);
});
}
- RED.nodes.registerType("xmpp out",XmppOutNode);
+ RED.nodes.registerType("xmpp out",XmppOutNode,{
+ credentials: {
+ password: {type: "password"}
+ }
+ });
}
diff --git a/social/xmpp/package.json b/social/xmpp/package.json
index 44381f89..7b611b78 100644
--- a/social/xmpp/package.json
+++ b/social/xmpp/package.json
@@ -1,10 +1,13 @@
{
"name": "node-red-node-xmpp",
- "version": "0.3.1",
+ "version": "0.5.1",
"description": "A Node-RED node to talk to an XMPP server",
"dependencies": {
- "@xmpp/client": "^0.11.1"
+ "@xmpp/client": "^0.12.0"
},
+ "bundledDependencies": [
+ "@xmpp/client"
+ ],
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red-nodes/tree/master/social/xmpp"
diff --git a/storage/leveldb/67-leveldb.html b/storage/leveldb/67-leveldb.html
index 60b75414..ae7969ce 100644
--- a/storage/leveldb/67-leveldb.html
+++ b/storage/leveldb/67-leveldb.html
@@ -6,7 +6,7 @@
Values are
-
+
text
JSON
@@ -39,13 +39,6 @@
-
-
-
-
-
-
+
+
diff --git a/storage/mongodb/66-mongodb.html b/storage/mongodb/66-mongodb.html
index 12f904e5..b1201b1d 100644
--- a/storage/mongodb/66-mongodb.html
+++ b/storage/mongodb/66-mongodb.html
@@ -74,44 +74,6 @@
});
-
-
-
-
-
-
-
+
+
+
+
diff --git a/storage/mongodb/package.json b/storage/mongodb/package.json
index 599f8a52..aaa8ee79 100644
--- a/storage/mongodb/package.json
+++ b/storage/mongodb/package.json
@@ -1,9 +1,9 @@
{
"name" : "node-red-node-mongodb",
- "version" : "0.2.4",
- "description" : "Node-RED nodes to talk to an Mongo database",
+ "version" : "0.2.5",
+ "description" : "Node-RED nodes to talk to a Mongo database",
"dependencies" : {
- "mongodb" : "^3.6.2"
+ "mongodb" : "^3.6.3"
},
"repository" : {
"type":"git",
diff --git a/storage/mysql/68-mysql.html b/storage/mysql/68-mysql.html
index 37dca22d..8a12a9af 100644
--- a/storage/mysql/68-mysql.html
+++ b/storage/mysql/68-mysql.html
@@ -55,13 +55,6 @@
});
-
-
-
-
-
+
+
diff --git a/storage/mysql/package.json b/storage/mysql/package.json
index 8ac128f0..fdeedac3 100644
--- a/storage/mysql/package.json
+++ b/storage/mysql/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-mysql",
- "version": "0.1.1",
+ "version": "0.1.7",
"description": "A Node-RED node to read and write to a MySQL database",
"dependencies": {
"mysql": "^2.18.1"
diff --git a/storage/redis/65-redisout.html b/storage/redis/65-redisout.html
index 1811ad16..126bae50 100644
--- a/storage/redis/65-redisout.html
+++ b/storage/redis/65-redisout.html
@@ -1,5 +1,5 @@
-
-
-
diff --git a/storage/redis/package.json b/storage/redis/package.json
index ed01e7e5..23d26d83 100644
--- a/storage/redis/package.json
+++ b/storage/redis/package.json
@@ -1,6 +1,6 @@
{
"name" : "node-red-node-redis",
- "version" : "0.0.5",
+ "version" : "0.0.6",
"description" : "A Node-RED node to save data to an Redis database",
"dependencies" : {
"redis" : "0.12.1"
diff --git a/storage/sqlite/locales/de/sqlite.json b/storage/sqlite/locales/de/sqlite.json
new file mode 100644
index 00000000..00357295
--- /dev/null
+++ b/storage/sqlite/locales/de/sqlite.json
@@ -0,0 +1,20 @@
+{
+ "sqlite": {
+ "label": {
+ "database": "Datenbank",
+ "sqlQuery": "SQL-Abfrage",
+ "viaMsgTopic": "Via msg.topic",
+ "fixedStatement": "Fester Ausdruck",
+ "preparedStatement": "Vorbereiteter Ausdruck",
+ "batchWithoutResponse": "Batch ohne Antwort",
+ "sqlStatement": "SQL-Ausdruck (Statement)",
+ "mode": "Modus",
+ "readWriteCreate": "Lesen-Schreiben-Erzeugen",
+ "readWrite": "Lesen-Schreiben",
+ "readOnly": "Nur-Lesen"
+ },
+ "tips": {
+ "memoryDb": "Hinweis: Das Setzen der Datenbank auf :memory:
erzeugt eine flüchtige (nicht-persistente) Datenbank im Speicher."
+ }
+ }
+}
diff --git a/storage/sqlite/package.json b/storage/sqlite/package.json
index ed646689..a6baa081 100644
--- a/storage/sqlite/package.json
+++ b/storage/sqlite/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-sqlite",
- "version": "0.4.4",
+ "version": "0.6.0",
"description": "A sqlite node for Node-RED",
"dependencies": {
"sqlite3": "~4.2.0"
diff --git a/storage/tail/28-tail.html b/storage/tail/28-tail.html
index 4f7c2063..2138dd2c 100644
--- a/storage/tail/28-tail.html
+++ b/storage/tail/28-tail.html
@@ -1,4 +1,4 @@
-
diff --git a/storage/tail/locales/de/28-tail.json b/storage/tail/locales/de/28-tail.json
new file mode 100644
index 00000000..75482856
--- /dev/null
+++ b/storage/tail/locales/de/28-tail.json
@@ -0,0 +1,24 @@
+{
+ "tail": {
+ "tail": "tail",
+ "label": {
+ "filename": "Dateiname",
+ "type": "Dateityp",
+ "splitlines": "Aufteilung mittels",
+ "name": "Name",
+ "regex": "Aufteilungszeichen oder regulärer Ausdruck (Regex)"
+ },
+ "action": {
+ "text": "Text (String-Rückgabe)",
+ "binary": "Binary (Buffer-Rückgabe)"
+ },
+ "errors": {
+ "windowsnotsupport": "Zurzeit nicht unterstützt unter Windows",
+ "filenotfound": "Datei nicht gefunden"
+ },
+ "state":{
+ "stopped": "Gestoppt",
+ "nofilename":"Fehlender Dateiname am Eingang"
+ }
+ }
+}
diff --git a/storage/tail/locales/en-US/28-tail.html b/storage/tail/locales/en-US/28-tail.html
index 25633430..deb674df 100644
--- a/storage/tail/locales/en-US/28-tail.html
+++ b/storage/tail/locales/en-US/28-tail.html
@@ -1,6 +1,5 @@
diff --git a/storage/tail/package.json b/storage/tail/package.json
index 38a7e4b2..ca5b5911 100644
--- a/storage/tail/package.json
+++ b/storage/tail/package.json
@@ -1,9 +1,9 @@
{
"name": "node-red-node-tail",
- "version": "0.1.1",
+ "version": "0.3.1",
"description": "A node to tail files for Node-RED",
"dependencies": {
- "tail": "^2.0.3"
+ "tail": "^2.2.2"
},
"repository": {
"type": "git",
diff --git a/test/function/rbe/rbe_spec.js b/test/function/rbe/rbe_spec.js
index 8d220c15..0f8e8747 100644
--- a/test/function/rbe/rbe_spec.js
+++ b/test/function/rbe/rbe_spec.js
@@ -27,7 +27,7 @@ describe('rbe node', function() {
});
});
- it('should only send output if payload changes (rbe)', function(done) {
+ it('should only send output if payload changes - with multiple topics (rbe)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
@@ -57,8 +57,24 @@ describe('rbe node', function() {
msg.should.have.a.property("payload",false);
c+=1;
}
- else {
+ else if (c == 5) {
msg.should.have.a.property("payload",true);
+ c+=1;
+ }
+ else if (c == 6) {
+ msg.should.have.a.property("topic","a");
+ msg.should.have.a.property("payload",1);
+ c+=1;
+ }
+ else if (c == 7) {
+ msg.should.have.a.property("topic","b");
+ msg.should.have.a.property("payload",1);
+ c+=1;
+ }
+ else {
+ c += 1;
+ msg.should.have.a.property("topic","c");
+ msg.should.have.a.property("payload",1);
done();
}
});
@@ -74,6 +90,80 @@ describe('rbe node', function() {
n1.emit("input", {payload:false});
n1.emit("input", {payload:false});
n1.emit("input", {payload:true});
+
+ n1.emit("input", {topic:"a",payload:1});
+ n1.emit("input", {topic:"b",payload:1});
+ n1.emit("input", {topic:"b",payload:1});
+ n1.emit("input", {topic:"a",payload:1});
+ n1.emit("input", {topic:"c",payload:1});
+
+ });
+ });
+
+ it('should ignore multiple topics if told to (rbe)', function(done) {
+ var flow = [{id:"n1", type:"rbe", func:"rbe", gap:"0", septopics:false, wires:[["n2"]] },
+ {id:"n2", type:"helper"} ];
+ helper.load(testNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ var c = 0;
+ n2.on("input", function(msg) {
+ if (c === 0) {
+ msg.should.have.a.property("payload", "a");
+ c+=1;
+ }
+ else if (c === 1) {
+ msg.should.have.a.property("payload", 2);
+ c+=1;
+ }
+ else if (c == 2) {
+ msg.should.have.a.property("payload");
+ msg.payload.should.have.a.property("b",1);
+ msg.payload.should.have.a.property("c",2);
+ c+=1;
+ }
+ else if (c == 3) {
+ msg.should.have.a.property("payload",true);
+ c+=1;
+ }
+ else if (c == 4) {
+ msg.should.have.a.property("payload",false);
+ c+=1;
+ }
+ else if (c == 5) {
+ msg.should.have.a.property("payload",true);
+ c+=1;
+ }
+ else if (c == 6) {
+ msg.should.have.a.property("topic","a");
+ msg.should.have.a.property("payload",1);
+ c+=1;
+ }
+ else {
+ msg.should.have.a.property("topic","a");
+ msg.should.have.a.property("payload",2);
+ done();
+ }
+ });
+ n1.emit("input", {topic:"a",payload:"a"});
+ n1.emit("input", {topic:"b",payload:"a"});
+ n1.emit("input", {topic:"c",payload:"a"});
+ n1.emit("input", {topic:"a",payload:2});
+ n1.emit("input", {topic:"b",payload:2});
+ n1.emit("input", {payload:{b:1,c:2}});
+ n1.emit("input", {payload:{c:2,b:1}});
+ n1.emit("input", {payload:{c:2,b:1}});
+ n1.emit("input", {topic:"a",payload:true});
+ n1.emit("input", {topic:"b",payload:false});
+ n1.emit("input", {topic:"c",payload:false});
+ n1.emit("input", {topic:"d",payload:true});
+
+ n1.emit("input", {topic:"a",payload:1});
+ n1.emit("input", {topic:"b",payload:1});
+ n1.emit("input", {topic:"c",payload:1});
+ n1.emit("input", {topic:"d",payload:1});
+ n1.emit("input", {topic:"a",payload:2});
+
});
});
diff --git a/test/hardware/PiGpio/36-rpi-gpio_spec.js b/test/hardware/PiGpio/36-rpi-gpio_spec.js
index 5f5ac75e..d4c7ddf0 100644
--- a/test/hardware/PiGpio/36-rpi-gpio_spec.js
+++ b/test/hardware/PiGpio/36-rpi-gpio_spec.js
@@ -92,14 +92,14 @@ describe('RPI GPIO Node', function() {
it('should read a dummy value high (not on Pi)', function(done) {
- var flow = [{id:"n1", type:"rpi-gpio in", pin:"7", intype:"up", debounce:"25", read:true, wires:[["n2"]] },
- {id:"n2", type:"helper"}];
+ var flow = [{id:"n1", type:"rpi-gpio in", pin:"7", intype:"up", debounce:"25", read:true, bcm:true, wires:[["n2"]] },
+ {id:"n2", type:"helper"}];
helper.load(rpiNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
- msg.should.have.property('topic', 'pi/7');
+ msg.should.have.property('topic', 'gpio/7');
msg.should.have.property('payload', 1);
done();
} catch(err) {
@@ -110,14 +110,14 @@ describe('RPI GPIO Node', function() {
});
it('should read a dummy value low (not on Pi)', function(done) {
- var flow = [{id:"n1", type:"rpi-gpio in", pin:"11", intype:"down", debounce:"25", read:true, wires:[["n2"]] },
- {id:"n2", type:"helper"}];
+ var flow = [{id:"n1", type:"rpi-gpio in", pin:"11", intype:"down", debounce:"25", read:true, bcm:true, wires:[["n2"]] },
+ {id:"n2", type:"helper"}];
helper.load(rpiNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
- msg.should.have.property('topic', 'pi/11');
+ msg.should.have.property('topic', 'gpio/11');
msg.should.have.property('payload', 0);
done();
} catch(err) {
diff --git a/test/utility/exif/94-exif_spec.js b/test/utility/exif/94-exif_spec.js
index 1e2efd37..6a5460e9 100644
--- a/test/utility/exif/94-exif_spec.js
+++ b/test/utility/exif/94-exif_spec.js
@@ -1,9 +1,12 @@
-var should = require("should");
+// var should = require("should");
var sinon = require('sinon');
-//var fs = require("fs");
+var fs = require("fs");
var helper = require("node-red-node-test-helper");
var exifNode = require('../../../utility/exif/94-exif.js');
+// var exif = require('exif');
+var path = require("path");
+var image = fs.readFileSync(path.join(__dirname,"test.jpeg"));
describe('exif node', function() {
"use strict";
@@ -19,45 +22,47 @@ describe('exif node', function() {
});
it('extracts location data from Exif data of JPEG', function(done) {
- var exif = require('exif');
- var ExifImage = exif.ExifImage;
+ //var ExifImage = exif.ExifImage;
// the jpg file is a single black dot but it was originally a photo taken at IBM Hursley
//console.log(process.cwd());
//var data = fs.readFileSync("test/utility/exif/exif_test_image.jpg", null); // extracting genuine exif data to be fed back as the result of the stubbed ExifImage constructor
//var data = fs.readFileSync("exif_test_image.jpg", null); // extracting genuine exif data to be fed back as the result of the stubbed ExifImage constructor
- var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]},
+ var flow = [{id:"exifNode1", type:"exif", mode:"normal", property:"payload", wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper"}];
- var gpsmsg = { gps: { GPSLatitudeRef: 'N',
- GPSLatitude: [ 50, 57, 22.4697 ],
- GPSLongitudeRef: 'W',
- GPSLongitude: [ 1, 22, 1.2467 ],
- GPSAltitudeRef: 0,
- GPSAltitude: 50,
- GPSTimeStamp: [ 7, 32, 2 ],
- GPSImgDirectionRef: 'M',
- GPSImgDirection: 267,
- GPSProcessingMethod: 'ASCII\u0000\u0000\u0000FUSED',
- GPSDateStamp: '2014:06:10' }
- };
- var spy = sinon.stub(exif, 'ExifImage').callsFake(function(arg1,arg2) { arg2(null,gpsmsg); });
+ // var gpsmsg = { gps: { GPSLatitudeRef: 'N',
+ // GPSLatitude: [ 50, 57, 22.4697 ],
+ // GPSLongitudeRef: 'W',
+ // GPSLongitude: [ 1, 22, 1.2467 ],
+ // GPSAltitudeRef: 0,
+ // GPSAltitude: 50,
+ // GPSTimeStamp: [ 7, 32, 2 ],
+ // GPSImgDirectionRef: 'M',
+ // GPSImgDirection: 267,
+ // GPSProcessingMethod: 'ASCII\u0000\u0000\u0000FUSED',
+ // GPSDateStamp: '2014:06:10' }
+ // };
+ // var stub = sinon.stub(exif, 'ExifImage').callsFake(function(arg1,arg2) {
+ // console.log("DING",arg1,arg2);
+ // arg2(null,gpsmsg);
+ // });
helper.load(exifNode, flow, function() {
var exifNode1 = helper.getNode("exifNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
- msg.location.lat.should.equal(50.95624); // this data is stored in the jpg file
- msg.location.lon.should.equal(-1.36701);
- exif.ExifImage.restore();
+ // exif.ExifImage.restore();
+ msg.location.lat.should.equal(51.04365); // this data is stored in the jpg file
+ msg.location.lon.should.equal(-1.31525);
done();
});
- exifNode1.receive({payload:new Buffer.from("hello")});
+ exifNode1.receive({payload:image});
});
});
- it('extracts location data in Southern and Eastern hemispheres', function(done) {
+ it.skip('extracts location data in Southern and Eastern hemispheres', function(done) {
var exif = require('exif');
var ExifImage = exif.ExifImage;
// the jpg file is a single black dot but it was originally a photo taken at IBM Hursley
@@ -77,16 +82,18 @@ describe('exif node', function() {
GPSProcessingMethod: 'ASCII\u0000\u0000\u0000FUSED',
GPSDateStamp: '2014:06:10' }
};
- var spy = sinon.stub(exif, 'ExifImage').callsFake(function(arg1,arg2) { arg2(null,gpsmsg); });
+ var spy = sinon.stub(exif, 'ExifImage').callsFake(function(arg1,arg2) {
+ arg2(null,gpsmsg);
+ });
helper.load(exifNode, flow, function() {
var exifNode1 = helper.getNode("exifNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
+ exif.ExifImage.restore();
msg.location.lat.should.equal(-50.95624); // this data is stored in the jpg file
msg.location.lon.should.equal(1.36701);
- exif.ExifImage.restore();
done();
});
@@ -99,7 +106,6 @@ describe('exif node', function() {
var ExifImage = exif.ExifImage;
// this time just use a buffer that isn't an jpeg image
var data = new Buffer.from("hello");
- var eD;
var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper"}];
@@ -125,7 +131,6 @@ describe('exif node', function() {
var exif = require('exif');
var ExifImage = exif.ExifImage;
var data = "hello";
- var eD;
var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper"}];
@@ -151,8 +156,7 @@ describe('exif node', function() {
var exif = require('exif');
var ExifImage = exif.ExifImage;
var data = new Buffer.from("hello");
- var eD;
- var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]},
+ var flow = [{id:"exifNode1", type:"exif", property:"payload", wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper"}];
helper.load(exifNode, flow, function() {
@@ -165,7 +169,7 @@ describe('exif node', function() {
});
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
- logEvents[0][0].msg.toString().should.startWith("No payload received, ");
+ logEvents[0][0].msg.toString().should.startWith("No input received, ");
done();
},150);
@@ -173,11 +177,10 @@ describe('exif node', function() {
});
});
- it('should report if bad latitude', function(done) {
+ it.skip('should report if bad latitude', function(done) {
var exif = require('exif');
var ExifImage = exif.ExifImage;
var data = new Buffer.from("hello");
- var eD;
var flow = [{id:"exifNode1", type:"exif", wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper"}];
@@ -189,23 +192,22 @@ describe('exif node', function() {
GPSAltitude: 50,
GPSTimeStamp: [ 7, 32, 2 ] }
};
- var spy = sinon.stub(exif, 'ExifImage').callsFake(
- function(arg1,arg2){
- arg2(null,gpsmsg);
- });
+ var spy = sinon.stub(exif, 'ExifImage').callsFake( function(arg1,arg2){
+ arg2(null,gpsmsg);
+ });
helper.load(exifNode, flow, function() {
var exifNode1 = helper.getNode("exifNode1");
var helperNode1 = helper.getNode("helperNode1");
setTimeout(function() {
+ exif.ExifImage.restore();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "exif";
});
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("Invalid latitude data,");
- exif.ExifImage.restore();
done();
},150);
@@ -213,7 +215,7 @@ describe('exif node', function() {
});
});
- it('should report if bad longitude', function(done) {
+ it.skip('should report if bad longitude', function(done) {
var exif = require('exif');
var ExifImage = exif.ExifImage;
var data = new Buffer.from("hello");
@@ -229,23 +231,22 @@ describe('exif node', function() {
GPSAltitude: 50,
GPSTimeStamp: [ 7, 32, 2 ] }
};
- var spy = sinon.stub(exif, 'ExifImage').callsFake(
- function(arg1,arg2){
- arg2(null,gpsmsg);
- });
+ var spy = sinon.stub(exif, 'ExifImage').callsFake( function(arg1,arg2){
+ arg2(null,gpsmsg);
+ });
helper.load(exifNode, flow, function() {
var exifNode1 = helper.getNode("exifNode1");
var helperNode1 = helper.getNode("helperNode1");
setTimeout(function() {
+ exif.ExifImage.restore();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "exif";
});
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("Invalid longitude data,");
- exif.ExifImage.restore();
done();
},150);
@@ -253,7 +254,7 @@ describe('exif node', function() {
});
});
- it('should report if unsure about location', function(done) {
+ it.skip('should report if unsure about location', function(done) {
var exif = require('exif');
var ExifImage = exif.ExifImage;
var data = new Buffer.from("hello");
@@ -267,28 +268,26 @@ describe('exif node', function() {
GPSAltitude: 50,
GPSTimeStamp: [ 7, 32, 2 ] }
};
- var spy = sinon.stub(exif, 'ExifImage').callsFake(
- function(arg1,arg2){
- arg2(null,gpsmsg);
- });
+ var spy = sinon.stub(exif, 'ExifImage').callsFake( function(arg1,arg2) {
+ arg2(null,gpsmsg);
+ });
helper.load(exifNode, flow, function() {
var exifNode1 = helper.getNode("exifNode1");
var helperNode1 = helper.getNode("helperNode1");
setTimeout(function() {
+ exif.ExifImage.restore();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "exif";
});
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("The location of this image cannot be determined safely");
- exif.ExifImage.restore();
done();
},150);
exifNode1.receive({payload:data});
});
});
-
});
diff --git a/test/utility/exif/test.jpeg b/test/utility/exif/test.jpeg
new file mode 100644
index 00000000..c7794c9d
Binary files /dev/null and b/test/utility/exif/test.jpeg differ
diff --git a/time/suncalc/79-suncalc.html b/time/suncalc/79-suncalc.html
index 16689f3e..65742792 100644
--- a/time/suncalc/79-suncalc.html
+++ b/time/suncalc/79-suncalc.html
@@ -41,17 +41,6 @@
-
-
diff --git a/time/timeswitch/locales/en-US/timeswitch.html b/time/timeswitch/locales/en-US/timeswitch.html
new file mode 100644
index 00000000..4609435c
--- /dev/null
+++ b/time/timeswitch/locales/en-US/timeswitch.html
@@ -0,0 +1,12 @@
+
+
diff --git a/time/timeswitch/package.json b/time/timeswitch/package.json
index 58a628cd..9cb751d0 100644
--- a/time/timeswitch/package.json
+++ b/time/timeswitch/package.json
@@ -1,9 +1,10 @@
{
"name" : "node-red-node-timeswitch",
- "version" : "0.0.7",
+ "version" : "0.1.0",
"description" : "A Node-RED node to provide a simple timeswitch to schedule daily on/off events.",
"dependencies" : {
- "suncalc": "1.6.0"
+ "spacetime": "^6.12.5",
+ "suncalc": "^1.8.0"
},
"repository" : {
"type":"git",
@@ -20,5 +21,10 @@
"name": "Dave Conway-Jones",
"email": "ceejay@vnet.ibm.com",
"url": "http://nodered.org"
- }
+ },
+ "contributors": [
+ {
+ "name": "@pmacostapdi"
+ }
+ ]
}
diff --git a/time/timeswitch/timeswitch.html b/time/timeswitch/timeswitch.html
index 1f4a1f92..e7041a5a 100644
--- a/time/timeswitch/timeswitch.html
+++ b/time/timeswitch/timeswitch.html
@@ -1,5 +1,5 @@
-
-
-
-
-
diff --git a/utility/annotate-image/package.json b/utility/annotate-image/package.json
index 48b164f3..8b3564e4 100644
--- a/utility/annotate-image/package.json
+++ b/utility/annotate-image/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-annotate-image",
- "version": "0.1.0",
+ "version": "0.1.1",
"description": "A Node-RED node that can annotate an image",
"dependencies": {
"pureimage": "^0.2.5"
diff --git a/utility/daemon/daemon.html b/utility/daemon/daemon.html
index 8b89688c..1e99b8a9 100644
--- a/utility/daemon/daemon.html
+++ b/utility/daemon/daemon.html
@@ -49,16 +49,6 @@
All parameters should be passed in as arguments.
-
-
diff --git a/utility/daemon/package.json b/utility/daemon/package.json
index 308023c4..1cf49665 100644
--- a/utility/daemon/package.json
+++ b/utility/daemon/package.json
@@ -1,6 +1,6 @@
{
"name" : "node-red-node-daemon",
- "version" : "0.1.2",
+ "version" : "0.2.1",
"description" : "A Node-RED node that runs and monitors a long running system command.",
"dependencies" : {
},
diff --git a/utility/exif/94-exif.html b/utility/exif/94-exif.html
index 3f7d99ac..7d5c23a9 100644
--- a/utility/exif/94-exif.html
+++ b/utility/exif/94-exif.html
@@ -17,17 +17,6 @@
-
-
diff --git a/utility/exif/package.json b/utility/exif/package.json
index 639bbcef..89c587d5 100644
--- a/utility/exif/package.json
+++ b/utility/exif/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-exif",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "A Node-RED node that extracts Exif information from JPEG image buffers.",
"dependencies": {
"exif": "^0.6.0"