@node-red/nodes 2.2.0 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/common/05-junction.html +5 -0
- package/core/common/05-junction.js +12 -0
- package/core/common/20-inject.html +25 -13
- package/core/common/21-debug.html +60 -6
- package/core/common/21-debug.js +60 -29
- package/core/common/60-link.html +66 -29
- package/core/common/60-link.js +169 -20
- package/core/common/lib/debug/debug-utils.js +34 -1
- package/core/function/10-function.html +57 -21
- package/core/function/10-switch.html +3 -1
- package/core/function/10-switch.js +1 -0
- package/core/function/15-change.html +40 -12
- package/core/function/16-range.html +14 -5
- package/core/function/80-template.html +16 -12
- package/core/function/89-delay.html +46 -6
- package/core/function/89-trigger.html +12 -4
- package/core/function/rbe.html +7 -3
- package/core/network/05-tls.html +10 -4
- package/core/network/06-httpproxy.html +10 -1
- package/core/network/10-mqtt.html +73 -17
- package/core/network/10-mqtt.js +205 -95
- package/core/network/21-httpin.html +6 -2
- package/core/network/21-httprequest.html +217 -12
- package/core/network/21-httprequest.js +98 -17
- package/core/network/22-websocket.html +19 -5
- package/core/network/22-websocket.js +16 -13
- package/core/network/31-tcpin.html +47 -10
- package/core/network/31-tcpin.js +8 -3
- package/core/network/32-udp.html +14 -2
- package/core/parsers/70-CSV.html +4 -1
- package/core/parsers/70-JSON.html +3 -2
- package/core/parsers/70-XML.html +2 -1
- package/core/parsers/70-YAML.html +2 -1
- package/core/sequence/17-split.html +5 -1
- package/core/sequence/19-batch.html +28 -4
- package/core/storage/10-file.html +68 -8
- package/core/storage/10-file.js +46 -3
- package/core/storage/23-watch.html +2 -1
- package/core/storage/23-watch.js +21 -43
- package/locales/de/messages.json +1 -0
- package/locales/en-US/common/60-link.html +18 -3
- package/locales/en-US/messages.json +68 -17
- package/locales/en-US/network/21-httprequest.html +1 -1
- package/locales/en-US/storage/10-file.html +6 -2
- package/locales/ja/common/60-link.html +12 -0
- package/locales/ja/messages.json +65 -18
- package/locales/ko/messages.json +1 -0
- package/locales/ru/messages.json +1 -0
- package/locales/zh-CN/messages.json +1 -0
- package/locales/zh-TW/messages.json +1 -0
- package/package.json +12 -12
package/core/network/10-mqtt.js
CHANGED
|
@@ -20,7 +20,30 @@ module.exports = function(RED) {
|
|
|
20
20
|
var isUtf8 = require('is-utf8');
|
|
21
21
|
var HttpsProxyAgent = require('https-proxy-agent');
|
|
22
22
|
var url = require('url');
|
|
23
|
-
|
|
23
|
+
const knownMediaTypes = {
|
|
24
|
+
"text/css":"string",
|
|
25
|
+
"text/html":"string",
|
|
26
|
+
"text/plain":"string",
|
|
27
|
+
"text/html":"string",
|
|
28
|
+
"application/json":"json",
|
|
29
|
+
"application/octet-stream":"buffer",
|
|
30
|
+
"application/pdf":"buffer",
|
|
31
|
+
"application/x-gtar":"buffer",
|
|
32
|
+
"application/x-gzip":"buffer",
|
|
33
|
+
"application/x-tar":"buffer",
|
|
34
|
+
"application/xml":"string",
|
|
35
|
+
"application/zip":"buffer",
|
|
36
|
+
"audio/aac":"buffer",
|
|
37
|
+
"audio/ac3":"buffer",
|
|
38
|
+
"audio/basic":"buffer",
|
|
39
|
+
"audio/mp4":"buffer",
|
|
40
|
+
"audio/ogg":"buffer",
|
|
41
|
+
"image/bmp":"buffer",
|
|
42
|
+
"image/gif":"buffer",
|
|
43
|
+
"image/jpeg":"buffer",
|
|
44
|
+
"image/tiff":"buffer",
|
|
45
|
+
"image/png":"buffer",
|
|
46
|
+
}
|
|
24
47
|
//#region "Supporting functions"
|
|
25
48
|
function matchTopic(ts,t) {
|
|
26
49
|
if (ts == "#") {
|
|
@@ -68,12 +91,21 @@ module.exports = function(RED) {
|
|
|
68
91
|
}
|
|
69
92
|
|
|
70
93
|
/**
|
|
71
|
-
* Test a topic string is valid
|
|
94
|
+
* Test a topic string is valid for subscription
|
|
72
95
|
* @param {string} topic
|
|
73
96
|
* @returns `true` if it is a valid topic
|
|
74
97
|
*/
|
|
75
98
|
function isValidSubscriptionTopic(topic) {
|
|
76
|
-
return /^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/.test(topic)
|
|
99
|
+
return /^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/.test(topic);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Test a topic string is valid for publishing
|
|
104
|
+
* @param {string} topic
|
|
105
|
+
* @returns `true` if it is a valid topic
|
|
106
|
+
*/
|
|
107
|
+
function isValidPublishTopic(topic) {
|
|
108
|
+
return !/[\+#\b\f\n\r\t\v\0]/.test(topic);
|
|
77
109
|
}
|
|
78
110
|
|
|
79
111
|
/**
|
|
@@ -103,7 +135,7 @@ module.exports = function(RED) {
|
|
|
103
135
|
if(src[propName] === "true" || src[propName] === true) {
|
|
104
136
|
dst[propName] = true;
|
|
105
137
|
} else if(src[propName] === "false" || src[propName] === false) {
|
|
106
|
-
dst[propName] =
|
|
138
|
+
dst[propName] = false;
|
|
107
139
|
}
|
|
108
140
|
} else {
|
|
109
141
|
if(def != undefined) dst[propName] = def;
|
|
@@ -188,6 +220,19 @@ module.exports = function(RED) {
|
|
|
188
220
|
*/
|
|
189
221
|
function subscriptionHandler(node, datatype ,topic, payload, packet) {
|
|
190
222
|
const v5 = node.brokerConn.options && node.brokerConn.options.protocolVersion == 5;
|
|
223
|
+
var msg = {topic:topic, payload:null, qos:packet.qos, retain:packet.retain};
|
|
224
|
+
if(v5 && packet.properties) {
|
|
225
|
+
setStrProp(packet.properties, msg, "responseTopic");
|
|
226
|
+
setBufferProp(packet.properties, msg, "correlationData");
|
|
227
|
+
setStrProp(packet.properties, msg, "contentType");
|
|
228
|
+
setIntProp(packet.properties, msg, "messageExpiryInterval", 0);
|
|
229
|
+
setBoolProp(packet.properties, msg, "payloadFormatIndicator");
|
|
230
|
+
setStrProp(packet.properties, msg, "reasonString");
|
|
231
|
+
setUserProperties(packet.properties.userProperties, msg);
|
|
232
|
+
}
|
|
233
|
+
const v5isUtf8 = v5 ? msg.payloadFormatIndicator === true : null;
|
|
234
|
+
const v5HasMediaType = v5 ? !!msg.contentType : null;
|
|
235
|
+
const v5MediaTypeLC = v5 ? (msg.contentType + "").toLowerCase() : null;
|
|
191
236
|
|
|
192
237
|
if (datatype === "buffer") {
|
|
193
238
|
// payload = payload;
|
|
@@ -196,25 +241,65 @@ module.exports = function(RED) {
|
|
|
196
241
|
} else if (datatype === "utf8") {
|
|
197
242
|
payload = payload.toString('utf8');
|
|
198
243
|
} else if (datatype === "json") {
|
|
199
|
-
if (isUtf8(payload)) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
catch(e) {
|
|
244
|
+
if (v5isUtf8 || isUtf8(payload)) {
|
|
245
|
+
try {
|
|
246
|
+
payload = JSON.parse(payload.toString());
|
|
247
|
+
} catch (e) {
|
|
248
|
+
node.error(RED._("mqtt.errors.invalid-json-parse"), { payload: payload, topic: topic, qos: packet.qos, retain: packet.retain }); return;
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
node.error((RED._("mqtt.errors.invalid-json-string")), { payload: payload, topic: topic, qos: packet.qos, retain: packet.retain }); return;
|
|
203
252
|
}
|
|
204
|
-
else { node.error((RED._("mqtt.errors.invalid-json-string")),{payload:payload, topic:topic, qos:packet.qos, retain:packet.retain}); return; }
|
|
205
253
|
} else {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
254
|
+
//"auto" (legacy) or "auto-detect" (new default)
|
|
255
|
+
if (v5isUtf8 || v5HasMediaType) {
|
|
256
|
+
const outputType = knownMediaTypes[v5MediaTypeLC]
|
|
257
|
+
switch (outputType) {
|
|
258
|
+
case "string":
|
|
259
|
+
payload = payload.toString();
|
|
260
|
+
break;
|
|
261
|
+
case "buffer":
|
|
262
|
+
//no change
|
|
263
|
+
break;
|
|
264
|
+
case "json":
|
|
265
|
+
try {
|
|
266
|
+
//since v5 type states this should be JSON, parse it & error out if NOT JSON
|
|
267
|
+
payload = payload.toString()
|
|
268
|
+
const obj = JSON.parse(payload);
|
|
269
|
+
if (datatype === "auto-detect") {
|
|
270
|
+
payload = obj; //as mode is "auto-detect", return the parsed JSON
|
|
271
|
+
}
|
|
272
|
+
} catch (e) {
|
|
273
|
+
node.error(RED._("mqtt.errors.invalid-json-parse"), { payload: payload, topic: topic, qos: packet.qos, retain: packet.retain }); return;
|
|
274
|
+
}
|
|
275
|
+
break;
|
|
276
|
+
default:
|
|
277
|
+
if (v5isUtf8 || isUtf8(payload)) {
|
|
278
|
+
payload = payload.toString(); //auto String
|
|
279
|
+
if (datatype === "auto-detect") {
|
|
280
|
+
try {
|
|
281
|
+
payload = JSON.parse(payload); //auto to parsed object (attempt)
|
|
282
|
+
} catch (e) {
|
|
283
|
+
/* mute error - it simply isnt JSON, just leave payload as a string */
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
} else if (isUtf8(payload)) {
|
|
290
|
+
payload = payload.toString(); //auto String
|
|
291
|
+
if (datatype === "auto-detect") {
|
|
292
|
+
try {
|
|
293
|
+
payload = JSON.parse(payload);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
/* mute error - it simply isnt JSON, just leave payload as a string */
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
} //else {
|
|
299
|
+
//leave as buffer
|
|
300
|
+
//}
|
|
217
301
|
}
|
|
302
|
+
msg.payload = payload;
|
|
218
303
|
if ((node.brokerConn.broker === "localhost")||(node.brokerConn.broker === "127.0.0.1")) {
|
|
219
304
|
msg._topic = topic;
|
|
220
305
|
}
|
|
@@ -264,38 +349,15 @@ module.exports = function(RED) {
|
|
|
264
349
|
msg.messageExpiryInterval = node.messageExpiryInterval;
|
|
265
350
|
}
|
|
266
351
|
}
|
|
267
|
-
if (msg.userProperties && typeof msg.userProperties !== "object") {
|
|
268
|
-
delete msg.userProperties;
|
|
269
|
-
}
|
|
270
|
-
if (hasProperty(msg, "topicAlias") && !isNaN(msg.topicAlias) && (msg.topicAlias === 0 || bsp.topicAliasMaximum === 0 || msg.topicAlias > bsp.topicAliasMaximum)) {
|
|
271
|
-
delete msg.topicAlias;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
352
|
if (hasProperty(msg, "payload")) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
//NOTE: A value of 0 (in server props topicAliasMaximum) indicates that the Server does not accept any Topic Aliases on this connection
|
|
281
|
-
if (hasProperty(msg, "topicAlias") && !isNaN(msg.topicAlias) && msg.topicAlias >= 0 && bsp.topicAliasMaximum && bsp.topicAliasMaximum >= msg.topicAlias) {
|
|
282
|
-
topicOK = true;
|
|
283
|
-
msg.topic = ""; //must be empty string
|
|
284
|
-
} else if (hasProperty(msg, "responseTopic") && (typeof msg.responseTopic === "string") && (msg.responseTopic !== "")) {
|
|
285
|
-
//TODO: if topic is empty but responseTopic has a string value, use that instead. Is this desirable?
|
|
286
|
-
topicOK = true;
|
|
287
|
-
msg.topic = msg.responseTopic;
|
|
288
|
-
//TODO: delete msg.responseTopic - to prevent it being resent?
|
|
353
|
+
// send the message
|
|
354
|
+
node.brokerConn.publish(msg, function(err) {
|
|
355
|
+
if(err && err.warn) {
|
|
356
|
+
node.warn(err);
|
|
357
|
+
return;
|
|
289
358
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (topicOK) {
|
|
294
|
-
node.brokerConn.publish(msg, done); // send the message
|
|
295
|
-
} else {
|
|
296
|
-
node.warn(RED._("mqtt.errors.invalid-topic"));
|
|
297
|
-
done();
|
|
298
|
-
}
|
|
359
|
+
done(err);
|
|
360
|
+
});
|
|
299
361
|
} else {
|
|
300
362
|
done();
|
|
301
363
|
}
|
|
@@ -415,8 +477,12 @@ module.exports = function(RED) {
|
|
|
415
477
|
setIfHasProperty(opts, node, "topicAliasMaximum", init);
|
|
416
478
|
setIfHasProperty(opts, node, "maximumPacketSize", init);
|
|
417
479
|
setIfHasProperty(opts, node, "receiveMaximum", init);
|
|
418
|
-
|
|
419
|
-
|
|
480
|
+
//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116
|
|
481
|
+
if (hasProperty(opts, "userProperties")) {
|
|
482
|
+
node.userProperties = opts.userProperties;
|
|
483
|
+
} else if (hasProperty(opts, "userProps")) {
|
|
484
|
+
node.userProperties = opts.userProps;
|
|
485
|
+
}
|
|
420
486
|
|
|
421
487
|
function createLWT(topic, payload, qos, retain, v5opts, v5SubPropName) {
|
|
422
488
|
let message = undefined;
|
|
@@ -465,7 +531,7 @@ module.exports = function(RED) {
|
|
|
465
531
|
};
|
|
466
532
|
if(hasProperty(opts, "willTopic")) {
|
|
467
533
|
//will v5 properties must be set in the "properties" sub object
|
|
468
|
-
node.options.will = createLWT(opts.willTopic, opts.willPayload, opts.willQos, opts.willRetain, opts.willMsg, "
|
|
534
|
+
node.options.will = createLWT(opts.willTopic, opts.willPayload, opts.willQos, opts.willRetain, opts.willMsg, "properties");
|
|
469
535
|
};
|
|
470
536
|
} else {
|
|
471
537
|
//update options
|
|
@@ -637,24 +703,8 @@ module.exports = function(RED) {
|
|
|
637
703
|
|
|
638
704
|
node.deregister = function(mqttNode,done) {
|
|
639
705
|
delete node.users[mqttNode.id];
|
|
640
|
-
if (node.closing) {
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
if (Object.keys(node.users).length === 0) {
|
|
644
|
-
if (node.client && node.client.connected) {
|
|
645
|
-
// Send close message
|
|
646
|
-
if (node.closeMessage) {
|
|
647
|
-
node.publish(node.closeMessage,function(err) {
|
|
648
|
-
node.client.end(done);
|
|
649
|
-
});
|
|
650
|
-
} else {
|
|
651
|
-
node.client.end(done);
|
|
652
|
-
}
|
|
653
|
-
return;
|
|
654
|
-
} else {
|
|
655
|
-
if (node.client) { node.client.end(); }
|
|
656
|
-
return done();
|
|
657
|
-
}
|
|
706
|
+
if (!node.closing && node.connected && Object.keys(node.users).length === 0) {
|
|
707
|
+
node.disconnect();
|
|
658
708
|
}
|
|
659
709
|
done();
|
|
660
710
|
};
|
|
@@ -663,6 +713,7 @@ module.exports = function(RED) {
|
|
|
663
713
|
}
|
|
664
714
|
node.connect = function (callback) {
|
|
665
715
|
if (node.canConnect()) {
|
|
716
|
+
node.closing = false;
|
|
666
717
|
node.connecting = true;
|
|
667
718
|
setStatusConnecting(node, true);
|
|
668
719
|
try {
|
|
@@ -672,6 +723,7 @@ module.exports = function(RED) {
|
|
|
672
723
|
let callbackDone = false; //prevent re-connects causing node.client.on('connect' firing callback multiple times
|
|
673
724
|
// Register successful connect or reconnect handler
|
|
674
725
|
node.client.on('connect', function (connack) {
|
|
726
|
+
node.closing = false;
|
|
675
727
|
node.connecting = false;
|
|
676
728
|
node.connected = true;
|
|
677
729
|
if(!callbackDone && typeof callback == "function") {
|
|
@@ -740,6 +792,7 @@ module.exports = function(RED) {
|
|
|
740
792
|
reasonCode: rc,
|
|
741
793
|
reasonString: rs
|
|
742
794
|
}
|
|
795
|
+
node.connected = false;
|
|
743
796
|
node.log(RED._("mqtt.state.broker-disconnected", details));
|
|
744
797
|
setStatusDisconnected(node, true);
|
|
745
798
|
});
|
|
@@ -763,26 +816,48 @@ module.exports = function(RED) {
|
|
|
763
816
|
}
|
|
764
817
|
}
|
|
765
818
|
};
|
|
819
|
+
|
|
766
820
|
node.disconnect = function (callback) {
|
|
767
821
|
const _callback = function () {
|
|
768
|
-
|
|
822
|
+
if(node.connected || node.connecting) {
|
|
823
|
+
setStatusDisconnected(node, true);
|
|
824
|
+
}
|
|
825
|
+
if(node.client) { node.client.removeAllListeners(); }
|
|
769
826
|
node.connecting = false;
|
|
770
827
|
node.connected = false;
|
|
771
828
|
callback && typeof callback == "function" && callback();
|
|
772
829
|
};
|
|
773
|
-
|
|
774
|
-
if(node.
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
830
|
+
if(!node.client) { return _callback(); }
|
|
831
|
+
if(node.closing) { return _callback(); }
|
|
832
|
+
|
|
833
|
+
let waitEnd = (client, ms) => {
|
|
834
|
+
return new Promise( (resolve, reject) => {
|
|
835
|
+
node.closing = true;
|
|
836
|
+
if(!client) {
|
|
837
|
+
resolve();
|
|
838
|
+
} else {
|
|
839
|
+
const t = setTimeout(reject, ms);
|
|
840
|
+
client.end(() => {
|
|
841
|
+
clearTimeout(t);
|
|
842
|
+
resolve()
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
};
|
|
847
|
+
if(node.connected && node.closeMessage) {
|
|
848
|
+
node.publish(node.closeMessage, function (err) {
|
|
849
|
+
waitEnd(node.client, 2000).then(() => {
|
|
850
|
+
_callback();
|
|
851
|
+
}).catch((e) => {
|
|
852
|
+
_callback();
|
|
853
|
+
})
|
|
854
|
+
});
|
|
784
855
|
} else {
|
|
785
|
-
|
|
856
|
+
waitEnd(node.client, 2000).then(() => {
|
|
857
|
+
_callback();
|
|
858
|
+
}).catch((e) => {
|
|
859
|
+
_callback();
|
|
860
|
+
})
|
|
786
861
|
}
|
|
787
862
|
}
|
|
788
863
|
node.subscriptionIds = {};
|
|
@@ -864,8 +939,18 @@ module.exports = function(RED) {
|
|
|
864
939
|
qos: msg.qos || 0,
|
|
865
940
|
retain: msg.retain || false
|
|
866
941
|
};
|
|
942
|
+
let topicOK = hasProperty(msg, "topic") && (typeof msg.topic === "string") && (isValidPublishTopic(msg.topic));
|
|
867
943
|
//https://github.com/mqttjs/MQTT.js/blob/master/README.md#mqttclientpublishtopic-message-options-callback
|
|
868
944
|
if(node.options.protocolVersion == 5) {
|
|
945
|
+
const bsp = node.serverProperties || {};
|
|
946
|
+
if (msg.userProperties && typeof msg.userProperties !== "object") {
|
|
947
|
+
delete msg.userProperties;
|
|
948
|
+
}
|
|
949
|
+
if (hasProperty(msg, "topicAlias") && !isNaN(Number(msg.topicAlias))) {
|
|
950
|
+
msg.topicAlias = parseInt(msg.topicAlias);
|
|
951
|
+
} else {
|
|
952
|
+
delete msg.topicAlias;
|
|
953
|
+
}
|
|
869
954
|
options.properties = options.properties || {};
|
|
870
955
|
setStrProp(msg, options.properties, "responseTopic");
|
|
871
956
|
setBufferProp(msg, options.properties, "correlationData");
|
|
@@ -875,29 +960,46 @@ module.exports = function(RED) {
|
|
|
875
960
|
setIntProp(msg, options.properties, "topicAlias", 1, node.serverProperties.topicAliasMaximum || 0);
|
|
876
961
|
setBoolProp(msg, options.properties, "payloadFormatIndicator");
|
|
877
962
|
//FUTURE setIntProp(msg, options.properties, "subscriptionIdentifier", 1, 268435455);
|
|
878
|
-
|
|
879
|
-
|
|
963
|
+
|
|
964
|
+
//check & sanitise topic
|
|
965
|
+
if (topicOK && options.properties.topicAlias) {
|
|
966
|
+
let aliasValid = (bsp.topicAliasMaximum && bsp.topicAliasMaximum >= options.properties.topicAlias);
|
|
967
|
+
if (!aliasValid) {
|
|
880
968
|
done("Invalid topicAlias");
|
|
881
969
|
return
|
|
882
970
|
}
|
|
883
971
|
if (node.topicAliases[options.properties.topicAlias] === msg.topic) {
|
|
884
|
-
msg.topic = ""
|
|
972
|
+
msg.topic = "";
|
|
885
973
|
} else {
|
|
886
|
-
node.topicAliases[options.properties.topicAlias] = msg.topic
|
|
974
|
+
node.topicAliases[options.properties.topicAlias] = msg.topic;
|
|
887
975
|
}
|
|
976
|
+
} else if (!msg.topic && options.properties.responseTopic) {
|
|
977
|
+
msg.topic = msg.responseTopic;
|
|
978
|
+
topicOK = isValidPublishTopic(msg.topic);
|
|
979
|
+
delete msg.responseTopic; //prevent responseTopic being resent?
|
|
888
980
|
}
|
|
889
981
|
}
|
|
890
982
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
983
|
+
if (topicOK) {
|
|
984
|
+
node.client.publish(msg.topic, msg.payload, options, function(err) {
|
|
985
|
+
done && done(err);
|
|
986
|
+
return
|
|
987
|
+
});
|
|
988
|
+
} else {
|
|
989
|
+
const error = new Error(RED._("mqtt.errors.invalid-topic"));
|
|
990
|
+
error.warn = true;
|
|
991
|
+
done(error);
|
|
992
|
+
}
|
|
895
993
|
}
|
|
896
994
|
};
|
|
897
995
|
|
|
898
996
|
node.on('close', function(done) {
|
|
899
|
-
node.
|
|
900
|
-
|
|
997
|
+
node.disconnect(function() {
|
|
998
|
+
if(node.client) {
|
|
999
|
+
node.client.removeAllListeners();
|
|
1000
|
+
}
|
|
1001
|
+
done();
|
|
1002
|
+
});
|
|
901
1003
|
});
|
|
902
1004
|
|
|
903
1005
|
}
|
|
@@ -1074,6 +1176,9 @@ module.exports = function(RED) {
|
|
|
1074
1176
|
node.brokerConn.unsubscribe(node.topic,node.id, removed);
|
|
1075
1177
|
}
|
|
1076
1178
|
node.brokerConn.deregister(node, done);
|
|
1179
|
+
node.brokerConn = null;
|
|
1180
|
+
} else {
|
|
1181
|
+
done();
|
|
1077
1182
|
}
|
|
1078
1183
|
});
|
|
1079
1184
|
} else {
|
|
@@ -1134,7 +1239,12 @@ module.exports = function(RED) {
|
|
|
1134
1239
|
}
|
|
1135
1240
|
node.brokerConn.register(node);
|
|
1136
1241
|
node.on('close', function(done) {
|
|
1137
|
-
node.brokerConn
|
|
1242
|
+
if (node.brokerConn) {
|
|
1243
|
+
node.brokerConn.deregister(node,done);
|
|
1244
|
+
node.brokerConn = null;
|
|
1245
|
+
} else {
|
|
1246
|
+
done();
|
|
1247
|
+
}
|
|
1138
1248
|
});
|
|
1139
1249
|
} else {
|
|
1140
1250
|
node.error(RED._("mqtt.errors.missing-config"));
|
|
@@ -70,7 +70,8 @@
|
|
|
70
70
|
color:"rgb(231, 231, 174)",
|
|
71
71
|
defaults: {
|
|
72
72
|
name: {value:""},
|
|
73
|
-
url: {value:"",required:true
|
|
73
|
+
url: {value:"", required:true,
|
|
74
|
+
label:RED._("node-red:httpin.label.url")},
|
|
74
75
|
method: {value:"get",required:true},
|
|
75
76
|
upload: {value:false},
|
|
76
77
|
swaggerDoc: {type:"swagger-doc", required:false}
|
|
@@ -146,7 +147,10 @@
|
|
|
146
147
|
color:"rgb(231, 231, 174)",
|
|
147
148
|
defaults: {
|
|
148
149
|
name: {value:""},
|
|
149
|
-
statusCode: {
|
|
150
|
+
statusCode: {
|
|
151
|
+
value:"",
|
|
152
|
+
label: RED._("node-red:httpin.label.status"),
|
|
153
|
+
validate: RED.validators.number(true)},
|
|
150
154
|
headers: {value:{}}
|
|
151
155
|
},
|
|
152
156
|
inputs:1,
|