hassio-plejd/plejd/MqttClient.js

190 lines
5.2 KiB
JavaScript
Raw Normal View History

2019-12-04 11:17:06 +01:00
const EventEmitter = require('events');
const mqtt = require('mqtt');
const Logger = require('./Logger');
2019-12-04 11:17:06 +01:00
const startTopic = 'hass/status';
const logger = Logger.getLogger('plejd-mqtt');
2019-12-04 11:17:06 +01:00
// #region discovery
const discoveryPrefix = 'homeassistant';
const nodeId = 'plejd';
const getSubscribePath = () => `${discoveryPrefix}/+/${nodeId}/#`;
const getPath = ({ id, type }) => `${discoveryPrefix}/${type}/${nodeId}/${id}`;
const getConfigPath = (plug) => `${getPath(plug)}/config`;
const getStateTopic = (plug) => `${getPath(plug)}/state`;
const getCommandTopic = (plug) => `${getPath(plug)}/set`;
const getSceneEventTopic = () => 'plejd/event/scene';
const getDiscoveryPayload = (device) => ({
2019-12-21 15:01:15 +00:00
schema: 'json',
name: device.name,
2020-01-27 20:43:52 +00:00
unique_id: `light.plejd.${device.name.toLowerCase().replace(/ /g, '')}`,
state_topic: getStateTopic(device),
command_topic: getCommandTopic(device),
2019-12-21 15:01:15 +00:00
optimistic: false,
brightness: `${device.dimmable}`,
device: {
identifiers: `${device.serialNumber}_${device.id}`,
manufacturer: 'Plejd',
model: device.typeName,
name: device.name,
sw_version: device.version,
},
});
const getSwitchPayload = (device) => ({
2020-02-20 13:02:47 +01:00
name: device.name,
state_topic: getStateTopic(device),
command_topic: getCommandTopic(device),
optimistic: false,
device: {
identifiers: `${device.serialNumber}_${device.id}`,
2020-02-20 13:02:47 +01:00
manufacturer: 'Plejd',
model: device.typeName,
name: device.name,
sw_version: device.version,
},
2020-02-20 13:02:47 +01:00
});
2019-12-04 11:17:06 +01:00
// #endregion
class MqttClient extends EventEmitter {
constructor(mqttBroker, username, password) {
super();
this.mqttBroker = mqttBroker;
this.username = username;
this.password = password;
this.deviceMap = {};
this.devices = [];
}
init() {
logger.info('Initializing MQTT connection for Plejd addon');
2019-12-04 11:17:06 +01:00
const self = this;
this.client = mqtt.connect(this.mqttBroker, {
username: this.username,
password: this.password,
2019-12-04 11:17:06 +01:00
});
this.client.on('connect', () => {
logger.info('Connected to MQTT.');
2019-12-04 11:17:06 +01:00
this.client.subscribe(startTopic, (err) => {
if (err) {
logger.error(`Unable to subscribe to ${startTopic}`);
2019-12-04 11:17:06 +01:00
}
self.emit('connected');
});
this.client.subscribe(getSubscribePath(), (err) => {
if (err) {
logger.error('Unable to subscribe to control topics');
2019-12-04 11:17:06 +01:00
}
});
});
this.client.on('close', () => {
logger.verbose('Warning: mqtt channel closed event, reconnecting...');
2019-12-04 11:17:06 +01:00
self.reconnect();
});
this.client.on('message', (topic, message) => {
// const command = message.toString();
const command = message.toString().substring(0, 1) === '{'
? JSON.parse(message.toString())
: message.toString();
2019-12-04 11:17:06 +01:00
if (topic === startTopic) {
logger.info('Home Assistant has started. lets do discovery.');
2019-12-04 11:17:06 +01:00
self.emit('connected');
} else if (topic.includes('set')) {
logger.verbose(`Got mqtt command on ${topic} - ${message}`);
const device = self.devices.find((x) => getCommandTopic(x) === topic);
if (device) {
self.emit('stateChanged', device, command);
} else {
logger.warn(
`Device for topic ${topic} not found! Can happen if HA calls previously existing devices.`,
);
}
} else if (topic.includes('state')) {
logger.verbose(`State update sent over mqtt to HA ${topic} - ${message}`);
} else {
logger.verbose(`Warning: Got unrecognized mqtt command on ${topic} - ${message}`);
}
2019-12-04 11:17:06 +01:00
});
}
reconnect() {
this.client.reconnect();
}
discover(devices) {
this.devices = devices;
const self = this;
logger.debug(`Sending discovery of ${devices.length} device(s).`);
2019-12-04 11:17:06 +01:00
devices.forEach((device) => {
logger.debug(`Sending discovery for ${device.name}`);
2019-12-04 11:17:06 +01:00
const payload = device.type === 'switch' ? getSwitchPayload(device) : getDiscoveryPayload(device);
logger.info(
`Discovered ${device.type} (${device.typeName}) named ${device.name} with PID ${device.id}.`,
);
2019-12-13 14:13:00 +01:00
2019-12-04 11:17:06 +01:00
self.deviceMap[device.id] = payload.unique_id;
self.client.publish(getConfigPath(device), JSON.stringify(payload));
2019-12-04 11:17:06 +01:00
});
}
2019-12-21 15:01:15 +00:00
updateState(deviceId, data) {
const device = this.devices.find((x) => x.id === deviceId);
2019-12-04 11:17:06 +01:00
if (!device) {
logger.warn(`Unknown device id ${deviceId} - not handled by us.`);
2019-12-04 11:17:06 +01:00
return;
}
logger.verbose(
`Updating state for ${device.name}: ${data.state}${
data.brightness ? `, dim: ${data.brightness}` : ''
}`,
);
2019-12-21 15:01:15 +00:00
let payload = null;
2019-12-04 11:17:06 +01:00
if (device.type === 'switch') {
payload = data.state === 1 ? 'ON' : 'OFF';
} else {
if (device.dimmable) {
payload = {
state: data.state === 1 ? 'ON' : 'OFF',
brightness: data.brightness,
};
} else {
payload = {
state: data.state === 1 ? 'ON' : 'OFF',
};
}
payload = JSON.stringify(payload);
2019-12-04 11:17:06 +01:00
}
this.client.publish(getStateTopic(device), payload);
2019-12-04 11:17:06 +01:00
}
2019-12-22 17:48:16 +00:00
sceneTriggered(scene) {
logger.verbose(`Scene triggered: ${scene}`);
this.client.publish(getSceneEventTopic(), JSON.stringify({ scene }));
2019-12-22 17:48:16 +00:00
}
2019-12-04 11:17:06 +01:00
}
module.exports = MqttClient;