1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Update mqtt node options to include will/cleansession/keepalive

This commit is contained in:
Nick O'Leary 2015-09-01 22:30:15 +01:00
parent 437b2d506b
commit fa5e37993e
3 changed files with 198 additions and 95 deletions

View File

@ -30,8 +30,6 @@
<p>MQTT input node. Connects to a broker and subscribes to the specified topic. The topic may contain MQTT wildcards.</p> <p>MQTT input node. Connects to a broker and subscribes to the specified topic. The topic may contain MQTT wildcards.</p>
<p>Outputs an object called <b>msg</b> containing <b>msg.topic, msg.payload, msg.qos</b> and <b>msg.retain</b>.</p> <p>Outputs an object called <b>msg</b> containing <b>msg.topic, msg.payload, msg.qos</b> and <b>msg.retain</b>.</p>
<p><b>msg.payload</b> is usually a string, but can also be a binary buffer.</p> <p><b>msg.payload</b> is usually a string, but can also be a binary buffer.</p>
<p>If a secure connection is being used, certificate checking can be disabled to allow connections to brokers using a self signed or non-trusted CA certificate.</p>
<p>Compatibility mode allows connections to brokers which do not support the MQTT V3.1.1 standard.</p>
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
@ -89,9 +87,6 @@
<p>Connects to a MQTT broker and publishes <b>msg.payload</b> either to the <b>msg.topic</b> or to the topic specified in the edit window. The value in the edit window has precedence.</p> <p>Connects to a MQTT broker and publishes <b>msg.payload</b> either to the <b>msg.topic</b> or to the topic specified in the edit window. The value in the edit window has precedence.</p>
<p>Likewise QoS and/or retain values in the edit panel will overwrite any <b>msg.qos</b> and <b>msg.retain</b> properties. If nothing is set they default to <i>0</i> and <i>false</i> respectively.</p> <p>Likewise QoS and/or retain values in the edit panel will overwrite any <b>msg.qos</b> and <b>msg.retain</b> properties. If nothing is set they default to <i>0</i> and <i>false</i> respectively.</p>
<p>If <b>msg.payload</b> contains an object it will be stringified before being sent.</p> <p>If <b>msg.payload</b> contains an object it will be stringified before being sent.</p>
<p>If a secure connection is being used, certificate checking can be disabled to allow connections to brokers using a self signed or non-trusted CA certificate.</p>
<p>Compatibility mode allows connections to brokers which do not support the MQTT V3.1.1 standard.</p>
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
@ -119,39 +114,77 @@
</script> </script>
<script type="text/x-red" data-template-name="mqtt-broker"> <script type="text/x-red" data-template-name="mqtt-broker">
<div class="form-row node-input-broker">
<label for="node-config-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
<input class="input-append-left" type="text" id="node-config-input-broker" placeholder="localhost" style="width: 40%;" >
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> <span data-i18n="mqtt.label.port"></span></label>
<input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:45px">
</div>
<div class="form-row"> <div class="form-row">
<label>&nbsp;</label> <ul style="background: #fff; min-width: 550px; margin-bottom: 20px;" id="node-config-mqtt-broker-tabs"></ul>
<input type="checkbox" id="node-config-input-secureconn" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-secureconn" style="width: 70%;">Enable secure (SSL/TLS) connection</label>
</div> </div>
<div class="form-row" id="row-disablecertauth"> <div id="node-config-mqtt-broker-tabs-content" style="min-height: 170px;">
<label>&nbsp;</label> <div id="mqtt-broker-tab-connection" style="display:none">
<input type="checkbox" id="node-config-input-disablecertauth" style="display: inline-block; width: auto; vertical-align: top;"> <div class="form-row node-input-broker">
<label for="node-config-input-disablecertauth" style="width: 70%;">Disable certificate authentication (secure connections only)</label> <label for="node-config-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
</div> <input class="input-append-left" type="text" id="node-config-input-broker" placeholder="localhost" style="width: 40%;" >
<div class="form-row"> <label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> <span data-i18n="mqtt.label.port"></span></label>
<label>&nbsp;</label> <input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:45px">
<input type="checkbox" id="node-config-input-compatmode" style="display: inline-block; width: auto; vertical-align: top;"> </div>
<label for="node-config-input-compatmode" style="width: 70%;">Compatibility for brokers not supporting MQTT v3.1.1</label> <div class="form-row">
</div> <label for="node-config-input-clientid"><i class="fa fa-tag"></i> <span data-i18n="mqtt.label.clientid"></span></label>
<div class="form-row"> <input type="text" id="node-config-input-clientid" data-i18n="[placeholder]mqtt.placeholder.clientid">
<label for="node-config-input-clientid"><i class="fa fa-tag"></i> Client ID</label> </div>
<input type="text" id="node-config-input-clientid" placeholder="Leave blank for auto generated">
</div> <div class="form-row">
<div class="form-row"> <label for="node-config-input-keepalive" style="width: auto"><i class="fa fa-clock-o"></i> <span data-i18n="mqtt.label.keepalive"></span></label>
<label for="node-config-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label> <input type="text" id="node-config-input-keepalive" style="width: 50px">
<input type="text" id="node-config-input-user"> <input type="checkbox" id="node-config-input-cleansession" style="margin-left: 30px; height: 1em;display: inline-block; width: auto; vertical-align: middle;">
</div> <label for="node-config-input-cleansession" style="width: auto;" data-i18n="mqtt.label.cleansession"></label>
<div class="form-row"> </div>
<label for="node-config-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label> <div class="form-row">
<input type="password" id="node-config-input-password"> <input type="checkbox" id="node-config-input-compatmode" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-compatmode" style="width: auto;" data-i18n="mqtt.label.compatmode"></label>
</div>
</div>
<div id="mqtt-broker-tab-security" style="display:none">
<div class="form-row">
<label for="node-config-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label>
<input type="text" id="node-config-input-user">
</div>
<div class="form-row">
<label for="node-config-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label>
<input type="password" id="node-config-input-password">
</div>
<div class="form-row">
<input type="checkbox" id="node-config-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-usetls" style="width: 70%;" data-i18n="mqtt.label.use-tls"></label>
</div>
<div class="form-row">
<input type="checkbox" id="node-config-input-verifyservercert" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-verifyservercert" style="width: 70%;" data-i18n="mqtt.label.verify-server-cert"></label>
</div>
</div>
<div id="mqtt-broker-tab-will" style="display:none">
<div class="form-row">
<label for="node-config-input-willTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
<input type="text" id="node-config-input-willTopic" data-i18n="[placeholder]mqtt.placeholder.will-topic">
</div>
<div class="form-row">
<label for="node-config-input-willQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
<select id="node-config-input-willQos" style="width:125px !important">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
&nbsp;&nbsp;<i class="fa fa-history"></i>&nbsp;<span data-i18n="mqtt.retain"></span> &nbsp;<select id="node-config-input-willRetain" style="width:125px !important">
<option value="false" data-i18n="mqtt.false"></option>
<option value="true" data-i18n="mqtt.true"></option>
</select>
</div>
<div class="form-row">
<label for="node-config-input-willPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
<input type="text" id="node-config-input-willPayload" data-i18n="[placeholder]common.label.payload">
</div>
</div>
</div> </div>
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
@ -160,11 +193,23 @@
defaults: { defaults: {
broker: {value:"",required:true}, broker: {value:"",required:true},
port: {value:1883,required:true,validate:RED.validators.number()}, port: {value:1883,required:true,validate:RED.validators.number()},
clientid: { value:"" }, clientid: { value:"", validate: function(v) {
secureconn: {value: false}, if ($("#node-config-input-clientid").length) {
disablecertauth: { value: false}, // Currently editing the node
return $("#node-config-input-cleansession").is(":checked") || v.length > 0;
} else {
return this.cleansession || v.length > 0;
}
}},
usetls: {value: false},
verifyservercert: { value: false},
compatmode: { value: true}, compatmode: { value: true},
mqttkeepalive: {value:15} keepalive: {value:15,validate:RED.validators.number()},
cleansession: {value: true},
willTopic: {value:""},
willQos: {value:0},
willRetain: {value:false},
willPayload: {value:""}
}, },
credentials: { credentials: {
user: {type:"text"}, user: {type:"text"},
@ -175,32 +220,67 @@
return (this.clientid?this.clientid+"@":"")+this.broker+":"+this.port; return (this.clientid?this.clientid+"@":"")+this.broker+":"+this.port;
}, },
oneditprepare: function () { oneditprepare: function () {
if (this.broker && typeof this.secureconn === 'undefined'){ var tabs = RED.tabs.create({
this.secureconn = false; id: "node-config-mqtt-broker-tabs",
onchange: function(tab) {
$("#node-config-mqtt-broker-tabs-content").children().hide();
$("#" + tab.id).show();
}
});
tabs.addTab({
id: "mqtt-broker-tab-connection",
label: this._("mqtt.tabs-label.connection")
});
tabs.addTab({
id: "mqtt-broker-tab-security",
label: this._("mqtt.tabs-label.security")
});
tabs.addTab({
id: "mqtt-broker-tab-will",
label: this._("mqtt.tabs-label.will")
});
setTimeout(function() { tabs.resize()},0);
if (typeof this.usetls === 'undefined'){
this.usetls = false;
$("#node-config-input-usetls").prop("checked",false);
} }
if (this.broker && typeof this.disablecertauth === 'undefined'){ if (typeof this.verifyservercert === 'undefined'){
this.disablecertauth = false; this.verifyservercert = true;
$("#node-config-input-verifyservercert").prop("checked",true);
} }
if (this.broker && typeof this.compatmode === 'undefined'){ if (typeof this.compatmode === 'undefined'){
this.compatmode = true; this.compatmode = true;
$("#node-config-input-compatmode").prop('checked', true); $("#node-config-input-compatmode").prop('checked', true);
} }
if (this.broker && typeof this.mqttkeepalive === 'undefined'){ if (typeof this.keepalive === 'undefined'){
this.mqttkeepalive = 15; this.keepalive = 15;
} }
if (this.secureconn == true) { function updateTLSOptions() {
$("#row-disablecertauth").show(); if ($("#node-config-input-usetls").is(':checked')) {
} else { $("#node-config-input-verifyservercert").prop("disabled", false);
$("#row-disablecertauth").hide(); $("#node-config-input-verifyservercert").next().css("color","");
}
$("#node-config-input-secureconn").on("click",function() {
if($(this).is(':checked')) {
$("#row-disablecertauth").show();
} else { } else {
$("#row-disablecertauth").hide(); $("#node-config-input-verifyservercert").prop("disabled", true);
$("#node-config-input-verifyservercert").next().css("color","#aaa");
} }
}
updateTLSOptions();
$("#node-config-input-usetls").on("click",function() {
updateTLSOptions();
});
var node = this;
function updateClientId() {
if ($("#node-config-input-cleansession").is(":checked")) {
$("#node-config-input-clientid").attr("placeholder",node._("mqtt.placeholder.clientid"));
} else {
$("#node-config-input-clientid").attr("placeholder",node._("mqtt.placeholder.clientid-nonclean"));
}
$("#node-config-input-clientid").change();
}
setTimeout(updateClientId,0);
$("#node-config-input-cleansession").on("click",function() {
updateClientId();
}); });
} }
}); });

View File

@ -36,17 +36,17 @@ module.exports = function(RED) {
this.broker = n.broker; this.broker = n.broker;
this.port = n.port; this.port = n.port;
this.clientid = n.clientid; this.clientid = n.clientid;
this.secureconn = n.secureconn; this.usetls = n.usetls;
this.disablecertauth = n.disablecertauth; this.verifyservercert = n.verifyservercert;
this.compatmode = n.compatmode; this.compatmode = n.compatmode;
this.mqttkeepalive = n.mqttkeepalive; this.keepalive = n.keepalive;
this.cleansession = n.cleansession;
// Config node state // Config node state
this.brokerurl = ""; this.brokerurl = "";
this.connected = false; this.connected = false;
this.connecting = false; this.connecting = false;
this.usecount = 0; this.usecount = 0;
this.logdisconnect = true;
this.options = {}; this.options = {};
this.queue = []; this.queue = [];
this.subscriptions = {}; this.subscriptions = {};
@ -61,22 +61,27 @@ module.exports = function(RED) {
// If the config node is missing certain options (it was probably deployed prior to an update to the node code), // If the config node is missing certain options (it was probably deployed prior to an update to the node code),
// select/generate sensible options for the new fields // select/generate sensible options for the new fields
if (typeof this.secureconn === 'undefined'){ if (typeof this.usetls === 'undefined'){
this.secureconn = false; this.usetls = false;
} }
if (typeof this.compatmode === 'undefined'){ if (typeof this.compatmode === 'undefined'){
this.compatmode = true; this.compatmode = true;
} }
if (typeof this.disablecertauth === 'undefined'){ if (typeof this.verifyservercert === 'undefined'){
this.disablecertauth = false; this.verifyservercert = false;
} }
if (typeof this.mqttkeepalive === 'undefined'){ if (typeof this.keepalive === 'undefined'){
this.mqttkeepalive = 15; this.keepalive = 15;
} else if (typeof this.keepalive === 'string') {
this.keepalive = Number(this.keepalive);
}
if (typeof this.cleansession === 'undefined') {
this.cleansession = true;
} }
// Create the URL to pass in to the MQTT.js library // Create the URL to pass in to the MQTT.js library
if (this.brokerurl == "") { if (this.brokerurl == "") {
if (this.secureconn) { if (this.usetls) {
this.brokerurl="mqtts://"; this.brokerurl="mqtts://";
} else { } else {
this.brokerurl="mqtt://"; this.brokerurl="mqtt://";
@ -88,22 +93,32 @@ module.exports = function(RED) {
} }
} }
if (!this.cleansession && !this.clientid) {
this.cleansession = true;
this.warn(RED._("mqtt.errors.nonclean-missingclientid"));
}
// Build options for passing to the MQTT.js API // Build options for passing to the MQTT.js API
this.options.clientId = this.clientid || 'mqtt_' + (1+Math.random()*4294967295).toString(16); this.options.clientId = this.clientid || 'mqtt_' + (1+Math.random()*4294967295).toString(16);
this.options.username = this.username; this.options.username = this.username;
this.options.password = this.password; this.options.password = this.password;
this.options.keepalive = this.mqttkeepalive; this.options.keepalive = this.keepalive;
this.options.reconnectPeriod = 5000; this.options.clean = this.clean;
this.options.reconnectPeriod = RED.settings.mqttReconnectTime||5000;
if (this.compatmode == "true" || this.compatmode === true){ if (this.compatmode == "true" || this.compatmode === true){
this.log('Using compatibility mode for non-MQTT v3.1.1 brokers');
this.options.protocolId = 'MQIsdp'; this.options.protocolId = 'MQIsdp';
this.options.protocolVersion = 3; this.options.protocolVersion = 3;
} }
if (this.disablecertauth == "true" || this.disablecertauth === true) {
this.log(' Warning: Certificate checking is disabled for this connection'); this.options.rejectUnauthorized = (this.verifyservercert == "true" || this.verifyservercert === true)
this.options.rejectUnauthorized = false;
} else { if (n.willTopic) {
this.options.rejectUnauthorized = true; this.options.will = {
topic: n.willTopic,
payload: n.willPayload || "",
qos: Number(n.willQos||0),
retain: n.willRetain=="true"|| n.willRetain===true
};
} }
// Define functions called by MQTT in and out nodes // Define functions called by MQTT in and out nodes
@ -123,12 +138,10 @@ module.exports = function(RED) {
if (!node.connected && !node.connecting) { if (!node.connected && !node.connecting) {
node.connecting = true; node.connecting = true;
node.client = mqtt.connect(node.brokerurl ,node.options); node.client = mqtt.connect(node.brokerurl ,node.options);
// Register successful connect or reconnect handler // Register successful connect or reconnect handler
node.client.on('connect', function () { node.client.on('connect', function () {
node.connected = true; node.connected = true;
node.logdisconnect = true; node.log(RED._("mqtt.state.connected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
node.log("Connected to broker: "+(node.clientid?node.clientid+"@":"")+node.brokerurl);
node.emit('connected'); node.emit('connected');
// Remove any existing listeners before resubscribing to avoid duplicates in the event of a re-connection // Remove any existing listeners before resubscribing to avoid duplicates in the event of a re-connection
@ -156,26 +169,18 @@ module.exports = function(RED) {
// Register disconnect handlers // Register disconnect handlers
node.client.on('close', function () { node.client.on('close', function () {
if (node.connected && node.logdisconnect ) { if (node.connected) {
node.connected = false; node.connected = false;
node.logdisconnect = false; node.log(RED._("mqtt.state.disconnected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
node.log("Disconnected from broker: "+(node.clientid?node.clientid+"@":"")+node.brokerurl);
node.emit('disconnected');
}
});
node.client.on('offline', function () {
if (node.connected && node.logdisconnect ) {
node.connected = false;
node.logdisconnect = false;
node.log("Disconnected from broker: "+(node.clientid?node.clientid+"@":"")+node.brokerurl);
node.emit('disconnected'); node.emit('disconnected');
} else if (node.connecting) {
node.log(RED._("mqtt.state.connect-failed",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
} }
}); });
// Register connect error handler // Register connect error handler
node.client.on('error', function (error) { node.client.on('error', function (error) {
node.log("" + error); console.log("ERROR",error);
if (node.connecting) { if (node.connecting) {
node.client.end(); node.client.end();
node.connecting = false; node.connecting = false;

View File

@ -211,13 +211,30 @@
}, },
"mqtt": { "mqtt": {
"label": { "label": {
"broker": "Broker", "broker": "Server",
"qos": "QoS", "qos": "QoS",
"clientid": "Client ID", "clientid": "Client ID",
"port": "Port" "port": "Port",
"keepalive": "Keep alive time (s)",
"cleansession": "Use clean session",
"use-tls": "Enable secure (SSL/TLS) connection",
"verify-server-cert":"Verify server certificate",
"compatmode": "Use legacy MQTT 3.1 support"
},
"tabs-label": {
"connection": "Connection",
"security": "Security",
"will": "Will Message"
}, },
"placeholder": { "placeholder": {
"clientid": "Leave blank for auto generated" "clientid": "Leave blank for auto generated",
"clientid-nonclean":"Must be set for non-clean sessions",
"will-topic": "Leave blank to disable will message"
},
"state": {
"connected": "Connected to broker: __broker__",
"disconnected": "Disconnected from broker: __broker__",
"connect-failed": "Connection failed to broker: __broker__"
}, },
"retain": "Retain", "retain": "Retain",
"true": "true", "true": "true",
@ -226,7 +243,8 @@
"errors": { "errors": {
"not-defined": "topic not defined", "not-defined": "topic not defined",
"missing-config": "missing broker configuration", "missing-config": "missing broker configuration",
"invalid-topic": "Invalid topic specified" "invalid-topic": "Invalid topic specified",
"nonclean-missingclientid": "No client ID set, using clean session"
} }
}, },
"httpin": { "httpin": {