2018-04-18 09:25:40 -07:00
|
|
|
const mqtt = require('mqtt');
|
|
|
|
const logger = require('./util/logger');
|
|
|
|
const settings = require('./util/settings');
|
2019-07-10 08:57:41 -07:00
|
|
|
const fs = require('fs');
|
2019-09-09 10:48:09 -07:00
|
|
|
const events = require('events');
|
2018-04-18 09:25:40 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
class MQTT extends events.EventEmitter {
|
2018-04-18 09:25:40 -07:00
|
|
|
constructor() {
|
2019-09-09 10:48:09 -07:00
|
|
|
super();
|
2018-11-16 12:23:11 -07:00
|
|
|
this.onMessage = this.onMessage.bind(this);
|
2020-11-16 09:03:22 -07:00
|
|
|
this.publishedTopics = new Set();
|
2018-04-18 09:25:40 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async connect() {
|
2018-04-18 09:25:40 -07:00
|
|
|
const mqttSettings = settings.get().mqtt;
|
|
|
|
logger.info(`Connecting to MQTT server at ${mqttSettings.server}`);
|
|
|
|
|
2018-08-09 09:23:04 -07:00
|
|
|
const options = {
|
|
|
|
will: {
|
2018-11-16 12:23:11 -07:00
|
|
|
topic: `${settings.get().mqtt.base_topic}/bridge/state`,
|
2018-08-09 09:23:04 -07:00
|
|
|
payload: 'offline',
|
2020-11-16 09:27:49 -07:00
|
|
|
retain: settings.get().mqtt.force_disable_retain ? false : true,
|
2018-08-09 09:23:04 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2020-03-12 12:25:37 -07:00
|
|
|
if (mqttSettings.version) {
|
|
|
|
options.protocolVersion = mqttSettings.version;
|
|
|
|
}
|
|
|
|
|
2020-01-17 13:38:46 -07:00
|
|
|
if (mqttSettings.keepalive) {
|
|
|
|
logger.debug(`Using MQTT keepalive: ${mqttSettings.keepalive}`);
|
|
|
|
options.keepalive = mqttSettings.keepalive;
|
|
|
|
}
|
|
|
|
|
2019-07-10 08:57:41 -07:00
|
|
|
if (mqttSettings.ca) {
|
|
|
|
logger.debug(`MQTT SSL/TLS: Path to CA certificate = ${mqttSettings.ca}`);
|
2019-09-09 10:48:09 -07:00
|
|
|
options.ca = fs.readFileSync(mqttSettings.ca);
|
2019-07-10 08:57:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mqttSettings.key && mqttSettings.cert) {
|
|
|
|
logger.debug(`MQTT SSL/TLS: Path to client key = ${mqttSettings.key}`);
|
|
|
|
logger.debug(`MQTT SSL/TLS: Path to client certificate = ${mqttSettings.cert}`);
|
2019-09-09 10:48:09 -07:00
|
|
|
options.key = fs.readFileSync(mqttSettings.key);
|
|
|
|
options.cert = fs.readFileSync(mqttSettings.cert);
|
2019-07-10 08:57:41 -07:00
|
|
|
}
|
|
|
|
|
2018-04-18 09:25:40 -07:00
|
|
|
if (mqttSettings.user && mqttSettings.password) {
|
2020-11-02 10:00:02 -07:00
|
|
|
logger.debug(`Using MQTT login with username: ${mqttSettings.user}`);
|
2018-04-18 09:25:40 -07:00
|
|
|
options.username = mqttSettings.user;
|
|
|
|
options.password = mqttSettings.password;
|
2020-11-02 10:00:02 -07:00
|
|
|
} else {
|
|
|
|
logger.debug(`Using MQTT anonymous login`);
|
2018-04-18 09:25:40 -07:00
|
|
|
}
|
|
|
|
|
2018-06-26 10:33:26 -07:00
|
|
|
if (mqttSettings.client_id) {
|
|
|
|
logger.debug(`Using MQTT client ID: '${mqttSettings.client_id}'`);
|
|
|
|
options.clientId = mqttSettings.client_id;
|
|
|
|
}
|
|
|
|
|
2018-08-17 09:39:45 -07:00
|
|
|
if (mqttSettings.hasOwnProperty('reject_unauthorized') && !mqttSettings.reject_unauthorized) {
|
|
|
|
logger.debug(`MQTT reject_unauthorized set false, ignoring certificate warnings.`);
|
2018-08-17 09:22:27 -07:00
|
|
|
options.rejectUnauthorized = false;
|
2018-08-17 09:15:44 -07:00
|
|
|
}
|
2018-06-26 10:33:26 -07:00
|
|
|
|
2018-04-18 09:25:40 -07:00
|
|
|
// Set timer at interval to check if connected to MQTT server.
|
|
|
|
const interval = 10 * 1000; // seconds * 1000.
|
|
|
|
this.connectionTimer = setInterval(() => {
|
|
|
|
if (this.client.reconnecting) {
|
|
|
|
logger.error('Not connected to MQTT server!');
|
|
|
|
}
|
|
|
|
}, interval);
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
return new Promise((resolve) => {
|
|
|
|
this.client = mqtt.connect(mqttSettings.server, options);
|
2018-04-18 09:25:40 -07:00
|
|
|
|
2020-11-15 05:22:29 -07:00
|
|
|
const self = this;
|
2019-09-09 10:48:09 -07:00
|
|
|
this.client.on('connect', () => {
|
|
|
|
logger.info('Connected to MQTT server');
|
2020-11-16 09:03:22 -07:00
|
|
|
self.subscribe(`${settings.get().mqtt.base_topic}/#`);
|
2020-11-15 05:22:29 -07:00
|
|
|
self.publish('bridge/state', 'online', {retain: true, qos: 0});
|
2019-09-09 10:48:09 -07:00
|
|
|
resolve();
|
|
|
|
});
|
2018-04-18 09:25:40 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
this.client.on('message', this.onMessage);
|
2018-04-18 11:53:22 -07:00
|
|
|
});
|
2018-04-18 09:25:40 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async disconnect() {
|
|
|
|
clearTimeout(this.connectionTimer);
|
|
|
|
this.connectionTimer = null;
|
|
|
|
await this.publish('bridge/state', 'offline', {retain: true, qos: 0});
|
|
|
|
logger.info('Disconnecting from MQTT server');
|
|
|
|
this.client.end();
|
2018-08-28 12:55:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
subscribe(topic) {
|
|
|
|
this.client.subscribe(topic);
|
2018-04-18 09:25:40 -07:00
|
|
|
}
|
|
|
|
|
2018-11-16 12:23:11 -07:00
|
|
|
onMessage(topic, message) {
|
2020-11-16 09:03:22 -07:00
|
|
|
// Since we subscribe to zigbee2mqtt/# we also receive the message we send ourselves, skip these.
|
|
|
|
if (!this.publishedTopics.has(topic)) {
|
|
|
|
this.emit('message', {topic, message: message + ''});
|
|
|
|
}
|
2018-04-18 09:25:40 -07:00
|
|
|
}
|
|
|
|
|
2020-04-30 10:06:17 -07:00
|
|
|
isConnected() {
|
|
|
|
return this.client && !this.client.reconnecting;
|
|
|
|
}
|
|
|
|
|
2020-11-16 09:03:22 -07:00
|
|
|
async publish(topic, payload, options, base=settings.get().mqtt.base_topic, skipLog=false, skipReceive=true) {
|
2018-11-16 12:23:11 -07:00
|
|
|
topic = `${base}/${topic}`;
|
2019-09-09 10:48:09 -07:00
|
|
|
options = {qos: 0, retain: false, ...options};
|
2018-04-18 09:25:40 -07:00
|
|
|
|
2020-11-16 09:03:22 -07:00
|
|
|
if (skipReceive) {
|
|
|
|
this.publishedTopics.add(topic);
|
|
|
|
}
|
|
|
|
|
2020-04-30 10:06:17 -07:00
|
|
|
if (!this.isConnected()) {
|
2020-07-28 13:12:22 -07:00
|
|
|
if (!skipLog) {
|
|
|
|
logger.error(`Not connected to MQTT server!`);
|
|
|
|
logger.error(`Cannot send message: topic: '${topic}', payload: '${payload}`);
|
|
|
|
}
|
2018-04-18 09:25:40 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-28 13:12:22 -07:00
|
|
|
if (!skipLog) {
|
|
|
|
logger.info(`MQTT publish: topic '${topic}', payload '${payload}'`);
|
|
|
|
}
|
2019-09-09 10:48:09 -07:00
|
|
|
|
2020-11-16 09:27:49 -07:00
|
|
|
const actualOptions = {...options};
|
|
|
|
if (settings.get().mqtt.force_disable_retain) {
|
|
|
|
actualOptions.retain = false;
|
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
return new Promise((resolve) => {
|
2020-11-16 09:27:49 -07:00
|
|
|
this.client.publish(topic, payload, actualOptions, () => {
|
2020-09-04 09:42:24 -07:00
|
|
|
this.emit('publishedMessage', {topic, payload, options});
|
|
|
|
resolve();
|
|
|
|
});
|
2019-09-09 10:48:09 -07:00
|
|
|
});
|
2018-04-18 09:25:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = MQTT;
|