zigbee2mqtt
Version:
Zigbee to MQTT bridge using Zigbee-herdsman
424 lines • 44.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_assert_1 = __importDefault(require("node:assert"));
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const bind_decorator_1 = __importDefault(require("bind-decorator"));
const json_stable_stringify_without_jsonify_1 = __importDefault(require("json-stable-stringify-without-jsonify"));
const zigbee_herdsman_1 = require("zigbee-herdsman");
const device_1 = __importDefault(require("../model/device"));
const data_1 = __importDefault(require("../util/data"));
const logger_1 = __importDefault(require("../util/logger"));
const settings = __importStar(require("../util/settings"));
const utils_1 = __importDefault(require("../util/utils"));
const extension_1 = __importDefault(require("./extension"));
/**
* Write to `dataDir` and return created path
*/
function writeFirmwareHexToDataDir(hex, fileName, deviceIeee) {
if (!fileName) {
fileName = `${deviceIeee}_${Date.now()}`;
}
const baseDir = data_1.default.joinPath("ota");
if (!(0, node_fs_1.existsSync)(baseDir)) {
(0, node_fs_1.mkdirSync)(baseDir, { recursive: true });
}
const filePath = (0, node_path_1.join)(baseDir, fileName);
(0, node_fs_1.writeFileSync)(filePath, Buffer.from(hex, "hex"));
return filePath;
}
class OTAUpdate extends extension_1.default {
#topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/device/ota_update/(update|check|schedule|unschedule)/?(downgrade)?`, "i");
#inProgress = new Set();
#lastChecked = new Map();
// biome-ignore lint/suspicious/useAwait: API
async start() {
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
this.eventBus.onDeviceMessage(this, this.onZigbeeEvent);
(0, zigbee_herdsman_1.setOtaConfiguration)(data_1.default.getPath(), settings.get().ota.zigbee_ota_override_index_location);
// In case Zigbee2MQTT is restared during an update, progress and remaining values are still in state, remove them.
for (const device of this.zigbee.devicesIterator(utils_1.default.deviceNotCoordinator)) {
this.#removeProgressAndRemainingFromState(device);
// Reset update state, e.g. when Z2M restarted during update.
if (this.state.get(device).update?.state === "updating") {
this.state.get(device).update.state = "idle";
}
}
}
// mostly intended for testing
clearState() {
this.#inProgress.clear();
this.#lastChecked.clear();
}
#removeProgressAndRemainingFromState(device) {
const deviceState = this.state.get(device);
if (deviceState.update) {
delete deviceState.update.progress;
delete deviceState.update.remaining;
}
}
async onZigbeeEvent(data) {
if (data.type !== "commandQueryNextImageRequest" || !data.device.definition || this.#inProgress.has(data.device.ieeeAddr)) {
return;
}
// `commandQueryNextImageRequest` check above should ensures this is valid but...
(0, node_assert_1.default)(data.meta.zclTransactionSequenceNumber !== undefined, "Missing 'queryNextImageRequest' transaction sequence number (cannot match reply)");
logger_1.default.debug(`Device '${data.device.name}' requested OTA`);
if (data.device.zh.scheduledOta) {
// allow custom source to override check for definition `ota`
if (data.device.zh.scheduledOta?.url !== undefined || data.device.definition.ota) {
this.#inProgress.add(data.device.ieeeAddr);
logger_1.default.info(`Updating '${data.device.name}' to latest firmware`);
try {
const otaSettings = settings.get().ota;
const [, toVersion] = await this.#update(undefined, // uses internally registered schedule
data.device, data.data, data.meta.zclTransactionSequenceNumber, {
// fallbacks are only to satisfy typing, should always be defined from settings defaults
requestTimeout: otaSettings.image_block_request_timeout ?? /* v8 ignore next */ 150000,
responseDelay: otaSettings.image_block_response_delay ?? /* v8 ignore next */ 250,
baseSize: otaSettings.default_maximum_data_size ?? /* v8 ignore next */ 50,
}, data.endpoint);
if (toVersion === undefined) {
logger_1.default.info(`No OTA image currently available for '${data.device.name}'. Unscheduled.`);
}
}
catch (e) {
logger_1.default.debug(`OTA update of '${data.device.name}' failed (${e}). Retry scheduled for next request.`);
this.#removeProgressAndRemainingFromState(data.device);
await this.publishEntityState(data.device, this.#getEntityPublishPayload(data.device, "scheduled"));
}
this.#inProgress.delete(data.device.ieeeAddr);
return; // we're done
}
}
if (data.device.definition.ota) {
if (!data.device.options.disable_automatic_update_check && !settings.get().ota.disable_automatic_update_check) {
// When a device does a next image request, it will usually do it a few times after each other
// with only 10 - 60 seconds inbetween. It doesn't make sense to check for a new update
// each time, so this interval can be set by the user. The default is 1,440 minutes (one day).
const updateCheckInterval = settings.get().ota.update_check_interval * 1000 * 60;
const deviceLastChecked = this.#lastChecked.get(data.device.ieeeAddr);
const check = deviceLastChecked !== undefined ? Date.now() - deviceLastChecked > updateCheckInterval : true;
if (!check) {
return;
}
this.#inProgress.add(data.device.ieeeAddr);
this.#lastChecked.set(data.device.ieeeAddr, Date.now());
let availableResult;
try {
// auto-check defaults to zigbee-OTA + potential local index, and never `downgrade`
availableResult = await data.device.zh.checkOta({ downgrade: false }, data.data, data.device.otaExtraMetas, data.endpoint);
}
catch (error) {
logger_1.default.debug(`Failed to check if OTA update available for '${data.device.name}' (${error})`);
}
await this.publishEntityState(data.device, this.#getEntityPublishPayload(data.device, availableResult ?? "idle"));
if (availableResult?.available) {
logger_1.default.info(`OTA update available for '${data.device.name}'`);
}
}
}
// Respond to stop the client from requesting OTAs
await data.endpoint.commandResponse("genOta", "queryNextImageResponse", { status: zigbee_herdsman_1.Zcl.Status.NO_IMAGE_AVAILABLE }, undefined, data.meta.zclTransactionSequenceNumber);
logger_1.default.debug(`Responded to OTA request of '${data.device.name}' with 'NO_IMAGE_AVAILABLE'`);
this.#inProgress.delete(data.device.ieeeAddr);
}
async #readSoftwareBuildIDAndDateCode(device, sendPolicy) {
try {
const endpoint = device.zh.endpoints.find((e) => e.supportsInputCluster("genBasic"));
(0, node_assert_1.default)(endpoint);
const result = await endpoint.read("genBasic", ["dateCode", "swBuildId"], { sendPolicy });
return { softwareBuildID: result.swBuildId, dateCode: result.dateCode };
}
catch {
return undefined;
}
}
#getEntityPublishPayload(device, state, progress, remaining) {
const deviceUpdateState = this.state.get(device).update;
const update = typeof state === "string"
? {
state,
installed_version: deviceUpdateState?.installed_version,
latest_version: deviceUpdateState?.latest_version,
latest_source: deviceUpdateState?.latest_source,
latest_release_notes: deviceUpdateState?.latest_release_notes,
}
: {
state: state.available ? "available" : "idle",
installed_version: state.current.fileVersion,
latest_version: state.availableMeta?.fileVersion ?? state.current.fileVersion,
latest_source: state.availableMeta?.url || null,
latest_release_notes: state.availableMeta?.releaseNotes || null,
};
if (progress !== undefined) {
update.progress = progress;
}
if (remaining !== undefined) {
update.remaining = Math.round(remaining);
}
return { update };
}
async onMQTTMessage(data) {
const topicMatch = data.topic.match(this.#topicRegex);
if (!topicMatch) {
return;
}
const message = utils_1.default.parseJSON(data.message, data.message);
// TODO: deprecated 3.0 should remove string payload, enforce object
const messageObject = typeof message === "object";
if (messageObject) {
(0, node_assert_1.default)(message.id, "Invalid payload");
}
const ID = (messageObject ? message.id : message);
const device = this.zigbee.resolveEntity(ID);
const type = topicMatch[1];
const downgrade = topicMatch[2] === "downgrade";
let error;
let errorStack;
if (!(device instanceof device_1.default)) {
error = `Device '${ID}' does not exist`;
}
else if (this.#inProgress.has(device.ieeeAddr)) {
// also guards against scheduling while check/update op in progress that could result in undesired OTA state
error = `OTA update or check for update already in progress for '${device.name}'`;
}
else {
switch (type) {
case "check": {
this.#inProgress.add(device.ieeeAddr);
const source = { downgrade };
if (messageObject) {
const payload = message;
if (payload.url) {
source.url = payload.url;
}
else if (!device.definition || !device.definition.ota) {
error = `Device '${device.name}' does not support OTA updates`;
break;
}
}
else if (!device.definition || !device.definition.ota) {
error = `Device '${device.name}' does not support OTA updates`;
break;
}
logger_1.default.info(`Checking if OTA update available for '${device.name}'`);
try {
const availableResult = await device.zh.checkOta(source, undefined, device.otaExtraMetas);
logger_1.default.info(`${availableResult.available ? "" : "No "}OTA update available for '${device.name}'`);
await this.publishEntityState(device, this.#getEntityPublishPayload(device, availableResult));
this.#lastChecked.set(device.ieeeAddr, Date.now());
const response = utils_1.default.getResponse(message, {
id: ID,
update_available: availableResult.available,
downgrade: source.downgrade,
source: availableResult.availableMeta?.url,
release_notes: availableResult.availableMeta?.releaseNotes,
});
await this.mqtt.publish("bridge/response/device/ota_update/check", (0, json_stable_stringify_without_jsonify_1.default)(response));
}
catch (e) {
error = `Failed to check if OTA update available for '${device.name}' (${e.message})`;
errorStack = e.stack;
}
break;
}
case "update": {
this.#inProgress.add(device.ieeeAddr);
const otaSettings = settings.get().ota;
const source = { downgrade };
const dataSettings = {
// fallbacks are only to satisfy typing, should always be defined from settings defaults
requestTimeout: otaSettings.image_block_request_timeout ?? /* v8 ignore next */ 150000,
responseDelay: otaSettings.image_block_response_delay ?? /* v8 ignore next */ 250,
baseSize: otaSettings.default_maximum_data_size ?? /* v8 ignore next */ 50,
};
if (messageObject) {
const payload = message;
if (payload.hex) {
(0, node_assert_1.default)(payload.hex.data);
// write to `dataDir` and pass created path as source URL
source.url = writeFirmwareHexToDataDir(payload.hex.data, payload.hex.file_name, device.ieeeAddr);
}
else if (payload.url) {
source.url = payload.url;
}
else if (!device.definition || !device.definition.ota) {
error = `Device '${device.name}' does not support OTA updates`;
break;
}
if (payload.image_block_request_timeout) {
dataSettings.requestTimeout = payload.image_block_request_timeout;
}
if (payload.image_block_response_delay) {
dataSettings.responseDelay = payload.image_block_response_delay;
}
if (payload.default_maximum_data_size) {
dataSettings.baseSize = payload.default_maximum_data_size;
}
}
else if (!device.definition || !device.definition.ota) {
error = `Device '${device.name}' does not support OTA updates`;
break;
}
logger_1.default.info(`OTA updating '${device.name}' to ${downgrade ? "previous" : "latest"} firmware`);
try {
const firmwareFrom = await this.#readSoftwareBuildIDAndDateCode(device, "immediate");
const [fromVersion, toVersion] = await this.#update(source, device, undefined, undefined, dataSettings);
if (toVersion === undefined) {
error = `Update of '${device.name}' failed (No image currently available)`;
break;
}
const firmwareTo = await this.#readSoftwareBuildIDAndDateCode(device);
const response = utils_1.default.getResponse(message, {
id: ID,
from: {
file_version: fromVersion,
software_build_id: firmwareFrom?.softwareBuildID,
date_code: firmwareFrom?.dateCode,
},
to: { file_version: toVersion, software_build_id: firmwareTo?.softwareBuildID, date_code: firmwareTo?.dateCode },
});
await this.mqtt.publish("bridge/response/device/ota_update/update", (0, json_stable_stringify_without_jsonify_1.default)(response));
}
catch (e) {
logger_1.default.debug(`OTA update of '${device.name}' failed (${e})`);
error = `OTA update of '${device.name}' failed (${e.message})`;
errorStack = e.stack;
this.#removeProgressAndRemainingFromState(device);
await this.publishEntityState(device, this.#getEntityPublishPayload(device, "available"));
}
break;
}
case "schedule": {
const source = { downgrade };
if (messageObject) {
const payload = message;
if (payload.hex) {
(0, node_assert_1.default)(payload.hex.data);
// write to `dataDir` and pass created path as source URL
source.url = writeFirmwareHexToDataDir(payload.hex.data, payload.hex.file_name, device.ieeeAddr);
}
else if (payload.url) {
source.url = payload.url;
}
else if (!device.definition || !device.definition.ota) {
error = `Device '${device.name}' does not support OTA updates`;
break;
}
}
else if (!device.definition || !device.definition.ota) {
error = `Device '${device.name}' does not support OTA updates`;
break;
}
device.zh.scheduleOta(source);
await this.publishEntityState(device, this.#getEntityPublishPayload(device, "scheduled"));
const response = utils_1.default.getResponse(message, { id: ID, url: source.url });
await this.mqtt.publish("bridge/response/device/ota_update/schedule", (0, json_stable_stringify_without_jsonify_1.default)(response));
break;
}
case "unschedule": {
if (device.zh.scheduledOta?.url?.startsWith(data_1.default.joinPath("ota"))) {
(0, node_fs_1.rmSync)(device.zh.scheduledOta.url, { force: true });
}
device.zh.unscheduleOta();
await this.publishEntityState(device, this.#getEntityPublishPayload(device, "idle"));
const response = utils_1.default.getResponse(message, {
id: ID,
});
await this.mqtt.publish("bridge/response/device/ota_update/unschedule", (0, json_stable_stringify_without_jsonify_1.default)(response));
break;
}
}
this.#inProgress.delete(device.ieeeAddr);
}
if (error) {
const response = utils_1.default.getResponse(message, {}, error);
await this.mqtt.publish(`bridge/response/device/ota_update/${type}`, (0, json_stable_stringify_without_jsonify_1.default)(response));
logger_1.default.error(error);
if (errorStack) {
logger_1.default.debug(errorStack);
}
}
}
/**
* Do the bulk of the update work (hand over to zigbee-herdsman, then re-interview).
*
* `dataSettings` object may be mutated by zigbee-herdsman to fit request (e.g. known manuf quirk)
*/
async #update(source, device, requestPayload, requestTsn, dataSettings, endpoint) {
const [from, to] = await device.zh.updateOta(source, requestPayload, requestTsn, device.otaExtraMetas, async (progress, remaining) => {
await this.publishEntityState(device, this.#getEntityPublishPayload(device, "updating", progress, remaining));
}, dataSettings, endpoint);
if (to === undefined) {
this.#removeProgressAndRemainingFromState(device);
await this.publishEntityState(device, this.#getEntityPublishPayload(device, { available: false, current: from }));
return [from.fileVersion, undefined];
}
logger_1.default.info(`Finished update of '${device.name}'`);
this.#removeProgressAndRemainingFromState(device);
await this.publishEntityState(device, this.#getEntityPublishPayload(device, { available: false, current: to }));
logger_1.default.info(() => `Device '${device.name}' was OTA updated from '${from.fileVersion}' to '${to.fileVersion}'`);
// OTA update can bring new features & co, force full re-interview and re-configure, same as a "device joined"
if (device.zh.meta.configured !== undefined) {
delete device.zh.meta.configured;
device.zh.save();
}
setTimeout(() => {
device.reInterview(this.eventBus).catch((error) => {
logger_1.default.error(`${error.message}. Re-try manually after some time.`);
});
}, 5000);
return [from.fileVersion, to.fileVersion];
}
}
exports.default = OTAUpdate;
__decorate([
bind_decorator_1.default
], OTAUpdate.prototype, "onZigbeeEvent", null);
__decorate([
bind_decorator_1.default
], OTAUpdate.prototype, "onMQTTMessage", null);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3RhVXBkYXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vbGliL2V4dGVuc2lvbi9vdGFVcGRhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSw4REFBaUM7QUFDakMscUNBQXFFO0FBQ3JFLHlDQUErQjtBQUMvQixvRUFBa0M7QUFDbEMsa0hBQThEO0FBQzlELHFEQUF5RDtBQUV6RCw2REFBcUM7QUFFckMsd0RBQW1DO0FBQ25DLDREQUFvQztBQUNwQywyREFBNkM7QUFDN0MsMERBQWtDO0FBQ2xDLDREQUFvQztBQWdCcEM7O0dBRUc7QUFDSCxTQUFTLHlCQUF5QixDQUFDLEdBQVcsRUFBRSxRQUE0QixFQUFFLFVBQWtCO0lBQzVGLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNaLFFBQVEsR0FBRyxHQUFHLFVBQVUsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQztJQUM3QyxDQUFDO0lBRUQsTUFBTSxPQUFPLEdBQUcsY0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUV4QyxJQUFJLENBQUMsSUFBQSxvQkFBVSxFQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDdkIsSUFBQSxtQkFBUyxFQUFDLE9BQU8sRUFBRSxFQUFDLFNBQVMsRUFBRSxJQUFJLEVBQUMsQ0FBQyxDQUFDO0lBQzFDLENBQUM7SUFFRCxNQUFNLFFBQVEsR0FBRyxJQUFBLGdCQUFJLEVBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBRXpDLElBQUEsdUJBQWEsRUFBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUVqRCxPQUFPLFFBQVEsQ0FBQztBQUNwQixDQUFDO0FBRUQsTUFBcUIsU0FBVSxTQUFRLG1CQUFTO0lBQzVDLFdBQVcsR0FBRyxJQUFJLE1BQU0sQ0FD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