mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2024-11-17 02:48:31 -07:00
88 lines
3.5 KiB
JavaScript
88 lines
3.5 KiB
JavaScript
const settings = require('../util/settings');
|
|
const logger = require('../util/logger');
|
|
const assert = require('assert');
|
|
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/ota_update/.+$`);
|
|
const BaseExtension = require('./baseExtension');
|
|
|
|
class OTAUpdate extends BaseExtension {
|
|
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
|
|
super(zigbee, mqtt, state, publishEntityState, eventBus);
|
|
this.inProgress = new Set();
|
|
}
|
|
|
|
onMQTTConnected() {
|
|
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/ota_update/check`);
|
|
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/ota_update/update`);
|
|
}
|
|
|
|
async readSoftwareBuildIDAndDateCode(device, update) {
|
|
const endpoint = device.endpoints.find((e) => e.supportsInputCluster('genBasic'));
|
|
const result = await endpoint.read('genBasic', ['dateCode', 'swBuildId']);
|
|
|
|
if (update) {
|
|
device.softwareBuildID = result.swBuildId;
|
|
device.dateCode = result.dateCode;
|
|
device.save();
|
|
}
|
|
|
|
return {softwareBuildID: result.swBuildId, dateCode: result.dateCode};
|
|
}
|
|
|
|
async onMQTTMessage(topic, message) {
|
|
if (!topic.match(topicRegex)) {
|
|
return null;
|
|
}
|
|
|
|
const device = this.zigbee.resolveEntity(message);
|
|
assert(device != null && device.type === 'device', 'Device not found or not a device');
|
|
|
|
if (!device.mapped || !device.mapped.ota) {
|
|
logger.error(`Device '${device.name}' does not support OTA updates`);
|
|
return;
|
|
}
|
|
|
|
if (this.inProgress.has(device.device.ieeeAddr)) {
|
|
logger.error(`Update or check already in progress for '${device.name}', skipping...`);
|
|
return;
|
|
}
|
|
this.inProgress.add(device.device.ieeeAddr);
|
|
|
|
const type = topic.split('/')[3];
|
|
if (type === 'check') {
|
|
logger.info(`Checking if update available for '${device.name}'`);
|
|
try {
|
|
const available = await device.mapped.ota.isUpdateAvailable(device.device, logger);
|
|
logger.info(available ?
|
|
`Update available for '${device.name}'` : `No update available for '${device.name}'`,
|
|
);
|
|
} catch (error) {
|
|
logger.error(`Failed to check if update available for '${device.name}' (${error.message})`);
|
|
}
|
|
} else { // type === 'update'
|
|
logger.info(`Updating '${device.name}' to latest firmware`);
|
|
try {
|
|
const onProgress = (progress, remaining) => {
|
|
let message = `Update of '${device.name}' at ${progress}%`;
|
|
if (remaining) {
|
|
message += `, +- ${Math.round(remaining / 60)} minutes remaining`;
|
|
}
|
|
|
|
logger.info(message);
|
|
};
|
|
|
|
const from_ = await this.readSoftwareBuildIDAndDateCode(device.device, false);
|
|
await device.mapped.ota.updateToLatest(device.device, logger, onProgress);
|
|
const to = await this.readSoftwareBuildIDAndDateCode(device.device, true);
|
|
const [fromS, toS] = [JSON.stringify(from_), JSON.stringify(to)];
|
|
logger.info(`Finished update of '${device.name}', from '${fromS}' to '${toS}'`);
|
|
} catch (error) {
|
|
logger.error(`Update of '${device.name}' failed (${error.message})`);
|
|
}
|
|
}
|
|
|
|
this.inProgress.delete(device.device.ieeeAddr);
|
|
}
|
|
}
|
|
|
|
module.exports = OTAUpdate;
|