node-red-contrib-zigbee2mqtt-devices
Version:
Nodes to interact with zigbee2mqtt for Node-RED
162 lines (161 loc) • 6.63 kB
JavaScript
;
const types_1 = require("./types");
const mqtt_1 = require("./lib/mqtt");
const nodeInit = (RED) => {
const utils = require("./lib/utils.js");
const bavaria = utils.bavaria();
function DeviceConfigNodeConstructor(config) {
RED.nodes.createNode(this, config);
this.name = config.name;
this.deviceName = config.deviceName;
this.brightnessSupport = config.brightnessSupport;
this.temperatureSupport = config.temperatureSupport;
this.colorSupport = config.colorSupport;
this.bridge = config.bridge;
this.genericMqttDevice = config.genericMqttDevice;
this.statusTopic = config.statusTopic;
this.commandTopic = config.commandTopic;
this.refreshTopic = config.refreshTopic;
}
RED.nodes.registerType("zigbee2mqtt-device-config", DeviceConfigNodeConstructor);
function BridgeConfigConstructor(config) {
RED.nodes.createNode(this, config);
const EventEmitter = require("events");
const emitter = new EventEmitter();
const subscriptions = {};
const node = this;
const broker = RED.nodes.getNode(config.broker);
const globalContext = node.context().global;
broker.register(this);
broker.subscribe(`${config.baseTopic}/#`, 0, (topic, payload, packet) => {
for (let key in subscriptions) {
subscriptions[key].forEach(sub => {
sub.invokeIfMatch(topic, payload.toString("utf8"));
});
}
}, this.id);
this.name = config.name;
this.baseTopic = config.baseTopic;
this.isConnected = () => broker.connected;
this.publish = (topic, payload) => {
let msg = {
qos: 0,
retain: false,
topic: topic,
payload: payload
};
broker.publish(msg);
};
this.knownDevices = globalContext.get(`knownDevices_${node.id.replace(".", "_")}`) || [];
this.getDeviceList = function (callback) {
if (this.knownDevices.length === 0 && callback !== undefined) {
callback();
}
return this.knownDevices;
};
this.subscribeDevice = function (nodeId, device, callback) {
this.subscribe(nodeId, `${node.baseTopic}/${device}`, callback, true);
};
this.publishDevice = function (device, msg) {
if (typeof msg !== "string") {
msg = JSON.stringify(msg);
}
this.publish(`${node.baseTopic}/${device}/set`, msg);
};
this.subscribe = (nodeId, topic, callback, jsonPayload = true) => {
if (!topic.startsWith(node.baseTopic)) {
node.error("Can't subscribe to " + topic);
return;
}
if (!(nodeId in subscriptions)) {
subscriptions[nodeId] = [];
}
subscriptions[nodeId].push(new mqtt_1.MqttSubscription(topic, jsonPayload, callback));
};
this.unsubscribe = (nodeId) => {
};
this.setDeviceState = (device, payload) => {
if (device !== undefined && device !== "") {
try {
payload = JSON.stringify(payload);
this.publish(`${node.baseTopic}/${device}/set`, payload);
}
catch (e) {
console.error(e);
}
}
};
this.refreshDevice = function (deviceName, force) {
if (deviceName !== "" && deviceName !== "---" && (config.allowDeviceStatusRefresh || force)) {
this.publish(`${node.baseTopic}/${deviceName}/get`, `{"state": ""}`);
}
};
let registeredOtaNodeId = "";
let otaCallback = () => { };
let otaDeviceCallback = () => { };
this.registerOtaNode = (nodeId, otaStatusCallback, deviceStatusCallback) => {
if (registeredOtaNodeId !== "" && registeredOtaNodeId !== nodeId) {
return false;
}
registeredOtaNodeId = nodeId;
otaCallback = otaStatusCallback;
otaDeviceCallback = deviceStatusCallback;
return true;
};
if (config.enabledLogging === true) {
this.subscribe(node.id, `${config.baseTopic}/bridge/logging`, (message) => {
node.error(message);
if (message.level.startsWith("warn")) {
node.warn(message.message);
}
else if (message.level.startsWith("err")) {
node.error(message.message);
}
});
}
this.subscribe(node.id, `${config.baseTopic}/bridge/state`, (message) => {
function getState(message) {
try {
// In newer versions the state topic has an object with a state property
const messageObj = JSON.parse(message);
return messageObj.state;
}
catch (e) {
// Backwards compability: in previous versions the state topic was just a string "online"
return message;
}
}
const state = getState(message);
if (state === "online") {
bavaria.observer.notify(node.id + "_connected");
}
}, false);
this.subscribe(node.id, `${config.baseTopic}/bridge/devices`, (message) => {
handleDeviceMessage(message);
});
function handleDeviceMessage(msg) {
msg.forEach((device) => {
const d = node.knownDevices.find(e => {
return e.ieee_address === device.ieee_address;
});
if (d) {
// replace already known device
const index = node.knownDevices.indexOf(d);
node.knownDevices.splice(index, 1, device);
}
else {
// new device
node.knownDevices.push(device);
}
});
globalContext.set(`knownDevices_${node.id.replace(".", "_")}`, node.knownDevices);
}
node.on("close", function (done) {
broker.deregister(node, done);
});
}
RED.nodes.registerType("zigbee2mqtt-bridge-config", BridgeConfigConstructor, {
credentials: types_1.BridgeConfigCredentials,
});
};
module.exports = nodeInit;