node-red-contrib-zigbee2mqtt-devices
Version:
Nodes to interact with zigbee2mqtt for Node-RED
247 lines (241 loc) • 9.89 kB
JavaScript
"use strict";
module.exports = function (RED) {
const utils = require("./lib/utils.js");
const bavaria = utils.bavaria();
function otaUpdate(config) {
RED.nodes.createNode(this, config);
var node = this;
var bridgeNode = RED.nodes.getNode(config.bridge);
var nodeContext = this.context();
var isUpdating = nodeContext.get("isUpdating") || false;
var currentDevice = nodeContext.get("currentDevice") || "";
var currentDeviceState = nodeContext.get("currentDeviceState") || {};
var overrideAutoUpdate = false;
var updateableDevices = [];
if (!bridgeNode.registerOtaNode(node.id, otaStatusUpdateReceived, deviceStatusReceived)) {
node.status({ fill: "red", text: "duplicate ota update" });
node.error("Duplicate ota update node. Only one ota update node per bridge allowed!");
return;
}
function otaStatusUpdateReceived(msg) {
switch (msg.status) {
case "available":
var found = bridgeNode.getDeviceList().find(d => d.friendly_name == msg.device && d.type === "Router");
if (!found) {
logVerbose(`'${msg.device}' can not be updated. Auto update is only available for routers. Please update it manually`);
return;
}
addAvailableDevice(msg.device);
if (isAutoUpdateEnabled() === true && isUpdating === false) {
startNext();
}
else {
node.send({
payload: {
device: msg.device,
message: "Update available",
}
});
}
break;
case "update_failed":
node.send({
payload: {
device: msg.device,
message: "Update failed",
}
});
cleanup();
setUpdateFlag(false);
node.error(msg);
node.status({ fill: "red", text: "error - updated failed" });
break;
case "update_progress":
setUpdateFlag(true);
setCurrentDevice(msg.device);
node.status({ fill: "yellow", text: `Updating... ${msg.progress}%` });
node.send([undefined, {
payload: {
device: msg.device,
message: "Progress changed",
progress: msg.progress
}
}]);
break;
case "update_succeeded":
setUpdateFlag(false);
var index = updateableDevices.indexOf(msg.device);
if (index > -1) {
updateableDevices.splice(index, 1);
}
nodeContext.set("updates_available", updateableDevices);
cleanup();
if (isAutoUpdateEnabled() === true && updateableDevices.length > 0) {
node.status({ fill: "grey", text: "Next update will start in 5 seconds..." });
setTimeout(function () {
startNext();
}, 5000);
}
else {
refreshStatus();
}
node.send({
payload: {
device: msg.device,
message: "Update succeeded",
devicesQueued: updateableDevices.length > 0
}
});
break;
}
}
function cleanup() {
bridgeNode.unsubscribe(node.id);
bridgeNode.setDeviceState(currentDevice, currentDeviceState);
setCurrentDevice("");
setCurrentDeviceState(null);
}
function deviceStatusReceived(deviceName, msg) {
if (deviceName !== currentDevice) {
if (msg.update_available === true) {
addAvailableDevice(deviceName);
if (isAutoUpdateEnabled() === true) {
startNext();
}
}
return;
}
if (currentDevice !== "") {
setCurrentDeviceState(msg);
}
}
function setCurrentDevice(device) {
if (currentDevice !== device && device !== null) {
bridgeNode.subscribeDevice(node.id, currentDevice, (msg) => {
deviceStatusReceived(currentDevice, msg);
});
bridgeNode.refreshDevice(device);
}
currentDevice = device;
nodeContext.set("currentDevice", currentDevice);
}
function setCurrentDeviceState(state) {
currentDeviceState = state;
nodeContext.set("currentDeviceState", currentDeviceState);
}
function setUpdateFlag(state) {
isUpdating = state;
nodeContext.set("isUpdating", isUpdating);
}
function addAvailableDevice(device) {
if (updateableDevices.indexOf(device) === -1
&& config.blacklist.indexOf(device) === -1) {
updateableDevices.push(device);
nodeContext.set("updates_available", updateableDevices);
node.send([null, null, {
payload: {
message: "Queue changed",
queue: updateableDevices,
autoUpdate: isAutoUpdateEnabled()
}
}]);
if (isUpdating !== true) {
refreshStatus();
}
}
}
function startNext() {
if (updateableDevices.length > 0) {
startUpdate(updateableDevices[0]);
}
}
function startUpdate(device) {
if (!device) {
node.error("msg.payload.device was not set");
return;
}
var found = bridgeNode.getDeviceList().find(d => d.friendly_name == device);
if (!found) {
node.error("Device not found");
return;
}
if (isUpdating === true) {
addAvailableDevice(device);
return;
}
setUpdateFlag(true);
setCurrentDevice(device);
node.status({ fill: "yellow", text: "Updating..." });
try {
bridgeNode.publish(bridgeNode.baseTopic + "/bridge/ota_update/update", device);
node.send({
payload: {
device: device,
message: "Update started"
}
});
}
catch (err) {
node.error(err);
}
}
function refreshStatus() {
if (isUpdating === true) {
node.status({ fill: "yellow", text: "Updating..." });
}
else if (updateableDevices.length > 0) {
node.status({ fill: "blue", text: updateableDevices.length + " updates available" });
}
else {
node.status({ fill: "green", text: "ready" });
}
}
function isAutoUpdateEnabled() {
return config.autoUpdate === true || overrideAutoUpdate === true;
}
node.on("input", function (msg) {
if (msg.payload.autoUpdate !== undefined) {
overrideAutoUpdate = msg.payload.autoUpdate;
if (overrideAutoUpdate) {
startNext();
}
return;
}
startUpdate(msg.payload.device);
});
node.on("close", function () {
bridgeNode.unsubscribe(node.id);
});
function logVerbose(msg) {
if (config.verboseLogging === true) {
node.warn(msg);
}
}
refreshStatus();
node.send([null, {
payload: {
device: currentDevice,
message: "Progress changed",
progress: 0
}
}, {
payload: {
message: "Queue changed",
queue: updateableDevices,
autoUpdate: isAutoUpdateEnabled()
}
}
]);
}
RED.nodes.registerType("ota-update", otaUpdate);
/*
AVAILABLE:
{"message":"Update available for 'Ikea Gang Mitte'","meta":{"device":"Ikea Gang Mitte","status":"available"},"type":"ota_update"}
PROGRESS:
{"message":"Update of 'Ikea Gang Mitte' at 100.00%","meta":{"device":"Ikea Gang Mitte","progress":100,"status":"update_progress"},"type":"ota_update"}
FAILED:
{"message":"Update of 'Ikea Gang Mitte' failed (Request failed with status code 434)","meta":{"device":"Ikea Gang Mitte","status":"update_failed"},"type":"ota_update"}
FINISHED:
{"message":"Ikea Gang Mitte","meta":{"device":"Ikea Gang Mitte","from":{"date_code":"20190407","software_build_id":"2.0.023"},"status":"update_succeeded","to":{"date_code":"20190407","software_build_id":"2.0.023"}},"type":"ota_update"}
*/
};