UNPKG

zwave-js

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

565 lines (564 loc) 28 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var FirmwareUpdate_exports = {}; __export(FirmwareUpdate_exports, { FirmwareUpdateMixin: () => FirmwareUpdateMixin, isFirmwareUpdateOTATask: () => isFirmwareUpdateOTATask }); module.exports = __toCommonJS(FirmwareUpdate_exports); var import_cc = require("@zwave-js/cc"); var import_core = require("@zwave-js/core"); var import_serialapi = require("@zwave-js/serial/serialapi"); var import_shared = require("@zwave-js/shared"); var import_waddle = require("@zwave-js/waddle"); var import_arrays = require("alcalzone-shared/arrays"); var import_async = require("alcalzone-shared/async"); var import_deferred_promise = require("alcalzone-shared/deferred-promise"); var import_math = require("alcalzone-shared/math"); var import_typeguards = require("alcalzone-shared/typeguards"); var import_Task = require("../../driver/Task.js"); var import_ScheduledPoll = require("./60_ScheduledPoll.js"); function isFirmwareUpdateOTATask(t) { return t.tag?.id === "firmware-update-ota"; } __name(isFirmwareUpdateOTATask, "isFirmwareUpdateOTATask"); class FirmwareUpdateMixin extends import_ScheduledPoll.SchedulePollMixin { static { __name(this, "FirmwareUpdateMixin"); } async getFirmwareUpdateCapabilities() { const api = this.commandClasses["Firmware Update Meta Data"]; const meta = await api.getMetaData(); if (!meta) { throw new import_core.ZWaveError(`Failed to request firmware update capabilities: The node did not respond in time!`, import_core.ZWaveErrorCodes.Controller_NodeTimeout); } else if (!meta.firmwareUpgradable) { return { firmwareUpgradable: false }; } return { firmwareUpgradable: true, // TODO: Targets are not the list of IDs - maybe expose the IDs as well? firmwareTargets: Array.from({ length: 1 + meta.additionalFirmwareIDs.length }).fill(0).map((_, i) => i), continuesToFunction: meta.continuesToFunction, supportsActivation: meta.supportsActivation, supportsResuming: meta.supportsResuming, supportsNonSecureTransfer: meta.supportsNonSecureTransfer }; } getFirmwareUpdateCapabilitiesCached() { const firmwareUpgradable = this.getValue(import_cc.FirmwareUpdateMetaDataCCValues.firmwareUpgradable.id); const supportsActivation = this.getValue(import_cc.FirmwareUpdateMetaDataCCValues.supportsActivation.id); const continuesToFunction = this.getValue(import_cc.FirmwareUpdateMetaDataCCValues.continuesToFunction.id); const additionalFirmwareIDs = this.getValue(import_cc.FirmwareUpdateMetaDataCCValues.additionalFirmwareIDs.id); const supportsResuming = this.getValue(import_cc.FirmwareUpdateMetaDataCCValues.supportsResuming.id); const supportsNonSecureTransfer = this.getValue(import_cc.FirmwareUpdateMetaDataCCValues.supportsNonSecureTransfer.id); if (!firmwareUpgradable || !(0, import_typeguards.isArray)(additionalFirmwareIDs)) { return { firmwareUpgradable: false }; } return { firmwareUpgradable: true, // TODO: Targets are not the list of IDs - maybe expose the IDs as well? firmwareTargets: Array.from({ length: 1 + additionalFirmwareIDs.length }).fill(0).map((_, i) => i), continuesToFunction, supportsActivation, supportsResuming, supportsNonSecureTransfer }; } _abortFirmwareUpdate; async abortFirmwareUpdate() { if (!this._abortFirmwareUpdate) return; await this._abortFirmwareUpdate(); } // Stores the CRC of the previously transferred firmware image. // Allows detecting whether resuming is supported and where to continue in a multi-file transfer. _previousFirmwareCRC; /** Is used to remember fragment requests that came in before they were able to be handled */ _firmwareUpdatePrematureRequest; async updateFirmware(updates, options = {}) { if (updates.length === 0) { throw new import_core.ZWaveError(`At least one update must be provided`, import_core.ZWaveErrorCodes.Argument_Invalid); } if (updates.some((u) => u.data.length === 0)) { throw new import_core.ZWaveError(`All firmware updates must have a non-empty data buffer`, import_core.ZWaveErrorCodes.Argument_Invalid); } if ((0, import_arrays.distinct)(updates.map((u) => u.firmwareTarget ?? 0)).length !== updates.length) { throw new import_core.ZWaveError(`The target of all provided firmware updates must be unique`, import_core.ZWaveErrorCodes.Argument_Invalid); } if (this.driver.isOTWFirmwareUpdateInProgress()) { throw new import_core.ZWaveError(`Failed to start the update: An OTW upgrade of the controller is in progress!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_Busy); } const task = this.getUpdateFirmwareTask(updates, options); if (task instanceof Promise) { throw new import_core.ZWaveError(`Failed to start the update: A firmware update is already in progress for this node!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_Busy); } return this.driver.scheduler.queueTask(task); } isFirmwareUpdateInProgress() { return !!this.driver.scheduler.findTask(isFirmwareUpdateOTATask); } getUpdateFirmwareTask(updates, options = {}) { const self = this; const existingTask = this.driver.scheduler.findTask((t) => t.tag?.id === "firmware-update-ota" && t.tag.nodeId === self.id); if (existingTask) return existingTask; let keepAwake; return { // Firmware updates cause a lot of traffic. Execute them in the background. priority: import_Task.TaskPriority.Lower, tag: { id: "firmware-update-ota", nodeId: self.id }, task: /* @__PURE__ */ __name(async function* firmwareUpdateTask() { keepAwake = self.keepAwake; self.keepAwake = true; const abortContext = { abort: false, tooLateToAbort: false, abortPromise: (0, import_deferred_promise.createDeferredPromise)() }; self._abortFirmwareUpdate = async () => { if (abortContext.tooLateToAbort) { throw new import_core.ZWaveError(`The firmware update was transmitted completely, cannot abort anymore.`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_FailedToAbort); } self.driver.controllerLog.logNode(self.id, { message: `Aborting firmware update...`, direction: "outbound" }); abortContext.abort = true; const aborted = await abortContext.abortPromise; if (!aborted) { throw new import_core.ZWaveError(`The node did not acknowledge the aborted update`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_FailedToAbort); } self.driver.controllerLog.logNode(self.id, { message: `Firmware update aborted`, direction: "inbound" }); }; let fragmentSizeSecure; let fragmentSizeNonSecure; let hardwareVersion; let meta; try { const prepareResult = await self.prepareFirmwareUpdateInternal(updates.map((u) => u.firmwareTarget ?? 0), abortContext); if (abortContext.abort) { const result2 = { success: false, status: import_cc.FirmwareUpdateStatus.Error_TransmissionFailed, reInterview: false }; self._emit("firmware update finished", self, result2); return result2; } ({ fragmentSizeSecure, fragmentSizeNonSecure, hardwareVersion, ...meta } = prepareResult); } catch { const result2 = { success: false, status: import_cc.FirmwareUpdateStatus.Error_TransmissionFailed, reInterview: false }; return result2; } yield; if (!meta.supportsResuming) options.resume = false; const securityClass = self.getHighestSecurityClass(); const isSecure = securityClass === import_core.SecurityClass.S0_Legacy || (0, import_core.securityClassIsS2)(securityClass); if (!isSecure) { options.nonSecureTransfer = false; } else if (!meta.supportsNonSecureTransfer) { options.nonSecureTransfer = false; } const notifyProgress = (0, import_shared.throttle)((progress) => self._emit("firmware update progress", self, progress), 250, true); const updatesWithChecksum = updates.map((u) => ({ ...u, checksum: (0, import_core.CRC16_CCITT)(u.data) })); let skipFinishedFiles = -1; let shouldResume = options.resume && self._previousFirmwareCRC != void 0; if (shouldResume) { skipFinishedFiles = updatesWithChecksum.findIndex((u) => u.checksum === self._previousFirmwareCRC); if (skipFinishedFiles === -1) shouldResume = false; } let updateResult; let conservativeWaitTime; const totalBytes = updatesWithChecksum.reduce((total, update) => total + update.data.length, 0); let sentBytesOfPreviousFiles = 0; for (let i = 0; i < updatesWithChecksum.length; i++) { const { // If the firmware target is not given, update the Z-Wave chip firmwareTarget: target = 0, // If the firmware ID is not given, use the current one for the given chip firmwareId = target === 0 ? meta.firmwareId : meta.additionalFirmwareIDs[target - 1], data, checksum } = updatesWithChecksum[i]; if (i < skipFinishedFiles) { self.driver.controllerLog.logNode(self.id, `Skipping already completed firmware update (part ${i + 1} / ${updatesWithChecksum.length})...`); sentBytesOfPreviousFiles += data.length; continue; } self.driver.controllerLog.logNode(self.id, `Updating firmware (part ${i + 1} / ${updatesWithChecksum.length})...`); let fragmentSize = options.nonSecureTransfer ? fragmentSizeNonSecure : fragmentSizeSecure; const { resume, nonSecureTransfer } = yield* self.beginFirmwareUpdateInternal(data, meta.manufacturerId, target, firmwareId, fragmentSize, checksum, hardwareVersion, shouldResume, options.nonSecureTransfer); if (options.nonSecureTransfer && !nonSecureTransfer) { fragmentSize = fragmentSizeSecure; } self._previousFirmwareCRC = checksum; if (shouldResume) { self.driver.controllerLog.logNode(self.id, `Node ${resume ? "accepted" : "did not accept"} resuming the update...`); } if (nonSecureTransfer) { self.driver.controllerLog.logNode(self.id, `Firmware will be transferred without encryption...`); } yield; updateResult = yield* self.doFirmwareUpdateInternal(data, fragmentSize, nonSecureTransfer, abortContext, (fragment, total) => { const progress = { currentFile: i + 1, totalFiles: updatesWithChecksum.length, sentFragments: fragment, totalFragments: total, progress: (0, import_math.roundTo)((sentBytesOfPreviousFiles + Math.min(fragment * fragmentSize, data.length)) / totalBytes * 100, 2) }; notifyProgress(progress); if (fragment === total) { sentBytesOfPreviousFiles += data.length; } }); conservativeWaitTime = self.driver.getConservativeWaitTimeAfterFirmwareUpdate(updateResult.waitTime); if (!updateResult.success) { self.driver.controllerLog.logNode(self.id, { message: `Firmware update (part ${i + 1} / ${updatesWithChecksum.length}) failed with status ${(0, import_shared.getEnumMemberName)(import_cc.FirmwareUpdateStatus, updateResult.status)}`, direction: "inbound" }); const result2 = { ...updateResult, waitTime: void 0, reInterview: false }; self._emit("firmware update finished", self, result2); return result2; } else if (i < updatesWithChecksum.length - 1) { self.driver.controllerLog.logNode(self.id, { message: `Firmware update (part ${i + 1} / ${updatesWithChecksum.length}) succeeded with status ${(0, import_shared.getEnumMemberName)(import_cc.FirmwareUpdateStatus, updateResult.status)}`, direction: "inbound" }); self.driver.controllerLog.logNode(self.id, `Continuing with next part in ${conservativeWaitTime} seconds...`); shouldResume = false; yield* (0, import_waddle.waitFor)((0, import_async.wait)(conservativeWaitTime * 1e3, true)); } } self._previousFirmwareCRC = void 0; const result = { ...updateResult, waitTime: conservativeWaitTime, reInterview: true }; keepAwake = true; self._emit("firmware update finished", self, result); return result; }, "firmwareUpdateTask"), cleanup() { self._abortFirmwareUpdate = void 0; self._firmwareUpdatePrematureRequest = void 0; self.keepAwake = keepAwake; if (!keepAwake) { setImmediate(() => { self.driver.debounceSendNodeToSleep(self); }); } return Promise.resolve(); } }; } /** Prepares the firmware update of a single target by collecting the necessary information */ async prepareFirmwareUpdateInternal(targets, abortContext) { const api = this.commandClasses["Firmware Update Meta Data"]; const meta = await api.getMetaData(); if (!meta) { throw new import_core.ZWaveError(`Failed to start the update: The node did not respond in time!`, import_core.ZWaveErrorCodes.Controller_NodeTimeout); } for (const target of targets) { if (target === 0) { if (!meta.firmwareUpgradable) { throw new import_core.ZWaveError(`Failed to start the update: The Z-Wave chip firmware is not upgradable!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_NotUpgradable); } } else { if (api.version < 3) { throw new import_core.ZWaveError(`Failed to start the update: The node does not support upgrading a different firmware target than 0!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_TargetNotFound); } else if (meta.additionalFirmwareIDs[target - 1] == void 0) { throw new import_core.ZWaveError(`Failed to start the update: Firmware target #${target} not found on this node!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_TargetNotFound); } } } const fcc = new import_cc.FirmwareUpdateMetaDataCC({ nodeId: this.id }); fcc.toggleEncapsulationFlag(import_core.EncapsulationFlags.Security, this.driver.isCCSecure(fcc.ccId, this.id)); const maxGrossPayloadSizeSecure = this.driver.computeNetCCPayloadSize(fcc); const maxGrossPayloadSizeNonSecure = this.driver.computeNetCCPayloadSize(fcc, true); const ccVersion = (0, import_cc.getEffectiveCCVersion)(this.driver, fcc); const maxNetPayloadSizeSecure = maxGrossPayloadSizeSecure - 2 - (ccVersion >= 2 ? 2 : 0); const maxNetPayloadSizeNonSecure = maxGrossPayloadSizeNonSecure - 2 - (ccVersion >= 2 ? 2 : 0); const fragmentSizeSecure = Math.min(maxNetPayloadSizeSecure, meta.maxFragmentSize ?? Number.POSITIVE_INFINITY); const fragmentSizeNonSecure = Math.min(maxNetPayloadSizeNonSecure, meta.maxFragmentSize ?? Number.POSITIVE_INFINITY); if (abortContext.abort) { abortContext.abortPromise.resolve(true); return; } else { return { ...meta, fragmentSizeSecure, fragmentSizeNonSecure }; } } async handleUnexpectedFirmwareUpdateGet(command) { if (this.isFirmwareUpdateInProgress()) { this._firmwareUpdatePrematureRequest = command; return; } this.driver.controllerLog.logNode(this.id, { message: `Received Firmware Update Get, but no firmware update is in progress. Forcing the node to abort...`, direction: "inbound" }); const fcc = new import_cc.FirmwareUpdateMetaDataCC({ nodeId: this.id }); fcc.toggleEncapsulationFlag(import_core.EncapsulationFlags.Security, !!(command.encapsulationFlags & import_core.EncapsulationFlags.Security)); const ccVersion = (0, import_cc.getEffectiveCCVersion)(this.driver, fcc); const fragmentSize = this.driver.computeNetCCPayloadSize(fcc) - 2 - (ccVersion >= 2 ? 2 : 0); const fragment = (0, import_core.randomBytes)(fragmentSize); try { await this.sendCorruptedFirmwareUpdateReport(command.reportNumber, fragment); } catch { } } /** Kicks off a firmware update of a single target. Returns whether the node accepted resuming and non-secure transfer */ async *beginFirmwareUpdateInternal(data, manufacturerId, target, firmwareId, fragmentSize, checksum, hardwareVersion, resume, nonSecureTransfer) { const api = this.commandClasses["Firmware Update Meta Data"]; this.driver.controllerLog.logNode(this.id, { message: `Starting firmware update...`, direction: "outbound" }); let result = await api.requestUpdate({ // TODO: Should manufacturer id be provided externally? manufacturerId, firmwareId, firmwareTarget: target, fragmentSize, checksum, hardwareVersion, resume, nonSecureTransfer }); if (!result) { result = yield* (0, import_waddle.waitFor)(this.driver.waitForCommand((cc) => cc instanceof import_cc.FirmwareUpdateMetaDataCCRequestReport && cc.nodeId === this.id, 6e4)); } switch (result.status) { case import_cc.FirmwareUpdateRequestStatus.Error_AuthenticationExpected: throw new import_core.ZWaveError(`Failed to start the update: A manual authentication event (e.g. button push) was expected!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_FailedToStart); case import_cc.FirmwareUpdateRequestStatus.Error_BatteryLow: throw new import_core.ZWaveError(`Failed to start the update: The battery level is too low!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_FailedToStart); case import_cc.FirmwareUpdateRequestStatus.Error_FirmwareUpgradeInProgress: throw new import_core.ZWaveError(`Failed to start the update: A firmware upgrade is already in progress!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_Busy); case import_cc.FirmwareUpdateRequestStatus.Error_InvalidManufacturerOrFirmwareID: throw new import_core.ZWaveError(`Failed to start the update: Invalid manufacturer or firmware id!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_FailedToStart); case import_cc.FirmwareUpdateRequestStatus.Error_InvalidHardwareVersion: throw new import_core.ZWaveError(`Failed to start the update: Invalid hardware version!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_FailedToStart); case import_cc.FirmwareUpdateRequestStatus.Error_NotUpgradable: throw new import_core.ZWaveError(`Failed to start the update: Firmware target #${target} is not upgradable!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_NotUpgradable); case import_cc.FirmwareUpdateRequestStatus.Error_FragmentSizeTooLarge: throw new import_core.ZWaveError(`Failed to start the update: The chosen fragment size is too large!`, import_core.ZWaveErrorCodes.FirmwareUpdateCC_FailedToStart); case import_cc.FirmwareUpdateRequestStatus.OK: this.keepAwake = true; } return { resume: !!result.resume, nonSecureTransfer: !!result.nonSecureTransfer }; } async handleFirmwareUpdateMetaDataGet(command) { const endpoint = this.getEndpoint(command.endpointIndex) ?? this; const api = endpoint.createAPI(import_core.CommandClasses["Firmware Update Meta Data"], false).withOptions({ // Answer with the same encapsulation as asked, but omit // Supervision as it shouldn't be used for Get-Report flows encapsulationFlags: command.encapsulationFlags & ~import_core.EncapsulationFlags.Supervision }); await api.reportMetaData({ manufacturerId: this.driver.options.vendor?.manufacturerId ?? 65535, firmwareUpgradable: false, hardwareVersion: this.driver.options.vendor?.hardwareVersion ?? 0, // We must advertise Z-Wave JS itself as firmware 1 // No firmware is upgradable, so we advertise firmware id 0 additionalFirmwareIDs: [0] }); } async handleFirmwareUpdateRequestGet(command) { const endpoint = this.getEndpoint(command.endpointIndex) ?? this; const api = endpoint.createAPI(import_core.CommandClasses["Firmware Update Meta Data"], false).withOptions({ // Answer with the same encapsulation as asked, but omit // Supervision as it shouldn't be used for Get-Report flows encapsulationFlags: command.encapsulationFlags & ~import_core.EncapsulationFlags.Supervision }); await api.respondToUpdateRequest({ status: import_cc.FirmwareUpdateRequestStatus.Error_NotUpgradable }); } async handleFirmwareUpdatePrepareGet(command) { const endpoint = this.getEndpoint(command.endpointIndex) ?? this; const api = endpoint.createAPI(import_core.CommandClasses["Firmware Update Meta Data"], false).withOptions({ // Answer with the same encapsulation as asked, but omit // Supervision as it shouldn't be used for Get-Report flows encapsulationFlags: command.encapsulationFlags & ~import_core.EncapsulationFlags.Supervision }); await api.respondToDownloadRequest({ status: import_cc.FirmwareDownloadStatus.Error_NotDownloadable, checksum: 0 }); } async sendCorruptedFirmwareUpdateReport(reportNum, fragment, nonSecureTransfer = false) { try { await this.commandClasses["Firmware Update Meta Data"].withOptions({ // Only encapsulate if the transfer is secure autoEncapsulate: !nonSecureTransfer }).sendFirmwareFragment(reportNum, true, fragment); } catch { } } hasPendingFirmwareUpdateFragment(fragmentNumber) { const isCurrentFirmwareFragment = /* @__PURE__ */ __name((t) => t.message.getNodeId() === this.id && (0, import_serialapi.containsCC)(t.message) && t.message.command instanceof import_cc.FirmwareUpdateMetaDataCCReport && t.message.command.reportNumber === fragmentNumber, "isCurrentFirmwareFragment"); return this.driver.hasPendingTransactions(isCurrentFirmwareFragment); } async *doFirmwareUpdateInternal(data, fragmentSize, nonSecureTransfer, abortContext, onProgress) { const numFragments = Math.ceil(data.length / fragmentSize); this._firmwareUpdatePrematureRequest = void 0; update: while (true) { yield; let fragmentRequest; if (this._firmwareUpdatePrematureRequest) { fragmentRequest = this._firmwareUpdatePrematureRequest; this._firmwareUpdatePrematureRequest = void 0; } else { try { fragmentRequest = yield* (0, import_waddle.waitFor)(this.driver.waitForCommand( (cc) => cc.nodeId === this.id && cc instanceof import_cc.FirmwareUpdateMetaDataCCGet, // Wait up to 2 minutes for each fragment request. // Some users try to update devices with unstable connections, where 30s can be too short. import_core.timespan.minutes(2) )); } catch { this.driver.controllerLog.logNode(this.id, { message: `Firmware update timed out`, direction: "none", level: "warn" }); return { success: false, status: import_cc.FirmwareUpdateStatus.Error_Timeout }; } } this.markAsAwake(); if (fragmentRequest.reportNumber > numFragments) { this.driver.controllerLog.logNode(this.id, { message: `Received Firmware Update Get for an out-of-bounds fragment. Forcing the node to abort...`, direction: "inbound" }); await this.sendCorruptedFirmwareUpdateReport(fragmentRequest.reportNumber, (0, import_core.randomBytes)(fragmentSize), nonSecureTransfer); break update; } request: for (let num = fragmentRequest.reportNumber; num < fragmentRequest.reportNumber + fragmentRequest.numReports; num++) { yield; if (num > numFragments) { break; } const fragment = data.subarray((num - 1) * fragmentSize, num * fragmentSize); if (abortContext.abort) { await this.sendCorruptedFirmwareUpdateReport(fragmentRequest.reportNumber, (0, import_core.randomBytes)(fragment.length), nonSecureTransfer); break update; } else { if (this.hasPendingFirmwareUpdateFragment(num)) { this.driver.controllerLog.logNode(this.id, { message: `Firmware fragment ${num} already queued`, level: "warn" }); continue request; } this.driver.controllerLog.logNode(this.id, { message: `Sending firmware fragment ${num} / ${numFragments}`, direction: "outbound" }); const isLast = num === numFragments; try { await this.commandClasses["Firmware Update Meta Data"].withOptions({ // Only encapsulate if the transfer is secure autoEncapsulate: !nonSecureTransfer }).sendFirmwareFragment(num, isLast, fragment); onProgress(num, numFragments); if (isLast) { abortContext.tooLateToAbort = true; break update; } } catch { this.driver.controllerLog.logNode(this.id, { message: `Failed to send firmware fragment ${num} / ${numFragments}`, direction: "outbound", level: "warn" }); break request; } } } } yield; const statusReport = yield* (0, import_waddle.waitFor)(this.driver.waitForCommand( (cc) => cc.nodeId === this.id && cc instanceof import_cc.FirmwareUpdateMetaDataCCStatusReport, // Wait up to 5 minutes. It should never take that long, but the specs // don't say anything specific 5 * 6e4 ).catch(() => void 0)); if (abortContext.abort) { abortContext.abortPromise.resolve(statusReport?.status === import_cc.FirmwareUpdateStatus.Error_TransmissionFailed); } if (!statusReport) { this.driver.controllerLog.logNode(this.id, `The node did not acknowledge the completed update`, "warn"); return { success: false, status: import_cc.FirmwareUpdateStatus.Error_Timeout }; } const { status, waitTime } = statusReport; const success = status >= import_cc.FirmwareUpdateStatus.OK_WaitingForActivation; return { success, status, waitTime }; } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { FirmwareUpdateMixin, isFirmwareUpdateOTATask }); //# sourceMappingURL=70_FirmwareUpdate.js.map