diff --git a/lib/extension/otaUpdate.ts b/lib/extension/otaUpdate.ts index d55e9816..4b9e4b11 100644 --- a/lib/extension/otaUpdate.ts +++ b/lib/extension/otaUpdate.ts @@ -45,7 +45,7 @@ export default class OTAUpdate extends Extension { if (data.type !== 'commandQueryNextImageRequest' || !data.device.definition) return; logger.debug(`Device '${data.device.name}' requested OTA`); - const supportsOTA = data.device.definition.hasOwnProperty('ota'); + let supportsOTA = data.device.definition.hasOwnProperty('ota'); if (supportsOTA) { // 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 @@ -56,8 +56,14 @@ export default class OTAUpdate extends Extension { if (!check || this.inProgress.has(data.device.ieeeAddr)) return; this.lastChecked[data.device.ieeeAddr] = Date.now(); - const available = await data.device.definition.ota.isUpdateAvailable( - data.device.zh, logger, data.data); + let available = false; + try { + available = await data.device.definition.ota.isUpdateAvailable(data.device.zh, logger, data.data); + } catch (e) { + supportsOTA = false; + logger.debug(`Failed to check if update available for '${data.device.name}' (${e.message})`); + } + const payload = this.getEntityPublishPayload(available ? 'available' : 'idle'); this.publishEntityState(data.device, payload); diff --git a/test/otaUpdate.test.js b/test/otaUpdate.test.js index 7f410b92..4da146ac 100644 --- a/test/otaUpdate.test.js +++ b/test/otaUpdate.test.js @@ -265,6 +265,28 @@ describe('OTA update', () => { ); }); + it('Should respond with NO_IMAGE_AVAILABLE when update available request fails', async () => { + const device = zigbeeHerdsman.devices.bulb; + device.endpoints[0].commandResponse.mockClear(); + const data = {imageType: 12382}; + const mapped = zigbeeHerdsmanConverters.findByDevice(device) + mockClear(mapped); + mapped.ota.isUpdateAvailable.mockImplementationOnce(() => {throw new Error('Nothing to find here')}) + const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10}; + logger.info.mockClear(); + await zigbeeHerdsman.events.message(payload); + await flushPromises(); + expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1); + expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382}); + expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1); + expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98}); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bulb', + stringify({"update_available":false,"update":{"state":"idle"}}), + {retain: true, qos: 0}, expect.any(Function) + ); + }); + it('Should check for update when device requests it and it is not available', async () => { const device = zigbeeHerdsman.devices.bulb; device.endpoints[0].commandResponse.mockClear();