/** * Copyright JS Foundation and other contributors, http://js.foundation * * 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. **/ var util = require("util"); var mqtt = require("mqtt"); var events = require("events"); util.log("[warn] nodes/core/io/lib/mqtt.js is deprecated and will be removed in a future release of Node-RED. Please report this usage to the Node-RED mailing list."); //var inspect = require("util").inspect; //var Client = module.exports.Client = function( var port = 1883; var host = "localhost"; function MQTTClient(port,host) { this.port = port||1883; this.host = host||"localhost"; this.messageId = 1; this.pendingSubscriptions = {}; this.inboundMessages = {}; this.lastOutbound = (new Date()).getTime(); this.lastInbound = (new Date()).getTime(); this.connected = false; this._nextMessageId = function() { this.messageId += 1; if (this.messageId > 0xFFFF) { this.messageId = 1; } return this.messageId; } events.EventEmitter.call(this); } util.inherits(MQTTClient, events.EventEmitter); MQTTClient.prototype.connect = function(options) { if (!this.connected) { var self = this; options = options||{}; self.options = options; self.options.keepalive = options.keepalive||15; self.options.clean = self.options.clean||true; self.options.protocolId = 'MQIsdp'; self.options.protocolVersion = 3; self.client = mqtt.createConnection(this.port,this.host,function(err,client) { if (err) { self.connected = false; clearInterval(self.watchdog); self.connectionError = true; //util.log('[mqtt] ['+self.uid+'] connection error 1 : '+inspect(err)); self.emit('connectionlost',err); return; } client.on('close',function(e) { //util.log('[mqtt] ['+self.uid+'] on close'); clearInterval(self.watchdog); if (!self.connectionError) { if (self.connected) { self.connected = false; self.emit('connectionlost',e); } else { self.emit('disconnect'); } } }); client.on('error',function(e) { //util.log('[mqtt] ['+self.uid+'] on error : '+inspect(e)); clearInterval(self.watchdog); if (self.connected) { self.connected = false; self.emit('connectionlost',e); } }); client.on('connack',function(packet) { if (packet.returnCode === 0) { self.watchdog = setInterval(function(self) { var now = (new Date()).getTime(); //util.log('[mqtt] ['+self.uid+'] watchdog '+inspect({connected:self.connected,connectionError:self.connectionError,pingOutstanding:self.pingOutstanding,now:now,lastOutbound:self.lastOutbound,lastInbound:self.lastInbound})); if (now - self.lastOutbound > self.options.keepalive*500 || now - self.lastInbound > self.options.keepalive*500) { if (self.pingOutstanding) { //util.log('[mqtt] ['+self.uid+'] watchdog pingOustanding - disconnect'); try { self.client.disconnect(); } catch (err) { } } else { //util.log('[mqtt] ['+self.uid+'] watchdog pinging'); self.lastOutbound = (new Date()).getTime(); self.lastInbound = (new Date()).getTime(); self.pingOutstanding = true; self.client.pingreq(); } } },self.options.keepalive*500,self); self.pingOutstanding = false; self.lastInbound = (new Date()).getTime() self.lastOutbound = (new Date()).getTime() self.connected = true; self.connectionError = false; self.emit('connect'); } else { self.connected = false; self.emit('connectionlost'); } }); client.on('suback',function(packet) { self.lastInbound = (new Date()).getTime() var topic = self.pendingSubscriptions[packet.messageId]; self.emit('subscribe',topic,packet.granted[0]); delete self.pendingSubscriptions[packet.messageId]; }); client.on('unsuback',function(packet) { self.lastInbound = (new Date()).getTime() var topic = self.pendingSubscriptions[packet.messageId]; self.emit('unsubscribe',topic); delete self.pendingSubscriptions[packet.messageId]; }); client.on('publish',function(packet) { self.lastInbound = (new Date()).getTime(); if (packet.qos < 2) { var p = packet; self.emit('message',p.topic,p.payload,p.qos,p.retain); } else { self.inboundMessages[packet.messageId] = packet; this.lastOutbound = (new Date()).getTime() self.client.pubrec(packet); } if (packet.qos == 1) { this.lastOutbound = (new Date()).getTime() self.client.puback(packet); } }); client.on('pubrel',function(packet) { self.lastInbound = (new Date()).getTime() var p = self.inboundMessages[packet.messageId]; if (p) { self.emit('message',p.topic,p.payload,p.qos,p.retain); delete self.inboundMessages[packet.messageId]; } self.lastOutbound = (new Date()).getTime() self.client.pubcomp(packet); }); client.on('puback',function(packet) { self.lastInbound = (new Date()).getTime() // outbound qos-1 complete }); client.on('pubrec',function(packet) { self.lastInbound = (new Date()).getTime() self.lastOutbound = (new Date()).getTime() self.client.pubrel(packet); }); client.on('pubcomp',function(packet) { self.lastInbound = (new Date()).getTime() // outbound qos-2 complete }); client.on('pingresp',function(packet) { //util.log('[mqtt] ['+self.uid+'] received pingresp'); self.lastInbound = (new Date()).getTime() self.pingOutstanding = false; }); this.lastOutbound = (new Date()).getTime() this.connectionError = false; client.connect(self.options); }); } } MQTTClient.prototype.subscribe = function(topic,qos) { var self = this; if (self.connected) { var options = { subscriptions:[{topic:topic,qos:qos}], messageId: self._nextMessageId() }; this.pendingSubscriptions[options.messageId] = topic; this.lastOutbound = (new Date()).getTime(); self.client.subscribe(options); self.client.setPacketEncoding('binary'); } } MQTTClient.prototype.unsubscribe = function(topic) { var self = this; if (self.connected) { var options = { unsubscriptions:[topic], messageId: self._nextMessageId() }; this.pendingSubscriptions[options.messageId] = topic; this.lastOutbound = (new Date()).getTime() self.client.unsubscribe(options); } } MQTTClient.prototype.publish = function(topic,payload,qos,retain) { var self = this; if (self.connected) { if (!Buffer.isBuffer(payload)) { if (typeof payload === "object") { payload = JSON.stringify(payload); } else if (typeof payload !== "string") { payload = ""+payload; } } var options = { topic: topic, payload: payload, qos: qos||0, retain:retain||false }; if (options.qos !== 0) { options.messageId = self._nextMessageId(); } this.lastOutbound = (new Date()).getTime() self.client.publish(options); } } MQTTClient.prototype.disconnect = function() { var self = this; if (this.connected) { this.connected = false; try { this.client.disconnect(); } catch(err) { } } } MQTTClient.prototype.isConnected = function() { return this.connected; } module.exports.createClient = function(port,host) { var mqtt_client = new MQTTClient(port,host); return mqtt_client; }