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/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 ffa0a018..0c07689e 100644
--- a/hardware/pigpiod/package.json
+++ b/hardware/pigpiod/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-pi-gpiod",
- "version": "0.2.0",
+ "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 86018771..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..f990b48f
--- /dev/null
+++ b/social/email/locales/de/61-email.json
@@ -0,0 +1,67 @@
+{
+ "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"
+ },
+ "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..e0f5b392 100644
--- a/social/email/locales/en-US/61-email.html
+++ b/social/email/locales/en-US/61-email.html
@@ -6,11 +6,13 @@
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
@@ -21,7 +23,7 @@
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/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..cba9efdb 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.6",
"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..8604a078 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.0",
"description": "A node to tail files for Node-RED",
"dependencies": {
- "tail": "^2.0.3"
+ "tail": "^2.2.0"
},
"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/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 acaf2bf0..9cb751d0 100644
--- a/time/timeswitch/package.json
+++ b/time/timeswitch/package.json
@@ -1,8 +1,9 @@
{
"name" : "node-red-node-timeswitch",
- "version" : "0.0.8",
+ "version" : "0.1.0",
"description" : "A Node-RED node to provide a simple timeswitch to schedule daily on/off events.",
"dependencies" : {
+ "spacetime": "^6.12.5",
"suncalc": "^1.8.0"
},
"repository" : {
@@ -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 910cca6a..e7041a5a 100644
--- a/time/timeswitch/timeswitch.html
+++ b/time/timeswitch/timeswitch.html
@@ -214,6 +214,616 @@
+
+ Timezone
+
+ Africa/Abidjan
+ Africa/Accra
+ Africa/Addis_Ababa
+ Africa/Algiers
+ Africa/Asmara
+ Africa/Asmera
+ Africa/Bamako
+ Africa/Bangui
+ Africa/Banjul
+ Africa/Bissau
+ Africa/Blantyre
+ Africa/Brazzaville
+ Africa/Bujumbura
+ Africa/Cairo
+ Africa/Casablanca
+ Africa/Ceuta
+ Africa/Conakry
+ Africa/Dakar
+ Africa/Dar_Es_Salaam
+ Africa/Djibouti
+ Africa/Douala
+ Africa/El_Aaiun
+ Africa/Freetown
+ Africa/Gaborone
+ Africa/Harare
+ Africa/Johannesburg
+ Africa/Juba
+ Africa/Kampala
+ Africa/Khartoum
+ Africa/Kigali
+ Africa/Kinshasa
+ Africa/Lagos
+ Africa/Libreville
+ Africa/Lome
+ Africa/Luanda
+ Africa/Lubumbashi
+ Africa/Lusaka
+ Africa/Malabo
+ Africa/Maputo
+ Africa/Maseru
+ Africa/Mbabane
+ Africa/Mogadishu
+ Africa/Monrovia
+ Africa/Nairobi
+ Africa/Ndjamena
+ Africa/Niamey
+ Africa/Nouakchott
+ Africa/Ouagadougou
+ Africa/Porto-novo
+ Africa/Sao_Tome
+ Africa/Timbuktu
+ Africa/Tripoli
+ Africa/Tunis
+ Africa/Windhoek
+ America/Adak
+ America/Anchorage
+ America/Anguilla
+ America/Antigua
+ America/Araguaina
+ America/Argentina
+ America/Aruba
+ America/Asuncion
+ America/Atikokan
+ America/Atka
+ America/Bahia
+ America/Bahia_Banderas
+ America/Barbados
+ America/Belem
+ America/Belize
+ America/Blanc-sablon
+ America/Boa_Vista
+ America/Bogota
+ America/Boise
+ America/Buenos_Aires
+ America/Cambridge_Bay
+ America/Campo_Grande
+ America/Cancun
+ America/Caracas
+ America/Catamarca
+ America/Cayenne
+ America/Cayman
+ America/Chicago
+ America/Chihuahua
+ America/Coral_Harbour
+ America/Cordoba
+ America/Costa_Rica
+ America/Creston
+ America/Cuiaba
+ America/Curacao
+ America/Danmarkshavn
+ America/Dawson
+ America/Dawson_Creek
+ America/Denver
+ America/Detroit
+ America/Dominica
+ America/Edmonton
+ America/Eirunepe
+ America/El_Salvador
+ America/Ensenada
+ America/Fort_Nelson
+ America/Fort_Wayne
+ America/Fortaleza
+ America/Glace_Bay
+ America/Godthab
+ America/Goose_Bay
+ America/Grand_Turk
+ America/Grenada
+ America/Guadeloupe
+ America/Guatemala
+ America/Guayaquil
+ America/Guyana
+ America/Halifax
+ America/Havana
+ America/Hermosillo
+ America/Indiana
+ America/Indianapolis
+ America/Inuvik
+ America/Iqaluit
+ America/Jamaica
+ America/Jujuy
+ America/Juneau
+ America/Kentucky
+ America/Knox_In
+ America/Kralendijk
+ America/La_Paz
+ America/Lima
+ America/Los_Angeles
+ America/Louisville
+ America/Lower_Princes
+ America/Maceio
+ America/Managua
+ America/Manaus
+ America/Marigot
+ America/Martinique
+ America/Matamoros
+ America/Mazatlan
+ America/Mendoza
+ America/Menominee
+ America/Merida
+ America/Metlakatla
+ America/Mexico_City
+ America/Miquelon
+ America/Moncton
+ America/Monterrey
+ America/Montevideo
+ America/Montreal
+ America/Montserrat
+ America/Nassau
+ America/New_York
+ America/Nipigon
+ America/Nome
+ America/Noronha
+ America/North_Dakota
+ America/Nuuk
+ America/Ojinaga
+ America/Panama
+ America/Pangnirtung
+ America/Paramaribo
+ America/Phoenix
+ America/Port-au-prince
+ America/Port_Of_Spain
+ America/Porto_Acre
+ America/Porto_Velho
+ America/Puerto_Rico
+ America/Punta_Arenas
+ America/Rainy_River
+ America/Rankin_Inlet
+ America/Recife
+ America/Regina
+ America/Resolute
+ America/Rio_Branco
+ America/Rosario
+ America/Santa_Isabel
+ America/Santarem
+ America/Santiago
+ America/Santo_Domingo
+ America/Sao_Paulo
+ America/Scoresbysund
+ America/Shiprock
+ America/Sitka
+ America/St_Barthelemy
+ America/St_Johns
+ America/St_Kitts
+ America/St_Lucia
+ America/St_Thomas
+ America/St_Vincent
+ America/Swift_Current
+ America/Tegucigalpa
+ America/Thule
+ America/Thunder_Bay
+ America/Tijuana
+ America/Toronto
+ America/Tortola
+ America/Vancouver
+ America/Virgin
+ America/Whitehorse
+ America/Winnipeg
+ America/Yakutat
+ America/Yellowknife
+ Antarctica/Casey
+ Antarctica/Davis
+ Antarctica/Dumontdurville
+ Antarctica/Macquarie
+ Antarctica/Mawson
+ Antarctica/Mcmurdo
+ Antarctica/Palmer
+ Antarctica/Rothera
+ Antarctica/South_Pole
+ Antarctica/Syowa
+ Antarctica/Troll
+ Antarctica/Vostok
+ Arctic/Longyearbyen
+ Asia/Aden
+ Asia/Almaty
+ Asia/Amman
+ Asia/Anadyr
+ Asia/Aqtau
+ Asia/Aqtobe
+ Asia/Ashgabat
+ Asia/Ashkhabad
+ Asia/Atyrau
+ Asia/Baghdad
+ Asia/Bahrain
+ Asia/Baku
+ Asia/Bangkok
+ Asia/Barnaul
+ Asia/Beirut
+ Asia/Bishkek
+ Asia/Brunei
+ Asia/Calcutta
+ Asia/Chita
+ Asia/Choibalsan
+ Asia/Chongqing
+ Asia/Chungking
+ Asia/Colombo
+ Asia/Dacca
+ Asia/Damascus
+ Asia/Dhaka
+ Asia/Dili
+ Asia/Dubai
+ Asia/Dushanbe
+ Asia/Famagusta
+ Asia/Gaza
+ Asia/Harbin
+ Asia/Hebron
+ Asia/Ho_Chi_Minh
+ Asia/Hong_Kong
+ Asia/Hovd
+ Asia/Irkutsk
+ Asia/Istanbul
+ Asia/Jakarta
+ Asia/Jayapura
+ Asia/Jerusalem
+ Asia/Kabul
+ Asia/Kamchatka
+ Asia/Karachi
+ Asia/Kashgar
+ Asia/Kathmandu
+ Asia/Katmandu
+ Asia/Khandyga
+ Asia/Kolkata
+ Asia/Krasnoyarsk
+ Asia/Kuala_Lumpur
+ Asia/Kuching
+ Asia/Kuwait
+ Asia/Macao
+ Asia/Macau
+ Asia/Magadan
+ Asia/Makassar
+ Asia/Manila
+ Asia/Muscat
+ Asia/Nicosia
+ Asia/Novokuznetsk
+ Asia/Novosibirsk
+ Asia/Omsk
+ Asia/Oral
+ Asia/Phnom_Penh
+ Asia/Pontianak
+ Asia/Pyongyang
+ Asia/Qatar
+ Asia/Qostanay
+ Asia/Qyzylorda
+ Asia/Rangoon
+ Asia/Riyadh
+ Asia/Saigon
+ Asia/Sakhalin
+ Asia/Samarkand
+ Asia/Seoul
+ Asia/Shanghai
+ Asia/Singapore
+ Asia/Srednekolymsk
+ Asia/Taipei
+ Asia/Tashkent
+ Asia/Tbilisi
+ Asia/Tehran
+ Asia/Tel_Aviv
+ Asia/Thimbu
+ Asia/Thimphu
+ Asia/Tokyo
+ Asia/Tomsk
+ Asia/Ujung_Pandang
+ Asia/Ulaanbaatar
+ Asia/Ulan_Bator
+ Asia/Urumqi
+ Asia/Ust-nera
+ Asia/Vientiane
+ Asia/Vladivostok
+ Asia/Volgograd
+ Asia/Yakutsk
+ Asia/Yangon
+ Asia/Yekaterinburg
+ Asia/Yerevan
+ Atlantic/Azores
+ Atlantic/Bermuda
+ Atlantic/Canary
+ Atlantic/Cape_Verde
+ Atlantic/Faeroe
+ Atlantic/Faroe
+ Atlantic/Jan_Mayen
+ Atlantic/Madeira
+ Atlantic/Reykjavik
+ Atlantic/South_Georgia
+ Atlantic/St_Helena
+ Atlantic/Stanley
+ Australia/Act
+ Australia/Adelaide
+ Australia/Brisbane
+ Australia/Broken_Hill
+ Australia/Canberra
+ Australia/Currie
+ Australia/Darwin
+ Australia/Eucla
+ Australia/Hobart
+ Australia/Lhi
+ Australia/Lindeman
+ Australia/Lord_Howe
+ Australia/Melbourne
+ Australia/North
+ Australia/Nsw
+ Australia/Perth
+ Australia/Queensland
+ Australia/South
+ Australia/Sydney
+ Australia/Tasmania
+ Australia/Victoria
+ Australia/West
+ Australia/Yancowinna
+ Brazil/Acre
+ Brazil/Denoronha
+ Brazil/East
+ Brazil/West
+ Canada/Atlantic
+ Canada/Central
+ Canada/East-saskatchewan
+ Canada/Eastern
+ Canada/Mountain
+ Canada/Newfoundland
+ Canada/Pacific
+ Canada/Saskatchewan
+ Canada/Yukon
+ Chile/Continental
+ Chile/Easterisland
+ ETC/GMT+0.5
+ ETC/GMT+0
+ ETC/GMT+1.5
+ ETC/GMT+10.5
+ ETC/GMT+10
+ ETC/GMT+11.5
+ ETC/GMT+11
+ ETC/GMT+12.5
+ ETC/GMT+12
+ ETC/GMT+13.5
+ ETC/GMT+13
+ ETC/GMT+14
+ ETC/GMT+1
+ ETC/GMT+2.5
+ ETC/GMT+2
+ ETC/GMT+3.5
+ ETC/GMT+3
+ ETC/GMT+4.5
+ ETC/GMT+4
+ ETC/GMT+5.5
+ ETC/GMT+5
+ ETC/GMT+6.5
+ ETC/GMT+6
+ ETC/GMT+7.5
+ ETC/GMT+7
+ ETC/GMT+8.5
+ ETC/GMT+8
+ ETC/GMT+9.5
+ ETC/GMT+9
+ ETC/GMT-0.5
+ ETC/GMT-0
+ ETC/GMT-1.5
+ ETC/GMT-10.5
+ ETC/GMT-10
+ ETC/GMT-11.5
+ ETC/GMT-11
+ ETC/GMT-12.5
+ ETC/GMT-12
+ ETC/GMT-13.5
+ ETC/GMT-13
+ ETC/GMT-14
+ ETC/GMT-1
+ ETC/GMT-2.5
+ ETC/GMT-2
+ ETC/GMT-3.5
+ ETC/GMT-3
+ ETC/GMT-4.5
+ ETC/GMT-4
+ ETC/GMT-5.5
+ ETC/GMT-5
+ ETC/GMT-6.5
+ ETC/GMT-6
+ ETC/GMT-7.5
+ ETC/GMT-7
+ ETC/GMT-8.5
+ ETC/GMT-8
+ ETC/GMT-9.5
+ ETC/GMT-9
+ ETC/GMT0
+ ETC/GMT
+ ETC/Greenwich
+ ETC/Universal
+ ETC/Utc
+ ETC/Zulu
+ Europe/Amsterdam
+ Europe/Andorra
+ Europe/Astrakhan
+ Europe/Athens
+ Europe/Belfast
+ Europe/Belgrade
+ Europe/Berlin
+ Europe/Bratislava
+ Europe/Brussels
+ Europe/Bucharest
+ Europe/Budapest
+ Europe/Busingen
+ Europe/Chisinau
+ Europe/Copenhagen
+ Europe/Dublin
+ Europe/Gibraltar
+ Europe/Guernsey
+ Europe/Helsinki
+ Europe/Isle_Of_Man
+ Europe/Istanbul
+ Europe/Jersey
+ Europe/Kaliningrad
+ Europe/Kiev
+ Europe/Kirov
+ Europe/Lisbon
+ Europe/Ljubljana
+ Europe/London
+ Europe/Luxembourg
+ Europe/Madrid
+ Europe/Malta
+ Europe/Mariehamn
+ Europe/Minsk
+ Europe/Monaco
+ Europe/Moscow
+ Europe/Nicosia
+ Europe/Oslo
+ Europe/Paris
+ Europe/Podgorica
+ Europe/Prague
+ Europe/Riga
+ Europe/Rome
+ Europe/Samara
+ Europe/San_Marino
+ Europe/Sarajevo
+ Europe/Saratov
+ Europe/Simferopol
+ Europe/Skopje
+ Europe/Sofia
+ Europe/Stockholm
+ Europe/Tallinn
+ Europe/Tirane
+ Europe/Tiraspol
+ Europe/Ulyanovsk
+ Europe/Uzhgorod
+ Europe/Vaduz
+ Europe/Vatican
+ Europe/Vienna
+ Europe/Vilnius
+ Europe/Volgograd
+ Europe/Warsaw
+ Europe/Zagreb
+ Europe/Zaporozhye
+ Europe/Zurich
+ Indian/Antananarivo
+ Indian/Chagos
+ Indian/Christmas
+ Indian/Cocos
+ Indian/Comoro
+ Indian/Kerguelen
+ Indian/Mahe
+ Indian/Maldives
+ Indian/Mauritius
+ Indian/Mayotte
+ Indian/Reunion
+ Mexico/Bajanorte
+ Mexico/Bajasur
+ Mexico/General
+ Pacific/Apia
+ Pacific/Auckland
+ Pacific/Bougainville
+ Pacific/Chatham
+ Pacific/Chuuk
+ Pacific/Easter
+ Pacific/Efate
+ Pacific/Enderbury
+ Pacific/Fakaofo
+ Pacific/Fiji
+ Pacific/Funafuti
+ Pacific/Galapagos
+ Pacific/Gambier
+ Pacific/Guadalcanal
+ Pacific/Guam
+ Pacific/Honolulu
+ Pacific/Johnston
+ Pacific/Kiritimati
+ Pacific/Kosrae
+ Pacific/Kwajalein
+ Pacific/Majuro
+ Pacific/Marquesas
+ Pacific/Midway
+ Pacific/Nauru
+ Pacific/Niue
+ Pacific/Norfolk
+ Pacific/Noumea
+ Pacific/Pago_Pago
+ Pacific/Palau
+ Pacific/Pitcairn
+ Pacific/Pohnpei
+ Pacific/Ponape
+ Pacific/Port_Moresby
+ Pacific/Rarotonga
+ Pacific/Saipan
+ Pacific/Samoa
+ Pacific/Tahiti
+ Pacific/Tarawa
+ Pacific/Tongatapu
+ Pacific/Truk
+ Pacific/Wake
+ Pacific/Wallis
+ Pacific/Yap
+ UTC/GMT+0.5
+ UTC/GMT+1.5
+ UTC/GMT+10.5
+ UTC/GMT+10
+ UTC/GMT+11.5
+ UTC/GMT+11
+ UTC/GMT+12.5
+ UTC/GMT+12
+ UTC/GMT+13.5
+ UTC/GMT+13
+ UTC/GMT+14
+ UTC/GMT+1
+ UTC/GMT+2.5
+ UTC/GMT+2
+ UTC/GMT+3.5
+ UTC/GMT+3
+ UTC/GMT+4.5
+ UTC/GMT+4
+ UTC/GMT+5.5
+ UTC/GMT+5
+ UTC/GMT+6.5
+ UTC/GMT+6
+ UTC/GMT+7.5
+ UTC/GMT+7
+ UTC/GMT+8.5
+ UTC/GMT+8
+ UTC/GMT+9.5
+ UTC/GMT+9
+ UTC/GMT-0.5
+ UTC/GMT-1.5
+ UTC/GMT-10.5
+ UTC/GMT-10
+ UTC/GMT-11.5
+ UTC/GMT-11
+ UTC/GMT-12.5
+ UTC/GMT-12
+ UTC/GMT-13.5
+ UTC/GMT-13
+ UTC/GMT-14
+ UTC/GMT-1
+ UTC/GMT-2.5
+ UTC/GMT-2
+ UTC/GMT-3.5
+ UTC/GMT-3
+ UTC/GMT-4.5
+ UTC/GMT-4
+ UTC/GMT-5.5
+ UTC/GMT-5
+ UTC/GMT-6.5
+ UTC/GMT-6
+ UTC/GMT-7.5
+ UTC/GMT-7
+ UTC/GMT-8.5
+ UTC/GMT-8
+ UTC/GMT-9.5
+ UTC/GMT-9
+ UTC/GMT0
+ UTC
+
+
+
Place Lat
@@ -266,20 +876,17 @@
-
-
-
-
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 @@
-
-