From 55de84772cf37c7bc42b3c0a69c06d377ea99a1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathana=C3=ABl=20L=C3=A9caud=C3=A9?=
Date: Wed, 13 Jan 2021 05:13:46 -0500
Subject: [PATCH 01/88] Ensure serial node has a default reconnect time (#733)
---
io/serialport/25-serial.js | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/io/serialport/25-serial.js b/io/serialport/25-serial.js
index 2d9b4f2c..9612e943 100644
--- a/io/serialport/25-serial.js
+++ b/io/serialport/25-serial.js
@@ -5,6 +5,7 @@ module.exports = function(RED) {
var events = require("events");
var serialp = require("serialport");
var bufMaxSize = 32768; // Max serial buffer size, for inputs...
+ const serialReconnectTime = settings.serialReconnectTime || 15000;
// TODO: 'serialPool' should be encapsulated in SerialPortNode
@@ -350,7 +351,7 @@ module.exports = function(RED) {
}
obj.tout = setTimeout(function() {
setupSerial();
- }, settings.serialReconnectTime);
+ }, serialReconnectTime);
}
});
obj.serial.on('error', function(err) {
@@ -359,7 +360,7 @@ module.exports = function(RED) {
if (obj.tout) { clearTimeout(obj.tout); }
obj.tout = setTimeout(function() {
setupSerial();
- }, settings.serialReconnectTime);
+ }, serialReconnectTime);
});
obj.serial.on('close', function() {
if (!obj._closing) {
@@ -371,7 +372,7 @@ module.exports = function(RED) {
if (obj.tout) { clearTimeout(obj.tout); }
obj.tout = setTimeout(function() {
setupSerial();
- }, settings.serialReconnectTime);
+ }, serialReconnectTime);
}
});
obj.serial.on('open',function() {
From f082306778989f32a06388391e0bbcabf800f7dd Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Thu, 14 Jan 2021 13:26:49 +0000
Subject: [PATCH 02/88] partially fix exif tests to fix overall grunt
---
test/utility/exif/94-exif_spec.js | 99 +++++++++++++++---------------
test/utility/exif/test.jpeg | Bin 0 -> 2839 bytes
utility/exif/94-exif.js | 5 +-
utility/exif/package.json | 2 +-
4 files changed, 53 insertions(+), 53 deletions(-)
create mode 100644 test/utility/exif/test.jpeg
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 0000000000000000000000000000000000000000..c7794c9d5908bede62a589d4fa8ccc64624177ef
GIT binary patch
literal 2839
zcmb7Gd2Ccg9RB9*(cSIA+X5C9WRd&WvhVG(-7RV@r8N}_O$rf+QA$~$p)Cc9F>)#K
zAWNM9;IT#CKD^#F{G{5R|gs
zC0vHwKzz4_+lU8Q9EDh;Z8l{)t4`dF@*r#N3r@rKEOGzkCickCEX9qDbqUl~&YIJZ
z2vn5@2I&pc12Yp1iFwscwR0K<1H%|zW?;*Q$}1`ZdcGFok8wh}Rv6L>wManI3nTGD
zT|)qGE;F;8%CuHlcoy-!%gqtVYzJCcW{DLUa8iWyu;y4{X5L7W)56aZi
zLm!QX(42s&?-Tj)Q$&iq=A5=V$5kw{;#@)
z)d~iW7T^l{R%lLuuv1Ygbmotp2N6CFWYFlmp%^T7(KMndL#SW@F70IX{;xICvCsu
z+4d_wCBK!nTj_N#ExsUg3wF@XDIOHP#1(2z;Tv3_p99kSF?z0b-ySB2@Ng3NIO#c^aN!^bQ=S&nx%|zvCv4<{Xj4
z&+OfTUpPZ@+`EnE0B7VfdropTb}-9rY#+lVoM3M!v-zCQPORe$ti~%?gs1Ta7IG$9
z3{ThbBeQuGOECvEcpQ^Zg+|0M8Lx5N*BJc*?LQ`e4O!3PDa^zJe6OeXqxgz_NBA74
zJVe$muEl0d!4l5VCg!q-axLS?71_=APGgN4@Fb&5z-&B0<~-EnIp)@aRm^=g-hy%A
z@S9NN?v%xrW(!J|{k5FnG3#`>GdvS#)K$-%Kk
xClUumYLd@N;g)fhcmJ7N0l^2)L)(aCz%=(s*wD=tk1ml-`eRW>swz6$giqQ|3AJyfg(qooDW
zU?eiCI2enB3xcC`t)xsJ5sT=fqrioeMc9$-l9F;jE>>IK?j8+HtFDWK{1}fg!9zfv+~ZgkdgU>PO}w8Smi`g8otD0ndU@a36YDlwz**|$aV!0d
zVa>Mmm#9}QeFgP?OMi{}yfl3eOaB}7y{XS4_d2mGVOAMW`j+yU9PBO(Q{$Z$=Ij0hun4kd6XvdFiC#p!m+;&HoOZm-AZ
z^Lo5qUsjhaUuI6G*PHFn&gr76e$|(i+cj71N~}sJLRvbU9vZ5d-b`x$XGwO#@8NiO
znE0Xig~KnB`+0fD5vCV6wcYu5E?viYa3;?p%fY2o9V(kuBc-wW4;KIborQ
Date: Thu, 14 Jan 2021 11:09:57 -0800
Subject: [PATCH 03/88] update node-blink1 library (#734)
---
hardware/blink1/package.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hardware/blink1/package.json b/hardware/blink1/package.json
index 32f29182..cbc9c384 100644
--- a/hardware/blink1/package.json
+++ b/hardware/blink1/package.json
@@ -1,9 +1,9 @@
{
"name" : "node-red-node-blink1",
- "version" : "0.0.17",
+ "version" : "0.0.18",
"description" : "A Node-RED node to control a Thingm Blink(1)",
"dependencies" : {
- "node-blink1" : "0.2.2"
+ "node-blink1" : "0.5.1"
},
"repository" : {
"type":"git",
From 54be2e679e333b623661906e9c80077047205723 Mon Sep 17 00:00:00 2001
From: meeki007 <5952964+meeki007@users.noreply.github.com>
Date: Sun, 17 Jan 2021 12:19:12 -0500
Subject: [PATCH 04/88] node-red-contrib-facial-recognition (enhancments)
(#726)
* add labelLocation documentation
* Update README.md
---
utility/annotate-image/README.md | 2 ++
utility/annotate-image/annotate.html | 3 +++
utility/annotate-image/annotate.js | 36 ++++++++++++++++++++++++++--
3 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/utility/annotate-image/README.md b/utility/annotate-image/README.md
index de17bc47..ba7bde25 100644
--- a/utility/annotate-image/README.md
+++ b/utility/annotate-image/README.md
@@ -37,6 +37,8 @@ Each annotation is an object with the following properties:
- `lineWidth` (*number*) : the stroke width used to draw the annotation. Default: `5`
- `fontSize` (*number*) : the font size to use for the label. Default: `24`
- `fontColor` (*string*) : the color of the font to use for the label. Default: `#ffC000`
+ - `labelLocation` (*string*) : The Location to place the label. `top` or `bottom`.
+ If this propery is not set it will default to `automatic` and make the best guess based on location.
Examples
diff --git a/utility/annotate-image/annotate.html b/utility/annotate-image/annotate.html
index f87a76b6..d0500625 100644
--- a/utility/annotate-image/annotate.html
+++ b/utility/annotate-image/annotate.html
@@ -64,6 +64,9 @@
The font size to use for the label. Default: 24
fontColor string
The color of the font to use for the label. Default: "#ffC000"
+
labelLocation string
+
The Location to place the label. Default: "top" or "bottom".
+ If this propery is not set it will default to "automatic"
Examples
msg.annotations = [ {
diff --git a/utility/annotate-image/annotate.js b/utility/annotate-image/annotate.js
index 2b1b35e6..bf12fc72 100644
--- a/utility/annotate-image/annotate.js
+++ b/utility/annotate-image/annotate.js
@@ -21,7 +21,7 @@ module.exports = function(RED) {
const defaultStroke = n.stroke || "#ffC000";
const defaultLineWidth = parseInt(n.lineWidth) || 5;
const defaultFontSize = n.fontSize || 24;
- const defaultFontColor = n.fontColor || "#ffC000"
+ const defaultFontColor = n.fontColor || "#ffC000";
loadFont();
this.on("input", function(msg) {
@@ -83,7 +83,39 @@ module.exports = function(RED) {
ctx.fillStyle = annotation.fontColor || defaultFontColor;
ctx.textBaseline = "top";
ctx.textAlign = "left";
- ctx.fillText(annotation.label, x+2,y)
+ //set offset value so txt is above or below image
+ if (annotation.labelLocation) {
+ if (annotation.labelLocation === "top") {
+ y = y - (20+((defaultLineWidth*0.5)+(Number(defaultFontSize))));
+ if (y < 0)
+ {
+ y = 0;
+ }
+ }
+ else if (annotation.labelLocation === "bottom") {
+ y = y + (10+h+(((defaultLineWidth*0.5)+(Number(defaultFontSize)))));
+ ctx.textBaseline = "bottom";
+
+ }
+ }
+ //if not user defined make best guess for top or bottom based on location
+ else {
+ //not enought room above imagebox, put label on the bottom
+ if (y < 0 + (20+((defaultLineWidth*0.5)+(Number(defaultFontSize))))) {
+ y = y + (10+h+(((defaultLineWidth*0.5)+(Number(defaultFontSize)))));
+ ctx.textBaseline = "bottom";
+ }
+ //else put the label on the top
+ else {
+ y = y - (20+((defaultLineWidth*0.5)+(Number(defaultFontSize))));
+ if (y < 0) {
+ y = 0;
+ }
+ }
+ }
+
+
+ ctx.fillText(annotation.label, x,y);
}
break;
case 'circle':
From 3bc16fbd559ebb67ede1784da3e4b30b2e7ce6f6 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Sun, 17 Jan 2021 17:29:15 +0000
Subject: [PATCH 05/88] Bump up libraries for "old" blinkstick
---
hardware/blinkstick/76-blinkstick.html | 4 +-
hardware/blinkstick/blinkstick.html | 50 ++++++++++++++++++++
hardware/blinkstick/blinkstick.js | 64 ++++++++++++++++++++++++++
hardware/blinkstick/package.json | 4 +-
4 files changed, 118 insertions(+), 4 deletions(-)
create mode 100644 hardware/blinkstick/blinkstick.html
create mode 100644 hardware/blinkstick/blinkstick.js
diff --git a/hardware/blinkstick/76-blinkstick.html b/hardware/blinkstick/76-blinkstick.html
index 0f9ce707..f72f3203 100644
--- a/hardware/blinkstick/76-blinkstick.html
+++ b/hardware/blinkstick/76-blinkstick.html
@@ -1,5 +1,5 @@
-
-
+
+
+
+
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",
From ea92f293e2ba42d62c507a9fdc78be522a5f769f Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Sun, 17 Jan 2021 17:29:57 +0000
Subject: [PATCH 06/88] Bump annotate image version from PR.
---
utility/annotate-image/README.md | 4 ++--
utility/annotate-image/annotate.html | 11 ++++-------
utility/annotate-image/package.json | 2 +-
3 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/utility/annotate-image/README.md b/utility/annotate-image/README.md
index ba7bde25..2952ad15 100644
--- a/utility/annotate-image/README.md
+++ b/utility/annotate-image/README.md
@@ -11,7 +11,7 @@ detected in the image by a TensorFlow node.
Install
-------
-Run the following command in your Node-RED user directory - typically `~/.node-red`
+Either use the Edit Menu - Manage Palette option to install, or run the following command in your Node-RED user directory - typically `~/.node-red`
npm install node-red-node-annotate-image
@@ -37,7 +37,7 @@ Each annotation is an object with the following properties:
- `lineWidth` (*number*) : the stroke width used to draw the annotation. Default: `5`
- `fontSize` (*number*) : the font size to use for the label. Default: `24`
- `fontColor` (*string*) : the color of the font to use for the label. Default: `#ffC000`
- - `labelLocation` (*string*) : The Location to place the label. `top` or `bottom`.
+ - `labelLocation` (*string*) : The location to place the label. `top` or `bottom`.
If this propery is not set it will default to `automatic` and make the best guess based on location.
diff --git a/utility/annotate-image/annotate.html b/utility/annotate-image/annotate.html
index d0500625..b43eae69 100644
--- a/utility/annotate-image/annotate.html
+++ b/utility/annotate-image/annotate.html
@@ -1,5 +1,5 @@
-
-
-
-
@@ -66,7 +65,8 @@
});
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index 4ba648ed..60df52d0 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,7 +50,7 @@ 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
+ // 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;
@@ -75,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();
}
}
@@ -84,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) {
@@ -91,11 +93,10 @@ 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) => {
if (stanza.is('message')) {
if (stanza.attrs.type == 'error') {
if (RED.settings.verbose || LOGITALL) {
@@ -106,17 +107,17 @@ module.exports = function(RED) {
if (err) {
var textObj = err.getChild('text');
var text = "node-red:common.status.error";
- if ("undefined" !== typeof textObj) {
+ if (typeof textObj !== "undefined") {
text = textObj.getText();
}
- else{
+ else {
textObj = err.getChild('code');
- if ("undefined" !== typeof textObj) {
+ if (typeof textObj !== "undefined") {
text = textObj.getText();
}
}
if (RED.settings.verbose || LOGITALL) {that.log("Culprit: "+that.lastUsed.id); }
- if ("undefined" !== typeof that.lastUsed) {
+ if (typeof that.lastUsed !== "undefined") {
that.lastUsed.status({fill:"red",shape:"ring",text:text});
that.lastUsed.warn(text);
if (that.lastUsed.join) {
@@ -165,12 +166,10 @@ module.exports = function(RED) {
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 => {
@@ -217,6 +216,24 @@ 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 getInfo(thing,id,xmpp) {
+ // Now try to get a list of all info about a thing
+ var stanza = xml('iq',
+ {type:'get', id:id, to:thing},
+ xml('query', 'http://jabber.org/protocol/disco#info')
+ );
+ 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
@@ -231,13 +248,15 @@ module.exports = function(RED) {
}
else {
var stanza = xml('presence',
- {"to": name},
- xml("x",'http://jabber.org/protocol/muc'),
- { maxstanzas:0, seconds:1 }
- );
+ {"to":name},
+ xml("x",'http://jabber.org/protocol/muc',
+ xml("history", {maxstanzas:0, seconds:1}) // We don't want any history
+ )
+ );
node.serverConfig.used(node);
node.serverConfig.MUCs[name] = "joined";
xmpp.send(stanza);
+ if (RED.settings.verbose || LOGITALL) { node.log("JOINED",name); }
}
}
@@ -248,6 +267,7 @@ module.exports = function(RED) {
}
config.MUCs = {};
}
+
// separated out since we want the same functionality from both in and out nodes
function errorHandler(node, err){
if (!node.quiet) {
@@ -276,7 +296,6 @@ module.exports = function(RED) {
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);
@@ -293,7 +312,8 @@ module.exports = function(RED) {
}
}
}
-
+
+
function XmppInNode(n) {
RED.nodes.createNode(this,n);
this.server = n.server;
@@ -306,6 +326,8 @@ module.exports = function(RED) {
this.quiet = false;
// MUC == Multi-User-Chat == chatroom
this.muc = this.join && (this.from !== "")
+ // list of possible rooms - queried from server
+ this.roomsFound = {};
var node = this;
var xmpp = this.serverConfig.client;
@@ -324,24 +346,30 @@ module.exports = function(RED) {
*/
// 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:"node-red:common.status.connected"});
- if(node.muc) {
- joinMUC(node, xmpp, node.from+'/'+node.nick);
- }
- }
+ // if (xmpp.status === "online") {
+ // node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ // if (node.muc) {
+ // joinMUC(node, xmpp, node.from+'/'+node.nick);
+ // }
+ // }
// sod it, register it anyway, that way things will work better on a reconnect:
xmpp.on('online', async address => {
node.quiet = false;
node.status({fill:"green",shape:"dot",text:"node-red:common.status.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);
+ if (node.from.toUpperCase() === "ALL_ROOMS") {
+ // try to get list of all rooms and join them all.
+ getItems(this.serverConfig.server,this.serverConfig.id,xmpp);
+ }
+ else {
+ // 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 => {
- if(!node.quiet) {
+ if (!node.quiet) {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
}
});
@@ -372,8 +400,7 @@ module.exports = function(RED) {
});
// Meat of it, a stanza object contains chat messages (and other things)
- xmpp.on('stanza', async (stanza) =>{
- // node.log("Received stanza");
+ xmpp.on('stanza', async (stanza) => {
if (RED.settings.verbose || LOGITALL) {node.log(stanza); }
if (stanza.is('message')) {
if (stanza.attrs.type == 'chat') {
@@ -385,7 +412,7 @@ module.exports = function(RED) {
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 (RED.settings.verbose || LOGITALL) {node.log("Received a message from "+stanza.attrs.from); }
if (!node.join && ((node.from === "") || (node.from === stanza.attrs.to))) {
node.send([msg,null]);
}
@@ -397,12 +424,12 @@ module.exports = function(RED) {
var from = parts[1];
var body = stanza.getChild('body');
var payload = "";
- if ("undefined" !== typeof body) {
+ if (typeof body !== "undefined") {
payload = body.getText();
}
var msg = { topic:from, payload:payload, room:conference };
- if (stanza.attrs.from != node.nick) {
- if ((node.join) && (node.from === conference)) {
+ if (from && stanza.attrs.from != node.nick && from != node.nick) {
+ if (node.from.toUpperCase() === "ALL_ROOMS" || node.from === conference) {
node.send([msg,null]);
}
}
@@ -411,26 +438,28 @@ module.exports = function(RED) {
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 {
+ 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} };
node.send([null,msg]);
}
- else{
+ else {
if (RED.settings.verbose || LOGITALL) {
node.log("not propagating blank status");
node.log(stanza);
@@ -438,6 +467,32 @@ 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 {
+ node.roomsFound[_items[i].attrs.name] = _items[i].attrs.jid;
+ var name = _items[i].attrs.jid+'/'+node.serverConfig.username;
+ if (!(name in node.serverConfig.MUCs)) {
+ if (RED.settings.verbose || LOGITALL) {this.log("Need to Join room:"+name); }
+ joinMUC(node, xmpp, name)
+ }
+ }
+ }
+ if (RED.settings.verbose || LOGITALL) {this.log("ROOMS:"+this.server+this.roomsFound); }
+ }
+ }
});
// xmpp.on('subscribe', from => {
@@ -451,7 +506,7 @@ module.exports = function(RED) {
if (xmpp.status === "online") {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
}
- else{
+ else {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
if (xmpp.status === "offline") {
if (RED.settings.verbose || LOGITALL) {
@@ -504,23 +559,25 @@ module.exports = function(RED) {
*/
// 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:"node-red:common.status.connected"});
- if(node.muc){
- joinMUC(node, xmpp, node.to+'/'+node.nick);
- }
- }
+ // if (xmpp.status === "online") {
+ // node.status({fill:"green",shape:"dot",text:"node-red:common.status.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.quiet = false;
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
if (node.muc) {
- joinMUC(node, xmpp,node.to+"/"+node.nick);
+ // 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 => {
- if(!node.quiet) {
+ if (!node.quiet) {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
}
});
@@ -555,7 +612,7 @@ module.exports = function(RED) {
if (xmpp.status === "online") {
node.status({fill:"green",shape:"dot",text:"online"});
}
- else{
+ else {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
if (xmpp.status === "offline") {
xmpp.start().catch(error => {
@@ -571,9 +628,7 @@ module.exports = function(RED) {
node.on("input", function(msg) {
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);
}
@@ -581,22 +636,22 @@ 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()); }
xmpp.send(stanza);
}
-
}
else {
var to = node.to || msg.topic || "";
diff --git a/social/xmpp/package.json b/social/xmpp/package.json
index ffd2b95e..7d1aa843 100644
--- a/social/xmpp/package.json
+++ b/social/xmpp/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-xmpp",
- "version": "0.3.4",
+ "version": "0.4.0",
"description": "A Node-RED node to talk to an XMPP server",
"dependencies": {
"@xmpp/client": "^0.12.0"
From 7485760db97a8e3d1d29150e50596eb8977050e8 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Tue, 23 Feb 2021 11:37:53 +0000
Subject: [PATCH 26/88] xmpp node - use empty field to signify all rooms
---
social/xmpp/92-xmpp.html | 2 +-
social/xmpp/92-xmpp.js | 16 ++++++++--------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/social/xmpp/92-xmpp.html b/social/xmpp/92-xmpp.html
index e7659951..1b81a8ef 100644
--- a/social/xmpp/92-xmpp.html
+++ b/social/xmpp/92-xmpp.html
@@ -17,7 +17,7 @@
-
Note: By setting Buddy to "ALL_ROOMS" and ticking "Is a chat room",
+
Note: By leaving Buddy empty and ticking "Is a chat room",
the node will try to listen to all the rooms the user has access to.
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index 60df52d0..d056ef51 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -322,10 +322,10 @@ 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 || "";
+ this.from = (n.to || "").trim();
this.quiet = false;
// MUC == Multi-User-Chat == chatroom
- this.muc = this.join && (this.from !== "")
+ //this.muc = this.join && (this.from !== "")
// list of possible rooms - queried from server
this.roomsFound = {};
var node = this;
@@ -356,8 +356,8 @@ module.exports = function(RED) {
xmpp.on('online', async address => {
node.quiet = false;
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
- if (node.muc) {
- if (node.from.toUpperCase() === "ALL_ROOMS") {
+ if (node.join) {
+ if (node.from === "") {
// try to get list of all rooms and join them all.
getItems(this.serverConfig.server,this.serverConfig.id,xmpp);
}
@@ -428,11 +428,11 @@ module.exports = function(RED) {
payload = body.getText();
}
var msg = { topic:from, payload:payload, room:conference };
- if (from && stanza.attrs.from != node.nick && from != node.nick) {
- if (node.from.toUpperCase() === "ALL_ROOMS" || node.from === conference) {
- node.send([msg,null]);
- }
+ //if (from && stanza.attrs.from != node.nick && from != node.nick) {
+ if (from && node.join && (node.from === "" || node.from === conference)) {
+ node.send([msg,null]);
}
+ //}
}
}
else if (stanza.is('presence')) {
From 40362ee985a52290883176acdde7c7def8cc844a Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Tue, 23 Feb 2021 12:45:55 +0000
Subject: [PATCH 27/88] Let xmpp in handle a list of rooms
---
social/xmpp/92-xmpp.html | 1 +
social/xmpp/92-xmpp.js | 13 ++++++++-----
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/social/xmpp/92-xmpp.html b/social/xmpp/92-xmpp.html
index 1b81a8ef..dbf3eb9a 100644
--- a/social/xmpp/92-xmpp.html
+++ b/social/xmpp/92-xmpp.html
@@ -19,6 +19,7 @@
Note: By leaving Buddy empty and ticking "Is a chat room",
the node will try to listen to all the rooms the user has access to.
+ You can specify multiple rooms by separating them by a :
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index d056ef51..ce6b90d9 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -322,7 +322,8 @@ 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 || "").trim();
+ // (because it's where you are asking to get messages from...)
+ this.from = ((n.to || "").split(':')).map(s => s.trim());
this.quiet = false;
// MUC == Multi-User-Chat == chatroom
//this.muc = this.join && (this.from !== "")
@@ -357,13 +358,15 @@ module.exports = function(RED) {
node.quiet = false;
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
if (node.join) {
- if (node.from === "") {
+ if (node.from[0] === "") {
// try to get list of all rooms and join them all.
getItems(this.serverConfig.server,this.serverConfig.id,xmpp);
}
else {
// 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);
+ for (var i=0; i
Date: Tue, 23 Feb 2021 22:15:57 +0000
Subject: [PATCH 28/88] add info command to xmpp nodes
---
social/xmpp/92-xmpp.js | 52 +++++++++++++++++++++++++-----------------
1 file changed, 31 insertions(+), 21 deletions(-)
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index ce6b90d9..e575ee90 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -161,12 +161,6 @@ 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); }
- }
}
});
@@ -225,15 +219,6 @@ module.exports = function(RED) {
xmpp.send(stanza);
}
- function getInfo(thing,id,xmpp) {
- // Now try to get a list of all info about a thing
- var stanza = xml('iq',
- {type:'get', id:id, to:thing},
- xml('query', 'http://jabber.org/protocol/disco#info')
- );
- 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
@@ -404,7 +389,7 @@ module.exports = function(RED) {
// Meat of it, a stanza object contains chat messages (and other things)
xmpp.on('stanza', async (stanza) => {
- if (RED.settings.verbose || LOGITALL) {node.log(stanza); }
+ if (RED.settings.verbose || LOGITALL) { node.log(stanza); }
if (stanza.is('message')) {
if (stanza.attrs.type == 'chat') {
var body = stanza.getChild('body');
@@ -415,7 +400,7 @@ module.exports = function(RED) {
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 (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]);
}
@@ -473,8 +458,8 @@ 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); }
+ 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") {
@@ -495,6 +480,21 @@ module.exports = function(RED) {
}
if (RED.settings.verbose || LOGITALL) {this.log("ROOMS:"+this.server+this.roomsFound); }
}
+ 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]);
+ }
}
});
@@ -652,7 +652,17 @@ module.exports = function(RED) {
)
);
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 to = node.to || msg.topic || "";
+ 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);
}
}
@@ -698,7 +708,7 @@ module.exports = function(RED) {
});
node.on("close", function(removed, done) {
- if (RED.settings.verbose || LOGITALL) {node.log("Closing"); }
+ if (RED.settings.verbose || LOGITALL) { node.log("Closing"); }
node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
node.serverConfig.deregister(node, done);
});
From c368e3bcd472a567885dda060c618cb94fa995dd Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Thu, 25 Feb 2021 09:05:11 +0000
Subject: [PATCH 29/88] xmpp auto create rooms correctly and auto join every 60
secs if required
---
social/xmpp/92-xmpp.js | 146 ++++++++++++++++++++++++---------------
social/xmpp/package.json | 2 +-
2 files changed, 91 insertions(+), 57 deletions(-)
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index e575ee90..d6b2bb0c 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -97,6 +97,7 @@ module.exports = function(RED) {
// 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) => {
+ //console.log("STAN",stanza.toString())
if (stanza.is('message')) {
if (stanza.attrs.type == 'error') {
if (RED.settings.verbose || LOGITALL) {
@@ -106,27 +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";
+ var text = "error";
if (typeof textObj !== "undefined") {
text = textObj.getText();
}
else {
- textObj = err.getChild('code');
+ textObj = err.getAttr('code');
if (typeof textObj !== "undefined") {
- text = textObj.getText();
+ text = textObj;
}
}
if (RED.settings.verbose || LOGITALL) {that.log("Culprit: "+that.lastUsed.id); }
if (typeof that.lastUsed !== "undefined") {
- that.lastUsed.status({fill:"red",shape:"ring",text:text});
- that.lastUsed.warn(text);
+ that.lastUsed.status({fill:"red",shape:"ring",text:"error "+text});
+ that.lastUsed.warn("Error "+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);
}
@@ -151,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.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"); }
@@ -188,18 +207,22 @@ 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'}));
- try{
- if (RED.settings.verbose || LOGITALL) {
- that.log("Calling stop() after close, status is "+that.client.status);
- }
- await that.client.stop().then(that.log("XMPP client stopped")).catch(error=>{that.warn("Got an error whilst closing xmpp session: "+error)});
- }
- catch(e) {
- that.warn(e);
- }
+ const rooms = Object.keys(this.MUCs)
+ for (const room of rooms) {
+ await that.client.send(xml('presence', {to:room, type:'unavailable'}));
}
+ // if (that.client.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);
+ }
+ await that.client.stop().then(that.log("XMPP client stopped")).catch(error=>{that.warn("Got an error whilst closing xmpp session: "+error)});
+ }
+ catch(e) {
+ that.warn(e);
+ }
+ // }
done();
});
}
@@ -227,9 +250,7 @@ module.exports = function(RED) {
// Yes, there's a race condition, but it's not a huge problem to send two messages
// so we don't care.
if (name in node.serverConfig.MUCs) {
- if (RED.settings.verbose || LOGITALL) {
- node.log("already joined MUC "+name);
- }
+ if (RED.settings.verbose || LOGITALL) { node.log("already joined MUC "+name); }
}
else {
var stanza = xml('presence',
@@ -240,8 +261,8 @@ module.exports = function(RED) {
);
node.serverConfig.used(node);
node.serverConfig.MUCs[name] = "joined";
+ if (RED.settings.verbose || LOGITALL) { node.log("JOINED "+name); }
xmpp.send(stanza);
- if (RED.settings.verbose || LOGITALL) { node.log("JOINED",name); }
}
}
@@ -293,7 +314,7 @@ module.exports = function(RED) {
// nothing we've seen before!
else {
node.error("Unknown error: "+err,err);
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
+ node.status({fill:"red",shape:"ring",text:"error"});
}
}
}
@@ -312,10 +333,21 @@ module.exports = function(RED) {
this.quiet = false;
// MUC == Multi-User-Chat == chatroom
//this.muc = this.join && (this.from !== "")
- // list of possible rooms - queried from server
- this.roomsFound = {};
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.quiet = false;
- node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ node.status({fill:"green",shape:"dot",text:"connected"});
if (node.join) {
- if (node.from[0] === "") {
- // try to get list of all rooms and join them all.
- getItems(this.serverConfig.server,this.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 {
if (!node.quiet) {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ 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"});
@@ -374,7 +398,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"});
@@ -391,6 +415,7 @@ module.exports = function(RED) {
xmpp.on('stanza', async (stanza) => {
if (RED.settings.verbose || LOGITALL) { node.log(stanza); }
if (stanza.is('message')) {
+ // console.log(stanza.toString())
if (stanza.attrs.type == 'chat') {
var body = stanza.getChild('body');
if (body) {
@@ -470,15 +495,16 @@ module.exports = function(RED) {
getItems(_items[i].attrs.jid,this.serverConfig.jid,xmpp);
}
else {
- node.roomsFound[_items[i].attrs.name] = _items[i].attrs.jid;
var name = _items[i].attrs.jid+'/'+node.serverConfig.username;
if (!(name in node.serverConfig.MUCs)) {
- if (RED.settings.verbose || LOGITALL) {this.log("Need to Join room:"+name); }
- joinMUC(node, xmpp, name)
+ if (RED.settings.verbose || LOGITALL) { node.log("Need to Join room:"+name); }
+ joinMUC(node, xmpp, name);
+ }
+ else {
+ if (RED.settings.verbose || LOGITALL) { node.log("Already joined:"+name); }
}
}
}
- if (RED.settings.verbose || LOGITALL) {this.log("ROOMS:"+this.server+this.roomsFound); }
}
if (query && query.attrs.hasOwnProperty("xmlns") && query.attrs["xmlns"] === "http://jabber.org/protocol/disco#info") {
var fe = [];
@@ -507,26 +533,30 @@ 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"});
+ 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.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);
});
}
@@ -563,7 +593,7 @@ module.exports = function(RED) {
// 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:"node-red:common.status.connected"});
+ // 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);
@@ -572,7 +602,7 @@ module.exports = function(RED) {
// sod it, register it anyway, that way things will work better on a reconnect:
xmpp.on('online', function(data) {
node.quiet = false;
- node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ 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);
@@ -581,11 +611,11 @@ module.exports = function(RED) {
xmpp.on('connecting', async address => {
if (!node.quiet) {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ 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"});
@@ -609,6 +639,11 @@ module.exports = function(RED) {
errorHandler(node, err)
});
+ xmpp.on('stanza', async (stanza) => {
+ // if (stanza.is('presence')) {
+ // }
+ });
+
//register with config
this.serverConfig.register(this);
// Now actually make the connection
@@ -616,13 +651,13 @@ module.exports = function(RED) {
node.status({fill:"green",shape:"dot",text:"online"});
}
else {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ 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"});
});
}
}
@@ -683,7 +718,6 @@ module.exports = function(RED) {
{ type: type, to: to },
xml("body", {}, JSON.stringify(msg))
);
-
}
else if (msg.payload) {
if (typeof(msg.payload) === "object") {
@@ -709,7 +743,7 @@ module.exports = function(RED) {
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"});
+ node.status({fill:"grey",shape:"ring",text:"disconnected"});
node.serverConfig.deregister(node, done);
});
}
diff --git a/social/xmpp/package.json b/social/xmpp/package.json
index 7d1aa843..c249eb03 100644
--- a/social/xmpp/package.json
+++ b/social/xmpp/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-xmpp",
- "version": "0.4.0",
+ "version": "0.4.1",
"description": "A Node-RED node to talk to an XMPP server",
"dependencies": {
"@xmpp/client": "^0.12.0"
From d69da748437b59fc5e8dc9298af087a1043cc761 Mon Sep 17 00:00:00 2001
From: Pablo Acosta-Serafini <63065092+pmacostapdi@users.noreply.github.com>
Date: Fri, 26 Feb 2021 09:38:28 -0500
Subject: [PATCH 30/88] timeswitch node: time zone support; do not mark as
misconfigured when sunrise/sunset not used and lat/lon not given (#757)
* timeswitch node: a) do not mark node as misconfigured if sunrise and
sunset are not used and latitude/longitude are not given. b) support for
specifying time zone when on and/or off times are not specified as
sunrise and/or sunset.
* Replaced moment dependency with spacetime
* Timezone defaults to UTC for compatibility with previous node version
---
time/timeswitch/package.json | 1 +
time/timeswitch/timeswitch.html | 646 +++++++++++++++++++++++++++++++-
time/timeswitch/timeswitch.js | 9 +-
3 files changed, 649 insertions(+), 7 deletions(-)
diff --git a/time/timeswitch/package.json b/time/timeswitch/package.json
index acaf2bf0..6d0fca3b 100644
--- a/time/timeswitch/package.json
+++ b/time/timeswitch/package.json
@@ -3,6 +3,7 @@
"version" : "0.0.8",
"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" : {
diff --git a/time/timeswitch/timeswitch.html b/time/timeswitch/timeswitch.html
index 910cca6a..52506229 100644
--- a/time/timeswitch/timeswitch.html
+++ b/time/timeswitch/timeswitch.html
@@ -214,6 +214,616 @@
+
Die Nachricht in msg.payload kann als HTML formatiert sein.
+
Die Nachricht in msg.payload kann als HTML formatiert sein.
+ Ein separater, davon abweichender Plaintext kann in msg.plaintext angegeben werden. Ansonsten wird auch msg.payload verwendet.
+ msg.plaintext wird ignoriert, wenn msg.payload kein HTML enthält.
Wenn msg.payload ein binärer Buffer ist, so wird sie in einen Nachrichten-Dateianhang (attachment) konvertiert.
Der Dateiname sollte mittels msg.filename angegeben werden.
Optional kann msg.description als Nachrichtentext hinzugefügt werden.
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
From 1c256fd3b3038bcc53941b65e7c7f29408fee077 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Thu, 1 Apr 2021 09:36:36 +0100
Subject: [PATCH 80/88] refix email attachments array
---
social/email/61-email.js | 7 ++++---
social/email/package.json | 2 +-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/social/email/61-email.js b/social/email/61-email.js
index 3bb4e130..b4a51385 100644
--- a/social/email/61-email.js
+++ b/social/email/61-email.js
@@ -119,15 +119,16 @@ module.exports = function(RED) {
else {
var payload = RED.util.ensureString(msg.payload);
sendopts.text = payload; // plaintext body
- if (/<[a-z][\s\S]*>/i.test(payload)) {
+ if (/<[a-z][\s\S]*>/i.test(payload)) {
sendopts.html = payload; // html body
if (msg.hasOwnProperty("plaintext")) {
var plaintext = RED.util.ensureString(msg.plaintext);
sendopts.text = plaintext; // plaintext body - specific plaintext version
}
}
- if (msg.attachments && Array.isArray(msg.attachments)) {
- sendopts.attachments = msg.attachments;
+ if (msg.attachments) {
+ if (!Array.isArray(msg.attachments)) { sendopts.attachments = [ msg.attachments ]; }
+ else { sendopts.attachments = msg.attachments; }
for (var a=0; a < sendopts.attachments.length; a++) {
if (sendopts.attachments[a].hasOwnProperty("content")) {
if (typeof sendopts.attachments[a].content !== "string" && !Buffer.isBuffer(sendopts.attachments[a].content)) {
diff --git a/social/email/package.json b/social/email/package.json
index ed986d3e..e103ac71 100644
--- a/social/email/package.json
+++ b/social/email/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-email",
- "version": "1.10.1",
+ "version": "1.11.0",
"description": "Node-RED nodes to send and receive simple emails.",
"dependencies": {
"imap": "^0.8.19",
From 61a03d7cc3f0bcd4b340c70e19c5c7a51e2ef539 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Sat, 3 Apr 2021 16:55:45 +0100
Subject: [PATCH 81/88] Tidy email node README to remove false "only one mail"
statement.
and tidy some other newer functionality. like plaintext.
to close #792
---
social/email/README.md | 27 ++++++++----------------
social/email/locales/en-US/61-email.html | 6 +++---
social/email/package.json | 2 +-
3 files changed, 13 insertions(+), 22 deletions(-)
diff --git a/social/email/README.md b/social/email/README.md
index 8a096719..85c8ad3e 100644
--- a/social/email/README.md
+++ b/social/email/README.md
@@ -3,7 +3,6 @@ node-red-node-email
Node-RED nodes to send and receive simple emails.
-
Pre-requisite
-------------
@@ -12,13 +11,10 @@ getting an application password if you have two-factor authentication enabled.
**Note :** Version 1.x of this node requires **Node.js v8** or newer.
-
Install
-------
-Version 0.x of this node is usually installed by default by Node-RED.
-As long as you have at least version 0.19.x of Node-RED you can install the new version
-by using the `Menu - Manage Palette` option, or running the following command in your
+You can install by using the `Menu - Manage Palette` option, or running the following command in your
Node-RED user directory - typically `~/.node-red`
cd ~/.node-red
@@ -35,9 +31,9 @@ Usage
Nodes to send and receive simple emails.
-### Input
+### Input node
-Repeatedly gets emails from an IMAP or POP3 server and forwards them onwards as messages if not already seen.
+Fetches emails from an IMAP or POP3 server and forwards them onwards as messages if not already seen.
The subject is loaded into `msg.topic` and `msg.payload` is the plain text body.
If there is text/html then that is returned in `msg.html`. `msg.from` and
@@ -46,24 +42,19 @@ If there is text/html then that is returned in `msg.html`. `msg.from` and
Additionally `msg.header` contains the complete header object including
**to**, **cc** and other potentially useful properties.
-**Note:** this node *only* gets the most recent single email from the inbox,
-so set the repeat (polling) time appropriately.
-
-Uses the *imap* npm module.
-
-### Output
+### Output node
Sends the `msg.payload` as an email, with a subject of `msg.topic`.
The default message recipient can be configured in the node, if it is left
blank it should be set using the `msg.to` property of the incoming message.
-The email *from* can be set using `msg.from` but not all mail services allow
-this unless `msg.from` is also a valid userid or email address associated with
-the password. Note: if `userid` or msg.from does not contain a valid email
-address (userxx@some_domain.com), you may see (No Sender) in the email.
+The email *from* can be set using `msg.from` but not all mail services allow
+this unless `msg.from` is also a valid userid or email address associated with
+the password. Note: if `userid` or msg.from does not contain a valid email
+address (userxx@some_domain.com), you may see *(No Sender)* in the email.
-The payload can be html format.
+The payload can be html format. You can also specify `msg.plaintext` if the main payload is html.
If the payload is a binary buffer then it will be converted to an attachment.
diff --git a/social/email/locales/en-US/61-email.html b/social/email/locales/en-US/61-email.html
index e0f5b392..9fd86047 100644
--- a/social/email/locales/en-US/61-email.html
+++ b/social/email/locales/en-US/61-email.html
@@ -10,8 +10,8 @@
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.
+
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.
@@ -19,7 +19,7 @@
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: For POP3, the default port numbers are 110 for plain TCP and 995 for SSL. For IMAP the port numbers are 143 for plain TCP and 993 for SSL.
+
Note: With option 'STARTTLS' an established plain connection is upgraded to an encrypted one. Set to 'always' to always attempt connection upgrades via STARTTLS, 'required' only if upgrading is required, or 'never' to never attempt upgrading.
Note: The maximum refresh interval is 2147483 seconds (24.8 days).