Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support OTA update/check all devices #23386

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 118 additions & 101 deletions lib/extension/otaUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
}

const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/ota_update/.+$`);
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/device/ota_update/(update|check)`, 'i');
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/device/ota_update/(update|check|check_all|update_all)`, 'i');

export default class OTAUpdate extends Extension {
private inProgress = new Set();
Expand Down Expand Up @@ -173,147 +173,164 @@
return null;
}

const type = data.topic.substring(data.topic.lastIndexOf('/') + 1);
const forAll = type.endsWith('_all');
const message = utils.parseJSON(data.message, data.message);
const ID = (typeof message === 'object' && message.hasOwnProperty('id') ? message.id : message) as string;
const device = this.zigbee.resolveEntity(ID);
const type = data.topic.substring(data.topic.lastIndexOf('/') + 1);
const responseData: {id: string; updateAvailable?: boolean; from?: string; to?: string} = {id: ID};
const responseDataSingle: {id: string; updateAvailable?: boolean; from?: string; to?: string} = {id: ID};
let error = null;
let errorStack = null;

if (!(device instanceof Device)) {
error = `Device '${ID}' does not exist`;
} else if (!device.definition || !device.definition.ota) {
error = `Device '${device.name}' does not support OTA updates`;

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `not_supported`, device: device.name};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: error, meta}));
}
} else if (this.inProgress.has(device.ieeeAddr)) {
error = `Update or check for update already in progress for '${device.name}'`;
let devices: Device[] = [];
if (forAll) {
devices = this.zigbee.devices(false).filter((d) => d.definition?.ota);
} else {
this.inProgress.add(device.ieeeAddr);

if (type === 'check') {
const msg = `Checking if update available for '${device.name}'`;
logger.info(msg);
const device = this.zigbee.resolveEntity(ID);
if (!(device instanceof Device)) {
error = `Device '${ID}' does not exist`;
} else if (!device.definition || !device.definition.ota) {
error = `Device '${device.name}' does not support OTA updates`;

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `checking_if_available`, device: device.name};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: msg, meta}));
const meta = {status: `not_supported`, device: device.name};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: error, meta}));
}
} else {
devices.push(device);
}
}

for (const device of devices) {
if (this.inProgress.has(device.ieeeAddr)) {
const message = `Update or check for update already in progress for '${device.name}'`;
if (forAll) {
logger.warning(message);
} else {
error = message;
}
} else {
this.inProgress.add(device.ieeeAddr);

try {
const availableResult = await device.definition.ota.isUpdateAvailable(device.zh, null);
const msg = `${availableResult.available ? 'Update' : 'No update'} available for '${device.name}'`;
if (type === 'check') {
const msg = `Checking if update available for '${device.name}'`;
logger.info(msg);

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {
status: availableResult.available ? 'available' : 'not_available',
device: device.name,
};
const meta = {status: `checking_if_available`, device: device.name};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: msg, meta}));
}

const payload = this.getEntityPublishPayload(device, availableResult);
await this.publishEntityState(device, payload);
this.lastChecked[device.ieeeAddr] = Date.now();
responseData.updateAvailable = availableResult.available;
} catch (e) {
error = `Failed to check if update available for '${device.name}' (${e.message})`;
errorStack = e.stack;
try {
const availableResult = await device.definition.ota.isUpdateAvailable(device.zh, null);
const msg = `${availableResult.available ? 'Update' : 'No update'} available for '${device.name}'`;
logger.info(msg);

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {
status: availableResult.available ? 'available' : 'not_available',
device: device.name,
};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: msg, meta}));
}

const payload = this.getEntityPublishPayload(device, availableResult);
await this.publishEntityState(device, payload);
this.lastChecked[device.ieeeAddr] = Date.now();
responseData.updateAvailable = availableResult.available;

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 22)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / ci

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / ci

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 22)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 242 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?
} catch (e) {
error = `Failed to check if update available for '${device.name}' (${e.message})`;
errorStack = e.stack;

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `check_failed`, device: device.name};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: error, meta}));
}
}
} else {
// type === 'update'
const msg = `Updating '${device.name}' to latest firmware`;
logger.info(msg);

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `check_failed`, device: device.name};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: error, meta}));
const meta = {status: `update_in_progress`, device: device.name};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: msg, meta}));
}
}
} else {
// type === 'update'
const msg = `Updating '${device.name}' to latest firmware`;
logger.info(msg);

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `update_in_progress`, device: device.name};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: msg, meta}));
}
try {
const onProgress = async (progress: number, remaining: number): Promise<void> => {
let msg = `Update of '${device.name}' at ${progress.toFixed(2)}%`;
if (remaining) {
msg += `, ≈ ${Math.round(remaining / 60)} minutes remaining`;
}

try {
const onProgress = async (progress: number, remaining: number): Promise<void> => {
let msg = `Update of '${device.name}' at ${progress.toFixed(2)}%`;
if (remaining) {
msg += `, ≈ ${Math.round(remaining / 60)} minutes remaining`;
}
logger.info(msg);

logger.info(msg);
const payload = this.getEntityPublishPayload(device, 'updating', progress, remaining);
await this.publishEntityState(device, payload);

const payload = this.getEntityPublishPayload(device, 'updating', progress, remaining);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `update_progress`, device: device.name, progress};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: msg, meta}));
}
};

const from_ = await this.readSoftwareBuildIDAndDateCode(device, 'immediate');
const fileVersion = await device.definition.ota.updateToLatest(device.zh, onProgress);
logger.info(`Finished update of '${device.name}'`);
this.removeProgressAndRemainingFromState(device);
const payload = this.getEntityPublishPayload(device, {
available: false,
currentFileVersion: fileVersion,
otaFileVersion: fileVersion,
});
await this.publishEntityState(device, payload);
const to = await this.readSoftwareBuildIDAndDateCode(device);
const [fromS, toS] = [stringify(from_), stringify(to)];
logger.info(`Device '${device.name}' was updated from '${fromS}' to '${toS}'`);
responseData.from = from_ ? utils.toSnakeCase(from_) : null;

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 22)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / ci

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / ci

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 22)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 296 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?
responseData.to = to ? utils.toSnakeCase(to) : null;

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 22)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / ci

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / ci

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 22)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 18)

Cannot find name 'responseData'. Did you mean 'Response'?

Check failure on line 297 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 20)

Cannot find name 'responseData'. Did you mean 'Response'?
/**
* Re-configure after reading software build ID and date code, some devices use a
* custom attribute for this (e.g. Develco SMSZB-120)
*/
this.eventBus.emitReconfigure({device});
this.eventBus.emitDevicesChanged();

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `update_progress`, device: device.name, progress};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: msg, meta}));
const meta = {status: `update_succeeded`, device: device.name, from: from_, to};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message, meta}));
}
};

const from_ = await this.readSoftwareBuildIDAndDateCode(device, 'immediate');
const fileVersion = await device.definition.ota.updateToLatest(device.zh, onProgress);
logger.info(`Finished update of '${device.name}'`);
this.removeProgressAndRemainingFromState(device);
const payload = this.getEntityPublishPayload(device, {
available: false,
currentFileVersion: fileVersion,
otaFileVersion: fileVersion,
});
await this.publishEntityState(device, payload);
const to = await this.readSoftwareBuildIDAndDateCode(device);
const [fromS, toS] = [stringify(from_), stringify(to)];
logger.info(`Device '${device.name}' was updated from '${fromS}' to '${toS}'`);
responseData.from = from_ ? utils.toSnakeCase(from_) : null;
responseData.to = to ? utils.toSnakeCase(to) : null;
/**
* Re-configure after reading software build ID and date code, some devices use a
* custom attribute for this (e.g. Develco SMSZB-120)
*/
this.eventBus.emitReconfigure({device});
this.eventBus.emitDevicesChanged();
} catch (e) {
logger.debug(`Update of '${device.name}' failed (${e})`);
error = `Update of '${device.name}' failed (${e.message})`;
errorStack = e.stack;

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `update_succeeded`, device: device.name, from: from_, to};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message, meta}));
}
} catch (e) {
logger.debug(`Update of '${device.name}' failed (${e})`);
error = `Update of '${device.name}' failed (${e.message})`;
errorStack = e.stack;

this.removeProgressAndRemainingFromState(device);
const payload = this.getEntityPublishPayload(device, 'available');
await this.publishEntityState(device, payload);
this.removeProgressAndRemainingFromState(device);
const payload = this.getEntityPublishPayload(device, 'available');
await this.publishEntityState(device, payload);

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `update_failed`, device: device.name};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: error, meta}));
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `update_failed`, device: device.name};
await this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: error, meta}));
}
}
}
}

this.inProgress.delete(device.ieeeAddr);
this.inProgress.delete(device.ieeeAddr);
}
}

const triggeredViaLegacyApi = data.topic.match(legacyTopicRegex);
if (!triggeredViaLegacyApi) {
const response = utils.getResponse(message, responseData, error);

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 18)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 20)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 18)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 20)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 22)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / ci

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 20)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 18)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 18)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / ci

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 20)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 22)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 18)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 20)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 18)

Cannot find name 'responseData'. Did you mean 'response'?

Check failure on line 333 in lib/extension/otaUpdate.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 20)

Cannot find name 'responseData'. Did you mean 'response'?
await this.mqtt.publish(`bridge/response/device/ota_update/${type}`, stringify(response));
}

Expand Down
42 changes: 42 additions & 0 deletions test/otaUpdate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const stringify = require('json-stable-stringify-without-jsonify');
const zigbeeOTA = require('zigbee-herdsman-converters/lib/ota/zigbeeOTA');

zigbeeHerdsman.returnDevices.push(zigbeeHerdsman.devices.coordinator.ieeeAddr);
zigbeeHerdsman.returnDevices.push(zigbeeHerdsman.devices.bulb.ieeeAddr);
zigbeeHerdsman.returnDevices.push(zigbeeHerdsman.devices.bulb_color.ieeeAddr);
zigbeeHerdsman.returnDevices.push(zigbeeHerdsman.devices.HGZB04D.ieeeAddr);
zigbeeHerdsman.returnDevices.push(zigbeeHerdsman.devices.SV01.ieeeAddr);

const spyUseIndexOverride = jest.spyOn(zigbeeOTA, 'useIndexOverride');

describe('OTA update', () => {
Expand Down Expand Up @@ -181,6 +187,42 @@ describe('OTA update', () => {
expect.any(Function),
);
});

it('onlythis Should be able to check if OTA update is available for all devices', async () => {
const bulb = await zigbeeHerdsmanConverters.findByDevice(zigbeeHerdsman.devices.bulb);
const bulb_color = await zigbeeHerdsmanConverters.findByDevice(zigbeeHerdsman.devices.bulb_color);
mockClear(bulb);
mockClear(bulb_color);

bulb.ota.isUpdateAvailable.mockReturnValueOnce({available: false, currentFileVersion: 10, otaFileVersion: 10});
bulb_color.ota.isUpdateAvailable.mockReturnValueOnce({available: false, currentFileVersion: 10, otaFileVersion: 10});

MQTT.events.message('zigbee2mqtt/bridge/request/device/ota_update/check_all', '');
await flushPromises();
expect(bulb.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
expect(bulb.ota.updateToLatest).toHaveBeenCalledTimes(0);
expect(bulb_color.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
expect(bulb_color.ota.updateToLatest).toHaveBeenCalledTimes(0);
// expect(MQTT.publish).toHaveBeenCalledWith(
// 'zigbee2mqtt/bridge/response/device/ota_update/check',
// stringify({data: {id: 'bulb', updateAvailable: false}, status: 'ok'}),
// {retain: false, qos: 0},
// expect.any(Function),
// );

// MQTT.publish.mockClear();
// mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: true, currentFileVersion: 10, otaFileVersion: 12});
// MQTT.events.message('zigbee2mqtt/bridge/request/device/ota_update/check', 'bulb');
// await flushPromises();
// expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(2);
// expect(mapped.ota.updateToLatest).toHaveBeenCalledTimes(0);
// expect(MQTT.publish).toHaveBeenCalledWith(
// 'zigbee2mqtt/bridge/response/device/ota_update/check',
// stringify({data: {id: 'bulb', updateAvailable: true}, status: 'ok'}),
// {retain: false, qos: 0},
// expect.any(Function),
// );
});

it('Should handle if OTA update check fails', async () => {
const device = zigbeeHerdsman.devices.bulb;
Expand Down
Loading