zwave-js 14.3.6 → 14.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/build/cjs/lib/_version.d.ts +1 -1
  2. package/build/cjs/lib/_version.js +1 -1
  3. package/build/cjs/lib/_version.js.map +1 -1
  4. package/build/cjs/lib/controller/Controller.d.ts +2 -2
  5. package/build/cjs/lib/controller/Controller.js +1 -1
  6. package/build/cjs/lib/controller/Controller.js.map +2 -2
  7. package/build/cjs/lib/driver/Driver.d.ts +2 -2
  8. package/build/cjs/lib/driver/Driver.js +1 -4
  9. package/build/cjs/lib/driver/Driver.js.map +2 -2
  10. package/build/cjs/lib/driver/Statistics.js.map +2 -2
  11. package/build/cjs/lib/node/Node.d.ts +2 -2
  12. package/build/cjs/lib/node/Node.js +1 -2
  13. package/build/cjs/lib/node/Node.js.map +2 -2
  14. package/build/cjs/lib/node/mixins/70_FirmwareUpdate.js +5 -2
  15. package/build/cjs/lib/node/mixins/70_FirmwareUpdate.js.map +2 -2
  16. package/build/cjs/lib/zniffer/Zniffer.d.ts +2 -2
  17. package/build/cjs/lib/zniffer/Zniffer.js +1 -1
  18. package/build/cjs/lib/zniffer/Zniffer.js.map +2 -2
  19. package/build/esm/lib/_version.d.ts +1 -1
  20. package/build/esm/lib/_version.js +1 -1
  21. package/build/esm/lib/controller/Controller.d.ts +2 -2
  22. package/build/esm/lib/controller/Controller.d.ts.map +1 -1
  23. package/build/esm/lib/controller/Controller.js +2 -2
  24. package/build/esm/lib/controller/Controller.js.map +1 -1
  25. package/build/esm/lib/driver/Driver.d.ts +2 -2
  26. package/build/esm/lib/driver/Driver.d.ts.map +1 -1
  27. package/build/esm/lib/driver/Driver.js +2 -7
  28. package/build/esm/lib/driver/Driver.js.map +1 -1
  29. package/build/esm/lib/driver/Statistics.d.ts.map +1 -1
  30. package/build/esm/lib/driver/Statistics.js.map +1 -1
  31. package/build/esm/lib/node/Node.d.ts +2 -2
  32. package/build/esm/lib/node/Node.d.ts.map +1 -1
  33. package/build/esm/lib/node/Node.js +2 -3
  34. package/build/esm/lib/node/Node.js.map +1 -1
  35. package/build/esm/lib/node/mixins/70_FirmwareUpdate.d.ts.map +1 -1
  36. package/build/esm/lib/node/mixins/70_FirmwareUpdate.js +5 -2
  37. package/build/esm/lib/node/mixins/70_FirmwareUpdate.js.map +1 -1
  38. package/build/esm/lib/zniffer/Zniffer.d.ts +2 -2
  39. package/build/esm/lib/zniffer/Zniffer.d.ts.map +1 -1
  40. package/build/esm/lib/zniffer/Zniffer.js +2 -2
  41. package/build/esm/lib/zniffer/Zniffer.js.map +1 -1
  42. package/package.json +11 -11
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/controller/Controller.ts"],
4
- "sourcesContent": ["import {\n\ttype AssociationAddress,\n\tAssociationCC,\n\ttype AssociationCheckResult,\n\ttype AssociationGroup,\n\tECDHProfiles,\n\tFLiRS2WakeUpTime,\n\ttype FirmwareUpdateOptions,\n\ttype FirmwareUpdateResult,\n\tInclusionControllerCCComplete,\n\tInclusionControllerCCInitiate,\n\tInclusionControllerStatus,\n\tInclusionControllerStep,\n\tKEXFailType,\n\tKEXSchemes,\n\tManufacturerSpecificCCValues,\n\tMultiChannelAssociationCC,\n\tPowerlevel,\n\tSecurity2CCKEXFail,\n\tSecurity2CCKEXGet,\n\ttype Security2CCKEXReport,\n\tSecurity2CCKEXSet,\n\tSecurity2CCNetworkKeyGet,\n\tSecurity2CCNetworkKeyReport,\n\tSecurity2CCNetworkKeyVerify,\n\tSecurity2CCPublicKeyReport,\n\tSecurity2CCTransferEnd,\n\tSecurity2Command,\n\tSecurityCCNetworkKeySet,\n\tSecurityCCNonceGet,\n\tSecurityCCSchemeGet,\n\tSecurityCCSchemeInherit,\n\tVersionCCValues,\n\tVersionCommand,\n\tZWaveProtocolCCAssignReturnRoute,\n\tZWaveProtocolCCAssignReturnRoutePriority,\n\tZWaveProtocolCCAssignSUCReturnRoute,\n\tZWaveProtocolCCAssignSUCReturnRoutePriority,\n\tinclusionTimeouts,\n\tutils as ccUtils,\n} from \"@zwave-js/cc\";\nimport { type IndicatorObject } from \"@zwave-js/cc/IndicatorCC\";\nimport {\n\tBasicDeviceClass,\n\ttype CCId,\n\tCommandClasses,\n\ttype ControllerCapabilities,\n\tControllerRole,\n\tControllerStatus,\n\tEMPTY_ROUTE,\n\ttype Firmware,\n\tLongRangeChannel,\n\tMAX_NODES,\n\ttype MaybeNotKnown,\n\ttype MaybeUnknown,\n\tNODE_ID_BROADCAST,\n\tNODE_ID_BROADCAST_LR,\n\tNOT_KNOWN,\n\tNodeIDType,\n\tNodeType,\n\ttype ProtocolDataRate,\n\tProtocolType,\n\tProtocols,\n\tRFRegion,\n\ttype RFRegionInfo,\n\ttype RSSI,\n\ttype Route,\n\tRouteKind,\n\tSecurityClass,\n\tSecurityManager,\n\tSecurityManager2,\n\ttype SerialApiInitData,\n\ttype SinglecastCC,\n\tTransmitStatus,\n\tUNKNOWN_STATE,\n\ttype UnknownZWaveChipType,\n\tValueDB,\n\ttype ZWaveApiVersion,\n\ttype ZWaveDataRate,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tZWaveLibraryTypes,\n\tauthHomeIdFromDSK,\n\taverageRSSI,\n\tcomputePRKAsync,\n\tderiveTempKeysAsync,\n\tdskFromString,\n\tdskToString,\n\textractRawECDHPublicKeySync,\n\tgenerateECDHKeyPairSync,\n\tgetChipTypeAndVersion,\n\tgetHighestSecurityClass,\n\timportRawECDHPublicKeySync,\n\tindexDBsByNode,\n\tisEmptyRoute,\n\tisLongRangeNodeId,\n\tisValidDSK,\n\tisZWaveError,\n\tnwiHomeIdFromDSK,\n\tparseBitMask,\n\tsdkVersionGt,\n\tsdkVersionGte,\n\tsdkVersionLt,\n\tsdkVersionLte,\n\tsecurityClassIsS2,\n\tsecurityClassOrder,\n} from \"@zwave-js/core\";\nimport {\n\tBufferedNVMReader,\n\tNVM3,\n\tNVM3Adapter,\n\tNVM500,\n\tNVM500Adapter,\n\ttype NVMAdapter,\n\tmigrateNVM,\n} from \"@zwave-js/nvmedit\";\nimport {\n\ttype BootloaderChunk,\n\tBootloaderChunkType,\n\tFunctionType,\n\ttype Message,\n\ttype SuccessIndicator,\n\tXModemMessageHeaders,\n} from \"@zwave-js/serial\";\nimport {\n\ttype ApplicationUpdateRequest,\n\tApplicationUpdateRequestNodeAdded,\n\tApplicationUpdateRequestNodeInfoReceived,\n\tApplicationUpdateRequestNodeRemoved,\n\tApplicationUpdateRequestSUCIdChanged,\n\tApplicationUpdateRequestSmartStartHomeIDReceived,\n\tApplicationUpdateRequestSmartStartLongRangeHomeIDReceived,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tShutdownRequest,\n\ttype ShutdownResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetControllerCapabilitiesRequest,\n\ttype GetControllerCapabilitiesResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetControllerVersionRequest,\n\ttype GetControllerVersionResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetLongRangeNodesRequest,\n\ttype GetLongRangeNodesResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetProtocolVersionRequest,\n\ttype GetProtocolVersionResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetSerialApiCapabilitiesRequest,\n\ttype GetSerialApiCapabilitiesResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetSerialApiInitDataRequest,\n\ttype GetSerialApiInitDataResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport { HardResetRequest } from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetLongRangeChannelRequest,\n\ttype GetLongRangeChannelResponse,\n\tSetLongRangeChannelRequest,\n\ttype SetLongRangeChannelResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tSerialAPISetupCommand,\n\tSerialAPISetup_CommandUnsupportedResponse,\n\tSerialAPISetup_GetLongRangeMaximumPayloadSizeRequest,\n\ttype SerialAPISetup_GetLongRangeMaximumPayloadSizeResponse,\n\tSerialAPISetup_GetLongRangeMaximumTxPowerRequest,\n\ttype SerialAPISetup_GetLongRangeMaximumTxPowerResponse,\n\tSerialAPISetup_GetMaximumPayloadSizeRequest,\n\ttype SerialAPISetup_GetMaximumPayloadSizeResponse,\n\tSerialAPISetup_GetPowerlevel16BitRequest,\n\ttype SerialAPISetup_GetPowerlevel16BitResponse,\n\tSerialAPISetup_GetPowerlevelRequest,\n\ttype SerialAPISetup_GetPowerlevelResponse,\n\tSerialAPISetup_GetRFRegionRequest,\n\ttype SerialAPISetup_GetRFRegionResponse,\n\tSerialAPISetup_GetRegionInfoRequest,\n\ttype SerialAPISetup_GetRegionInfoResponse,\n\tSerialAPISetup_GetSupportedCommandsRequest,\n\ttype SerialAPISetup_GetSupportedCommandsResponse,\n\tSerialAPISetup_GetSupportedRegionsRequest,\n\ttype SerialAPISetup_GetSupportedRegionsResponse,\n\tSerialAPISetup_SetLongRangeMaximumTxPowerRequest,\n\ttype SerialAPISetup_SetLongRangeMaximumTxPowerResponse,\n\tSerialAPISetup_SetNodeIDTypeRequest,\n\ttype SerialAPISetup_SetNodeIDTypeResponse,\n\tSerialAPISetup_SetPowerlevel16BitRequest,\n\ttype SerialAPISetup_SetPowerlevel16BitResponse,\n\tSerialAPISetup_SetPowerlevelRequest,\n\ttype SerialAPISetup_SetPowerlevelResponse,\n\tSerialAPISetup_SetRFRegionRequest,\n\ttype SerialAPISetup_SetRFRegionResponse,\n\tSerialAPISetup_SetTXStatusReportRequest,\n\ttype SerialAPISetup_SetTXStatusReportResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport { SetApplicationNodeInformationRequest } from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetControllerIdRequest,\n\ttype GetControllerIdResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetBackgroundRSSIRequest,\n\ttype GetBackgroundRSSIResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tSetRFReceiveModeRequest,\n\ttype SetRFReceiveModeResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tSetSerialApiTimeoutsRequest,\n\ttype SetSerialApiTimeoutsResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tStartWatchdogRequest,\n\tStopWatchdogRequest,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tAddNodeDSKToNetworkRequest,\n\tAddNodeStatus,\n\tAddNodeToNetworkRequest,\n\ttype AddNodeToNetworkRequestStatusReport,\n\tAddNodeType,\n\tEnableSmartStartListenRequest,\n\tcomputeNeighborDiscoveryTimeout,\n} from \"@zwave-js/serial/serialapi\";\nimport { AssignPriorityReturnRouteRequest } from \"@zwave-js/serial/serialapi\";\nimport {\n\tAssignPrioritySUCReturnRouteRequest,\n\ttype AssignPrioritySUCReturnRouteRequestTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tAssignReturnRouteRequest,\n\ttype AssignReturnRouteRequestTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tAssignSUCReturnRouteRequest,\n\tAssignSUCReturnRouteRequestTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tDeleteReturnRouteRequest,\n\ttype DeleteReturnRouteRequestTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tDeleteSUCReturnRouteRequest,\n\tDeleteSUCReturnRouteRequestTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetPriorityRouteRequest,\n\ttype GetPriorityRouteResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetRoutingInfoRequest,\n\ttype GetRoutingInfoResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetSUCNodeIdRequest,\n\ttype GetSUCNodeIdResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tIsFailedNodeRequest,\n\ttype IsFailedNodeResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tRemoveFailedNodeRequest,\n\ttype RemoveFailedNodeRequestStatusReport,\n\tRemoveFailedNodeResponse,\n\tRemoveFailedNodeStartFlags,\n\tRemoveFailedNodeStatus,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tRemoveNodeFromNetworkRequest,\n\ttype RemoveNodeFromNetworkRequestStatusReport,\n\tRemoveNodeStatus,\n\tRemoveNodeType,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tReplaceFailedNodeRequest,\n\ttype ReplaceFailedNodeRequestStatusReport,\n\ttype ReplaceFailedNodeResponse,\n\tReplaceFailedNodeStartFlags,\n\tReplaceFailedNodeStatus,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tNodeNeighborUpdateStatus,\n\ttype RequestNodeNeighborUpdateReport,\n\tRequestNodeNeighborUpdateRequest,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tLearnModeIntent,\n\tLearnModeStatus,\n\ttype SetLearnModeCallback,\n\tSetLearnModeRequest,\n} from \"@zwave-js/serial/serialapi\";\nimport { SetPriorityRouteRequest } from \"@zwave-js/serial/serialapi\";\nimport { SetSUCNodeIdRequest } from \"@zwave-js/serial/serialapi\";\nimport {\n\tExtNVMReadLongBufferRequest,\n\ttype ExtNVMReadLongBufferResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tExtNVMReadLongByteRequest,\n\ttype ExtNVMReadLongByteResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tExtNVMWriteLongBufferRequest,\n\ttype ExtNVMWriteLongBufferResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tExtNVMWriteLongByteRequest,\n\ttype ExtNVMWriteLongByteResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tExtendedNVMOperationStatus,\n\tExtendedNVMOperationsCloseRequest,\n\tExtendedNVMOperationsCommand,\n\tExtendedNVMOperationsOpenRequest,\n\tExtendedNVMOperationsReadRequest,\n\ttype ExtendedNVMOperationsResponse,\n\tExtendedNVMOperationsWriteRequest,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tFirmwareUpdateNVM_GetNewImageRequest,\n\ttype FirmwareUpdateNVM_GetNewImageResponse,\n\tFirmwareUpdateNVM_InitRequest,\n\ttype FirmwareUpdateNVM_InitResponse,\n\tFirmwareUpdateNVM_IsValidCRC16Request,\n\ttype FirmwareUpdateNVM_IsValidCRC16Response,\n\tFirmwareUpdateNVM_SetNewImageRequest,\n\ttype FirmwareUpdateNVM_SetNewImageResponse,\n\tFirmwareUpdateNVM_UpdateCRC16Request,\n\ttype FirmwareUpdateNVM_UpdateCRC16Response,\n\tFirmwareUpdateNVM_WriteRequest,\n\ttype FirmwareUpdateNVM_WriteResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetNVMIdRequest,\n\ttype GetNVMIdResponse,\n\ttype NVMId,\n\tnvmSizeToBufferSize,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tNVMOperationStatus,\n\tNVMOperationsCloseRequest,\n\tNVMOperationsOpenRequest,\n\tNVMOperationsReadRequest,\n\ttype NVMOperationsResponse,\n\tNVMOperationsWriteRequest,\n} from \"@zwave-js/serial/serialapi\";\nimport type { TransmitReport } from \"@zwave-js/serial/serialapi\";\nimport {\n\tBytes,\n\tMixin,\n\ttype ReadonlyObjectKeyMap,\n\ttype ReadonlyThrowingMap,\n\ttype ThrowingMap,\n\tTypedEventEmitter,\n\tareUint8ArraysEqual,\n\tcloneDeep,\n\tcreateThrowingMap,\n\tflatMap,\n\tgetEnumMemberName,\n\tgetErrorMessage,\n\tnoop,\n\tnum2hex,\n\tpick,\n} from \"@zwave-js/shared\";\nimport { distinct } from \"alcalzone-shared/arrays\";\nimport { wait } from \"alcalzone-shared/async\";\nimport {\n\ttype DeferredPromise,\n\tcreateDeferredPromise,\n} from \"alcalzone-shared/deferred-promise\";\nimport { roundTo } from \"alcalzone-shared/math\";\nimport { isObject } from \"alcalzone-shared/typeguards\";\nimport crypto from \"node:crypto\";\nimport type { Driver } from \"../driver/Driver.js\";\nimport { cacheKeyUtils, cacheKeys } from \"../driver/NetworkCache.js\";\nimport type { StatisticsEventCallbacks } from \"../driver/Statistics.js\";\nimport { type TaskBuilder, TaskPriority } from \"../driver/Task.js\";\nimport { DeviceClass } from \"../node/DeviceClass.js\";\nimport { ZWaveNode } from \"../node/Node.js\";\nimport { VirtualNode } from \"../node/VirtualNode.js\";\nimport {\n\tInterviewStage,\n\ttype LifelineRoutes,\n\tNodeStatus,\n} from \"../node/_Types.js\";\nimport {\n\ttype ControllerStatistics,\n\tControllerStatisticsHost,\n} from \"./ControllerStatistics.js\";\nimport { ZWaveFeature, minFeatureVersions } from \"./Features.js\";\nimport {\n\tdownloadFirmwareUpdate,\n\tgetAvailableFirmwareUpdates,\n} from \"./FirmwareUpdateService.js\";\nimport {\n\ttype ExclusionOptions,\n\tExclusionStrategy,\n\ttype FoundNode,\n\ttype InclusionGrant,\n\ttype InclusionOptions,\n\ttype InclusionOptionsInternal,\n\ttype InclusionResult,\n\tInclusionState,\n\tInclusionStrategy,\n\ttype InclusionUserCallbacks,\n\ttype JoinNetworkOptions,\n\tJoinNetworkResult,\n\tJoinNetworkStrategy,\n\ttype JoinNetworkUserCallbacks,\n\tLeaveNetworkResult,\n\ttype PlannedProvisioningEntry,\n\tProvisioningEntryStatus,\n\tRemoveNodeReason,\n\ttype ReplaceNodeOptions,\n\tSecurityBootstrapFailure,\n\ttype SmartStartProvisioningEntry,\n} from \"./Inclusion.js\";\nimport { SerialNVMIO500, SerialNVMIO700 } from \"./NVMIO.js\";\nimport { determineNIF } from \"./NodeInformationFrame.js\";\nimport { protocolVersionToSDKVersion } from \"./ZWaveSDKVersions.js\";\nimport {\n\ttype ControllerFirmwareUpdateProgress,\n\ttype ControllerFirmwareUpdateResult,\n\tControllerFirmwareUpdateStatus,\n\ttype FirmwareUpdateDeviceID,\n\ttype FirmwareUpdateInfo,\n\ttype GetFirmwareUpdatesOptions,\n\ttype RebuildRoutesOptions,\n\ttype RebuildRoutesStatus,\n\ttype SDKVersion,\n} from \"./_Types.js\";\nimport { assertProvisioningEntry, isRebuildRoutesTask } from \"./utils.js\";\n\n// Strongly type the event emitter events\ninterface ControllerEventCallbacks\n\textends StatisticsEventCallbacks<ControllerStatistics>\n{\n\t\"inclusion failed\": () => void;\n\t\"exclusion failed\": () => void;\n\t\"inclusion started\": (strategy: InclusionStrategy) => void;\n\t\"exclusion started\": () => void;\n\t\"inclusion stopped\": () => void;\n\t\"exclusion stopped\": () => void;\n\t\"inclusion state changed\": (state: InclusionState) => void;\n\t\"node found\": (node: FoundNode) => void;\n\t\"node added\": (node: ZWaveNode, result: InclusionResult) => void;\n\t\"node removed\": (node: ZWaveNode, reason: RemoveNodeReason) => void;\n\t\"network found\": (homeId: number, ownNodeId: number) => void;\n\t\"joining network failed\": () => void;\n\t\"network joined\": () => void;\n\t\"network left\": () => void;\n\t\"leaving network failed\": () => void;\n\t\"rebuild routes progress\": (\n\t\tprogress: ReadonlyMap<number, RebuildRoutesStatus>,\n\t) => void;\n\t\"rebuild routes done\": (\n\t\tresult: ReadonlyMap<number, RebuildRoutesStatus>,\n\t) => void;\n\t\"firmware update progress\": (\n\t\tprogress: ControllerFirmwareUpdateProgress,\n\t) => void;\n\t\"firmware update finished\": (\n\t\tresult: ControllerFirmwareUpdateResult,\n\t) => void;\n\tidentify: (node: ZWaveNode) => void;\n\t\"status changed\": (status: ControllerStatus) => void;\n}\n\nexport type ControllerEvents = Extract<keyof ControllerEventCallbacks, string>;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ZWaveController extends ControllerStatisticsHost {}\n\n@Mixin([ControllerStatisticsHost])\nexport class ZWaveController\n\textends TypedEventEmitter<ControllerEventCallbacks>\n{\n\t/** @internal */\n\tpublic constructor(\n\t\tprivate readonly driver: Driver,\n\t\tbootloaderOnly: boolean = false,\n\t) {\n\t\tsuper();\n\n\t\tthis._nodes = createThrowingMap((nodeId) => {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Node ${nodeId} was not found!`,\n\t\t\t\tZWaveErrorCodes.Controller_NodeNotFound,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t});\n\n\t\t// Limit interaction with the controller in bootloader-only mode\n\t\tif (bootloaderOnly) return;\n\n\t\t// register message handlers\n\t\tdriver.registerRequestHandler(\n\t\t\tFunctionType.AddNodeToNetwork,\n\t\t\tthis.handleAddNodeStatusReport.bind(this),\n\t\t);\n\t\tdriver.registerRequestHandler(\n\t\t\tFunctionType.RemoveNodeFromNetwork,\n\t\t\tthis.handleRemoveNodeStatusReport.bind(this),\n\t\t);\n\t\tdriver.registerRequestHandler(\n\t\t\tFunctionType.ReplaceFailedNode,\n\t\t\tthis.handleReplaceNodeStatusReport.bind(this),\n\t\t);\n\t\tdriver.registerRequestHandler(\n\t\t\tFunctionType.SetLearnMode,\n\t\t\tthis.handleLearnModeCallback.bind(this),\n\t\t);\n\t}\n\n\tprivate _type: MaybeNotKnown<ZWaveLibraryTypes>;\n\tpublic get type(): MaybeNotKnown<ZWaveLibraryTypes> {\n\t\treturn this._type;\n\t}\n\n\tprivate _protocolVersion: MaybeNotKnown<string>;\n\tpublic get protocolVersion(): MaybeNotKnown<string> {\n\t\treturn this._protocolVersion;\n\t}\n\n\tprivate _sdkVersion: MaybeNotKnown<string>;\n\tpublic get sdkVersion(): MaybeNotKnown<string> {\n\t\treturn this._sdkVersion;\n\t}\n\n\tprivate _zwaveApiVersion: MaybeNotKnown<ZWaveApiVersion>;\n\tpublic get zwaveApiVersion(): MaybeNotKnown<ZWaveApiVersion> {\n\t\treturn this._zwaveApiVersion;\n\t}\n\n\tprivate _zwaveChipType: MaybeNotKnown<string | UnknownZWaveChipType>;\n\tpublic get zwaveChipType(): MaybeNotKnown<string | UnknownZWaveChipType> {\n\t\treturn this._zwaveChipType;\n\t}\n\n\tprivate _homeId: MaybeNotKnown<number>;\n\t/** A 32bit number identifying the current network */\n\tpublic get homeId(): MaybeNotKnown<number> {\n\t\treturn this._homeId;\n\t}\n\n\tprivate _ownNodeId: MaybeNotKnown<number>;\n\t/** The ID of the controller in the current network */\n\tpublic get ownNodeId(): MaybeNotKnown<number> {\n\t\treturn this._ownNodeId;\n\t}\n\n\tprivate _dsk: Uint8Array | undefined;\n\t/**\n\t * The device specific key (DSK) of the controller in binary format.\n\t */\n\tpublic get dsk(): Uint8Array {\n\t\tif (this._dsk == undefined) {\n\t\t\tconst keyPair = this.driver.getLearnModeAuthenticatedKeyPair();\n\t\t\tconst publicKey = extractRawECDHPublicKeySync(keyPair.publicKey);\n\t\t\tthis._dsk = publicKey.subarray(0, 16);\n\t\t}\n\t\treturn this._dsk;\n\t}\n\n\t/** @deprecated Use {@link role} instead */\n\tpublic get isPrimary(): MaybeNotKnown<boolean> {\n\t\tswitch (this.role) {\n\t\t\tcase NOT_KNOWN:\n\t\t\t\treturn NOT_KNOWN;\n\t\t\tcase ControllerRole.Primary:\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\t// This seems odd, but isPrimary comes from the Serial API init data command,\n\t// while isSecondary comes from the GetControllerCapabilities command. They don't really do what their name implies\n\t// and sometimes contradict each other...\n\tprivate _isPrimary: MaybeNotKnown<boolean>;\n\tprivate _isSecondary: MaybeNotKnown<boolean>;\n\n\tprivate _isUsingHomeIdFromOtherNetwork: MaybeNotKnown<boolean>;\n\t/** @deprecated Use {@link role} instead */\n\tpublic get isUsingHomeIdFromOtherNetwork(): MaybeNotKnown<boolean> {\n\t\treturn this._isUsingHomeIdFromOtherNetwork;\n\t}\n\n\tprivate _isSISPresent: MaybeNotKnown<boolean>;\n\tpublic get isSISPresent(): MaybeNotKnown<boolean> {\n\t\treturn this._isSISPresent;\n\t}\n\n\tprivate _wasRealPrimary: MaybeNotKnown<boolean>;\n\t/** @deprecated Use {@link role} instead */\n\tpublic get wasRealPrimary(): MaybeNotKnown<boolean> {\n\t\treturn this._wasRealPrimary;\n\t}\n\n\tprivate _isSIS: MaybeNotKnown<boolean>;\n\tpublic get isSIS(): MaybeNotKnown<boolean> {\n\t\treturn this._isSIS;\n\t}\n\n\tprivate _isSUC: MaybeNotKnown<boolean>;\n\tpublic get isSUC(): MaybeNotKnown<boolean> {\n\t\treturn this._isSUC;\n\t}\n\n\tprivate _noNodesIncluded: MaybeNotKnown<boolean>;\n\n\tprivate _nodeType: MaybeNotKnown<NodeType>;\n\tpublic get nodeType(): MaybeNotKnown<NodeType> {\n\t\treturn this._nodeType;\n\t}\n\n\t/** Checks if the SDK version is greater than the given one */\n\tpublic sdkVersionGt(version: SDKVersion): MaybeNotKnown<boolean> {\n\t\treturn sdkVersionGt(this._sdkVersion, version);\n\t}\n\n\t/** Checks if the SDK version is greater than or equal to the given one */\n\tpublic sdkVersionGte(version: SDKVersion): MaybeNotKnown<boolean> {\n\t\treturn sdkVersionGte(this._sdkVersion, version);\n\t}\n\n\t/** Checks if the SDK version is lower than the given one */\n\tpublic sdkVersionLt(version: SDKVersion): MaybeNotKnown<boolean> {\n\t\treturn sdkVersionLt(this._sdkVersion, version);\n\t}\n\n\t/** Checks if the SDK version is lower than or equal to the given one */\n\tpublic sdkVersionLte(version: SDKVersion): MaybeNotKnown<boolean> {\n\t\treturn sdkVersionLte(this._sdkVersion, version);\n\t}\n\n\tprivate _manufacturerId: MaybeNotKnown<number>;\n\tpublic get manufacturerId(): MaybeNotKnown<number> {\n\t\treturn this._manufacturerId;\n\t}\n\n\tprivate _productType: MaybeNotKnown<number>;\n\tpublic get productType(): MaybeNotKnown<number> {\n\t\treturn this._productType;\n\t}\n\n\tprivate _productId: MaybeNotKnown<number>;\n\tpublic get productId(): MaybeNotKnown<number> {\n\t\treturn this._productId;\n\t}\n\n\tprivate _firmwareVersion: MaybeNotKnown<string>;\n\tpublic get firmwareVersion(): MaybeNotKnown<string> {\n\t\treturn this._firmwareVersion;\n\t}\n\n\tprivate _supportedFunctionTypes: MaybeNotKnown<FunctionType[]>;\n\tpublic get supportedFunctionTypes(): MaybeNotKnown<\n\t\treadonly FunctionType[]\n\t> {\n\t\treturn this._supportedFunctionTypes;\n\t}\n\n\tprivate _status: ControllerStatus = ControllerStatus.Ready;\n\t/**\n\t * Which status the controller is believed to be in\n\t */\n\tpublic get status(): ControllerStatus {\n\t\treturn this._status;\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tpublic setStatus(newStatus: ControllerStatus): void {\n\t\t// Ignore duplicate events\n\t\tif (newStatus === this._status) return;\n\n\t\tconst oldStatus = this._status;\n\t\tthis._status = newStatus;\n\n\t\tif (newStatus === ControllerStatus.Jammed) {\n\t\t\tthis.driver.controllerLog.print(\"The controller is jammed\", \"warn\");\n\t\t} else if (newStatus === ControllerStatus.Unresponsive) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\"The controller is unresponsive\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t} else if (newStatus === ControllerStatus.Ready) {\n\t\t\tif (oldStatus === ControllerStatus.Jammed) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"The controller is no longer jammed\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t} else if (oldStatus === ControllerStatus.Unresponsive) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"The controller is no longer unresponsive\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\"The controller is ready\");\n\t\t\t}\n\t\t}\n\n\t\tthis.emit(\"status changed\", newStatus);\n\t}\n\n\t/**\n\t * Checks if a given Z-Wave function type is supported by this controller.\n\t * Returns `NOT_KNOWN`/`undefined` if this information isn't known yet.\n\t */\n\tpublic isFunctionSupported(\n\t\tfunctionType: FunctionType,\n\t): MaybeNotKnown<boolean> {\n\t\tif (!this._supportedFunctionTypes) return NOT_KNOWN;\n\t\treturn this._supportedFunctionTypes.includes(functionType);\n\t}\n\n\tprivate _supportedSerialAPISetupCommands:\n\t\t| SerialAPISetupCommand[]\n\t\t| undefined;\n\tpublic get supportedSerialAPISetupCommands():\n\t\t| readonly SerialAPISetupCommand[]\n\t\t| undefined\n\t{\n\t\treturn this._supportedSerialAPISetupCommands;\n\t}\n\n\t/**\n\t * Checks if a given Serial API setup command is supported by this controller.\n\t * Returns `NOT_KNOWN`/`undefined` if this information isn't known yet.\n\t */\n\tpublic isSerialAPISetupCommandSupported(\n\t\tcommand: SerialAPISetupCommand,\n\t): MaybeNotKnown<boolean> {\n\t\tif (!this._supportedSerialAPISetupCommands) return NOT_KNOWN;\n\t\treturn this._supportedSerialAPISetupCommands.includes(command);\n\t}\n\n\t/**\n\t * Tests if the controller supports a certain feature.\n\t * Returns `undefined` if this information isn't known yet.\n\t */\n\tpublic supportsFeature(feature: ZWaveFeature): MaybeNotKnown<boolean> {\n\t\tswitch (feature) {\n\t\t\tcase ZWaveFeature.SmartStart:\n\t\t\t\treturn this.sdkVersionGte(minFeatureVersions[feature]);\n\t\t}\n\t}\n\n\t/** Throws if the controller does not support a certain feature */\n\tprivate assertFeature(feature: ZWaveFeature): void {\n\t\tif (!this.supportsFeature(feature)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The controller does not support the ${\n\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\tZWaveFeature,\n\t\t\t\t\t\tfeature,\n\t\t\t\t\t)\n\t\t\t\t} feature`,\n\t\t\t\tZWaveErrorCodes.Controller_NotSupported,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate _sucNodeId: MaybeNotKnown<number>;\n\tpublic get sucNodeId(): MaybeNotKnown<number> {\n\t\treturn this._sucNodeId;\n\t}\n\n\tprivate _supportsTimers: MaybeNotKnown<boolean>;\n\tpublic get supportsTimers(): MaybeNotKnown<boolean> {\n\t\treturn this._supportsTimers;\n\t}\n\n\tprivate _supportedRegions: MaybeNotKnown<Map<RFRegion, RFRegionInfo>>;\n\t/** Which RF regions are supported by the controller, including information about them */\n\tpublic get supportedRegions(): MaybeNotKnown<\n\t\tReadonlyMap<RFRegion, Readonly<RFRegionInfo>>\n\t> {\n\t\treturn this._supportedRegions;\n\t}\n\n\tprivate _rfRegion: MaybeNotKnown<RFRegion>;\n\t/** Which RF region the controller is currently set to, or `undefined` if it could not be determined (yet). This value is cached and can be changed through {@link setRFRegion}. */\n\tpublic get rfRegion(): MaybeNotKnown<RFRegion> {\n\t\treturn this._rfRegion;\n\t}\n\n\tprivate _supportsLongRange: MaybeNotKnown<boolean>;\n\t/** Whether the controller supports the Z-Wave Long Range protocol */\n\tpublic get supportsLongRange(): MaybeNotKnown<boolean> {\n\t\treturn this._supportsLongRange;\n\t}\n\n\tprivate _maxLongRangePowerlevel: MaybeNotKnown<number>;\n\t/** The maximum powerlevel to use for Z-Wave Long Range, or `undefined` if it could not be determined (yet). This value is cached and can be changed through {@link setMaxLongRangePowerlevel}. */\n\tpublic get maxLongRangePowerlevel(): MaybeNotKnown<number> {\n\t\treturn this._maxLongRangePowerlevel;\n\t}\n\n\tprivate _longRangeChannel: MaybeNotKnown<LongRangeChannel>;\n\t/** The channel to use for Z-Wave Long Range, or `undefined` if it could not be determined (yet). This value is cached and can be changed through {@link setLongRangeChannel}. */\n\tpublic get longRangeChannel(): MaybeNotKnown<LongRangeChannel> {\n\t\treturn this._longRangeChannel;\n\t}\n\n\tprivate _supportsLongRangeAutoChannelSelection: MaybeNotKnown<boolean>;\n\t/** Whether automatic LR channel selection is supported, or `undefined` if it could not be determined (yet). */\n\tpublic get supportsLongRangeAutoChannelSelection(): MaybeNotKnown<boolean> {\n\t\treturn this._supportsLongRangeAutoChannelSelection;\n\t}\n\n\tprivate _maxPayloadSize: MaybeNotKnown<number>;\n\t/** The maximum payload size that can be transmitted with a Z-Wave explorer frame */\n\tpublic get maxPayloadSize(): MaybeNotKnown<number> {\n\t\treturn this._maxPayloadSize;\n\t}\n\n\tprivate _maxPayloadSizeLR: MaybeNotKnown<number>;\n\t/** The maximum payload size that can be transmitted with a Z-Wave Long Range frame */\n\tpublic get maxPayloadSizeLR(): MaybeNotKnown<number> {\n\t\treturn this._maxPayloadSizeLR;\n\t}\n\n\tprivate _nodes: ThrowingMap<number, ZWaveNode>;\n\t/** A dictionary of the nodes connected to this controller */\n\tpublic get nodes(): ReadonlyThrowingMap<number, ZWaveNode> {\n\t\treturn this._nodes;\n\t}\n\n\tprivate _nodeIdType: NodeIDType = NodeIDType.Short;\n\t/** Whether the controller is configured to use 8 or 16 bit node IDs */\n\tpublic get nodeIdType(): NodeIDType {\n\t\treturn this._nodeIdType;\n\t}\n\t/** @internal */\n\tpublic set nodeIdType(value: NodeIDType) {\n\t\tthis._nodeIdType = value;\n\t}\n\n\t/** Returns the node with the given DSK */\n\tpublic getNodeByDSK(dsk: Uint8Array | string): ZWaveNode | undefined {\n\t\ttry {\n\t\t\tif (typeof dsk === \"string\") dsk = dskFromString(dsk);\n\t\t} catch (e) {\n\t\t\t// Return undefined if the DSK is invalid\n\t\t\tif (\n\t\t\t\tisZWaveError(e) && e.code === ZWaveErrorCodes.Argument_Invalid\n\t\t\t) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t\tfor (const node of this._nodes.values()) {\n\t\t\tif (node.dsk && Bytes.view(node.dsk).equals(dsk)) return node;\n\t\t}\n\t}\n\n\t/** Returns the controller node's value DB */\n\tpublic get valueDB(): ValueDB {\n\t\treturn this._nodes.get(this._ownNodeId!)!.valueDB;\n\t}\n\n\t/** @internal Which associations are currently configured */\n\tpublic get associations(): readonly AssociationAddress[] {\n\t\treturn (\n\t\t\tthis.driver.cacheGet(cacheKeys.controller.associations(1)) ?? []\n\t\t);\n\t}\n\n\t/** @internal */\n\tpublic set associations(value: readonly AssociationAddress[]) {\n\t\tthis.driver.cacheSet(cacheKeys.controller.associations(1), value);\n\t}\n\n\tprivate _powerlevel: { powerlevel: Powerlevel; until: Date } | undefined;\n\t/**\n\t * @internal\n\t * Remembers which powerlevel was set by another node.\n\t */\n\tpublic get powerlevel(): { powerlevel: Powerlevel; until: Date } {\n\t\treturn this._powerlevel ?? {\n\t\t\tpowerlevel: Powerlevel[\"Normal Power\"],\n\t\t\tuntil: new Date(),\n\t\t};\n\t}\n\n\t/** @internal */\n\tpublic set powerlevel(value: { powerlevel: Powerlevel; until: Date }) {\n\t\tthis._powerlevel = value;\n\t}\n\n\t/** The role of the controller on the network */\n\tpublic get role(): MaybeNotKnown<ControllerRole> {\n\t\tif (this._wasRealPrimary) return ControllerRole.Primary;\n\t\t// Ideally we'd rely on wasRealPrimary, but there are some older controllers out there where this flag isn't set.\n\t\tif (this._isPrimary && this._isSIS && this._isSecondary === false) {\n\t\t\treturn ControllerRole.Primary;\n\t\t}\n\n\t\tswitch (this._isSecondary) {\n\t\t\tcase true:\n\t\t\t\treturn ControllerRole.Secondary;\n\t\t\tcase false:\n\t\t\t\treturn ControllerRole.Inclusion;\n\t\t\tdefault:\n\t\t\t\treturn NOT_KNOWN;\n\t\t}\n\t}\n\n\t/** Returns whether learn mode may be enabled on this controller */\n\tpublic get isLearnModePermitted(): boolean {\n\t\t// The primary controller may only enter learn mode, if hasn't included nodes yet\n\t\tif (this.role === ControllerRole.Primary) {\n\t\t\treturn !!this._noNodesIncluded;\n\t\t} else {\n\t\t\t// Secondary controllers may only enter learn mode if they are not the SUC\n\t\t\treturn this._isSUC === false;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Remembers the indicator values set by another node\n\t */\n\tpublic readonly indicatorValues = new Map<number, IndicatorObject[]>();\n\n\t/** Returns whether the routes are currently being rebuilt for one or more nodes. */\n\tpublic get isRebuildingRoutes(): boolean {\n\t\treturn !!this.driver.scheduler.findTask(isRebuildRoutesTask);\n\t}\n\n\t/**\n\t * Returns a reference to the (virtual) broadcast node, which allows sending commands to all nodes.\n\t * This automatically groups nodes by security class, ignores nodes that cannot be controlled via multicast/broadcast, and will fall back to multicast(s) if necessary.\n\t */\n\tpublic getBroadcastNode(): VirtualNode {\n\t\treturn new VirtualNode(\n\t\t\tNODE_ID_BROADCAST,\n\t\t\tthis.driver,\n\t\t\tthis.nodes.values(),\n\t\t);\n\t}\n\n\t/**\n\t * Returns a reference to the (virtual) broadcast node for Z-Wave Long Range, which allows sending commands to all LR nodes.\n\t * This automatically groups nodes by security class, ignores nodes that cannot be controlled via multicast/broadcast, and will fall back to multicast(s) if necessary.\n\t */\n\tpublic getBroadcastNodeLR(): VirtualNode {\n\t\treturn new VirtualNode(\n\t\t\tNODE_ID_BROADCAST_LR,\n\t\t\tthis.driver,\n\t\t\tthis.nodes.values(),\n\t\t);\n\t}\n\n\t/**\n\t * Creates a virtual node that can be used to send one or more multicast commands to several nodes.\n\t * This automatically groups nodes by security class and ignores nodes that cannot be controlled via multicast.\n\t */\n\tpublic getMulticastGroup(nodeIDs: number[]): VirtualNode {\n\t\tif (nodeIDs.length === 0) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Cannot create an empty multicast group\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tconst firstNodeIsLR = isLongRangeNodeId(nodeIDs[0]);\n\t\tif (nodeIDs.some((id) => isLongRangeNodeId(id) !== firstNodeIsLR)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Cannot create a multicast group with mixed Z-Wave Classic and Z-Wave Long Range nodes\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tconst nodes = nodeIDs.map((id) => this._nodes.getOrThrow(id));\n\t\treturn new VirtualNode(undefined, this.driver, nodes);\n\t}\n\n\t/** @internal */\n\tpublic get provisioningList(): readonly SmartStartProvisioningEntry[] {\n\t\treturn (\n\t\t\tthis.driver.cacheGet(cacheKeys.controller.provisioningList) ?? []\n\t\t);\n\t}\n\tprivate set provisioningList(\n\t\tvalue: readonly SmartStartProvisioningEntry[],\n\t) {\n\t\tthis.driver.cacheSet(cacheKeys.controller.provisioningList, value);\n\t}\n\n\t/** Adds the given entry (DSK and security classes) to the controller's SmartStart provisioning list or replaces an existing entry */\n\tpublic provisionSmartStartNode(entry: PlannedProvisioningEntry): void {\n\t\t// Make sure the controller supports SmartStart\n\t\tthis.assertFeature(ZWaveFeature.SmartStart);\n\n\t\t// And that the entry contains valid data\n\t\tassertProvisioningEntry(entry);\n\n\t\tconst provisioningList = [...this.provisioningList];\n\t\tconst index = provisioningList.findIndex((e) => e.dsk === entry.dsk);\n\t\tif (index === -1) {\n\t\t\tprovisioningList.push(entry);\n\t\t} else {\n\t\t\tprovisioningList[index] = entry;\n\t\t}\n\t\tthis.provisioningList = provisioningList;\n\n\t\tthis.autoProvisionSmartStart();\n\t}\n\n\t/**\n\t * Removes the given DSK or node ID from the controller's SmartStart provisioning list.\n\t *\n\t * **Note:** If this entry corresponds to an included node, it will **NOT** be excluded\n\t */\n\tpublic unprovisionSmartStartNode(dskOrNodeId: string | number): void {\n\t\tconst provisioningList = [...this.provisioningList];\n\n\t\tconst entry = this.getProvisioningEntryInternal(dskOrNodeId);\n\t\tif (!entry) return;\n\n\t\tconst index = provisioningList.indexOf(entry);\n\t\tif (index >= 0) {\n\t\t\tprovisioningList.splice(index, 1);\n\t\t\tthis.provisioningList = provisioningList;\n\n\t\t\tthis.autoProvisionSmartStart();\n\t\t}\n\t}\n\n\tprivate getProvisioningEntryInternal(\n\t\tdskOrNodeId: string | number,\n\t): SmartStartProvisioningEntry | undefined {\n\t\tif (typeof dskOrNodeId === \"string\") {\n\t\t\treturn this.provisioningList.find((e) => e.dsk === dskOrNodeId);\n\t\t} else {\n\t\t\t// The provisioning list may or may not contain the node ID for an entry, even if the node is already included.\n\t\t\tlet ret = this.provisioningList.find(\n\t\t\t\t(e) => \"nodeId\" in e && e.nodeId === dskOrNodeId,\n\t\t\t);\n\t\t\tif (!ret) {\n\t\t\t\t// Try to get the DSK from the node instance\n\t\t\t\tconst dsk = this.nodes.get(dskOrNodeId)?.dsk;\n\t\t\t\tif (dsk) {\n\t\t\t\t\tret = this.provisioningList.find(\n\t\t\t\t\t\t(e) => e.dsk === dskToString(dsk),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the entry for the given DSK or node ID from the controller's SmartStart provisioning list.\n\t */\n\tpublic getProvisioningEntry(\n\t\tdskOrNodeId: string | number,\n\t): Readonly<SmartStartProvisioningEntry> | undefined {\n\t\tconst entry = this.getProvisioningEntryInternal(dskOrNodeId);\n\t\t// Try to look up the node ID for this entry\n\t\tif (entry) {\n\t\t\tconst ret: SmartStartProvisioningEntry = {\n\t\t\t\t...entry,\n\t\t\t};\n\t\t\tconst node = typeof dskOrNodeId === \"string\"\n\t\t\t\t? this.getNodeByDSK(dskOrNodeId)\n\t\t\t\t: this.nodes.get(dskOrNodeId);\n\t\t\tif (node) ret.nodeId = node.id;\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all entries from the controller's SmartStart provisioning list.\n\t */\n\tpublic getProvisioningEntries(): SmartStartProvisioningEntry[] {\n\t\t// Determine which DSKs belong to which node IDs\n\t\tconst dskNodeMap = new Map<string, number>();\n\t\tfor (const node of this.nodes.values()) {\n\t\t\tif (node.dsk) dskNodeMap.set(dskToString(node.dsk), node.id);\n\t\t}\n\t\t// Make copies so no one can modify the internal list (except for user info)\n\t\treturn this.provisioningList.map((e) => {\n\t\t\tconst { dsk, securityClasses, nodeId, ...rest } = e;\n\t\t\treturn {\n\t\t\t\tdsk,\n\t\t\t\tsecurityClasses: [...securityClasses],\n\t\t\t\t...(dskNodeMap.has(dsk)\n\t\t\t\t\t? { nodeId: dskNodeMap.get(dsk)! }\n\t\t\t\t\t: {}),\n\t\t\t\t...rest,\n\t\t\t};\n\t\t});\n\t}\n\n\t/** Returns whether the SmartStart provisioning list contains active entries that have not been included yet */\n\tpublic hasPlannedProvisioningEntries(): boolean {\n\t\treturn this.provisioningList.some(\n\t\t\t(e) =>\n\t\t\t\t(e.status == undefined\n\t\t\t\t\t|| e.status === ProvisioningEntryStatus.Active)\n\t\t\t\t&& !this.getNodeByDSK(e.dsk),\n\t\t);\n\t}\n\n\t/**\n\t * @internal\n\t * Automatically starts smart start inclusion if there are nodes pending inclusion.\n\t */\n\tpublic autoProvisionSmartStart(): void {\n\t\t// Make sure the controller supports SmartStart\n\t\tif (!this.supportsFeature(ZWaveFeature.SmartStart)) return;\n\n\t\tif (this.hasPlannedProvisioningEntries()) {\n\t\t\t// SmartStart should be enabled\n\t\t\tvoid this.enableSmartStart().catch(noop);\n\t\t} else {\n\t\t\t// SmartStart should be disabled\n\t\t\tvoid this.disableSmartStart().catch(noop);\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Queries the controller / serial API capabilities.\n\t * Returns a list of Z-Wave classic node IDs that are currently in the network.\n\t */\n\tpublic async queryCapabilities(): Promise<{ nodeIds: readonly number[] }> {\n\t\t// Figure out what the serial API can do\n\t\tthis.driver.controllerLog.print(`querying Serial API capabilities...`);\n\t\tconst apiCaps = await this.driver.sendMessage<\n\t\t\tGetSerialApiCapabilitiesResponse\n\t\t>(\n\t\t\tnew GetSerialApiCapabilitiesRequest(),\n\t\t\t{\n\t\t\t\tsupportCheck: false,\n\t\t\t},\n\t\t);\n\t\tthis._firmwareVersion = apiCaps.firmwareVersion;\n\t\tthis._manufacturerId = apiCaps.manufacturerId;\n\t\tthis._productType = apiCaps.productType;\n\t\tthis._productId = apiCaps.productId;\n\t\tthis._supportedFunctionTypes = apiCaps.supportedFunctionTypes;\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received API capabilities:\n firmware version: ${this._firmwareVersion}\n manufacturer ID: ${num2hex(this._manufacturerId)}\n product type: ${num2hex(this._productType)}\n product ID: ${num2hex(this._productId)}\n supported functions: ${\n\t\t\t\tthis._supportedFunctionTypes\n\t\t\t\t\t.map((fn) => `\\n \u00B7 ${FunctionType[fn]} (${num2hex(fn)})`)\n\t\t\t\t\t.join(\"\")\n\t\t\t}`,\n\t\t);\n\n\t\t// Request additional information about the controller/Z-Wave chip\n\t\tconst initData = await this.getSerialApiInitData();\n\n\t\t// Get basic controller version info\n\t\tthis.driver.controllerLog.print(`querying version info...`);\n\t\tconst version = await this.driver.sendMessage<\n\t\t\tGetControllerVersionResponse\n\t\t>(\n\t\t\tnew GetControllerVersionRequest(),\n\t\t\t{\n\t\t\t\tsupportCheck: false,\n\t\t\t},\n\t\t);\n\t\tthis._protocolVersion = version.libraryVersion;\n\t\tthis._type = version.controllerType;\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received version info:\n controller type: ${getEnumMemberName(ZWaveLibraryTypes, this._type)}\n library version: ${this._protocolVersion}`,\n\t\t);\n\n\t\t// If supported, get more fine-grained version info\n\t\tif (this.isFunctionSupported(FunctionType.GetProtocolVersion)) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`querying protocol version info...`,\n\t\t\t);\n\t\t\tconst protocol = await this.driver.sendMessage<\n\t\t\t\tGetProtocolVersionResponse\n\t\t\t>(\n\t\t\t\tnew GetProtocolVersionRequest(),\n\t\t\t);\n\n\t\t\tthis._protocolVersion = protocol.protocolVersion;\n\n\t\t\tlet message = `received protocol version info:\n protocol type: ${\n\t\t\t\tgetEnumMemberName(\n\t\t\t\t\tProtocolType,\n\t\t\t\t\tprotocol.protocolType,\n\t\t\t\t)\n\t\t\t}\n protocol version: ${protocol.protocolVersion}`;\n\t\t\tif (protocol.applicationFrameworkBuildNumber) {\n\t\t\t\tmessage += `\n appl. framework build no.: ${protocol.applicationFrameworkBuildNumber}`;\n\t\t\t}\n\t\t\tif (protocol.gitCommitHash) {\n\t\t\t\tmessage += `\n git commit hash: ${protocol.gitCommitHash}`;\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.print(message);\n\t\t}\n\n\t\t// The SDK version cannot be queried directly, but we can deduce it from the protocol version\n\t\tthis._sdkVersion = protocolVersionToSDKVersion(this._protocolVersion);\n\n\t\t// find out what the controller can do\n\t\tawait this.getControllerCapabilities();\n\n\t\t// If the serial API can be configured, figure out which sub commands are supported\n\t\t// This MUST be done after querying the SDK version due to a bug in some 7.xx firmwares, which incorrectly encode the bitmask\n\t\tif (this.isFunctionSupported(FunctionType.SerialAPISetup)) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`querying serial API setup capabilities...`,\n\t\t\t);\n\t\t\tconst setupCaps = await this.driver.sendMessage<\n\t\t\t\tSerialAPISetup_GetSupportedCommandsResponse\n\t\t\t>(\n\t\t\t\tnew SerialAPISetup_GetSupportedCommandsRequest(),\n\t\t\t);\n\t\t\tthis._supportedSerialAPISetupCommands = setupCaps.supportedCommands;\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`supported serial API setup commands:${\n\t\t\t\t\tthis._supportedSerialAPISetupCommands\n\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t(cmd) =>\n\t\t\t\t\t\t\t\t`\\n\u00B7 ${\n\t\t\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\t\t\tSerialAPISetupCommand,\n\t\t\t\t\t\t\t\t\t\tcmd,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}`,\n\t\t\t);\n\t\t} else {\n\t\t\tthis._supportedSerialAPISetupCommands = [];\n\t\t}\n\n\t\t// Figure out the maximum payload size for outgoing commands\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetMaximumPayloadSize,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(`querying max. payload size...`);\n\t\t\tthis._maxPayloadSize = await this.getMaxPayloadSize();\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`maximum payload size: ${this._maxPayloadSize} bytes`,\n\t\t\t);\n\t\t}\n\n\t\t// On older controllers with soft-reset disabled, supportsLongRange is not automatically reported by the controller\n\t\t// so we should set it manually\n\t\tif (!this.isLongRangeCapable()) {\n\t\t\tthis._supportsLongRange = false;\n\t\t\tthis._supportsLongRangeAutoChannelSelection = false;\n\t\t}\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`supported Z-Wave features: ${\n\t\t\t\tObject.keys(ZWaveFeature)\n\t\t\t\t\t.filter((k) => /^\\d+$/.test(k))\n\t\t\t\t\t.map((k) => parseInt(k) as ZWaveFeature)\n\t\t\t\t\t.filter((feat) => this.supportsFeature(feat))\n\t\t\t\t\t.map((feat) =>\n\t\t\t\t\t\t`\\n \u00B7 ${getEnumMemberName(ZWaveFeature, feat)}`\n\t\t\t\t\t)\n\t\t\t\t\t.join(\"\")\n\t\t\t}`,\n\t\t);\n\n\t\treturn {\n\t\t\tnodeIds: initData.nodeIds,\n\t\t};\n\t}\n\n\t/**\n\t * @internal\n\t * Queries the controller's capabilities in regards to Z-Wave Long Range.\n\t * Returns the list of Long Range node IDs\n\t */\n\tpublic async queryLongRangeCapabilities(): Promise<{\n\t\tlrNodeIds: readonly number[];\n\t}> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`querying Z-Wave Long Range capabilities...`,\n\t\t);\n\n\t\t// Fetch the list of Long Range nodes\n\t\tconst lrNodeIds = await this.getLongRangeNodes();\n\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetLongRangeMaximumPayloadSize,\n\t\t\t)\n\t\t) {\n\t\t\tthis._maxPayloadSizeLR = await this.getMaxPayloadSizeLongRange();\n\t\t}\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received Z-Wave Long Range capabilities:\n max. payload size: ${this._maxPayloadSizeLR} bytes\n nodes: ${lrNodeIds.join(\", \")}`,\n\t\t);\n\n\t\treturn {\n\t\t\tlrNodeIds,\n\t\t};\n\t}\n\n\tprivate isLongRangeCapable(): MaybeNotKnown<boolean> {\n\t\t// Z-Wave Long Range is supported if the controller supports changing the node ID type to 16 bit\n\t\t// FIXME: Consider using the ZWaveFeature enum for this instead\n\t\treturn this.isSerialAPISetupCommandSupported(\n\t\t\tSerialAPISetupCommand.SetNodeIDType,\n\t\t);\n\t}\n\n\t/** Tries to determine the LR capable replacement of the given region. If none is found, the given region is returned. */\n\tprivate tryGetLRCapableRegion(region: RFRegion): RFRegion {\n\t\tif (this._supportedRegions) {\n\t\t\t// If the region supports LR, use it\n\t\t\tif (this._supportedRegions.get(region)?.supportsLongRange) {\n\t\t\t\treturn region;\n\t\t\t}\n\n\t\t\t// Find a possible LR capable superset for this region\n\t\t\tfor (const info of this._supportedRegions.values()) {\n\t\t\t\tif (info.supportsLongRange && info.includesRegion === region) {\n\t\t\t\t\treturn info.region;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// US_LR is the first supported LR region, so if the controller supports LR, US_LR is supported\n\t\tif (region === RFRegion.USA && this.isLongRangeCapable()) {\n\t\t\treturn RFRegion[\"USA (Long Range)\"];\n\t\t}\n\n\t\treturn region;\n\t}\n\n\t/**\n\t * @internal\n\t * Queries the region and powerlevel settings and configures them if necessary\n\t */\n\tpublic async queryAndConfigureRF(): Promise<void> {\n\t\t// Figure out which regions are supported\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetSupportedRegions,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Querying supported RF regions and their information...`,\n\t\t\t);\n\t\t\tconst supportedRegions = await this.querySupportedRFRegions().catch(\n\t\t\t\t() => [],\n\t\t\t);\n\t\t\tthis._supportedRegions = new Map();\n\n\t\t\tfor (const region of supportedRegions) {\n\t\t\t\ttry {\n\t\t\t\t\tconst info = await this.queryRFRegionInfo(region);\n\t\t\t\t\tif (info.region === RFRegion.Unknown) continue;\n\t\t\t\t\tthis._supportedRegions.set(region, info);\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`supported regions:${\n\t\t\t\t\t[...this._supportedRegions.values()]\n\t\t\t\t\t\t.map((info) => {\n\t\t\t\t\t\t\tlet ret = `\\n\u00B7 ${\n\t\t\t\t\t\t\t\tgetEnumMemberName(RFRegion, info.region)\n\t\t\t\t\t\t\t}`;\n\t\t\t\t\t\t\tif (info.includesRegion != undefined) {\n\t\t\t\t\t\t\t\tret += ` \u00B7 superset of ${\n\t\t\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\t\t\tRFRegion,\n\t\t\t\t\t\t\t\t\t\tinfo.includesRegion,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (info.supportsLongRange) {\n\t\t\t\t\t\t\t\tret += \" \u00B7 ZWLR\";\n\t\t\t\t\t\t\t\tif (!info.supportsZWave) {\n\t\t\t\t\t\t\t\t\tret += \" only\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn ret;\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}`,\n\t\t\t);\n\t\t}\n\n\t\t// Check and possibly update the RF region to the desired value\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetRFRegion,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(`Querying configured RF region...`);\n\t\t\tconst resp = await this.getRFRegion().catch(() => undefined);\n\t\t\tif (resp != undefined) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`The controller is using RF region ${\n\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\tRFRegion,\n\t\t\t\t\t\t\tresp,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Querying the RF region failed!`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tlet desiredRFRegion: RFRegion | undefined;\n\t\t// If the user has set a region in the options, use that\n\t\tif (this.driver.options.rf?.region != undefined) {\n\t\t\tdesiredRFRegion = this.driver.options.rf.region;\n\t\t}\n\t\t// Unless preferring LR regions is disabled, try to find a suitable replacement region\n\t\tif (this.driver.options.rf?.preferLRRegion !== false) {\n\t\t\tdesiredRFRegion ??= this.rfRegion;\n\t\t\tif (desiredRFRegion != undefined) {\n\t\t\t\tdesiredRFRegion = this.tryGetLRCapableRegion(desiredRFRegion);\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.SetRFRegion,\n\t\t\t)\n\t\t\t&& desiredRFRegion != undefined\n\t\t\t&& this.rfRegion != desiredRFRegion\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Current RF region (${\n\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\tRFRegion,\n\t\t\t\t\t\tthis.rfRegion ?? RFRegion.Unknown,\n\t\t\t\t\t)\n\t\t\t\t}) differs from desired region (${\n\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\tRFRegion,\n\t\t\t\t\t\tdesiredRFRegion,\n\t\t\t\t\t)\n\t\t\t\t}), configuring it...`,\n\t\t\t);\n\t\t\tconst resp = await this.setRFRegionInternal(\n\t\t\t\tdesiredRFRegion,\n\t\t\t\t// Do not soft reset here, we'll do it later\n\t\t\t\tfalse,\n\t\t\t).catch((e) => (e as Error).message);\n\t\t\tif (resp === true) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Changed RF region to ${\n\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\tRFRegion,\n\t\t\t\t\t\t\tdesiredRFRegion,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Changing the RF region failed!${\n\t\t\t\t\t\tresp ? ` Reason: ${resp}` : \"\"\n\t\t\t\t\t}`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Check and possibly update the powerlevel settings\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetPowerlevel,\n\t\t\t)\n\t\t\t&& this.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.SetPowerlevel,\n\t\t\t)\n\t\t\t&& this.driver.options.rf?.txPower != undefined\n\t\t) {\n\t\t\tconst desired = this.driver.options.rf.txPower;\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Querying configured powerlevel...`,\n\t\t\t);\n\t\t\tconst current = await this.getPowerlevel().catch(() => undefined);\n\t\t\tif (current != undefined) {\n\t\t\t\tif (\n\t\t\t\t\tcurrent.powerlevel !== desired.powerlevel\n\t\t\t\t\t|| current.measured0dBm !== desired.measured0dBm\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t`Current powerlevel ${current.powerlevel} dBm (${current.measured0dBm} dBm) differs from desired powerlevel ${desired.powerlevel} dBm (${desired.measured0dBm} dBm), configuring it...`,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst resp = await this.setPowerlevel(\n\t\t\t\t\t\tdesired.powerlevel,\n\t\t\t\t\t\tdesired.measured0dBm,\n\t\t\t\t\t).catch((e) => (e as Error).message);\n\t\t\t\t\tif (resp === true) {\n\t\t\t\t\t\tthis.driver.controllerLog.print(`Powerlevel updated`);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t\t`Changing the powerlevel failed!${\n\t\t\t\t\t\t\t\tresp ? ` Reason: ${resp}` : \"\"\n\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Querying the powerlevel failed!`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Check and possibly update the Long Range powerlevel settings\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetLongRangeMaximumTxPower,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Querying configured max. Long Range powerlevel...`,\n\t\t\t);\n\t\t\tconst resp = await this.getMaxLongRangePowerlevel().catch(() =>\n\t\t\t\tundefined\n\t\t\t);\n\t\t\tif (resp != undefined) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`The max. LR powerlevel is ${resp.toFixed(1)} dBm`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Querying the max. Long Range powerlevel failed!`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.SetLongRangeMaximumTxPower,\n\t\t\t)\n\t\t\t&& this.driver.options.rf?.maxLongRangePowerlevel != undefined\n\t\t\t&& this.maxLongRangePowerlevel\n\t\t\t\t!== this.driver.options.rf.maxLongRangePowerlevel\n\t\t) {\n\t\t\tconst desired = this.driver.options.rf.maxLongRangePowerlevel;\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Current max. Long Range powerlevel ${\n\t\t\t\t\tthis.maxLongRangePowerlevel?.toFixed(1)\n\t\t\t\t} dBm differs from desired powerlevel ${desired} dBm, configuring it...`,\n\t\t\t);\n\n\t\t\tconst resp = await this.setMaxLongRangePowerlevel(desired)\n\t\t\t\t.catch((e) => (e as Error).message);\n\t\t\tif (resp === true) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`max. Long Range powerlevel updated`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Changing the max. Long Range powerlevel failed!${\n\t\t\t\t\t\tresp ? ` Reason: ${resp}` : \"\"\n\t\t\t\t\t}`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Check and possibly update the Long Range channel settings\n\t\tif (\n\t\t\tthis.isFunctionSupported(FunctionType.GetLongRangeChannel)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Querying configured Long Range channel information...`,\n\t\t\t);\n\t\t\tconst resp = await this.getLongRangeChannel().catch(() =>\n\t\t\t\tundefined\n\t\t\t);\n\t\t\tif (resp != undefined) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`received Z-Wave Long Range channel information:\n channel: ${\n\t\t\t\t\t\tresp.channel != undefined\n\t\t\t\t\t\t\t? getEnumMemberName(LongRangeChannel, resp.channel)\n\t\t\t\t\t\t\t: \"(unknown)\"\n\t\t\t\t\t}\n supports auto channel selection: ${resp.supportsAutoChannelSelection}\n`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Querying the Long Range channel information failed!`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tthis._supportsLongRangeAutoChannelSelection = false;\n\t\t}\n\t\tif (\n\t\t\tthis.isFunctionSupported(FunctionType.SetLongRangeChannel)\n\t\t\t&& this.driver.options.rf?.longRangeChannel != undefined\n\t\t\t&& this.longRangeChannel\n\t\t\t\t!== this.driver.options.rf.longRangeChannel\n\t\t) {\n\t\t\tconst desired = this.driver.options.rf.longRangeChannel;\n\t\t\tif (\n\t\t\t\tdesired === LongRangeChannel.Auto\n\t\t\t\t&& !this._supportsLongRangeAutoChannelSelection\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Cannot set desired LR channel to Auto because the controller does not support it!`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Current LR channel ${\n\t\t\t\t\t\tthis.longRangeChannel != undefined\n\t\t\t\t\t\t\t? getEnumMemberName(\n\t\t\t\t\t\t\t\tLongRangeChannel,\n\t\t\t\t\t\t\t\tthis.longRangeChannel,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: \"(unknown)\"\n\t\t\t\t\t} differs from desired channel ${\n\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\tLongRangeChannel,\n\t\t\t\t\t\t\tdesired,\n\t\t\t\t\t\t)\n\t\t\t\t\t}, configuring it...`,\n\t\t\t\t);\n\n\t\t\t\tconst resp = await this.setLongRangeChannel(desired)\n\t\t\t\t\t.catch((e) => (e as Error).message);\n\t\t\t\tif (resp === true) {\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t`LR channel updated`,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t`Changing the LR channel failed!${\n\t\t\t\t\t\t\tresp ? ` Reason: ${resp}` : \"\"\n\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Queries the home and node id of the controller\n\t */\n\tpublic async identify(): Promise<void> {\n\t\tthis.driver.controllerLog.print(`querying controller IDs...`);\n\t\tconst ids = await this.driver.sendMessage<GetControllerIdResponse>(\n\t\t\tnew GetControllerIdRequest(),\n\t\t\t{ supportCheck: false },\n\t\t);\n\t\tthis._homeId = ids.homeId;\n\t\tthis._ownNodeId = ids.ownNodeId;\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received controller IDs:\n home ID: ${num2hex(this._homeId)}\n own node ID: ${this._ownNodeId}`,\n\t\t);\n\t}\n\n\t/**\n\t * @internal\n\t * Performs additional controller configuration\n\t */\n\tpublic async configure(): Promise<void> {\n\t\t// Enable TX status report if supported\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.SetTxStatusReport,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(`Enabling TX status report...`);\n\t\t\tconst resp = await this.driver.sendMessage<\n\t\t\t\tSerialAPISetup_SetTXStatusReportResponse\n\t\t\t>(\n\t\t\t\tnew SerialAPISetup_SetTXStatusReportRequest({\n\t\t\t\t\tenabled: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Enabling TX status report ${\n\t\t\t\t\tresp.success ? \"successful\" : \"failed\"\n\t\t\t\t}...`,\n\t\t\t);\n\t\t}\n\n\t\t// find the SUC\n\t\tthis.driver.controllerLog.print(`finding SUC...`);\n\t\tconst suc = await this.driver.sendMessage<GetSUCNodeIdResponse>(\n\t\t\tnew GetSUCNodeIdRequest(),\n\t\t\t{ supportCheck: false },\n\t\t);\n\t\tthis._sucNodeId = suc.sucNodeId;\n\t\tif (this._sucNodeId === 0) {\n\t\t\tthis.driver.controllerLog.print(`No SUC present in the network`);\n\t\t} else if (this._sucNodeId === this._ownNodeId) {\n\t\t\tthis.driver.controllerLog.print(`This is the SUC`);\n\t\t} else {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`SUC has node ID ${this.sucNodeId}`,\n\t\t\t);\n\t\t}\n\n\t\t// There needs to be a SUC/SIS in the network. If not, we promote ourselves to one if the following conditions are met:\n\t\t// We are the primary controller, but we are not SUC, there is no SUC and there is no SIS, and there are no nodes in the network yet\n\t\tif (\n\t\t\tthis.role === ControllerRole.Primary\n\t\t\t&& this._noNodesIncluded\n\t\t\t&& this._sucNodeId === 0\n\t\t\t&& !this._isSUC\n\t\t\t&& !this._isSISPresent\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`There is no SUC/SIS in the network - promoting ourselves...`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst result = await this.configureSUC(\n\t\t\t\t\tthis._ownNodeId!,\n\t\t\t\t\ttrue,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tif (result) {\n\t\t\t\t\tthis._sucNodeId = this._ownNodeId;\n\t\t\t\t\tthis._isSUC = true;\n\t\t\t\t\tthis._isSISPresent = true;\n\t\t\t\t}\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Promotion to SUC/SIS ${result ? \"succeeded\" : \"failed\"}.`,\n\t\t\t\t\tresult ? undefined : \"warn\",\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Error while promoting to SUC/SIS: ${getErrorMessage(e)}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// TODO: if it's a bridge controller, request the virtual nodes\n\n\t\tif (\n\t\t\tthis.type !== ZWaveLibraryTypes[\"Bridge Controller\"]\n\t\t\t&& this.isFunctionSupported(FunctionType.SetSerialApiTimeouts)\n\t\t) {\n\t\t\tconst { ack, byte } = this.driver.options.timeouts;\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`setting serial API timeouts: ack = ${ack} ms, byte = ${byte} ms`,\n\t\t\t);\n\t\t\tconst resp = await this.driver.sendMessage<\n\t\t\t\tSetSerialApiTimeoutsResponse\n\t\t\t>(\n\t\t\t\tnew SetSerialApiTimeoutsRequest({\n\t\t\t\t\tackTimeout: ack,\n\t\t\t\t\tbyteTimeout: byte,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`serial API timeouts overwritten. The old values were: ack = ${resp.oldAckTimeout} ms, byte = ${resp.oldByteTimeout} ms`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Interviews the controller for the necessary information.\n\t * @param restoreFromCache Asynchronous callback for the driver to restore the network from cache after nodes are created\n\t */\n\tpublic async initNodes(\n\t\tclassicNodeIds: readonly number[],\n\t\tlrNodeIds: readonly number[],\n\t\trestoreFromCache: () => Promise<void>,\n\t): Promise<void> {\n\t\t// Index the value DB for optimal performance\n\t\tconst valueDBIndexes = indexDBsByNode([\n\t\t\tthis.driver.valueDB!,\n\t\t\tthis.driver.metadataDB!,\n\t\t]);\n\n\t\tconst nodeIds = [...classicNodeIds];\n\t\tif (nodeIds.length === 0) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Controller reports no nodes in its network. This could be an indication of a corrupted controller memory.`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tnodeIds.unshift(this._ownNodeId!);\n\t\t}\n\t\tnodeIds.push(...lrNodeIds);\n\n\t\t// For each node, create an empty entry in the nodes map so we can initialize them afterwards\n\t\tfor (const nodeId of nodeIds) {\n\t\t\tthis._nodes.set(\n\t\t\t\tnodeId,\n\t\t\t\tnew ZWaveNode(\n\t\t\t\t\tnodeId,\n\t\t\t\t\tthis.driver,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\t// Use the previously created index to avoid doing extra work when creating the value DB\n\t\t\t\t\tthis.createValueDBForNode(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tvalueDBIndexes.get(nodeId),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// Now try to deserialize all nodes from the cache\n\t\tawait restoreFromCache();\n\n\t\t// Set manufacturer information for the controller node\n\t\tconst controllerValueDB = this.valueDB;\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tManufacturerSpecificCCValues.manufacturerId.id,\n\t\t\tManufacturerSpecificCCValues.manufacturerId.meta,\n\t\t);\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tManufacturerSpecificCCValues.productType.id,\n\t\t\tManufacturerSpecificCCValues.productType.meta,\n\t\t);\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tManufacturerSpecificCCValues.productId.id,\n\t\t\tManufacturerSpecificCCValues.productId.meta,\n\t\t);\n\t\tcontrollerValueDB.setValue(\n\t\t\tManufacturerSpecificCCValues.manufacturerId.id,\n\t\t\tthis._manufacturerId,\n\t\t);\n\t\tcontrollerValueDB.setValue(\n\t\t\tManufacturerSpecificCCValues.productType.id,\n\t\t\tthis._productType,\n\t\t);\n\t\tcontrollerValueDB.setValue(\n\t\t\tManufacturerSpecificCCValues.productId.id,\n\t\t\tthis._productId,\n\t\t);\n\n\t\t// Set firmware version information for the controller node\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tVersionCCValues.firmwareVersions.id,\n\t\t\tVersionCCValues.firmwareVersions.meta,\n\t\t);\n\t\tcontrollerValueDB.setValue(VersionCCValues.firmwareVersions.id, [\n\t\t\tthis._firmwareVersion,\n\t\t]);\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tVersionCCValues.zWaveProtocolVersion.id,\n\t\t\tVersionCCValues.zWaveProtocolVersion.meta,\n\t\t);\n\t\tcontrollerValueDB.setValue(\n\t\t\tVersionCCValues.zWaveProtocolVersion.id,\n\t\t\tthis._protocolVersion,\n\t\t);\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tVersionCCValues.sdkVersion.id,\n\t\t\tVersionCCValues.sdkVersion.meta,\n\t\t);\n\t\tcontrollerValueDB.setValue(\n\t\t\tVersionCCValues.sdkVersion.id,\n\t\t\tthis._sdkVersion,\n\t\t);\n\n\t\tthis.driver.controllerLog.print(\"Interview completed\");\n\t}\n\n\tprivate createValueDBForNode(nodeId: number, ownKeys?: Set<string>) {\n\t\treturn new ValueDB(\n\t\t\tnodeId,\n\t\t\tthis.driver.valueDB!,\n\t\t\tthis.driver.metadataDB!,\n\t\t\townKeys,\n\t\t);\n\t}\n\n\t/**\n\t * Gets the list of long range nodes from the controller.\n\t */\n\tpublic async getLongRangeNodes(): Promise<readonly number[]> {\n\t\tconst nodeIds: number[] = [];\n\n\t\tif (this.supportsLongRange) {\n\t\t\tfor (let segment = 0;; segment++) {\n\t\t\t\tconst nodesResponse = await this.driver.sendMessage<\n\t\t\t\t\tGetLongRangeNodesResponse\n\t\t\t\t>(\n\t\t\t\t\tnew GetLongRangeNodesRequest({\n\t\t\t\t\t\tsegmentNumber: segment,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tnodeIds.push(...nodesResponse.nodeIds);\n\n\t\t\t\tif (!nodesResponse.moreNodes) break;\n\t\t\t}\n\t\t}\n\t\treturn nodeIds;\n\t}\n\n\t/**\n\t * Sets the NIF of the controller to the Gateway device type and to include the CCs supported by Z-Wave JS.\n\t * Warning: This only works when followed up by a hard-reset, so don't call this directly\n\t * @internal\n\t */\n\tpublic async setControllerNIF(): Promise<void> {\n\t\tthis.driver.controllerLog.print(\"Updating the controller NIF...\");\n\t\tawait this.driver.sendMessage(\n\t\t\tnew SetApplicationNodeInformationRequest({\n\t\t\t\tisListening: true,\n\t\t\t\t...determineNIF(),\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Performs a hard reset on the controller. This wipes out all configuration!\n\t * Warning: The driver needs to re-interview the controller, so don't call this directly\n\t * @internal\n\t */\n\tpublic async hardReset(): Promise<void> {\n\t\t// begin the reset process\n\t\ttry {\n\t\t\tconst associations = this.associations;\n\t\t\tif (associations?.length) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Notifying associated nodes about reset...\",\n\t\t\t\t);\n\t\t\t\tconst nodeIdDestinations = distinct(\n\t\t\t\t\tassociations.map(({ nodeId }) => nodeId),\n\t\t\t\t);\n\t\t\t\tfor (const nodeId of nodeIdDestinations) {\n\t\t\t\t\tconst node = this.nodes.get(nodeId);\n\t\t\t\t\tif (!node) continue;\n\n\t\t\t\t\tawait node.sendResetLocallyNotification().catch(noop);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.print(\"performing hard reset...\");\n\t\t\tawait this.driver.sendMessage(new HardResetRequest(), {\n\t\t\t\tsupportCheck: false,\n\t\t\t});\n\n\t\t\tthis.driver.controllerLog.print(`hard reset succeeded`);\n\t\t\t// Clean up\n\t\t\tthis._nodes.forEach((node) => node.removeAllListeners());\n\t\t\tthis._nodes.clear();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`hard reset failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tpublic async shutdown(): Promise<boolean> {\n\t\t// begin the reset process\n\t\ttry {\n\t\t\tthis.driver.controllerLog.print(\"Shutting down the Z-Wave API...\");\n\t\t\tconst response = await this.driver.sendMessage<ShutdownResponse>(\n\t\t\t\tnew ShutdownRequest(),\n\t\t\t);\n\t\t\tif (response.success) {\n\t\t\t\tthis.driver.controllerLog.print(\"Z-Wave API was shut down\");\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Failed to shut down the Z-Wave API\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn response.success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`shutdown failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Starts the hardware watchdog on supporting 700+ series controllers.\n\t * Returns whether the operation was successful.\n\t */\n\tpublic async startWatchdog(): Promise<boolean> {\n\t\tif (\n\t\t\tthis.sdkVersionGte(\"7.0\")\n\t\t\t&& this.isFunctionSupported(FunctionType.StartWatchdog)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Starting hardware watchdog...\",\n\t\t\t\t);\n\t\t\t\tawait this.driver.sendMessage(\n\t\t\t\t\tnew StartWatchdogRequest(),\n\t\t\t\t);\n\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Starting the hardware watchdog failed: ${\n\t\t\t\t\t\tgetErrorMessage(e)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Stops the hardware watchdog on supporting controllers.\n\t * Returns whether the operation was successful.\n\t */\n\tpublic async stopWatchdog(): Promise<boolean> {\n\t\tif (this.isFunctionSupported(FunctionType.StopWatchdog)) {\n\t\t\ttry {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Stopping hardware watchdog...\",\n\t\t\t\t);\n\t\t\t\tawait this.driver.sendMessage(\n\t\t\t\t\tnew StopWatchdogRequest(),\n\t\t\t\t);\n\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Stopping the hardware watchdog failed: ${\n\t\t\t\t\t\tgetErrorMessage(e)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate _inclusionState: InclusionState = InclusionState.Idle;\n\tpublic get inclusionState(): InclusionState {\n\t\treturn this._inclusionState;\n\t}\n\n\t/** @internal */\n\tpublic setInclusionState(state: InclusionState): void {\n\t\tif (this._inclusionState === state) return;\n\t\tthis._inclusionState = state;\n\t\tthis.emit(\"inclusion state changed\", state);\n\t\tif (\n\t\t\tstate === InclusionState.Idle\n\t\t\t&& this._smartStartEnabled\n\t\t\t&& this.supportsFeature(ZWaveFeature.SmartStart)\n\t\t) {\n\t\t\t// If Smart Start was enabled before the inclusion/exclusion,\n\t\t\t// enable it again and ignore errors\n\t\t\tthis.enableSmartStart().catch(noop);\n\t\t}\n\t}\n\n\tprivate _smartStartEnabled: boolean = false;\n\n\tprivate _includeController: boolean = false;\n\tprivate _exclusionOptions: ExclusionOptions | undefined;\n\tprivate _inclusionOptions: InclusionOptionsInternal | undefined;\n\tprivate _nodePendingInclusion: ZWaveNode | undefined;\n\tprivate _nodePendingExclusion: ZWaveNode | undefined;\n\tprivate _nodePendingReplace: ZWaveNode | undefined;\n\tprivate _replaceFailedPromise: DeferredPromise<boolean> | undefined;\n\n\t/**\n\t * Starts the inclusion process of new nodes.\n\t * Resolves to true when the process was started, and false if the inclusion was already active.\n\t *\n\t * @param options Defines the inclusion strategy to use.\n\t */\n\tpublic async beginInclusion(\n\t\toptions: InclusionOptions = {\n\t\t\tstrategy: InclusionStrategy.Insecure,\n\t\t},\n\t): Promise<boolean> {\n\t\tif (\n\t\t\tthis._inclusionState === InclusionState.Including\n\t\t\t|| this._inclusionState === InclusionState.Excluding\n\t\t\t|| this._inclusionState === InclusionState.Busy\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Protect against invalid inclusion options\n\t\tif (\n\t\t\t!(options.strategy in InclusionStrategy)\n\t\t\t// @ts-expect-error We're checking for user errors\n\t\t\t|| options.strategy === InclusionStrategy.SmartStart\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Invalid inclusion strategy: ${options.strategy}`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\t// Leave SmartStart listening mode so we can switch to exclusion mode\n\t\tawait this.pauseSmartStart();\n\n\t\tthis.setInclusionState(InclusionState.Including);\n\t\tthis._inclusionOptions = options;\n\n\t\ttry {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Starting inclusion process with strategy ${\n\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\tInclusionStrategy,\n\t\t\t\t\t\toptions.strategy,\n\t\t\t\t\t)\n\t\t\t\t}...`,\n\t\t\t);\n\n\t\t\t// kick off the inclusion process\n\t\t\tawait this.driver.sendMessage(\n\t\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\t\taddNodeType: AddNodeType.Any,\n\t\t\t\t\thighPower: true,\n\t\t\t\t\tnetworkWide: true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`The controller is now ready to add nodes`,\n\t\t\t);\n\n\t\t\tthis.emit(\"inclusion started\", options.strategy);\n\t\t} catch (e) {\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Starting the inclusion failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The inclusion could not be started.\",\n\t\t\t\t\tZWaveErrorCodes.Controller_InclusionFailed,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/** @internal */\n\tpublic async beginInclusionSmartStart(\n\t\tprovisioningEntry: PlannedProvisioningEntry,\n\t): Promise<boolean> {\n\t\tif (\n\t\t\tthis._inclusionState === InclusionState.Including\n\t\t\t|| this._inclusionState === InclusionState.Excluding\n\t\t\t|| this._inclusionState === InclusionState.Busy\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Disable listening mode so we can switch to inclusion mode\n\t\tawait this.stopInclusion();\n\n\t\tthis.setInclusionState(InclusionState.Including);\n\t\tthis._inclusionOptions = {\n\t\t\tstrategy: InclusionStrategy.SmartStart,\n\t\t\tprovisioning: provisioningEntry,\n\t\t};\n\n\t\ttry {\n\t\t\t// Kick off the inclusion process using either the\n\t\t\t// specified protocol or the first supported one\n\t\t\tconst dskBuffer = dskFromString(provisioningEntry.dsk);\n\t\t\tconst protocol = provisioningEntry.protocol\n\t\t\t\t?? provisioningEntry.supportedProtocols?.[0]\n\t\t\t\t?? Protocols.ZWave;\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Including SmartStart node with DSK ${provisioningEntry.dsk}${\n\t\t\t\t\tprotocol == Protocols.ZWaveLongRange\n\t\t\t\t\t\t? \" using Z-Wave Long Range\"\n\t\t\t\t\t\t: \"\"\n\t\t\t\t}`,\n\t\t\t);\n\n\t\t\tawait this.driver.sendMessage(\n\t\t\t\tnew AddNodeDSKToNetworkRequest({\n\t\t\t\t\tnwiHomeId: nwiHomeIdFromDSK(dskBuffer),\n\t\t\t\t\tauthHomeId: authHomeIdFromDSK(dskBuffer),\n\t\t\t\t\tprotocol,\n\t\t\t\t\thighPower: true,\n\t\t\t\t\tnetworkWide: true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tthis.emit(\"inclusion started\", InclusionStrategy.SmartStart);\n\t\t} catch (e) {\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t// Error handling for this happens at the call site\n\t\t\tthrow e;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Is used internally to stop an active inclusion process without waiting for a confirmation\n\t * @internal\n\t */\n\tpublic async stopInclusionNoCallback(): Promise<void> {\n\t\tawait this.driver.sendMessage(\n\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\tcallbackId: 0, // disable callbacks\n\t\t\t\taddNodeType: AddNodeType.Stop,\n\t\t\t\thighPower: true,\n\t\t\t\tnetworkWide: true,\n\t\t\t}),\n\t\t);\n\t\tthis.driver.controllerLog.print(`The inclusion process was stopped`);\n\t\tthis.emit(\"inclusion stopped\");\n\t}\n\n\t/**\n\t * Finishes an inclusion process. This must only be called after the ProtocolDone status is received.\n\t * Returns the ID of the newly added node.\n\t */\n\tprivate async finishInclusion(): Promise<number> {\n\t\tthis.driver.controllerLog.print(`finishing inclusion process...`);\n\n\t\tconst response = await this.driver.sendMessage<\n\t\t\tAddNodeToNetworkRequestStatusReport\n\t\t>(\n\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\taddNodeType: AddNodeType.Stop,\n\t\t\t\thighPower: true,\n\t\t\t\tnetworkWide: true,\n\t\t\t}),\n\t\t);\n\t\tif (response.status === AddNodeStatus.Done) {\n\t\t\treturn response.statusContext!.nodeId;\n\t\t}\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`Finishing the inclusion failed`,\n\t\t\t\"error\",\n\t\t);\n\t\tthrow new ZWaveError(\n\t\t\t\"Finishing the inclusion failed\",\n\t\t\tZWaveErrorCodes.Controller_InclusionFailed,\n\t\t);\n\t}\n\n\t/**\n\t * Stops an active inclusion process. Resolves to true when the controller leaves inclusion mode,\n\t * and false if the inclusion was not active.\n\t */\n\tpublic async stopInclusion(): Promise<boolean> {\n\t\tif (this._inclusionState !== InclusionState.Including) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.print(`stopping inclusion process...`);\n\n\t\ttry {\n\t\t\t// stop the inclusion process\n\t\t\tawait this.driver.sendMessage(\n\t\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\t\taddNodeType: AddNodeType.Stop,\n\t\t\t\t\thighPower: true,\n\t\t\t\t\tnetworkWide: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`The inclusion process was stopped`,\n\t\t\t);\n\t\t\tthis.emit(\"inclusion stopped\");\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Stopping the inclusion failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The inclusion could not be stopped.\",\n\t\t\t\t\tZWaveErrorCodes.Controller_InclusionFailed,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Puts the controller into listening mode for Smart Start inclusion.\n\t * Whenever a node on the provisioning list announces itself, it will automatically be added.\n\t *\n\t * Resolves to `true` when the listening mode is started or was active, and `false` if it is scheduled for later activation.\n\t */\n\tprivate async enableSmartStart(): Promise<boolean> {\n\t\tif (!this.supportsFeature(ZWaveFeature.SmartStart)) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Smart Start is not supported by this controller, NOT enabling listening mode...`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t}\n\n\t\tthis._smartStartEnabled = true;\n\n\t\tif (this._inclusionState === InclusionState.Idle) {\n\t\t\tthis.setInclusionState(InclusionState.SmartStart);\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Enabling Smart Start listening mode...`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendMessage(\n\t\t\t\t\tnew EnableSmartStartListenRequest({}),\n\t\t\t\t);\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start listening mode enabled`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start listening mode could not be enabled: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t} else if (this._inclusionState === InclusionState.SmartStart) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Smart Start listening mode scheduled for later activation...`,\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Disables the listening mode for Smart Start inclusion.\n\t *\n\t * Resolves to `true` when the listening mode is stopped, and `false` if was not active.\n\t */\n\tprivate async disableSmartStart(): Promise<boolean> {\n\t\tif (!this.supportsFeature(ZWaveFeature.SmartStart)) return true;\n\t\tthis._smartStartEnabled = false;\n\n\t\tif (this._inclusionState === InclusionState.SmartStart) {\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`disabling Smart Start listening mode...`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendMessage(\n\t\t\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\t\t\tcallbackId: 0, // disable callbacks\n\t\t\t\t\t\taddNodeType: AddNodeType.Stop,\n\t\t\t\t\t\thighPower: true,\n\t\t\t\t\t\tnetworkWide: true,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start listening mode disabled`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tthis.setInclusionState(InclusionState.SmartStart);\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start listening mode could not be disabled: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t} else if (this._inclusionState === InclusionState.Idle) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Smart Start listening mode disabled`,\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tprivate async pauseSmartStart(): Promise<boolean> {\n\t\tif (!this.supportsFeature(ZWaveFeature.SmartStart)) return true;\n\n\t\tif (this._inclusionState === InclusionState.SmartStart) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Leaving Smart Start listening mode...`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendMessage(\n\t\t\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\t\t\tcallbackId: 0, // disable callbacks\n\t\t\t\t\t\taddNodeType: AddNodeType.Stop,\n\t\t\t\t\t\thighPower: true,\n\t\t\t\t\t\tnetworkWide: true,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Left Smart Start listening mode`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start listening mode could not be left: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t} else {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/**\n\t * Starts the exclusion process of new nodes.\n\t * Resolves to true when the process was started, and false if an inclusion or exclusion process was already active.\n\t *\n\t * @param options Influences the exclusion process and what happens with the Smart Start provisioning list.\n\t */\n\tpublic async beginExclusion(\n\t\toptions: ExclusionOptions = {\n\t\t\tstrategy: ExclusionStrategy.DisableProvisioningEntry,\n\t\t},\n\t): Promise<boolean> {\n\t\tif (\n\t\t\tthis._inclusionState === InclusionState.Including\n\t\t\t|| this._inclusionState === InclusionState.Excluding\n\t\t\t|| this._inclusionState === InclusionState.Busy\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Leave SmartStart listening mode so we can switch to exclusion mode\n\t\tawait this.pauseSmartStart();\n\n\t\tthis.setInclusionState(InclusionState.Excluding);\n\t\tthis.driver.controllerLog.print(`starting exclusion process...`);\n\n\t\ttry {\n\t\t\t// kick off the inclusion process\n\t\t\tawait this.driver.sendMessage(\n\t\t\t\tnew RemoveNodeFromNetworkRequest({\n\t\t\t\t\tremoveNodeType: RemoveNodeType.Any,\n\t\t\t\t\thighPower: true,\n\t\t\t\t\tnetworkWide: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`The controller is now ready to remove nodes`,\n\t\t\t);\n\t\t\tthis._exclusionOptions = options;\n\t\t\tthis.emit(\"exclusion started\");\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Starting the exclusion failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The exclusion could not be started.\",\n\t\t\t\t\tZWaveErrorCodes.Controller_ExclusionFailed,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Is used internally to stop an active exclusion process without waiting for confirmation\n\t * @internal\n\t */\n\tpublic async stopExclusionNoCallback(): Promise<void> {\n\t\tawait this.driver.sendMessage(\n\t\t\tnew RemoveNodeFromNetworkRequest({\n\t\t\t\tcallbackId: 0, // disable callbacks\n\t\t\t\tremoveNodeType: RemoveNodeType.Stop,\n\t\t\t\thighPower: true,\n\t\t\t\tnetworkWide: true,\n\t\t\t}),\n\t\t);\n\t\tthis.driver.controllerLog.print(`the exclusion process was stopped`);\n\t\tthis.emit(\"exclusion stopped\");\n\t}\n\n\t/**\n\t * Stops an active exclusion process. Resolves to true when the controller leaves exclusion mode,\n\t * and false if the inclusion was not active.\n\t */\n\tpublic async stopExclusion(): Promise<boolean> {\n\t\tif (this._inclusionState !== InclusionState.Excluding) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.print(`stopping exclusion process...`);\n\n\t\ttry {\n\t\t\t// kick off the inclusion process\n\t\t\tawait this.driver.sendMessage(\n\t\t\t\tnew RemoveNodeFromNetworkRequest({\n\t\t\t\t\tremoveNodeType: RemoveNodeType.Stop,\n\t\t\t\t\thighPower: true,\n\t\t\t\t\tnetworkWide: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`the exclusion process was stopped`,\n\t\t\t);\n\t\t\tthis.emit(\"exclusion stopped\");\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Stopping the exclusion failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The exclusion could not be stopped.\",\n\t\t\t\t\tZWaveErrorCodes.Controller_ExclusionFailed,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/** @internal */\n\tpublic async handleApplicationUpdateRequest(\n\t\tmsg: ApplicationUpdateRequest,\n\t): Promise<void> {\n\t\tconst nodeId = msg.getNodeId();\n\t\tlet node: ZWaveNode | undefined;\n\t\tif (nodeId != undefined) {\n\t\t\tnode = this.nodes.get(nodeId);\n\t\t}\n\n\t\tif (msg instanceof ApplicationUpdateRequestNodeInfoReceived) {\n\t\t\tif (node) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage: \"Received updated node info\",\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\t\t\t\tnode.updateNodeInfo(msg.nodeInformation);\n\n\t\t\t\t// This came from the node\n\t\t\t\tnode.lastSeen = new Date();\n\n\t\t\t\t// Resolve active pings that would fail otherwise\n\t\t\t\tthis.driver.resolvePendingPings(node.id);\n\n\t\t\t\tnode.emit(\"node info received\", node);\n\n\t\t\t\tif (\n\t\t\t\t\tnode.canSleep\n\t\t\t\t\t&& node.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t\t\t) {\n\t\t\t\t\t// In case this is a sleeping node and there are no messages in the queue, the node may go back to sleep very soon\n\t\t\t\t\tthis.driver.debounceSendNodeToSleep(node);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (\n\t\t\tmsg instanceof ApplicationUpdateRequestSmartStartHomeIDReceived\n\t\t\t|| msg\n\t\t\t\tinstanceof ApplicationUpdateRequestSmartStartLongRangeHomeIDReceived\n\t\t) {\n\t\t\tconst isLongRange = msg\n\t\t\t\tinstanceof ApplicationUpdateRequestSmartStartLongRangeHomeIDReceived;\n\t\t\t// The controller is in Smart Start learn mode and a node requests inclusion via Smart Start\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Received Smart Start inclusion request${\n\t\t\t\t\tisLongRange ? \" (Z-Wave Long Range)\" : \"\"\n\t\t\t\t}`,\n\t\t\t);\n\n\t\t\tif (\n\t\t\t\tthis.inclusionState !== InclusionState.Idle\n\t\t\t\t&& this.inclusionState !== InclusionState.SmartStart\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Controller is busy and cannot handle this inclusion request right now...\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check if the node is on the provisioning list\n\t\t\tconst entry = this.provisioningList.find((entry) => {\n\t\t\t\tif (\n\t\t\t\t\t!areUint8ArraysEqual(\n\t\t\t\t\t\tnwiHomeIdFromDSK(dskFromString(entry.dsk)),\n\t\t\t\t\t\tmsg.nwiHomeId,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t// TODO: This is duplicated with the logic in beginInclusionSmartStart\n\t\t\t\tconst entryProtocol = entry.protocol\n\t\t\t\t\t?? entry.supportedProtocols?.[0]\n\t\t\t\t\t?? Protocols.ZWave;\n\t\t\t\treturn (entryProtocol === Protocols.ZWaveLongRange)\n\t\t\t\t\t=== isLongRange;\n\t\t\t});\n\t\t\tif (!entry) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"NWI Home ID not found in provisioning list, ignoring request...\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t} else if (\n\t\t\t\tentry.status === ProvisioningEntryStatus.Inactive\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"The provisioning entry for this node is inactive, ignoring request...\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Discard any invalid security classes that may have been granted. This can happen\n\t\t\t// when switching the protocol to ZWLR for a device that requests S2 Unauthenticated\n\t\t\t// for Z-Wave Classic.\n\t\t\tconst provisioningEntry = cloneDeep(entry);\n\t\t\tif (isLongRange) {\n\t\t\t\tprovisioningEntry.securityClasses = provisioningEntry\n\t\t\t\t\t.securityClasses.filter((sc) =>\n\t\t\t\t\t\tsc === SecurityClass.S2_AccessControl\n\t\t\t\t\t\t|| sc === SecurityClass.S2_Authenticated\n\t\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Ignore provisioning entries where some of the granted keys are not configured\n\t\t\tconst securityManager = isLongRange\n\t\t\t\t? this.driver.securityManagerLR\n\t\t\t\t: this.driver.securityManager2;\n\t\t\tconst missingKeys = provisioningEntry.securityClasses.filter(\n\t\t\t\t(sc) => !securityManager?.hasKeysForSecurityClass(sc),\n\t\t\t);\n\t\t\tif (missingKeys.length > 0) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Ignoring inclusion request because the following security classes were granted but have no key configured:${\n\t\t\t\t\t\tmissingKeys.map((sc) =>\n\t\t\t\t\t\t\t`\\n\u00B7 ${getEnumMemberName(SecurityClass, sc)}${\n\t\t\t\t\t\t\t\tisLongRange ? \" (Long Range)\" : \"\"\n\t\t\t\t\t\t\t}`\n\t\t\t\t\t\t).join(\"\")\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\"NWI Home ID found in provisioning list, including node...\",\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst result = await this.beginInclusionSmartStart(\n\t\t\t\t\tprovisioningEntry,\n\t\t\t\t);\n\t\t\t\tif (!result) {\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t\"Smart Start inclusion could not be started\",\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start inclusion could not be started: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (msg instanceof ApplicationUpdateRequestNodeRemoved) {\n\t\t\t// A node was removed by another controller\n\t\t\tconst node = this.nodes.get(msg.nodeId);\n\t\t\tif (node) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tnode.id,\n\t\t\t\t\t\"was removed from the network by another controller\",\n\t\t\t\t);\n\n\t\t\t\tthis.emit(\"node removed\", node, RemoveNodeReason.ProxyExcluded);\n\t\t\t}\n\t\t} else if (msg instanceof ApplicationUpdateRequestNodeAdded) {\n\t\t\t// A node was included by another controller\n\t\t\tconst nodeId = msg.nodeId;\n\t\t\tconst nodeInfo = msg.nodeInformation;\n\n\t\t\t// It can happen that this is received for a node that is already part of the network:\n\t\t\t// https://github.com/zwave-js/node-zwave-js/issues/5781\n\t\t\t// In this case, ignore this message to prevent chaos.\n\n\t\t\tif (this._nodes.has(nodeId)) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Node ${nodeId} was (supposedly) included by another controller, but it is already part of the network. Ignoring the message...`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.setInclusionState(InclusionState.Busy);\n\n\t\t\tconst deviceClass = new DeviceClass(\n\t\t\t\tnodeInfo.basicDeviceClass,\n\t\t\t\tnodeInfo.genericDeviceClass,\n\t\t\t\tnodeInfo.specificDeviceClass,\n\t\t\t);\n\n\t\t\tconst newNode = new ZWaveNode(\n\t\t\t\tnodeId,\n\t\t\t\tthis.driver,\n\t\t\t\tdeviceClass,\n\t\t\t\tnodeInfo.supportedCCs,\n\t\t\t\tundefined,\n\t\t\t\t// Create an empty value DB and specify that it contains no values\n\t\t\t\t// to avoid indexing the existing values\n\t\t\t\tthis.createValueDBForNode(nodeId, new Set()),\n\t\t\t);\n\t\t\tthis._nodes.set(nodeId, newNode);\n\n\t\t\tthis.emit(\"node found\", {\n\t\t\t\tid: nodeId,\n\t\t\t\tdeviceClass,\n\t\t\t\tsupportedCCs: nodeInfo.supportedCCs,\n\t\t\t});\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Node ${newNode.id} was included by another controller:${\n\t\t\t\t\tnewNode.deviceClass\n\t\t\t\t\t\t? `\n basic device class: ${\n\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\tBasicDeviceClass,\n\t\t\t\t\t\t\t\tnewNode.deviceClass.basic,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n generic device class: ${newNode.deviceClass.generic.label}\n specific device class: ${newNode.deviceClass.specific.label}`\n\t\t\t\t\t\t: \"\"\n\t\t\t\t}\n supported CCs: ${\n\t\t\t\t\tnodeInfo.supportedCCs\n\t\t\t\t\t\t.map((cc) =>\n\t\t\t\t\t\t\t`\\n \u00B7 ${CommandClasses[cc]} (${num2hex(cc)})`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}`,\n\t\t\t);\n\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t\"Waiting for initiate command to bootstrap node...\",\n\t\t\t);\n\n\t\t\t// Handle inclusion in the background\n\t\t\tprocess.nextTick(async () => {\n\t\t\t\t// If an Inclusion Controller that does not support the Inclusion Controller Command Class includes a\n\t\t\t\t// new node in a network, the SIS will never receive an Inclusion Controller Initiate Command. If no\n\t\t\t\t// Initiate Command has been received approximately 10 seconds after a new node has been added to a\n\t\t\t\t// network, the SIS SHOULD start interviewing the newly included node\n\n\t\t\t\tconst initiate = await this.driver\n\t\t\t\t\t.waitForCommand<\n\t\t\t\t\t\tSinglecastCC<InclusionControllerCCInitiate>\n\t\t\t\t\t>(\n\t\t\t\t\t\t(cc) =>\n\t\t\t\t\t\t\tcc instanceof InclusionControllerCCInitiate\n\t\t\t\t\t\t\t&& cc.isSinglecast()\n\t\t\t\t\t\t\t&& cc.includedNodeId === nodeId\n\t\t\t\t\t\t\t&& cc.step\n\t\t\t\t\t\t\t\t=== InclusionControllerStep.ProxyInclusion,\n\t\t\t\t\t\t10000,\n\t\t\t\t\t)\n\t\t\t\t\t.catch(() => undefined);\n\n\t\t\t\t// Assume the device is alive\n\t\t\t\t// If it is actually a sleeping device, it will be marked as such later\n\t\t\t\tnewNode.markAsAlive();\n\n\t\t\t\tlet inclCtrlr: ZWaveNode | undefined;\n\t\t\t\tlet bootstrapFailure: SecurityBootstrapFailure | undefined;\n\n\t\t\t\tif (initiate) {\n\t\t\t\t\tinclCtrlr = this.nodes.getOrThrow(initiate.nodeId);\n\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t`Initiate command received from node ${inclCtrlr.id}`,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Inclusion is handled by the inclusion controller, which (hopefully) sets the SUC return route\n\t\t\t\t\tnewNode.hasSUCReturnRoute = true;\n\n\t\t\t\t\t// SIS, A, MUST request a Node Info Frame from Joining Node, B\n\t\t\t\t\tconst requestedNodeInfo = await newNode\n\t\t\t\t\t\t.requestNodeInfo()\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tif (requestedNodeInfo) {\n\t\t\t\t\t\tnewNode.updateNodeInfo(requestedNodeInfo);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Perform S0/S2 bootstrapping\n\t\t\t\t\tbootstrapFailure = await this.proxyBootstrap(\n\t\t\t\t\t\tnewNode,\n\t\t\t\t\t\tinclCtrlr,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\t// No command received, bootstrap node by ourselves\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t\"no initiate command received, bootstrapping node...\",\n\t\t\t\t\t);\n\n\t\t\t\t\tif (newNode.protocol == Protocols.ZWave) {\n\t\t\t\t\t\t// Assign SUC return route to make sure the node knows where to get its routes from\n\t\t\t\t\t\tnewNode.hasSUCReturnRoute = await this\n\t\t\t\t\t\t\t.assignSUCReturnRoutes(newNode.id);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Include using the default inclusion strategy:\n\t\t\t\t\t// * Use S2 if possible,\n\t\t\t\t\t// * only use S0 if necessary,\n\t\t\t\t\t// * use no encryption otherwise\n\t\t\t\t\tif (newNode.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS2(\n\t\t\t\t\t\t\tnewNode,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t\t\t.getHighestSecurityClass();\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tactualSecurityClass == undefined\n\t\t\t\t\t\t\t\t|| actualSecurityClass\n\t\t\t\t\t\t\t\t\t< SecurityClass.S2_Unauthenticated\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\t\tSecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (\n\t\t\t\t\t\tnewNode.supportsCC(CommandClasses.Security)\n\t\t\t\t\t\t&& (deviceClass.specific ?? deviceClass.generic)\n\t\t\t\t\t\t\t.requiresSecurity\n\t\t\t\t\t) {\n\t\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS0(\n\t\t\t\t\t\t\tnewNode,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t\t\t.getHighestSecurityClass();\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tactualSecurityClass == undefined\n\t\t\t\t\t\t\t\t|| actualSecurityClass < SecurityClass.S0_Legacy\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\t\tSecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Remember that no security classes were granted\n\t\t\t\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\t\t\t\tnewNode.securityClasses.set(secClass, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// We're done adding this node, notify listeners\n\t\t\t\tconst result: InclusionResult = bootstrapFailure != undefined\n\t\t\t\t\t? {\n\t\t\t\t\t\tlowSecurity: true,\n\t\t\t\t\t\tlowSecurityReason: bootstrapFailure,\n\t\t\t\t\t}\n\t\t\t\t\t: { lowSecurity: false };\n\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\tthis.emit(\"node added\", newNode, result);\n\n\t\t\t\tif (inclCtrlr && initiate) {\n\t\t\t\t\tconst inclCtrlrId = inclCtrlr.id;\n\t\t\t\t\tconst step = initiate.step;\n\t\t\t\t\tnewNode.once(\"ready\", () => {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t\t`Notifying node ${inclCtrlrId} of finished inclusion`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// Create API without checking for support\n\t\t\t\t\t\tconst api = inclCtrlr.createAPI(\n\t\t\t\t\t\t\tCommandClasses[\"Inclusion Controller\"],\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tvoid api\n\t\t\t\t\t\t\t.completeStep(step, InclusionControllerStatus.OK)\n\t\t\t\t\t\t\t.catch(noop);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t} else if (msg instanceof ApplicationUpdateRequestSUCIdChanged) {\n\t\t\tthis._sucNodeId = msg.sucNodeID;\n\t\t\t// TODO: Emit event or what?\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Handles replace requests from an inclusion controller\n\t */\n\tpublic handleInclusionControllerCCInitiateReplace(\n\t\tinitiate: InclusionControllerCCInitiate,\n\t): void {\n\t\tif (initiate.step !== InclusionControllerStep.ProxyInclusionReplace) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Expected an inclusion controller replace request, but got a different step\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\t\tthis.setInclusionState(InclusionState.Busy);\n\n\t\tconst inclCtrlr = this.nodes.getOrThrow(initiate.nodeId as number);\n\n\t\tconst replacedNodeId = initiate.includedNodeId;\n\t\tconst oldNode = this.nodes.get(replacedNodeId);\n\t\tif (oldNode) {\n\t\t\tthis.emit(\"node removed\", oldNode, RemoveNodeReason.ProxyReplaced);\n\t\t\tthis._nodes.delete(oldNode.id);\n\t\t}\n\n\t\t// Create a fresh node instance and forget the old one\n\t\tconst newNode = new ZWaveNode(\n\t\t\treplacedNodeId,\n\t\t\tthis.driver,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\t// Create an empty value DB and specify that it contains no values\n\t\t\t// to avoid indexing the existing values\n\t\t\tthis.createValueDBForNode(replacedNodeId, new Set()),\n\t\t);\n\t\tthis._nodes.set(newNode.id, newNode);\n\n\t\tthis.emit(\"node found\", {\n\t\t\tid: newNode.id,\n\t\t});\n\n\t\t// Assume the device is alive\n\t\t// If it is actually a sleeping device, it will be marked as such later\n\t\tnewNode.markAsAlive();\n\n\t\t// Inclusion is handled by the inclusion controller, which (hopefully) sets the SUC return route\n\t\tnewNode.hasSUCReturnRoute = true;\n\n\t\t// Handle communication with the node in the background\n\t\tprocess.nextTick(async () => {\n\t\t\t// SIS, A, MUST request a Node Info Frame from Joining Node, B\n\t\t\tconst requestedNodeInfo = await newNode\n\t\t\t\t.requestNodeInfo()\n\t\t\t\t.catch(() => undefined);\n\t\t\tif (requestedNodeInfo) {\n\t\t\t\tnewNode.updateNodeInfo(requestedNodeInfo);\n\n\t\t\t\t// TODO: Check if this stuff works for a normal replace too\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/dot-notation\n\t\t\t\tnewNode[\"deviceClass\"] = new DeviceClass(\n\t\t\t\t\trequestedNodeInfo.basicDeviceClass,\n\t\t\t\t\trequestedNodeInfo.genericDeviceClass,\n\t\t\t\t\trequestedNodeInfo.specificDeviceClass,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Perform S0/S2 bootstrapping\n\t\t\tconst bootstrapFailure = await this.proxyBootstrap(\n\t\t\t\tnewNode,\n\t\t\t\tinclCtrlr,\n\t\t\t);\n\n\t\t\t// We're done adding this node, notify listeners\n\t\t\tconst result: InclusionResult = bootstrapFailure != undefined\n\t\t\t\t? {\n\t\t\t\t\tlowSecurity: true,\n\t\t\t\t\tlowSecurityReason: bootstrapFailure,\n\t\t\t\t}\n\t\t\t\t: { lowSecurity: false };\n\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tthis.emit(\"node added\", newNode, result);\n\n\t\t\t// And notify the inclusion controller after we're done interviewing\n\t\t\tnewNode.once(\"ready\", () => {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tinclCtrlr.nodeId,\n\t\t\t\t\t`Notifying inclusion controller of finished inclusion`,\n\t\t\t\t);\n\t\t\t\t// Create API without checking for support\n\t\t\t\tconst api = inclCtrlr.createAPI(\n\t\t\t\t\tCommandClasses[\"Inclusion Controller\"],\n\t\t\t\t\tfalse,\n\t\t\t\t);\n\t\t\t\tvoid api\n\t\t\t\t\t.completeStep(initiate.step, InclusionControllerStatus.OK)\n\t\t\t\t\t.catch(noop);\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Handles bootstrapping the security keys for a node that was included by an inclusion controller\n\t */\n\tprivate async proxyBootstrap(\n\t\tnewNode: ZWaveNode,\n\t\tinclCtrlr: ZWaveNode,\n\t): Promise<SecurityBootstrapFailure | undefined> {\n\t\t// This part is to be done before the interview\n\n\t\tconst deviceClass = newNode.deviceClass!;\n\t\tlet bootstrapFailure: SecurityBootstrapFailure | undefined;\n\n\t\t// Include using the default inclusion strategy:\n\t\t// * Use S2 if possible,\n\t\t// * only use S0 if necessary,\n\t\t// * use no encryption otherwise\n\t\tif (newNode.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\tbootstrapFailure = await this.secureBootstrapS2(newNode);\n\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\tconst actualSecurityClass = newNode.getHighestSecurityClass();\n\t\t\t\tif (\n\t\t\t\t\tactualSecurityClass == undefined\n\t\t\t\t\t|| actualSecurityClass < SecurityClass.S2_Unauthenticated\n\t\t\t\t) {\n\t\t\t\t\tbootstrapFailure = SecurityBootstrapFailure.Unknown;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (\n\t\t\tnewNode.supportsCC(CommandClasses.Security)\n\t\t\t&& (deviceClass.specific ?? deviceClass.generic).requiresSecurity\n\t\t) {\n\t\t\t// S0 bootstrapping is deferred to the inclusion controller\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnewNode.id,\n\t\t\t\t`Waiting for node ${inclCtrlr.id} to perform S0 bootstrapping...`,\n\t\t\t);\n\n\t\t\tawait inclCtrlr.commandClasses[\"Inclusion Controller\"].initiateStep(\n\t\t\t\tnewNode.id,\n\t\t\t\tInclusionControllerStep.S0Inclusion,\n\t\t\t);\n\t\t\t// Wait 60s for the S0 bootstrapping to complete\n\t\t\tconst s0result = await this.driver\n\t\t\t\t.waitForCommand<InclusionControllerCCComplete>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc.nodeId === inclCtrlr.id\n\t\t\t\t\t\t&& cc instanceof InclusionControllerCCComplete\n\t\t\t\t\t\t&& cc.step === InclusionControllerStep.S0Inclusion,\n\t\t\t\t\t60000,\n\t\t\t\t)\n\t\t\t\t.catch(() => undefined);\n\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnewNode.id,\n\t\t\t\t`S0 bootstrapping ${\n\t\t\t\t\ts0result == undefined\n\t\t\t\t\t\t? \"timed out\"\n\t\t\t\t\t\t: s0result.status === InclusionControllerStatus.OK\n\t\t\t\t\t\t? \"succeeded\"\n\t\t\t\t\t\t: \"failed\"\n\t\t\t\t}`,\n\t\t\t);\n\t\t\tbootstrapFailure = s0result == undefined\n\t\t\t\t? SecurityBootstrapFailure.Timeout\n\t\t\t\t: s0result.status === InclusionControllerStatus.OK\n\t\t\t\t? undefined\n\t\t\t\t: SecurityBootstrapFailure.Unknown;\n\n\t\t\t// When bootstrapping with S0, no other keys are granted\n\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\tif (secClass !== SecurityClass.S0_Legacy) {\n\t\t\t\t\tnewNode.securityClasses.set(secClass, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Whether the S0 key is granted depends on the result\n\t\t\t// received from the inclusion controller\n\t\t\tnewNode.securityClasses.set(\n\t\t\t\tSecurityClass.S0_Legacy,\n\t\t\t\ts0result?.status === InclusionControllerStatus.OK,\n\t\t\t);\n\t\t} else {\n\t\t\t// Remember that no security classes were granted\n\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\tnewNode.securityClasses.set(secClass, false);\n\t\t\t}\n\t\t}\n\n\t\treturn bootstrapFailure;\n\t}\n\n\tprivate async secureBootstrapS0(\n\t\tnode: ZWaveNode,\n\t\tassumeSupported: boolean = false,\n\t): Promise<SecurityBootstrapFailure | undefined> {\n\t\t// When bootstrapping with S0, no other keys are granted\n\t\tfor (const secClass of securityClassOrder) {\n\t\t\tif (secClass !== SecurityClass.S0_Legacy) {\n\t\t\t\tnode.securityClasses.set(secClass, false);\n\t\t\t}\n\t\t}\n\n\t\tif (!this.driver.securityManager) {\n\t\t\t// Remember that the node was NOT granted the S0 security class\n\t\t\tnode.securityClasses.set(SecurityClass.S0_Legacy, false);\n\t\t\treturn SecurityBootstrapFailure.NoKeysConfigured;\n\t\t}\n\n\t\t// If security has been set up and we are allowed to include the node securely, try to do it\n\t\ttry {\n\t\t\t// When replacing a node, we receive no NIF, so we cannot know that the Security CC is supported.\n\t\t\t// Querying the node info however kicks some devices out of secure inclusion mode.\n\t\t\t// Therefore we must assume that the node supports Security in order to support replacing a node securely\n\t\t\tif (assumeSupported && !node.supportsCC(CommandClasses.Security)) {\n\t\t\t\tnode.addCC(CommandClasses.Security, {\n\t\t\t\t\tsecure: true,\n\t\t\t\t\tisSupported: true,\n\t\t\t\t\tversion: 1,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// At most 10s may pass between receiving each command. We enforce this twofold:\n\t\t\t// 1. by imposing a report timeout on the requests, so they don't linger too long. This does not consider\n\t\t\t// the time it takes to transmit and receiv the ACK.\n\t\t\t// 2. by imposing a timeout around the whole API call.\n\t\t\tconst S0_TIMEOUT = 10000;\n\t\t\tconst api = node.commandClasses.Security.withOptions({\n\t\t\t\treportTimeoutMs: S0_TIMEOUT,\n\t\t\t});\n\n\t\t\tconst tasks: (() => Promise<any>)[] = [\n\t\t\t\t// Request security scheme (and ignore the result), because it is required by the specs\n\t\t\t\t() => api.getSecurityScheme(),\n\t\t\t\t// Request nonce (for network key) separately, so we can impose a timeout\n\t\t\t\t() => api.getNonce(),\n\t\t\t\t// send the network key\n\t\t\t\t() =>\n\t\t\t\t\tapi.setNetworkKey(this.driver.securityManager!.networkKey),\n\t\t\t];\n\t\t\tif (this._includeController) {\n\t\t\t\t// Tell the controller which security scheme to use\n\t\t\t\ttasks.push(async () => {\n\t\t\t\t\t// Request nonce (for security scheme) manually, so it has the longer timeout\n\t\t\t\t\tawait api.getNonce();\n\t\t\t\t\tawait api.inheritSecurityScheme();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tfor (const task of tasks) {\n\t\t\t\tconst result = await Promise.race([\n\t\t\t\t\twait(S0_TIMEOUT, true).then(() => false as const),\n\t\t\t\t\ttask().catch(() => false as const),\n\t\t\t\t]);\n\t\t\t\tif (result === false) {\n\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t`A secure inclusion timer has elapsed`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_NodeTimeout,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remember that the node was granted the S0 security class\n\t\t\tnode.securityClasses.set(SecurityClass.S0_Legacy, true);\n\n\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\tmessage: `Security S0 bootstrapping successful`,\n\t\t\t});\n\n\t\t\t// success \uD83C\uDF89\n\t\t} catch (e) {\n\t\t\tlet errorMessage =\n\t\t\t\t`Security S0 bootstrapping failed, the node was not granted the S0 security class`;\n\t\t\tlet failure: SecurityBootstrapFailure =\n\t\t\t\tSecurityBootstrapFailure.Unknown;\n\t\t\tif (!isZWaveError(e)) {\n\t\t\t\terrorMessage += `: ${e as any}`;\n\t\t\t} else if (\n\t\t\t\te.code !== ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t&& e.code !== ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t) {\n\t\t\t\terrorMessage += `: ${e.message}`;\n\t\t\t\tfailure = SecurityBootstrapFailure.Timeout;\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(node.id, errorMessage, \"warn\");\n\t\t\t// Remember that the node was NOT granted the S0 security class\n\t\t\tnode.securityClasses.set(SecurityClass.S0_Legacy, false);\n\t\t\tnode.removeCC(CommandClasses.Security);\n\n\t\t\treturn failure;\n\t\t}\n\t}\n\n\tprivate _bootstrappingS2NodeId: number | undefined;\n\t/**\n\t * @internal\n\t * Returns which node is currently being bootstrapped with S2\n\t */\n\tpublic get bootstrappingS2NodeId(): number | undefined {\n\t\treturn this._bootstrappingS2NodeId;\n\t}\n\n\tprivate cancelBootstrapS2Promise: DeferredPromise<KEXFailType> | undefined;\n\tpublic cancelSecureBootstrapS2(reason: KEXFailType): void {\n\t\tif (this.cancelBootstrapS2Promise) {\n\t\t\tthis.cancelBootstrapS2Promise.resolve(reason);\n\t\t\tthis.cancelBootstrapS2Promise = undefined;\n\t\t}\n\t}\n\n\tprivate async secureBootstrapS2(\n\t\tnode: ZWaveNode,\n\t\tassumeSupported: boolean = false,\n\t): Promise<SecurityBootstrapFailure | undefined> {\n\t\tconst unGrantSecurityClasses = () => {\n\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\tnode.securityClasses.set(secClass, false);\n\t\t\t}\n\t\t};\n\n\t\tconst securityManager = node.protocol === Protocols.ZWaveLongRange\n\t\t\t? this.driver.securityManagerLR\n\t\t\t: this.driver.securityManager2;\n\n\t\tif (!securityManager) {\n\t\t\t// Remember that the node was NOT granted any S2 security classes\n\t\t\tunGrantSecurityClasses();\n\t\t\treturn SecurityBootstrapFailure.NoKeysConfigured;\n\t\t}\n\n\t\tlet userCallbacks: InclusionUserCallbacks;\n\t\tconst inclusionOptions = this._inclusionOptions as\n\t\t\t| (InclusionOptionsInternal & {\n\t\t\t\t// This is the type when we end up here during normal inclusion\n\t\t\t\tstrategy:\n\t\t\t\t\t| InclusionStrategy.Security_S2\n\t\t\t\t\t| InclusionStrategy.SmartStart;\n\t\t\t})\n\t\t\t// And this when we do proxy bootstrapping for an inclusion controller\n\t\t\t| undefined;\n\t\tif (\n\t\t\tinclusionOptions\n\t\t\t&& \"provisioning\" in inclusionOptions\n\t\t\t&& !!inclusionOptions.provisioning\n\t\t) {\n\t\t\tconst grantedSecurityClasses =\n\t\t\t\tinclusionOptions.provisioning.securityClasses;\n\t\t\tconst fullDSK = inclusionOptions.provisioning.dsk;\n\t\t\t// SmartStart and S2 with QR code are pre-provisioned, so we don't need to ask the user for anything\n\t\t\tuserCallbacks = {\n\t\t\t\tabort() {},\n\t\t\t\tgrantSecurityClasses: (requested) => {\n\t\t\t\t\treturn Promise.resolve({\n\t\t\t\t\t\tclientSideAuth: false,\n\t\t\t\t\t\tsecurityClasses: requested.securityClasses.filter((r) =>\n\t\t\t\t\t\t\tgrantedSecurityClasses.includes(r)\n\t\t\t\t\t\t),\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tvalidateDSKAndEnterPIN: (dsk) => {\n\t\t\t\t\tconst pin = fullDSK.slice(0, 5);\n\t\t\t\t\t// Make sure the DSK matches\n\t\t\t\t\tif (pin + dsk !== fullDSK) return Promise.resolve(false);\n\t\t\t\t\treturn Promise.resolve(pin);\n\t\t\t\t},\n\t\t\t};\n\t\t} else if (\n\t\t\tinclusionOptions\n\t\t\t&& \"userCallbacks\" in inclusionOptions\n\t\t\t&& !!inclusionOptions.userCallbacks\n\t\t) {\n\t\t\t// Use the callbacks provided to this inclusion attempt\n\t\t\tuserCallbacks = inclusionOptions.userCallbacks;\n\t\t} else if (this.driver.options.inclusionUserCallbacks) {\n\t\t\t// Use the callbacks defined in the driver options as fallback\n\t\t\tuserCallbacks = this.driver.options.inclusionUserCallbacks;\n\t\t} else {\n\t\t\t// Cannot bootstrap S2 without user callbacks, abort.\n\t\t\t// Remember that the node was NOT granted any S2 security classes\n\t\t\tunGrantSecurityClasses();\n\t\t\treturn SecurityBootstrapFailure.S2NoUserCallbacks;\n\t\t}\n\n\t\t// When replacing a node, we receive no NIF, so we cannot know that the Security CC is supported.\n\t\t// Querying the node info however kicks some devices out of secure inclusion mode.\n\t\t// Therefore we must assume that the node supports Security in order to support replacing a node securely\n\t\tif (assumeSupported && !node.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\tnode.addCC(CommandClasses[\"Security 2\"], {\n\t\t\t\tsecure: true,\n\t\t\t\tisSupported: true,\n\t\t\t\tversion: 1,\n\t\t\t});\n\t\t}\n\n\t\tconst deleteTempKey = () => {\n\t\t\t// Whatever happens, no further communication needs the temporary key\n\t\t\tsecurityManager.deleteNonce(node.id);\n\t\t\tsecurityManager.tempKeys.delete(node.id);\n\t\t};\n\n\t\t// Allow canceling the bootstrapping process\n\t\tthis._bootstrappingS2NodeId = node.id;\n\t\tthis.cancelBootstrapS2Promise = createDeferredPromise();\n\n\t\ttry {\n\t\t\tconst api = node.commandClasses[\"Security 2\"].withOptions({\n\t\t\t\t// Do not wait for Nonce Reports after SET-type commands.\n\t\t\t\t// Timing is critical here\n\t\t\t\ts2VerifyDelivery: false,\n\t\t\t});\n\t\t\tconst abort = async (failType?: KEXFailType): Promise<void> => {\n\t\t\t\tif (failType != undefined) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait api.abortKeyExchange(failType);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Un-grant S2 security classes we might have granted\n\t\t\t\tunGrantSecurityClasses();\n\t\t\t\tdeleteTempKey();\n\t\t\t\t// We're no longer bootstrapping\n\t\t\t\tthis._bootstrappingS2NodeId = undefined;\n\t\t\t\tthis.cancelBootstrapS2Promise = undefined;\n\t\t\t};\n\n\t\t\tconst abortUser = async () => {\n\t\t\t\tsetImmediate(() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserCallbacks.abort();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// ignore errors in application callbacks\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.BootstrappingCanceled);\n\t\t\t\treturn SecurityBootstrapFailure.UserCanceled;\n\t\t\t};\n\n\t\t\tconst abortTimeout = async () => {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: a secure inclusion timer has elapsed`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\n\t\t\t\tawait abort();\n\t\t\t\treturn SecurityBootstrapFailure.Timeout;\n\t\t\t};\n\n\t\t\t// Ask the node for its desired security classes and key exchange params\n\t\t\tconst kexParams = await api\n\t\t\t\t.withOptions({ reportTimeoutMs: inclusionTimeouts.TA1 })\n\t\t\t\t.getKeyExchangeParameters();\n\t\t\tif (!kexParams) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: did not receive the node's desired security classes.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort();\n\t\t\t\treturn SecurityBootstrapFailure.Timeout;\n\t\t\t}\n\n\t\t\t// Validate the response\n\t\t\t// Echo flag must be false\n\t\t\tif (kexParams.echo) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: KEX Report unexpectedly has the echo flag set.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoVerify);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t}\n\t\t\t// At the time of implementation, only KEXScheme1 and Curve25519 are defined.\n\t\t\t// The certification testing ensures that no other bits are set, so we need to check that too.\n\t\t\t// Not sure why this choice is made, since it essentially breaks any forwards compatibility\n\t\t\tif (\n\t\t\t\tkexParams.supportedKEXSchemes.length !== 1\n\t\t\t\t|| !kexParams.supportedKEXSchemes.includes(\n\t\t\t\t\tKEXSchemes.KEXScheme1,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: No supported key exchange scheme or invalid list.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoSupportedScheme);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t} else if (\n\t\t\t\tkexParams.supportedECDHProfiles.length !== 1\n\t\t\t\t|| !kexParams.supportedECDHProfiles.includes(\n\t\t\t\t\tECDHProfiles.Curve25519,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: No supported ECDH profile or invalid list.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoSupportedCurve);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t} else if (kexParams.requestCSA) {\n\t\t\t\t// We do not support CSA at the moment, so it is never granted.\n\t\t\t\t// Alternatively, filter out S2 Authenticated and S2 Access Control\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: CSA requested but not granted.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.BootstrappingCanceled);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t}\n\n\t\t\tconst supportedKeys = kexParams.requestedKeys.filter((k) =>\n\t\t\t\tsecurityClassOrder.includes(k as any)\n\t\t\t);\n\t\t\tif (!supportedKeys.length) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: None of the requested security classes are supported.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoKeyMatch);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t}\n\n\t\t\t// TODO: Validate client-side auth if requested\n\t\t\tconst grantResult = await Promise.race([\n\t\t\t\twait(inclusionTimeouts.TAI1, true).then(() => false as const),\n\t\t\t\tuserCallbacks\n\t\t\t\t\t.grantSecurityClasses({\n\t\t\t\t\t\tsecurityClasses: supportedKeys,\n\t\t\t\t\t\tclientSideAuth: false,\n\t\t\t\t\t})\n\t\t\t\t\t// ignore errors in application callbacks\n\t\t\t\t\t.catch(() => false as const),\n\t\t\t]);\n\t\t\tif (grantResult === false) {\n\t\t\t\t// There was a timeout or the user did not confirm the request, abort\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: User rejected the requested security classes or interaction timed out.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\treturn abortUser();\n\t\t\t}\n\t\t\tconst grantedKeys = supportedKeys.filter((k) =>\n\t\t\t\tgrantResult.securityClasses.includes(k)\n\t\t\t);\n\t\t\tif (!grantedKeys.length) {\n\t\t\t\t// The user did not grant any of the requested keys\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: None of the requested keys were granted by the user.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\treturn abortUser();\n\t\t\t}\n\n\t\t\t// Tell the node how we want the inclusion to go and grant it the keys\n\t\t\t// It will send its public key in response\n\t\t\tawait api.grantKeys({\n\t\t\t\tgrantedKeys,\n\t\t\t\tpermitCSA: false,\n\t\t\t\tselectedECDHProfile: ECDHProfiles.Curve25519,\n\t\t\t\tselectedKEXScheme: KEXSchemes.KEXScheme1,\n\t\t\t});\n\n\t\t\tconst pubKeyResponse = await this.driver.waitForCommand<\n\t\t\t\tSecurity2CCPublicKeyReport | Security2CCKEXFail\n\t\t\t>(\n\t\t\t\t(cc) =>\n\t\t\t\t\tcc instanceof Security2CCPublicKeyReport\n\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\tinclusionTimeouts.TA2,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (pubKeyResponse === \"timeout\") return abortTimeout();\n\t\t\tif (\n\t\t\t\tpubKeyResponse instanceof Security2CCKEXFail\n\t\t\t\t|| pubKeyResponse.includingNode\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`The joining node canceled the Security S2 bootstrapping.`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort();\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t}\n\t\t\tconst nodePublicKey = Bytes.from(pubKeyResponse.publicKey);\n\n\t\t\t// This is the starting point of the timer TAI2.\n\t\t\tconst timerStartTAI2 = Date.now();\n\n\t\t\t// Generate ECDH key pair. We need to immediately send the other node our public key,\n\t\t\t// so it won't abort bootstrapping\n\t\t\tconst keyPair = generateECDHKeyPairSync();\n\t\t\tconst publicKey = extractRawECDHPublicKeySync(keyPair.publicKey);\n\t\t\tawait api.sendPublicKey(publicKey);\n\t\t\t// After this, the node will start sending us a KEX SET every 10 seconds.\n\t\t\t// We won't be able to decode it until the DSK was verified\n\n\t\t\tif (\n\t\t\t\tgrantedKeys.includes(SecurityClass.S2_AccessControl)\n\t\t\t\t|| grantedKeys.includes(SecurityClass.S2_Authenticated)\n\t\t\t) {\n\t\t\t\t// For authenticated encryption, the DSK (first 16 bytes of the public key) is obfuscated (missing the first 2 bytes)\n\t\t\t\t// Request the user to enter the missing part as a 5-digit PIN\n\t\t\t\tconst dsk = dskToString(nodePublicKey.subarray(0, 16)).slice(5);\n\n\t\t\t\t// The time the user has to enter the PIN is limited by the timeout TAI2\n\t\t\t\tconst tai2RemainingMs = inclusionTimeouts.TAI2\n\t\t\t\t\t- (Date.now() - timerStartTAI2);\n\n\t\t\t\tlet pinResult: string | false;\n\t\t\t\tif (\n\t\t\t\t\tinclusionOptions\n\t\t\t\t\t&& \"dsk\" in inclusionOptions\n\t\t\t\t\t&& typeof inclusionOptions.dsk === \"string\"\n\t\t\t\t\t&& isValidDSK(inclusionOptions.dsk)\n\t\t\t\t) {\n\t\t\t\t\tpinResult = inclusionOptions.dsk.slice(0, 5);\n\t\t\t\t} else {\n\t\t\t\t\tpinResult = await Promise.race([\n\t\t\t\t\t\twait(tai2RemainingMs, true).then(() => false as const),\n\t\t\t\t\t\tuserCallbacks\n\t\t\t\t\t\t\t.validateDSKAndEnterPIN(dsk)\n\t\t\t\t\t\t\t// ignore errors in application callbacks\n\t\t\t\t\t\t\t.catch(() => false as const),\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\ttypeof pinResult !== \"string\"\n\t\t\t\t\t|| !/^\\d{5}$/.test(pinResult)\n\t\t\t\t) {\n\t\t\t\t\t// There was a timeout, the user did not confirm the DSK or entered an invalid PIN\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: User rejected the DSK, entered an invalid PIN or the interaction timed out.`,\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\treturn abortUser();\n\t\t\t\t}\n\n\t\t\t\t// Fill in the missing two bytes of the public key\n\t\t\t\tnodePublicKey.writeUInt16BE(parseInt(pinResult, 10), 0);\n\t\t\t}\n\n\t\t\t// After the user has verified the DSK, we can derive the shared secret\n\t\t\t// Z-Wave works with the \"raw\" keys, so this is a tad complicated\n\t\t\tconst sharedSecret = crypto.diffieHellman({\n\t\t\t\tpublicKey: importRawECDHPublicKeySync(nodePublicKey),\n\t\t\t\tprivateKey: keyPair.privateKey,\n\t\t\t});\n\n\t\t\t// Derive temporary key from ECDH key pair - this will allow us to receive the node's KEX SET commands\n\t\t\tconst tempKeys = await deriveTempKeysAsync(\n\t\t\t\tawait computePRKAsync(sharedSecret, publicKey, nodePublicKey),\n\t\t\t);\n\t\t\tsecurityManager.deleteNonce(node.id);\n\t\t\tsecurityManager.tempKeys.set(node.id, {\n\t\t\t\tkeyCCM: tempKeys.tempKeyCCM,\n\t\t\t\tpersonalizationString: tempKeys.tempPersonalizationString,\n\t\t\t});\n\n\t\t\t// Now wait for the next KEXSet from the node (if there is even time left)\n\t\t\tconst tai2RemainingMs = inclusionTimeouts.TAI2\n\t\t\t\t- (Date.now() - timerStartTAI2);\n\t\t\tif (tai2RemainingMs < 1) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: a secure inclusion timer has elapsed`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\treturn abortUser();\n\t\t\t}\n\n\t\t\tconst kexSetEcho = await Promise.race([\n\t\t\t\tthis.driver.waitForCommand<\n\t\t\t\t\tSecurity2CCKEXSet | Security2CCKEXFail\n\t\t\t\t>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc instanceof Security2CCKEXSet\n\t\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\t\ttai2RemainingMs,\n\t\t\t\t).catch(() => \"timeout\" as const),\n\t\t\t\tthis.cancelBootstrapS2Promise,\n\t\t\t]);\n\t\t\tif (kexSetEcho === \"timeout\") return abortTimeout();\n\t\t\tif (typeof kexSetEcho === \"number\") {\n\t\t\t\t// The bootstrapping process was canceled - this is most likely because the PIN was incorrect\n\t\t\t\t// and the node's commands cannot be decoded\n\t\t\t\tawait abort(kexSetEcho);\n\t\t\t\treturn SecurityBootstrapFailure.S2IncorrectPIN;\n\t\t\t}\n\t\t\t// Validate that the received command contains the correct list of keys\n\t\t\tif (kexSetEcho instanceof Security2CCKEXFail) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`The joining node canceled the Security S2 bootstrapping.`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort();\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (!kexSetEcho.echo) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: KEXSet received without echo flag`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (kexSetEcho._reserved !== 0) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Invalid KEXSet received`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (\n\t\t\t\t!kexSetEcho.isEncapsulatedWith(\n\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Command received without encryption`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t} else if (\n\t\t\t\tkexSetEcho.grantedKeys.length !== grantedKeys.length\n\t\t\t\t|| !kexSetEcho.grantedKeys.every((k) => grantedKeys.includes(k))\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Granted key mismatch.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t}\n\t\t\t// Confirm the keys - the node will start requesting the granted keys in response\n\t\t\tawait api.confirmRequestedKeys({\n\t\t\t\trequestCSA: kexParams.requestCSA,\n\t\t\t\trequestedKeys: [...kexParams.requestedKeys],\n\t\t\t\tsupportedECDHProfiles: [...kexParams.supportedECDHProfiles],\n\t\t\t\tsupportedKEXSchemes: [...kexParams.supportedKEXSchemes],\n\t\t\t\t_reserved: kexParams._reserved,\n\t\t\t});\n\n\t\t\tfor (let i = 0; i < grantedKeys.length; i++) {\n\t\t\t\t// Wait for the key request\n\t\t\t\tconst keyRequest = await this.driver.waitForCommand<\n\t\t\t\t\tSecurity2CCNetworkKeyGet | Security2CCKEXFail\n\t\t\t\t>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc instanceof Security2CCNetworkKeyGet\n\t\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\t\tinclusionTimeouts.TA3,\n\t\t\t\t).catch(() => \"timeout\" as const);\n\t\t\t\tif (keyRequest === \"timeout\") {\n\t\t\t\t\treturn abortTimeout();\n\t\t\t\t} else if (keyRequest instanceof Security2CCKEXFail) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`The joining node canceled the Security S2 bootstrapping.`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort();\n\t\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t\t} else if (\n\t\t\t\t\t!keyRequest.isEncapsulatedWith(\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Command received without encryption`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t}\n\n\t\t\t\tconst securityClass = keyRequest.requestedKey;\n\t\t\t\t// Ensure it was received encrypted with the temporary key\n\t\t\t\tif (\n\t\t\t\t\t!securityManager.hasUsedSecurityClass(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\tSecurityClass.Temporary,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Node used wrong key to communicate.`,\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t} else if (!grantedKeys.includes(securityClass)) {\n\t\t\t\t\t// and that the requested key is one of the granted keys\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Node used key it was not granted.`,\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.KeyNotGranted);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t}\n\n\t\t\t\t// We need to temporarily mark this security class as granted, so the following exchange will use this\n\t\t\t\t// key for decryption\n\t\t\t\t// FIXME: Is this actually necessary?\n\t\t\t\tnode.securityClasses.set(securityClass, true);\n\n\t\t\t\t// Send the node the requested key\n\t\t\t\tawait api.sendNetworkKey(\n\t\t\t\t\tsecurityClass,\n\t\t\t\t\tsecurityManager.getKeysForSecurityClass(\n\t\t\t\t\t\tsecurityClass,\n\t\t\t\t\t).pnk,\n\t\t\t\t);\n\n\t\t\t\t// And wait for verification\n\t\t\t\tconst verify = await this.driver.waitForCommand<\n\t\t\t\t\tSecurity2CCNetworkKeyVerify | Security2CCKEXFail\n\t\t\t\t>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc instanceof Security2CCNetworkKeyVerify\n\t\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\t\tinclusionTimeouts.TA4,\n\t\t\t\t).catch(() => \"timeout\" as const);\n\t\t\t\tif (verify === \"timeout\") return abortTimeout();\n\n\t\t\t\tif (verify instanceof Security2CCKEXFail) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`The joining node canceled the Security S2 bootstrapping.`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort();\n\t\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t!securityManager.hasUsedSecurityClass(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\tsecurityClass,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Node used wrong key to communicate.`,\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.NoVerify);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t}\n\n\t\t\t\t// Tell the node that verification was successful. We need to reset the SPAN state\n\t\t\t\t// so the temporary key will be used again. Also we don't know in which order the node requests the keys\n\t\t\t\t// so our logic to use the highest security class for decryption might be problematic. Therefore delete the\n\t\t\t\t// security class for now.\n\t\t\t\tnode.securityClasses.delete(securityClass);\n\t\t\t\tsecurityManager.deleteNonce(node.id);\n\t\t\t\tawait api.confirmKeyVerification();\n\t\t\t}\n\n\t\t\t// After all keys were sent and verified, we need to wait for the node to confirm that it is done\n\t\t\tconst transferEnd = await this.driver.waitForCommand<\n\t\t\t\tSecurity2CCTransferEnd\n\t\t\t>(\n\t\t\t\t(cc) => cc instanceof Security2CCTransferEnd,\n\t\t\t\tinclusionTimeouts.TA5,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (transferEnd === \"timeout\") return abortTimeout();\n\t\t\tif (!transferEnd.keyRequestComplete) {\n\t\t\t\t// S2 bootstrapping failed\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Node did not confirm completion of the key exchange`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoVerify);\n\t\t\t\treturn SecurityBootstrapFailure.Timeout;\n\t\t\t}\n\n\t\t\t// Remember all security classes we have granted\n\t\t\tfor (const securityClass of securityClassOrder) {\n\t\t\t\tnode.securityClasses.set(\n\t\t\t\t\tsecurityClass,\n\t\t\t\t\tgrantedKeys.includes(securityClass),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Remember the DSK (first 16 bytes of the public key)\n\t\t\tnode.dsk = nodePublicKey.subarray(0, 16);\n\n\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Security S2 bootstrapping successful with these security classes:${\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t...node.securityClasses.entries(),\n\t\t\t\t\t\t]\n\t\t\t\t\t\t\t.filter(([, v]) => v)\n\t\t\t\t\t\t\t.map(([k]) =>\n\t\t\t\t\t\t\t\t`\\n\u00B7 ${getEnumMemberName(SecurityClass, k)}`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.join(\"\")\n\t\t\t\t\t}`,\n\t\t\t});\n\n\t\t\t// success \uD83C\uDF89\n\t\t} catch (e) {\n\t\t\tlet errorMessage =\n\t\t\t\t`Security S2 bootstrapping failed, the node was not granted any S2 security class`;\n\t\t\tlet result = SecurityBootstrapFailure.Unknown;\n\t\t\tif (!isZWaveError(e)) {\n\t\t\t\terrorMessage += `: ${e as any}`;\n\t\t\t} else if (e.code === ZWaveErrorCodes.Controller_MessageExpired) {\n\t\t\t\terrorMessage += \": a secure inclusion timer has elapsed.\";\n\t\t\t\tresult = SecurityBootstrapFailure.Timeout;\n\t\t\t} else if (\n\t\t\t\te.code !== ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t&& e.code !== ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t) {\n\t\t\t\terrorMessage += `: ${e.message}`;\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(node.id, errorMessage, \"warn\");\n\t\t\t// Remember that the node was NOT granted any S2 security classes\n\t\t\tunGrantSecurityClasses();\n\t\t\tnode.removeCC(CommandClasses[\"Security 2\"]);\n\n\t\t\treturn result;\n\t\t} finally {\n\t\t\t// Whatever happens, no further communication needs the temporary key\n\t\t\tdeleteTempKey();\n\t\t\t// And we're no longer bootstrapping\n\t\t\tthis._bootstrappingS2NodeId = undefined;\n\t\t\tthis.cancelBootstrapS2Promise = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Is called when an AddNode request is received from the controller.\n\t * Handles and controls the inclusion process.\n\t */\n\tprivate async handleAddNodeStatusReport(\n\t\tmsg: AddNodeToNetworkRequestStatusReport,\n\t): Promise<boolean> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`handling add node request (status = ${AddNodeStatus[msg.status]})`,\n\t\t);\n\n\t\tif (\n\t\t\tthis._inclusionState !== InclusionState.Including\n\t\t\t|| this._inclusionOptions == undefined\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t` inclusion is NOT active, ignoring it...`,\n\t\t\t);\n\t\t\treturn true; // Don't invoke any more handlers\n\t\t}\n\n\t\tswitch (msg.status) {\n\t\t\tcase AddNodeStatus.Failed:\n\t\t\t\t// This code is handled elsewhere for starting the inclusion, so this means\n\t\t\t\t// that adding a node failed\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Adding the node failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthis.emit(\"inclusion failed\");\n\n\t\t\t\t// in any case, stop the inclusion process so we don't accidentally add another node\n\t\t\t\ttry {\n\t\t\t\t\tawait this.stopInclusion();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ok */\n\t\t\t\t}\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\tcase AddNodeStatus.AddingController:\n\t\t\t\tthis._includeController = true;\n\t\t\t// fall through!\n\t\t\tcase AddNodeStatus.AddingSlave: {\n\t\t\t\t// this is called when a new node is added\n\t\t\t\tthis._nodePendingInclusion = new ZWaveNode(\n\t\t\t\t\tmsg.statusContext!.nodeId,\n\t\t\t\t\tthis.driver,\n\t\t\t\t\tnew DeviceClass(\n\t\t\t\t\t\tmsg.statusContext!.basicDeviceClass!,\n\t\t\t\t\t\tmsg.statusContext!.genericDeviceClass!,\n\t\t\t\t\t\tmsg.statusContext!.specificDeviceClass!,\n\t\t\t\t\t),\n\t\t\t\t\tmsg.statusContext!.supportedCCs,\n\t\t\t\t\tmsg.statusContext!.controlledCCs,\n\t\t\t\t\t// Create an empty value DB and specify that it contains no values\n\t\t\t\t\t// to avoid indexing the existing values\n\t\t\t\t\tthis.createValueDBForNode(\n\t\t\t\t\t\tmsg.statusContext!.nodeId,\n\t\t\t\t\t\tnew Set(),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\t// TODO: According to INS13954, there are several more steps and different timeouts when including a controller\n\t\t\t\t// For now do the absolute minimum - that is include the controller\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\t}\n\t\t\tcase AddNodeStatus.ProtocolDone: {\n\t\t\t\t// this is called after a new node is added\n\t\t\t\t// stop the inclusion process so we don't accidentally add another node\n\t\t\t\tlet nodeId: number | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tnodeId = await this.finishInclusion();\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore the error\n\t\t\t\t}\n\n\t\t\t\t// It is recommended to send another STOP command to the controller\n\t\t\t\ttry {\n\t\t\t\t\tawait this.stopInclusionNoCallback();\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore the error\n\t\t\t\t}\n\n\t\t\t\tif (!nodeId || !this._nodePendingInclusion) {\n\t\t\t\t\t// The inclusion did not succeed\n\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\tthis._nodePendingInclusion = undefined;\n\t\t\t\t\treturn true;\n\t\t\t\t} else if (\n\t\t\t\t\tnodeId === NODE_ID_BROADCAST\n\t\t\t\t\t|| nodeId === NODE_ID_BROADCAST_LR\n\t\t\t\t) {\n\t\t\t\t\t// No idea how this can happen but it dit at least once\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t`Cannot add a node with the broadcast node ID, aborting...`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\tthis._nodePendingInclusion = undefined;\n\t\t\t\t\treturn true;\n\t\t\t\t} else if (this._nodes.has(nodeId)) {\n\t\t\t\t\t// Someone tried to include a node again, although it is part of the network already.\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t`Cannot add node ${nodeId} as it is already part of the network. Aborting...`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\tthis._nodePendingInclusion = undefined;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// We're technically done with the inclusion but should not include\n\t\t\t\t// anything else until the node has been bootstrapped\n\t\t\t\tthis.setInclusionState(InclusionState.Busy);\n\n\t\t\t\t// Inclusion is now completed, bootstrap the node\n\t\t\t\tconst newNode = this._nodePendingInclusion;\n\n\t\t\t\tconst supportedCCs = [\n\t\t\t\t\t...newNode.implementedCommandClasses.entries(),\n\t\t\t\t]\n\t\t\t\t\t.filter(([, info]) => info.isSupported)\n\t\t\t\t\t.map(([cc]) => cc);\n\t\t\t\tconst controlledCCs = [\n\t\t\t\t\t...newNode.implementedCommandClasses.entries(),\n\t\t\t\t]\n\t\t\t\t\t.filter(([, info]) => info.isControlled)\n\t\t\t\t\t.map(([cc]) => cc);\n\n\t\t\t\tthis.emit(\"node found\", {\n\t\t\t\t\tid: newNode.id,\n\t\t\t\t\tdeviceClass: newNode.deviceClass,\n\t\t\t\t\tsupportedCCs,\n\t\t\t\t\tcontrolledCCs,\n\t\t\t\t});\n\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`finished adding node ${newNode.id}:${\n\t\t\t\t\t\tnewNode.deviceClass\n\t\t\t\t\t\t\t? `\n basic device class: ${\n\t\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\t\tBasicDeviceClass,\n\t\t\t\t\t\t\t\t\tnewNode.deviceClass.basic,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n generic device class: ${newNode.deviceClass.generic.label}\n specific device class: ${newNode.deviceClass.specific.label}`\n\t\t\t\t\t\t\t: \"\"\n\t\t\t\t\t}\n supported CCs: ${\n\t\t\t\t\t\tsupportedCCs\n\t\t\t\t\t\t\t.map((cc) =>\n\t\t\t\t\t\t\t\t`\\n \u00B7 ${CommandClasses[cc]} (${num2hex(cc)})`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.join(\"\")\n\t\t\t\t\t}\n controlled CCs: ${\n\t\t\t\t\t\tcontrolledCCs\n\t\t\t\t\t\t\t.map((cc) =>\n\t\t\t\t\t\t\t\t`\\n \u00B7 ${CommandClasses[cc]} (${num2hex(cc)})`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.join(\"\")\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t\t// remember the node\n\t\t\t\tthis._nodes.set(newNode.id, newNode);\n\t\t\t\tthis._nodePendingInclusion = undefined;\n\n\t\t\t\t// We're communicating with the device, so assume it is alive\n\t\t\t\t// If it is actually a sleeping device, it will be marked as such later\n\t\t\t\tnewNode.markAsAlive();\n\n\t\t\t\tif (newNode.protocol == Protocols.ZWave) {\n\t\t\t\t\t// Assign SUC return route to make sure the node knows where to get its routes from\n\t\t\t\t\tnewNode.hasSUCReturnRoute = await this\n\t\t\t\t\t\t.assignSUCReturnRoutes(\n\t\t\t\t\t\t\tnewNode.id,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst opts = this._inclusionOptions;\n\n\t\t\t\tlet bootstrapFailure: SecurityBootstrapFailure | undefined;\n\t\t\t\tlet smartStartFailed = false;\n\n\t\t\t\t// A controller performing a SmartStart network inclusion shall perform S2 bootstrapping,\n\t\t\t\t// even if the joining node does not show the S2 Command Class in its supported Command Class list.\n\t\t\t\tlet forceAddedS2Support = false;\n\t\t\t\tif (\n\t\t\t\t\topts.strategy === InclusionStrategy.SmartStart\n\t\t\t\t\t&& !newNode.supportsCC(CommandClasses[\"Security 2\"])\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(newNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"does not list S2 as supported, but was included using SmartStart which implies S2 support.\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\n\t\t\t\t\tforceAddedS2Support = true;\n\t\t\t\t\tnewNode.addCC(CommandClasses[\"Security 2\"], {\n\t\t\t\t\t\tisSupported: true,\n\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// The default inclusion strategy is: Use S2 if possible, only use S0 if necessary, use no encryption otherwise\n\t\t\t\tif (\n\t\t\t\t\tnewNode.supportsCC(CommandClasses[\"Security 2\"])\n\t\t\t\t\t&& (opts.strategy === InclusionStrategy.Default\n\t\t\t\t\t\t|| opts.strategy === InclusionStrategy.Security_S2\n\t\t\t\t\t\t|| opts.strategy === InclusionStrategy.SmartStart)\n\t\t\t\t) {\n\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS2(newNode);\n\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t.getHighestSecurityClass();\n\n\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\tif (actualSecurityClass == SecurityClass.S0_Legacy) {\n\t\t\t\t\t\t\t// Notify user about potential S0 downgrade attack.\n\t\t\t\t\t\t\t// S0 is considered insecure if both controller and node are S2-capable\n\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\tSecurityBootstrapFailure.S0Downgrade;\n\n\t\t\t\t\t\t\tthis.driver.controllerLog.logNode(newNode.id, {\n\t\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\t\"Possible S0 downgrade attack detected!\",\n\t\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (!securityClassIsS2(actualSecurityClass)) {\n\t\t\t\t\t\t\tbootstrapFailure = SecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (opts.strategy === InclusionStrategy.SmartStart) {\n\t\t\t\t\t\tsmartStartFailed = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tforceAddedS2Support\n\t\t\t\t\t\t&& !securityClassIsS2(actualSecurityClass)\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Remove the fake S2 support again\n\t\t\t\t\t\tnewNode.removeCC(CommandClasses[\"Security 2\"]);\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tnewNode.supportsCC(CommandClasses.Security)\n\t\t\t\t\t&& (opts.strategy === InclusionStrategy.Security_S0\n\t\t\t\t\t\t|| (opts.strategy === InclusionStrategy.Default\n\t\t\t\t\t\t\t&& (opts.forceSecurity\n\t\t\t\t\t\t\t\t|| (\n\t\t\t\t\t\t\t\t\tnewNode.deviceClass?.specific\n\t\t\t\t\t\t\t\t\t\t?? newNode.deviceClass?.generic\n\t\t\t\t\t\t\t\t)?.requiresSecurity)))\n\t\t\t\t) {\n\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS0(newNode);\n\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t\t.getHighestSecurityClass();\n\t\t\t\t\t\tif (actualSecurityClass == SecurityClass.S0_Legacy) {\n\t\t\t\t\t\t\t// If the user chose this, i.e. InclusionStrategy.Security_S0 was used,\n\t\t\t\t\t\t\t// then this is the expected outcome and not a failure\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\topts.strategy !== InclusionStrategy.Security_S0\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// S0 is considered insecure if both controller and node are S2-capable\n\t\t\t\t\t\t\t\tconst nif = await newNode\n\t\t\t\t\t\t\t\t\t.requestNodeInfo()\n\t\t\t\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tnif?.supportedCCs.includes(\n\t\t\t\t\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t// Notify user about potential S0 downgrade attack.\n\t\t\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\t\t\tSecurityBootstrapFailure.S0Downgrade;\n\n\t\t\t\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\t\t\t\tnewNode.id,\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\t\t\t\t\"Possible S0 downgrade attack detected!\",\n\t\t\t\t\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbootstrapFailure = SecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Remember that no security classes were granted\n\t\t\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\t\t\tnewNode.securityClasses.set(secClass, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis._includeController = false;\n\n\t\t\t\t// After an unsuccessful SmartStart inclusion, the node MUST leave the network and return to SmartStart learn mode\n\t\t\t\t// The controller should consider the node to be failed.\n\t\t\t\tif (smartStartFailed) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(newNode.id, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\"SmartStart inclusion failed. Checking if the node needs to be removed.\",\n\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tawait this.removeFailedNodeInternal(\n\t\t\t\t\t\t\tnewNode.id,\n\t\t\t\t\t\t\tRemoveNodeReason.SmartStartFailed,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(newNode.id, {\n\t\t\t\t\t\t\tmessage: \"was removed\",\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// The node was removed. Do not emit the \"node added\" event\n\t\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// The node could not be removed, continue\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(newNode.id, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\"The node is still part of the network, continuing with insecure communication.\",\n\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\n\t\t\t\t// We're done adding this node, notify listeners\n\t\t\t\tconst result: InclusionResult = bootstrapFailure != undefined\n\t\t\t\t\t? {\n\t\t\t\t\t\tlowSecurity: true,\n\t\t\t\t\t\tlowSecurityReason: bootstrapFailure,\n\t\t\t\t\t}\n\t\t\t\t\t: { lowSecurity: false };\n\n\t\t\t\tthis.emit(\"node added\", newNode, result);\n\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\t}\n\t\t}\n\t\t// not sure what to do with this message\n\t\treturn false;\n\t}\n\n\t/**\n\t * Is called when an ReplaceFailed request is received from the controller.\n\t * Handles and controls the replace process.\n\t */\n\tprivate async handleReplaceNodeStatusReport(\n\t\tmsg: ReplaceFailedNodeRequestStatusReport,\n\t): Promise<boolean> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`handling replace node request (status = ${\n\t\t\t\tReplaceFailedNodeStatus[msg.replaceStatus]\n\t\t\t})`,\n\t\t);\n\n\t\tif (this._inclusionOptions == undefined) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t` currently NOT replacing a node, ignoring it...`,\n\t\t\t);\n\t\t\treturn true; // Don't invoke any more handlers\n\t\t}\n\n\t\tswitch (msg.replaceStatus) {\n\t\t\tcase ReplaceFailedNodeStatus.NodeOK:\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\tthis._replaceFailedPromise?.reject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`The node could not be replaced because it has responded`,\n\t\t\t\t\t\tZWaveErrorCodes.ReplaceFailedNode_NodeOK,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase ReplaceFailedNodeStatus.FailedNodeReplaceFailed:\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\tthis._replaceFailedPromise?.reject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`The failed node has not been replaced`,\n\t\t\t\t\t\tZWaveErrorCodes.ReplaceFailedNode_Failed,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase ReplaceFailedNodeStatus.FailedNodeReplace:\n\t\t\t\t// failed node is now ready to be replaced and controller is ready to add a new\n\t\t\t\t// node with the nodeID of the failed node\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`The failed node is ready to be replaced, inclusion started...`,\n\t\t\t\t);\n\t\t\t\tthis.emit(\"inclusion started\", this._inclusionOptions.strategy);\n\t\t\t\tthis.setInclusionState(InclusionState.Including);\n\t\t\t\tthis._replaceFailedPromise?.resolve(true);\n\n\t\t\t\t// stop here, don't emit inclusion failed\n\t\t\t\treturn true;\n\t\t\tcase ReplaceFailedNodeStatus.FailedNodeReplaceDone:\n\t\t\t\tthis.driver.controllerLog.print(`The failed node was replaced`);\n\t\t\t\tthis.emit(\"inclusion stopped\");\n\n\t\t\t\tif (this._nodePendingReplace) {\n\t\t\t\t\tthis.emit(\n\t\t\t\t\t\t\"node removed\",\n\t\t\t\t\t\tthis._nodePendingReplace,\n\t\t\t\t\t\tRemoveNodeReason.Replaced,\n\t\t\t\t\t);\n\t\t\t\t\tthis._nodes.delete(this._nodePendingReplace.id);\n\n\t\t\t\t\t// We're technically done with the replacing but should not include\n\t\t\t\t\t// anything else until the node has been bootstrapped\n\t\t\t\t\tthis.setInclusionState(InclusionState.Busy);\n\n\t\t\t\t\t// Create a fresh node instance and forget the old one\n\t\t\t\t\tconst newNode = new ZWaveNode(\n\t\t\t\t\t\tthis._nodePendingReplace.id,\n\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t// Create an empty value DB and specify that it contains no values\n\t\t\t\t\t\t// to avoid indexing the existing values\n\t\t\t\t\t\tthis.createValueDBForNode(\n\t\t\t\t\t\t\tthis._nodePendingReplace.id,\n\t\t\t\t\t\t\tnew Set(),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tthis._nodePendingReplace = undefined;\n\t\t\t\t\tthis._nodes.set(newNode.id, newNode);\n\n\t\t\t\t\tthis.emit(\"node found\", {\n\t\t\t\t\t\tid: newNode.id,\n\t\t\t\t\t});\n\n\t\t\t\t\t// We're communicating with the device, so assume it is alive\n\t\t\t\t\t// If it is actually a sleeping device, it will be marked as such later\n\t\t\t\t\tnewNode.markAsAlive();\n\n\t\t\t\t\tif (newNode.protocol == Protocols.ZWave) {\n\t\t\t\t\t\t// Assign SUC return route to make sure the node knows where to get its routes from\n\t\t\t\t\t\tnewNode.hasSUCReturnRoute = await this\n\t\t\t\t\t\t\t.assignSUCReturnRoutes(newNode.id);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Try perform the security bootstrap process. When replacing a node, we don't know any supported CCs\n\t\t\t\t\t// yet, so we need to trust the chosen inclusion strategy.\n\t\t\t\t\tconst strategy = this._inclusionOptions.strategy;\n\t\t\t\t\tlet bootstrapFailure: SecurityBootstrapFailure | undefined;\n\t\t\t\t\tif (strategy === InclusionStrategy.Security_S2) {\n\t\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS2(\n\t\t\t\t\t\t\tnewNode,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t\t\t.getHighestSecurityClass();\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tactualSecurityClass == undefined\n\t\t\t\t\t\t\t\t|| actualSecurityClass\n\t\t\t\t\t\t\t\t\t< SecurityClass.S2_Unauthenticated\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\t\tSecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (strategy === InclusionStrategy.Security_S0) {\n\t\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS0(\n\t\t\t\t\t\t\tnewNode,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t\t\t.getHighestSecurityClass();\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tactualSecurityClass == undefined\n\t\t\t\t\t\t\t\t|| actualSecurityClass < SecurityClass.S0_Legacy\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\t\tSecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Remember that no security classes were granted\n\t\t\t\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\t\t\t\tnewNode.securityClasses.set(secClass, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// We're done adding this node, notify listeners. This also kicks off the node interview\n\t\t\t\t\tconst result: InclusionResult =\n\t\t\t\t\t\tbootstrapFailure != undefined\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tlowSecurity: true,\n\t\t\t\t\t\t\t\tlowSecurityReason: bootstrapFailure,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: { lowSecurity: false };\n\n\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\tthis.emit(\"node added\", newNode, result);\n\t\t\t\t}\n\n\t\t\t\t// stop here, don't emit inclusion failed\n\t\t\t\treturn true;\n\t\t}\n\n\t\tthis.emit(\"inclusion failed\");\n\n\t\treturn false; // Don't invoke any more handlers\n\t}\n\n\t/**\n\t * Is called when a RemoveNode request is received from the controller.\n\t * Handles and controls the exclusion process.\n\t */\n\tprivate async handleRemoveNodeStatusReport(\n\t\tmsg: RemoveNodeFromNetworkRequestStatusReport,\n\t): Promise<boolean> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`handling remove node request (status = ${\n\t\t\t\tRemoveNodeStatus[msg.status]\n\t\t\t})`,\n\t\t);\n\t\tif (this._inclusionState !== InclusionState.Excluding) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t` exclusion is NOT active, ignoring it...`,\n\t\t\t);\n\t\t\treturn true; // Don't invoke any more handlers\n\t\t}\n\n\t\tswitch (msg.status) {\n\t\t\tcase RemoveNodeStatus.Failed:\n\t\t\t\t// This code is handled elsewhere for starting the exclusion, so this means\n\t\t\t\t// that removing a node failed\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Removing the node failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthis.emit(\"exclusion failed\");\n\n\t\t\t\t// in any case, stop the exclusion process so we don't accidentally remove another node\n\t\t\t\ttry {\n\t\t\t\t\tawait this.stopExclusion();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ok */\n\t\t\t\t}\n\t\t\t\treturn true; // Don't invoke any more handlers\n\n\t\t\tcase RemoveNodeStatus.RemovingSlave:\n\t\t\tcase RemoveNodeStatus.RemovingController: {\n\t\t\t\t// this is called when a node is removed\n\t\t\t\tthis._nodePendingExclusion = this.nodes.get(\n\t\t\t\t\tmsg.statusContext!.nodeId,\n\t\t\t\t);\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\t}\n\n\t\t\tcase RemoveNodeStatus.Reserved_0x05:\n\t\t\t// The reserved status can be triggered on some controllers by doing the following:\n\t\t\t// - factory reset the controller without excluding nodes\n\t\t\t// - include a new node with the same node ID as one on the previous network\n\t\t\t// - attempt to exclude the old node while the new node is responsive\n\t\t\tcase RemoveNodeStatus.Done: {\n\t\t\t\t// this is called when the exclusion was completed\n\t\t\t\t// stop the exclusion process so we don't accidentally remove another node\n\t\t\t\ttry {\n\t\t\t\t\tawait this.stopExclusionNoCallback();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ok */\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\tmsg.status === RemoveNodeStatus.Reserved_0x05\n\t\t\t\t\t|| !this._nodePendingExclusion\n\t\t\t\t) {\n\t\t\t\t\t// The exclusion did not succeed\n\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tconst nodeId = this._nodePendingExclusion.id;\n\t\t\t\tthis.driver.controllerLog.print(`Node ${nodeId} was removed`);\n\n\t\t\t\t// Avoid automatic re-inclusion using SmartStart if desired\n\t\t\t\tswitch (this._exclusionOptions?.strategy) {\n\t\t\t\t\tcase ExclusionStrategy.Unprovision:\n\t\t\t\t\t\tthis.unprovisionSmartStartNode(nodeId);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ExclusionStrategy.DisableProvisioningEntry: {\n\t\t\t\t\t\tconst entry = this.getProvisioningEntryInternal(nodeId);\n\t\t\t\t\t\tif (entry) {\n\t\t\t\t\t\t\tentry.status = ProvisioningEntryStatus.Inactive;\n\t\t\t\t\t\t\tthis.provisionSmartStartNode(entry);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis._exclusionOptions = undefined;\n\n\t\t\t\t// notify listeners\n\t\t\t\tthis.emit(\n\t\t\t\t\t\"node removed\",\n\t\t\t\t\tthis._nodePendingExclusion,\n\t\t\t\t\tRemoveNodeReason.Excluded,\n\t\t\t\t);\n\t\t\t\t// and forget the node\n\t\t\t\tthis._nodes.delete(nodeId);\n\t\t\t\tthis._nodePendingExclusion = undefined;\n\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\t}\n\t\t}\n\t\t// not sure what to do with this message\n\t\treturn false;\n\t}\n\n\tprivate _rebuildRoutesProgress = new Map<number, RebuildRoutesStatus>();\n\t/**\n\t * If routes are currently being rebuilt for the entire network, this returns the current progress.\n\t * The information is the same as in the `\"rebuild routes progress\"` event.\n\t */\n\tpublic get rebuildRoutesProgress():\n\t\t| ReadonlyMap<\n\t\t\tnumber,\n\t\t\tRebuildRoutesStatus\n\t\t>\n\t\t| undefined\n\t{\n\t\tif (!this.isRebuildingRoutes) return undefined;\n\t\treturn new Map(this._rebuildRoutesProgress);\n\t}\n\n\t/**\n\t * Starts the process of rebuilding routes for all alive nodes in the network,\n\t * requesting updated neighbor lists and assigning fresh routes to\n\t * association targets.\n\t *\n\t * Returns `true` if the process was started, otherwise `false`. Also returns\n\t * `false` if the process was already active.\n\t */\n\tpublic beginRebuildingRoutes(options: RebuildRoutesOptions = {}): boolean {\n\t\t// Don't start the process twice\n\t\tconst existingTask = this.driver.scheduler.findTask(\n\t\t\t(t) => t.tag?.id === \"rebuild-routes\",\n\t\t);\n\t\tif (existingTask) return false;\n\n\t\toptions.includeSleeping ??= true;\n\t\toptions.deletePriorityReturnRoutes ??= false;\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`rebuilding routes${\n\t\t\t\toptions.includeSleeping ? \"\" : \" for mains-powered nodes\"\n\t\t\t}...`,\n\t\t);\n\n\t\t// Reset the progress for all nodes\n\t\tthis._rebuildRoutesProgress.clear();\n\t\tfor (const [id, node] of this._nodes) {\n\t\t\tif (id === this._ownNodeId) continue;\n\t\t\tif (\n\t\t\t\t// The node is known to be dead\n\t\t\t\tnode.status === NodeStatus.Dead\n\t\t\t\t// The node is assumed asleep but has never been interviewed.\n\t\t\t\t// It is most likely dead\n\t\t\t\t|| (node.status === NodeStatus.Asleep\n\t\t\t\t\t&& node.interviewStage === InterviewStage.ProtocolInfo)\n\t\t\t) {\n\t\t\t\t// Skip dead nodes\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tid,\n\t\t\t\t\t`Skipping route rebuild because the node is not responding.`,\n\t\t\t\t);\n\t\t\t\tthis._rebuildRoutesProgress.set(id, \"skipped\");\n\t\t\t} else if (!options.includeSleeping && node.canSleep) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tid,\n\t\t\t\t\t`Skipping route rebuild because the node is sleeping.`,\n\t\t\t\t);\n\t\t\t\tthis._rebuildRoutesProgress.set(id, \"skipped\");\n\t\t\t} else if (\n\t\t\t\t!options.deletePriorityReturnRoutes\n\t\t\t\t&& (this.getPrioritySUCReturnRouteCached(id)\n\t\t\t\t\t|| Object.keys(this.getPriorityReturnRoutesCached(id))\n\t\t\t\t\t\t\t.length > 0)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tid,\n\t\t\t\t\t`Skipping route rebuild because the node has priority return routes.`,\n\t\t\t\t);\n\t\t\t\tthis._rebuildRoutesProgress.set(id, \"skipped\");\n\t\t\t} else {\n\t\t\t\tthis._rebuildRoutesProgress.set(id, \"pending\");\n\t\t\t}\n\t\t}\n\n\t\t// Rebuild routes in the background\n\t\tvoid this.rebuildRoutesInternal(options).catch(noop);\n\n\t\t// And update the progress once at the start\n\t\tthis.emit(\n\t\t\t\"rebuild routes progress\",\n\t\t\tnew Map(this._rebuildRoutesProgress),\n\t\t);\n\n\t\treturn true;\n\t}\n\n\tprivate rebuildRoutesInternal(\n\t\toptions: RebuildRoutesOptions,\n\t): Promise<void> {\n\t\treturn this.driver.scheduler.queueTask(\n\t\t\tthis.getRebuildRoutesTask(options),\n\t\t);\n\t}\n\n\tprivate getRebuildRoutesTask(\n\t\toptions: RebuildRoutesOptions,\n\t): TaskBuilder<void> {\n\t\tconst pendingNodes = new Set(\n\t\t\t[...this._rebuildRoutesProgress]\n\t\t\t\t.filter(([, status]) => status === \"pending\")\n\t\t\t\t.map(([nodeId]) => nodeId),\n\t\t);\n\n\t\tconst todoListening: number[] = [];\n\t\tconst todoSleeping: number[] = [];\n\n\t\tconst addTodo = (nodeId: number) => {\n\t\t\t// Z-Wave Long Range does not route\n\t\t\tif (isLongRangeNodeId(nodeId)) return;\n\n\t\t\tif (pendingNodes.has(nodeId)) {\n\t\t\t\tpendingNodes.delete(nodeId);\n\t\t\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\t\t\tif (node.canSleep) {\n\t\t\t\t\tif (options.includeSleeping) {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t\t\"added to route rebuilding queue for sleeping nodes\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\ttodoSleeping.push(nodeId);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t\"added to route rebuilding queue for listening nodes\",\n\t\t\t\t\t);\n\t\t\t\t\ttodoListening.push(nodeId);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tconst self = this;\n\n\t\treturn {\n\t\t\tpriority: TaskPriority.Lower,\n\t\t\ttag: { id: \"rebuild-routes\" },\n\t\t\ttask: async function* rebuildRoutesTask() {\n\t\t\t\t// We work our way outwards from the controller and start with non-sleeping nodes, one by one\n\t\t\t\ttry {\n\t\t\t\t\tconst neighbors = await self.getNodeNeighbors(\n\t\t\t\t\t\tself._ownNodeId!,\n\t\t\t\t\t);\n\t\t\t\t\tneighbors.forEach((id) => addTodo(id));\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\n\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\tasync function* doRebuildRoutes(nodeId: number) {\n\t\t\t\t\t// Await the process for each node and convert errors to a non-successful result\n\t\t\t\t\tlet result: boolean;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst node = self.nodes.getOrThrow(nodeId);\n\t\t\t\t\t\tresult = yield () =>\n\t\t\t\t\t\t\tself.getRebuildNodeRoutesTask(node);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tresult = false;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Track the success in a map\n\t\t\t\t\tself._rebuildRoutesProgress.set(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tresult ? \"done\" : \"failed\",\n\t\t\t\t\t);\n\t\t\t\t\t// Notify listeners about the progress\n\t\t\t\t\tself.emit(\n\t\t\t\t\t\t\"rebuild routes progress\",\n\t\t\t\t\t\tnew Map(self._rebuildRoutesProgress),\n\t\t\t\t\t);\n\n\t\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\t\t// Figure out which nodes to do next\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst neighbors = await self.getNodeNeighbors(nodeId);\n\t\t\t\t\t\tneighbors.forEach((id) => addTodo(id));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t}\n\n\t\t\t\t\tyield; // Give the task scheduler time to do something else\n\t\t\t\t}\n\n\t\t\t\t// First try to rebuild routes for as many nodes as possible one by one\n\t\t\t\twhile (todoListening.length > 0) {\n\t\t\t\t\tconst nodeId = todoListening.shift()!;\n\t\t\t\t\tyield* doRebuildRoutes(nodeId);\n\t\t\t\t}\n\n\t\t\t\t// We might end up with a few unconnected listening nodes, try to rebuild routes for them too\n\t\t\t\tpendingNodes.forEach((nodeId) => addTodo(nodeId));\n\t\t\t\twhile (todoListening.length > 0) {\n\t\t\t\t\tconst nodeId = todoListening.shift()!;\n\t\t\t\t\tyield* doRebuildRoutes(nodeId);\n\t\t\t\t}\n\n\t\t\t\tif (options.includeSleeping) {\n\t\t\t\t\t// Now do all sleeping nodes at once\n\t\t\t\t\tself.driver.controllerLog.print(\n\t\t\t\t\t\t\"Rebuilding routes for sleeping nodes when they wake up\",\n\t\t\t\t\t);\n\n\t\t\t\t\tconst sleepingNodes = todoSleeping.map((nodeId) =>\n\t\t\t\t\t\tself.nodes.get(nodeId)\n\t\t\t\t\t).filter((node) => node != undefined);\n\n\t\t\t\t\tconst wakeupPromises = new Map(\n\t\t\t\t\t\tsleepingNodes.map((node) =>\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t\t\tnode.waitForWakeup().then(() => node),\n\t\t\t\t\t\t\t] as const\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\t// As long as there are sleeping nodes that haven't had their routes rebuilt yet,\n\t\t\t\t\t// wait for any of them to wake up\n\t\t\t\t\twhile (wakeupPromises.size > 0) {\n\t\t\t\t\t\tconst wakeUpPromise = Promise.race(\n\t\t\t\t\t\t\twakeupPromises.values(),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst wokenUpNode = (\n\t\t\t\t\t\t\tyield () => wakeUpPromise\n\t\t\t\t\t\t) as Awaited<typeof wakeUpPromise>;\n\t\t\t\t\t\tif (wokenUpNode.status === NodeStatus.Asleep) {\n\t\t\t\t\t\t\t// The node has gone to sleep again since the promise was resolved. Wait again\n\t\t\t\t\t\t\twakeupPromises.set(\n\t\t\t\t\t\t\t\twokenUpNode.id,\n\t\t\t\t\t\t\t\twokenUpNode.waitForWakeup().then(() =>\n\t\t\t\t\t\t\t\t\twokenUpNode\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Once the node has woken up, remove it from the list and rebuild its routes\n\t\t\t\t\t\twakeupPromises.delete(wokenUpNode.id);\n\t\t\t\t\t\tyield* doRebuildRoutes(wokenUpNode.id);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tself.driver.controllerLog.print(\n\t\t\t\t\t\"rebuilding routes completed\",\n\t\t\t\t);\n\n\t\t\t\tself.emit(\n\t\t\t\t\t\"rebuild routes done\",\n\t\t\t\t\tnew Map(self._rebuildRoutesProgress),\n\t\t\t\t);\n\n\t\t\t\t// We're done!\n\t\t\t\tself._rebuildRoutesProgress.clear();\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Stops the route rebuilding process. Resolves false if the process was not active, true otherwise.\n\t */\n\tpublic stopRebuildingRoutes(): boolean {\n\t\tconst hasTasks = !!this.driver.scheduler.findTask(isRebuildRoutesTask);\n\n\t\t// don't stop it twice\n\t\tif (!hasTasks) return false;\n\n\t\tthis.driver.controllerLog.print(`stopping route rebuilding process...`);\n\n\t\t// Stop all tasks that are part of the route rebuilding process\n\t\t// FIXME: This should be an async function that waits for the task removal\n\t\tvoid this.driver.scheduler.removeTasks(isRebuildRoutesTask).then(() => {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\"rebuilding routes aborted\",\n\t\t\t);\n\t\t});\n\n\t\t// Cancel all transactions that were created by the route rebuilding process\n\t\tthis.driver.rejectTransactions(\n\t\t\t(t) =>\n\t\t\t\tt.message instanceof RequestNodeNeighborUpdateRequest\n\t\t\t\t|| t.message instanceof DeleteReturnRouteRequest\n\t\t\t\t|| t.message instanceof AssignReturnRouteRequest,\n\t\t);\n\n\t\tthis._rebuildRoutesProgress.clear();\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Rebuilds routes for a single alive node in the network,\n\t * updating the neighbor list and assigning fresh routes to\n\t * association targets.\n\t *\n\t * Returns `true` if the process succeeded, `false` otherwise.\n\t */\n\tpublic async rebuildNodeRoutes(nodeId: number): Promise<boolean> {\n\t\t// We cannot rebuild routes for the controller\n\t\tif (nodeId === this._ownNodeId) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Rebuilding routes for the controller itself is not possible!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\t// Z-Wave Long Range does not route\n\t\tif (node.protocol == Protocols.ZWaveLongRange) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot rebuild routes for nodes using Z-Wave Long Range!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\t// Figure out if nodes are responsive before attempting to rebuild routes\n\t\tif (\n\t\t\t// The node is known to be dead\n\t\t\tnode.status === NodeStatus.Dead\n\t\t\t// The node is assumed asleep but has never been interviewed.\n\t\t\t// It is most likely dead\n\t\t\t|| (node.status === NodeStatus.Asleep\n\t\t\t\t&& node.interviewStage === InterviewStage.ProtocolInfo)\n\t\t) {\n\t\t\tif (!(await node.ping())) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tnodeId,\n\t\t\t\t\t`Cannot rebuild routes because the node is not responding.`,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn this.rebuildNodeRoutesInternal(nodeId);\n\t}\n\n\tprivate rebuildNodeRoutesInternal(\n\t\tnodeId: number,\n\t): Promise<boolean> {\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\tconst task = this.getRebuildNodeRoutesTask(node);\n\t\tif (task instanceof Promise) return task;\n\n\t\treturn this.driver.scheduler.queueTask(task);\n\t}\n\n\tprivate getRebuildNodeRoutesTask(\n\t\tnode: ZWaveNode,\n\t): Promise<boolean> | TaskBuilder<boolean> {\n\t\t// This task should only run once at a time\n\t\tconst existingTask = this.driver.scheduler.findTask<boolean>((t) =>\n\t\t\tt.tag?.id === \"rebuild-node-routes\" && t.tag.nodeId === node.id\n\t\t);\n\t\tif (existingTask) return existingTask;\n\n\t\tconst self = this;\n\t\tlet keepAwake: boolean;\n\n\t\treturn {\n\t\t\t// This task is executed by users and by the network-wide route rebuilding process.\n\t\t\tpriority: TaskPriority.Lower,\n\t\t\ttag: { id: \"rebuild-node-routes\", nodeId: node.id },\n\t\t\ttask: async function* rebuildNodeRoutesTask() {\n\t\t\t\t// Keep battery powered nodes awake during the process\n\t\t\t\tkeepAwake = node.keepAwake;\n\t\t\t\tnode.keepAwake = true;\n\n\t\t\t\tif (\n\t\t\t\t\tnode.canSleep && node.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t\t\t) {\n\t\t\t\t\tyield () => node.waitForWakeup();\n\t\t\t\t}\n\n\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage: `Rebuilding routes...`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t});\n\n\t\t\t\t// The process consists of four steps, each step is tried up to 5 times before it is considered failed\n\t\t\t\tconst maxAttempts = 5;\n\n\t\t\t\t// 1. command the node to refresh its neighbor list\n\t\t\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`refreshing neighbor list (attempt ${attempt})...`,\n\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t});\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await self.discoverNodeNeighbors(\n\t\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (result) {\n\t\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\t\tmessage: \"neighbor list refreshed...\",\n\t\t\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t// this step was successful, continue with the next\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\t\tmessage: \"refreshing neighbor list failed...\",\n\t\t\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tself.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t\t`refreshing neighbor list failed: ${\n\t\t\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (attempt === maxAttempts) {\n\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`rebuilding routes failed: could not update the neighbor list after ${maxAttempts} attempts`,\n\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\t// 2. re-create the SUC return route, just in case\n\t\t\t\tnode.hasSUCReturnRoute = await self.assignSUCReturnRoutes(\n\t\t\t\t\tnode.id,\n\t\t\t\t);\n\n\t\t\t\t// 3. delete all return routes to get rid of potential priority return routes\n\t\t\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`deleting return routes (attempt ${attempt})...`,\n\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t});\n\n\t\t\t\t\tif (await self.deleteReturnRoutes(node.id)) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (attempt === maxAttempts) {\n\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`rebuilding routes failed: failed to delete return routes after ${maxAttempts} attempts`,\n\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// 4. Assign return routes to all association destinations...\n\t\t\t\tlet associatedNodes: number[] = [];\n\t\t\t\ttry {\n\t\t\t\t\tassociatedNodes = distinct(\n\t\t\t\t\t\tflatMap<number, AssociationAddress[]>(\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t...(self.getAssociations({ nodeId: node.id })\n\t\t\t\t\t\t\t\t\t.values() as any),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t(assocs: AssociationAddress[]) =>\n\t\t\t\t\t\t\t\tassocs.map((a) => a.nodeId),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\t\t// ...except the controller itself, which was handled by step 2\n\t\t\t\t\t\t.filter((id) => id !== self._ownNodeId!)\n\t\t\t\t\t\t// ...and the node itself\n\t\t\t\t\t\t.filter((id) => id !== node.id)\n\t\t\t\t\t\t.sort();\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\n\t\t\t\tif (associatedNodes.length > 0) {\n\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`assigning return routes to the following nodes:\n\t${associatedNodes.join(\", \")}`,\n\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t});\n\t\t\t\t\tfor (const destinationNodeId of associatedNodes) {\n\t\t\t\t\t\tfor (\n\t\t\t\t\t\t\tlet attempt = 1;\n\t\t\t\t\t\t\tattempt <= maxAttempts;\n\t\t\t\t\t\t\tattempt++\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\t`assigning return route to node ${destinationNodeId} (attempt ${attempt})...`,\n\t\t\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tawait self.assignReturnRoutes(\n\t\t\t\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// this step was successful, continue with the next\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (attempt === maxAttempts) {\n\t\t\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\t\t`rebuilding routes failed: failed to assign return route after ${maxAttempts} attempts`,\n\t\t\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage: `rebuilt routes successfully`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t});\n\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tcleanup: () => {\n\t\t\t\t// Make sure that the keepAwake flag gets reset at the end\n\t\t\t\tnode.keepAwake = keepAwake;\n\t\t\t\tif (!keepAwake) {\n\t\t\t\t\tsetImmediate(() => {\n\t\t\t\t\t\tthis.driver.debounceSendNodeToSleep(node);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t};\n\t}\n\n\t/** Configures the given Node to be SUC/SIS or not */\n\tpublic async configureSUC(\n\t\tnodeId: number,\n\t\tenableSUC: boolean,\n\t\tenableSIS: boolean,\n\t): Promise<boolean> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\tMessage & SuccessIndicator\n\t\t>(\n\t\t\tnew SetSUCNodeIdRequest({\n\t\t\t\townNodeId: this.ownNodeId!,\n\t\t\t\tsucNodeId: nodeId,\n\t\t\t\tenableSUC,\n\t\t\t\tenableSIS,\n\t\t\t}),\n\t\t);\n\n\t\treturn result.isOK();\n\t}\n\n\t// After a lot of experimenting, it seems to make sense to document how assigning return routes works in the controller.\n\t// Each node has a list of 4 return routes per destination (and probably a separate list for the SUC):\n\t// - #0, repeaters..., speed, wakeup\n\t// - #1, repeaters..., speed, wakeup\n\t// - #2, repeaters..., speed, wakeup\n\t// - #3, repeaters..., speed, wakeup\n\t//\n\t// Empty slots are filled with 0 repeaters, 9.6kbit/s, no wakeup\n\t//\n\t// Calling assignReturnRoute will assign all 4 slots, some of which may be empty.\n\t// Calling deleteReturnRoute will assign an empty route to all 4 slots.\n\t//\n\t// Priority return routes are indicated by a separate \"pointer\" byte which tells the node which route is the priority.\n\t// Calling assignPriorityReturnRoute will first assign 4 routes, one of which is then marked as priority.\n\t// This is not fully understood yet, but it seems that the priority route is actually the last non-empty route.\n\t// If the priority byte points to an empty route, it is ignored.\n\t//\n\t// Calling assignReturnRoute after having assigned a priority return route will not clear that pointer byte. This\n\t// means that a previously-assigned priority route can randomly change if assignReturnRoute assigns enough routes.\n\t// deleteReturnRoute does also clear the priority byte.\n\n\t/**\n\t * Instructs the controller to assign static routes from the given end node to the SUC.\n\t * This will assign up to 4 routes, depending on the network topology (that the controller knows about).\n\t */\n\tpublic async assignSUCReturnRoutes(nodeId: number): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Assigning SUC return route...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\t// Since there is only one SUC, we can do the right thing here and delete all routes first, which clears any dangling priority return routes.\n\t\t// Afterwards, we'll set up all routes again anyways.\n\t\tawait this.deleteSUCReturnRoutes(nodeId);\n\n\t\ttry {\n\t\t\t// Some controllers have a bug where they incorrectly respond\n\t\t\t// to an AssignSUCReturnRouteRequest with DeleteSUCReturnRoute\n\t\t\tconst disableCallbackFunctionTypeCheck = !!this.driver\n\t\t\t\t.getDeviceConfig?.(this.ownNodeId!)\n\t\t\t\t?.compat\n\t\t\t\t?.disableCallbackFunctionTypeCheck\n\t\t\t\t?.includes(FunctionType.AssignSUCReturnRoute);\n\t\t\tconst result = await this.driver.sendMessage(\n\t\t\t\tnew AssignSUCReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tdisableCallbackFunctionTypeCheck,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (\n\t\t\t\t!(result instanceof AssignSUCReturnRouteRequestTransmitReport)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tnodeId,\n\t\t\t\t\t`Assigning SUC return route failed: Invalid callback received`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// Custom assigned are no longer valid\n\t\t\t\tthis.setCustomSUCReturnRoutesCached(nodeId, undefined);\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Assigning SUC return route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Returns which custom static routes are currently assigned from the given end node to the SUC.\n\t *\n\t * **Note:** This only considers routes that were assigned using {@link assignCustomSUCReturnRoutes}.\n\t * If another controller has assigned routes in the meantime, this information may be out of date.\n\t */\n\tpublic getCustomSUCReturnRoutesCached(nodeId: number): Route[] {\n\t\treturn (\n\t\t\tthis.driver.cacheGet<Route[]>(\n\t\t\t\tcacheKeys.node(nodeId).customSUCReturnRoutes,\n\t\t\t) ?? []\n\t\t);\n\t}\n\n\tprivate setCustomSUCReturnRoutesCached(\n\t\tnodeId: number,\n\t\troutes: Route[] | undefined,\n\t): void {\n\t\tthis.driver.cacheSet(\n\t\t\tcacheKeys.node(nodeId).customSUCReturnRoutes,\n\t\t\troutes,\n\t\t);\n\t}\n\n\t/**\n\t * Assigns static routes from the given end node to the SUC. Unlike {@link assignSUCReturnRoutes}, this method assigns\n\t * the given routes instead of having the controller calculate them. At most 4 routes can be assigned. If less are\n\t * specified, the remaining routes are cleared.\n\t *\n\t * To mark a route as a priority route, pass it as the optional `priorityRoute` parameter. At most 3 routes of the\n\t * `routes` array will then be used as fallback routes.\n\t *\n\t * **Note:** Calling {@link assignSUCReturnRoutes} or {@link deleteSUCReturnRoutes} will override the custom routes.\n\t *\n\t * Returns `true` when the process was successful, or `false` if at least one step failed.\n\t */\n\tpublic async assignCustomSUCReturnRoutes(\n\t\tnodeId: number,\n\t\troutes: Route[],\n\t\tpriorityRoute?: Route,\n\t): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Assigning custom SUC return routes...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\t// Since there is only one SUC, we can do the right thing here and delete all routes first, which clears the priority return routes.\n\t\tawait this.deleteSUCReturnRoutes(nodeId);\n\n\t\tlet result = true;\n\t\tconst MAX_ROUTES = 4;\n\n\t\t// Keep track of which routes have been assigned\n\t\tconst assignedRoutes = new Array(MAX_ROUTES).fill(EMPTY_ROUTE);\n\n\t\tlet priorityRouteIndex = -1;\n\t\t// If a priority route is given, add it to the end of the routes array to mimick what the Z-Wave controller does\n\t\tif (priorityRoute) {\n\t\t\tpriorityRouteIndex = Math.min(MAX_ROUTES - 1, routes.length);\n\t\t\troutes[priorityRouteIndex] = priorityRoute;\n\t\t}\n\n\t\tfor (let i = 0; i < MAX_ROUTES; i++) {\n\t\t\tconst route = routes[i] ?? EMPTY_ROUTE;\n\t\t\tconst isEmpty = isEmptyRoute(route);\n\n\t\t\t// We are always listening\n\t\t\tconst targetWakeup = false;\n\n\t\t\tconst cc = new ZWaveProtocolCCAssignSUCReturnRoute({\n\t\t\t\tnodeId,\n\t\t\t\t// Empty routes are marked with a nodeId of 0\n\t\t\t\tdestinationNodeId: isEmpty ? 0 : this.ownNodeId ?? 1,\n\t\t\t\trouteIndex: i,\n\t\t\t\trepeaters: route.repeaters,\n\t\t\t\tdestinationSpeed: route.routeSpeed,\n\t\t\t\tdestinationWakeUp: FLiRS2WakeUpTime(targetWakeup ?? false),\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendZWaveProtocolCC(cc);\n\n\t\t\t\t// Remember that this route has been assigned\n\t\t\t\tif (i !== priorityRouteIndex) assignedRoutes[i] = route;\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage: `Assigning custom SUC return route #${i} failed`,\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\n\t\t\t\tresult = false;\n\t\t\t}\n\t\t}\n\n\t\t// If a priority route was passed, tell the node to use it\n\t\tif (priorityRouteIndex >= 0) {\n\t\t\tconst cc = new ZWaveProtocolCCAssignSUCReturnRoutePriority({\n\t\t\t\tnodeId,\n\t\t\t\ttargetNodeId: this.ownNodeId ?? 1,\n\t\t\t\trouteNumber: priorityRouteIndex,\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendZWaveProtocolCC(cc);\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Marking custom SUC return route as priority failed`,\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\n\t\t\t\tresult = false;\n\t\t\t}\n\t\t}\n\n\t\t// Trim empty routes off the end. We may end up with empty routes in the middle\n\t\t// if an assignment fails.\n\t\twhile (\n\t\t\tassignedRoutes.length > 0\n\t\t\t&& isEmptyRoute(assignedRoutes.at(-1))\n\t\t) {\n\t\t\tassignedRoutes.pop();\n\t\t}\n\n\t\t// Remember the routes we assigned\n\t\tthis.setPrioritySUCReturnRouteCached(nodeId, priorityRoute);\n\t\tthis.setCustomSUCReturnRoutesCached(nodeId, assignedRoutes);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Instructs the controller to assign static routes from the given end node to the SUC.\n\t * This will assign up to 4 routes, depending on the network topology (that the controller knows about).\n\t */\n\tpublic async deleteSUCReturnRoutes(nodeId: number): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Deleting SUC return route...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\t// Some controllers have a bug where they incorrectly respond\n\t\t\t// to an DeleteSUCReturnRouteRequest with a different function type\n\t\t\tconst disableCallbackFunctionTypeCheck = !!this.driver\n\t\t\t\t.getDeviceConfig?.(this.ownNodeId!)\n\t\t\t\t?.compat\n\t\t\t\t?.disableCallbackFunctionTypeCheck\n\t\t\t\t?.includes(FunctionType.DeleteSUCReturnRoute);\n\t\t\tconst result = await this.driver.sendMessage(\n\t\t\t\tnew DeleteSUCReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tdisableCallbackFunctionTypeCheck,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (\n\t\t\t\t!(result instanceof DeleteSUCReturnRouteRequestTransmitReport)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tnodeId,\n\t\t\t\t\t`Deleting SUC return route failed: Invalid callback received`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// Custom assigned and priority return routes are no longer valid\n\t\t\t\tthis.setPrioritySUCReturnRouteCached(nodeId, undefined);\n\t\t\t\tthis.setCustomSUCReturnRoutesCached(nodeId, undefined);\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Deleting SUC return route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Returns which custom static routes are currently assigned between the given end nodes.\n\t *\n\t * **Note:** This only considers routes that were assigned using {@link assignCustomReturnRoutes}.\n\t * If another controller has assigned routes in the meantime, this information may be out of date.\n\t */\n\tpublic getCustomReturnRoutesCached(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t): Route[] {\n\t\treturn (\n\t\t\tthis.driver.cacheGet<Route[]>(\n\t\t\t\tcacheKeys.node(nodeId).customReturnRoutes(destinationNodeId),\n\t\t\t) ?? []\n\t\t);\n\t}\n\n\tprivate setCustomReturnRoutesCached(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t\troutes: Route[] | undefined,\n\t): void {\n\t\tthis.driver.cacheSet(\n\t\t\tcacheKeys.node(nodeId).customReturnRoutes(destinationNodeId),\n\t\t\troutes,\n\t\t);\n\t}\n\n\tprivate clearCustomReturnRoutesCached(nodeId: number): void {\n\t\t// This is a bit ugly, but the best we can do right now.\n\t\tfor (let dest = 1; dest <= MAX_NODES; dest++) {\n\t\t\tthis.setCustomReturnRoutesCached(nodeId, dest, undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Instructs the controller to assign static routes between the two given end nodes.\n\t * This will assign up to 4 routes, depending on the network topology (that the controller knows about).\n\t */\n\tpublic async assignReturnRoutes(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t} else if (isLongRangeNodeId(destinationNodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tdestinationNodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\t// Make sure this is not misused by passing the controller's node ID\n\t\tif (destinationNodeId === this.ownNodeId) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`To assign a return route to the SUC (node ID ${destinationNodeId}), assignSUCReturnRoutes() must be used!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Assigning return routes to node ${destinationNodeId}...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tAssignReturnRouteRequestTransmitReport\n\t\t\t>(\n\t\t\t\tnew AssignReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// Custom assigned are no longer valid\n\t\t\t\tthis.setCustomReturnRoutesCached(\n\t\t\t\t\tnodeId,\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\tundefined,\n\t\t\t\t);\n\t\t\t\t// The priority route probably is invalid too now, but it may also point to a random route\n\t\t\t\tif (\n\t\t\t\t\tthis.hasPriorityReturnRouteCached(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\t) !== false\n\t\t\t\t) {\n\t\t\t\t\tthis.setPriorityReturnRouteCached(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\t\tUNKNOWN_STATE,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Assigning return routes failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Assigns static routes between the two given end nodes. Unlike {@link assignReturnRoutes}, this method assigns\n\t * the given routes instead of having the controller calculate them. At most 4 routes can be assigned. If less are\n\t * specified, the remaining routes are cleared.\n\t *\n\t * **Note:** Calling {@link assignReturnRoutes} or {@link deleteReturnRoutes} will override the custom routes.\n\t */\n\tpublic async assignCustomReturnRoutes(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t\troutes: Route[],\n\t\tpriorityRoute?: Route,\n\t): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t} else if (isLongRangeNodeId(destinationNodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tdestinationNodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\t// Make sure this is not misused by passing the controller's node ID\n\t\tif (destinationNodeId === this.ownNodeId) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`To assign custom return routes to the SUC (node ID ${destinationNodeId}), assignCustomSUCReturnRoutes() must be used!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage:\n\t\t\t\t`Assigning custom return routes to node ${destinationNodeId}...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\tlet result = true;\n\t\tconst MAX_ROUTES = 4;\n\n\t\t// Keep track of which routes have been assigned\n\t\tconst assignedRoutes = new Array(MAX_ROUTES).fill(EMPTY_ROUTE);\n\n\t\tlet priorityRouteIndex = -1;\n\t\t// If a priority route is given, add it to the end of the routes array to mimick what the Z-Wave controller does\n\t\tif (priorityRoute) {\n\t\t\tpriorityRouteIndex = Math.min(MAX_ROUTES - 1, routes.length);\n\t\t\troutes[priorityRouteIndex] = priorityRoute;\n\t\t}\n\n\t\tfor (let i = 0; i < MAX_ROUTES; i++) {\n\t\t\tconst route = routes[i] ?? EMPTY_ROUTE;\n\t\t\tconst isEmpty = isEmptyRoute(route);\n\n\t\t\tconst targetWakeup = !isEmpty\n\t\t\t\t? this.nodes.get(destinationNodeId)?.isFrequentListening\n\t\t\t\t: undefined;\n\n\t\t\tconst cc = new ZWaveProtocolCCAssignReturnRoute({\n\t\t\t\tnodeId,\n\t\t\t\t// Empty routes are marked with a nodeId of 0\n\t\t\t\tdestinationNodeId: isEmpty ? 0 : destinationNodeId,\n\t\t\t\trouteIndex: i,\n\t\t\t\trepeaters: route.repeaters,\n\t\t\t\tdestinationSpeed: route.routeSpeed,\n\t\t\t\tdestinationWakeUp: FLiRS2WakeUpTime(targetWakeup ?? false),\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendZWaveProtocolCC(cc);\n\n\t\t\t\t// Remember that this route has been assigned\n\t\t\t\tif (i !== priorityRouteIndex) assignedRoutes[i] = route;\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage: `Assigning custom return route #${i} failed`,\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\n\t\t\t\tresult = false;\n\t\t\t}\n\t\t}\n\n\t\t// If a priority route was passed, tell the node to use it\n\t\tif (priorityRouteIndex >= 0) {\n\t\t\tconst cc = new ZWaveProtocolCCAssignReturnRoutePriority({\n\t\t\t\tnodeId,\n\t\t\t\ttargetNodeId: destinationNodeId,\n\t\t\t\trouteNumber: priorityRouteIndex,\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendZWaveProtocolCC(cc);\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage: `Marking custom return route as priority failed`,\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\n\t\t\t\tresult = false;\n\t\t\t}\n\t\t}\n\n\t\t// Trim empty routes off the end. We may end up with empty routes in the middle\n\t\t// if an assignment fails.\n\t\twhile (\n\t\t\tassignedRoutes.length > 0\n\t\t\t&& isEmptyRoute(assignedRoutes.at(-1))\n\t\t) {\n\t\t\tassignedRoutes.pop();\n\t\t}\n\n\t\tthis.setCustomReturnRoutesCached(\n\t\t\tnodeId,\n\t\t\tdestinationNodeId,\n\t\t\tassignedRoutes,\n\t\t);\n\t\tif (priorityRoute) {\n\t\t\tthis.setPriorityReturnRouteCached(\n\t\t\t\tnodeId,\n\t\t\t\tdestinationNodeId,\n\t\t\t\tpriorityRoute,\n\t\t\t);\n\t\t} else if (\n\t\t\tthis.hasPriorityReturnRouteCached(nodeId, destinationNodeId)\n\t\t\t\t!== false\n\t\t) {\n\t\t\t// The priority route is probably invalid now, but it may also point to a random route\n\t\t\tthis.setPriorityReturnRouteCached(\n\t\t\t\tnodeId,\n\t\t\t\tdestinationNodeId,\n\t\t\t\tUNKNOWN_STATE,\n\t\t\t);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Instructs the controller to delete all static routes between the given node and all\n\t * other end nodes, including the priority return routes.\n\t */\n\tpublic async deleteReturnRoutes(nodeId: number): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Deleting all return routes...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tDeleteReturnRouteRequestTransmitReport\n\t\t\t>(\n\t\t\t\tnew DeleteReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// All custom assigned routes are no longer valid\n\t\t\t\tthis.clearPriorityReturnRoutesCached(nodeId);\n\t\t\t\tthis.clearCustomReturnRoutesCached(nodeId);\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Deleting return routes failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Assigns a priority route between two end nodes. This route will always be used for the first transmission attempt.\n\t * @param nodeId The ID of the source node of the route\n\t * @param destinationNodeId The ID of the destination node of the route\n\t * @param repeaters The IDs of the nodes that should be used as repeaters, or an empty array for direct connection\n\t * @param routeSpeed The transmission speed to use for the route\n\t */\n\tpublic async assignPriorityReturnRoute(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t\trepeaters: number[],\n\t\trouteSpeed: ZWaveDataRate,\n\t): Promise<boolean> {\n\t\t// Make sure this is not misused by passing the controller's node ID\n\t\tif (destinationNodeId === this.ownNodeId) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`To assign a priority return route to the SUC (node ID ${destinationNodeId}), assignPrioritySUCReturnRoute() must be used!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage:\n\t\t\t\t`Assigning priority return route to node ${destinationNodeId}...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tAssignReturnRouteRequestTransmitReport\n\t\t\t>(\n\t\t\t\tnew AssignPriorityReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\trepeaters,\n\t\t\t\t\trouteSpeed,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// Update the cached priority route\n\t\t\t\tthis.setPriorityReturnRouteCached(nodeId, destinationNodeId, {\n\t\t\t\t\trepeaters,\n\t\t\t\t\trouteSpeed,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Assigning priority return route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate hasPriorityReturnRouteCached(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t): MaybeUnknown<boolean> {\n\t\tconst ret = this.driver.cacheGet<MaybeUnknown<Route>>(\n\t\t\tcacheKeys.node(nodeId).priorityReturnRoute(destinationNodeId),\n\t\t);\n\t\tif (ret === UNKNOWN_STATE) return UNKNOWN_STATE;\n\t\treturn ret !== undefined;\n\t}\n\n\tprivate setPriorityReturnRouteCached(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t\troute: MaybeUnknown<Route> | undefined,\n\t): void {\n\t\tthis.driver.cacheSet(\n\t\t\tcacheKeys.node(nodeId).priorityReturnRoute(destinationNodeId),\n\t\t\troute,\n\t\t);\n\t}\n\n\tprivate clearPriorityReturnRoutesCached(nodeId: number): void {\n\t\t// This is a bit ugly, but the best we can do right now.\n\t\tfor (let dest = 1; dest <= MAX_NODES; dest++) {\n\t\t\tthis.setPriorityReturnRouteCached(nodeId, dest, undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Returns which priority route is currently assigned between the given end nodes.\n\t *\n\t * **Note:** This is using cached information, since there's no way to query priority routes from a node.\n\t * If another controller has assigned routes in the meantime, this information may be out of date.\n\t */\n\tpublic getPriorityReturnRouteCached(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t): MaybeUnknown<Route> | undefined {\n\t\treturn this.driver.cacheGet(\n\t\t\tcacheKeys.node(nodeId).priorityReturnRoute(destinationNodeId),\n\t\t);\n\t}\n\n\t/**\n\t * For the given node, returns all end node destinations and the priority routes to them.\n\t *\n\t * **Note:** This is using cached information, since there's no way to query priority routes from a node.\n\t * If another controller has assigned routes in the meantime, this information may be out of date.\n\t */\n\tpublic getPriorityReturnRoutesCached(\n\t\tnodeId: number,\n\t): Record<number, Route> {\n\t\tconst ret: Record<number, Route> = {};\n\n\t\tconst routes = this.driver.cacheList<Route>(\n\t\t\tcacheKeys.node(nodeId)._priorityReturnRouteBaseKey,\n\t\t);\n\t\tfor (const [key, route] of Object.entries(routes)) {\n\t\t\tconst destination = cacheKeyUtils\n\t\t\t\t.destinationFromPriorityReturnRouteKey(key);\n\t\t\tif (destination !== undefined) ret[destination] = route;\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Assigns a priority route from an end node to the SUC. This route will always be used for the first transmission attempt.\n\t * @param nodeId The ID of the end node for which to assign the route\n\t * @param repeaters The IDs of the nodes that should be used as repeaters, or an empty array for direct connection\n\t * @param routeSpeed The transmission speed to use for the route\n\t */\n\tpublic async assignPrioritySUCReturnRoute(\n\t\tnodeId: number,\n\t\trepeaters: number[],\n\t\trouteSpeed: ZWaveDataRate,\n\t): Promise<boolean> {\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Assigning priority SUC return route...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tAssignPrioritySUCReturnRouteRequestTransmitReport\n\t\t\t>(\n\t\t\t\tnew AssignPrioritySUCReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\trepeaters,\n\t\t\t\t\trouteSpeed,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// Update the cached priority route\n\t\t\t\tthis.setPrioritySUCReturnRouteCached(nodeId, {\n\t\t\t\t\trepeaters,\n\t\t\t\t\trouteSpeed,\n\t\t\t\t});\n\t\t\t\t// The command above assigns a full set of new routes, so\n\t\t\t\t// custom SUC return routes are no longer valid\n\t\t\t\tthis.setCustomSUCReturnRoutesCached(nodeId, undefined);\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Assigning priority SUC return route failed: ${\n\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\te,\n\t\t\t\t\t)\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate setPrioritySUCReturnRouteCached(\n\t\tnodeId: number,\n\t\troute: Route | undefined,\n\t): void {\n\t\tthis.driver.cacheSet(\n\t\t\tcacheKeys.node(nodeId).prioritySUCReturnRoute,\n\t\t\troute,\n\t\t);\n\t}\n\n\t/**\n\t * Returns which priority route is currently assigned from the given end node to the SUC.\n\t *\n\t * **Note:** This is using cached information, since there's no way to query priority routes from a node.\n\t * If another controller has assigned routes in the meantime, this information may be out of date.\n\t */\n\tpublic getPrioritySUCReturnRouteCached(nodeId: number): Route | undefined {\n\t\treturn this.driver.cacheGet(\n\t\t\tcacheKeys.node(nodeId).prioritySUCReturnRoute,\n\t\t);\n\t}\n\n\tprivate handleRouteAssignmentTransmitReport(\n\t\tmsg: TransmitReport,\n\t\tnodeId: number,\n\t): boolean {\n\t\tswitch (msg.transmitStatus) {\n\t\t\tcase TransmitStatus.OK:\n\t\t\t\treturn true;\n\t\t\tcase TransmitStatus.NoAck:\n\t\t\t\treturn false;\n\t\t\tcase TransmitStatus.NoRoute:\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tnodeId,\n\t\t\t\t\t`Route resolution failed`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Sets the priority route which will always be used for the first transmission attempt from the controller to the given node.\n\t * @param destinationNodeId The ID of the node that should be reached via the priority route\n\t * @param repeaters The IDs of the nodes that should be used as repeaters, or an empty array for direct connection\n\t * @param routeSpeed The transmission speed to use for the route\n\t */\n\tpublic async setPriorityRoute(\n\t\tdestinationNodeId: number,\n\t\trepeaters: number[],\n\t\trouteSpeed: ZWaveDataRate,\n\t): Promise<boolean> {\n\t\t// 7.xx firmwares (up to at least 7.19.2) have a bug where the response to\n\t\t// SetPriorityRoute is missing the result byte when used with 16-bit node IDs.\n\t\t// So we temporarily switch back to 8-bit node IDs for this message\n\t\tawait this.trySetNodeIDType(NodeIDType.Short);\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`Setting priority route to node ${destinationNodeId}...`,\n\t\t);\n\n\t\tlet ret: boolean;\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetPriorityRouteRequest({\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\trepeaters,\n\t\t\t\t\trouteSpeed,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tret = result.isOK();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Setting priority route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\tret = false;\n\t\t}\n\n\t\t// Switch back to 16-bit node IDs\n\t\tawait this.trySetNodeIDType(NodeIDType.Long);\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Removes the priority route used for the first transmission attempt from the controller to the given node.\n\t * @param destinationNodeId The ID of the node that should be reached via the priority route\n\t */\n\tpublic async removePriorityRoute(\n\t\tdestinationNodeId: number,\n\t): Promise<boolean> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`Removing priority route to node ${destinationNodeId}...`,\n\t\t);\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetPriorityRouteRequest({\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\t// no repeaters = remove\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\treturn result.isOK();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Removing priority route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the priority route which is currently set for a node.\n\t * If none is set, either the LWR or the NLWR is returned.\n\t * If no route is known yet, this returns `undefined`.\n\t *\n\t * @param destinationNodeId The ID of the node for which the priority route should be returned\n\t */\n\tpublic async getPriorityRoute(destinationNodeId: number): Promise<\n\t\t| {\n\t\t\trouteKind:\n\t\t\t\t| RouteKind.LWR\n\t\t\t\t| RouteKind.NLWR\n\t\t\t\t| RouteKind.Application;\n\t\t\trepeaters: number[];\n\t\t\trouteSpeed: ZWaveDataRate;\n\t\t}\n\t\t| undefined\n\t> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`Retrieving priority route to node ${destinationNodeId}...`,\n\t\t);\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tGetPriorityRouteResponse\n\t\t\t>(\n\t\t\t\tnew GetPriorityRouteRequest({\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (result.routeKind === RouteKind.None) return undefined;\n\n\t\t\t// If we do not have any route statistics for the node yet, use this information to\n\t\t\t// to at least partially populate it\n\t\t\tconst node = this.nodes.get(destinationNodeId);\n\t\t\tif (\n\t\t\t\tnode\n\t\t\t\t&& (result.routeKind === RouteKind.LWR\n\t\t\t\t\t|| result.routeKind === RouteKind.NLWR)\n\t\t\t) {\n\t\t\t\tconst routeName = result.routeKind === RouteKind.LWR\n\t\t\t\t\t? \"lwr\"\n\t\t\t\t\t: \"nlwr\";\n\n\t\t\t\tif (!node.statistics[routeName]) {\n\t\t\t\t\tnode.updateStatistics((current) => {\n\t\t\t\t\t\tconst ret = { ...current };\n\t\t\t\t\t\tret[routeName] = {\n\t\t\t\t\t\t\trepeaters: result.repeaters!,\n\t\t\t\t\t\t\tprotocolDataRate:\n\t\t\t\t\t\t\t\t// ZWaveDataRate is a subset of ProtocolDataRate\n\t\t\t\t\t\t\t\tresult\n\t\t\t\t\t\t\t\t\t.routeSpeed as unknown as ProtocolDataRate,\n\t\t\t\t\t\t};\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\trouteKind: result.routeKind,\n\t\t\t\trepeaters: result.repeaters!,\n\t\t\t\trouteSpeed: result.routeSpeed!,\n\t\t\t};\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Retrieving priority route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Returns a dictionary of all association groups of this node or endpoint and their information.\n\t * If no endpoint is given, the associations of the root device (endpoint 0) are returned.\n\t * This only works AFTER the interview process\n\t */\n\tpublic getAssociationGroups(\n\t\tsource: AssociationAddress,\n\t): ReadonlyMap<number, AssociationGroup> {\n\t\tconst node = this.nodes.getOrThrow(source.nodeId);\n\t\tconst endpoint = node.getEndpointOrThrow(source.endpoint ?? 0);\n\n\t\treturn ccUtils.getAssociationGroups(this.driver, endpoint);\n\t}\n\n\t/**\n\t * Returns all association groups that exist on a node and all its endpoints.\n\t * The returned map uses the endpoint index as keys and its values are maps of group IDs to their definition\n\t */\n\tpublic getAllAssociationGroups(\n\t\tnodeId: number,\n\t): ReadonlyMap<number, ReadonlyMap<number, AssociationGroup>> {\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\treturn ccUtils.getAllAssociationGroups(this.driver, node);\n\t}\n\n\t/**\n\t * Returns all associations (Multi Channel or normal) that are configured on the root device or an endpoint of a node.\n\t * If no endpoint is given, the associations of the root device (endpoint 0) are returned.\n\t */\n\tpublic getAssociations(\n\t\tsource: AssociationAddress,\n\t): ReadonlyMap<number, readonly AssociationAddress[]> {\n\t\tconst node = this.nodes.getOrThrow(source.nodeId);\n\t\tconst endpoint = node.getEndpointOrThrow(source.endpoint ?? 0);\n\n\t\treturn ccUtils.getAssociations(this.driver, endpoint);\n\t}\n\n\t/**\n\t * Returns all associations (Multi Channel or normal) that are configured on a node and all its endpoints.\n\t * The returned map uses the source node+endpoint as keys and its values are a map of association group IDs to target node+endpoint.\n\t */\n\tpublic getAllAssociations(\n\t\tnodeId: number,\n\t): ReadonlyObjectKeyMap<\n\t\tAssociationAddress,\n\t\tReadonlyMap<number, readonly AssociationAddress[]>\n\t> {\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\treturn ccUtils.getAllAssociations(this.driver, node);\n\t}\n\n\t/**\n\t * Checks if a given association is allowed.\n\t */\n\tpublic checkAssociation(\n\t\tsource: AssociationAddress,\n\t\tgroup: number,\n\t\tdestination: AssociationAddress,\n\t): AssociationCheckResult {\n\t\tconst node = this.nodes.getOrThrow(source.nodeId);\n\t\tconst endpoint = node.getEndpointOrThrow(source.endpoint ?? 0);\n\n\t\treturn ccUtils.checkAssociation(\n\t\t\tthis.driver,\n\t\t\tendpoint,\n\t\t\tgroup,\n\t\t\tdestination,\n\t\t);\n\t}\n\n\t/**\n\t * Adds associations to a node or endpoint.\n\t *\n\t * **Note:** This method will throw if:\n\t * * the source node, endpoint or association group does not exist,\n\t * * the source node is a ZWLR node and the destination is not the SIS\n\t * * the destination node is a ZWLR node\n\t * * the association is not allowed for other reasons. In this case, the error's\n\t * `context` property will contain an array with all forbidden destinations, each with an added `checkResult` property\n\t * which contains the reason why the association is forbidden:\n\t * ```ts\n\t * {\n\t * checkResult: AssociationCheckResult;\n\t * nodeId: number;\n\t * endpoint?: number | undefined;\n\t * }[]\n\t * ```\n\t */\n\tpublic async addAssociations(\n\t\tsource: AssociationAddress,\n\t\tgroup: number,\n\t\tdestinations: AssociationAddress[],\n\t): Promise<void> {\n\t\tconst node = this.nodes.getOrThrow(source.nodeId);\n\t\tconst endpoint = node.getEndpointOrThrow(source.endpoint ?? 0);\n\n\t\tawait ccUtils.addAssociations(\n\t\t\tthis.driver,\n\t\t\tendpoint,\n\t\t\tgroup,\n\t\t\tdestinations,\n\t\t);\n\n\t\tif (isLongRangeNodeId(source.nodeId)) return;\n\n\t\t// Nodes need a return route to be able to send commands to other nodes\n\t\tconst destinationNodeIDs = distinct(\n\t\t\tdestinations.map((d) => d.nodeId),\n\t\t\t// Except to the controller itself - this route is already known\n\t\t).filter((id) => id !== this.ownNodeId);\n\t\tfor (const id of destinationNodeIDs) {\n\t\t\tif (id === this._ownNodeId) {\n\t\t\t\tawait this.assignSUCReturnRoutes(source.nodeId);\n\t\t\t} else {\n\t\t\t\tawait this.assignReturnRoutes(source.nodeId, id);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Removes the given associations from a node or endpoint\n\t */\n\tpublic removeAssociations(\n\t\tsource: AssociationAddress,\n\t\tgroup: number,\n\t\tdestinations: AssociationAddress[],\n\t): Promise<void> {\n\t\tconst node = this.nodes.getOrThrow(source.nodeId);\n\t\tconst endpoint = node.getEndpointOrThrow(source.endpoint ?? 0);\n\n\t\treturn ccUtils.removeAssociations(\n\t\t\tthis.driver,\n\t\t\tendpoint,\n\t\t\tgroup,\n\t\t\tdestinations,\n\t\t);\n\t}\n\n\t/**\n\t * Removes a node from all other nodes' associations\n\t * WARNING: It is not recommended to await this method\n\t */\n\tpublic async removeNodeFromAllAssociations(nodeId: number): Promise<void> {\n\t\tconst tasks: Promise<any>[] = [];\n\t\t// Check each endpoint of each node if they have an association to this node\n\t\tfor (const node of this.nodes.values()) {\n\t\t\tif (node.id === this._ownNodeId || node.id === nodeId) continue;\n\t\t\tif (node.interviewStage !== InterviewStage.Complete) continue;\n\n\t\t\tfor (const endpoint of node.getAllEndpoints()) {\n\t\t\t\t// Prefer multi channel associations if that is available\n\t\t\t\tif (\n\t\t\t\t\tendpoint.commandClasses[\n\t\t\t\t\t\t\"Multi Channel Association\"\n\t\t\t\t\t].isSupported()\n\t\t\t\t) {\n\t\t\t\t\tconst existing = MultiChannelAssociationCC\n\t\t\t\t\t\t.getAllDestinationsCached(\n\t\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\t\tendpoint,\n\t\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\t[...existing.values()].some((dests) =>\n\t\t\t\t\t\t\tdests.some((a) => a.nodeId === nodeId)\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\ttasks.push(\n\t\t\t\t\t\t\tendpoint.commandClasses[\n\t\t\t\t\t\t\t\t\"Multi Channel Association\"\n\t\t\t\t\t\t\t].removeDestinations({\n\t\t\t\t\t\t\t\tnodeIds: [nodeId],\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else if (endpoint.commandClasses.Association.isSupported()) {\n\t\t\t\t\tconst existing = AssociationCC.getAllDestinationsCached(\n\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\tendpoint,\n\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\t[...existing.values()].some((dests) =>\n\t\t\t\t\t\t\tdests.some((a) => a.nodeId === nodeId)\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\ttasks.push(\n\t\t\t\t\t\t\tendpoint.commandClasses.Association\n\t\t\t\t\t\t\t\t.removeNodeIdsFromAllGroups(\n\t\t\t\t\t\t\t\t\t[nodeId],\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tawait Promise.all(tasks);\n\t}\n\n\t/**\n\t * Tests if a node is marked as failed in the controller's memory\n\t * @param nodeId The id of the node in question\n\t */\n\tpublic async isFailedNode(nodeId: number): Promise<boolean> {\n\t\tconst result = await this.driver.sendMessage<IsFailedNodeResponse>(\n\t\t\tnew IsFailedNodeRequest({ failedNodeId: nodeId }),\n\t\t);\n\t\treturn result.result;\n\t}\n\n\t/**\n\t * Removes a failed node from the controller's memory. If the process fails, this will throw an exception with the details why.\n\t * @param nodeId The id of the node to remove\n\t */\n\tpublic async removeFailedNode(nodeId: number): Promise<void> {\n\t\tawait this.removeFailedNodeInternal(\n\t\t\tnodeId,\n\t\t\tRemoveNodeReason.RemoveFailed,\n\t\t);\n\t}\n\n\t/** @internal */\n\tpublic async removeFailedNodeInternal(\n\t\tnodeId: number,\n\t\treason: RemoveNodeReason,\n\t): Promise<void> {\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\n\t\t// It is possible that this method is called while the node is still in the process of resetting or leaving the network\n\t\t// Therefore, we ping multiple times in case of success and wait a bit in between\n\t\tlet didFail = false;\n\t\tconst MAX_ATTEMPTS = 3;\n\t\tfor (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n\t\t\tif (await node.ping()) {\n\t\t\t\tif (attempt < MAX_ATTEMPTS) await wait(2000);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tdidFail = true;\n\t\t\tbreak;\n\t\t}\n\t\tif (!didFail) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The node removal process could not be started because the node responded to a ping.`,\n\t\t\t\tZWaveErrorCodes.RemoveFailedNode_Failed,\n\t\t\t);\n\t\t}\n\n\t\tconst result = await this.driver.sendMessage<\n\t\t\tRemoveFailedNodeRequestStatusReport | RemoveFailedNodeResponse\n\t\t>(new RemoveFailedNodeRequest({ failedNodeId: nodeId }));\n\n\t\tif (result instanceof RemoveFailedNodeResponse) {\n\t\t\t// This implicates that the process was unsuccessful.\n\t\t\tlet message =\n\t\t\t\t`The node removal process could not be started due to the following reasons:`;\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.removeStatus\n\t\t\t\t\t& RemoveFailedNodeStartFlags.NotPrimaryController\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage += \"\\n\u00B7 This controller is not the primary controller\";\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.removeStatus\n\t\t\t\t\t& RemoveFailedNodeStartFlags.NodeNotFound\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage +=\n\t\t\t\t\t`\\n\u00B7 Node ${nodeId} is not in the list of failed nodes`;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.removeStatus\n\t\t\t\t\t& RemoveFailedNodeStartFlags.RemoveProcessBusy\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage += `\\n\u00B7 The node removal process is currently busy`;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.removeStatus\n\t\t\t\t\t& RemoveFailedNodeStartFlags.RemoveFailed\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage +=\n\t\t\t\t\t`\\n\u00B7 The controller is busy or the node has responded`;\n\t\t\t}\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.RemoveFailedNode_Failed,\n\t\t\t);\n\t\t} else {\n\t\t\tswitch (result.removeStatus) {\n\t\t\t\tcase RemoveFailedNodeStatus.NodeOK:\n\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t`The node could not be removed because it has responded`,\n\t\t\t\t\t\tZWaveErrorCodes.RemoveFailedNode_NodeOK,\n\t\t\t\t\t);\n\t\t\t\tcase RemoveFailedNodeStatus.NodeNotRemoved:\n\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t`The removal process could not be completed`,\n\t\t\t\t\t\tZWaveErrorCodes.RemoveFailedNode_Failed,\n\t\t\t\t\t);\n\t\t\t\tdefault:\n\t\t\t\t\t// If everything went well, the status is RemoveFailedNodeStatus.NodeRemoved\n\n\t\t\t\t\t// Emit the removed event so the driver and applications can react\n\t\t\t\t\tthis.emit(\"node removed\", this.nodes.get(nodeId)!, reason);\n\t\t\t\t\t// and forget the node\n\t\t\t\t\tthis._nodes.delete(nodeId);\n\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Replace a failed node from the controller's memory. If the process fails, this will throw an exception with the details why.\n\t * @param nodeId The id of the node to replace\n\t * @param options Defines the inclusion strategy to use for the replacement node\n\t */\n\tpublic async replaceFailedNode(\n\t\tnodeId: number,\n\t\toptions: ReplaceNodeOptions = {\n\t\t\tstrategy: InclusionStrategy.Insecure,\n\t\t},\n\t): Promise<boolean> {\n\t\tif (\n\t\t\tthis._inclusionState === InclusionState.Including\n\t\t\t|| this._inclusionState === InclusionState.Excluding\n\t\t\t|| this._inclusionState === InclusionState.Busy\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Leave SmartStart listening mode so we can switch to exclusion mode\n\t\tawait this.pauseSmartStart();\n\n\t\tthis.setInclusionState(InclusionState.Busy);\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`starting replace failed node process...`,\n\t\t);\n\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\tif (await node.ping()) {\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The node replace process could not be started because the node responded to a ping.`,\n\t\t\t\tZWaveErrorCodes.ReplaceFailedNode_Failed,\n\t\t\t);\n\t\t}\n\n\t\tthis._inclusionOptions = options;\n\n\t\tconst result = await this.driver.sendMessage<ReplaceFailedNodeResponse>(\n\t\t\tnew ReplaceFailedNodeRequest({\n\t\t\t\tfailedNodeId: nodeId,\n\t\t\t}),\n\t\t);\n\n\t\tif (!result.isOK()) {\n\t\t\t// This implicates that the process was unsuccessful.\n\t\t\tlet message =\n\t\t\t\t`The node replace process could not be started due to the following reasons:`;\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.replaceStatus\n\t\t\t\t\t& ReplaceFailedNodeStartFlags.NotPrimaryController\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage += \"\\n\u00B7 This controller is not the primary controller\";\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.replaceStatus\n\t\t\t\t\t& ReplaceFailedNodeStartFlags.NodeNotFound\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage +=\n\t\t\t\t\t`\\n\u00B7 Node ${nodeId} is not in the list of failed nodes`;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.replaceStatus\n\t\t\t\t\t& ReplaceFailedNodeStartFlags.ReplaceProcessBusy\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage += `\\n\u00B7 The node replace process is currently busy`;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.replaceStatus\n\t\t\t\t\t& ReplaceFailedNodeStartFlags.ReplaceFailed\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage +=\n\t\t\t\t\t`\\n\u00B7 The controller is busy or the node has responded`;\n\t\t\t}\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.ReplaceFailedNode_Failed,\n\t\t\t);\n\t\t} else {\n\t\t\t// Remember which node we're trying to replace\n\t\t\tthis._nodePendingReplace = this.nodes.get(nodeId);\n\t\t\tthis._replaceFailedPromise = createDeferredPromise();\n\t\t\treturn this._replaceFailedPromise;\n\t\t}\n\t}\n\n\t/** Configure the RF region at the Z-Wave API Module */\n\tpublic async setRFRegion(region: RFRegion): Promise<boolean> {\n\t\t// Setting the \"default\" region is not possible. Controllers are supposed to\n\t\t// default to the EU region, so we do just that.\n\t\tif (region === RFRegion[\"Default (EU)\"]) region = RFRegion.Europe;\n\n\t\t// Unless auto-upgrade to LR regions is disabled, try to find a suitable LR replacement region\n\t\tif (this.driver.options.rf?.preferLRRegion !== false) {\n\t\t\tregion = this.tryGetLRCapableRegion(region);\n\t\t}\n\t\treturn this.setRFRegionInternal(region, true);\n\t}\n\n\t/** Configure the RF region at the Z-Wave API Module */\n\tprivate async setRFRegionInternal(\n\t\tregion: RFRegion,\n\t\tsoftReset: boolean = true,\n\t): Promise<boolean> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_SetRFRegionResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(new SerialAPISetup_SetRFRegionRequest({ region }));\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support setting the RF region!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\tif (softReset && result.success) await this.driver.trySoftReset();\n\t\tthis._rfRegion = region;\n\t\treturn result.success;\n\t}\n\n\t/** Request the current RF region configured at the Z-Wave API Module */\n\tpublic async getRFRegion(): Promise<RFRegion> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetRFRegionResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(new SerialAPISetup_GetRFRegionRequest());\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the RF region!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\tthis._rfRegion = result.region;\n\t\treturn result.region;\n\t}\n\n\t/**\n\t * Query the supported regions of the Z-Wave API Module\n\t *\n\t * **Note:** Applications should prefer using {@link getSupportedRFRegions} instead\n\t */\n\tpublic async querySupportedRFRegions(): Promise<RFRegion[]> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetSupportedRegionsResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(new SerialAPISetup_GetSupportedRegionsRequest());\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the supported RF regions!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn result.supportedRegions;\n\t}\n\n\t/**\n\t * Query the supported regions of the Z-Wave API Module\n\t *\n\t * **Note:** Applications should prefer reading the cached value from {@link supportedRFRegions} instead\n\t */\n\tpublic async queryRFRegionInfo(\n\t\tregion: RFRegion,\n\t): Promise<{\n\t\tregion: RFRegion;\n\t\tsupportsZWave: boolean;\n\t\tsupportsLongRange: boolean;\n\t\tincludesRegion?: RFRegion;\n\t}> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetRegionInfoResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(new SerialAPISetup_GetRegionInfoRequest({ region }));\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the RF region info!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn pick(result, [\n\t\t\t\"region\",\n\t\t\t\"supportsZWave\",\n\t\t\t\"supportsLongRange\",\n\t\t\t\"includesRegion\",\n\t\t]);\n\t}\n\n\t/**\n\t * Returns the RF regions supported by this controller, or `undefined` if the information is not known yet.\n\t *\n\t * @param filterSubsets Whether to exclude regions that are subsets of other regions,\n\t * for example `USA` which is a subset of `USA (Long Range)`\n\t */\n\tpublic getSupportedRFRegions(\n\t\tfilterSubsets: boolean = true,\n\t): MaybeNotKnown<readonly RFRegion[]> {\n\t\t// If supported by the firmware, rely on the queried information\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetSupportedRegions,\n\t\t\t)\n\t\t) {\n\t\t\tif (this._supportedRegions == NOT_KNOWN) return NOT_KNOWN;\n\t\t\tconst allRegions = new Set(this._supportedRegions.keys());\n\t\t\tif (filterSubsets) {\n\t\t\t\tfor (const region of this._supportedRegions.values()) {\n\t\t\t\t\tif (region.includesRegion != undefined) {\n\t\t\t\t\t\tallRegions.delete(region.includesRegion);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn [...allRegions].sort((a, b) => a - b);\n\t\t}\n\n\t\t// Fallback: Hardcoded list of known supported regions\n\t\tconst ret = new Set([\n\t\t\t// Always supported\n\t\t\tRFRegion.Europe,\n\t\t\tRFRegion.USA,\n\t\t\tRFRegion[\"Australia/New Zealand\"],\n\t\t\tRFRegion[\"Hong Kong\"],\n\t\t\tRFRegion.India,\n\t\t\tRFRegion.Israel,\n\t\t\tRFRegion.Russia,\n\t\t\tRFRegion.China,\n\t\t\tRFRegion.Japan,\n\t\t\tRFRegion.Korea,\n\t\t\tRFRegion[\"Default (EU)\"],\n\t\t]);\n\n\t\tif (this.isLongRangeCapable()) {\n\t\t\t// All LR capable controllers support USA Long Range\n\t\t\tret.add(RFRegion[\"USA (Long Range)\"]);\n\t\t\tif (filterSubsets) ret.delete(RFRegion.USA);\n\n\t\t\t// EU Long Range was added in SDK 7.22 for 800 series chips\n\t\t\t// 7.22.1 adds support for querying the supported regions, so the following\n\t\t\t// is really only necessary for 7.22.0.\n\t\t\tif (\n\t\t\t\ttypeof this._zwaveChipType === \"string\"\n\t\t\t\t&& getChipTypeAndVersion(this._zwaveChipType)?.type === 8\n\t\t\t\t&& this.sdkVersionGte(\"7.22\")\n\t\t\t) {\n\t\t\t\tret.add(RFRegion[\"Europe (Long Range)\"]);\n\t\t\t\tif (filterSubsets) ret.delete(RFRegion.Europe);\n\t\t\t}\n\t\t}\n\n\t\treturn [...ret].sort((a, b) => a - b);\n\t}\n\n\t/** Configure the Powerlevel setting of the Z-Wave API */\n\tpublic async setPowerlevel(\n\t\tpowerlevel: number,\n\t\tmeasured0dBm: number,\n\t): Promise<boolean> {\n\t\tlet request: Message;\n\t\tif (\n\t\t\tthis.supportedSerialAPISetupCommands?.includes(\n\t\t\t\tSerialAPISetupCommand.SetPowerlevel16Bit,\n\t\t\t)\n\t\t) {\n\t\t\trequest = new SerialAPISetup_SetPowerlevel16BitRequest({\n\t\t\t\tpowerlevel,\n\t\t\t\tmeasured0dBm,\n\t\t\t});\n\t\t} else {\n\t\t\trequest = new SerialAPISetup_SetPowerlevelRequest({\n\t\t\t\tpowerlevel,\n\t\t\t\tmeasured0dBm,\n\t\t\t});\n\t\t}\n\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_SetPowerlevelResponse\n\t\t\t| SerialAPISetup_SetPowerlevel16BitResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(request);\n\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support setting the powerlevel!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn result.success;\n\t}\n\n\t/** Request the Powerlevel setting of the Z-Wave API */\n\tpublic async getPowerlevel(): Promise<\n\t\tPick<\n\t\t\tSerialAPISetup_GetPowerlevelResponse,\n\t\t\t\"powerlevel\" | \"measured0dBm\"\n\t\t>\n\t> {\n\t\tlet request: Message;\n\t\tif (\n\t\t\tthis.supportedSerialAPISetupCommands?.includes(\n\t\t\t\tSerialAPISetupCommand.GetPowerlevel16Bit,\n\t\t\t)\n\t\t) {\n\t\t\trequest = new SerialAPISetup_GetPowerlevel16BitRequest();\n\t\t} else {\n\t\t\trequest = new SerialAPISetup_GetPowerlevelRequest();\n\t\t}\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetPowerlevelResponse\n\t\t\t| SerialAPISetup_GetPowerlevel16BitResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(request);\n\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the powerlevel!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn pick(result, [\"powerlevel\", \"measured0dBm\"]);\n\t}\n\n\t/** Configure the maximum TX powerlevel for Z-Wave Long Range */\n\tpublic async setMaxLongRangePowerlevel(\n\t\tlimit: number,\n\t): Promise<boolean> {\n\t\tconst request = new SerialAPISetup_SetLongRangeMaximumTxPowerRequest({\n\t\t\tlimit,\n\t\t});\n\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_SetLongRangeMaximumTxPowerResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(request);\n\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support setting the max. Long Range powerlevel!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\tif (result.success) {\n\t\t\tthis._maxLongRangePowerlevel = limit;\n\t\t}\n\t\treturn result.success;\n\t}\n\n\t/** Request the maximum TX powerlevel setting for Z-Wave Long Range */\n\tpublic async getMaxLongRangePowerlevel(): Promise<number> {\n\t\tconst request = new SerialAPISetup_GetLongRangeMaximumTxPowerRequest();\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetLongRangeMaximumTxPowerResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(request);\n\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the max. Long Range powerlevel!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\tthis._maxLongRangePowerlevel = result.limit;\n\t\treturn result.limit;\n\t}\n\n\t/**\n\t * Configure channel to use for Z-Wave Long Range.\n\t */\n\tpublic async setLongRangeChannel(\n\t\tchannel:\n\t\t\t| LongRangeChannel.A\n\t\t\t| LongRangeChannel.B\n\t\t\t| LongRangeChannel.Auto,\n\t): Promise<boolean> {\n\t\tif (\n\t\t\t!this._supportsLongRangeAutoChannelSelection\n\t\t\t&& channel === LongRangeChannel.Auto\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support automatic Long Range channel selection!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\tconst result = await this.driver.sendMessage<\n\t\t\tSetLongRangeChannelResponse\n\t\t>(\n\t\t\tnew SetLongRangeChannelRequest({ channel }),\n\t\t);\n\n\t\tif (result.success) {\n\t\t\tthis._longRangeChannel = channel;\n\t\t}\n\t\treturn result.success;\n\t}\n\n\t/** Request the channel setting and capabilities for Z-Wave Long Range */\n\tpublic async getLongRangeChannel(): Promise<\n\t\t{ channel: LongRangeChannel; supportsAutoChannelSelection: boolean }\n\t> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\tGetLongRangeChannelResponse\n\t\t>(\n\t\t\tnew GetLongRangeChannelRequest(),\n\t\t);\n\n\t\tconst channel = result.autoChannelSelectionActive\n\t\t\t\t&& result.supportsAutoChannelSelection\n\t\t\t? LongRangeChannel.Auto\n\t\t\t: result.channel;\n\n\t\tthis._longRangeChannel = channel;\n\t\tthis._supportsLongRangeAutoChannelSelection =\n\t\t\tresult.supportsAutoChannelSelection;\n\n\t\treturn {\n\t\t\tchannel,\n\t\t\tsupportsAutoChannelSelection: result.supportsAutoChannelSelection,\n\t\t};\n\t}\n\n\t/**\n\t * @internal\n\t * Configure whether the Z-Wave API should use short (8 bit) or long (16 bit) Node IDs\n\t */\n\tpublic async setNodeIDType(nodeIdType: NodeIDType): Promise<boolean> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`Switching serial API to ${\n\t\t\t\tnodeIdType === NodeIDType.Short ? 8 : 16\n\t\t\t}-bit node IDs...`,\n\t\t);\n\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_SetNodeIDTypeResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(\n\t\t\tnew SerialAPISetup_SetNodeIDTypeRequest({\n\t\t\t\tnodeIdType,\n\t\t\t}),\n\t\t);\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support switching between short and long node IDs!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t} else {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Switching to ${\n\t\t\t\t\tnodeIdType === NodeIDType.Short ? 8 : 16\n\t\t\t\t}-bit node IDs ${result.success ? \"successful\" : \"failed\"}`,\n\t\t\t);\n\n\t\t\tif (result.success) {\n\t\t\t\tthis._nodeIdType = nodeIdType;\n\t\t\t}\n\t\t}\n\t\treturn result.success;\n\t}\n\n\tpublic async trySetNodeIDType(nodeIdType: NodeIDType): Promise<boolean> {\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.SetNodeIDType,\n\t\t\t)\n\t\t) {\n\t\t\ttry {\n\t\t\t\treturn await this.setNodeIDType(nodeIdType);\n\t\t\t} catch {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @internal\n\t * Request the maximum payload that the Z-Wave API Module can accept for transmitting Z-Wave frames. This value depends on the RF Profile\n\t */\n\tpublic async getMaxPayloadSize(): Promise<number> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetMaximumPayloadSizeResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(new SerialAPISetup_GetMaximumPayloadSizeRequest(), {\n\t\t\tsupportCheck: false,\n\t\t});\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the max. payload size!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn result.maxPayloadSize;\n\t}\n\n\t/**\n\t * @internal\n\t * Request the maximum payload that the Z-Wave API Module can accept for transmitting Z-Wave Long Range frames. This value depends on the RF Profile\n\t */\n\tpublic async getMaxPayloadSizeLongRange(): Promise<number> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetLongRangeMaximumPayloadSizeResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(\n\t\t\tnew SerialAPISetup_GetLongRangeMaximumPayloadSizeRequest(),\n\t\t);\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the max. long range payload size!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn result.maxPayloadSize;\n\t}\n\n\t/**\n\t * Instructs a node to (re-)discover its neighbors.\n\t *\n\t * **WARNING:** On some controllers, this can cause new SUC return routes to be assigned.\n\t *\n\t * @returns `true` if the update was successful and the new neighbors can be retrieved using\n\t * {@link getNodeNeighbors}. `false` if the update failed.\n\t */\n\tpublic async discoverNodeNeighbors(nodeId: number): Promise<boolean> {\n\t\t// TODO: Consider making this not block the send queue.\n\t\t// However, I haven't actually seen a UpdateStarted callback in the wild,\n\t\t// so we don't know if that would even work.\n\n\t\t// We cannot discover neighbors for the controller\n\t\tif (nodeId === this._ownNodeId) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Discovering neighbors for the controller itself is not possible!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\t// During inclusion, the timeout is mainly required for the node to detect all neighbors\n\t\t// We do the same here, so we just reuse the timeout\n\t\tconst discoveryTimeout = computeNeighborDiscoveryTimeout(\n\t\t\tthis.driver,\n\t\t\t// Controllers take longer, just assume the worst case here\n\t\t\tNodeType.Controller,\n\t\t);\n\n\t\tconst resp = await this.driver.sendMessage<\n\t\t\tRequestNodeNeighborUpdateReport\n\t\t>(\n\t\t\tnew RequestNodeNeighborUpdateRequest({\n\t\t\t\tnodeId,\n\t\t\t\tdiscoveryTimeout,\n\t\t\t}),\n\t\t);\n\t\tconst success =\n\t\t\tresp.updateStatus === NodeNeighborUpdateStatus.UpdateDone;\n\n\t\tif (success) {\n\t\t\t// Not sure why, but Zniffer traces show that a node neighbor update can cause the controller to\n\t\t\t// also do AssignSUCReturnRoute. As a result, we need to invalidate our route cache.\n\t\t\tthis.setCustomSUCReturnRoutesCached(nodeId, undefined);\n\t\t}\n\n\t\treturn success;\n\t}\n\n\t/**\n\t * Returns the known list of neighbors for a node.\n\t *\n\t * Throws when the node is a Long Range node.\n\t */\n\tpublic async getNodeNeighbors(\n\t\tnodeId: number,\n\t\tonlyRepeaters: boolean = false,\n\t): Promise<readonly number[]> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot request node neighbors for Long Range node ${nodeId}`,\n\t\t\t\tZWaveErrorCodes.Controller_NotSupportedForLongRange,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: \"requesting node neighbors...\",\n\t\t\tdirection: \"outbound\",\n\t\t});\n\t\ttry {\n\t\t\tconst resp = await this.driver.sendMessage<GetRoutingInfoResponse>(\n\t\t\t\tnew GetRoutingInfoRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tremoveBadLinks: false,\n\t\t\t\t\tremoveNonRepeaters: onlyRepeaters,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\tmessage: `node neighbors received: ${resp.nodeIds.join(\", \")}`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t\treturn resp.nodeIds;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`requesting the node neighbors failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the known routes the controller will use to communicate with the nodes.\n\t *\n\t * This information is dynamically built using TX status reports and may not be accurate at all times.\n\t * Also, it may not be available immediately after startup or at all if the controller doesn't support this feature.\n\t *\n\t * **Note:** To keep information returned by this method updated, use the information contained in each node's `\"statistics\"` event.\n\t */\n\tpublic getKnownLifelineRoutes(): ReadonlyMap<number, LifelineRoutes> {\n\t\tconst ret = new Map<number, LifelineRoutes>();\n\t\tfor (const node of this.nodes.values()) {\n\t\t\tif (node.isControllerNode) continue;\n\t\t\tret.set(node.id, {\n\t\t\t\tlwr: node.statistics.lwr,\n\t\t\t\tnlwr: node.statistics.nlwr,\n\t\t\t});\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/** Request additional information about the controller/Z-Wave chip */\n\tpublic async getSerialApiInitData(): Promise<SerialApiInitData> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`querying additional controller information...`,\n\t\t);\n\t\tconst initData = await this.driver.sendMessage<\n\t\t\tGetSerialApiInitDataResponse\n\t\t>(\n\t\t\tnew GetSerialApiInitDataRequest(),\n\t\t);\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received additional controller information:\n Z-Wave API version: ${initData.zwaveApiVersion.version} (${initData.zwaveApiVersion.kind})${\n\t\t\t\tinitData.zwaveChipType\n\t\t\t\t\t? `\n Z-Wave chip type: ${\n\t\t\t\t\t\ttypeof initData.zwaveChipType === \"string\"\n\t\t\t\t\t\t\t? initData.zwaveChipType\n\t\t\t\t\t\t\t: `unknown (type: ${\n\t\t\t\t\t\t\t\tnum2hex(initData.zwaveChipType.type)\n\t\t\t\t\t\t\t}, version: ${\n\t\t\t\t\t\t\t\tnum2hex(initData.zwaveChipType.version)\n\t\t\t\t\t\t\t})`\n\t\t\t\t\t}`\n\t\t\t\t\t: \"\"\n\t\t\t}\n node type ${getEnumMemberName(NodeType, initData.nodeType)}\n controller role: ${initData.isPrimary ? \"primary\" : \"secondary\"}\n controller is the SIS: ${initData.isSIS}\n controller supports timers: ${initData.supportsTimers}\n Z-Wave Classic nodes: ${initData.nodeIds.join(\", \")}`,\n\t\t);\n\n\t\tconst ret: SerialApiInitData = {\n\t\t\t...pick(initData, [\n\t\t\t\t\"zwaveApiVersion\",\n\t\t\t\t\"zwaveChipType\",\n\t\t\t\t\"isPrimary\",\n\t\t\t\t\"isSIS\",\n\t\t\t\t\"nodeType\",\n\t\t\t\t\"supportsTimers\",\n\t\t\t]),\n\t\t\tnodeIds: [...initData.nodeIds],\n\t\t\t// ignore the initVersion, no clue what to do with it\n\t\t};\n\n\t\t// and remember the new info\n\t\tthis._zwaveApiVersion = initData.zwaveApiVersion;\n\t\tthis._zwaveChipType = initData.zwaveChipType;\n\t\tthis._isPrimary = initData.isPrimary;\n\t\tthis._isSIS = initData.isSIS;\n\t\tthis._nodeType = initData.nodeType;\n\t\tthis._supportsTimers = initData.supportsTimers;\n\n\t\treturn ret;\n\t}\n\n\t/** Determines the controller's network role/capabilities */\n\tpublic async getControllerCapabilities(): Promise<ControllerCapabilities> {\n\t\tthis.driver.controllerLog.print(`querying controller capabilities...`);\n\t\tconst result = await this.driver.sendMessage<\n\t\t\tGetControllerCapabilitiesResponse\n\t\t>(\n\t\t\tnew GetControllerCapabilitiesRequest(),\n\t\t\t{ supportCheck: false },\n\t\t);\n\n\t\tconst ret: ControllerCapabilities = {\n\t\t\tisSecondary: result.isSecondary,\n\t\t\tisUsingHomeIdFromOtherNetwork: result.isUsingHomeIdFromOtherNetwork,\n\t\t\tisSISPresent: result.isSISPresent,\n\t\t\twasRealPrimary: result.wasRealPrimary,\n\t\t\tisSUC: result.isStaticUpdateController,\n\t\t\tnoNodesIncluded: result.noNodesIncluded,\n\t\t};\n\n\t\tthis._isSecondary = ret.isSecondary;\n\t\tthis._isUsingHomeIdFromOtherNetwork = ret.isUsingHomeIdFromOtherNetwork;\n\t\tthis._isSISPresent = ret.isSISPresent;\n\t\tthis._wasRealPrimary = ret.wasRealPrimary;\n\t\tthis._isSUC = ret.isSUC;\n\t\tthis._noNodesIncluded = ret.noNodesIncluded;\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received controller capabilities:\n controller role: ${getEnumMemberName(ControllerRole, this.role!)}\n is the SUC: ${ret.isSUC}\n started this network: ${!ret.isUsingHomeIdFromOtherNetwork}\n SIS is present: ${ret.isSISPresent}\n was real primary: ${ret.wasRealPrimary}`,\n\t\t);\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * @internal\n\t * Deserializes the controller information and all nodes from the cache.\n\t */\n\tpublic async deserialize(): Promise<void> {\n\t\tif (!this.driver.networkCache) return;\n\t\tconst cache = this.driver.networkCache;\n\n\t\t// Deserialize information for all nodes\n\t\tfor (const node of this.nodes.values()) {\n\t\t\tawait node.deserialize();\n\t\t}\n\n\t\t// Remove nodes which no longer exist from the cache\n\t\t// TODO: Do the same when removing a node\n\t\tfor (const cacheKey of cache.keys()) {\n\t\t\tconst nodeId = cacheKeyUtils.nodeIdFromKey(cacheKey);\n\t\t\tif (nodeId && !this.nodes.has(nodeId)) {\n\t\t\t\tcache.delete(cacheKey);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Turns the Z-Wave radio on or off */\n\tpublic async toggleRF(enabled: boolean): Promise<boolean> {\n\t\ttry {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Turning RF ${enabled ? \"on\" : \"off\"}...`,\n\t\t\t);\n\t\t\tconst ret = await this.driver.sendMessage<SetRFReceiveModeResponse>(\n\t\t\t\tnew SetRFReceiveModeRequest({ enabled }),\n\t\t\t);\n\t\t\treturn ret.isOK();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Error turning RF ${enabled ? \"on\" : \"off\"}: ${\n\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\te,\n\t\t\t\t\t)\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate _nvm: NVMAdapter | undefined;\n\t/** Provides access to the controller's non-volatile memory */\n\tpublic get nvm(): NVMAdapter {\n\t\tif (!this._nvm) {\n\t\t\tif (this.sdkVersionGte(\"7.0\")) {\n\t\t\t\tconst io = new BufferedNVMReader(new SerialNVMIO700(this));\n\t\t\t\tconst nvm3 = new NVM3(io);\n\t\t\t\tthis._nvm = new NVM3Adapter(nvm3);\n\t\t\t} else {\n\t\t\t\tconst io = new BufferedNVMReader(new SerialNVMIO500(this));\n\t\t\t\tconst nvm = new NVM500(io);\n\t\t\t\tthis._nvm = new NVM500Adapter(nvm);\n\t\t\t}\n\t\t}\n\n\t\treturn this._nvm;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Initialize the Firmware Update functionality and determine if the firmware can be updated.\n\t */\n\tprivate async firmwareUpdateNVMInit(): Promise<boolean> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tFirmwareUpdateNVM_InitResponse\n\t\t>(\n\t\t\tnew FirmwareUpdateNVM_InitRequest(),\n\t\t);\n\t\treturn ret.supported;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Set the NEWIMAGE marker in the NVM (to the given value), which is used to signal that a new firmware image is present\n\t */\n\tprivate async firmwareUpdateNVMSetNewImage(\n\t\tvalue: boolean = true,\n\t): Promise<void> {\n\t\tawait this.driver.sendMessage<FirmwareUpdateNVM_SetNewImageResponse>(\n\t\t\tnew FirmwareUpdateNVM_SetNewImageRequest({\n\t\t\t\tnewImage: value,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Return the value of the NEWIMAGE marker in the NVM, which is used to signal that a new firmware image is present\n\t */\n\tprivate async firmwareUpdateNVMGetNewImage(): Promise<boolean> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tFirmwareUpdateNVM_GetNewImageResponse\n\t\t>(\n\t\t\tnew FirmwareUpdateNVM_GetNewImageRequest(),\n\t\t);\n\t\treturn ret.newImage;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Calculates the CRC-16 for the specified block of data in the NVM\n\t */\n\tprivate async firmwareUpdateNVMUpdateCRC16(\n\t\toffset: number,\n\t\tblockLength: number,\n\t\tcrcSeed: number,\n\t): Promise<number> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tFirmwareUpdateNVM_UpdateCRC16Response\n\t\t>(\n\t\t\tnew FirmwareUpdateNVM_UpdateCRC16Request({\n\t\t\t\toffset,\n\t\t\t\tblockLength,\n\t\t\t\tcrcSeed,\n\t\t\t}),\n\t\t);\n\t\treturn ret.crc16;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Writes the given data into the firmware update region of the NVM.\n\t */\n\tprivate async firmwareUpdateNVMWrite(\n\t\toffset: number,\n\t\tbuffer: Uint8Array,\n\t): Promise<void> {\n\t\tawait this.driver.sendMessage<FirmwareUpdateNVM_WriteResponse>(\n\t\t\tnew FirmwareUpdateNVM_WriteRequest({\n\t\t\t\toffset,\n\t\t\t\tbuffer,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Checks if the firmware present in the NVM is valid\n\t */\n\tprivate async firmwareUpdateNVMIsValidCRC16(): Promise<boolean> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tFirmwareUpdateNVM_IsValidCRC16Response\n\t\t>(\n\t\t\tnew FirmwareUpdateNVM_IsValidCRC16Request(),\n\t\t);\n\t\treturn ret.isValid;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Returns information of the controller's external NVM\n\t */\n\tpublic async getNVMId(): Promise<NVMId> {\n\t\tconst ret = await this.driver.sendMessage<GetNVMIdResponse>(\n\t\t\tnew GetNVMIdRequest(),\n\t\t);\n\t\treturn pick(ret, [\"nvmManufacturerId\", \"memoryType\", \"memorySize\"]);\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Reads a byte from the external NVM at the given offset\n\t */\n\tpublic async externalNVMReadByte(offset: number): Promise<number> {\n\t\tconst ret = await this.driver.sendMessage<ExtNVMReadLongByteResponse>(\n\t\t\tnew ExtNVMReadLongByteRequest({ offset }),\n\t\t);\n\t\treturn ret.byte;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Writes a byte to the external NVM at the given offset\n\t * **WARNING:** This function can write in the full NVM address space and is not offset to start at the application area.\n\t * Take care not to accidentally overwrite the protocol NVM area!\n\t *\n\t * @returns `true` when writing succeeded, `false` otherwise\n\t */\n\tpublic async externalNVMWriteByte(\n\t\toffset: number,\n\t\tdata: number,\n\t): Promise<boolean> {\n\t\tconst ret = await this.driver.sendMessage<ExtNVMWriteLongByteResponse>(\n\t\t\tnew ExtNVMWriteLongByteRequest({ offset, byte: data }),\n\t\t);\n\t\treturn ret.success;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Reads a buffer from the external NVM at the given offset\n\t */\n\tpublic async externalNVMReadBuffer(\n\t\toffset: number,\n\t\tlength: number,\n\t): Promise<Uint8Array> {\n\t\tconst ret = await this.driver.sendMessage<ExtNVMReadLongBufferResponse>(\n\t\t\tnew ExtNVMReadLongBufferRequest({\n\t\t\t\toffset,\n\t\t\t\tlength,\n\t\t\t}),\n\t\t);\n\t\treturn ret.buffer;\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Reads a buffer from the external NVM at the given offset\n\t *\n\t * **Note:** Prefer {@link externalNVMReadBufferExt} if supported, as that command supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMReadBuffer700(\n\t\toffset: number,\n\t\tlength: number,\n\t): Promise<{ buffer: Uint8Array; endOfFile: boolean }> {\n\t\tconst ret = await this.driver.sendMessage<NVMOperationsResponse>(\n\t\t\tnew NVMOperationsReadRequest({\n\t\t\t\toffset,\n\t\t\t\tlength,\n\t\t\t}),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tlet message = \"Could not read from the external NVM\";\n\t\t\tif (ret.status === NVMOperationStatus.Error_OperationInterference) {\n\t\t\t\tmessage += \": interference between read and write operation.\";\n\t\t\t} else if (\n\t\t\t\tret.status === NVMOperationStatus.Error_OperationMismatch\n\t\t\t) {\n\t\t\t\tmessage += \": wrong operation requested.\";\n\t\t\t}\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tbuffer: ret.buffer,\n\t\t\tendOfFile: ret.status === NVMOperationStatus.EndOfFile,\n\t\t};\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Reads a buffer from the external NVM at the given offset\n\t *\n\t * **Note:** If supported, this command should be preferred over {@link externalNVMReadBuffer700} as it supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMReadBufferExt(\n\t\toffset: number,\n\t\tlength: number,\n\t): Promise<{ buffer: Uint8Array; endOfFile: boolean }> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tExtendedNVMOperationsResponse\n\t\t>(\n\t\t\tnew ExtendedNVMOperationsReadRequest({\n\t\t\t\toffset,\n\t\t\t\tlength,\n\t\t\t}),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tlet message = \"Could not read from the external NVM\";\n\t\t\tif (\n\t\t\t\tret.status\n\t\t\t\t\t=== ExtendedNVMOperationStatus.Error_OperationInterference\n\t\t\t) {\n\t\t\t\tmessage += \": interference between read and write operation.\";\n\t\t\t} else if (\n\t\t\t\tret.status\n\t\t\t\t\t=== ExtendedNVMOperationStatus.Error_OperationMismatch\n\t\t\t) {\n\t\t\t\tmessage += \": wrong operation requested.\";\n\t\t\t}\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tbuffer: ret.bufferOrBitmask,\n\t\t\tendOfFile: ret.status === ExtendedNVMOperationStatus.EndOfFile,\n\t\t};\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Writes a buffer to the external NVM at the given offset\n\t * **WARNING:** This function can write in the full NVM address space and is not offset to start at the application area.\n\t * Take care not to accidentally overwrite the protocol NVM area!\n\t *\n\t * @returns `true` when writing succeeded, `false` otherwise\n\t */\n\tpublic async externalNVMWriteBuffer(\n\t\toffset: number,\n\t\tbuffer: Uint8Array,\n\t): Promise<boolean> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tExtNVMWriteLongBufferResponse\n\t\t>(\n\t\t\tnew ExtNVMWriteLongBufferRequest({\n\t\t\t\toffset,\n\t\t\t\tbuffer,\n\t\t\t}),\n\t\t);\n\t\treturn ret.success;\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Writes a buffer to the external NVM at the given offset\n\t *\n\t * **Note:** Prefer {@link externalNVMWriteBufferExt} if supported, as that command supports larger NVMs than 64 KiB.\n\t *\n\t * **WARNING:** This function can write in the full NVM address space and is not offset to start at the application area.\n\t * Take care not to accidentally overwrite the protocol NVM area!\n\t */\n\tpublic async externalNVMWriteBuffer700(\n\t\toffset: number,\n\t\tbuffer: Uint8Array,\n\t): Promise<{ endOfFile: boolean }> {\n\t\tconst ret = await this.driver.sendMessage<NVMOperationsResponse>(\n\t\t\tnew NVMOperationsWriteRequest({\n\t\t\t\toffset,\n\t\t\t\tbuffer,\n\t\t\t}),\n\t\t);\n\n\t\tif (!ret.isOK()) {\n\t\t\tlet message = \"Could not write to the external NVM\";\n\t\t\tif (ret.status === NVMOperationStatus.Error_OperationInterference) {\n\t\t\t\tmessage += \": interference between read and write operation.\";\n\t\t\t} else if (\n\t\t\t\tret.status === NVMOperationStatus.Error_OperationMismatch\n\t\t\t) {\n\t\t\t\tmessage += \": wrong operation requested.\";\n\t\t\t}\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tendOfFile: ret.status === NVMOperationStatus.EndOfFile,\n\t\t};\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Writes a buffer to the external NVM at the given offset\n\t *\n\t * **Note:** If supported, this command should be preferred over {@link externalNVMWriteBuffer700} as it supports larger NVMs than 64 KiB.\n\t *\n\t * **WARNING:** This function can write in the full NVM address space and is not offset to start at the application area.\n\t * Take care not to accidentally overwrite the protocol NVM area!\n\t */\n\tpublic async externalNVMWriteBufferExt(\n\t\toffset: number,\n\t\tbuffer: Uint8Array,\n\t): Promise<{ endOfFile: boolean }> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tExtendedNVMOperationsResponse\n\t\t>(\n\t\t\tnew ExtendedNVMOperationsWriteRequest({\n\t\t\t\toffset,\n\t\t\t\tbuffer,\n\t\t\t}),\n\t\t);\n\n\t\tif (!ret.isOK()) {\n\t\t\tlet message = \"Could not write to the external NVM\";\n\t\t\tif (\n\t\t\t\tret.status\n\t\t\t\t\t=== ExtendedNVMOperationStatus.Error_OperationInterference\n\t\t\t) {\n\t\t\t\tmessage += \": interference between read and write operation.\";\n\t\t\t} else if (\n\t\t\t\tret.status\n\t\t\t\t\t=== ExtendedNVMOperationStatus.Error_OperationMismatch\n\t\t\t) {\n\t\t\t\tmessage += \": wrong operation requested.\";\n\t\t\t} else if (\n\t\t\t\tret.status\n\t\t\t\t\t=== ExtendedNVMOperationStatus.Error_SubCommandNotSupported\n\t\t\t) {\n\t\t\t\tmessage += \": sub-command not supported.\";\n\t\t\t}\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tendOfFile: ret.status === ExtendedNVMOperationStatus.EndOfFile,\n\t\t};\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Opens the controller's external NVM for reading/writing and returns the NVM size\n\t *\n\t * **Note:** Prefer {@link externalNVMOpenExt} if supported, as that command supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMOpen(): Promise<number> {\n\t\tconst ret = await this.driver.sendMessage<NVMOperationsResponse>(\n\t\t\tnew NVMOperationsOpenRequest(),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Failed to open the external NVM\",\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\t\treturn ret.offsetOrSize;\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Opens the controller's external NVM for reading/writing and returns the NVM size and supported operations.\n\t *\n\t * **Note:** If supported, this command should be preferred over {@link externalNVMOpen} as it supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMOpenExt(): Promise<{\n\t\tsize: number;\n\t\tsupportedOperations: ExtendedNVMOperationsCommand[];\n\t}> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tExtendedNVMOperationsResponse\n\t\t>(\n\t\t\tnew ExtendedNVMOperationsOpenRequest(),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Failed to open the external NVM\",\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\t\tconst size = ret.offsetOrSize;\n\t\tconst supportedOperations = parseBitMask(\n\t\t\tret.bufferOrBitmask,\n\t\t\tExtendedNVMOperationsCommand.Open,\n\t\t);\n\t\treturn {\n\t\t\tsize,\n\t\t\tsupportedOperations,\n\t\t};\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Closes the controller's external NVM\n\t *\n\t * **Note:** Prefer {@link externalNVMCloseExt} if supported, as that command supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMClose(): Promise<void> {\n\t\tconst ret = await this.driver.sendMessage<NVMOperationsResponse>(\n\t\t\tnew NVMOperationsCloseRequest(),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Failed to close the external NVM\",\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Closes the controller's external NVM\n\t *\n\t * **Note:** If supported, this command should be preferred over {@link externalNVMClose} as it supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMCloseExt(): Promise<void> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tExtendedNVMOperationsResponse\n\t\t>(\n\t\t\tnew ExtendedNVMOperationsCloseRequest(),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Failed to close the external NVM\",\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a backup of the NVM and returns the raw data as a Buffer. The Z-Wave radio is turned off/on automatically.\n\t * @param onProgress Can be used to monitor the progress of the operation, which may take several seconds up to a few minutes depending on the NVM size\n\t * @returns The raw NVM buffer\n\t */\n\tpublic async backupNVMRaw(\n\t\tonProgress?: (bytesRead: number, total: number) => void,\n\t): Promise<Uint8Array> {\n\t\tthis.driver.controllerLog.print(\"Backing up NVM...\");\n\n\t\t// Turn Z-Wave radio off to avoid having the protocol write to the NVM while dumping it\n\t\tif (!(await this.toggleRF(false))) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Could not turn off the Z-Wave radio before creating NVM backup!\",\n\t\t\t\tZWaveErrorCodes.Controller_ResponseNOK,\n\t\t\t);\n\t\t}\n\n\t\t// Disable watchdog to prevent resets during NVM access\n\t\tawait this.stopWatchdog();\n\n\t\tlet ret: Uint8Array;\n\t\ttry {\n\t\t\tif (this.sdkVersionGte(\"7.0\")) {\n\t\t\t\tret = await this.backupNVMRaw700(onProgress);\n\t\t\t\t// All 7.xx versions so far seem to have a bug where the NVM is not properly closed after reading\n\t\t\t\t// resulting in extremely strange controller behavior after a backup. To work around this, restart the stick if possible\n\t\t\t\tawait this.driver.trySoftReset();\n\t\t\t\t// Soft-resetting will enable the watchdog again\n\t\t\t} else {\n\t\t\t\tret = await this.backupNVMRaw500(onProgress);\n\t\t\t}\n\t\t\tthis.driver.controllerLog.print(\"NVM backup completed\");\n\t\t} finally {\n\t\t\t// Whatever happens, turn Z-Wave radio back on\n\t\t\tawait this.toggleRF(true);\n\t\t}\n\n\t\t// TODO: You can also get away with eliding all the 0xff pages. The NVR also holds the page size of the NVM (NVMP),\n\t\t// so you can figure out which pages you don't have to save or restore. If you do this, you need to make sure to issue a\n\t\t// \"factory reset\" before restoring the NVM - that'll blank out the NVM to 0xffs before initializing it.\n\n\t\treturn ret;\n\t}\n\n\tprivate async backupNVMRaw500(\n\t\tonProgress?: (bytesRead: number, total: number) => void,\n\t): Promise<Uint8Array> {\n\t\tconst size = nvmSizeToBufferSize((await this.getNVMId()).memorySize);\n\t\tif (!size) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Unknown NVM size - cannot backup!\",\n\t\t\t\tZWaveErrorCodes.Controller_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\tconst ret = new Bytes(size);\n\t\tlet offset = 0;\n\t\t// Try reading the maximum size at first, the Serial API should return chunks in a size it supports\n\t\t// For some reason, there is no documentation and no official command for this\n\t\tlet chunkSize: number = Math.min(0xffff, ret.length);\n\t\twhile (offset < ret.length) {\n\t\t\tconst chunk = await this.externalNVMReadBuffer(\n\t\t\t\toffset,\n\t\t\t\tMath.min(chunkSize, ret.length - offset),\n\t\t\t);\n\t\t\tif (chunk.length === 0) {\n\t\t\t\t// Some SDK versions return an empty buffer when trying to read a buffer that is too long\n\t\t\t\t// Fallback to a sane (but maybe slow) size\n\t\t\t\tchunkSize = 48;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tret.set(chunk, offset);\n\t\t\toffset += chunk.length;\n\t\t\tif (chunkSize > chunk.length) chunkSize = chunk.length;\n\n\t\t\t// Report progress for listeners\n\t\t\tif (onProgress) setImmediate(() => onProgress(offset, size));\n\t\t}\n\t\treturn ret;\n\t}\n\n\tprivate async backupNVMRaw700(\n\t\tonProgress?: (bytesRead: number, total: number) => void,\n\t): Promise<Uint8Array> {\n\t\tlet open: () => Promise<number>;\n\t\tlet read: (\n\t\t\toffset: number,\n\t\t\tlength: number,\n\t\t) => Promise<{ buffer: Uint8Array; endOfFile: boolean }>;\n\t\tlet close: () => Promise<void>;\n\n\t\tif (\n\t\t\tthis.supportedFunctionTypes?.includes(\n\t\t\t\tFunctionType.ExtendedNVMOperations,\n\t\t\t)\n\t\t) {\n\t\t\topen = async () => {\n\t\t\t\tconst { size } = await this.externalNVMOpenExt();\n\t\t\t\treturn size;\n\t\t\t};\n\t\t\tread = (offset, length) =>\n\t\t\t\tthis.externalNVMReadBufferExt(offset, length);\n\t\t\tclose = () => this.externalNVMCloseExt();\n\t\t} else {\n\t\t\topen = () => this.externalNVMOpen();\n\t\t\tread = (offset, length) =>\n\t\t\t\tthis.externalNVMReadBuffer700(offset, length);\n\t\t\tclose = () => this.externalNVMClose();\n\t\t}\n\n\t\t// Open NVM for reading\n\t\tconst size = await open();\n\n\t\tconst ret = new Bytes(size);\n\t\tlet offset = 0;\n\t\t// Try reading the maximum size at first, the Serial API should return chunks in a size it supports\n\t\t// For some reason, there is no documentation and no official command for this\n\t\tlet chunkSize: number = Math.min(0xff, ret.length);\n\t\ttry {\n\t\t\twhile (offset < ret.length) {\n\t\t\t\tconst { buffer: chunk, endOfFile } = await read(\n\t\t\t\t\toffset,\n\t\t\t\t\tMath.min(chunkSize, ret.length - offset),\n\t\t\t\t);\n\t\t\t\tif (chunkSize === 0xff && chunk.length === 0) {\n\t\t\t\t\t// Some SDK versions return an empty buffer when trying to read a buffer that is too long\n\t\t\t\t\t// Fallback to a sane (but maybe slow) size\n\t\t\t\t\tchunkSize = 48;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tret.set(chunk, offset);\n\t\t\t\toffset += chunk.length;\n\t\t\t\tif (chunkSize > chunk.length) chunkSize = chunk.length;\n\n\t\t\t\t// Report progress for listeners\n\t\t\t\tif (onProgress) setImmediate(() => onProgress(offset, size));\n\n\t\t\t\tif (endOfFile) break;\n\t\t\t}\n\t\t} finally {\n\t\t\t// Whatever happens, close the NVM\n\t\t\tawait close();\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Restores an NVM backup that was created with `backupNVMRaw`. The Z-Wave radio is turned off/on automatically.\n\t * If the given buffer is in a different NVM format, it is converted automatically. If a conversion is required but not supported, the operation will be aborted.\n\t *\n\t * **WARNING:** If both the source and target NVM use an an unsupported format, they will NOT be checked for compatibility!\n\t *\n\t * **WARNING:** A failure during this process may brick your controller. Use at your own risk!\n\t *\n\t * @param nvmData The NVM backup to be restored\n\t * @param convertProgress Can be used to monitor the progress of the NVM conversion, which may take several seconds up to a few minutes depending on the NVM size\n\t * @param restoreProgress Can be used to monitor the progress of the restore operation, which may take several seconds up to a few minutes depending on the NVM size\n\t */\n\tpublic async restoreNVM(\n\t\tnvmData: Uint8Array,\n\t\tconvertProgress?: (bytesRead: number, total: number) => void,\n\t\trestoreProgress?: (bytesWritten: number, total: number) => void,\n\t): Promise<void> {\n\t\t// Turn Z-Wave radio off to avoid having the protocol write to the NVM while dumping it\n\t\tif (!(await this.toggleRF(false))) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Could not turn off the Z-Wave radio before restoring NVM backup!\",\n\t\t\t\tZWaveErrorCodes.Controller_ResponseNOK,\n\t\t\t);\n\t\t}\n\n\t\t// Disable watchdog to prevent resets during NVM access\n\t\tawait this.stopWatchdog();\n\n\t\t// Restoring a potentially incompatible NVM happens in three steps:\n\t\t// 1. the current NVM is read\n\t\t// 2. the given NVM data is converted to match the current format\n\t\t// 3. the converted data is written to the NVM\n\n\t\ttry {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\"Converting NVM to target format...\",\n\t\t\t);\n\t\t\tlet targetNVM: Uint8Array;\n\t\t\tif (this.sdkVersionGte(\"7.0\")) {\n\t\t\t\ttargetNVM = await this.backupNVMRaw700(convertProgress);\n\t\t\t} else {\n\t\t\t\ttargetNVM = await this.backupNVMRaw500(convertProgress);\n\t\t\t}\n\t\t\tconst convertedNVM = await migrateNVM(nvmData, targetNVM);\n\n\t\t\tthis.driver.controllerLog.print(\"Restoring NVM backup...\");\n\t\t\tif (this.sdkVersionGte(\"7.0\")) {\n\t\t\t\tawait this.restoreNVMRaw700(convertedNVM, restoreProgress);\n\t\t\t} else {\n\t\t\t\tawait this.restoreNVMRaw500(convertedNVM, restoreProgress);\n\t\t\t}\n\t\t\tthis.driver.controllerLog.print(\"NVM backup restored\");\n\t\t} finally {\n\t\t\t// Whatever happens, turn Z-Wave radio back on\n\t\t\tawait this.toggleRF(true);\n\t\t}\n\n\t\t// After restoring an NVM backup, the controller's capabilities may have changed.\n\t\t// Also, we could be talking to different nodes than the cache file contains.\n\t\t// Reset all info about all nodes, so they get re-interviewed.\n\t\tthis._nodes.clear();\n\n\t\t// Normally we'd only need to soft reset the stick, but we also need to re-interview the controller and potentially all nodes.\n\t\t// Just forcing a restart of the driver seems easier.\n\n\t\tawait this.driver.softResetAndRestart(\n\t\t\t\"Restarting driver to activate restored NVM backup...\",\n\t\t\t\"Applying the NVM backup requires a driver restart!\",\n\t\t);\n\t}\n\n\t/**\n\t * Restores an NVM backup that was created with `backupNVMRaw`. The Z-Wave radio is turned off/on automatically.\n\t *\n\t * **WARNING:** The given buffer is NOT checked for compatibility with the current stick. To have Z-Wave JS do that, use the {@link restoreNVM} method instead.\n\t *\n\t * **WARNING:** A failure during this process may brick your controller. Use at your own risk!\n\t * @param nvmData The raw NVM backup to be restored\n\t * @param onProgress Can be used to monitor the progress of the operation, which may take several seconds up to a few minutes depending on the NVM size\n\t */\n\tpublic async restoreNVMRaw(\n\t\tnvmData: Uint8Array,\n\t\tonProgress?: (bytesWritten: number, total: number) => void,\n\t): Promise<void> {\n\t\tthis.driver.controllerLog.print(\"Restoring NVM...\");\n\n\t\t// Turn Z-Wave radio off to avoid having the protocol write to the NVM while dumping it\n\t\tif (!(await this.toggleRF(false))) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Could not turn off the Z-Wave radio before restoring NVM backup!\",\n\t\t\t\tZWaveErrorCodes.Controller_ResponseNOK,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tif (this.sdkVersionGte(\"7.0\")) {\n\t\t\t\tawait this.restoreNVMRaw700(nvmData, onProgress);\n\t\t\t} else {\n\t\t\t\tawait this.restoreNVMRaw500(nvmData, onProgress);\n\t\t\t}\n\t\t\tthis.driver.controllerLog.print(\"NVM backup restored\");\n\t\t} finally {\n\t\t\t// Whatever happens, turn Z-Wave radio back on\n\t\t\tawait this.toggleRF(true);\n\t\t}\n\n\t\t// TODO: You can also get away with eliding all the 0xff pages. The NVR also holds the page size of the NVM (NVMP),\n\t\t// so you can figure out which pages you don't have to save or restore. If you do this, you need to make sure to issue a\n\t\t// \"factory reset\" before restoring the NVM - that'll blank out the NVM to 0xffs before initializing it.\n\n\t\t// After a restored NVM backup, the controller's capabilities may have changed.\n\t\t// Normally we'd only need to soft reset the stick, but we also need to re-interview the controller and potentially all nodes.\n\t\t// Just forcing a restart of the driver seems easier.\n\n\t\t// if (this.driver.options.enableSoftReset) {\n\t\t// \tthis.driver.controllerLog.print(\n\t\t// \t\t\"Activating restored NVM backup...\",\n\t\t// \t);\n\t\t// \tawait this.driver.softReset();\n\t\t// } else {\n\t\t// \tthis.driver.controllerLog.print(\n\t\t// \t\t\"Soft reset not enabled, cannot automatically activate restored NVM backup!\",\n\t\t// \t\t\"warn\",\n\t\t// \t);\n\t\t// }\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t\"Restarting driver to activate restored NVM backup...\",\n\t\t);\n\n\t\tthis.driver.emit(\n\t\t\t\"error\",\n\t\t\tnew ZWaveError(\n\t\t\t\t\"Activating the NVM backup requires a driver restart!\",\n\t\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t\t),\n\t\t);\n\t\tawait this.driver.destroy();\n\t}\n\n\tprivate async restoreNVMRaw500(\n\t\tnvmData: Uint8Array,\n\t\tonProgress?: (bytesWritten: number, total: number) => void,\n\t): Promise<void> {\n\t\tconst size = nvmSizeToBufferSize((await this.getNVMId()).memorySize);\n\t\tif (!size) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Unknown NVM size - cannot restore!\",\n\t\t\t\tZWaveErrorCodes.Controller_NotSupported,\n\t\t\t);\n\t\t} else if (size !== nvmData.length) {\n\t\t\t// This might be a converted NVM buffer which contains only the first relevant part.\n\t\t\t// The first two bytes must point to the last byte in the buffer then\n\t\t\tconst actualSize = 1 + Bytes.view(nvmData).readUInt16BE(0);\n\t\t\tif (actualSize !== nvmData.length) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The given data does not match the NVM size - cannot restore!\",\n\t\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Now we only need to figure out which part of the NVM needs to be overwritten when restoring\n\t\t\tconst firstTwoNVMBytes = Bytes.view(\n\t\t\t\tawait this.externalNVMReadBuffer(0, 2),\n\t\t\t);\n\t\t\tconst oldSize = 1 + firstTwoNVMBytes.readUInt16BE(0);\n\t\t\tif (oldSize > actualSize) {\n\t\t\t\t// Pad the rest with 0xff\n\t\t\t\tnvmData = Bytes.concat([\n\t\t\t\t\tnvmData,\n\t\t\t\t\tBytes.alloc(oldSize - actualSize, 0xff),\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\n\t\t// Figure out the maximum chunk size the Serial API supports\n\t\t// For some reason, there is no documentation and no official command for this\n\t\t// The write requests need 5 bytes more than the read response, so subtract 5 from the returned length\n\t\tconst chunkSize = (await this.externalNVMReadBuffer(0, 0xffff)).length\n\t\t\t- 5;\n\n\t\tfor (let offset = 0; offset < nvmData.length; offset += chunkSize) {\n\t\t\tawait this.externalNVMWriteBuffer(\n\t\t\t\toffset,\n\t\t\t\tnvmData.subarray(offset, offset + chunkSize),\n\t\t\t);\n\t\t\t// Report progress for listeners\n\t\t\tif (onProgress) {\n\t\t\t\tsetImmediate(() => onProgress(offset, nvmData.length));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async restoreNVMRaw700(\n\t\tnvmData: Uint8Array,\n\t\tonProgress?: (bytesWritten: number, total: number) => void,\n\t): Promise<void> {\n\t\tlet open: () => Promise<number>;\n\t\tlet read: (\n\t\t\toffset: number,\n\t\t\tlength: number,\n\t\t) => Promise<{ buffer: Uint8Array; endOfFile: boolean }>;\n\t\tlet write: (\n\t\t\toffset: number,\n\t\t\tbuffer: Uint8Array,\n\t\t) => Promise<{ endOfFile: boolean }>;\n\t\tlet close: () => Promise<void>;\n\n\t\tif (\n\t\t\tthis.supportedFunctionTypes?.includes(\n\t\t\t\tFunctionType.ExtendedNVMOperations,\n\t\t\t)\n\t\t) {\n\t\t\topen = async () => {\n\t\t\t\tconst { size } = await this.externalNVMOpenExt();\n\t\t\t\treturn size;\n\t\t\t};\n\t\t\tread = (offset, length) =>\n\t\t\t\tthis.externalNVMReadBufferExt(offset, length);\n\t\t\twrite = (offset, buffer) =>\n\t\t\t\tthis.externalNVMWriteBufferExt(offset, buffer);\n\t\t\tclose = () => this.externalNVMCloseExt();\n\t\t} else {\n\t\t\topen = () => this.externalNVMOpen();\n\t\t\tread = (offset, length) =>\n\t\t\t\tthis.externalNVMReadBuffer700(offset, length);\n\t\t\twrite = (offset, buffer) =>\n\t\t\t\tthis.externalNVMWriteBuffer700(offset, buffer);\n\t\t\tclose = () => this.externalNVMClose();\n\t\t}\n\n\t\t// Open NVM for reading\n\t\tconst size = await open();\n\n\t\tif (size !== nvmData.length) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The given data does not match the NVM size - cannot restore!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\t// Figure out the maximum chunk size the Serial API supports\n\t\t// For some reason, there is no documentation and no official command for this\n\t\t// The write requests have the same size as the read response - if this yields no\n\t\t// data, default to a sane (but maybe slow) size\n\t\tconst chunkSize = (await read(0, 0xff)).buffer.length || 48;\n\n\t\t// Close NVM and re-open again for writing\n\t\tawait close();\n\t\tawait open();\n\n\t\tfor (let offset = 0; offset < nvmData.length; offset += chunkSize) {\n\t\t\tconst { endOfFile } = await write(\n\t\t\t\toffset,\n\t\t\t\tnvmData.subarray(offset, offset + chunkSize),\n\t\t\t);\n\n\t\t\t// Report progress for listeners\n\t\t\tif (onProgress) setImmediate(() => onProgress(offset, size));\n\n\t\t\tif (endOfFile) break;\n\t\t}\n\t\t// Close NVM\n\t\tawait close();\n\t}\n\n\t/**\n\t * Request the most recent background RSSI levels detected by the controller.\n\t *\n\t * **Note:** This only returns useful values if something was transmitted recently.\n\t */\n\tpublic async getBackgroundRSSI(): Promise<{\n\t\trssiChannel0: RSSI;\n\t\trssiChannel1: RSSI;\n\t\trssiChannel2?: RSSI;\n\t\trssiChannel3?: RSSI;\n\t}> {\n\t\tconst ret = await this.driver.sendMessage<GetBackgroundRSSIResponse>(\n\t\t\tnew GetBackgroundRSSIRequest(),\n\t\t);\n\t\tconst rssi = pick(ret, [\n\t\t\t\"rssiChannel0\",\n\t\t\t\"rssiChannel1\",\n\t\t\t\"rssiChannel2\",\n\t\t\t\"rssiChannel3\",\n\t\t]);\n\n\t\tthis.updateStatistics((current) => {\n\t\t\tconst updated = { ...current };\n\t\t\tupdated.backgroundRSSI = {} as any;\n\n\t\t\t// Average all channels, defaulting to the current measurement\n\t\t\tupdated.backgroundRSSI!.channel0 = {\n\t\t\t\tcurrent: rssi.rssiChannel0,\n\t\t\t\taverage: averageRSSI(\n\t\t\t\t\tcurrent.backgroundRSSI?.channel0.average,\n\t\t\t\t\trssi.rssiChannel0,\n\t\t\t\t\t0.9,\n\t\t\t\t),\n\t\t\t};\n\t\t\tupdated.backgroundRSSI!.channel1 = {\n\t\t\t\tcurrent: rssi.rssiChannel1,\n\t\t\t\taverage: averageRSSI(\n\t\t\t\t\tcurrent.backgroundRSSI?.channel1.average,\n\t\t\t\t\trssi.rssiChannel1,\n\t\t\t\t\t0.9,\n\t\t\t\t),\n\t\t\t};\n\n\t\t\tif (rssi.rssiChannel2 != undefined) {\n\t\t\t\tupdated.backgroundRSSI!.channel2 = {\n\t\t\t\t\tcurrent: rssi.rssiChannel2,\n\t\t\t\t\taverage: averageRSSI(\n\t\t\t\t\t\tcurrent.backgroundRSSI?.channel2?.average,\n\t\t\t\t\t\trssi.rssiChannel2,\n\t\t\t\t\t\t0.9,\n\t\t\t\t\t),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (rssi.rssiChannel3 != undefined) {\n\t\t\t\tupdated.backgroundRSSI!.channel3 = {\n\t\t\t\t\tcurrent: rssi.rssiChannel3,\n\t\t\t\t\taverage: averageRSSI(\n\t\t\t\t\t\tcurrent.backgroundRSSI?.channel3?.average,\n\t\t\t\t\t\trssi.rssiChannel3,\n\t\t\t\t\t\t0.9,\n\t\t\t\t\t),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tupdated.backgroundRSSI!.timestamp = Date.now();\n\n\t\t\treturn updated;\n\t\t});\n\n\t\treturn rssi;\n\t}\n\n\t/**\n\t * Returns whether an OTA firmware update is in progress for any node.\n\t */\n\tpublic isAnyOTAFirmwareUpdateInProgress(): boolean {\n\t\tfor (const node of this._nodes.values()) {\n\t\t\tif (!node.isControllerNode && node.isFirmwareUpdateInProgress()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Retrieves the available firmware updates for the given node from the Z-Wave JS firmware update service.\n\t *\n\t * **Note:** Sleeping nodes need to be woken up for this to work. This method will throw when called for a sleeping node\n\t * which did not wake up within a minute.\n\t *\n\t * **Note:** This requires an API key to be set in the driver options, or passed .\n\t */\n\tpublic async getAvailableFirmwareUpdates(\n\t\tnodeId: number,\n\t\toptions?: GetFirmwareUpdatesOptions,\n\t): Promise<FirmwareUpdateInfo[]> {\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\n\t\tconst { manufacturerId, productType, productId, firmwareVersion } =\n\t\t\tnode;\n\t\t// Be really sure that we have all the information we need\n\t\tif (\n\t\t\ttypeof manufacturerId !== \"number\"\n\t\t\t|| typeof productType !== \"number\"\n\t\t\t|| typeof productId !== \"number\"\n\t\t\t|| typeof firmwareVersion !== \"string\"\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot check for firmware updates for node ${nodeId}: fingerprint or firmware version is unknown!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_MissingInformation,\n\t\t\t);\n\t\t}\n\n\t\t// Now invoke the service\n\t\ttry {\n\t\t\treturn await getAvailableFirmwareUpdates(\n\t\t\t\t{\n\t\t\t\t\tmanufacturerId,\n\t\t\t\t\tproductType,\n\t\t\t\t\tproductId,\n\t\t\t\t\tfirmwareVersion,\n\t\t\t\t\trfRegion: this.rfRegion ?? options?.rfRegion,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tuserAgent: this.driver.getUserAgentStringWithComponents(\n\t\t\t\t\t\toptions?.additionalUserAgentComponents,\n\t\t\t\t\t),\n\t\t\t\t\tapiKey: options?.apiKey\n\t\t\t\t\t\t?? this.driver.options.apiKeys?.firmwareUpdateService,\n\t\t\t\t\tincludePrereleases: options?.includePrereleases,\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (e: any) {\n\t\t\tlet message =\n\t\t\t\t`Cannot check for firmware updates for node ${nodeId}: `;\n\t\t\tif (e.response) {\n\t\t\t\tif (isObject(e.response.data)) {\n\t\t\t\t\tif (typeof e.response.data.error === \"string\") {\n\t\t\t\t\t\tmessage += `${e.response.data.error} `;\n\t\t\t\t\t} else if (typeof e.response.data.message === \"string\") {\n\t\t\t\t\t\tmessage += `${e.response.data.message} `;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmessage += `[${e.response.status} ${e.response.statusText}]`;\n\t\t\t} else if (typeof e.message === \"string\") {\n\t\t\t\tmessage += e.message;\n\t\t\t} else {\n\t\t\t\tmessage += `Failed to download update information!`;\n\t\t\t}\n\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_RequestError,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Ensures that the device ID used to request a firmware update matches the device the firmware update is for */\n\tprivate async ensureFirmwareDeviceIdMatches(\n\t\tnode: ZWaveNode,\n\t\tdeviceId: FirmwareUpdateDeviceID,\n\t): Promise<void> {\n\t\tif (\n\t\t\tdeviceId.rfRegion !== undefined\n\t\t\t&& this.rfRegion !== NOT_KNOWN\n\t\t\t&& deviceId.rfRegion !== this.rfRegion\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot update firmware for node ${node.id}: The firmware update is for a different region!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_DeviceMismatch,\n\t\t\t);\n\t\t}\n\n\t\tconst manufacturerResponse = await node.commandClasses[\n\t\t\t\"Manufacturer Specific\"\n\t\t].get();\n\n\t\tif (!manufacturerResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot check for firmware updates for node ${node.id}: Failed to query fingerprint from the node!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_MissingInformation,\n\t\t\t);\n\t\t}\n\n\t\t// Query the version using both possible commands to ensure we have the full version\n\t\tconst versionResponse = await node.commandClasses.Version.get();\n\t\tif (!versionResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot check for firmware updates for node ${node.id}: Failed to query firmware version from the node!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_MissingInformation,\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tnode.commandClasses.Version.supportsCommand(\n\t\t\t\tVersionCommand.ZWaveSoftwareGet,\n\t\t\t)\n\t\t) {\n\t\t\tconst softwareResponse = await node.commandClasses.Version\n\t\t\t\t.getZWaveSoftware();\n\t\t\tif (!softwareResponse) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`Cannot check for firmware updates for node ${node.id}: Failed to query firmware version from the node!`,\n\t\t\t\t\tZWaveErrorCodes.FWUpdateService_MissingInformation,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst { manufacturerId, productType, productId, firmwareVersion } =\n\t\t\tnode;\n\n\t\tif (\n\t\t\tmanufacturerId !== node.manufacturerId\n\t\t\t|| productType !== node.productType\n\t\t\t|| productId !== node.productId\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot update firmware for node ${node.id}: The firmware update is for a different device!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_DeviceMismatch,\n\t\t\t);\n\t\t} else if (firmwareVersion !== deviceId.firmwareVersion) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot update firmware for node ${node.id}: The update is for a different original firmware version!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_DeviceMismatch,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Downloads the desired firmware update(s) from the Z-Wave JS firmware update service and updates the firmware of the given node.\n\t * @param updateInfo The desired entry from the updates array that was returned by {@link getAvailableFirmwareUpdates}.\n\t * Before applying the update, Z-Wave JS will check whether the device IDs, firmware version and region match.\n\t *\n\t * The return value indicates whether the update was successful.\n\t * **WARNING:** This method will throw instead of returning `false` if invalid arguments are passed or downloading files or starting an update fails.\n\t */\n\tpublic async firmwareUpdateOTA(\n\t\tnodeId: number,\n\t\tupdateInfo: FirmwareUpdateInfo,\n\t\toptions?: FirmwareUpdateOptions,\n\t): Promise<FirmwareUpdateResult> {\n\t\t// Don't let two firmware updates happen in parallel\n\t\tif (this.isAnyOTAFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to start the update: A firmware update is already in progress on this network!`;\n\t\t\tthis.driver.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.FirmwareUpdateCC_NetworkBusy,\n\t\t\t);\n\t\t}\n\t\t// Don't allow updating firmware when the controller is currently updating its own firmware\n\t\tif (this.isFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to start the update: The controller is currently being updated!`;\n\t\t\tthis.driver.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.FirmwareUpdateCC_NetworkBusy,\n\t\t\t);\n\t\t}\n\n\t\tconst files = updateInfo.files;\n\t\tconst validateDeviceId = updateInfo.device;\n\n\t\t// We made a breaking change to the method signature. files should never be undefined, unless applications still\n\t\t// use the old signature.\n\t\tif (files?.length === 0) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`At least one update must be provided`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tnodeId,\n\t\t\t`OTA firmware update started, downloading ${files.length} updates...`,\n\t\t);\n\n\t\tconst loglevel = this.driver.getLogConfig().level;\n\n\t\tconst firmwares: Firmware[] = [];\n\t\tfor (let i = 0; i < files.length; i++) {\n\t\t\tconst update = files[i];\n\t\t\tlet logMessage =\n\t\t\t\t`Downloading firmware update ${i} of ${files.length}...`;\n\t\t\tif (loglevel === \"silly\") {\n\t\t\t\tlogMessage += `\n URL: ${update.url}\n integrity: ${update.integrity}`;\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(nodeId, logMessage);\n\n\t\t\ttry {\n\t\t\t\tconst firmware = await downloadFirmwareUpdate(update);\n\t\t\t\tfirmwares.push(firmware);\n\t\t\t} catch (e: any) {\n\t\t\t\tlet message =\n\t\t\t\t\t`Downloading the firmware update for node ${nodeId} failed:\\n`;\n\t\t\t\tif (isZWaveError(e)) {\n\t\t\t\t\t// Pass \"real\" Z-Wave errors through\n\t\t\t\t\tthrow new ZWaveError(message + e.message, e.code);\n\t\t\t\t} else if (e.response) {\n\t\t\t\t\t// And construct a better error message for HTTP errors\n\t\t\t\t\tif (\n\t\t\t\t\t\tisObject(e.response.data)\n\t\t\t\t\t\t&& typeof e.response.data.message === \"string\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tmessage += `${e.response.data.message} `;\n\t\t\t\t\t}\n\t\t\t\t\tmessage +=\n\t\t\t\t\t\t`[${e.response.status} ${e.response.statusText}]`;\n\t\t\t\t} else if (typeof e.message === \"string\") {\n\t\t\t\t\tmessage += e.message;\n\t\t\t\t} else {\n\t\t\t\t\tmessage += `Failed to download firmware update!`;\n\t\t\t\t}\n\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\tmessage,\n\t\t\t\t\tZWaveErrorCodes.FWUpdateService_RequestError,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Make sure we're not applying the update to the wrong device\n\t\tif (validateDeviceId) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`All updates downloaded, validating device IDs...`,\n\t\t\t);\n\n\t\t\tawait this.ensureFirmwareDeviceIdMatches(node, validateDeviceId);\n\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Device IDs match, installing firmware updates...`,\n\t\t\t);\n\t\t} else {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`All updates downloaded, installing...`,\n\t\t\t);\n\t\t}\n\n\t\treturn node.updateFirmware(firmwares, options);\n\t}\n\n\tprivate _firmwareUpdateInProgress: boolean = false;\n\n\t/**\n\t * Returns whether a firmware update is in progress for the controller.\n\t */\n\tpublic isFirmwareUpdateInProgress(): boolean {\n\t\treturn this._firmwareUpdateInProgress;\n\t}\n\n\t/**\n\t * Updates the firmware of the controller using the given firmware file.\n\t *\n\t * The return value indicates whether the update was successful.\n\t * **WARNING:** After a successful update, the Z-Wave driver will destroy itself so it can be restarted.\n\t *\n\t * **WARNING:** A failure during this process may put your controller in recovery mode, rendering it unusable until a correct firmware image is uploaded. Use at your own risk!\n\t */\n\tpublic async firmwareUpdateOTW(\n\t\tdata: Uint8Array,\n\t): Promise<ControllerFirmwareUpdateResult> {\n\t\t// Don't let two firmware updates happen in parallel\n\t\tif (this.isAnyOTAFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to start the update: A firmware update is already in progress on this network!`;\n\t\t\tthis.driver.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.OTW_Update_Busy);\n\t\t}\n\t\t// Don't allow updating firmware when the controller is currently updating its own firmware\n\t\tif (this.isFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to start the update: The controller is currently being updated!`;\n\t\t\tthis.driver.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.OTW_Update_Busy);\n\t\t}\n\n\t\tif (this.driver.isInBootloader() || this.sdkVersionGte(\"7.0\")) {\n\t\t\t// If the controller is stuck in bootloader mode, always use the 700 series update method\n\t\t\treturn this.firmwareUpdateOTW700(data);\n\t\t} else if (\n\t\t\tthis.sdkVersionGte(\"6.50.0\")\n\t\t\t&& this.supportedFunctionTypes?.includes(\n\t\t\t\tFunctionType.FirmwareUpdateNVM,\n\t\t\t)\n\t\t) {\n\t\t\t// This is 500 series\n\t\t\tconst wasUpdated = await this.firmwareUpdateOTW500(data);\n\t\t\tif (wasUpdated.success) {\n\t\t\t\t// After updating the firmware on 500 series sticks, we MUST soft-reset them\n\t\t\t\tawait this.driver.softResetAndRestart(\n\t\t\t\t\t\"Activating new firmware and restarting driver...\",\n\t\t\t\t\t\"Controller firmware updates require a driver restart!\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn wasUpdated;\n\t\t} else {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Firmware updates are not supported on this controller`,\n\t\t\t\tZWaveErrorCodes.Controller_NotSupported,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async firmwareUpdateOTW500(\n\t\tdata: Uint8Array,\n\t): Promise<ControllerFirmwareUpdateResult> {\n\t\tthis._firmwareUpdateInProgress = true;\n\t\tlet turnedRadioOff = false;\n\t\ttry {\n\t\t\tthis.driver.controllerLog.print(\"Beginning firmware update\");\n\n\t\t\tconst canUpdate = await this.firmwareUpdateNVMInit();\n\t\t\tif (!canUpdate) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"OTW update failed: This controller does not support firmware updates\",\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\n\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: ControllerFirmwareUpdateStatus.Error_NotSupported,\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\t// Avoid interruption by incoming messages\n\t\t\tawait this.toggleRF(false);\n\t\t\tturnedRadioOff = true;\n\n\t\t\t// Upload the firmware data\n\t\t\tconst BLOCK_SIZE = 64;\n\t\t\tconst numFragments = Math.ceil(data.length / BLOCK_SIZE);\n\t\t\tfor (let fragment = 0; fragment < numFragments; fragment++) {\n\t\t\t\tconst fragmentData = data.subarray(\n\t\t\t\t\tfragment * BLOCK_SIZE,\n\t\t\t\t\t(fragment + 1) * BLOCK_SIZE,\n\t\t\t\t);\n\t\t\t\tawait this.firmwareUpdateNVMWrite(\n\t\t\t\t\tfragment * BLOCK_SIZE,\n\t\t\t\t\tfragmentData,\n\t\t\t\t);\n\n\t\t\t\t// This progress is technically too low, but we can keep 100% for after CRC checking this way\n\t\t\t\tconst progress: ControllerFirmwareUpdateProgress = {\n\t\t\t\t\tsentFragments: fragment,\n\t\t\t\t\ttotalFragments: numFragments,\n\t\t\t\t\tprogress: roundTo((fragment / numFragments) * 100, 2),\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update progress\", progress);\n\t\t\t}\n\n\t\t\t// Check if a valid image was written\n\t\t\tconst isValidCRC = await this.firmwareUpdateNVMIsValidCRC16();\n\t\t\tif (!isValidCRC) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"OTW update failed: The firmware image is invalid\",\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\n\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: ControllerFirmwareUpdateStatus.Error_Aborted,\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tthis.emit(\"firmware update progress\", {\n\t\t\t\tsentFragments: numFragments,\n\t\t\t\ttotalFragments: numFragments,\n\t\t\t\tprogress: 100,\n\t\t\t});\n\n\t\t\t// Enable the image\n\t\t\tawait this.firmwareUpdateNVMSetNewImage();\n\n\t\t\tthis.driver.controllerLog.print(\"Firmware update succeeded\");\n\n\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\tsuccess: true,\n\t\t\t\tstatus: ControllerFirmwareUpdateStatus.OK,\n\t\t\t};\n\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tthis._firmwareUpdateInProgress = false;\n\t\t\tif (turnedRadioOff) await this.toggleRF(true);\n\t\t}\n\t}\n\n\tprivate async firmwareUpdateOTW700(\n\t\tdata: Uint8Array,\n\t): Promise<ControllerFirmwareUpdateResult> {\n\t\tthis._firmwareUpdateInProgress = true;\n\t\tlet destroy = false;\n\n\t\ttry {\n\t\t\tif (!this.driver.isInBootloader()) {\n\t\t\t\tawait this.driver.enterBootloader();\n\t\t\t}\n\n\t\t\t// Start the update process\n\t\t\tthis.driver.controllerLog.print(\"Beginning firmware upload\");\n\t\t\tawait this.driver.bootloader.beginUpload();\n\n\t\t\t// Wait for the bootloader to accept fragments\n\t\t\ttry {\n\t\t\t\tawait this.driver.waitForBootloaderChunk(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\tc.type === BootloaderChunkType.Message\n\t\t\t\t\t\t&& c.message === \"begin upload\",\n\t\t\t\t\t5000,\n\t\t\t\t);\n\t\t\t\tawait this.driver.waitForBootloaderChunk(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\tc.type === BootloaderChunkType.FlowControl\n\t\t\t\t\t\t&& c.command === XModemMessageHeaders.C,\n\t\t\t\t\t1000,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"OTW update failed: Expected response not received from the bootloader\",\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: ControllerFirmwareUpdateStatus.Error_Timeout,\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst BLOCK_SIZE = 128;\n\t\t\tif (data.length % BLOCK_SIZE !== 0) {\n\t\t\t\t// Pad the data to a multiple of BLOCK_SIZE\n\t\t\t\tdata = Bytes.concat([\n\t\t\t\t\tdata,\n\t\t\t\t\tnew Bytes(BLOCK_SIZE - (data.length % BLOCK_SIZE)).fill(\n\t\t\t\t\t\t0xff,\n\t\t\t\t\t),\n\t\t\t\t]);\n\t\t\t}\n\t\t\tconst numFragments = Math.ceil(data.length / BLOCK_SIZE);\n\n\t\t\tlet aborted = false;\n\n\t\t\ttransfer: for (\n\t\t\t\tlet fragment = 1;\n\t\t\t\tfragment <= numFragments;\n\t\t\t\tfragment++\n\t\t\t) {\n\t\t\t\tconst fragmentData = data.subarray(\n\t\t\t\t\t(fragment - 1) * BLOCK_SIZE,\n\t\t\t\t\tfragment * BLOCK_SIZE,\n\t\t\t\t);\n\n\t\t\t\tretry: for (let retry = 0; retry < 3; retry++) {\n\t\t\t\t\tawait this.driver.bootloader.uploadFragment(\n\t\t\t\t\t\tfragment,\n\t\t\t\t\t\tfragmentData,\n\t\t\t\t\t);\n\t\t\t\t\tlet result: BootloaderChunk & {\n\t\t\t\t\t\ttype: BootloaderChunkType.FlowControl;\n\t\t\t\t\t};\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = await this.driver.waitForBootloaderChunk(\n\t\t\t\t\t\t\t(c) => c.type === BootloaderChunkType.FlowControl,\n\t\t\t\t\t\t\t1000,\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t\t\"OTW update failed: The bootloader did not acknowledge the start of transfer.\",\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\tstatus:\n\t\t\t\t\t\t\t\tControllerFirmwareUpdateStatus.Error_Timeout,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (result.command) {\n\t\t\t\t\t\tcase XModemMessageHeaders.ACK: {\n\t\t\t\t\t\t\t// The fragment was accepted\n\t\t\t\t\t\t\tconst progress: ControllerFirmwareUpdateProgress = {\n\t\t\t\t\t\t\t\tsentFragments: fragment,\n\t\t\t\t\t\t\t\ttotalFragments: numFragments,\n\t\t\t\t\t\t\t\tprogress: roundTo(\n\t\t\t\t\t\t\t\t\t(fragment / numFragments) * 100,\n\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tthis.emit(\"firmware update progress\", progress);\n\n\t\t\t\t\t\t\t// we've transmitted at least one fragment, so we need to destroy the driver afterwards\n\t\t\t\t\t\t\tdestroy = true;\n\n\t\t\t\t\t\t\tcontinue transfer;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase XModemMessageHeaders.NAK:\n\t\t\t\t\t\t\t// The fragment was rejected, try again\n\t\t\t\t\t\t\tcontinue retry;\n\t\t\t\t\t\tcase XModemMessageHeaders.CAN:\n\t\t\t\t\t\t\t// The bootloader aborted the update. We'll receive the reason afterwards as a message\n\t\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\t\tbreak transfer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"OTW update failed: Maximum retry attempts reached\",\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus:\n\t\t\t\t\t\tControllerFirmwareUpdateStatus.Error_RetryLimitReached,\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tif (aborted) {\n\t\t\t\t// wait for the reason to craft a good error message\n\t\t\t\tconst error = await this.driver\n\t\t\t\t\t.waitForBootloaderChunk<\n\t\t\t\t\t\tBootloaderChunk & { type: BootloaderChunkType.Message }\n\t\t\t\t\t>(\n\t\t\t\t\t\t(c) =>\n\t\t\t\t\t\t\tc.type === BootloaderChunkType.Message\n\t\t\t\t\t\t\t&& c.message.includes(\"error 0x\"),\n\t\t\t\t\t\t1000,\n\t\t\t\t\t)\n\t\t\t\t\t.catch(() => undefined);\n\n\t\t\t\t// wait for the menu screen so it doesn't show up in logs\n\t\t\t\tawait this.driver\n\t\t\t\t\t.waitForBootloaderChunk(\n\t\t\t\t\t\t(c) => c.type === BootloaderChunkType.Menu,\n\t\t\t\t\t\t1000,\n\t\t\t\t\t)\n\t\t\t\t\t.catch(() => undefined);\n\n\t\t\t\tlet message = `OTW update was aborted by the bootloader.`;\n\t\t\t\tif (error) {\n\t\t\t\t\tmessage += ` ${error.message}`;\n\t\t\t\t\t// TODO: parse error code\n\t\t\t\t}\n\t\t\t\tthis.driver.controllerLog.print(message, \"error\");\n\n\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: ControllerFirmwareUpdateStatus.Error_Aborted,\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\treturn result;\n\t\t\t} else {\n\t\t\t\t// We're done, send EOT and wait for the menu screen\n\t\t\t\tawait this.driver.bootloader.finishUpload();\n\t\t\t\ttry {\n\t\t\t\t\t// The bootloader sends the confirmation and the menu screen very quickly.\n\t\t\t\t\t// Waiting for them separately can cause us to miss the menu screen and\n\t\t\t\t\t// incorrectly assume the update timed out.\n\n\t\t\t\t\tawait Promise.all([\n\t\t\t\t\t\tthis.driver.waitForBootloaderChunk(\n\t\t\t\t\t\t\t(c) =>\n\t\t\t\t\t\t\t\tc.type === BootloaderChunkType.Message\n\t\t\t\t\t\t\t\t&& c.message.includes(\"upload complete\"),\n\t\t\t\t\t\t\t1000,\n\t\t\t\t\t\t),\n\n\t\t\t\t\t\tthis.driver.waitForBootloaderChunk(\n\t\t\t\t\t\t\t(c) => c.type === BootloaderChunkType.Menu,\n\t\t\t\t\t\t\t1000,\n\t\t\t\t\t\t),\n\t\t\t\t\t]);\n\t\t\t\t} catch {\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t\"OTW update failed: The bootloader did not acknowledge the end of transfer.\",\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tstatus: ControllerFirmwareUpdateStatus.Error_Timeout,\n\t\t\t\t\t};\n\t\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.print(\"Firmware update succeeded\");\n\n\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\tsuccess: true,\n\t\t\t\tstatus: ControllerFirmwareUpdateStatus.OK,\n\t\t\t};\n\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tawait this.driver.leaveBootloader(destroy);\n\t\t\tthis._firmwareUpdateInProgress = false;\n\t\t}\n\t}\n\n\tprivate _currentLearnMode: LearnModeIntent | undefined;\n\tprivate _joinNetworkOptions: JoinNetworkOptions | undefined;\n\n\tpublic async beginJoiningNetwork(\n\t\toptions?: JoinNetworkOptions,\n\t): Promise<JoinNetworkResult> {\n\t\tif (this._currentLearnMode != undefined) {\n\t\t\treturn JoinNetworkResult.Error_Busy;\n\t\t} else if (!this.isLearnModePermitted) {\n\t\t\treturn JoinNetworkResult.Error_NotPermitted;\n\t\t}\n\n\t\t// FIXME: If the join strategy says S0, remove S2 from the NIF before joining\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetLearnModeRequest({\n\t\t\t\t\tintent: LearnModeIntent.Inclusion,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (result.isOK()) {\n\t\t\t\tthis._currentLearnMode = LearnModeIntent.Inclusion;\n\t\t\t\tthis._joinNetworkOptions = options;\n\t\t\t\treturn JoinNetworkResult.OK;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Joining a network failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\n\t\tthis._currentLearnMode = undefined;\n\t\treturn JoinNetworkResult.Error_Failed;\n\t}\n\n\tpublic async stopJoiningNetwork(): Promise<boolean> {\n\t\tif (\n\t\t\tthis._currentLearnMode !== LearnModeIntent.LegacyInclusionExclusion\n\t\t\t// FIXME: ^ only for actual exclusion\n\t\t\t&& this._currentLearnMode !== LearnModeIntent.Inclusion\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetLearnModeRequest({\n\t\t\t\t\t// TODO: We should be using .Stop here for the non-legacy\n\t\t\t\t\t// inclusion/exclusion, but that command results in a\n\t\t\t\t\t// negative response on current firmwares, even though it works.\n\t\t\t\t\t// Using LegacyStop avoids that, but results in an unexpected\n\t\t\t\t\t// LearnModeFailed callback.\n\t\t\t\t\tintent: LearnModeIntent.LegacyStop,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (result.isOK()) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\tthis._joinNetworkOptions = undefined;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Failed to stop joining a network: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic async beginLeavingNetwork(): Promise<LeaveNetworkResult> {\n\t\tif (this._currentLearnMode != undefined) {\n\t\t\treturn LeaveNetworkResult.Error_Busy;\n\t\t} else if (!this.isLearnModePermitted) {\n\t\t\treturn LeaveNetworkResult.Error_NotPermitted;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetLearnModeRequest({\n\t\t\t\t\tintent: LearnModeIntent.NetworkWideExclusion,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (result.isOK()) {\n\t\t\t\tthis._currentLearnMode = LearnModeIntent.NetworkWideExclusion;\n\t\t\t\treturn LeaveNetworkResult.OK;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Leaving the current network failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\n\t\tthis._currentLearnMode = undefined;\n\t\treturn LeaveNetworkResult.Error_Failed;\n\t}\n\n\tpublic async stopLeavingNetwork(): Promise<boolean> {\n\t\tif (\n\t\t\tthis._currentLearnMode !== LearnModeIntent.LegacyInclusionExclusion\n\t\t\t// FIXME: ^ only for actual exclusion\n\t\t\t&& this._currentLearnMode\n\t\t\t\t!== LearnModeIntent.LegacyNetworkWideExclusion\n\t\t\t&& this._currentLearnMode !== LearnModeIntent.DirectExclusion\n\t\t\t&& this._currentLearnMode !== LearnModeIntent.NetworkWideExclusion\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetLearnModeRequest({\n\t\t\t\t\t// TODO: We should be using .Stop here for the non-legacy\n\t\t\t\t\t// inclusion/exclusion, but that command results in a\n\t\t\t\t\t// negative response on current firmwares, even though it works.\n\t\t\t\t\t// Using LegacyStop avoids that, but results in an unexpected\n\t\t\t\t\t// LearnModeFailed callback.\n\t\t\t\t\tintent: LearnModeIntent.LegacyStop,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (result.isOK()) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Failed to stop leaving a network: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Is called when a RemoveNode request is received from the controller.\n\t * Handles and controls the exclusion process.\n\t */\n\tprivate async handleLearnModeCallback(\n\t\tmsg: SetLearnModeCallback,\n\t): Promise<boolean> {\n\t\t// not sure what to do with this message, we're not in learn mode\n\t\tif (this._currentLearnMode == undefined) return false;\n\n\t\t// FIXME: Reset security manager on successful join or leave\n\n\t\tconst wasJoining = this._currentLearnMode === LearnModeIntent.Inclusion\n\t\t\t|| this._currentLearnMode === LearnModeIntent.SmartStart\n\t\t\t|| this._currentLearnMode\n\t\t\t\t=== LearnModeIntent.LegacyNetworkWideInclusion\n\t\t\t|| (this._currentLearnMode\n\t\t\t\t\t=== LearnModeIntent.LegacyInclusionExclusion\n\t\t\t\t// TODO: Secondary controller may also use this to accept controller shift\n\t\t\t\t// Figure out how to detect that.\n\t\t\t\t&& this.role === ControllerRole.Primary);\n\t\tconst wasLeaving =\n\t\t\tthis._currentLearnMode === LearnModeIntent.DirectExclusion\n\t\t\t|| this._currentLearnMode\n\t\t\t\t=== LearnModeIntent.NetworkWideExclusion\n\t\t\t|| this._currentLearnMode\n\t\t\t\t=== LearnModeIntent.LegacyNetworkWideExclusion\n\t\t\t|| (this._currentLearnMode\n\t\t\t\t\t=== LearnModeIntent.LegacyInclusionExclusion\n\t\t\t\t&& this.role !== ControllerRole.Primary);\n\n\t\tif (msg.status === LearnModeStatus.Started) {\n\t\t\t// cool, cool, cool...\n\t\t\treturn true;\n\t\t} else if (msg.status === LearnModeStatus.Failed) {\n\t\t\tif (wasJoining) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\tthis._joinNetworkOptions = undefined;\n\t\t\t\tthis.emit(\"joining network failed\");\n\t\t\t\treturn true;\n\t\t\t} else if (wasLeaving) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\tthis.emit(\"leaving network failed\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (\n\t\t\tmsg.status === LearnModeStatus.Completed\n\t\t\t|| (this._currentLearnMode >= LearnModeIntent.Inclusion\n\t\t\t\t&& msg.status === LearnModeStatus.ProtocolDone)\n\t\t) {\n\t\t\tif (wasJoining) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\tthis.driver[\"_securityManager\"] = undefined;\n\t\t\t\tthis.driver[\"_securityManager2\"] = await SecurityManager2\n\t\t\t\t\t.create();\n\t\t\t\tthis.driver[\"_securityManagerLR\"] = await SecurityManager2\n\t\t\t\t\t.create();\n\t\t\t\tthis._nodes.clear();\n\n\t\t\t\tprocess.nextTick(() => this.afterJoiningNetwork().catch(noop));\n\t\t\t\treturn true;\n\t\t\t} else if (wasLeaving) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\tthis.emit(\"network left\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// not sure what to do with this message\n\t\treturn false;\n\t}\n\n\tprivate async expectSecurityBootstrapS0(\n\t\tbootstrappingNode: ZWaveNode,\n\t): Promise<SecurityBootstrapFailure | undefined> {\n\t\t// When bootstrapping with S0, no other keys are granted\n\t\tfor (const secClass of securityClassOrder) {\n\t\t\tif (secClass !== SecurityClass.S0_Legacy) {\n\t\t\t\tbootstrappingNode.securityClasses.set(secClass, false);\n\t\t\t}\n\t\t}\n\n\t\tconst unGrantSecurityClass = () => {\n\t\t\tthis.driver[\"_securityManager\"] = undefined;\n\t\t\tbootstrappingNode.securityClasses.set(\n\t\t\t\tSecurityClass.S0_Legacy,\n\t\t\t\tfalse,\n\t\t\t);\n\t\t};\n\n\t\tconst abortTimeout = () => {\n\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Security S0 bootstrapping failed: a secure inclusion timer has elapsed`,\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\n\t\t\tunGrantSecurityClass();\n\t\t\treturn SecurityBootstrapFailure.Timeout;\n\t\t};\n\n\t\ttry {\n\t\t\tconst api = bootstrappingNode.commandClasses.Security;\n\n\t\t\t// For the first part of the bootstrapping, a temporary key needs to be used\n\t\t\tthis.driver[\"_securityManager\"] = new SecurityManager({\n\t\t\t\townNodeId: this._ownNodeId!,\n\t\t\t\tnetworkKey: new Uint8Array(16).fill(0),\n\t\t\t\tnonceTimeout: this.driver.options.timeouts.nonce,\n\t\t\t});\n\n\t\t\t// Report the supported schemes\n\t\t\tawait api.reportSecurityScheme(false);\n\n\t\t\t// Expect a NonceGet within 10 seconds\n\t\t\tlet nonceGet = await this.driver.waitForCommand<SecurityCCNonceGet>(\n\t\t\t\t(cc) => cc instanceof SecurityCCNonceGet,\n\t\t\t\t10000,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (nonceGet === \"timeout\") return abortTimeout();\n\n\t\t\t// Send nonce\n\t\t\tawait api.sendNonce();\n\n\t\t\t// Expect NetworkKeySet within 10 seconds\n\t\t\tconst networkKeySet = await this.driver.waitForCommand<\n\t\t\t\tSecurityCCNetworkKeySet\n\t\t\t>(\n\t\t\t\t(cc) => cc instanceof SecurityCCNetworkKeySet,\n\t\t\t\t10000,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (networkKeySet === \"timeout\") return abortTimeout();\n\n\t\t\t// Now that the key is known, we can create the real security manager\n\t\t\tthis.driver[\"_securityManager\"] = new SecurityManager({\n\t\t\t\townNodeId: this._ownNodeId!,\n\t\t\t\tnetworkKey: networkKeySet.networkKey,\n\t\t\t\tnonceTimeout: this.driver.options.timeouts.nonce,\n\t\t\t});\n\n\t\t\t// Request a new nonce to respond, which should be answered within 10 seconds\n\t\t\tlet nonce = await api.withOptions({ reportTimeoutMs: 10000 })\n\t\t\t\t.getNonce();\n\t\t\tif (!nonce) return abortTimeout();\n\n\t\t\t// Verify the key\n\t\t\tawait api.verifyNetworkKey();\n\n\t\t\t// We are a controller, so continue with scheme inherit\n\n\t\t\t// Expect a NonceGet within 10 seconds\n\t\t\tnonceGet = await this.driver.waitForCommand<SecurityCCNonceGet>(\n\t\t\t\t(cc) => cc instanceof SecurityCCNonceGet,\n\t\t\t\t10000,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (nonceGet === \"timeout\") return abortTimeout();\n\n\t\t\t// Send nonce\n\t\t\tawait api.sendNonce();\n\n\t\t\t// Expect SchemeInherit within 10 seconds\n\t\t\tconst schemeInherit = await this.driver.waitForCommand<\n\t\t\t\tSecurityCCSchemeInherit\n\t\t\t>(\n\t\t\t\t(cc) => cc instanceof SecurityCCSchemeInherit,\n\t\t\t\t10000,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (schemeInherit === \"timeout\") return abortTimeout();\n\n\t\t\t// Request a new nonce to respond, which should be answered within 10 seconds\n\t\t\tnonce = await api.withOptions({ reportTimeoutMs: 10000 })\n\t\t\t\t.getNonce();\n\t\t\tif (!nonce) return abortTimeout();\n\n\t\t\t// Report the supported schemes. This isn't technically correct, but since\n\t\t\t// S0 won't get any extensions, we can just report the default scheme again\n\t\t\tawait api.reportSecurityScheme(true);\n\n\t\t\t// Remember that the S0 key was granted\n\t\t\tbootstrappingNode.securityClasses.set(\n\t\t\t\tSecurityClass.S0_Legacy,\n\t\t\t\ttrue,\n\t\t\t);\n\n\t\t\t// Store the key\n\t\t\tthis.driver.cacheSet(\n\t\t\t\tcacheKeys.controller.securityKeys(SecurityClass.S0_Legacy),\n\t\t\t\tnetworkKeySet.networkKey,\n\t\t\t);\n\n\t\t\tthis.driver.driverLog.print(\n\t\t\t\t`Security S0 bootstrapping successful`,\n\t\t\t);\n\n\t\t\t// success \uD83C\uDF89\n\t\t} catch (e) {\n\t\t\tlet errorMessage = `Security S0 bootstrapping failed`;\n\t\t\tlet result = SecurityBootstrapFailure.Unknown;\n\t\t\tif (!isZWaveError(e)) {\n\t\t\t\terrorMessage += `: ${e as any}`;\n\t\t\t} else if (e.code === ZWaveErrorCodes.Controller_MessageExpired) {\n\t\t\t\terrorMessage += \": a secure inclusion timer has elapsed.\";\n\t\t\t\tresult = SecurityBootstrapFailure.Timeout;\n\t\t\t} else if (\n\t\t\t\te.code !== ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t&& e.code !== ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t) {\n\t\t\t\terrorMessage += `: ${e.message}`;\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tbootstrappingNode.id,\n\t\t\t\terrorMessage,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tunGrantSecurityClass();\n\n\t\t\treturn result;\n\t\t}\n\t}\n\n\tprivate async expectSecurityBootstrapS2(\n\t\tbootstrappingNode: ZWaveNode,\n\t\trequested: InclusionGrant,\n\t\tuserCallbacks: JoinNetworkUserCallbacks | undefined =\n\t\t\tthis.driver.options.joinNetworkUserCallbacks,\n\t): Promise<\n\t\tSecurityBootstrapFailure | undefined\n\t> {\n\t\tconst api = bootstrappingNode.commandClasses[\"Security 2\"]\n\t\t\t.withOptions({\n\t\t\t\t// Do not wait for Nonce Reports after SET-type commands.\n\t\t\t\t// Timing is critical here\n\t\t\t\ts2VerifyDelivery: false,\n\t\t\t});\n\n\t\tconst unGrantSecurityClasses = () => {\n\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\tbootstrappingNode.securityClasses.set(secClass, false);\n\t\t\t}\n\t\t};\n\n\t\t// FIXME: Abstract this out so it can be reused as primary and secondary\n\t\tconst securityManager = isLongRangeNodeId(this._ownNodeId!)\n\t\t\t? this.driver.securityManagerLR\n\t\t\t: this.driver.securityManager2;\n\n\t\tif (!securityManager) {\n\t\t\t// This should not happen when joining a network.\n\t\t\tunGrantSecurityClasses();\n\t\t\treturn SecurityBootstrapFailure.NoKeysConfigured;\n\t\t}\n\n\t\tconst receivedKeys = new Map<SecurityClass, Uint8Array>();\n\n\t\tconst deleteTempKey = () => {\n\t\t\t// Whatever happens, no further communication needs the temporary key\n\t\t\tsecurityManager.deleteNonce(bootstrappingNode.id);\n\t\t\tsecurityManager.tempKeys.delete(bootstrappingNode.id);\n\t\t};\n\n\t\tlet dskHidden = false;\n\t\tconst applicationHideDSK = () => {\n\t\t\tif (dskHidden) return;\n\t\t\tdskHidden = true;\n\t\t\ttry {\n\t\t\t\tuserCallbacks?.done();\n\t\t\t} catch {\n\t\t\t\t// ignore application-level errors\n\t\t\t}\n\t\t};\n\n\t\tconst abort = async (failType?: KEXFailType): Promise<void> => {\n\t\t\tapplicationHideDSK();\n\t\t\tif (failType != undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tawait api.abortKeyExchange(failType);\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Un-grant S2 security classes we might have granted\n\t\t\tunGrantSecurityClasses();\n\t\t\tdeleteTempKey();\n\t\t};\n\n\t\tconst abortTimeout = async () => {\n\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Security S2 bootstrapping failed: a secure inclusion timer has elapsed`,\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\n\t\t\tawait abort();\n\t\t\treturn SecurityBootstrapFailure.Timeout;\n\t\t};\n\n\t\tconst abortCanceled = async () => {\n\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`The including node canceled the Security S2 bootstrapping.`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\t\t\tawait abort();\n\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t};\n\n\t\ttry {\n\t\t\t// Send with our desired keys\n\t\t\tawait api.requestKeys({\n\t\t\t\trequestedKeys: requested.securityClasses,\n\t\t\t\trequestCSA: false,\n\t\t\t\tsupportedECDHProfiles: [ECDHProfiles.Curve25519],\n\t\t\t\tsupportedKEXSchemes: [KEXSchemes.KEXScheme1],\n\t\t\t});\n\n\t\t\t// Wait for including node to grant keys\n\t\t\tconst kexSet = await this.driver.waitForCommand<\n\t\t\t\tSecurity2CCKEXSet | Security2CCKEXFail\n\t\t\t>(\n\t\t\t\t(cc) =>\n\t\t\t\t\tcc instanceof Security2CCKEXSet\n\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\tinclusionTimeouts.TB2,\n\t\t\t).catch(() => \"timeout\" as const);\n\n\t\t\tif (kexSet === \"timeout\") return abortTimeout();\n\t\t\tif (kexSet instanceof Security2CCKEXFail) {\n\t\t\t\treturn abortCanceled();\n\t\t\t}\n\n\t\t\t// Validate the command\n\t\t\t// Echo flag must be false\n\t\t\tif (kexSet.echo) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: KEX Set unexpectedly has the echo flag set.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoVerify);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t} else if (\n\t\t\t\tkexSet.selectedKEXScheme !== KEXSchemes.KEXScheme1\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Unsupported key exchange scheme.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoSupportedScheme);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t} else if (\n\t\t\t\tkexSet.selectedECDHProfile !== ECDHProfiles.Curve25519\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Unsupported ECDH profile.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoSupportedCurve);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t} else if (kexSet.permitCSA !== false) {\n\t\t\t\t// We do not support CSA at the moment, so it is never requested.\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: CSA granted but not requested.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.BootstrappingCanceled);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t}\n\n\t\t\tconst matchingKeys = kexSet.grantedKeys.filter((k) =>\n\t\t\t\tsecurityClassOrder.includes(k as any)\n\t\t\t\t&& requested.securityClasses.includes(k)\n\t\t\t);\n\t\t\tif (!matchingKeys.length) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: None of the requested security classes are granted.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoKeyMatch);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t}\n\n\t\t\tconst highestGranted = getHighestSecurityClass(matchingKeys);\n\t\t\tconst requiresAuthentication =\n\t\t\t\thighestGranted === SecurityClass.S2_AccessControl\n\t\t\t\t|| highestGranted === SecurityClass.S2_Authenticated;\n\n\t\t\t// If authentication is required, use the (static) authenticated ECDH key pair,\n\t\t\t// otherwise generate a new one\n\t\t\tconst keyPair = requiresAuthentication\n\t\t\t\t? this.driver.getLearnModeAuthenticatedKeyPair()\n\t\t\t\t: generateECDHKeyPairSync();\n\t\t\tconst publicKey = extractRawECDHPublicKeySync(keyPair.publicKey);\n\t\t\tconst transmittedPublicKey = Bytes.from(publicKey);\n\t\t\tif (requiresAuthentication) {\n\t\t\t\t// Authentication requires obfuscating the public key\n\t\t\t\ttransmittedPublicKey.writeUInt16BE(0x0000, 0);\n\n\t\t\t\t// Show the DSK to the user\n\t\t\t\tconst dsk = dskToString(publicKey.subarray(0, 16));\n\t\t\t\ttry {\n\t\t\t\t\tuserCallbacks?.showDSK(dsk);\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore application-level errors\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait api.sendPublicKey(transmittedPublicKey, false);\n\n\t\t\t// Wait for including node to send its public key\n\t\t\tconst pubKeyReport = await this.driver.waitForCommand<\n\t\t\t\tSecurity2CCPublicKeyReport | Security2CCKEXFail\n\t\t\t>(\n\t\t\t\t(cc) =>\n\t\t\t\t\tcc instanceof Security2CCPublicKeyReport\n\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\tinclusionTimeouts.TB3,\n\t\t\t).catch(() => \"timeout\" as const);\n\n\t\t\tif (pubKeyReport === \"timeout\") return abortTimeout();\n\t\t\tif (pubKeyReport instanceof Security2CCKEXFail) {\n\t\t\t\treturn abortCanceled();\n\t\t\t}\n\n\t\t\tconst includingNodePubKey = pubKeyReport.publicKey;\n\t\t\tconst sharedSecret = crypto.diffieHellman({\n\t\t\t\tpublicKey: importRawECDHPublicKeySync(includingNodePubKey),\n\t\t\t\tprivateKey: keyPair.privateKey,\n\t\t\t});\n\n\t\t\t// Derive temporary key from ECDH key pair - this will allow us to receive the node's KEX SET commands\n\t\t\tconst tempKeys = await deriveTempKeysAsync(\n\t\t\t\tawait computePRKAsync(\n\t\t\t\t\tsharedSecret,\n\t\t\t\t\tincludingNodePubKey,\n\t\t\t\t\tpublicKey,\n\t\t\t\t),\n\t\t\t);\n\t\t\tsecurityManager.deleteNonce(bootstrappingNode.id);\n\t\t\tsecurityManager.tempKeys.set(bootstrappingNode.id, {\n\t\t\t\tkeyCCM: tempKeys.tempKeyCCM,\n\t\t\t\tpersonalizationString: tempKeys.tempPersonalizationString,\n\t\t\t});\n\n\t\t\t// Wait for the confirmation of the requested keys and\n\t\t\t// retransmit the KEXSet echo every 10 seconds until a response is\n\t\t\t// received or the process timed out.\n\t\t\tconst confirmKeysStartTime = Date.now();\n\t\t\tlet kexReportEcho:\n\t\t\t\t| Security2CCKEXReport\n\t\t\t\t| Security2CCKEXFail\n\t\t\t\t| \"timeout\"\n\t\t\t\t| undefined;\n\t\t\tfor (let i = 0; i <= 25; i++) {\n\t\t\t\ttry {\n\t\t\t\t\tkexReportEcho = await api.withOptions({\n\t\t\t\t\t\treportTimeoutMs: 10000,\n\t\t\t\t\t}).confirmGrantedKeys({\n\t\t\t\t\t\tgrantedKeys: kexSet.grantedKeys,\n\t\t\t\t\t\tpermitCSA: kexSet.permitCSA,\n\t\t\t\t\t\tselectedECDHProfile: kexSet.selectedECDHProfile,\n\t\t\t\t\t\tselectedKEXScheme: kexSet.selectedKEXScheme,\n\t\t\t\t\t\t_reserved: kexSet._reserved,\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t\tif (kexReportEcho != undefined) break;\n\t\t\t\tif (Date.now() - confirmKeysStartTime > 240000) break;\n\t\t\t}\n\n\t\t\tif (!kexReportEcho || kexReportEcho === \"timeout\") {\n\t\t\t\treturn abortTimeout();\n\t\t\t} else if (kexReportEcho instanceof Security2CCKEXFail) {\n\t\t\t\treturn abortCanceled();\n\t\t\t}\n\n\t\t\t// The application no longer needs to show the DSK\n\t\t\tapplicationHideDSK();\n\n\t\t\t// Validate the response\n\t\t\tif (!kexReportEcho.echo) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: KEXReport received without echo flag`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (kexReportEcho.requestCSA !== false) {\n\t\t\t\t// We don't request CSA\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Invalid KEXReport received`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (kexReportEcho._reserved !== 0) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Invalid KEXReport received`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (\n\t\t\t\t!kexReportEcho.isEncapsulatedWith(\n\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Command received without encryption`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t} else if (\n\t\t\t\tkexReportEcho.requestedKeys.length\n\t\t\t\t\t!== requested.securityClasses.length\n\t\t\t\t|| !kexReportEcho.requestedKeys.every((k) =>\n\t\t\t\t\trequested.securityClasses.includes(k)\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Granted key mismatch.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t}\n\n\t\t\tfor (const key of kexSet.grantedKeys) {\n\t\t\t\t// Request network key and wait for including node to respond\n\t\t\t\tconst keyReportPromise = this.driver.waitForCommand<\n\t\t\t\t\tSecurity2CCNetworkKeyReport | Security2CCKEXFail\n\t\t\t\t>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc instanceof Security2CCNetworkKeyReport\n\t\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\t\tinclusionTimeouts.TB4,\n\t\t\t\t).catch(() => \"timeout\" as const);\n\n\t\t\t\tawait api.requestNetworkKey(key);\n\t\t\t\tconst keyReport = await keyReportPromise;\n\n\t\t\t\tif (keyReport === \"timeout\") return abortTimeout();\n\t\t\t\tif (keyReport instanceof Security2CCKEXFail) {\n\t\t\t\t\treturn abortCanceled();\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t!keyReport.isEncapsulatedWith(\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Command received without encryption`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t}\n\n\t\t\t\t// Ensure it was received encrypted with the temporary key\n\t\t\t\tif (\n\t\t\t\t\t!securityManager.hasUsedSecurityClass(\n\t\t\t\t\t\tbootstrappingNode.id,\n\t\t\t\t\t\tSecurityClass.Temporary,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Node used wrong key to communicate.`,\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t}\n\n\t\t\t\tconst securityClass = keyReport.grantedKey;\n\t\t\t\tif (securityClass !== key) {\n\t\t\t\t\t// and that the granted key is the requested key\n\t\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Received key for wrong security class`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.DifferentKey);\n\t\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t\t}\n\n\t\t\t\t// Store the network key\n\t\t\t\treceivedKeys.set(securityClass, keyReport.networkKey);\n\t\t\t\tawait securityManager.setKeyAsync(\n\t\t\t\t\tsecurityClass,\n\t\t\t\t\tkeyReport.networkKey,\n\t\t\t\t);\n\t\t\t\tif (securityClass === SecurityClass.S0_Legacy) {\n\t\t\t\t\t// TODO: This is awkward to have here\n\t\t\t\t\tthis.driver[\"_securityManager\"] = new SecurityManager({\n\t\t\t\t\t\townNodeId: this._ownNodeId!,\n\t\t\t\t\t\tnetworkKey: keyReport.networkKey,\n\t\t\t\t\t\tnonceTimeout: this.driver.options.timeouts.nonce,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Force nonce synchronization, then verify the network key\n\t\t\t\tsecurityManager.deleteNonce(bootstrappingNode.id);\n\t\t\t\tawait api.withOptions({\n\t\t\t\t\ts2OverrideSecurityClass: securityClass,\n\t\t\t\t}).verifyNetworkKey();\n\n\t\t\t\t// Force nonce synchronization again for the temporary key\n\t\t\t\tsecurityManager.deleteNonce(bootstrappingNode.id);\n\n\t\t\t\t// Wait for including node to send its public key\n\t\t\t\tconst transferEnd = await this.driver.waitForCommand<\n\t\t\t\t\tSecurity2CCTransferEnd | Security2CCKEXFail\n\t\t\t\t>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc instanceof Security2CCTransferEnd\n\t\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\t\tinclusionTimeouts.TB5,\n\t\t\t\t).catch(() => \"timeout\" as const);\n\n\t\t\t\tif (transferEnd === \"timeout\") return abortTimeout();\n\t\t\t\tif (transferEnd instanceof Security2CCKEXFail) {\n\t\t\t\t\treturn abortCanceled();\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t!keyReport.isEncapsulatedWith(\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Command received without encryption`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t} else if (\n\t\t\t\t\t!transferEnd.keyVerified || transferEnd.keyRequestComplete\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Invalid TransferEnd received`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Confirm end of bootstrapping\n\t\t\tawait api.endKeyExchange();\n\n\t\t\t// Remember all security classes we were granted\n\t\t\tfor (const securityClass of securityClassOrder) {\n\t\t\t\tbootstrappingNode.securityClasses.set(\n\t\t\t\t\tsecurityClass,\n\t\t\t\t\tkexSet.grantedKeys.includes(securityClass),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// And store the keys\n\t\t\tfor (const [secClass, key] of receivedKeys) {\n\t\t\t\tthis.driver.cacheSet(\n\t\t\t\t\tcacheKeys.controller.securityKeys(secClass),\n\t\t\t\t\tkey,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.driver.driverLog.print(\n\t\t\t\t`Security S2 bootstrapping successful with these security classes:${\n\t\t\t\t\t[\n\t\t\t\t\t\t...bootstrappingNode.securityClasses.entries(),\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(([, v]) => v)\n\t\t\t\t\t\t.map(([k]) =>\n\t\t\t\t\t\t\t`\\n\u00B7 ${getEnumMemberName(SecurityClass, k)}`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}`,\n\t\t\t);\n\n\t\t\t// success \uD83C\uDF89\n\t\t} catch (e) {\n\t\t\tlet errorMessage =\n\t\t\t\t`Security S2 bootstrapping failed, no S2 security classes were granted`;\n\t\t\tlet result = SecurityBootstrapFailure.Unknown;\n\t\t\tif (!isZWaveError(e)) {\n\t\t\t\terrorMessage += `: ${e as any}`;\n\t\t\t} else if (e.code === ZWaveErrorCodes.Controller_MessageExpired) {\n\t\t\t\terrorMessage += \": a secure inclusion timer has elapsed.\";\n\t\t\t\tresult = SecurityBootstrapFailure.Timeout;\n\t\t\t} else if (\n\t\t\t\te.code !== ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t&& e.code !== ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t) {\n\t\t\t\terrorMessage += `: ${e.message}`;\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tbootstrappingNode.id,\n\t\t\t\terrorMessage,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\t// Remember that we were NOT granted any S2 security classes\n\t\t\tunGrantSecurityClasses();\n\t\t\tbootstrappingNode.removeCC(CommandClasses[\"Security 2\"]);\n\n\t\t\treturn result;\n\t\t} finally {\n\t\t\t// Whatever happens, no further communication needs the temporary key\n\t\t\tdeleteTempKey();\n\t\t}\n\t}\n\n\tprivate async afterJoiningNetwork(): Promise<void> {\n\t\tthis.driver.driverLog.print(\"waiting for security bootstrapping...\");\n\n\t\tconst bootstrapInitStart = Date.now();\n\t\tconst supportedCCs = determineNIF().supportedCCs;\n\t\tconst supportsS0 = supportedCCs.includes(CommandClasses.Security);\n\t\tconst supportsS2 = supportedCCs.includes(CommandClasses[\"Security 2\"]);\n\n\t\tlet initTimeout: number;\n\t\tlet initPredicate: (cc: CCId) => boolean;\n\n\t\t// KEX Get must be received:\n\t\t// - no later than 10..30 seconds after the inclusion if S0 is supported\n\t\t// - no later than 30 seconds after the inclusion if only S2 is supported\n\t\t// For simplicity, we wait the full 30s.\n\t\t// SecurityCCSchemeGet must be received no later than 10 seconds\n\t\t// after the inclusion if S0 is supported\n\t\tif (supportsS0 && supportsS2) {\n\t\t\tinitTimeout = inclusionTimeouts.TB1;\n\t\t\tinitPredicate = (cc) =>\n\t\t\t\tcc instanceof SecurityCCSchemeGet\n\t\t\t\t|| cc instanceof Security2CCKEXGet;\n\t\t} else if (supportsS2) {\n\t\t\tinitTimeout = inclusionTimeouts.TB1;\n\t\t\tinitPredicate = (cc) => cc instanceof Security2CCKEXGet;\n\t\t} else if (supportsS0) {\n\t\t\tinitTimeout = 10000;\n\t\t\tinitPredicate = (cc) => cc instanceof SecurityCCSchemeGet;\n\t\t} else {\n\t\t\tinitTimeout = 0;\n\t\t\tinitPredicate = () => false;\n\t\t}\n\n\t\tconst bootstrapInitPromise = this.driver.waitForCommand<\n\t\t\tSecurity2CCKEXGet | SecurityCCSchemeGet\n\t\t>(initPredicate, initTimeout).catch(() => \"timeout\" as const);\n\n\t\tconst identifySelf = async () => {\n\t\t\t// Update own node ID and other controller flags.\n\t\t\tawait this.identify().catch(noop);\n\n\t\t\t// Notify applications that we're now part of a new network\n\t\t\t// The driver will point the databases to the new home ID\n\t\t\tthis.emit(\"network found\", this._homeId!, this._ownNodeId!);\n\n\t\t\t// Figure out the controller's network role\n\t\t\tawait this.getControllerCapabilities().catch(noop);\n\n\t\t\t// Create new node instances\n\t\t\tconst { nodeIds } = await this.getSerialApiInitData();\n\t\t\tawait this.initNodes(nodeIds, [], () => Promise.resolve());\n\t\t};\n\n\t\t// Do the self-identification while waiting for the bootstrap init command\n\t\tconst [bootstrapInit] = await Promise.all([\n\t\t\tbootstrapInitPromise,\n\t\t\tidentifySelf(),\n\t\t]);\n\n\t\tif (bootstrapInit === \"timeout\") {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\"No security bootstrapping command received, continuing without encryption...\",\n\t\t\t);\n\t\t} else if (bootstrapInit instanceof SecurityCCSchemeGet) {\n\t\t\tconst nodeId = bootstrapInit.nodeId;\n\t\t\tconst bootstrappingNode = this.nodes.get(nodeId);\n\t\t\tif (!bootstrappingNode) {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Received S2 bootstrap initiation from unknown node, ignoring...\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t} else if (Date.now() - bootstrapInitStart > 10000) {\n\t\t\t\t// Received too late, S0 bootstrapping must not continue\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Security S0 bootstrapping command received too late, continuing without encryption...\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// We definitely know that the node supports S0\n\t\t\t\tbootstrappingNode.addCC(CommandClasses.Security, {\n\t\t\t\t\tsecure: true,\n\t\t\t\t\tisSupported: true,\n\t\t\t\t});\n\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage: `Received S0 bootstrap initiation`,\n\t\t\t\t});\n\n\t\t\t\tconst bootstrapResult = await this.expectSecurityBootstrapS0(\n\t\t\t\t\tbootstrappingNode,\n\t\t\t\t);\n\t\t\t\tif (bootstrapResult !== undefined) {\n\t\t\t\t\t// If there was a failure, mark S0 as not supported\n\t\t\t\t\tbootstrappingNode.removeCC(CommandClasses.Security);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (bootstrapInit instanceof Security2CCKEXGet) {\n\t\t\tconst nodeId = bootstrapInit.nodeId as number;\n\t\t\tconst bootstrappingNode = this.nodes.get(nodeId);\n\t\t\tif (!bootstrappingNode) {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Received S2 bootstrap initiation from unknown node, ignoring...\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// We definitely know that the node supports S2\n\t\t\t\tbootstrappingNode.addCC(CommandClasses[\"Security 2\"], {\n\t\t\t\t\tsecure: true,\n\t\t\t\t\tisSupported: true,\n\t\t\t\t});\n\n\t\t\t\tlet grant: InclusionGrant | undefined;\n\n\t\t\t\tswitch (this._joinNetworkOptions?.strategy) {\n\t\t\t\t\t// case JoinNetworkStrategy.Security_S2: {\n\t\t\t\t\t// \tgrant = this._joinNetworkOptions.requested;\n\t\t\t\t\t// \tbreak;\n\t\t\t\t\t// }\n\t\t\t\t\t// case JoinNetworkStrategy.SmartStart:\n\t\t\t\t\tcase JoinNetworkStrategy.Default:\n\t\t\t\t\tdefault: {\n\t\t\t\t\t\t// No options given, just request all keys\n\t\t\t\t\t\tgrant = {\n\t\t\t\t\t\t\tsecurityClasses: [...securityClassOrder],\n\t\t\t\t\t\t\tclientSideAuth: false,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (grant) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Received S2 bootstrap initiation, requesting keys: ${\n\t\t\t\t\t\t\t\tgrant.securityClasses.map((sc) =>\n\t\t\t\t\t\t\t\t\t`\\n\u00B7 ${\n\t\t\t\t\t\t\t\t\t\tgetEnumMemberName(SecurityClass, sc)\n\t\t\t\t\t\t\t\t\t}\\n`\n\t\t\t\t\t\t\t\t).join(\"\")\n\t\t\t\t\t\t\t}\n client-side auth: ${grant.clientSideAuth}`,\n\t\t\t\t\t});\n\n\t\t\t\t\tconst bootstrapResult = await this\n\t\t\t\t\t\t.expectSecurityBootstrapS2(\n\t\t\t\t\t\t\tbootstrappingNode,\n\t\t\t\t\t\t\tgrant,\n\t\t\t\t\t\t);\n\t\t\t\t\tif (bootstrapResult !== undefined) {\n\t\t\t\t\t\t// If there was a failure, mark S2 as not supported\n\t\t\t\t\t\tbootstrappingNode.removeCC(\n\t\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._joinNetworkOptions = undefined;\n\n\t\t// Read protocol information of all nodes\n\t\tfor (const node of this.nodes.values()) {\n\t\t\tif (node.isControllerNode) continue;\n\t\t\tawait node[\"queryProtocolInfo\"]();\n\t\t}\n\n\t\t// Notify applications that joining the network is complete\n\t\tthis.emit(\"network joined\");\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\ttype AssociationAddress,\n\tAssociationCC,\n\ttype AssociationCheckResult,\n\ttype AssociationGroup,\n\tECDHProfiles,\n\tFLiRS2WakeUpTime,\n\ttype FirmwareUpdateOptions,\n\ttype FirmwareUpdateResult,\n\tInclusionControllerCCComplete,\n\tInclusionControllerCCInitiate,\n\tInclusionControllerStatus,\n\tInclusionControllerStep,\n\tKEXFailType,\n\tKEXSchemes,\n\tManufacturerSpecificCCValues,\n\tMultiChannelAssociationCC,\n\tPowerlevel,\n\tSecurity2CCKEXFail,\n\tSecurity2CCKEXGet,\n\ttype Security2CCKEXReport,\n\tSecurity2CCKEXSet,\n\tSecurity2CCNetworkKeyGet,\n\tSecurity2CCNetworkKeyReport,\n\tSecurity2CCNetworkKeyVerify,\n\tSecurity2CCPublicKeyReport,\n\tSecurity2CCTransferEnd,\n\tSecurity2Command,\n\tSecurityCCNetworkKeySet,\n\tSecurityCCNonceGet,\n\tSecurityCCSchemeGet,\n\tSecurityCCSchemeInherit,\n\tVersionCCValues,\n\tVersionCommand,\n\tZWaveProtocolCCAssignReturnRoute,\n\tZWaveProtocolCCAssignReturnRoutePriority,\n\tZWaveProtocolCCAssignSUCReturnRoute,\n\tZWaveProtocolCCAssignSUCReturnRoutePriority,\n\tinclusionTimeouts,\n\tutils as ccUtils,\n} from \"@zwave-js/cc\";\nimport { type IndicatorObject } from \"@zwave-js/cc/IndicatorCC\";\nimport {\n\tBasicDeviceClass,\n\ttype CCId,\n\tCommandClasses,\n\ttype ControllerCapabilities,\n\tControllerRole,\n\tControllerStatus,\n\tEMPTY_ROUTE,\n\ttype Firmware,\n\tLongRangeChannel,\n\tMAX_NODES,\n\ttype MaybeNotKnown,\n\ttype MaybeUnknown,\n\tNODE_ID_BROADCAST,\n\tNODE_ID_BROADCAST_LR,\n\tNOT_KNOWN,\n\tNodeIDType,\n\tNodeType,\n\ttype ProtocolDataRate,\n\tProtocolType,\n\tProtocols,\n\tRFRegion,\n\ttype RFRegionInfo,\n\ttype RSSI,\n\ttype Route,\n\tRouteKind,\n\tSecurityClass,\n\tSecurityManager,\n\tSecurityManager2,\n\ttype SerialApiInitData,\n\ttype SinglecastCC,\n\tTransmitStatus,\n\tUNKNOWN_STATE,\n\ttype UnknownZWaveChipType,\n\tValueDB,\n\ttype ZWaveApiVersion,\n\ttype ZWaveDataRate,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tZWaveLibraryTypes,\n\tauthHomeIdFromDSK,\n\taverageRSSI,\n\tcomputePRKAsync,\n\tderiveTempKeysAsync,\n\tdskFromString,\n\tdskToString,\n\textractRawECDHPublicKeySync,\n\tgenerateECDHKeyPairSync,\n\tgetChipTypeAndVersion,\n\tgetHighestSecurityClass,\n\timportRawECDHPublicKeySync,\n\tindexDBsByNode,\n\tisEmptyRoute,\n\tisLongRangeNodeId,\n\tisValidDSK,\n\tisZWaveError,\n\tnwiHomeIdFromDSK,\n\tparseBitMask,\n\tsdkVersionGt,\n\tsdkVersionGte,\n\tsdkVersionLt,\n\tsdkVersionLte,\n\tsecurityClassIsS2,\n\tsecurityClassOrder,\n} from \"@zwave-js/core\";\nimport {\n\tBufferedNVMReader,\n\tNVM3,\n\tNVM3Adapter,\n\tNVM500,\n\tNVM500Adapter,\n\ttype NVMAdapter,\n\tmigrateNVM,\n} from \"@zwave-js/nvmedit\";\nimport {\n\ttype BootloaderChunk,\n\tBootloaderChunkType,\n\tFunctionType,\n\ttype Message,\n\ttype SuccessIndicator,\n\tXModemMessageHeaders,\n} from \"@zwave-js/serial\";\nimport {\n\ttype ApplicationUpdateRequest,\n\tApplicationUpdateRequestNodeAdded,\n\tApplicationUpdateRequestNodeInfoReceived,\n\tApplicationUpdateRequestNodeRemoved,\n\tApplicationUpdateRequestSUCIdChanged,\n\tApplicationUpdateRequestSmartStartHomeIDReceived,\n\tApplicationUpdateRequestSmartStartLongRangeHomeIDReceived,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tShutdownRequest,\n\ttype ShutdownResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetControllerCapabilitiesRequest,\n\ttype GetControllerCapabilitiesResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetControllerVersionRequest,\n\ttype GetControllerVersionResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetLongRangeNodesRequest,\n\ttype GetLongRangeNodesResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetProtocolVersionRequest,\n\ttype GetProtocolVersionResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetSerialApiCapabilitiesRequest,\n\ttype GetSerialApiCapabilitiesResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetSerialApiInitDataRequest,\n\ttype GetSerialApiInitDataResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport { HardResetRequest } from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetLongRangeChannelRequest,\n\ttype GetLongRangeChannelResponse,\n\tSetLongRangeChannelRequest,\n\ttype SetLongRangeChannelResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tSerialAPISetupCommand,\n\tSerialAPISetup_CommandUnsupportedResponse,\n\tSerialAPISetup_GetLongRangeMaximumPayloadSizeRequest,\n\ttype SerialAPISetup_GetLongRangeMaximumPayloadSizeResponse,\n\tSerialAPISetup_GetLongRangeMaximumTxPowerRequest,\n\ttype SerialAPISetup_GetLongRangeMaximumTxPowerResponse,\n\tSerialAPISetup_GetMaximumPayloadSizeRequest,\n\ttype SerialAPISetup_GetMaximumPayloadSizeResponse,\n\tSerialAPISetup_GetPowerlevel16BitRequest,\n\ttype SerialAPISetup_GetPowerlevel16BitResponse,\n\tSerialAPISetup_GetPowerlevelRequest,\n\ttype SerialAPISetup_GetPowerlevelResponse,\n\tSerialAPISetup_GetRFRegionRequest,\n\ttype SerialAPISetup_GetRFRegionResponse,\n\tSerialAPISetup_GetRegionInfoRequest,\n\ttype SerialAPISetup_GetRegionInfoResponse,\n\tSerialAPISetup_GetSupportedCommandsRequest,\n\ttype SerialAPISetup_GetSupportedCommandsResponse,\n\tSerialAPISetup_GetSupportedRegionsRequest,\n\ttype SerialAPISetup_GetSupportedRegionsResponse,\n\tSerialAPISetup_SetLongRangeMaximumTxPowerRequest,\n\ttype SerialAPISetup_SetLongRangeMaximumTxPowerResponse,\n\tSerialAPISetup_SetNodeIDTypeRequest,\n\ttype SerialAPISetup_SetNodeIDTypeResponse,\n\tSerialAPISetup_SetPowerlevel16BitRequest,\n\ttype SerialAPISetup_SetPowerlevel16BitResponse,\n\tSerialAPISetup_SetPowerlevelRequest,\n\ttype SerialAPISetup_SetPowerlevelResponse,\n\tSerialAPISetup_SetRFRegionRequest,\n\ttype SerialAPISetup_SetRFRegionResponse,\n\tSerialAPISetup_SetTXStatusReportRequest,\n\ttype SerialAPISetup_SetTXStatusReportResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport { SetApplicationNodeInformationRequest } from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetControllerIdRequest,\n\ttype GetControllerIdResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetBackgroundRSSIRequest,\n\ttype GetBackgroundRSSIResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tSetRFReceiveModeRequest,\n\ttype SetRFReceiveModeResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tSetSerialApiTimeoutsRequest,\n\ttype SetSerialApiTimeoutsResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tStartWatchdogRequest,\n\tStopWatchdogRequest,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tAddNodeDSKToNetworkRequest,\n\tAddNodeStatus,\n\tAddNodeToNetworkRequest,\n\ttype AddNodeToNetworkRequestStatusReport,\n\tAddNodeType,\n\tEnableSmartStartListenRequest,\n\tcomputeNeighborDiscoveryTimeout,\n} from \"@zwave-js/serial/serialapi\";\nimport { AssignPriorityReturnRouteRequest } from \"@zwave-js/serial/serialapi\";\nimport {\n\tAssignPrioritySUCReturnRouteRequest,\n\ttype AssignPrioritySUCReturnRouteRequestTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tAssignReturnRouteRequest,\n\ttype AssignReturnRouteRequestTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tAssignSUCReturnRouteRequest,\n\tAssignSUCReturnRouteRequestTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tDeleteReturnRouteRequest,\n\ttype DeleteReturnRouteRequestTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tDeleteSUCReturnRouteRequest,\n\tDeleteSUCReturnRouteRequestTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetPriorityRouteRequest,\n\ttype GetPriorityRouteResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetRoutingInfoRequest,\n\ttype GetRoutingInfoResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetSUCNodeIdRequest,\n\ttype GetSUCNodeIdResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tIsFailedNodeRequest,\n\ttype IsFailedNodeResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tRemoveFailedNodeRequest,\n\ttype RemoveFailedNodeRequestStatusReport,\n\tRemoveFailedNodeResponse,\n\tRemoveFailedNodeStartFlags,\n\tRemoveFailedNodeStatus,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tRemoveNodeFromNetworkRequest,\n\ttype RemoveNodeFromNetworkRequestStatusReport,\n\tRemoveNodeStatus,\n\tRemoveNodeType,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tReplaceFailedNodeRequest,\n\ttype ReplaceFailedNodeRequestStatusReport,\n\ttype ReplaceFailedNodeResponse,\n\tReplaceFailedNodeStartFlags,\n\tReplaceFailedNodeStatus,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tNodeNeighborUpdateStatus,\n\ttype RequestNodeNeighborUpdateReport,\n\tRequestNodeNeighborUpdateRequest,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tLearnModeIntent,\n\tLearnModeStatus,\n\ttype SetLearnModeCallback,\n\tSetLearnModeRequest,\n} from \"@zwave-js/serial/serialapi\";\nimport { SetPriorityRouteRequest } from \"@zwave-js/serial/serialapi\";\nimport { SetSUCNodeIdRequest } from \"@zwave-js/serial/serialapi\";\nimport {\n\tExtNVMReadLongBufferRequest,\n\ttype ExtNVMReadLongBufferResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tExtNVMReadLongByteRequest,\n\ttype ExtNVMReadLongByteResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tExtNVMWriteLongBufferRequest,\n\ttype ExtNVMWriteLongBufferResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tExtNVMWriteLongByteRequest,\n\ttype ExtNVMWriteLongByteResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tExtendedNVMOperationStatus,\n\tExtendedNVMOperationsCloseRequest,\n\tExtendedNVMOperationsCommand,\n\tExtendedNVMOperationsOpenRequest,\n\tExtendedNVMOperationsReadRequest,\n\ttype ExtendedNVMOperationsResponse,\n\tExtendedNVMOperationsWriteRequest,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tFirmwareUpdateNVM_GetNewImageRequest,\n\ttype FirmwareUpdateNVM_GetNewImageResponse,\n\tFirmwareUpdateNVM_InitRequest,\n\ttype FirmwareUpdateNVM_InitResponse,\n\tFirmwareUpdateNVM_IsValidCRC16Request,\n\ttype FirmwareUpdateNVM_IsValidCRC16Response,\n\tFirmwareUpdateNVM_SetNewImageRequest,\n\ttype FirmwareUpdateNVM_SetNewImageResponse,\n\tFirmwareUpdateNVM_UpdateCRC16Request,\n\ttype FirmwareUpdateNVM_UpdateCRC16Response,\n\tFirmwareUpdateNVM_WriteRequest,\n\ttype FirmwareUpdateNVM_WriteResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetNVMIdRequest,\n\ttype GetNVMIdResponse,\n\ttype NVMId,\n\tnvmSizeToBufferSize,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tNVMOperationStatus,\n\tNVMOperationsCloseRequest,\n\tNVMOperationsOpenRequest,\n\tNVMOperationsReadRequest,\n\ttype NVMOperationsResponse,\n\tNVMOperationsWriteRequest,\n} from \"@zwave-js/serial/serialapi\";\nimport type { TransmitReport } from \"@zwave-js/serial/serialapi\";\nimport {\n\tBytes,\n\tMixin,\n\ttype ReadonlyObjectKeyMap,\n\ttype ReadonlyThrowingMap,\n\ttype ThrowingMap,\n\tTypedEventTarget,\n\tareUint8ArraysEqual,\n\tcloneDeep,\n\tcreateThrowingMap,\n\tflatMap,\n\tgetEnumMemberName,\n\tgetErrorMessage,\n\tnoop,\n\tnum2hex,\n\tpick,\n} from \"@zwave-js/shared\";\nimport { distinct } from \"alcalzone-shared/arrays\";\nimport { wait } from \"alcalzone-shared/async\";\nimport {\n\ttype DeferredPromise,\n\tcreateDeferredPromise,\n} from \"alcalzone-shared/deferred-promise\";\nimport { roundTo } from \"alcalzone-shared/math\";\nimport { isObject } from \"alcalzone-shared/typeguards\";\nimport crypto from \"node:crypto\";\nimport type { Driver } from \"../driver/Driver.js\";\nimport { cacheKeyUtils, cacheKeys } from \"../driver/NetworkCache.js\";\nimport type { StatisticsEventCallbacks } from \"../driver/Statistics.js\";\nimport { type TaskBuilder, TaskPriority } from \"../driver/Task.js\";\nimport { DeviceClass } from \"../node/DeviceClass.js\";\nimport { ZWaveNode } from \"../node/Node.js\";\nimport { VirtualNode } from \"../node/VirtualNode.js\";\nimport {\n\tInterviewStage,\n\ttype LifelineRoutes,\n\tNodeStatus,\n} from \"../node/_Types.js\";\nimport {\n\ttype ControllerStatistics,\n\tControllerStatisticsHost,\n} from \"./ControllerStatistics.js\";\nimport { ZWaveFeature, minFeatureVersions } from \"./Features.js\";\nimport {\n\tdownloadFirmwareUpdate,\n\tgetAvailableFirmwareUpdates,\n} from \"./FirmwareUpdateService.js\";\nimport {\n\ttype ExclusionOptions,\n\tExclusionStrategy,\n\ttype FoundNode,\n\ttype InclusionGrant,\n\ttype InclusionOptions,\n\ttype InclusionOptionsInternal,\n\ttype InclusionResult,\n\tInclusionState,\n\tInclusionStrategy,\n\ttype InclusionUserCallbacks,\n\ttype JoinNetworkOptions,\n\tJoinNetworkResult,\n\tJoinNetworkStrategy,\n\ttype JoinNetworkUserCallbacks,\n\tLeaveNetworkResult,\n\ttype PlannedProvisioningEntry,\n\tProvisioningEntryStatus,\n\tRemoveNodeReason,\n\ttype ReplaceNodeOptions,\n\tSecurityBootstrapFailure,\n\ttype SmartStartProvisioningEntry,\n} from \"./Inclusion.js\";\nimport { SerialNVMIO500, SerialNVMIO700 } from \"./NVMIO.js\";\nimport { determineNIF } from \"./NodeInformationFrame.js\";\nimport { protocolVersionToSDKVersion } from \"./ZWaveSDKVersions.js\";\nimport {\n\ttype ControllerFirmwareUpdateProgress,\n\ttype ControllerFirmwareUpdateResult,\n\tControllerFirmwareUpdateStatus,\n\ttype FirmwareUpdateDeviceID,\n\ttype FirmwareUpdateInfo,\n\ttype GetFirmwareUpdatesOptions,\n\ttype RebuildRoutesOptions,\n\ttype RebuildRoutesStatus,\n\ttype SDKVersion,\n} from \"./_Types.js\";\nimport { assertProvisioningEntry, isRebuildRoutesTask } from \"./utils.js\";\n\n// Strongly type the event emitter events\ninterface ControllerEventCallbacks\n\textends StatisticsEventCallbacks<ControllerStatistics>\n{\n\t\"inclusion failed\": () => void;\n\t\"exclusion failed\": () => void;\n\t\"inclusion started\": (strategy: InclusionStrategy) => void;\n\t\"exclusion started\": () => void;\n\t\"inclusion stopped\": () => void;\n\t\"exclusion stopped\": () => void;\n\t\"inclusion state changed\": (state: InclusionState) => void;\n\t\"node found\": (node: FoundNode) => void;\n\t\"node added\": (node: ZWaveNode, result: InclusionResult) => void;\n\t\"node removed\": (node: ZWaveNode, reason: RemoveNodeReason) => void;\n\t\"network found\": (homeId: number, ownNodeId: number) => void;\n\t\"joining network failed\": () => void;\n\t\"network joined\": () => void;\n\t\"network left\": () => void;\n\t\"leaving network failed\": () => void;\n\t\"rebuild routes progress\": (\n\t\tprogress: ReadonlyMap<number, RebuildRoutesStatus>,\n\t) => void;\n\t\"rebuild routes done\": (\n\t\tresult: ReadonlyMap<number, RebuildRoutesStatus>,\n\t) => void;\n\t\"firmware update progress\": (\n\t\tprogress: ControllerFirmwareUpdateProgress,\n\t) => void;\n\t\"firmware update finished\": (\n\t\tresult: ControllerFirmwareUpdateResult,\n\t) => void;\n\tidentify: (node: ZWaveNode) => void;\n\t\"status changed\": (status: ControllerStatus) => void;\n}\n\nexport type ControllerEvents = Extract<keyof ControllerEventCallbacks, string>;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ZWaveController extends ControllerStatisticsHost {}\n\n@Mixin([ControllerStatisticsHost])\nexport class ZWaveController\n\textends TypedEventTarget<ControllerEventCallbacks>\n{\n\t/** @internal */\n\tpublic constructor(\n\t\tprivate readonly driver: Driver,\n\t\tbootloaderOnly: boolean = false,\n\t) {\n\t\tsuper();\n\n\t\tthis._nodes = createThrowingMap((nodeId) => {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Node ${nodeId} was not found!`,\n\t\t\t\tZWaveErrorCodes.Controller_NodeNotFound,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t});\n\n\t\t// Limit interaction with the controller in bootloader-only mode\n\t\tif (bootloaderOnly) return;\n\n\t\t// register message handlers\n\t\tdriver.registerRequestHandler(\n\t\t\tFunctionType.AddNodeToNetwork,\n\t\t\tthis.handleAddNodeStatusReport.bind(this),\n\t\t);\n\t\tdriver.registerRequestHandler(\n\t\t\tFunctionType.RemoveNodeFromNetwork,\n\t\t\tthis.handleRemoveNodeStatusReport.bind(this),\n\t\t);\n\t\tdriver.registerRequestHandler(\n\t\t\tFunctionType.ReplaceFailedNode,\n\t\t\tthis.handleReplaceNodeStatusReport.bind(this),\n\t\t);\n\t\tdriver.registerRequestHandler(\n\t\t\tFunctionType.SetLearnMode,\n\t\t\tthis.handleLearnModeCallback.bind(this),\n\t\t);\n\t}\n\n\tprivate _type: MaybeNotKnown<ZWaveLibraryTypes>;\n\tpublic get type(): MaybeNotKnown<ZWaveLibraryTypes> {\n\t\treturn this._type;\n\t}\n\n\tprivate _protocolVersion: MaybeNotKnown<string>;\n\tpublic get protocolVersion(): MaybeNotKnown<string> {\n\t\treturn this._protocolVersion;\n\t}\n\n\tprivate _sdkVersion: MaybeNotKnown<string>;\n\tpublic get sdkVersion(): MaybeNotKnown<string> {\n\t\treturn this._sdkVersion;\n\t}\n\n\tprivate _zwaveApiVersion: MaybeNotKnown<ZWaveApiVersion>;\n\tpublic get zwaveApiVersion(): MaybeNotKnown<ZWaveApiVersion> {\n\t\treturn this._zwaveApiVersion;\n\t}\n\n\tprivate _zwaveChipType: MaybeNotKnown<string | UnknownZWaveChipType>;\n\tpublic get zwaveChipType(): MaybeNotKnown<string | UnknownZWaveChipType> {\n\t\treturn this._zwaveChipType;\n\t}\n\n\tprivate _homeId: MaybeNotKnown<number>;\n\t/** A 32bit number identifying the current network */\n\tpublic get homeId(): MaybeNotKnown<number> {\n\t\treturn this._homeId;\n\t}\n\n\tprivate _ownNodeId: MaybeNotKnown<number>;\n\t/** The ID of the controller in the current network */\n\tpublic get ownNodeId(): MaybeNotKnown<number> {\n\t\treturn this._ownNodeId;\n\t}\n\n\tprivate _dsk: Uint8Array | undefined;\n\t/**\n\t * The device specific key (DSK) of the controller in binary format.\n\t */\n\tpublic get dsk(): Uint8Array {\n\t\tif (this._dsk == undefined) {\n\t\t\tconst keyPair = this.driver.getLearnModeAuthenticatedKeyPair();\n\t\t\tconst publicKey = extractRawECDHPublicKeySync(keyPair.publicKey);\n\t\t\tthis._dsk = publicKey.subarray(0, 16);\n\t\t}\n\t\treturn this._dsk;\n\t}\n\n\t/** @deprecated Use {@link role} instead */\n\tpublic get isPrimary(): MaybeNotKnown<boolean> {\n\t\tswitch (this.role) {\n\t\t\tcase NOT_KNOWN:\n\t\t\t\treturn NOT_KNOWN;\n\t\t\tcase ControllerRole.Primary:\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\t// This seems odd, but isPrimary comes from the Serial API init data command,\n\t// while isSecondary comes from the GetControllerCapabilities command. They don't really do what their name implies\n\t// and sometimes contradict each other...\n\tprivate _isPrimary: MaybeNotKnown<boolean>;\n\tprivate _isSecondary: MaybeNotKnown<boolean>;\n\n\tprivate _isUsingHomeIdFromOtherNetwork: MaybeNotKnown<boolean>;\n\t/** @deprecated Use {@link role} instead */\n\tpublic get isUsingHomeIdFromOtherNetwork(): MaybeNotKnown<boolean> {\n\t\treturn this._isUsingHomeIdFromOtherNetwork;\n\t}\n\n\tprivate _isSISPresent: MaybeNotKnown<boolean>;\n\tpublic get isSISPresent(): MaybeNotKnown<boolean> {\n\t\treturn this._isSISPresent;\n\t}\n\n\tprivate _wasRealPrimary: MaybeNotKnown<boolean>;\n\t/** @deprecated Use {@link role} instead */\n\tpublic get wasRealPrimary(): MaybeNotKnown<boolean> {\n\t\treturn this._wasRealPrimary;\n\t}\n\n\tprivate _isSIS: MaybeNotKnown<boolean>;\n\tpublic get isSIS(): MaybeNotKnown<boolean> {\n\t\treturn this._isSIS;\n\t}\n\n\tprivate _isSUC: MaybeNotKnown<boolean>;\n\tpublic get isSUC(): MaybeNotKnown<boolean> {\n\t\treturn this._isSUC;\n\t}\n\n\tprivate _noNodesIncluded: MaybeNotKnown<boolean>;\n\n\tprivate _nodeType: MaybeNotKnown<NodeType>;\n\tpublic get nodeType(): MaybeNotKnown<NodeType> {\n\t\treturn this._nodeType;\n\t}\n\n\t/** Checks if the SDK version is greater than the given one */\n\tpublic sdkVersionGt(version: SDKVersion): MaybeNotKnown<boolean> {\n\t\treturn sdkVersionGt(this._sdkVersion, version);\n\t}\n\n\t/** Checks if the SDK version is greater than or equal to the given one */\n\tpublic sdkVersionGte(version: SDKVersion): MaybeNotKnown<boolean> {\n\t\treturn sdkVersionGte(this._sdkVersion, version);\n\t}\n\n\t/** Checks if the SDK version is lower than the given one */\n\tpublic sdkVersionLt(version: SDKVersion): MaybeNotKnown<boolean> {\n\t\treturn sdkVersionLt(this._sdkVersion, version);\n\t}\n\n\t/** Checks if the SDK version is lower than or equal to the given one */\n\tpublic sdkVersionLte(version: SDKVersion): MaybeNotKnown<boolean> {\n\t\treturn sdkVersionLte(this._sdkVersion, version);\n\t}\n\n\tprivate _manufacturerId: MaybeNotKnown<number>;\n\tpublic get manufacturerId(): MaybeNotKnown<number> {\n\t\treturn this._manufacturerId;\n\t}\n\n\tprivate _productType: MaybeNotKnown<number>;\n\tpublic get productType(): MaybeNotKnown<number> {\n\t\treturn this._productType;\n\t}\n\n\tprivate _productId: MaybeNotKnown<number>;\n\tpublic get productId(): MaybeNotKnown<number> {\n\t\treturn this._productId;\n\t}\n\n\tprivate _firmwareVersion: MaybeNotKnown<string>;\n\tpublic get firmwareVersion(): MaybeNotKnown<string> {\n\t\treturn this._firmwareVersion;\n\t}\n\n\tprivate _supportedFunctionTypes: MaybeNotKnown<FunctionType[]>;\n\tpublic get supportedFunctionTypes(): MaybeNotKnown<\n\t\treadonly FunctionType[]\n\t> {\n\t\treturn this._supportedFunctionTypes;\n\t}\n\n\tprivate _status: ControllerStatus = ControllerStatus.Ready;\n\t/**\n\t * Which status the controller is believed to be in\n\t */\n\tpublic get status(): ControllerStatus {\n\t\treturn this._status;\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tpublic setStatus(newStatus: ControllerStatus): void {\n\t\t// Ignore duplicate events\n\t\tif (newStatus === this._status) return;\n\n\t\tconst oldStatus = this._status;\n\t\tthis._status = newStatus;\n\n\t\tif (newStatus === ControllerStatus.Jammed) {\n\t\t\tthis.driver.controllerLog.print(\"The controller is jammed\", \"warn\");\n\t\t} else if (newStatus === ControllerStatus.Unresponsive) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\"The controller is unresponsive\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t} else if (newStatus === ControllerStatus.Ready) {\n\t\t\tif (oldStatus === ControllerStatus.Jammed) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"The controller is no longer jammed\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t} else if (oldStatus === ControllerStatus.Unresponsive) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"The controller is no longer unresponsive\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\"The controller is ready\");\n\t\t\t}\n\t\t}\n\n\t\tthis.emit(\"status changed\", newStatus);\n\t}\n\n\t/**\n\t * Checks if a given Z-Wave function type is supported by this controller.\n\t * Returns `NOT_KNOWN`/`undefined` if this information isn't known yet.\n\t */\n\tpublic isFunctionSupported(\n\t\tfunctionType: FunctionType,\n\t): MaybeNotKnown<boolean> {\n\t\tif (!this._supportedFunctionTypes) return NOT_KNOWN;\n\t\treturn this._supportedFunctionTypes.includes(functionType);\n\t}\n\n\tprivate _supportedSerialAPISetupCommands:\n\t\t| SerialAPISetupCommand[]\n\t\t| undefined;\n\tpublic get supportedSerialAPISetupCommands():\n\t\t| readonly SerialAPISetupCommand[]\n\t\t| undefined\n\t{\n\t\treturn this._supportedSerialAPISetupCommands;\n\t}\n\n\t/**\n\t * Checks if a given Serial API setup command is supported by this controller.\n\t * Returns `NOT_KNOWN`/`undefined` if this information isn't known yet.\n\t */\n\tpublic isSerialAPISetupCommandSupported(\n\t\tcommand: SerialAPISetupCommand,\n\t): MaybeNotKnown<boolean> {\n\t\tif (!this._supportedSerialAPISetupCommands) return NOT_KNOWN;\n\t\treturn this._supportedSerialAPISetupCommands.includes(command);\n\t}\n\n\t/**\n\t * Tests if the controller supports a certain feature.\n\t * Returns `undefined` if this information isn't known yet.\n\t */\n\tpublic supportsFeature(feature: ZWaveFeature): MaybeNotKnown<boolean> {\n\t\tswitch (feature) {\n\t\t\tcase ZWaveFeature.SmartStart:\n\t\t\t\treturn this.sdkVersionGte(minFeatureVersions[feature]);\n\t\t}\n\t}\n\n\t/** Throws if the controller does not support a certain feature */\n\tprivate assertFeature(feature: ZWaveFeature): void {\n\t\tif (!this.supportsFeature(feature)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The controller does not support the ${\n\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\tZWaveFeature,\n\t\t\t\t\t\tfeature,\n\t\t\t\t\t)\n\t\t\t\t} feature`,\n\t\t\t\tZWaveErrorCodes.Controller_NotSupported,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate _sucNodeId: MaybeNotKnown<number>;\n\tpublic get sucNodeId(): MaybeNotKnown<number> {\n\t\treturn this._sucNodeId;\n\t}\n\n\tprivate _supportsTimers: MaybeNotKnown<boolean>;\n\tpublic get supportsTimers(): MaybeNotKnown<boolean> {\n\t\treturn this._supportsTimers;\n\t}\n\n\tprivate _supportedRegions: MaybeNotKnown<Map<RFRegion, RFRegionInfo>>;\n\t/** Which RF regions are supported by the controller, including information about them */\n\tpublic get supportedRegions(): MaybeNotKnown<\n\t\tReadonlyMap<RFRegion, Readonly<RFRegionInfo>>\n\t> {\n\t\treturn this._supportedRegions;\n\t}\n\n\tprivate _rfRegion: MaybeNotKnown<RFRegion>;\n\t/** Which RF region the controller is currently set to, or `undefined` if it could not be determined (yet). This value is cached and can be changed through {@link setRFRegion}. */\n\tpublic get rfRegion(): MaybeNotKnown<RFRegion> {\n\t\treturn this._rfRegion;\n\t}\n\n\tprivate _supportsLongRange: MaybeNotKnown<boolean>;\n\t/** Whether the controller supports the Z-Wave Long Range protocol */\n\tpublic get supportsLongRange(): MaybeNotKnown<boolean> {\n\t\treturn this._supportsLongRange;\n\t}\n\n\tprivate _maxLongRangePowerlevel: MaybeNotKnown<number>;\n\t/** The maximum powerlevel to use for Z-Wave Long Range, or `undefined` if it could not be determined (yet). This value is cached and can be changed through {@link setMaxLongRangePowerlevel}. */\n\tpublic get maxLongRangePowerlevel(): MaybeNotKnown<number> {\n\t\treturn this._maxLongRangePowerlevel;\n\t}\n\n\tprivate _longRangeChannel: MaybeNotKnown<LongRangeChannel>;\n\t/** The channel to use for Z-Wave Long Range, or `undefined` if it could not be determined (yet). This value is cached and can be changed through {@link setLongRangeChannel}. */\n\tpublic get longRangeChannel(): MaybeNotKnown<LongRangeChannel> {\n\t\treturn this._longRangeChannel;\n\t}\n\n\tprivate _supportsLongRangeAutoChannelSelection: MaybeNotKnown<boolean>;\n\t/** Whether automatic LR channel selection is supported, or `undefined` if it could not be determined (yet). */\n\tpublic get supportsLongRangeAutoChannelSelection(): MaybeNotKnown<boolean> {\n\t\treturn this._supportsLongRangeAutoChannelSelection;\n\t}\n\n\tprivate _maxPayloadSize: MaybeNotKnown<number>;\n\t/** The maximum payload size that can be transmitted with a Z-Wave explorer frame */\n\tpublic get maxPayloadSize(): MaybeNotKnown<number> {\n\t\treturn this._maxPayloadSize;\n\t}\n\n\tprivate _maxPayloadSizeLR: MaybeNotKnown<number>;\n\t/** The maximum payload size that can be transmitted with a Z-Wave Long Range frame */\n\tpublic get maxPayloadSizeLR(): MaybeNotKnown<number> {\n\t\treturn this._maxPayloadSizeLR;\n\t}\n\n\tprivate _nodes: ThrowingMap<number, ZWaveNode>;\n\t/** A dictionary of the nodes connected to this controller */\n\tpublic get nodes(): ReadonlyThrowingMap<number, ZWaveNode> {\n\t\treturn this._nodes;\n\t}\n\n\tprivate _nodeIdType: NodeIDType = NodeIDType.Short;\n\t/** Whether the controller is configured to use 8 or 16 bit node IDs */\n\tpublic get nodeIdType(): NodeIDType {\n\t\treturn this._nodeIdType;\n\t}\n\t/** @internal */\n\tpublic set nodeIdType(value: NodeIDType) {\n\t\tthis._nodeIdType = value;\n\t}\n\n\t/** Returns the node with the given DSK */\n\tpublic getNodeByDSK(dsk: Uint8Array | string): ZWaveNode | undefined {\n\t\ttry {\n\t\t\tif (typeof dsk === \"string\") dsk = dskFromString(dsk);\n\t\t} catch (e) {\n\t\t\t// Return undefined if the DSK is invalid\n\t\t\tif (\n\t\t\t\tisZWaveError(e) && e.code === ZWaveErrorCodes.Argument_Invalid\n\t\t\t) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t\tfor (const node of this._nodes.values()) {\n\t\t\tif (node.dsk && Bytes.view(node.dsk).equals(dsk)) return node;\n\t\t}\n\t}\n\n\t/** Returns the controller node's value DB */\n\tpublic get valueDB(): ValueDB {\n\t\treturn this._nodes.get(this._ownNodeId!)!.valueDB;\n\t}\n\n\t/** @internal Which associations are currently configured */\n\tpublic get associations(): readonly AssociationAddress[] {\n\t\treturn (\n\t\t\tthis.driver.cacheGet(cacheKeys.controller.associations(1)) ?? []\n\t\t);\n\t}\n\n\t/** @internal */\n\tpublic set associations(value: readonly AssociationAddress[]) {\n\t\tthis.driver.cacheSet(cacheKeys.controller.associations(1), value);\n\t}\n\n\tprivate _powerlevel: { powerlevel: Powerlevel; until: Date } | undefined;\n\t/**\n\t * @internal\n\t * Remembers which powerlevel was set by another node.\n\t */\n\tpublic get powerlevel(): { powerlevel: Powerlevel; until: Date } {\n\t\treturn this._powerlevel ?? {\n\t\t\tpowerlevel: Powerlevel[\"Normal Power\"],\n\t\t\tuntil: new Date(),\n\t\t};\n\t}\n\n\t/** @internal */\n\tpublic set powerlevel(value: { powerlevel: Powerlevel; until: Date }) {\n\t\tthis._powerlevel = value;\n\t}\n\n\t/** The role of the controller on the network */\n\tpublic get role(): MaybeNotKnown<ControllerRole> {\n\t\tif (this._wasRealPrimary) return ControllerRole.Primary;\n\t\t// Ideally we'd rely on wasRealPrimary, but there are some older controllers out there where this flag isn't set.\n\t\tif (this._isPrimary && this._isSIS && this._isSecondary === false) {\n\t\t\treturn ControllerRole.Primary;\n\t\t}\n\n\t\tswitch (this._isSecondary) {\n\t\t\tcase true:\n\t\t\t\treturn ControllerRole.Secondary;\n\t\t\tcase false:\n\t\t\t\treturn ControllerRole.Inclusion;\n\t\t\tdefault:\n\t\t\t\treturn NOT_KNOWN;\n\t\t}\n\t}\n\n\t/** Returns whether learn mode may be enabled on this controller */\n\tpublic get isLearnModePermitted(): boolean {\n\t\t// The primary controller may only enter learn mode, if hasn't included nodes yet\n\t\tif (this.role === ControllerRole.Primary) {\n\t\t\treturn !!this._noNodesIncluded;\n\t\t} else {\n\t\t\t// Secondary controllers may only enter learn mode if they are not the SUC\n\t\t\treturn this._isSUC === false;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Remembers the indicator values set by another node\n\t */\n\tpublic readonly indicatorValues = new Map<number, IndicatorObject[]>();\n\n\t/** Returns whether the routes are currently being rebuilt for one or more nodes. */\n\tpublic get isRebuildingRoutes(): boolean {\n\t\treturn !!this.driver.scheduler.findTask(isRebuildRoutesTask);\n\t}\n\n\t/**\n\t * Returns a reference to the (virtual) broadcast node, which allows sending commands to all nodes.\n\t * This automatically groups nodes by security class, ignores nodes that cannot be controlled via multicast/broadcast, and will fall back to multicast(s) if necessary.\n\t */\n\tpublic getBroadcastNode(): VirtualNode {\n\t\treturn new VirtualNode(\n\t\t\tNODE_ID_BROADCAST,\n\t\t\tthis.driver,\n\t\t\tthis.nodes.values(),\n\t\t);\n\t}\n\n\t/**\n\t * Returns a reference to the (virtual) broadcast node for Z-Wave Long Range, which allows sending commands to all LR nodes.\n\t * This automatically groups nodes by security class, ignores nodes that cannot be controlled via multicast/broadcast, and will fall back to multicast(s) if necessary.\n\t */\n\tpublic getBroadcastNodeLR(): VirtualNode {\n\t\treturn new VirtualNode(\n\t\t\tNODE_ID_BROADCAST_LR,\n\t\t\tthis.driver,\n\t\t\tthis.nodes.values(),\n\t\t);\n\t}\n\n\t/**\n\t * Creates a virtual node that can be used to send one or more multicast commands to several nodes.\n\t * This automatically groups nodes by security class and ignores nodes that cannot be controlled via multicast.\n\t */\n\tpublic getMulticastGroup(nodeIDs: number[]): VirtualNode {\n\t\tif (nodeIDs.length === 0) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Cannot create an empty multicast group\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tconst firstNodeIsLR = isLongRangeNodeId(nodeIDs[0]);\n\t\tif (nodeIDs.some((id) => isLongRangeNodeId(id) !== firstNodeIsLR)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Cannot create a multicast group with mixed Z-Wave Classic and Z-Wave Long Range nodes\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tconst nodes = nodeIDs.map((id) => this._nodes.getOrThrow(id));\n\t\treturn new VirtualNode(undefined, this.driver, nodes);\n\t}\n\n\t/** @internal */\n\tpublic get provisioningList(): readonly SmartStartProvisioningEntry[] {\n\t\treturn (\n\t\t\tthis.driver.cacheGet(cacheKeys.controller.provisioningList) ?? []\n\t\t);\n\t}\n\tprivate set provisioningList(\n\t\tvalue: readonly SmartStartProvisioningEntry[],\n\t) {\n\t\tthis.driver.cacheSet(cacheKeys.controller.provisioningList, value);\n\t}\n\n\t/** Adds the given entry (DSK and security classes) to the controller's SmartStart provisioning list or replaces an existing entry */\n\tpublic provisionSmartStartNode(entry: PlannedProvisioningEntry): void {\n\t\t// Make sure the controller supports SmartStart\n\t\tthis.assertFeature(ZWaveFeature.SmartStart);\n\n\t\t// And that the entry contains valid data\n\t\tassertProvisioningEntry(entry);\n\n\t\tconst provisioningList = [...this.provisioningList];\n\t\tconst index = provisioningList.findIndex((e) => e.dsk === entry.dsk);\n\t\tif (index === -1) {\n\t\t\tprovisioningList.push(entry);\n\t\t} else {\n\t\t\tprovisioningList[index] = entry;\n\t\t}\n\t\tthis.provisioningList = provisioningList;\n\n\t\tthis.autoProvisionSmartStart();\n\t}\n\n\t/**\n\t * Removes the given DSK or node ID from the controller's SmartStart provisioning list.\n\t *\n\t * **Note:** If this entry corresponds to an included node, it will **NOT** be excluded\n\t */\n\tpublic unprovisionSmartStartNode(dskOrNodeId: string | number): void {\n\t\tconst provisioningList = [...this.provisioningList];\n\n\t\tconst entry = this.getProvisioningEntryInternal(dskOrNodeId);\n\t\tif (!entry) return;\n\n\t\tconst index = provisioningList.indexOf(entry);\n\t\tif (index >= 0) {\n\t\t\tprovisioningList.splice(index, 1);\n\t\t\tthis.provisioningList = provisioningList;\n\n\t\t\tthis.autoProvisionSmartStart();\n\t\t}\n\t}\n\n\tprivate getProvisioningEntryInternal(\n\t\tdskOrNodeId: string | number,\n\t): SmartStartProvisioningEntry | undefined {\n\t\tif (typeof dskOrNodeId === \"string\") {\n\t\t\treturn this.provisioningList.find((e) => e.dsk === dskOrNodeId);\n\t\t} else {\n\t\t\t// The provisioning list may or may not contain the node ID for an entry, even if the node is already included.\n\t\t\tlet ret = this.provisioningList.find(\n\t\t\t\t(e) => \"nodeId\" in e && e.nodeId === dskOrNodeId,\n\t\t\t);\n\t\t\tif (!ret) {\n\t\t\t\t// Try to get the DSK from the node instance\n\t\t\t\tconst dsk = this.nodes.get(dskOrNodeId)?.dsk;\n\t\t\t\tif (dsk) {\n\t\t\t\t\tret = this.provisioningList.find(\n\t\t\t\t\t\t(e) => e.dsk === dskToString(dsk),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the entry for the given DSK or node ID from the controller's SmartStart provisioning list.\n\t */\n\tpublic getProvisioningEntry(\n\t\tdskOrNodeId: string | number,\n\t): Readonly<SmartStartProvisioningEntry> | undefined {\n\t\tconst entry = this.getProvisioningEntryInternal(dskOrNodeId);\n\t\t// Try to look up the node ID for this entry\n\t\tif (entry) {\n\t\t\tconst ret: SmartStartProvisioningEntry = {\n\t\t\t\t...entry,\n\t\t\t};\n\t\t\tconst node = typeof dskOrNodeId === \"string\"\n\t\t\t\t? this.getNodeByDSK(dskOrNodeId)\n\t\t\t\t: this.nodes.get(dskOrNodeId);\n\t\t\tif (node) ret.nodeId = node.id;\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all entries from the controller's SmartStart provisioning list.\n\t */\n\tpublic getProvisioningEntries(): SmartStartProvisioningEntry[] {\n\t\t// Determine which DSKs belong to which node IDs\n\t\tconst dskNodeMap = new Map<string, number>();\n\t\tfor (const node of this.nodes.values()) {\n\t\t\tif (node.dsk) dskNodeMap.set(dskToString(node.dsk), node.id);\n\t\t}\n\t\t// Make copies so no one can modify the internal list (except for user info)\n\t\treturn this.provisioningList.map((e) => {\n\t\t\tconst { dsk, securityClasses, nodeId, ...rest } = e;\n\t\t\treturn {\n\t\t\t\tdsk,\n\t\t\t\tsecurityClasses: [...securityClasses],\n\t\t\t\t...(dskNodeMap.has(dsk)\n\t\t\t\t\t? { nodeId: dskNodeMap.get(dsk)! }\n\t\t\t\t\t: {}),\n\t\t\t\t...rest,\n\t\t\t};\n\t\t});\n\t}\n\n\t/** Returns whether the SmartStart provisioning list contains active entries that have not been included yet */\n\tpublic hasPlannedProvisioningEntries(): boolean {\n\t\treturn this.provisioningList.some(\n\t\t\t(e) =>\n\t\t\t\t(e.status == undefined\n\t\t\t\t\t|| e.status === ProvisioningEntryStatus.Active)\n\t\t\t\t&& !this.getNodeByDSK(e.dsk),\n\t\t);\n\t}\n\n\t/**\n\t * @internal\n\t * Automatically starts smart start inclusion if there are nodes pending inclusion.\n\t */\n\tpublic autoProvisionSmartStart(): void {\n\t\t// Make sure the controller supports SmartStart\n\t\tif (!this.supportsFeature(ZWaveFeature.SmartStart)) return;\n\n\t\tif (this.hasPlannedProvisioningEntries()) {\n\t\t\t// SmartStart should be enabled\n\t\t\tvoid this.enableSmartStart().catch(noop);\n\t\t} else {\n\t\t\t// SmartStart should be disabled\n\t\t\tvoid this.disableSmartStart().catch(noop);\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Queries the controller / serial API capabilities.\n\t * Returns a list of Z-Wave classic node IDs that are currently in the network.\n\t */\n\tpublic async queryCapabilities(): Promise<{ nodeIds: readonly number[] }> {\n\t\t// Figure out what the serial API can do\n\t\tthis.driver.controllerLog.print(`querying Serial API capabilities...`);\n\t\tconst apiCaps = await this.driver.sendMessage<\n\t\t\tGetSerialApiCapabilitiesResponse\n\t\t>(\n\t\t\tnew GetSerialApiCapabilitiesRequest(),\n\t\t\t{\n\t\t\t\tsupportCheck: false,\n\t\t\t},\n\t\t);\n\t\tthis._firmwareVersion = apiCaps.firmwareVersion;\n\t\tthis._manufacturerId = apiCaps.manufacturerId;\n\t\tthis._productType = apiCaps.productType;\n\t\tthis._productId = apiCaps.productId;\n\t\tthis._supportedFunctionTypes = apiCaps.supportedFunctionTypes;\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received API capabilities:\n firmware version: ${this._firmwareVersion}\n manufacturer ID: ${num2hex(this._manufacturerId)}\n product type: ${num2hex(this._productType)}\n product ID: ${num2hex(this._productId)}\n supported functions: ${\n\t\t\t\tthis._supportedFunctionTypes\n\t\t\t\t\t.map((fn) => `\\n \u00B7 ${FunctionType[fn]} (${num2hex(fn)})`)\n\t\t\t\t\t.join(\"\")\n\t\t\t}`,\n\t\t);\n\n\t\t// Request additional information about the controller/Z-Wave chip\n\t\tconst initData = await this.getSerialApiInitData();\n\n\t\t// Get basic controller version info\n\t\tthis.driver.controllerLog.print(`querying version info...`);\n\t\tconst version = await this.driver.sendMessage<\n\t\t\tGetControllerVersionResponse\n\t\t>(\n\t\t\tnew GetControllerVersionRequest(),\n\t\t\t{\n\t\t\t\tsupportCheck: false,\n\t\t\t},\n\t\t);\n\t\tthis._protocolVersion = version.libraryVersion;\n\t\tthis._type = version.controllerType;\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received version info:\n controller type: ${getEnumMemberName(ZWaveLibraryTypes, this._type)}\n library version: ${this._protocolVersion}`,\n\t\t);\n\n\t\t// If supported, get more fine-grained version info\n\t\tif (this.isFunctionSupported(FunctionType.GetProtocolVersion)) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`querying protocol version info...`,\n\t\t\t);\n\t\t\tconst protocol = await this.driver.sendMessage<\n\t\t\t\tGetProtocolVersionResponse\n\t\t\t>(\n\t\t\t\tnew GetProtocolVersionRequest(),\n\t\t\t);\n\n\t\t\tthis._protocolVersion = protocol.protocolVersion;\n\n\t\t\tlet message = `received protocol version info:\n protocol type: ${\n\t\t\t\tgetEnumMemberName(\n\t\t\t\t\tProtocolType,\n\t\t\t\t\tprotocol.protocolType,\n\t\t\t\t)\n\t\t\t}\n protocol version: ${protocol.protocolVersion}`;\n\t\t\tif (protocol.applicationFrameworkBuildNumber) {\n\t\t\t\tmessage += `\n appl. framework build no.: ${protocol.applicationFrameworkBuildNumber}`;\n\t\t\t}\n\t\t\tif (protocol.gitCommitHash) {\n\t\t\t\tmessage += `\n git commit hash: ${protocol.gitCommitHash}`;\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.print(message);\n\t\t}\n\n\t\t// The SDK version cannot be queried directly, but we can deduce it from the protocol version\n\t\tthis._sdkVersion = protocolVersionToSDKVersion(this._protocolVersion);\n\n\t\t// find out what the controller can do\n\t\tawait this.getControllerCapabilities();\n\n\t\t// If the serial API can be configured, figure out which sub commands are supported\n\t\t// This MUST be done after querying the SDK version due to a bug in some 7.xx firmwares, which incorrectly encode the bitmask\n\t\tif (this.isFunctionSupported(FunctionType.SerialAPISetup)) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`querying serial API setup capabilities...`,\n\t\t\t);\n\t\t\tconst setupCaps = await this.driver.sendMessage<\n\t\t\t\tSerialAPISetup_GetSupportedCommandsResponse\n\t\t\t>(\n\t\t\t\tnew SerialAPISetup_GetSupportedCommandsRequest(),\n\t\t\t);\n\t\t\tthis._supportedSerialAPISetupCommands = setupCaps.supportedCommands;\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`supported serial API setup commands:${\n\t\t\t\t\tthis._supportedSerialAPISetupCommands\n\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t(cmd) =>\n\t\t\t\t\t\t\t\t`\\n\u00B7 ${\n\t\t\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\t\t\tSerialAPISetupCommand,\n\t\t\t\t\t\t\t\t\t\tcmd,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}`,\n\t\t\t);\n\t\t} else {\n\t\t\tthis._supportedSerialAPISetupCommands = [];\n\t\t}\n\n\t\t// Figure out the maximum payload size for outgoing commands\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetMaximumPayloadSize,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(`querying max. payload size...`);\n\t\t\tthis._maxPayloadSize = await this.getMaxPayloadSize();\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`maximum payload size: ${this._maxPayloadSize} bytes`,\n\t\t\t);\n\t\t}\n\n\t\t// On older controllers with soft-reset disabled, supportsLongRange is not automatically reported by the controller\n\t\t// so we should set it manually\n\t\tif (!this.isLongRangeCapable()) {\n\t\t\tthis._supportsLongRange = false;\n\t\t\tthis._supportsLongRangeAutoChannelSelection = false;\n\t\t}\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`supported Z-Wave features: ${\n\t\t\t\tObject.keys(ZWaveFeature)\n\t\t\t\t\t.filter((k) => /^\\d+$/.test(k))\n\t\t\t\t\t.map((k) => parseInt(k) as ZWaveFeature)\n\t\t\t\t\t.filter((feat) => this.supportsFeature(feat))\n\t\t\t\t\t.map((feat) =>\n\t\t\t\t\t\t`\\n \u00B7 ${getEnumMemberName(ZWaveFeature, feat)}`\n\t\t\t\t\t)\n\t\t\t\t\t.join(\"\")\n\t\t\t}`,\n\t\t);\n\n\t\treturn {\n\t\t\tnodeIds: initData.nodeIds,\n\t\t};\n\t}\n\n\t/**\n\t * @internal\n\t * Queries the controller's capabilities in regards to Z-Wave Long Range.\n\t * Returns the list of Long Range node IDs\n\t */\n\tpublic async queryLongRangeCapabilities(): Promise<{\n\t\tlrNodeIds: readonly number[];\n\t}> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`querying Z-Wave Long Range capabilities...`,\n\t\t);\n\n\t\t// Fetch the list of Long Range nodes\n\t\tconst lrNodeIds = await this.getLongRangeNodes();\n\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetLongRangeMaximumPayloadSize,\n\t\t\t)\n\t\t) {\n\t\t\tthis._maxPayloadSizeLR = await this.getMaxPayloadSizeLongRange();\n\t\t}\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received Z-Wave Long Range capabilities:\n max. payload size: ${this._maxPayloadSizeLR} bytes\n nodes: ${lrNodeIds.join(\", \")}`,\n\t\t);\n\n\t\treturn {\n\t\t\tlrNodeIds,\n\t\t};\n\t}\n\n\tprivate isLongRangeCapable(): MaybeNotKnown<boolean> {\n\t\t// Z-Wave Long Range is supported if the controller supports changing the node ID type to 16 bit\n\t\t// FIXME: Consider using the ZWaveFeature enum for this instead\n\t\treturn this.isSerialAPISetupCommandSupported(\n\t\t\tSerialAPISetupCommand.SetNodeIDType,\n\t\t);\n\t}\n\n\t/** Tries to determine the LR capable replacement of the given region. If none is found, the given region is returned. */\n\tprivate tryGetLRCapableRegion(region: RFRegion): RFRegion {\n\t\tif (this._supportedRegions) {\n\t\t\t// If the region supports LR, use it\n\t\t\tif (this._supportedRegions.get(region)?.supportsLongRange) {\n\t\t\t\treturn region;\n\t\t\t}\n\n\t\t\t// Find a possible LR capable superset for this region\n\t\t\tfor (const info of this._supportedRegions.values()) {\n\t\t\t\tif (info.supportsLongRange && info.includesRegion === region) {\n\t\t\t\t\treturn info.region;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// US_LR is the first supported LR region, so if the controller supports LR, US_LR is supported\n\t\tif (region === RFRegion.USA && this.isLongRangeCapable()) {\n\t\t\treturn RFRegion[\"USA (Long Range)\"];\n\t\t}\n\n\t\treturn region;\n\t}\n\n\t/**\n\t * @internal\n\t * Queries the region and powerlevel settings and configures them if necessary\n\t */\n\tpublic async queryAndConfigureRF(): Promise<void> {\n\t\t// Figure out which regions are supported\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetSupportedRegions,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Querying supported RF regions and their information...`,\n\t\t\t);\n\t\t\tconst supportedRegions = await this.querySupportedRFRegions().catch(\n\t\t\t\t() => [],\n\t\t\t);\n\t\t\tthis._supportedRegions = new Map();\n\n\t\t\tfor (const region of supportedRegions) {\n\t\t\t\ttry {\n\t\t\t\t\tconst info = await this.queryRFRegionInfo(region);\n\t\t\t\t\tif (info.region === RFRegion.Unknown) continue;\n\t\t\t\t\tthis._supportedRegions.set(region, info);\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`supported regions:${\n\t\t\t\t\t[...this._supportedRegions.values()]\n\t\t\t\t\t\t.map((info) => {\n\t\t\t\t\t\t\tlet ret = `\\n\u00B7 ${\n\t\t\t\t\t\t\t\tgetEnumMemberName(RFRegion, info.region)\n\t\t\t\t\t\t\t}`;\n\t\t\t\t\t\t\tif (info.includesRegion != undefined) {\n\t\t\t\t\t\t\t\tret += ` \u00B7 superset of ${\n\t\t\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\t\t\tRFRegion,\n\t\t\t\t\t\t\t\t\t\tinfo.includesRegion,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (info.supportsLongRange) {\n\t\t\t\t\t\t\t\tret += \" \u00B7 ZWLR\";\n\t\t\t\t\t\t\t\tif (!info.supportsZWave) {\n\t\t\t\t\t\t\t\t\tret += \" only\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn ret;\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}`,\n\t\t\t);\n\t\t}\n\n\t\t// Check and possibly update the RF region to the desired value\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetRFRegion,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(`Querying configured RF region...`);\n\t\t\tconst resp = await this.getRFRegion().catch(() => undefined);\n\t\t\tif (resp != undefined) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`The controller is using RF region ${\n\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\tRFRegion,\n\t\t\t\t\t\t\tresp,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Querying the RF region failed!`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tlet desiredRFRegion: RFRegion | undefined;\n\t\t// If the user has set a region in the options, use that\n\t\tif (this.driver.options.rf?.region != undefined) {\n\t\t\tdesiredRFRegion = this.driver.options.rf.region;\n\t\t}\n\t\t// Unless preferring LR regions is disabled, try to find a suitable replacement region\n\t\tif (this.driver.options.rf?.preferLRRegion !== false) {\n\t\t\tdesiredRFRegion ??= this.rfRegion;\n\t\t\tif (desiredRFRegion != undefined) {\n\t\t\t\tdesiredRFRegion = this.tryGetLRCapableRegion(desiredRFRegion);\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.SetRFRegion,\n\t\t\t)\n\t\t\t&& desiredRFRegion != undefined\n\t\t\t&& this.rfRegion != desiredRFRegion\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Current RF region (${\n\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\tRFRegion,\n\t\t\t\t\t\tthis.rfRegion ?? RFRegion.Unknown,\n\t\t\t\t\t)\n\t\t\t\t}) differs from desired region (${\n\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\tRFRegion,\n\t\t\t\t\t\tdesiredRFRegion,\n\t\t\t\t\t)\n\t\t\t\t}), configuring it...`,\n\t\t\t);\n\t\t\tconst resp = await this.setRFRegionInternal(\n\t\t\t\tdesiredRFRegion,\n\t\t\t\t// Do not soft reset here, we'll do it later\n\t\t\t\tfalse,\n\t\t\t).catch((e) => (e as Error).message);\n\t\t\tif (resp === true) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Changed RF region to ${\n\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\tRFRegion,\n\t\t\t\t\t\t\tdesiredRFRegion,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Changing the RF region failed!${\n\t\t\t\t\t\tresp ? ` Reason: ${resp}` : \"\"\n\t\t\t\t\t}`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Check and possibly update the powerlevel settings\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetPowerlevel,\n\t\t\t)\n\t\t\t&& this.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.SetPowerlevel,\n\t\t\t)\n\t\t\t&& this.driver.options.rf?.txPower != undefined\n\t\t) {\n\t\t\tconst desired = this.driver.options.rf.txPower;\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Querying configured powerlevel...`,\n\t\t\t);\n\t\t\tconst current = await this.getPowerlevel().catch(() => undefined);\n\t\t\tif (current != undefined) {\n\t\t\t\tif (\n\t\t\t\t\tcurrent.powerlevel !== desired.powerlevel\n\t\t\t\t\t|| current.measured0dBm !== desired.measured0dBm\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t`Current powerlevel ${current.powerlevel} dBm (${current.measured0dBm} dBm) differs from desired powerlevel ${desired.powerlevel} dBm (${desired.measured0dBm} dBm), configuring it...`,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst resp = await this.setPowerlevel(\n\t\t\t\t\t\tdesired.powerlevel,\n\t\t\t\t\t\tdesired.measured0dBm,\n\t\t\t\t\t).catch((e) => (e as Error).message);\n\t\t\t\t\tif (resp === true) {\n\t\t\t\t\t\tthis.driver.controllerLog.print(`Powerlevel updated`);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t\t`Changing the powerlevel failed!${\n\t\t\t\t\t\t\t\tresp ? ` Reason: ${resp}` : \"\"\n\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Querying the powerlevel failed!`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Check and possibly update the Long Range powerlevel settings\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetLongRangeMaximumTxPower,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Querying configured max. Long Range powerlevel...`,\n\t\t\t);\n\t\t\tconst resp = await this.getMaxLongRangePowerlevel().catch(() =>\n\t\t\t\tundefined\n\t\t\t);\n\t\t\tif (resp != undefined) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`The max. LR powerlevel is ${resp.toFixed(1)} dBm`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Querying the max. Long Range powerlevel failed!`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.SetLongRangeMaximumTxPower,\n\t\t\t)\n\t\t\t&& this.driver.options.rf?.maxLongRangePowerlevel != undefined\n\t\t\t&& this.maxLongRangePowerlevel\n\t\t\t\t!== this.driver.options.rf.maxLongRangePowerlevel\n\t\t) {\n\t\t\tconst desired = this.driver.options.rf.maxLongRangePowerlevel;\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Current max. Long Range powerlevel ${\n\t\t\t\t\tthis.maxLongRangePowerlevel?.toFixed(1)\n\t\t\t\t} dBm differs from desired powerlevel ${desired} dBm, configuring it...`,\n\t\t\t);\n\n\t\t\tconst resp = await this.setMaxLongRangePowerlevel(desired)\n\t\t\t\t.catch((e) => (e as Error).message);\n\t\t\tif (resp === true) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`max. Long Range powerlevel updated`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Changing the max. Long Range powerlevel failed!${\n\t\t\t\t\t\tresp ? ` Reason: ${resp}` : \"\"\n\t\t\t\t\t}`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Check and possibly update the Long Range channel settings\n\t\tif (\n\t\t\tthis.isFunctionSupported(FunctionType.GetLongRangeChannel)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Querying configured Long Range channel information...`,\n\t\t\t);\n\t\t\tconst resp = await this.getLongRangeChannel().catch(() =>\n\t\t\t\tundefined\n\t\t\t);\n\t\t\tif (resp != undefined) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`received Z-Wave Long Range channel information:\n channel: ${\n\t\t\t\t\t\tresp.channel != undefined\n\t\t\t\t\t\t\t? getEnumMemberName(LongRangeChannel, resp.channel)\n\t\t\t\t\t\t\t: \"(unknown)\"\n\t\t\t\t\t}\n supports auto channel selection: ${resp.supportsAutoChannelSelection}\n`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Querying the Long Range channel information failed!`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tthis._supportsLongRangeAutoChannelSelection = false;\n\t\t}\n\t\tif (\n\t\t\tthis.isFunctionSupported(FunctionType.SetLongRangeChannel)\n\t\t\t&& this.driver.options.rf?.longRangeChannel != undefined\n\t\t\t&& this.longRangeChannel\n\t\t\t\t!== this.driver.options.rf.longRangeChannel\n\t\t) {\n\t\t\tconst desired = this.driver.options.rf.longRangeChannel;\n\t\t\tif (\n\t\t\t\tdesired === LongRangeChannel.Auto\n\t\t\t\t&& !this._supportsLongRangeAutoChannelSelection\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Cannot set desired LR channel to Auto because the controller does not support it!`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Current LR channel ${\n\t\t\t\t\t\tthis.longRangeChannel != undefined\n\t\t\t\t\t\t\t? getEnumMemberName(\n\t\t\t\t\t\t\t\tLongRangeChannel,\n\t\t\t\t\t\t\t\tthis.longRangeChannel,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: \"(unknown)\"\n\t\t\t\t\t} differs from desired channel ${\n\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\tLongRangeChannel,\n\t\t\t\t\t\t\tdesired,\n\t\t\t\t\t\t)\n\t\t\t\t\t}, configuring it...`,\n\t\t\t\t);\n\n\t\t\t\tconst resp = await this.setLongRangeChannel(desired)\n\t\t\t\t\t.catch((e) => (e as Error).message);\n\t\t\t\tif (resp === true) {\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t`LR channel updated`,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t`Changing the LR channel failed!${\n\t\t\t\t\t\t\tresp ? ` Reason: ${resp}` : \"\"\n\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Queries the home and node id of the controller\n\t */\n\tpublic async identify(): Promise<void> {\n\t\tthis.driver.controllerLog.print(`querying controller IDs...`);\n\t\tconst ids = await this.driver.sendMessage<GetControllerIdResponse>(\n\t\t\tnew GetControllerIdRequest(),\n\t\t\t{ supportCheck: false },\n\t\t);\n\t\tthis._homeId = ids.homeId;\n\t\tthis._ownNodeId = ids.ownNodeId;\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received controller IDs:\n home ID: ${num2hex(this._homeId)}\n own node ID: ${this._ownNodeId}`,\n\t\t);\n\t}\n\n\t/**\n\t * @internal\n\t * Performs additional controller configuration\n\t */\n\tpublic async configure(): Promise<void> {\n\t\t// Enable TX status report if supported\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.SetTxStatusReport,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(`Enabling TX status report...`);\n\t\t\tconst resp = await this.driver.sendMessage<\n\t\t\t\tSerialAPISetup_SetTXStatusReportResponse\n\t\t\t>(\n\t\t\t\tnew SerialAPISetup_SetTXStatusReportRequest({\n\t\t\t\t\tenabled: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Enabling TX status report ${\n\t\t\t\t\tresp.success ? \"successful\" : \"failed\"\n\t\t\t\t}...`,\n\t\t\t);\n\t\t}\n\n\t\t// find the SUC\n\t\tthis.driver.controllerLog.print(`finding SUC...`);\n\t\tconst suc = await this.driver.sendMessage<GetSUCNodeIdResponse>(\n\t\t\tnew GetSUCNodeIdRequest(),\n\t\t\t{ supportCheck: false },\n\t\t);\n\t\tthis._sucNodeId = suc.sucNodeId;\n\t\tif (this._sucNodeId === 0) {\n\t\t\tthis.driver.controllerLog.print(`No SUC present in the network`);\n\t\t} else if (this._sucNodeId === this._ownNodeId) {\n\t\t\tthis.driver.controllerLog.print(`This is the SUC`);\n\t\t} else {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`SUC has node ID ${this.sucNodeId}`,\n\t\t\t);\n\t\t}\n\n\t\t// There needs to be a SUC/SIS in the network. If not, we promote ourselves to one if the following conditions are met:\n\t\t// We are the primary controller, but we are not SUC, there is no SUC and there is no SIS, and there are no nodes in the network yet\n\t\tif (\n\t\t\tthis.role === ControllerRole.Primary\n\t\t\t&& this._noNodesIncluded\n\t\t\t&& this._sucNodeId === 0\n\t\t\t&& !this._isSUC\n\t\t\t&& !this._isSISPresent\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`There is no SUC/SIS in the network - promoting ourselves...`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst result = await this.configureSUC(\n\t\t\t\t\tthis._ownNodeId!,\n\t\t\t\t\ttrue,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tif (result) {\n\t\t\t\t\tthis._sucNodeId = this._ownNodeId;\n\t\t\t\t\tthis._isSUC = true;\n\t\t\t\t\tthis._isSISPresent = true;\n\t\t\t\t}\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Promotion to SUC/SIS ${result ? \"succeeded\" : \"failed\"}.`,\n\t\t\t\t\tresult ? undefined : \"warn\",\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Error while promoting to SUC/SIS: ${getErrorMessage(e)}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// TODO: if it's a bridge controller, request the virtual nodes\n\n\t\tif (\n\t\t\tthis.type !== ZWaveLibraryTypes[\"Bridge Controller\"]\n\t\t\t&& this.isFunctionSupported(FunctionType.SetSerialApiTimeouts)\n\t\t) {\n\t\t\tconst { ack, byte } = this.driver.options.timeouts;\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`setting serial API timeouts: ack = ${ack} ms, byte = ${byte} ms`,\n\t\t\t);\n\t\t\tconst resp = await this.driver.sendMessage<\n\t\t\t\tSetSerialApiTimeoutsResponse\n\t\t\t>(\n\t\t\t\tnew SetSerialApiTimeoutsRequest({\n\t\t\t\t\tackTimeout: ack,\n\t\t\t\t\tbyteTimeout: byte,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`serial API timeouts overwritten. The old values were: ack = ${resp.oldAckTimeout} ms, byte = ${resp.oldByteTimeout} ms`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Interviews the controller for the necessary information.\n\t * @param restoreFromCache Asynchronous callback for the driver to restore the network from cache after nodes are created\n\t */\n\tpublic async initNodes(\n\t\tclassicNodeIds: readonly number[],\n\t\tlrNodeIds: readonly number[],\n\t\trestoreFromCache: () => Promise<void>,\n\t): Promise<void> {\n\t\t// Index the value DB for optimal performance\n\t\tconst valueDBIndexes = indexDBsByNode([\n\t\t\tthis.driver.valueDB!,\n\t\t\tthis.driver.metadataDB!,\n\t\t]);\n\n\t\tconst nodeIds = [...classicNodeIds];\n\t\tif (nodeIds.length === 0) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Controller reports no nodes in its network. This could be an indication of a corrupted controller memory.`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tnodeIds.unshift(this._ownNodeId!);\n\t\t}\n\t\tnodeIds.push(...lrNodeIds);\n\n\t\t// For each node, create an empty entry in the nodes map so we can initialize them afterwards\n\t\tfor (const nodeId of nodeIds) {\n\t\t\tthis._nodes.set(\n\t\t\t\tnodeId,\n\t\t\t\tnew ZWaveNode(\n\t\t\t\t\tnodeId,\n\t\t\t\t\tthis.driver,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\t// Use the previously created index to avoid doing extra work when creating the value DB\n\t\t\t\t\tthis.createValueDBForNode(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tvalueDBIndexes.get(nodeId),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// Now try to deserialize all nodes from the cache\n\t\tawait restoreFromCache();\n\n\t\t// Set manufacturer information for the controller node\n\t\tconst controllerValueDB = this.valueDB;\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tManufacturerSpecificCCValues.manufacturerId.id,\n\t\t\tManufacturerSpecificCCValues.manufacturerId.meta,\n\t\t);\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tManufacturerSpecificCCValues.productType.id,\n\t\t\tManufacturerSpecificCCValues.productType.meta,\n\t\t);\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tManufacturerSpecificCCValues.productId.id,\n\t\t\tManufacturerSpecificCCValues.productId.meta,\n\t\t);\n\t\tcontrollerValueDB.setValue(\n\t\t\tManufacturerSpecificCCValues.manufacturerId.id,\n\t\t\tthis._manufacturerId,\n\t\t);\n\t\tcontrollerValueDB.setValue(\n\t\t\tManufacturerSpecificCCValues.productType.id,\n\t\t\tthis._productType,\n\t\t);\n\t\tcontrollerValueDB.setValue(\n\t\t\tManufacturerSpecificCCValues.productId.id,\n\t\t\tthis._productId,\n\t\t);\n\n\t\t// Set firmware version information for the controller node\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tVersionCCValues.firmwareVersions.id,\n\t\t\tVersionCCValues.firmwareVersions.meta,\n\t\t);\n\t\tcontrollerValueDB.setValue(VersionCCValues.firmwareVersions.id, [\n\t\t\tthis._firmwareVersion,\n\t\t]);\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tVersionCCValues.zWaveProtocolVersion.id,\n\t\t\tVersionCCValues.zWaveProtocolVersion.meta,\n\t\t);\n\t\tcontrollerValueDB.setValue(\n\t\t\tVersionCCValues.zWaveProtocolVersion.id,\n\t\t\tthis._protocolVersion,\n\t\t);\n\t\tcontrollerValueDB.setMetadata(\n\t\t\tVersionCCValues.sdkVersion.id,\n\t\t\tVersionCCValues.sdkVersion.meta,\n\t\t);\n\t\tcontrollerValueDB.setValue(\n\t\t\tVersionCCValues.sdkVersion.id,\n\t\t\tthis._sdkVersion,\n\t\t);\n\n\t\tthis.driver.controllerLog.print(\"Interview completed\");\n\t}\n\n\tprivate createValueDBForNode(nodeId: number, ownKeys?: Set<string>) {\n\t\treturn new ValueDB(\n\t\t\tnodeId,\n\t\t\tthis.driver.valueDB!,\n\t\t\tthis.driver.metadataDB!,\n\t\t\townKeys,\n\t\t);\n\t}\n\n\t/**\n\t * Gets the list of long range nodes from the controller.\n\t */\n\tpublic async getLongRangeNodes(): Promise<readonly number[]> {\n\t\tconst nodeIds: number[] = [];\n\n\t\tif (this.supportsLongRange) {\n\t\t\tfor (let segment = 0;; segment++) {\n\t\t\t\tconst nodesResponse = await this.driver.sendMessage<\n\t\t\t\t\tGetLongRangeNodesResponse\n\t\t\t\t>(\n\t\t\t\t\tnew GetLongRangeNodesRequest({\n\t\t\t\t\t\tsegmentNumber: segment,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tnodeIds.push(...nodesResponse.nodeIds);\n\n\t\t\t\tif (!nodesResponse.moreNodes) break;\n\t\t\t}\n\t\t}\n\t\treturn nodeIds;\n\t}\n\n\t/**\n\t * Sets the NIF of the controller to the Gateway device type and to include the CCs supported by Z-Wave JS.\n\t * Warning: This only works when followed up by a hard-reset, so don't call this directly\n\t * @internal\n\t */\n\tpublic async setControllerNIF(): Promise<void> {\n\t\tthis.driver.controllerLog.print(\"Updating the controller NIF...\");\n\t\tawait this.driver.sendMessage(\n\t\t\tnew SetApplicationNodeInformationRequest({\n\t\t\t\tisListening: true,\n\t\t\t\t...determineNIF(),\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Performs a hard reset on the controller. This wipes out all configuration!\n\t * Warning: The driver needs to re-interview the controller, so don't call this directly\n\t * @internal\n\t */\n\tpublic async hardReset(): Promise<void> {\n\t\t// begin the reset process\n\t\ttry {\n\t\t\tconst associations = this.associations;\n\t\t\tif (associations?.length) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Notifying associated nodes about reset...\",\n\t\t\t\t);\n\t\t\t\tconst nodeIdDestinations = distinct(\n\t\t\t\t\tassociations.map(({ nodeId }) => nodeId),\n\t\t\t\t);\n\t\t\t\tfor (const nodeId of nodeIdDestinations) {\n\t\t\t\t\tconst node = this.nodes.get(nodeId);\n\t\t\t\t\tif (!node) continue;\n\n\t\t\t\t\tawait node.sendResetLocallyNotification().catch(noop);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.print(\"performing hard reset...\");\n\t\t\tawait this.driver.sendMessage(new HardResetRequest(), {\n\t\t\t\tsupportCheck: false,\n\t\t\t});\n\n\t\t\tthis.driver.controllerLog.print(`hard reset succeeded`);\n\t\t\t// Clean up\n\t\t\tthis._nodes.forEach((node) => node.removeAllListeners());\n\t\t\tthis._nodes.clear();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`hard reset failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tpublic async shutdown(): Promise<boolean> {\n\t\t// begin the reset process\n\t\ttry {\n\t\t\tthis.driver.controllerLog.print(\"Shutting down the Z-Wave API...\");\n\t\t\tconst response = await this.driver.sendMessage<ShutdownResponse>(\n\t\t\t\tnew ShutdownRequest(),\n\t\t\t);\n\t\t\tif (response.success) {\n\t\t\t\tthis.driver.controllerLog.print(\"Z-Wave API was shut down\");\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Failed to shut down the Z-Wave API\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn response.success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`shutdown failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Starts the hardware watchdog on supporting 700+ series controllers.\n\t * Returns whether the operation was successful.\n\t */\n\tpublic async startWatchdog(): Promise<boolean> {\n\t\tif (\n\t\t\tthis.sdkVersionGte(\"7.0\")\n\t\t\t&& this.isFunctionSupported(FunctionType.StartWatchdog)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Starting hardware watchdog...\",\n\t\t\t\t);\n\t\t\t\tawait this.driver.sendMessage(\n\t\t\t\t\tnew StartWatchdogRequest(),\n\t\t\t\t);\n\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Starting the hardware watchdog failed: ${\n\t\t\t\t\t\tgetErrorMessage(e)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Stops the hardware watchdog on supporting controllers.\n\t * Returns whether the operation was successful.\n\t */\n\tpublic async stopWatchdog(): Promise<boolean> {\n\t\tif (this.isFunctionSupported(FunctionType.StopWatchdog)) {\n\t\t\ttry {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Stopping hardware watchdog...\",\n\t\t\t\t);\n\t\t\t\tawait this.driver.sendMessage(\n\t\t\t\t\tnew StopWatchdogRequest(),\n\t\t\t\t);\n\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Stopping the hardware watchdog failed: ${\n\t\t\t\t\t\tgetErrorMessage(e)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate _inclusionState: InclusionState = InclusionState.Idle;\n\tpublic get inclusionState(): InclusionState {\n\t\treturn this._inclusionState;\n\t}\n\n\t/** @internal */\n\tpublic setInclusionState(state: InclusionState): void {\n\t\tif (this._inclusionState === state) return;\n\t\tthis._inclusionState = state;\n\t\tthis.emit(\"inclusion state changed\", state);\n\t\tif (\n\t\t\tstate === InclusionState.Idle\n\t\t\t&& this._smartStartEnabled\n\t\t\t&& this.supportsFeature(ZWaveFeature.SmartStart)\n\t\t) {\n\t\t\t// If Smart Start was enabled before the inclusion/exclusion,\n\t\t\t// enable it again and ignore errors\n\t\t\tthis.enableSmartStart().catch(noop);\n\t\t}\n\t}\n\n\tprivate _smartStartEnabled: boolean = false;\n\n\tprivate _includeController: boolean = false;\n\tprivate _exclusionOptions: ExclusionOptions | undefined;\n\tprivate _inclusionOptions: InclusionOptionsInternal | undefined;\n\tprivate _nodePendingInclusion: ZWaveNode | undefined;\n\tprivate _nodePendingExclusion: ZWaveNode | undefined;\n\tprivate _nodePendingReplace: ZWaveNode | undefined;\n\tprivate _replaceFailedPromise: DeferredPromise<boolean> | undefined;\n\n\t/**\n\t * Starts the inclusion process of new nodes.\n\t * Resolves to true when the process was started, and false if the inclusion was already active.\n\t *\n\t * @param options Defines the inclusion strategy to use.\n\t */\n\tpublic async beginInclusion(\n\t\toptions: InclusionOptions = {\n\t\t\tstrategy: InclusionStrategy.Insecure,\n\t\t},\n\t): Promise<boolean> {\n\t\tif (\n\t\t\tthis._inclusionState === InclusionState.Including\n\t\t\t|| this._inclusionState === InclusionState.Excluding\n\t\t\t|| this._inclusionState === InclusionState.Busy\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Protect against invalid inclusion options\n\t\tif (\n\t\t\t!(options.strategy in InclusionStrategy)\n\t\t\t// @ts-expect-error We're checking for user errors\n\t\t\t|| options.strategy === InclusionStrategy.SmartStart\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Invalid inclusion strategy: ${options.strategy}`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\t// Leave SmartStart listening mode so we can switch to exclusion mode\n\t\tawait this.pauseSmartStart();\n\n\t\tthis.setInclusionState(InclusionState.Including);\n\t\tthis._inclusionOptions = options;\n\n\t\ttry {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Starting inclusion process with strategy ${\n\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\tInclusionStrategy,\n\t\t\t\t\t\toptions.strategy,\n\t\t\t\t\t)\n\t\t\t\t}...`,\n\t\t\t);\n\n\t\t\t// kick off the inclusion process\n\t\t\tawait this.driver.sendMessage(\n\t\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\t\taddNodeType: AddNodeType.Any,\n\t\t\t\t\thighPower: true,\n\t\t\t\t\tnetworkWide: true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`The controller is now ready to add nodes`,\n\t\t\t);\n\n\t\t\tthis.emit(\"inclusion started\", options.strategy);\n\t\t} catch (e) {\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Starting the inclusion failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The inclusion could not be started.\",\n\t\t\t\t\tZWaveErrorCodes.Controller_InclusionFailed,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/** @internal */\n\tpublic async beginInclusionSmartStart(\n\t\tprovisioningEntry: PlannedProvisioningEntry,\n\t): Promise<boolean> {\n\t\tif (\n\t\t\tthis._inclusionState === InclusionState.Including\n\t\t\t|| this._inclusionState === InclusionState.Excluding\n\t\t\t|| this._inclusionState === InclusionState.Busy\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Disable listening mode so we can switch to inclusion mode\n\t\tawait this.stopInclusion();\n\n\t\tthis.setInclusionState(InclusionState.Including);\n\t\tthis._inclusionOptions = {\n\t\t\tstrategy: InclusionStrategy.SmartStart,\n\t\t\tprovisioning: provisioningEntry,\n\t\t};\n\n\t\ttry {\n\t\t\t// Kick off the inclusion process using either the\n\t\t\t// specified protocol or the first supported one\n\t\t\tconst dskBuffer = dskFromString(provisioningEntry.dsk);\n\t\t\tconst protocol = provisioningEntry.protocol\n\t\t\t\t?? provisioningEntry.supportedProtocols?.[0]\n\t\t\t\t?? Protocols.ZWave;\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Including SmartStart node with DSK ${provisioningEntry.dsk}${\n\t\t\t\t\tprotocol == Protocols.ZWaveLongRange\n\t\t\t\t\t\t? \" using Z-Wave Long Range\"\n\t\t\t\t\t\t: \"\"\n\t\t\t\t}`,\n\t\t\t);\n\n\t\t\tawait this.driver.sendMessage(\n\t\t\t\tnew AddNodeDSKToNetworkRequest({\n\t\t\t\t\tnwiHomeId: nwiHomeIdFromDSK(dskBuffer),\n\t\t\t\t\tauthHomeId: authHomeIdFromDSK(dskBuffer),\n\t\t\t\t\tprotocol,\n\t\t\t\t\thighPower: true,\n\t\t\t\t\tnetworkWide: true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tthis.emit(\"inclusion started\", InclusionStrategy.SmartStart);\n\t\t} catch (e) {\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t// Error handling for this happens at the call site\n\t\t\tthrow e;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Is used internally to stop an active inclusion process without waiting for a confirmation\n\t * @internal\n\t */\n\tpublic async stopInclusionNoCallback(): Promise<void> {\n\t\tawait this.driver.sendMessage(\n\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\tcallbackId: 0, // disable callbacks\n\t\t\t\taddNodeType: AddNodeType.Stop,\n\t\t\t\thighPower: true,\n\t\t\t\tnetworkWide: true,\n\t\t\t}),\n\t\t);\n\t\tthis.driver.controllerLog.print(`The inclusion process was stopped`);\n\t\tthis.emit(\"inclusion stopped\");\n\t}\n\n\t/**\n\t * Finishes an inclusion process. This must only be called after the ProtocolDone status is received.\n\t * Returns the ID of the newly added node.\n\t */\n\tprivate async finishInclusion(): Promise<number> {\n\t\tthis.driver.controllerLog.print(`finishing inclusion process...`);\n\n\t\tconst response = await this.driver.sendMessage<\n\t\t\tAddNodeToNetworkRequestStatusReport\n\t\t>(\n\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\taddNodeType: AddNodeType.Stop,\n\t\t\t\thighPower: true,\n\t\t\t\tnetworkWide: true,\n\t\t\t}),\n\t\t);\n\t\tif (response.status === AddNodeStatus.Done) {\n\t\t\treturn response.statusContext!.nodeId;\n\t\t}\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`Finishing the inclusion failed`,\n\t\t\t\"error\",\n\t\t);\n\t\tthrow new ZWaveError(\n\t\t\t\"Finishing the inclusion failed\",\n\t\t\tZWaveErrorCodes.Controller_InclusionFailed,\n\t\t);\n\t}\n\n\t/**\n\t * Stops an active inclusion process. Resolves to true when the controller leaves inclusion mode,\n\t * and false if the inclusion was not active.\n\t */\n\tpublic async stopInclusion(): Promise<boolean> {\n\t\tif (this._inclusionState !== InclusionState.Including) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.print(`stopping inclusion process...`);\n\n\t\ttry {\n\t\t\t// stop the inclusion process\n\t\t\tawait this.driver.sendMessage(\n\t\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\t\taddNodeType: AddNodeType.Stop,\n\t\t\t\t\thighPower: true,\n\t\t\t\t\tnetworkWide: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`The inclusion process was stopped`,\n\t\t\t);\n\t\t\tthis.emit(\"inclusion stopped\");\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Stopping the inclusion failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The inclusion could not be stopped.\",\n\t\t\t\t\tZWaveErrorCodes.Controller_InclusionFailed,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Puts the controller into listening mode for Smart Start inclusion.\n\t * Whenever a node on the provisioning list announces itself, it will automatically be added.\n\t *\n\t * Resolves to `true` when the listening mode is started or was active, and `false` if it is scheduled for later activation.\n\t */\n\tprivate async enableSmartStart(): Promise<boolean> {\n\t\tif (!this.supportsFeature(ZWaveFeature.SmartStart)) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Smart Start is not supported by this controller, NOT enabling listening mode...`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t}\n\n\t\tthis._smartStartEnabled = true;\n\n\t\tif (this._inclusionState === InclusionState.Idle) {\n\t\t\tthis.setInclusionState(InclusionState.SmartStart);\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Enabling Smart Start listening mode...`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendMessage(\n\t\t\t\t\tnew EnableSmartStartListenRequest({}),\n\t\t\t\t);\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start listening mode enabled`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start listening mode could not be enabled: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t} else if (this._inclusionState === InclusionState.SmartStart) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Smart Start listening mode scheduled for later activation...`,\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Disables the listening mode for Smart Start inclusion.\n\t *\n\t * Resolves to `true` when the listening mode is stopped, and `false` if was not active.\n\t */\n\tprivate async disableSmartStart(): Promise<boolean> {\n\t\tif (!this.supportsFeature(ZWaveFeature.SmartStart)) return true;\n\t\tthis._smartStartEnabled = false;\n\n\t\tif (this._inclusionState === InclusionState.SmartStart) {\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`disabling Smart Start listening mode...`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendMessage(\n\t\t\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\t\t\tcallbackId: 0, // disable callbacks\n\t\t\t\t\t\taddNodeType: AddNodeType.Stop,\n\t\t\t\t\t\thighPower: true,\n\t\t\t\t\t\tnetworkWide: true,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start listening mode disabled`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tthis.setInclusionState(InclusionState.SmartStart);\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start listening mode could not be disabled: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t} else if (this._inclusionState === InclusionState.Idle) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Smart Start listening mode disabled`,\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tprivate async pauseSmartStart(): Promise<boolean> {\n\t\tif (!this.supportsFeature(ZWaveFeature.SmartStart)) return true;\n\n\t\tif (this._inclusionState === InclusionState.SmartStart) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Leaving Smart Start listening mode...`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendMessage(\n\t\t\t\t\tnew AddNodeToNetworkRequest({\n\t\t\t\t\t\tcallbackId: 0, // disable callbacks\n\t\t\t\t\t\taddNodeType: AddNodeType.Stop,\n\t\t\t\t\t\thighPower: true,\n\t\t\t\t\t\tnetworkWide: true,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Left Smart Start listening mode`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start listening mode could not be left: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t} else {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/**\n\t * Starts the exclusion process of new nodes.\n\t * Resolves to true when the process was started, and false if an inclusion or exclusion process was already active.\n\t *\n\t * @param options Influences the exclusion process and what happens with the Smart Start provisioning list.\n\t */\n\tpublic async beginExclusion(\n\t\toptions: ExclusionOptions = {\n\t\t\tstrategy: ExclusionStrategy.DisableProvisioningEntry,\n\t\t},\n\t): Promise<boolean> {\n\t\tif (\n\t\t\tthis._inclusionState === InclusionState.Including\n\t\t\t|| this._inclusionState === InclusionState.Excluding\n\t\t\t|| this._inclusionState === InclusionState.Busy\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Leave SmartStart listening mode so we can switch to exclusion mode\n\t\tawait this.pauseSmartStart();\n\n\t\tthis.setInclusionState(InclusionState.Excluding);\n\t\tthis.driver.controllerLog.print(`starting exclusion process...`);\n\n\t\ttry {\n\t\t\t// kick off the inclusion process\n\t\t\tawait this.driver.sendMessage(\n\t\t\t\tnew RemoveNodeFromNetworkRequest({\n\t\t\t\t\tremoveNodeType: RemoveNodeType.Any,\n\t\t\t\t\thighPower: true,\n\t\t\t\t\tnetworkWide: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`The controller is now ready to remove nodes`,\n\t\t\t);\n\t\t\tthis._exclusionOptions = options;\n\t\t\tthis.emit(\"exclusion started\");\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Starting the exclusion failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The exclusion could not be started.\",\n\t\t\t\t\tZWaveErrorCodes.Controller_ExclusionFailed,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Is used internally to stop an active exclusion process without waiting for confirmation\n\t * @internal\n\t */\n\tpublic async stopExclusionNoCallback(): Promise<void> {\n\t\tawait this.driver.sendMessage(\n\t\t\tnew RemoveNodeFromNetworkRequest({\n\t\t\t\tcallbackId: 0, // disable callbacks\n\t\t\t\tremoveNodeType: RemoveNodeType.Stop,\n\t\t\t\thighPower: true,\n\t\t\t\tnetworkWide: true,\n\t\t\t}),\n\t\t);\n\t\tthis.driver.controllerLog.print(`the exclusion process was stopped`);\n\t\tthis.emit(\"exclusion stopped\");\n\t}\n\n\t/**\n\t * Stops an active exclusion process. Resolves to true when the controller leaves exclusion mode,\n\t * and false if the inclusion was not active.\n\t */\n\tpublic async stopExclusion(): Promise<boolean> {\n\t\tif (this._inclusionState !== InclusionState.Excluding) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.print(`stopping exclusion process...`);\n\n\t\ttry {\n\t\t\t// kick off the inclusion process\n\t\t\tawait this.driver.sendMessage(\n\t\t\t\tnew RemoveNodeFromNetworkRequest({\n\t\t\t\t\tremoveNodeType: RemoveNodeType.Stop,\n\t\t\t\t\thighPower: true,\n\t\t\t\t\tnetworkWide: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`the exclusion process was stopped`,\n\t\t\t);\n\t\t\tthis.emit(\"exclusion stopped\");\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Stopping the exclusion failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The exclusion could not be stopped.\",\n\t\t\t\t\tZWaveErrorCodes.Controller_ExclusionFailed,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/** @internal */\n\tpublic async handleApplicationUpdateRequest(\n\t\tmsg: ApplicationUpdateRequest,\n\t): Promise<void> {\n\t\tconst nodeId = msg.getNodeId();\n\t\tlet node: ZWaveNode | undefined;\n\t\tif (nodeId != undefined) {\n\t\t\tnode = this.nodes.get(nodeId);\n\t\t}\n\n\t\tif (msg instanceof ApplicationUpdateRequestNodeInfoReceived) {\n\t\t\tif (node) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage: \"Received updated node info\",\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\t\t\t\tnode.updateNodeInfo(msg.nodeInformation);\n\n\t\t\t\t// This came from the node\n\t\t\t\tnode.lastSeen = new Date();\n\n\t\t\t\t// Resolve active pings that would fail otherwise\n\t\t\t\tthis.driver.resolvePendingPings(node.id);\n\n\t\t\t\tnode.emit(\"node info received\", node);\n\n\t\t\t\tif (\n\t\t\t\t\tnode.canSleep\n\t\t\t\t\t&& node.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t\t\t) {\n\t\t\t\t\t// In case this is a sleeping node and there are no messages in the queue, the node may go back to sleep very soon\n\t\t\t\t\tthis.driver.debounceSendNodeToSleep(node);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (\n\t\t\tmsg instanceof ApplicationUpdateRequestSmartStartHomeIDReceived\n\t\t\t|| msg\n\t\t\t\tinstanceof ApplicationUpdateRequestSmartStartLongRangeHomeIDReceived\n\t\t) {\n\t\t\tconst isLongRange = msg\n\t\t\t\tinstanceof ApplicationUpdateRequestSmartStartLongRangeHomeIDReceived;\n\t\t\t// The controller is in Smart Start learn mode and a node requests inclusion via Smart Start\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Received Smart Start inclusion request${\n\t\t\t\t\tisLongRange ? \" (Z-Wave Long Range)\" : \"\"\n\t\t\t\t}`,\n\t\t\t);\n\n\t\t\tif (\n\t\t\t\tthis.inclusionState !== InclusionState.Idle\n\t\t\t\t&& this.inclusionState !== InclusionState.SmartStart\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Controller is busy and cannot handle this inclusion request right now...\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check if the node is on the provisioning list\n\t\t\tconst entry = this.provisioningList.find((entry) => {\n\t\t\t\tif (\n\t\t\t\t\t!areUint8ArraysEqual(\n\t\t\t\t\t\tnwiHomeIdFromDSK(dskFromString(entry.dsk)),\n\t\t\t\t\t\tmsg.nwiHomeId,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t// TODO: This is duplicated with the logic in beginInclusionSmartStart\n\t\t\t\tconst entryProtocol = entry.protocol\n\t\t\t\t\t?? entry.supportedProtocols?.[0]\n\t\t\t\t\t?? Protocols.ZWave;\n\t\t\t\treturn (entryProtocol === Protocols.ZWaveLongRange)\n\t\t\t\t\t=== isLongRange;\n\t\t\t});\n\t\t\tif (!entry) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"NWI Home ID not found in provisioning list, ignoring request...\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t} else if (\n\t\t\t\tentry.status === ProvisioningEntryStatus.Inactive\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"The provisioning entry for this node is inactive, ignoring request...\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Discard any invalid security classes that may have been granted. This can happen\n\t\t\t// when switching the protocol to ZWLR for a device that requests S2 Unauthenticated\n\t\t\t// for Z-Wave Classic.\n\t\t\tconst provisioningEntry = cloneDeep(entry);\n\t\t\tif (isLongRange) {\n\t\t\t\tprovisioningEntry.securityClasses = provisioningEntry\n\t\t\t\t\t.securityClasses.filter((sc) =>\n\t\t\t\t\t\tsc === SecurityClass.S2_AccessControl\n\t\t\t\t\t\t|| sc === SecurityClass.S2_Authenticated\n\t\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Ignore provisioning entries where some of the granted keys are not configured\n\t\t\tconst securityManager = isLongRange\n\t\t\t\t? this.driver.securityManagerLR\n\t\t\t\t: this.driver.securityManager2;\n\t\t\tconst missingKeys = provisioningEntry.securityClasses.filter(\n\t\t\t\t(sc) => !securityManager?.hasKeysForSecurityClass(sc),\n\t\t\t);\n\t\t\tif (missingKeys.length > 0) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Ignoring inclusion request because the following security classes were granted but have no key configured:${\n\t\t\t\t\t\tmissingKeys.map((sc) =>\n\t\t\t\t\t\t\t`\\n\u00B7 ${getEnumMemberName(SecurityClass, sc)}${\n\t\t\t\t\t\t\t\tisLongRange ? \" (Long Range)\" : \"\"\n\t\t\t\t\t\t\t}`\n\t\t\t\t\t\t).join(\"\")\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\"NWI Home ID found in provisioning list, including node...\",\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst result = await this.beginInclusionSmartStart(\n\t\t\t\t\tprovisioningEntry,\n\t\t\t\t);\n\t\t\t\tif (!result) {\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t\"Smart Start inclusion could not be started\",\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Smart Start inclusion could not be started: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (msg instanceof ApplicationUpdateRequestNodeRemoved) {\n\t\t\t// A node was removed by another controller\n\t\t\tconst node = this.nodes.get(msg.nodeId);\n\t\t\tif (node) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tnode.id,\n\t\t\t\t\t\"was removed from the network by another controller\",\n\t\t\t\t);\n\n\t\t\t\tthis.emit(\"node removed\", node, RemoveNodeReason.ProxyExcluded);\n\t\t\t}\n\t\t} else if (msg instanceof ApplicationUpdateRequestNodeAdded) {\n\t\t\t// A node was included by another controller\n\t\t\tconst nodeId = msg.nodeId;\n\t\t\tconst nodeInfo = msg.nodeInformation;\n\n\t\t\t// It can happen that this is received for a node that is already part of the network:\n\t\t\t// https://github.com/zwave-js/node-zwave-js/issues/5781\n\t\t\t// In this case, ignore this message to prevent chaos.\n\n\t\t\tif (this._nodes.has(nodeId)) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Node ${nodeId} was (supposedly) included by another controller, but it is already part of the network. Ignoring the message...`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.setInclusionState(InclusionState.Busy);\n\n\t\t\tconst deviceClass = new DeviceClass(\n\t\t\t\tnodeInfo.basicDeviceClass,\n\t\t\t\tnodeInfo.genericDeviceClass,\n\t\t\t\tnodeInfo.specificDeviceClass,\n\t\t\t);\n\n\t\t\tconst newNode = new ZWaveNode(\n\t\t\t\tnodeId,\n\t\t\t\tthis.driver,\n\t\t\t\tdeviceClass,\n\t\t\t\tnodeInfo.supportedCCs,\n\t\t\t\tundefined,\n\t\t\t\t// Create an empty value DB and specify that it contains no values\n\t\t\t\t// to avoid indexing the existing values\n\t\t\t\tthis.createValueDBForNode(nodeId, new Set()),\n\t\t\t);\n\t\t\tthis._nodes.set(nodeId, newNode);\n\n\t\t\tthis.emit(\"node found\", {\n\t\t\t\tid: nodeId,\n\t\t\t\tdeviceClass,\n\t\t\t\tsupportedCCs: nodeInfo.supportedCCs,\n\t\t\t});\n\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Node ${newNode.id} was included by another controller:${\n\t\t\t\t\tnewNode.deviceClass\n\t\t\t\t\t\t? `\n basic device class: ${\n\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\tBasicDeviceClass,\n\t\t\t\t\t\t\t\tnewNode.deviceClass.basic,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n generic device class: ${newNode.deviceClass.generic.label}\n specific device class: ${newNode.deviceClass.specific.label}`\n\t\t\t\t\t\t: \"\"\n\t\t\t\t}\n supported CCs: ${\n\t\t\t\t\tnodeInfo.supportedCCs\n\t\t\t\t\t\t.map((cc) =>\n\t\t\t\t\t\t\t`\\n \u00B7 ${CommandClasses[cc]} (${num2hex(cc)})`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}`,\n\t\t\t);\n\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t\"Waiting for initiate command to bootstrap node...\",\n\t\t\t);\n\n\t\t\t// Handle inclusion in the background\n\t\t\tprocess.nextTick(async () => {\n\t\t\t\t// If an Inclusion Controller that does not support the Inclusion Controller Command Class includes a\n\t\t\t\t// new node in a network, the SIS will never receive an Inclusion Controller Initiate Command. If no\n\t\t\t\t// Initiate Command has been received approximately 10 seconds after a new node has been added to a\n\t\t\t\t// network, the SIS SHOULD start interviewing the newly included node\n\n\t\t\t\tconst initiate = await this.driver\n\t\t\t\t\t.waitForCommand<\n\t\t\t\t\t\tSinglecastCC<InclusionControllerCCInitiate>\n\t\t\t\t\t>(\n\t\t\t\t\t\t(cc) =>\n\t\t\t\t\t\t\tcc instanceof InclusionControllerCCInitiate\n\t\t\t\t\t\t\t&& cc.isSinglecast()\n\t\t\t\t\t\t\t&& cc.includedNodeId === nodeId\n\t\t\t\t\t\t\t&& cc.step\n\t\t\t\t\t\t\t\t=== InclusionControllerStep.ProxyInclusion,\n\t\t\t\t\t\t10000,\n\t\t\t\t\t)\n\t\t\t\t\t.catch(() => undefined);\n\n\t\t\t\t// Assume the device is alive\n\t\t\t\t// If it is actually a sleeping device, it will be marked as such later\n\t\t\t\tnewNode.markAsAlive();\n\n\t\t\t\tlet inclCtrlr: ZWaveNode | undefined;\n\t\t\t\tlet bootstrapFailure: SecurityBootstrapFailure | undefined;\n\n\t\t\t\tif (initiate) {\n\t\t\t\t\tinclCtrlr = this.nodes.getOrThrow(initiate.nodeId);\n\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t`Initiate command received from node ${inclCtrlr.id}`,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Inclusion is handled by the inclusion controller, which (hopefully) sets the SUC return route\n\t\t\t\t\tnewNode.hasSUCReturnRoute = true;\n\n\t\t\t\t\t// SIS, A, MUST request a Node Info Frame from Joining Node, B\n\t\t\t\t\tconst requestedNodeInfo = await newNode\n\t\t\t\t\t\t.requestNodeInfo()\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tif (requestedNodeInfo) {\n\t\t\t\t\t\tnewNode.updateNodeInfo(requestedNodeInfo);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Perform S0/S2 bootstrapping\n\t\t\t\t\tbootstrapFailure = await this.proxyBootstrap(\n\t\t\t\t\t\tnewNode,\n\t\t\t\t\t\tinclCtrlr,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\t// No command received, bootstrap node by ourselves\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t\"no initiate command received, bootstrapping node...\",\n\t\t\t\t\t);\n\n\t\t\t\t\tif (newNode.protocol == Protocols.ZWave) {\n\t\t\t\t\t\t// Assign SUC return route to make sure the node knows where to get its routes from\n\t\t\t\t\t\tnewNode.hasSUCReturnRoute = await this\n\t\t\t\t\t\t\t.assignSUCReturnRoutes(newNode.id);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Include using the default inclusion strategy:\n\t\t\t\t\t// * Use S2 if possible,\n\t\t\t\t\t// * only use S0 if necessary,\n\t\t\t\t\t// * use no encryption otherwise\n\t\t\t\t\tif (newNode.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS2(\n\t\t\t\t\t\t\tnewNode,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t\t\t.getHighestSecurityClass();\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tactualSecurityClass == undefined\n\t\t\t\t\t\t\t\t|| actualSecurityClass\n\t\t\t\t\t\t\t\t\t< SecurityClass.S2_Unauthenticated\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\t\tSecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (\n\t\t\t\t\t\tnewNode.supportsCC(CommandClasses.Security)\n\t\t\t\t\t\t&& (deviceClass.specific ?? deviceClass.generic)\n\t\t\t\t\t\t\t.requiresSecurity\n\t\t\t\t\t) {\n\t\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS0(\n\t\t\t\t\t\t\tnewNode,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t\t\t.getHighestSecurityClass();\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tactualSecurityClass == undefined\n\t\t\t\t\t\t\t\t|| actualSecurityClass < SecurityClass.S0_Legacy\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\t\tSecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Remember that no security classes were granted\n\t\t\t\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\t\t\t\tnewNode.securityClasses.set(secClass, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// We're done adding this node, notify listeners\n\t\t\t\tconst result: InclusionResult = bootstrapFailure != undefined\n\t\t\t\t\t? {\n\t\t\t\t\t\tlowSecurity: true,\n\t\t\t\t\t\tlowSecurityReason: bootstrapFailure,\n\t\t\t\t\t}\n\t\t\t\t\t: { lowSecurity: false };\n\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\tthis.emit(\"node added\", newNode, result);\n\n\t\t\t\tif (inclCtrlr && initiate) {\n\t\t\t\t\tconst inclCtrlrId = inclCtrlr.id;\n\t\t\t\t\tconst step = initiate.step;\n\t\t\t\t\tnewNode.once(\"ready\", () => {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t\t`Notifying node ${inclCtrlrId} of finished inclusion`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// Create API without checking for support\n\t\t\t\t\t\tconst api = inclCtrlr.createAPI(\n\t\t\t\t\t\t\tCommandClasses[\"Inclusion Controller\"],\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tvoid api\n\t\t\t\t\t\t\t.completeStep(step, InclusionControllerStatus.OK)\n\t\t\t\t\t\t\t.catch(noop);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t} else if (msg instanceof ApplicationUpdateRequestSUCIdChanged) {\n\t\t\tthis._sucNodeId = msg.sucNodeID;\n\t\t\t// TODO: Emit event or what?\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Handles replace requests from an inclusion controller\n\t */\n\tpublic handleInclusionControllerCCInitiateReplace(\n\t\tinitiate: InclusionControllerCCInitiate,\n\t): void {\n\t\tif (initiate.step !== InclusionControllerStep.ProxyInclusionReplace) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Expected an inclusion controller replace request, but got a different step\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\t\tthis.setInclusionState(InclusionState.Busy);\n\n\t\tconst inclCtrlr = this.nodes.getOrThrow(initiate.nodeId as number);\n\n\t\tconst replacedNodeId = initiate.includedNodeId;\n\t\tconst oldNode = this.nodes.get(replacedNodeId);\n\t\tif (oldNode) {\n\t\t\tthis.emit(\"node removed\", oldNode, RemoveNodeReason.ProxyReplaced);\n\t\t\tthis._nodes.delete(oldNode.id);\n\t\t}\n\n\t\t// Create a fresh node instance and forget the old one\n\t\tconst newNode = new ZWaveNode(\n\t\t\treplacedNodeId,\n\t\t\tthis.driver,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\t// Create an empty value DB and specify that it contains no values\n\t\t\t// to avoid indexing the existing values\n\t\t\tthis.createValueDBForNode(replacedNodeId, new Set()),\n\t\t);\n\t\tthis._nodes.set(newNode.id, newNode);\n\n\t\tthis.emit(\"node found\", {\n\t\t\tid: newNode.id,\n\t\t});\n\n\t\t// Assume the device is alive\n\t\t// If it is actually a sleeping device, it will be marked as such later\n\t\tnewNode.markAsAlive();\n\n\t\t// Inclusion is handled by the inclusion controller, which (hopefully) sets the SUC return route\n\t\tnewNode.hasSUCReturnRoute = true;\n\n\t\t// Handle communication with the node in the background\n\t\tprocess.nextTick(async () => {\n\t\t\t// SIS, A, MUST request a Node Info Frame from Joining Node, B\n\t\t\tconst requestedNodeInfo = await newNode\n\t\t\t\t.requestNodeInfo()\n\t\t\t\t.catch(() => undefined);\n\t\t\tif (requestedNodeInfo) {\n\t\t\t\tnewNode.updateNodeInfo(requestedNodeInfo);\n\n\t\t\t\t// TODO: Check if this stuff works for a normal replace too\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/dot-notation\n\t\t\t\tnewNode[\"deviceClass\"] = new DeviceClass(\n\t\t\t\t\trequestedNodeInfo.basicDeviceClass,\n\t\t\t\t\trequestedNodeInfo.genericDeviceClass,\n\t\t\t\t\trequestedNodeInfo.specificDeviceClass,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Perform S0/S2 bootstrapping\n\t\t\tconst bootstrapFailure = await this.proxyBootstrap(\n\t\t\t\tnewNode,\n\t\t\t\tinclCtrlr,\n\t\t\t);\n\n\t\t\t// We're done adding this node, notify listeners\n\t\t\tconst result: InclusionResult = bootstrapFailure != undefined\n\t\t\t\t? {\n\t\t\t\t\tlowSecurity: true,\n\t\t\t\t\tlowSecurityReason: bootstrapFailure,\n\t\t\t\t}\n\t\t\t\t: { lowSecurity: false };\n\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tthis.emit(\"node added\", newNode, result);\n\n\t\t\t// And notify the inclusion controller after we're done interviewing\n\t\t\tnewNode.once(\"ready\", () => {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tinclCtrlr.nodeId,\n\t\t\t\t\t`Notifying inclusion controller of finished inclusion`,\n\t\t\t\t);\n\t\t\t\t// Create API without checking for support\n\t\t\t\tconst api = inclCtrlr.createAPI(\n\t\t\t\t\tCommandClasses[\"Inclusion Controller\"],\n\t\t\t\t\tfalse,\n\t\t\t\t);\n\t\t\t\tvoid api\n\t\t\t\t\t.completeStep(initiate.step, InclusionControllerStatus.OK)\n\t\t\t\t\t.catch(noop);\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Handles bootstrapping the security keys for a node that was included by an inclusion controller\n\t */\n\tprivate async proxyBootstrap(\n\t\tnewNode: ZWaveNode,\n\t\tinclCtrlr: ZWaveNode,\n\t): Promise<SecurityBootstrapFailure | undefined> {\n\t\t// This part is to be done before the interview\n\n\t\tconst deviceClass = newNode.deviceClass!;\n\t\tlet bootstrapFailure: SecurityBootstrapFailure | undefined;\n\n\t\t// Include using the default inclusion strategy:\n\t\t// * Use S2 if possible,\n\t\t// * only use S0 if necessary,\n\t\t// * use no encryption otherwise\n\t\tif (newNode.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\tbootstrapFailure = await this.secureBootstrapS2(newNode);\n\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\tconst actualSecurityClass = newNode.getHighestSecurityClass();\n\t\t\t\tif (\n\t\t\t\t\tactualSecurityClass == undefined\n\t\t\t\t\t|| actualSecurityClass < SecurityClass.S2_Unauthenticated\n\t\t\t\t) {\n\t\t\t\t\tbootstrapFailure = SecurityBootstrapFailure.Unknown;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (\n\t\t\tnewNode.supportsCC(CommandClasses.Security)\n\t\t\t&& (deviceClass.specific ?? deviceClass.generic).requiresSecurity\n\t\t) {\n\t\t\t// S0 bootstrapping is deferred to the inclusion controller\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnewNode.id,\n\t\t\t\t`Waiting for node ${inclCtrlr.id} to perform S0 bootstrapping...`,\n\t\t\t);\n\n\t\t\tawait inclCtrlr.commandClasses[\"Inclusion Controller\"].initiateStep(\n\t\t\t\tnewNode.id,\n\t\t\t\tInclusionControllerStep.S0Inclusion,\n\t\t\t);\n\t\t\t// Wait 60s for the S0 bootstrapping to complete\n\t\t\tconst s0result = await this.driver\n\t\t\t\t.waitForCommand<InclusionControllerCCComplete>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc.nodeId === inclCtrlr.id\n\t\t\t\t\t\t&& cc instanceof InclusionControllerCCComplete\n\t\t\t\t\t\t&& cc.step === InclusionControllerStep.S0Inclusion,\n\t\t\t\t\t60000,\n\t\t\t\t)\n\t\t\t\t.catch(() => undefined);\n\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnewNode.id,\n\t\t\t\t`S0 bootstrapping ${\n\t\t\t\t\ts0result == undefined\n\t\t\t\t\t\t? \"timed out\"\n\t\t\t\t\t\t: s0result.status === InclusionControllerStatus.OK\n\t\t\t\t\t\t? \"succeeded\"\n\t\t\t\t\t\t: \"failed\"\n\t\t\t\t}`,\n\t\t\t);\n\t\t\tbootstrapFailure = s0result == undefined\n\t\t\t\t? SecurityBootstrapFailure.Timeout\n\t\t\t\t: s0result.status === InclusionControllerStatus.OK\n\t\t\t\t? undefined\n\t\t\t\t: SecurityBootstrapFailure.Unknown;\n\n\t\t\t// When bootstrapping with S0, no other keys are granted\n\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\tif (secClass !== SecurityClass.S0_Legacy) {\n\t\t\t\t\tnewNode.securityClasses.set(secClass, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Whether the S0 key is granted depends on the result\n\t\t\t// received from the inclusion controller\n\t\t\tnewNode.securityClasses.set(\n\t\t\t\tSecurityClass.S0_Legacy,\n\t\t\t\ts0result?.status === InclusionControllerStatus.OK,\n\t\t\t);\n\t\t} else {\n\t\t\t// Remember that no security classes were granted\n\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\tnewNode.securityClasses.set(secClass, false);\n\t\t\t}\n\t\t}\n\n\t\treturn bootstrapFailure;\n\t}\n\n\tprivate async secureBootstrapS0(\n\t\tnode: ZWaveNode,\n\t\tassumeSupported: boolean = false,\n\t): Promise<SecurityBootstrapFailure | undefined> {\n\t\t// When bootstrapping with S0, no other keys are granted\n\t\tfor (const secClass of securityClassOrder) {\n\t\t\tif (secClass !== SecurityClass.S0_Legacy) {\n\t\t\t\tnode.securityClasses.set(secClass, false);\n\t\t\t}\n\t\t}\n\n\t\tif (!this.driver.securityManager) {\n\t\t\t// Remember that the node was NOT granted the S0 security class\n\t\t\tnode.securityClasses.set(SecurityClass.S0_Legacy, false);\n\t\t\treturn SecurityBootstrapFailure.NoKeysConfigured;\n\t\t}\n\n\t\t// If security has been set up and we are allowed to include the node securely, try to do it\n\t\ttry {\n\t\t\t// When replacing a node, we receive no NIF, so we cannot know that the Security CC is supported.\n\t\t\t// Querying the node info however kicks some devices out of secure inclusion mode.\n\t\t\t// Therefore we must assume that the node supports Security in order to support replacing a node securely\n\t\t\tif (assumeSupported && !node.supportsCC(CommandClasses.Security)) {\n\t\t\t\tnode.addCC(CommandClasses.Security, {\n\t\t\t\t\tsecure: true,\n\t\t\t\t\tisSupported: true,\n\t\t\t\t\tversion: 1,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// At most 10s may pass between receiving each command. We enforce this twofold:\n\t\t\t// 1. by imposing a report timeout on the requests, so they don't linger too long. This does not consider\n\t\t\t// the time it takes to transmit and receiv the ACK.\n\t\t\t// 2. by imposing a timeout around the whole API call.\n\t\t\tconst S0_TIMEOUT = 10000;\n\t\t\tconst api = node.commandClasses.Security.withOptions({\n\t\t\t\treportTimeoutMs: S0_TIMEOUT,\n\t\t\t});\n\n\t\t\tconst tasks: (() => Promise<any>)[] = [\n\t\t\t\t// Request security scheme (and ignore the result), because it is required by the specs\n\t\t\t\t() => api.getSecurityScheme(),\n\t\t\t\t// Request nonce (for network key) separately, so we can impose a timeout\n\t\t\t\t() => api.getNonce(),\n\t\t\t\t// send the network key\n\t\t\t\t() =>\n\t\t\t\t\tapi.setNetworkKey(this.driver.securityManager!.networkKey),\n\t\t\t];\n\t\t\tif (this._includeController) {\n\t\t\t\t// Tell the controller which security scheme to use\n\t\t\t\ttasks.push(async () => {\n\t\t\t\t\t// Request nonce (for security scheme) manually, so it has the longer timeout\n\t\t\t\t\tawait api.getNonce();\n\t\t\t\t\tawait api.inheritSecurityScheme();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tfor (const task of tasks) {\n\t\t\t\tconst result = await Promise.race([\n\t\t\t\t\twait(S0_TIMEOUT, true).then(() => false as const),\n\t\t\t\t\ttask().catch(() => false as const),\n\t\t\t\t]);\n\t\t\t\tif (result === false) {\n\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t`A secure inclusion timer has elapsed`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_NodeTimeout,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remember that the node was granted the S0 security class\n\t\t\tnode.securityClasses.set(SecurityClass.S0_Legacy, true);\n\n\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\tmessage: `Security S0 bootstrapping successful`,\n\t\t\t});\n\n\t\t\t// success \uD83C\uDF89\n\t\t} catch (e) {\n\t\t\tlet errorMessage =\n\t\t\t\t`Security S0 bootstrapping failed, the node was not granted the S0 security class`;\n\t\t\tlet failure: SecurityBootstrapFailure =\n\t\t\t\tSecurityBootstrapFailure.Unknown;\n\t\t\tif (!isZWaveError(e)) {\n\t\t\t\terrorMessage += `: ${e as any}`;\n\t\t\t} else if (\n\t\t\t\te.code !== ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t&& e.code !== ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t) {\n\t\t\t\terrorMessage += `: ${e.message}`;\n\t\t\t\tfailure = SecurityBootstrapFailure.Timeout;\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(node.id, errorMessage, \"warn\");\n\t\t\t// Remember that the node was NOT granted the S0 security class\n\t\t\tnode.securityClasses.set(SecurityClass.S0_Legacy, false);\n\t\t\tnode.removeCC(CommandClasses.Security);\n\n\t\t\treturn failure;\n\t\t}\n\t}\n\n\tprivate _bootstrappingS2NodeId: number | undefined;\n\t/**\n\t * @internal\n\t * Returns which node is currently being bootstrapped with S2\n\t */\n\tpublic get bootstrappingS2NodeId(): number | undefined {\n\t\treturn this._bootstrappingS2NodeId;\n\t}\n\n\tprivate cancelBootstrapS2Promise: DeferredPromise<KEXFailType> | undefined;\n\tpublic cancelSecureBootstrapS2(reason: KEXFailType): void {\n\t\tif (this.cancelBootstrapS2Promise) {\n\t\t\tthis.cancelBootstrapS2Promise.resolve(reason);\n\t\t\tthis.cancelBootstrapS2Promise = undefined;\n\t\t}\n\t}\n\n\tprivate async secureBootstrapS2(\n\t\tnode: ZWaveNode,\n\t\tassumeSupported: boolean = false,\n\t): Promise<SecurityBootstrapFailure | undefined> {\n\t\tconst unGrantSecurityClasses = () => {\n\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\tnode.securityClasses.set(secClass, false);\n\t\t\t}\n\t\t};\n\n\t\tconst securityManager = node.protocol === Protocols.ZWaveLongRange\n\t\t\t? this.driver.securityManagerLR\n\t\t\t: this.driver.securityManager2;\n\n\t\tif (!securityManager) {\n\t\t\t// Remember that the node was NOT granted any S2 security classes\n\t\t\tunGrantSecurityClasses();\n\t\t\treturn SecurityBootstrapFailure.NoKeysConfigured;\n\t\t}\n\n\t\tlet userCallbacks: InclusionUserCallbacks;\n\t\tconst inclusionOptions = this._inclusionOptions as\n\t\t\t| (InclusionOptionsInternal & {\n\t\t\t\t// This is the type when we end up here during normal inclusion\n\t\t\t\tstrategy:\n\t\t\t\t\t| InclusionStrategy.Security_S2\n\t\t\t\t\t| InclusionStrategy.SmartStart;\n\t\t\t})\n\t\t\t// And this when we do proxy bootstrapping for an inclusion controller\n\t\t\t| undefined;\n\t\tif (\n\t\t\tinclusionOptions\n\t\t\t&& \"provisioning\" in inclusionOptions\n\t\t\t&& !!inclusionOptions.provisioning\n\t\t) {\n\t\t\tconst grantedSecurityClasses =\n\t\t\t\tinclusionOptions.provisioning.securityClasses;\n\t\t\tconst fullDSK = inclusionOptions.provisioning.dsk;\n\t\t\t// SmartStart and S2 with QR code are pre-provisioned, so we don't need to ask the user for anything\n\t\t\tuserCallbacks = {\n\t\t\t\tabort() {},\n\t\t\t\tgrantSecurityClasses: (requested) => {\n\t\t\t\t\treturn Promise.resolve({\n\t\t\t\t\t\tclientSideAuth: false,\n\t\t\t\t\t\tsecurityClasses: requested.securityClasses.filter((r) =>\n\t\t\t\t\t\t\tgrantedSecurityClasses.includes(r)\n\t\t\t\t\t\t),\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tvalidateDSKAndEnterPIN: (dsk) => {\n\t\t\t\t\tconst pin = fullDSK.slice(0, 5);\n\t\t\t\t\t// Make sure the DSK matches\n\t\t\t\t\tif (pin + dsk !== fullDSK) return Promise.resolve(false);\n\t\t\t\t\treturn Promise.resolve(pin);\n\t\t\t\t},\n\t\t\t};\n\t\t} else if (\n\t\t\tinclusionOptions\n\t\t\t&& \"userCallbacks\" in inclusionOptions\n\t\t\t&& !!inclusionOptions.userCallbacks\n\t\t) {\n\t\t\t// Use the callbacks provided to this inclusion attempt\n\t\t\tuserCallbacks = inclusionOptions.userCallbacks;\n\t\t} else if (this.driver.options.inclusionUserCallbacks) {\n\t\t\t// Use the callbacks defined in the driver options as fallback\n\t\t\tuserCallbacks = this.driver.options.inclusionUserCallbacks;\n\t\t} else {\n\t\t\t// Cannot bootstrap S2 without user callbacks, abort.\n\t\t\t// Remember that the node was NOT granted any S2 security classes\n\t\t\tunGrantSecurityClasses();\n\t\t\treturn SecurityBootstrapFailure.S2NoUserCallbacks;\n\t\t}\n\n\t\t// When replacing a node, we receive no NIF, so we cannot know that the Security CC is supported.\n\t\t// Querying the node info however kicks some devices out of secure inclusion mode.\n\t\t// Therefore we must assume that the node supports Security in order to support replacing a node securely\n\t\tif (assumeSupported && !node.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\tnode.addCC(CommandClasses[\"Security 2\"], {\n\t\t\t\tsecure: true,\n\t\t\t\tisSupported: true,\n\t\t\t\tversion: 1,\n\t\t\t});\n\t\t}\n\n\t\tconst deleteTempKey = () => {\n\t\t\t// Whatever happens, no further communication needs the temporary key\n\t\t\tsecurityManager.deleteNonce(node.id);\n\t\t\tsecurityManager.tempKeys.delete(node.id);\n\t\t};\n\n\t\t// Allow canceling the bootstrapping process\n\t\tthis._bootstrappingS2NodeId = node.id;\n\t\tthis.cancelBootstrapS2Promise = createDeferredPromise();\n\n\t\ttry {\n\t\t\tconst api = node.commandClasses[\"Security 2\"].withOptions({\n\t\t\t\t// Do not wait for Nonce Reports after SET-type commands.\n\t\t\t\t// Timing is critical here\n\t\t\t\ts2VerifyDelivery: false,\n\t\t\t});\n\t\t\tconst abort = async (failType?: KEXFailType): Promise<void> => {\n\t\t\t\tif (failType != undefined) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait api.abortKeyExchange(failType);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Un-grant S2 security classes we might have granted\n\t\t\t\tunGrantSecurityClasses();\n\t\t\t\tdeleteTempKey();\n\t\t\t\t// We're no longer bootstrapping\n\t\t\t\tthis._bootstrappingS2NodeId = undefined;\n\t\t\t\tthis.cancelBootstrapS2Promise = undefined;\n\t\t\t};\n\n\t\t\tconst abortUser = async () => {\n\t\t\t\tsetImmediate(() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserCallbacks.abort();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// ignore errors in application callbacks\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.BootstrappingCanceled);\n\t\t\t\treturn SecurityBootstrapFailure.UserCanceled;\n\t\t\t};\n\n\t\t\tconst abortTimeout = async () => {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: a secure inclusion timer has elapsed`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\n\t\t\t\tawait abort();\n\t\t\t\treturn SecurityBootstrapFailure.Timeout;\n\t\t\t};\n\n\t\t\t// Ask the node for its desired security classes and key exchange params\n\t\t\tconst kexParams = await api\n\t\t\t\t.withOptions({ reportTimeoutMs: inclusionTimeouts.TA1 })\n\t\t\t\t.getKeyExchangeParameters();\n\t\t\tif (!kexParams) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: did not receive the node's desired security classes.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort();\n\t\t\t\treturn SecurityBootstrapFailure.Timeout;\n\t\t\t}\n\n\t\t\t// Validate the response\n\t\t\t// Echo flag must be false\n\t\t\tif (kexParams.echo) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: KEX Report unexpectedly has the echo flag set.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoVerify);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t}\n\t\t\t// At the time of implementation, only KEXScheme1 and Curve25519 are defined.\n\t\t\t// The certification testing ensures that no other bits are set, so we need to check that too.\n\t\t\t// Not sure why this choice is made, since it essentially breaks any forwards compatibility\n\t\t\tif (\n\t\t\t\tkexParams.supportedKEXSchemes.length !== 1\n\t\t\t\t|| !kexParams.supportedKEXSchemes.includes(\n\t\t\t\t\tKEXSchemes.KEXScheme1,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: No supported key exchange scheme or invalid list.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoSupportedScheme);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t} else if (\n\t\t\t\tkexParams.supportedECDHProfiles.length !== 1\n\t\t\t\t|| !kexParams.supportedECDHProfiles.includes(\n\t\t\t\t\tECDHProfiles.Curve25519,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: No supported ECDH profile or invalid list.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoSupportedCurve);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t} else if (kexParams.requestCSA) {\n\t\t\t\t// We do not support CSA at the moment, so it is never granted.\n\t\t\t\t// Alternatively, filter out S2 Authenticated and S2 Access Control\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: CSA requested but not granted.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.BootstrappingCanceled);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t}\n\n\t\t\tconst supportedKeys = kexParams.requestedKeys.filter((k) =>\n\t\t\t\tsecurityClassOrder.includes(k as any)\n\t\t\t);\n\t\t\tif (!supportedKeys.length) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: None of the requested security classes are supported.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoKeyMatch);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t}\n\n\t\t\t// TODO: Validate client-side auth if requested\n\t\t\tconst grantResult = await Promise.race([\n\t\t\t\twait(inclusionTimeouts.TAI1, true).then(() => false as const),\n\t\t\t\tuserCallbacks\n\t\t\t\t\t.grantSecurityClasses({\n\t\t\t\t\t\tsecurityClasses: supportedKeys,\n\t\t\t\t\t\tclientSideAuth: false,\n\t\t\t\t\t})\n\t\t\t\t\t// ignore errors in application callbacks\n\t\t\t\t\t.catch(() => false as const),\n\t\t\t]);\n\t\t\tif (grantResult === false) {\n\t\t\t\t// There was a timeout or the user did not confirm the request, abort\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: User rejected the requested security classes or interaction timed out.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\treturn abortUser();\n\t\t\t}\n\t\t\tconst grantedKeys = supportedKeys.filter((k) =>\n\t\t\t\tgrantResult.securityClasses.includes(k)\n\t\t\t);\n\t\t\tif (!grantedKeys.length) {\n\t\t\t\t// The user did not grant any of the requested keys\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: None of the requested keys were granted by the user.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\treturn abortUser();\n\t\t\t}\n\n\t\t\t// Tell the node how we want the inclusion to go and grant it the keys\n\t\t\t// It will send its public key in response\n\t\t\tawait api.grantKeys({\n\t\t\t\tgrantedKeys,\n\t\t\t\tpermitCSA: false,\n\t\t\t\tselectedECDHProfile: ECDHProfiles.Curve25519,\n\t\t\t\tselectedKEXScheme: KEXSchemes.KEXScheme1,\n\t\t\t});\n\n\t\t\tconst pubKeyResponse = await this.driver.waitForCommand<\n\t\t\t\tSecurity2CCPublicKeyReport | Security2CCKEXFail\n\t\t\t>(\n\t\t\t\t(cc) =>\n\t\t\t\t\tcc instanceof Security2CCPublicKeyReport\n\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\tinclusionTimeouts.TA2,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (pubKeyResponse === \"timeout\") return abortTimeout();\n\t\t\tif (\n\t\t\t\tpubKeyResponse instanceof Security2CCKEXFail\n\t\t\t\t|| pubKeyResponse.includingNode\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`The joining node canceled the Security S2 bootstrapping.`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort();\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t}\n\t\t\tconst nodePublicKey = Bytes.from(pubKeyResponse.publicKey);\n\n\t\t\t// This is the starting point of the timer TAI2.\n\t\t\tconst timerStartTAI2 = Date.now();\n\n\t\t\t// Generate ECDH key pair. We need to immediately send the other node our public key,\n\t\t\t// so it won't abort bootstrapping\n\t\t\tconst keyPair = generateECDHKeyPairSync();\n\t\t\tconst publicKey = extractRawECDHPublicKeySync(keyPair.publicKey);\n\t\t\tawait api.sendPublicKey(publicKey);\n\t\t\t// After this, the node will start sending us a KEX SET every 10 seconds.\n\t\t\t// We won't be able to decode it until the DSK was verified\n\n\t\t\tif (\n\t\t\t\tgrantedKeys.includes(SecurityClass.S2_AccessControl)\n\t\t\t\t|| grantedKeys.includes(SecurityClass.S2_Authenticated)\n\t\t\t) {\n\t\t\t\t// For authenticated encryption, the DSK (first 16 bytes of the public key) is obfuscated (missing the first 2 bytes)\n\t\t\t\t// Request the user to enter the missing part as a 5-digit PIN\n\t\t\t\tconst dsk = dskToString(nodePublicKey.subarray(0, 16)).slice(5);\n\n\t\t\t\t// The time the user has to enter the PIN is limited by the timeout TAI2\n\t\t\t\tconst tai2RemainingMs = inclusionTimeouts.TAI2\n\t\t\t\t\t- (Date.now() - timerStartTAI2);\n\n\t\t\t\tlet pinResult: string | false;\n\t\t\t\tif (\n\t\t\t\t\tinclusionOptions\n\t\t\t\t\t&& \"dsk\" in inclusionOptions\n\t\t\t\t\t&& typeof inclusionOptions.dsk === \"string\"\n\t\t\t\t\t&& isValidDSK(inclusionOptions.dsk)\n\t\t\t\t) {\n\t\t\t\t\tpinResult = inclusionOptions.dsk.slice(0, 5);\n\t\t\t\t} else {\n\t\t\t\t\tpinResult = await Promise.race([\n\t\t\t\t\t\twait(tai2RemainingMs, true).then(() => false as const),\n\t\t\t\t\t\tuserCallbacks\n\t\t\t\t\t\t\t.validateDSKAndEnterPIN(dsk)\n\t\t\t\t\t\t\t// ignore errors in application callbacks\n\t\t\t\t\t\t\t.catch(() => false as const),\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\ttypeof pinResult !== \"string\"\n\t\t\t\t\t|| !/^\\d{5}$/.test(pinResult)\n\t\t\t\t) {\n\t\t\t\t\t// There was a timeout, the user did not confirm the DSK or entered an invalid PIN\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: User rejected the DSK, entered an invalid PIN or the interaction timed out.`,\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\treturn abortUser();\n\t\t\t\t}\n\n\t\t\t\t// Fill in the missing two bytes of the public key\n\t\t\t\tnodePublicKey.writeUInt16BE(parseInt(pinResult, 10), 0);\n\t\t\t}\n\n\t\t\t// After the user has verified the DSK, we can derive the shared secret\n\t\t\t// Z-Wave works with the \"raw\" keys, so this is a tad complicated\n\t\t\tconst sharedSecret = crypto.diffieHellman({\n\t\t\t\tpublicKey: importRawECDHPublicKeySync(nodePublicKey),\n\t\t\t\tprivateKey: keyPair.privateKey,\n\t\t\t});\n\n\t\t\t// Derive temporary key from ECDH key pair - this will allow us to receive the node's KEX SET commands\n\t\t\tconst tempKeys = await deriveTempKeysAsync(\n\t\t\t\tawait computePRKAsync(sharedSecret, publicKey, nodePublicKey),\n\t\t\t);\n\t\t\tsecurityManager.deleteNonce(node.id);\n\t\t\tsecurityManager.tempKeys.set(node.id, {\n\t\t\t\tkeyCCM: tempKeys.tempKeyCCM,\n\t\t\t\tpersonalizationString: tempKeys.tempPersonalizationString,\n\t\t\t});\n\n\t\t\t// Now wait for the next KEXSet from the node (if there is even time left)\n\t\t\tconst tai2RemainingMs = inclusionTimeouts.TAI2\n\t\t\t\t- (Date.now() - timerStartTAI2);\n\t\t\tif (tai2RemainingMs < 1) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: a secure inclusion timer has elapsed`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\treturn abortUser();\n\t\t\t}\n\n\t\t\tconst kexSetEcho = await Promise.race([\n\t\t\t\tthis.driver.waitForCommand<\n\t\t\t\t\tSecurity2CCKEXSet | Security2CCKEXFail\n\t\t\t\t>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc instanceof Security2CCKEXSet\n\t\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\t\ttai2RemainingMs,\n\t\t\t\t).catch(() => \"timeout\" as const),\n\t\t\t\tthis.cancelBootstrapS2Promise,\n\t\t\t]);\n\t\t\tif (kexSetEcho === \"timeout\") return abortTimeout();\n\t\t\tif (typeof kexSetEcho === \"number\") {\n\t\t\t\t// The bootstrapping process was canceled - this is most likely because the PIN was incorrect\n\t\t\t\t// and the node's commands cannot be decoded\n\t\t\t\tawait abort(kexSetEcho);\n\t\t\t\treturn SecurityBootstrapFailure.S2IncorrectPIN;\n\t\t\t}\n\t\t\t// Validate that the received command contains the correct list of keys\n\t\t\tif (kexSetEcho instanceof Security2CCKEXFail) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`The joining node canceled the Security S2 bootstrapping.`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort();\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (!kexSetEcho.echo) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: KEXSet received without echo flag`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (kexSetEcho._reserved !== 0) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Invalid KEXSet received`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (\n\t\t\t\t!kexSetEcho.isEncapsulatedWith(\n\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Command received without encryption`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t} else if (\n\t\t\t\tkexSetEcho.grantedKeys.length !== grantedKeys.length\n\t\t\t\t|| !kexSetEcho.grantedKeys.every((k) => grantedKeys.includes(k))\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Granted key mismatch.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t}\n\t\t\t// Confirm the keys - the node will start requesting the granted keys in response\n\t\t\tawait api.confirmRequestedKeys({\n\t\t\t\trequestCSA: kexParams.requestCSA,\n\t\t\t\trequestedKeys: [...kexParams.requestedKeys],\n\t\t\t\tsupportedECDHProfiles: [...kexParams.supportedECDHProfiles],\n\t\t\t\tsupportedKEXSchemes: [...kexParams.supportedKEXSchemes],\n\t\t\t\t_reserved: kexParams._reserved,\n\t\t\t});\n\n\t\t\tfor (let i = 0; i < grantedKeys.length; i++) {\n\t\t\t\t// Wait for the key request\n\t\t\t\tconst keyRequest = await this.driver.waitForCommand<\n\t\t\t\t\tSecurity2CCNetworkKeyGet | Security2CCKEXFail\n\t\t\t\t>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc instanceof Security2CCNetworkKeyGet\n\t\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\t\tinclusionTimeouts.TA3,\n\t\t\t\t).catch(() => \"timeout\" as const);\n\t\t\t\tif (keyRequest === \"timeout\") {\n\t\t\t\t\treturn abortTimeout();\n\t\t\t\t} else if (keyRequest instanceof Security2CCKEXFail) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`The joining node canceled the Security S2 bootstrapping.`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort();\n\t\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t\t} else if (\n\t\t\t\t\t!keyRequest.isEncapsulatedWith(\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Command received without encryption`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t}\n\n\t\t\t\tconst securityClass = keyRequest.requestedKey;\n\t\t\t\t// Ensure it was received encrypted with the temporary key\n\t\t\t\tif (\n\t\t\t\t\t!securityManager.hasUsedSecurityClass(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\tSecurityClass.Temporary,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Node used wrong key to communicate.`,\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t} else if (!grantedKeys.includes(securityClass)) {\n\t\t\t\t\t// and that the requested key is one of the granted keys\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Node used key it was not granted.`,\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.KeyNotGranted);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t}\n\n\t\t\t\t// We need to temporarily mark this security class as granted, so the following exchange will use this\n\t\t\t\t// key for decryption\n\t\t\t\t// FIXME: Is this actually necessary?\n\t\t\t\tnode.securityClasses.set(securityClass, true);\n\n\t\t\t\t// Send the node the requested key\n\t\t\t\tawait api.sendNetworkKey(\n\t\t\t\t\tsecurityClass,\n\t\t\t\t\tsecurityManager.getKeysForSecurityClass(\n\t\t\t\t\t\tsecurityClass,\n\t\t\t\t\t).pnk,\n\t\t\t\t);\n\n\t\t\t\t// And wait for verification\n\t\t\t\tconst verify = await this.driver.waitForCommand<\n\t\t\t\t\tSecurity2CCNetworkKeyVerify | Security2CCKEXFail\n\t\t\t\t>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc instanceof Security2CCNetworkKeyVerify\n\t\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\t\tinclusionTimeouts.TA4,\n\t\t\t\t).catch(() => \"timeout\" as const);\n\t\t\t\tif (verify === \"timeout\") return abortTimeout();\n\n\t\t\t\tif (verify instanceof Security2CCKEXFail) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`The joining node canceled the Security S2 bootstrapping.`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort();\n\t\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t!securityManager.hasUsedSecurityClass(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\tsecurityClass,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Node used wrong key to communicate.`,\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.NoVerify);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t}\n\n\t\t\t\t// Tell the node that verification was successful. We need to reset the SPAN state\n\t\t\t\t// so the temporary key will be used again. Also we don't know in which order the node requests the keys\n\t\t\t\t// so our logic to use the highest security class for decryption might be problematic. Therefore delete the\n\t\t\t\t// security class for now.\n\t\t\t\tnode.securityClasses.delete(securityClass);\n\t\t\t\tsecurityManager.deleteNonce(node.id);\n\t\t\t\tawait api.confirmKeyVerification();\n\t\t\t}\n\n\t\t\t// After all keys were sent and verified, we need to wait for the node to confirm that it is done\n\t\t\tconst transferEnd = await this.driver.waitForCommand<\n\t\t\t\tSecurity2CCTransferEnd\n\t\t\t>(\n\t\t\t\t(cc) => cc instanceof Security2CCTransferEnd,\n\t\t\t\tinclusionTimeouts.TA5,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (transferEnd === \"timeout\") return abortTimeout();\n\t\t\tif (!transferEnd.keyRequestComplete) {\n\t\t\t\t// S2 bootstrapping failed\n\t\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Node did not confirm completion of the key exchange`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoVerify);\n\t\t\t\treturn SecurityBootstrapFailure.Timeout;\n\t\t\t}\n\n\t\t\t// Remember all security classes we have granted\n\t\t\tfor (const securityClass of securityClassOrder) {\n\t\t\t\tnode.securityClasses.set(\n\t\t\t\t\tsecurityClass,\n\t\t\t\t\tgrantedKeys.includes(securityClass),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Remember the DSK (first 16 bytes of the public key)\n\t\t\tnode.dsk = nodePublicKey.subarray(0, 16);\n\n\t\t\tthis.driver.controllerLog.logNode(node.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Security S2 bootstrapping successful with these security classes:${\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t...node.securityClasses.entries(),\n\t\t\t\t\t\t]\n\t\t\t\t\t\t\t.filter(([, v]) => v)\n\t\t\t\t\t\t\t.map(([k]) =>\n\t\t\t\t\t\t\t\t`\\n\u00B7 ${getEnumMemberName(SecurityClass, k)}`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.join(\"\")\n\t\t\t\t\t}`,\n\t\t\t});\n\n\t\t\t// success \uD83C\uDF89\n\t\t} catch (e) {\n\t\t\tlet errorMessage =\n\t\t\t\t`Security S2 bootstrapping failed, the node was not granted any S2 security class`;\n\t\t\tlet result = SecurityBootstrapFailure.Unknown;\n\t\t\tif (!isZWaveError(e)) {\n\t\t\t\terrorMessage += `: ${e as any}`;\n\t\t\t} else if (e.code === ZWaveErrorCodes.Controller_MessageExpired) {\n\t\t\t\terrorMessage += \": a secure inclusion timer has elapsed.\";\n\t\t\t\tresult = SecurityBootstrapFailure.Timeout;\n\t\t\t} else if (\n\t\t\t\te.code !== ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t&& e.code !== ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t) {\n\t\t\t\terrorMessage += `: ${e.message}`;\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(node.id, errorMessage, \"warn\");\n\t\t\t// Remember that the node was NOT granted any S2 security classes\n\t\t\tunGrantSecurityClasses();\n\t\t\tnode.removeCC(CommandClasses[\"Security 2\"]);\n\n\t\t\treturn result;\n\t\t} finally {\n\t\t\t// Whatever happens, no further communication needs the temporary key\n\t\t\tdeleteTempKey();\n\t\t\t// And we're no longer bootstrapping\n\t\t\tthis._bootstrappingS2NodeId = undefined;\n\t\t\tthis.cancelBootstrapS2Promise = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Is called when an AddNode request is received from the controller.\n\t * Handles and controls the inclusion process.\n\t */\n\tprivate async handleAddNodeStatusReport(\n\t\tmsg: AddNodeToNetworkRequestStatusReport,\n\t): Promise<boolean> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`handling add node request (status = ${AddNodeStatus[msg.status]})`,\n\t\t);\n\n\t\tif (\n\t\t\tthis._inclusionState !== InclusionState.Including\n\t\t\t|| this._inclusionOptions == undefined\n\t\t) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t` inclusion is NOT active, ignoring it...`,\n\t\t\t);\n\t\t\treturn true; // Don't invoke any more handlers\n\t\t}\n\n\t\tswitch (msg.status) {\n\t\t\tcase AddNodeStatus.Failed:\n\t\t\t\t// This code is handled elsewhere for starting the inclusion, so this means\n\t\t\t\t// that adding a node failed\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Adding the node failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthis.emit(\"inclusion failed\");\n\n\t\t\t\t// in any case, stop the inclusion process so we don't accidentally add another node\n\t\t\t\ttry {\n\t\t\t\t\tawait this.stopInclusion();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ok */\n\t\t\t\t}\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\tcase AddNodeStatus.AddingController:\n\t\t\t\tthis._includeController = true;\n\t\t\t// fall through!\n\t\t\tcase AddNodeStatus.AddingSlave: {\n\t\t\t\t// this is called when a new node is added\n\t\t\t\tthis._nodePendingInclusion = new ZWaveNode(\n\t\t\t\t\tmsg.statusContext!.nodeId,\n\t\t\t\t\tthis.driver,\n\t\t\t\t\tnew DeviceClass(\n\t\t\t\t\t\tmsg.statusContext!.basicDeviceClass!,\n\t\t\t\t\t\tmsg.statusContext!.genericDeviceClass!,\n\t\t\t\t\t\tmsg.statusContext!.specificDeviceClass!,\n\t\t\t\t\t),\n\t\t\t\t\tmsg.statusContext!.supportedCCs,\n\t\t\t\t\tmsg.statusContext!.controlledCCs,\n\t\t\t\t\t// Create an empty value DB and specify that it contains no values\n\t\t\t\t\t// to avoid indexing the existing values\n\t\t\t\t\tthis.createValueDBForNode(\n\t\t\t\t\t\tmsg.statusContext!.nodeId,\n\t\t\t\t\t\tnew Set(),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\t// TODO: According to INS13954, there are several more steps and different timeouts when including a controller\n\t\t\t\t// For now do the absolute minimum - that is include the controller\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\t}\n\t\t\tcase AddNodeStatus.ProtocolDone: {\n\t\t\t\t// this is called after a new node is added\n\t\t\t\t// stop the inclusion process so we don't accidentally add another node\n\t\t\t\tlet nodeId: number | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tnodeId = await this.finishInclusion();\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore the error\n\t\t\t\t}\n\n\t\t\t\t// It is recommended to send another STOP command to the controller\n\t\t\t\ttry {\n\t\t\t\t\tawait this.stopInclusionNoCallback();\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore the error\n\t\t\t\t}\n\n\t\t\t\tif (!nodeId || !this._nodePendingInclusion) {\n\t\t\t\t\t// The inclusion did not succeed\n\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\tthis._nodePendingInclusion = undefined;\n\t\t\t\t\treturn true;\n\t\t\t\t} else if (\n\t\t\t\t\tnodeId === NODE_ID_BROADCAST\n\t\t\t\t\t|| nodeId === NODE_ID_BROADCAST_LR\n\t\t\t\t) {\n\t\t\t\t\t// No idea how this can happen but it dit at least once\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t`Cannot add a node with the broadcast node ID, aborting...`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\tthis._nodePendingInclusion = undefined;\n\t\t\t\t\treturn true;\n\t\t\t\t} else if (this._nodes.has(nodeId)) {\n\t\t\t\t\t// Someone tried to include a node again, although it is part of the network already.\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t`Cannot add node ${nodeId} as it is already part of the network. Aborting...`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\tthis._nodePendingInclusion = undefined;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// We're technically done with the inclusion but should not include\n\t\t\t\t// anything else until the node has been bootstrapped\n\t\t\t\tthis.setInclusionState(InclusionState.Busy);\n\n\t\t\t\t// Inclusion is now completed, bootstrap the node\n\t\t\t\tconst newNode = this._nodePendingInclusion;\n\n\t\t\t\tconst supportedCCs = [\n\t\t\t\t\t...newNode.implementedCommandClasses.entries(),\n\t\t\t\t]\n\t\t\t\t\t.filter(([, info]) => info.isSupported)\n\t\t\t\t\t.map(([cc]) => cc);\n\t\t\t\tconst controlledCCs = [\n\t\t\t\t\t...newNode.implementedCommandClasses.entries(),\n\t\t\t\t]\n\t\t\t\t\t.filter(([, info]) => info.isControlled)\n\t\t\t\t\t.map(([cc]) => cc);\n\n\t\t\t\tthis.emit(\"node found\", {\n\t\t\t\t\tid: newNode.id,\n\t\t\t\t\tdeviceClass: newNode.deviceClass,\n\t\t\t\t\tsupportedCCs,\n\t\t\t\t\tcontrolledCCs,\n\t\t\t\t});\n\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`finished adding node ${newNode.id}:${\n\t\t\t\t\t\tnewNode.deviceClass\n\t\t\t\t\t\t\t? `\n basic device class: ${\n\t\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\t\tBasicDeviceClass,\n\t\t\t\t\t\t\t\t\tnewNode.deviceClass.basic,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n generic device class: ${newNode.deviceClass.generic.label}\n specific device class: ${newNode.deviceClass.specific.label}`\n\t\t\t\t\t\t\t: \"\"\n\t\t\t\t\t}\n supported CCs: ${\n\t\t\t\t\t\tsupportedCCs\n\t\t\t\t\t\t\t.map((cc) =>\n\t\t\t\t\t\t\t\t`\\n \u00B7 ${CommandClasses[cc]} (${num2hex(cc)})`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.join(\"\")\n\t\t\t\t\t}\n controlled CCs: ${\n\t\t\t\t\t\tcontrolledCCs\n\t\t\t\t\t\t\t.map((cc) =>\n\t\t\t\t\t\t\t\t`\\n \u00B7 ${CommandClasses[cc]} (${num2hex(cc)})`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.join(\"\")\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t\t// remember the node\n\t\t\t\tthis._nodes.set(newNode.id, newNode);\n\t\t\t\tthis._nodePendingInclusion = undefined;\n\n\t\t\t\t// We're communicating with the device, so assume it is alive\n\t\t\t\t// If it is actually a sleeping device, it will be marked as such later\n\t\t\t\tnewNode.markAsAlive();\n\n\t\t\t\tif (newNode.protocol == Protocols.ZWave) {\n\t\t\t\t\t// Assign SUC return route to make sure the node knows where to get its routes from\n\t\t\t\t\tnewNode.hasSUCReturnRoute = await this\n\t\t\t\t\t\t.assignSUCReturnRoutes(\n\t\t\t\t\t\t\tnewNode.id,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst opts = this._inclusionOptions;\n\n\t\t\t\tlet bootstrapFailure: SecurityBootstrapFailure | undefined;\n\t\t\t\tlet smartStartFailed = false;\n\n\t\t\t\t// A controller performing a SmartStart network inclusion shall perform S2 bootstrapping,\n\t\t\t\t// even if the joining node does not show the S2 Command Class in its supported Command Class list.\n\t\t\t\tlet forceAddedS2Support = false;\n\t\t\t\tif (\n\t\t\t\t\topts.strategy === InclusionStrategy.SmartStart\n\t\t\t\t\t&& !newNode.supportsCC(CommandClasses[\"Security 2\"])\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(newNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"does not list S2 as supported, but was included using SmartStart which implies S2 support.\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\n\t\t\t\t\tforceAddedS2Support = true;\n\t\t\t\t\tnewNode.addCC(CommandClasses[\"Security 2\"], {\n\t\t\t\t\t\tisSupported: true,\n\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// The default inclusion strategy is: Use S2 if possible, only use S0 if necessary, use no encryption otherwise\n\t\t\t\tif (\n\t\t\t\t\tnewNode.supportsCC(CommandClasses[\"Security 2\"])\n\t\t\t\t\t&& (opts.strategy === InclusionStrategy.Default\n\t\t\t\t\t\t|| opts.strategy === InclusionStrategy.Security_S2\n\t\t\t\t\t\t|| opts.strategy === InclusionStrategy.SmartStart)\n\t\t\t\t) {\n\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS2(newNode);\n\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t.getHighestSecurityClass();\n\n\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\tif (actualSecurityClass == SecurityClass.S0_Legacy) {\n\t\t\t\t\t\t\t// Notify user about potential S0 downgrade attack.\n\t\t\t\t\t\t\t// S0 is considered insecure if both controller and node are S2-capable\n\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\tSecurityBootstrapFailure.S0Downgrade;\n\n\t\t\t\t\t\t\tthis.driver.controllerLog.logNode(newNode.id, {\n\t\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\t\"Possible S0 downgrade attack detected!\",\n\t\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (!securityClassIsS2(actualSecurityClass)) {\n\t\t\t\t\t\t\tbootstrapFailure = SecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (opts.strategy === InclusionStrategy.SmartStart) {\n\t\t\t\t\t\tsmartStartFailed = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tforceAddedS2Support\n\t\t\t\t\t\t&& !securityClassIsS2(actualSecurityClass)\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Remove the fake S2 support again\n\t\t\t\t\t\tnewNode.removeCC(CommandClasses[\"Security 2\"]);\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tnewNode.supportsCC(CommandClasses.Security)\n\t\t\t\t\t&& (opts.strategy === InclusionStrategy.Security_S0\n\t\t\t\t\t\t|| (opts.strategy === InclusionStrategy.Default\n\t\t\t\t\t\t\t&& (opts.forceSecurity\n\t\t\t\t\t\t\t\t|| (\n\t\t\t\t\t\t\t\t\tnewNode.deviceClass?.specific\n\t\t\t\t\t\t\t\t\t\t?? newNode.deviceClass?.generic\n\t\t\t\t\t\t\t\t)?.requiresSecurity)))\n\t\t\t\t) {\n\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS0(newNode);\n\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t\t.getHighestSecurityClass();\n\t\t\t\t\t\tif (actualSecurityClass == SecurityClass.S0_Legacy) {\n\t\t\t\t\t\t\t// If the user chose this, i.e. InclusionStrategy.Security_S0 was used,\n\t\t\t\t\t\t\t// then this is the expected outcome and not a failure\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\topts.strategy !== InclusionStrategy.Security_S0\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// S0 is considered insecure if both controller and node are S2-capable\n\t\t\t\t\t\t\t\tconst nif = await newNode\n\t\t\t\t\t\t\t\t\t.requestNodeInfo()\n\t\t\t\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tnif?.supportedCCs.includes(\n\t\t\t\t\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t// Notify user about potential S0 downgrade attack.\n\t\t\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\t\t\tSecurityBootstrapFailure.S0Downgrade;\n\n\t\t\t\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\t\t\t\tnewNode.id,\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\t\t\t\t\"Possible S0 downgrade attack detected!\",\n\t\t\t\t\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbootstrapFailure = SecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Remember that no security classes were granted\n\t\t\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\t\t\tnewNode.securityClasses.set(secClass, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis._includeController = false;\n\n\t\t\t\t// After an unsuccessful SmartStart inclusion, the node MUST leave the network and return to SmartStart learn mode\n\t\t\t\t// The controller should consider the node to be failed.\n\t\t\t\tif (smartStartFailed) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(newNode.id, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\"SmartStart inclusion failed. Checking if the node needs to be removed.\",\n\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tawait this.removeFailedNodeInternal(\n\t\t\t\t\t\t\tnewNode.id,\n\t\t\t\t\t\t\tRemoveNodeReason.SmartStartFailed,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(newNode.id, {\n\t\t\t\t\t\t\tmessage: \"was removed\",\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// The node was removed. Do not emit the \"node added\" event\n\t\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// The node could not be removed, continue\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(newNode.id, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\"The node is still part of the network, continuing with insecure communication.\",\n\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\n\t\t\t\t// We're done adding this node, notify listeners\n\t\t\t\tconst result: InclusionResult = bootstrapFailure != undefined\n\t\t\t\t\t? {\n\t\t\t\t\t\tlowSecurity: true,\n\t\t\t\t\t\tlowSecurityReason: bootstrapFailure,\n\t\t\t\t\t}\n\t\t\t\t\t: { lowSecurity: false };\n\n\t\t\t\tthis.emit(\"node added\", newNode, result);\n\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\t}\n\t\t}\n\t\t// not sure what to do with this message\n\t\treturn false;\n\t}\n\n\t/**\n\t * Is called when an ReplaceFailed request is received from the controller.\n\t * Handles and controls the replace process.\n\t */\n\tprivate async handleReplaceNodeStatusReport(\n\t\tmsg: ReplaceFailedNodeRequestStatusReport,\n\t): Promise<boolean> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`handling replace node request (status = ${\n\t\t\t\tReplaceFailedNodeStatus[msg.replaceStatus]\n\t\t\t})`,\n\t\t);\n\n\t\tif (this._inclusionOptions == undefined) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t` currently NOT replacing a node, ignoring it...`,\n\t\t\t);\n\t\t\treturn true; // Don't invoke any more handlers\n\t\t}\n\n\t\tswitch (msg.replaceStatus) {\n\t\t\tcase ReplaceFailedNodeStatus.NodeOK:\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\tthis._replaceFailedPromise?.reject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`The node could not be replaced because it has responded`,\n\t\t\t\t\t\tZWaveErrorCodes.ReplaceFailedNode_NodeOK,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase ReplaceFailedNodeStatus.FailedNodeReplaceFailed:\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\tthis._replaceFailedPromise?.reject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`The failed node has not been replaced`,\n\t\t\t\t\t\tZWaveErrorCodes.ReplaceFailedNode_Failed,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase ReplaceFailedNodeStatus.FailedNodeReplace:\n\t\t\t\t// failed node is now ready to be replaced and controller is ready to add a new\n\t\t\t\t// node with the nodeID of the failed node\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`The failed node is ready to be replaced, inclusion started...`,\n\t\t\t\t);\n\t\t\t\tthis.emit(\"inclusion started\", this._inclusionOptions.strategy);\n\t\t\t\tthis.setInclusionState(InclusionState.Including);\n\t\t\t\tthis._replaceFailedPromise?.resolve(true);\n\n\t\t\t\t// stop here, don't emit inclusion failed\n\t\t\t\treturn true;\n\t\t\tcase ReplaceFailedNodeStatus.FailedNodeReplaceDone:\n\t\t\t\tthis.driver.controllerLog.print(`The failed node was replaced`);\n\t\t\t\tthis.emit(\"inclusion stopped\");\n\n\t\t\t\tif (this._nodePendingReplace) {\n\t\t\t\t\tthis.emit(\n\t\t\t\t\t\t\"node removed\",\n\t\t\t\t\t\tthis._nodePendingReplace,\n\t\t\t\t\t\tRemoveNodeReason.Replaced,\n\t\t\t\t\t);\n\t\t\t\t\tthis._nodes.delete(this._nodePendingReplace.id);\n\n\t\t\t\t\t// We're technically done with the replacing but should not include\n\t\t\t\t\t// anything else until the node has been bootstrapped\n\t\t\t\t\tthis.setInclusionState(InclusionState.Busy);\n\n\t\t\t\t\t// Create a fresh node instance and forget the old one\n\t\t\t\t\tconst newNode = new ZWaveNode(\n\t\t\t\t\t\tthis._nodePendingReplace.id,\n\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t// Create an empty value DB and specify that it contains no values\n\t\t\t\t\t\t// to avoid indexing the existing values\n\t\t\t\t\t\tthis.createValueDBForNode(\n\t\t\t\t\t\t\tthis._nodePendingReplace.id,\n\t\t\t\t\t\t\tnew Set(),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tthis._nodePendingReplace = undefined;\n\t\t\t\t\tthis._nodes.set(newNode.id, newNode);\n\n\t\t\t\t\tthis.emit(\"node found\", {\n\t\t\t\t\t\tid: newNode.id,\n\t\t\t\t\t});\n\n\t\t\t\t\t// We're communicating with the device, so assume it is alive\n\t\t\t\t\t// If it is actually a sleeping device, it will be marked as such later\n\t\t\t\t\tnewNode.markAsAlive();\n\n\t\t\t\t\tif (newNode.protocol == Protocols.ZWave) {\n\t\t\t\t\t\t// Assign SUC return route to make sure the node knows where to get its routes from\n\t\t\t\t\t\tnewNode.hasSUCReturnRoute = await this\n\t\t\t\t\t\t\t.assignSUCReturnRoutes(newNode.id);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Try perform the security bootstrap process. When replacing a node, we don't know any supported CCs\n\t\t\t\t\t// yet, so we need to trust the chosen inclusion strategy.\n\t\t\t\t\tconst strategy = this._inclusionOptions.strategy;\n\t\t\t\t\tlet bootstrapFailure: SecurityBootstrapFailure | undefined;\n\t\t\t\t\tif (strategy === InclusionStrategy.Security_S2) {\n\t\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS2(\n\t\t\t\t\t\t\tnewNode,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t\t\t.getHighestSecurityClass();\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tactualSecurityClass == undefined\n\t\t\t\t\t\t\t\t|| actualSecurityClass\n\t\t\t\t\t\t\t\t\t< SecurityClass.S2_Unauthenticated\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\t\tSecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (strategy === InclusionStrategy.Security_S0) {\n\t\t\t\t\t\tbootstrapFailure = await this.secureBootstrapS0(\n\t\t\t\t\t\t\tnewNode,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (bootstrapFailure == undefined) {\n\t\t\t\t\t\t\tconst actualSecurityClass = newNode\n\t\t\t\t\t\t\t\t.getHighestSecurityClass();\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tactualSecurityClass == undefined\n\t\t\t\t\t\t\t\t|| actualSecurityClass < SecurityClass.S0_Legacy\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tbootstrapFailure =\n\t\t\t\t\t\t\t\t\tSecurityBootstrapFailure.Unknown;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Remember that no security classes were granted\n\t\t\t\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\t\t\t\tnewNode.securityClasses.set(secClass, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// We're done adding this node, notify listeners. This also kicks off the node interview\n\t\t\t\t\tconst result: InclusionResult =\n\t\t\t\t\t\tbootstrapFailure != undefined\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tlowSecurity: true,\n\t\t\t\t\t\t\t\tlowSecurityReason: bootstrapFailure,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: { lowSecurity: false };\n\n\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\tthis.emit(\"node added\", newNode, result);\n\t\t\t\t}\n\n\t\t\t\t// stop here, don't emit inclusion failed\n\t\t\t\treturn true;\n\t\t}\n\n\t\tthis.emit(\"inclusion failed\");\n\n\t\treturn false; // Don't invoke any more handlers\n\t}\n\n\t/**\n\t * Is called when a RemoveNode request is received from the controller.\n\t * Handles and controls the exclusion process.\n\t */\n\tprivate async handleRemoveNodeStatusReport(\n\t\tmsg: RemoveNodeFromNetworkRequestStatusReport,\n\t): Promise<boolean> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`handling remove node request (status = ${\n\t\t\t\tRemoveNodeStatus[msg.status]\n\t\t\t})`,\n\t\t);\n\t\tif (this._inclusionState !== InclusionState.Excluding) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t` exclusion is NOT active, ignoring it...`,\n\t\t\t);\n\t\t\treturn true; // Don't invoke any more handlers\n\t\t}\n\n\t\tswitch (msg.status) {\n\t\t\tcase RemoveNodeStatus.Failed:\n\t\t\t\t// This code is handled elsewhere for starting the exclusion, so this means\n\t\t\t\t// that removing a node failed\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t`Removing the node failed`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tthis.emit(\"exclusion failed\");\n\n\t\t\t\t// in any case, stop the exclusion process so we don't accidentally remove another node\n\t\t\t\ttry {\n\t\t\t\t\tawait this.stopExclusion();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ok */\n\t\t\t\t}\n\t\t\t\treturn true; // Don't invoke any more handlers\n\n\t\t\tcase RemoveNodeStatus.RemovingSlave:\n\t\t\tcase RemoveNodeStatus.RemovingController: {\n\t\t\t\t// this is called when a node is removed\n\t\t\t\tthis._nodePendingExclusion = this.nodes.get(\n\t\t\t\t\tmsg.statusContext!.nodeId,\n\t\t\t\t);\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\t}\n\n\t\t\tcase RemoveNodeStatus.Reserved_0x05:\n\t\t\t// The reserved status can be triggered on some controllers by doing the following:\n\t\t\t// - factory reset the controller without excluding nodes\n\t\t\t// - include a new node with the same node ID as one on the previous network\n\t\t\t// - attempt to exclude the old node while the new node is responsive\n\t\t\tcase RemoveNodeStatus.Done: {\n\t\t\t\t// this is called when the exclusion was completed\n\t\t\t\t// stop the exclusion process so we don't accidentally remove another node\n\t\t\t\ttry {\n\t\t\t\t\tawait this.stopExclusionNoCallback();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ok */\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\tmsg.status === RemoveNodeStatus.Reserved_0x05\n\t\t\t\t\t|| !this._nodePendingExclusion\n\t\t\t\t) {\n\t\t\t\t\t// The exclusion did not succeed\n\t\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tconst nodeId = this._nodePendingExclusion.id;\n\t\t\t\tthis.driver.controllerLog.print(`Node ${nodeId} was removed`);\n\n\t\t\t\t// Avoid automatic re-inclusion using SmartStart if desired\n\t\t\t\tswitch (this._exclusionOptions?.strategy) {\n\t\t\t\t\tcase ExclusionStrategy.Unprovision:\n\t\t\t\t\t\tthis.unprovisionSmartStartNode(nodeId);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase ExclusionStrategy.DisableProvisioningEntry: {\n\t\t\t\t\t\tconst entry = this.getProvisioningEntryInternal(nodeId);\n\t\t\t\t\t\tif (entry) {\n\t\t\t\t\t\t\tentry.status = ProvisioningEntryStatus.Inactive;\n\t\t\t\t\t\t\tthis.provisionSmartStartNode(entry);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis._exclusionOptions = undefined;\n\n\t\t\t\t// notify listeners\n\t\t\t\tthis.emit(\n\t\t\t\t\t\"node removed\",\n\t\t\t\t\tthis._nodePendingExclusion,\n\t\t\t\t\tRemoveNodeReason.Excluded,\n\t\t\t\t);\n\t\t\t\t// and forget the node\n\t\t\t\tthis._nodes.delete(nodeId);\n\t\t\t\tthis._nodePendingExclusion = undefined;\n\n\t\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\t}\n\t\t}\n\t\t// not sure what to do with this message\n\t\treturn false;\n\t}\n\n\tprivate _rebuildRoutesProgress = new Map<number, RebuildRoutesStatus>();\n\t/**\n\t * If routes are currently being rebuilt for the entire network, this returns the current progress.\n\t * The information is the same as in the `\"rebuild routes progress\"` event.\n\t */\n\tpublic get rebuildRoutesProgress():\n\t\t| ReadonlyMap<\n\t\t\tnumber,\n\t\t\tRebuildRoutesStatus\n\t\t>\n\t\t| undefined\n\t{\n\t\tif (!this.isRebuildingRoutes) return undefined;\n\t\treturn new Map(this._rebuildRoutesProgress);\n\t}\n\n\t/**\n\t * Starts the process of rebuilding routes for all alive nodes in the network,\n\t * requesting updated neighbor lists and assigning fresh routes to\n\t * association targets.\n\t *\n\t * Returns `true` if the process was started, otherwise `false`. Also returns\n\t * `false` if the process was already active.\n\t */\n\tpublic beginRebuildingRoutes(options: RebuildRoutesOptions = {}): boolean {\n\t\t// Don't start the process twice\n\t\tconst existingTask = this.driver.scheduler.findTask(\n\t\t\t(t) => t.tag?.id === \"rebuild-routes\",\n\t\t);\n\t\tif (existingTask) return false;\n\n\t\toptions.includeSleeping ??= true;\n\t\toptions.deletePriorityReturnRoutes ??= false;\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`rebuilding routes${\n\t\t\t\toptions.includeSleeping ? \"\" : \" for mains-powered nodes\"\n\t\t\t}...`,\n\t\t);\n\n\t\t// Reset the progress for all nodes\n\t\tthis._rebuildRoutesProgress.clear();\n\t\tfor (const [id, node] of this._nodes) {\n\t\t\tif (id === this._ownNodeId) continue;\n\t\t\tif (\n\t\t\t\t// The node is known to be dead\n\t\t\t\tnode.status === NodeStatus.Dead\n\t\t\t\t// The node is assumed asleep but has never been interviewed.\n\t\t\t\t// It is most likely dead\n\t\t\t\t|| (node.status === NodeStatus.Asleep\n\t\t\t\t\t&& node.interviewStage === InterviewStage.ProtocolInfo)\n\t\t\t) {\n\t\t\t\t// Skip dead nodes\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tid,\n\t\t\t\t\t`Skipping route rebuild because the node is not responding.`,\n\t\t\t\t);\n\t\t\t\tthis._rebuildRoutesProgress.set(id, \"skipped\");\n\t\t\t} else if (!options.includeSleeping && node.canSleep) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tid,\n\t\t\t\t\t`Skipping route rebuild because the node is sleeping.`,\n\t\t\t\t);\n\t\t\t\tthis._rebuildRoutesProgress.set(id, \"skipped\");\n\t\t\t} else if (\n\t\t\t\t!options.deletePriorityReturnRoutes\n\t\t\t\t&& (this.getPrioritySUCReturnRouteCached(id)\n\t\t\t\t\t|| Object.keys(this.getPriorityReturnRoutesCached(id))\n\t\t\t\t\t\t\t.length > 0)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tid,\n\t\t\t\t\t`Skipping route rebuild because the node has priority return routes.`,\n\t\t\t\t);\n\t\t\t\tthis._rebuildRoutesProgress.set(id, \"skipped\");\n\t\t\t} else {\n\t\t\t\tthis._rebuildRoutesProgress.set(id, \"pending\");\n\t\t\t}\n\t\t}\n\n\t\t// Rebuild routes in the background\n\t\tvoid this.rebuildRoutesInternal(options).catch(noop);\n\n\t\t// And update the progress once at the start\n\t\tthis.emit(\n\t\t\t\"rebuild routes progress\",\n\t\t\tnew Map(this._rebuildRoutesProgress),\n\t\t);\n\n\t\treturn true;\n\t}\n\n\tprivate rebuildRoutesInternal(\n\t\toptions: RebuildRoutesOptions,\n\t): Promise<void> {\n\t\treturn this.driver.scheduler.queueTask(\n\t\t\tthis.getRebuildRoutesTask(options),\n\t\t);\n\t}\n\n\tprivate getRebuildRoutesTask(\n\t\toptions: RebuildRoutesOptions,\n\t): TaskBuilder<void> {\n\t\tconst pendingNodes = new Set(\n\t\t\t[...this._rebuildRoutesProgress]\n\t\t\t\t.filter(([, status]) => status === \"pending\")\n\t\t\t\t.map(([nodeId]) => nodeId),\n\t\t);\n\n\t\tconst todoListening: number[] = [];\n\t\tconst todoSleeping: number[] = [];\n\n\t\tconst addTodo = (nodeId: number) => {\n\t\t\t// Z-Wave Long Range does not route\n\t\t\tif (isLongRangeNodeId(nodeId)) return;\n\n\t\t\tif (pendingNodes.has(nodeId)) {\n\t\t\t\tpendingNodes.delete(nodeId);\n\t\t\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\t\t\tif (node.canSleep) {\n\t\t\t\t\tif (options.includeSleeping) {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t\t\"added to route rebuilding queue for sleeping nodes\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\ttodoSleeping.push(nodeId);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t\"added to route rebuilding queue for listening nodes\",\n\t\t\t\t\t);\n\t\t\t\t\ttodoListening.push(nodeId);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tconst self = this;\n\n\t\treturn {\n\t\t\tpriority: TaskPriority.Lower,\n\t\t\ttag: { id: \"rebuild-routes\" },\n\t\t\ttask: async function* rebuildRoutesTask() {\n\t\t\t\t// We work our way outwards from the controller and start with non-sleeping nodes, one by one\n\t\t\t\ttry {\n\t\t\t\t\tconst neighbors = await self.getNodeNeighbors(\n\t\t\t\t\t\tself._ownNodeId!,\n\t\t\t\t\t);\n\t\t\t\t\tneighbors.forEach((id) => addTodo(id));\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\n\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\tasync function* doRebuildRoutes(nodeId: number) {\n\t\t\t\t\t// Await the process for each node and convert errors to a non-successful result\n\t\t\t\t\tlet result: boolean;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst node = self.nodes.getOrThrow(nodeId);\n\t\t\t\t\t\tresult = yield () =>\n\t\t\t\t\t\t\tself.getRebuildNodeRoutesTask(node);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tresult = false;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Track the success in a map\n\t\t\t\t\tself._rebuildRoutesProgress.set(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tresult ? \"done\" : \"failed\",\n\t\t\t\t\t);\n\t\t\t\t\t// Notify listeners about the progress\n\t\t\t\t\tself.emit(\n\t\t\t\t\t\t\"rebuild routes progress\",\n\t\t\t\t\t\tnew Map(self._rebuildRoutesProgress),\n\t\t\t\t\t);\n\n\t\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\t\t// Figure out which nodes to do next\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst neighbors = await self.getNodeNeighbors(nodeId);\n\t\t\t\t\t\tneighbors.forEach((id) => addTodo(id));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t}\n\n\t\t\t\t\tyield; // Give the task scheduler time to do something else\n\t\t\t\t}\n\n\t\t\t\t// First try to rebuild routes for as many nodes as possible one by one\n\t\t\t\twhile (todoListening.length > 0) {\n\t\t\t\t\tconst nodeId = todoListening.shift()!;\n\t\t\t\t\tyield* doRebuildRoutes(nodeId);\n\t\t\t\t}\n\n\t\t\t\t// We might end up with a few unconnected listening nodes, try to rebuild routes for them too\n\t\t\t\tpendingNodes.forEach((nodeId) => addTodo(nodeId));\n\t\t\t\twhile (todoListening.length > 0) {\n\t\t\t\t\tconst nodeId = todoListening.shift()!;\n\t\t\t\t\tyield* doRebuildRoutes(nodeId);\n\t\t\t\t}\n\n\t\t\t\tif (options.includeSleeping) {\n\t\t\t\t\t// Now do all sleeping nodes at once\n\t\t\t\t\tself.driver.controllerLog.print(\n\t\t\t\t\t\t\"Rebuilding routes for sleeping nodes when they wake up\",\n\t\t\t\t\t);\n\n\t\t\t\t\tconst sleepingNodes = todoSleeping.map((nodeId) =>\n\t\t\t\t\t\tself.nodes.get(nodeId)\n\t\t\t\t\t).filter((node) => node != undefined);\n\n\t\t\t\t\tconst wakeupPromises = new Map(\n\t\t\t\t\t\tsleepingNodes.map((node) =>\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t\t\tnode.waitForWakeup().then(() => node),\n\t\t\t\t\t\t\t] as const\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\t// As long as there are sleeping nodes that haven't had their routes rebuilt yet,\n\t\t\t\t\t// wait for any of them to wake up\n\t\t\t\t\twhile (wakeupPromises.size > 0) {\n\t\t\t\t\t\tconst wakeUpPromise = Promise.race(\n\t\t\t\t\t\t\twakeupPromises.values(),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst wokenUpNode = (\n\t\t\t\t\t\t\tyield () => wakeUpPromise\n\t\t\t\t\t\t) as Awaited<typeof wakeUpPromise>;\n\t\t\t\t\t\tif (wokenUpNode.status === NodeStatus.Asleep) {\n\t\t\t\t\t\t\t// The node has gone to sleep again since the promise was resolved. Wait again\n\t\t\t\t\t\t\twakeupPromises.set(\n\t\t\t\t\t\t\t\twokenUpNode.id,\n\t\t\t\t\t\t\t\twokenUpNode.waitForWakeup().then(() =>\n\t\t\t\t\t\t\t\t\twokenUpNode\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Once the node has woken up, remove it from the list and rebuild its routes\n\t\t\t\t\t\twakeupPromises.delete(wokenUpNode.id);\n\t\t\t\t\t\tyield* doRebuildRoutes(wokenUpNode.id);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tself.driver.controllerLog.print(\n\t\t\t\t\t\"rebuilding routes completed\",\n\t\t\t\t);\n\n\t\t\t\tself.emit(\n\t\t\t\t\t\"rebuild routes done\",\n\t\t\t\t\tnew Map(self._rebuildRoutesProgress),\n\t\t\t\t);\n\n\t\t\t\t// We're done!\n\t\t\t\tself._rebuildRoutesProgress.clear();\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Stops the route rebuilding process. Resolves false if the process was not active, true otherwise.\n\t */\n\tpublic stopRebuildingRoutes(): boolean {\n\t\tconst hasTasks = !!this.driver.scheduler.findTask(isRebuildRoutesTask);\n\n\t\t// don't stop it twice\n\t\tif (!hasTasks) return false;\n\n\t\tthis.driver.controllerLog.print(`stopping route rebuilding process...`);\n\n\t\t// Stop all tasks that are part of the route rebuilding process\n\t\t// FIXME: This should be an async function that waits for the task removal\n\t\tvoid this.driver.scheduler.removeTasks(isRebuildRoutesTask).then(() => {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\"rebuilding routes aborted\",\n\t\t\t);\n\t\t});\n\n\t\t// Cancel all transactions that were created by the route rebuilding process\n\t\tthis.driver.rejectTransactions(\n\t\t\t(t) =>\n\t\t\t\tt.message instanceof RequestNodeNeighborUpdateRequest\n\t\t\t\t|| t.message instanceof DeleteReturnRouteRequest\n\t\t\t\t|| t.message instanceof AssignReturnRouteRequest,\n\t\t);\n\n\t\tthis._rebuildRoutesProgress.clear();\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Rebuilds routes for a single alive node in the network,\n\t * updating the neighbor list and assigning fresh routes to\n\t * association targets.\n\t *\n\t * Returns `true` if the process succeeded, `false` otherwise.\n\t */\n\tpublic async rebuildNodeRoutes(nodeId: number): Promise<boolean> {\n\t\t// We cannot rebuild routes for the controller\n\t\tif (nodeId === this._ownNodeId) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Rebuilding routes for the controller itself is not possible!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\t// Z-Wave Long Range does not route\n\t\tif (node.protocol == Protocols.ZWaveLongRange) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot rebuild routes for nodes using Z-Wave Long Range!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\t// Figure out if nodes are responsive before attempting to rebuild routes\n\t\tif (\n\t\t\t// The node is known to be dead\n\t\t\tnode.status === NodeStatus.Dead\n\t\t\t// The node is assumed asleep but has never been interviewed.\n\t\t\t// It is most likely dead\n\t\t\t|| (node.status === NodeStatus.Asleep\n\t\t\t\t&& node.interviewStage === InterviewStage.ProtocolInfo)\n\t\t) {\n\t\t\tif (!(await node.ping())) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tnodeId,\n\t\t\t\t\t`Cannot rebuild routes because the node is not responding.`,\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn this.rebuildNodeRoutesInternal(nodeId);\n\t}\n\n\tprivate rebuildNodeRoutesInternal(\n\t\tnodeId: number,\n\t): Promise<boolean> {\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\tconst task = this.getRebuildNodeRoutesTask(node);\n\t\tif (task instanceof Promise) return task;\n\n\t\treturn this.driver.scheduler.queueTask(task);\n\t}\n\n\tprivate getRebuildNodeRoutesTask(\n\t\tnode: ZWaveNode,\n\t): Promise<boolean> | TaskBuilder<boolean> {\n\t\t// This task should only run once at a time\n\t\tconst existingTask = this.driver.scheduler.findTask<boolean>((t) =>\n\t\t\tt.tag?.id === \"rebuild-node-routes\" && t.tag.nodeId === node.id\n\t\t);\n\t\tif (existingTask) return existingTask;\n\n\t\tconst self = this;\n\t\tlet keepAwake: boolean;\n\n\t\treturn {\n\t\t\t// This task is executed by users and by the network-wide route rebuilding process.\n\t\t\tpriority: TaskPriority.Lower,\n\t\t\ttag: { id: \"rebuild-node-routes\", nodeId: node.id },\n\t\t\ttask: async function* rebuildNodeRoutesTask() {\n\t\t\t\t// Keep battery powered nodes awake during the process\n\t\t\t\tkeepAwake = node.keepAwake;\n\t\t\t\tnode.keepAwake = true;\n\n\t\t\t\tif (\n\t\t\t\t\tnode.canSleep && node.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t\t\t) {\n\t\t\t\t\tyield () => node.waitForWakeup();\n\t\t\t\t}\n\n\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage: `Rebuilding routes...`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t});\n\n\t\t\t\t// The process consists of four steps, each step is tried up to 5 times before it is considered failed\n\t\t\t\tconst maxAttempts = 5;\n\n\t\t\t\t// 1. command the node to refresh its neighbor list\n\t\t\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`refreshing neighbor list (attempt ${attempt})...`,\n\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t});\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await self.discoverNodeNeighbors(\n\t\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (result) {\n\t\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\t\tmessage: \"neighbor list refreshed...\",\n\t\t\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t// this step was successful, continue with the next\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\t\tmessage: \"refreshing neighbor list failed...\",\n\t\t\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tself.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t\t`refreshing neighbor list failed: ${\n\t\t\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (attempt === maxAttempts) {\n\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`rebuilding routes failed: could not update the neighbor list after ${maxAttempts} attempts`,\n\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\t// 2. re-create the SUC return route, just in case\n\t\t\t\tnode.hasSUCReturnRoute = await self.assignSUCReturnRoutes(\n\t\t\t\t\tnode.id,\n\t\t\t\t);\n\n\t\t\t\t// 3. delete all return routes to get rid of potential priority return routes\n\t\t\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`deleting return routes (attempt ${attempt})...`,\n\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t});\n\n\t\t\t\t\tif (await self.deleteReturnRoutes(node.id)) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (attempt === maxAttempts) {\n\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`rebuilding routes failed: failed to delete return routes after ${maxAttempts} attempts`,\n\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// 4. Assign return routes to all association destinations...\n\t\t\t\tlet associatedNodes: number[] = [];\n\t\t\t\ttry {\n\t\t\t\t\tassociatedNodes = distinct(\n\t\t\t\t\t\tflatMap<number, AssociationAddress[]>(\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t...(self.getAssociations({ nodeId: node.id })\n\t\t\t\t\t\t\t\t\t.values() as any),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t(assocs: AssociationAddress[]) =>\n\t\t\t\t\t\t\t\tassocs.map((a) => a.nodeId),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\t\t// ...except the controller itself, which was handled by step 2\n\t\t\t\t\t\t.filter((id) => id !== self._ownNodeId!)\n\t\t\t\t\t\t// ...and the node itself\n\t\t\t\t\t\t.filter((id) => id !== node.id)\n\t\t\t\t\t\t.sort();\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\n\t\t\t\tif (associatedNodes.length > 0) {\n\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`assigning return routes to the following nodes:\n\t${associatedNodes.join(\", \")}`,\n\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t});\n\t\t\t\t\tfor (const destinationNodeId of associatedNodes) {\n\t\t\t\t\t\tfor (\n\t\t\t\t\t\t\tlet attempt = 1;\n\t\t\t\t\t\t\tattempt <= maxAttempts;\n\t\t\t\t\t\t\tattempt++\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tyield; // Give the task scheduler time to do something else\n\n\t\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\t`assigning return route to node ${destinationNodeId} (attempt ${attempt})...`,\n\t\t\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tawait self.assignReturnRoutes(\n\t\t\t\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// this step was successful, continue with the next\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (attempt === maxAttempts) {\n\t\t\t\t\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\t\t`rebuilding routes failed: failed to assign return route after ${maxAttempts} attempts`,\n\t\t\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tself.driver.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage: `rebuilt routes successfully`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t});\n\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tcleanup: () => {\n\t\t\t\t// Make sure that the keepAwake flag gets reset at the end\n\t\t\t\tnode.keepAwake = keepAwake;\n\t\t\t\tif (!keepAwake) {\n\t\t\t\t\tsetImmediate(() => {\n\t\t\t\t\t\tthis.driver.debounceSendNodeToSleep(node);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t};\n\t}\n\n\t/** Configures the given Node to be SUC/SIS or not */\n\tpublic async configureSUC(\n\t\tnodeId: number,\n\t\tenableSUC: boolean,\n\t\tenableSIS: boolean,\n\t): Promise<boolean> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\tMessage & SuccessIndicator\n\t\t>(\n\t\t\tnew SetSUCNodeIdRequest({\n\t\t\t\townNodeId: this.ownNodeId!,\n\t\t\t\tsucNodeId: nodeId,\n\t\t\t\tenableSUC,\n\t\t\t\tenableSIS,\n\t\t\t}),\n\t\t);\n\n\t\treturn result.isOK();\n\t}\n\n\t// After a lot of experimenting, it seems to make sense to document how assigning return routes works in the controller.\n\t// Each node has a list of 4 return routes per destination (and probably a separate list for the SUC):\n\t// - #0, repeaters..., speed, wakeup\n\t// - #1, repeaters..., speed, wakeup\n\t// - #2, repeaters..., speed, wakeup\n\t// - #3, repeaters..., speed, wakeup\n\t//\n\t// Empty slots are filled with 0 repeaters, 9.6kbit/s, no wakeup\n\t//\n\t// Calling assignReturnRoute will assign all 4 slots, some of which may be empty.\n\t// Calling deleteReturnRoute will assign an empty route to all 4 slots.\n\t//\n\t// Priority return routes are indicated by a separate \"pointer\" byte which tells the node which route is the priority.\n\t// Calling assignPriorityReturnRoute will first assign 4 routes, one of which is then marked as priority.\n\t// This is not fully understood yet, but it seems that the priority route is actually the last non-empty route.\n\t// If the priority byte points to an empty route, it is ignored.\n\t//\n\t// Calling assignReturnRoute after having assigned a priority return route will not clear that pointer byte. This\n\t// means that a previously-assigned priority route can randomly change if assignReturnRoute assigns enough routes.\n\t// deleteReturnRoute does also clear the priority byte.\n\n\t/**\n\t * Instructs the controller to assign static routes from the given end node to the SUC.\n\t * This will assign up to 4 routes, depending on the network topology (that the controller knows about).\n\t */\n\tpublic async assignSUCReturnRoutes(nodeId: number): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Assigning SUC return route...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\t// Since there is only one SUC, we can do the right thing here and delete all routes first, which clears any dangling priority return routes.\n\t\t// Afterwards, we'll set up all routes again anyways.\n\t\tawait this.deleteSUCReturnRoutes(nodeId);\n\n\t\ttry {\n\t\t\t// Some controllers have a bug where they incorrectly respond\n\t\t\t// to an AssignSUCReturnRouteRequest with DeleteSUCReturnRoute\n\t\t\tconst disableCallbackFunctionTypeCheck = !!this.driver\n\t\t\t\t.getDeviceConfig?.(this.ownNodeId!)\n\t\t\t\t?.compat\n\t\t\t\t?.disableCallbackFunctionTypeCheck\n\t\t\t\t?.includes(FunctionType.AssignSUCReturnRoute);\n\t\t\tconst result = await this.driver.sendMessage(\n\t\t\t\tnew AssignSUCReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tdisableCallbackFunctionTypeCheck,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (\n\t\t\t\t!(result instanceof AssignSUCReturnRouteRequestTransmitReport)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tnodeId,\n\t\t\t\t\t`Assigning SUC return route failed: Invalid callback received`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// Custom assigned are no longer valid\n\t\t\t\tthis.setCustomSUCReturnRoutesCached(nodeId, undefined);\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Assigning SUC return route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Returns which custom static routes are currently assigned from the given end node to the SUC.\n\t *\n\t * **Note:** This only considers routes that were assigned using {@link assignCustomSUCReturnRoutes}.\n\t * If another controller has assigned routes in the meantime, this information may be out of date.\n\t */\n\tpublic getCustomSUCReturnRoutesCached(nodeId: number): Route[] {\n\t\treturn (\n\t\t\tthis.driver.cacheGet<Route[]>(\n\t\t\t\tcacheKeys.node(nodeId).customSUCReturnRoutes,\n\t\t\t) ?? []\n\t\t);\n\t}\n\n\tprivate setCustomSUCReturnRoutesCached(\n\t\tnodeId: number,\n\t\troutes: Route[] | undefined,\n\t): void {\n\t\tthis.driver.cacheSet(\n\t\t\tcacheKeys.node(nodeId).customSUCReturnRoutes,\n\t\t\troutes,\n\t\t);\n\t}\n\n\t/**\n\t * Assigns static routes from the given end node to the SUC. Unlike {@link assignSUCReturnRoutes}, this method assigns\n\t * the given routes instead of having the controller calculate them. At most 4 routes can be assigned. If less are\n\t * specified, the remaining routes are cleared.\n\t *\n\t * To mark a route as a priority route, pass it as the optional `priorityRoute` parameter. At most 3 routes of the\n\t * `routes` array will then be used as fallback routes.\n\t *\n\t * **Note:** Calling {@link assignSUCReturnRoutes} or {@link deleteSUCReturnRoutes} will override the custom routes.\n\t *\n\t * Returns `true` when the process was successful, or `false` if at least one step failed.\n\t */\n\tpublic async assignCustomSUCReturnRoutes(\n\t\tnodeId: number,\n\t\troutes: Route[],\n\t\tpriorityRoute?: Route,\n\t): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Assigning custom SUC return routes...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\t// Since there is only one SUC, we can do the right thing here and delete all routes first, which clears the priority return routes.\n\t\tawait this.deleteSUCReturnRoutes(nodeId);\n\n\t\tlet result = true;\n\t\tconst MAX_ROUTES = 4;\n\n\t\t// Keep track of which routes have been assigned\n\t\tconst assignedRoutes = new Array(MAX_ROUTES).fill(EMPTY_ROUTE);\n\n\t\tlet priorityRouteIndex = -1;\n\t\t// If a priority route is given, add it to the end of the routes array to mimick what the Z-Wave controller does\n\t\tif (priorityRoute) {\n\t\t\tpriorityRouteIndex = Math.min(MAX_ROUTES - 1, routes.length);\n\t\t\troutes[priorityRouteIndex] = priorityRoute;\n\t\t}\n\n\t\tfor (let i = 0; i < MAX_ROUTES; i++) {\n\t\t\tconst route = routes[i] ?? EMPTY_ROUTE;\n\t\t\tconst isEmpty = isEmptyRoute(route);\n\n\t\t\t// We are always listening\n\t\t\tconst targetWakeup = false;\n\n\t\t\tconst cc = new ZWaveProtocolCCAssignSUCReturnRoute({\n\t\t\t\tnodeId,\n\t\t\t\t// Empty routes are marked with a nodeId of 0\n\t\t\t\tdestinationNodeId: isEmpty ? 0 : this.ownNodeId ?? 1,\n\t\t\t\trouteIndex: i,\n\t\t\t\trepeaters: route.repeaters,\n\t\t\t\tdestinationSpeed: route.routeSpeed,\n\t\t\t\tdestinationWakeUp: FLiRS2WakeUpTime(targetWakeup ?? false),\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendZWaveProtocolCC(cc);\n\n\t\t\t\t// Remember that this route has been assigned\n\t\t\t\tif (i !== priorityRouteIndex) assignedRoutes[i] = route;\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage: `Assigning custom SUC return route #${i} failed`,\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\n\t\t\t\tresult = false;\n\t\t\t}\n\t\t}\n\n\t\t// If a priority route was passed, tell the node to use it\n\t\tif (priorityRouteIndex >= 0) {\n\t\t\tconst cc = new ZWaveProtocolCCAssignSUCReturnRoutePriority({\n\t\t\t\tnodeId,\n\t\t\t\ttargetNodeId: this.ownNodeId ?? 1,\n\t\t\t\trouteNumber: priorityRouteIndex,\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendZWaveProtocolCC(cc);\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Marking custom SUC return route as priority failed`,\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\n\t\t\t\tresult = false;\n\t\t\t}\n\t\t}\n\n\t\t// Trim empty routes off the end. We may end up with empty routes in the middle\n\t\t// if an assignment fails.\n\t\twhile (\n\t\t\tassignedRoutes.length > 0\n\t\t\t&& isEmptyRoute(assignedRoutes.at(-1))\n\t\t) {\n\t\t\tassignedRoutes.pop();\n\t\t}\n\n\t\t// Remember the routes we assigned\n\t\tthis.setPrioritySUCReturnRouteCached(nodeId, priorityRoute);\n\t\tthis.setCustomSUCReturnRoutesCached(nodeId, assignedRoutes);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Instructs the controller to assign static routes from the given end node to the SUC.\n\t * This will assign up to 4 routes, depending on the network topology (that the controller knows about).\n\t */\n\tpublic async deleteSUCReturnRoutes(nodeId: number): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Deleting SUC return route...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\t// Some controllers have a bug where they incorrectly respond\n\t\t\t// to an DeleteSUCReturnRouteRequest with a different function type\n\t\t\tconst disableCallbackFunctionTypeCheck = !!this.driver\n\t\t\t\t.getDeviceConfig?.(this.ownNodeId!)\n\t\t\t\t?.compat\n\t\t\t\t?.disableCallbackFunctionTypeCheck\n\t\t\t\t?.includes(FunctionType.DeleteSUCReturnRoute);\n\t\t\tconst result = await this.driver.sendMessage(\n\t\t\t\tnew DeleteSUCReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tdisableCallbackFunctionTypeCheck,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (\n\t\t\t\t!(result instanceof DeleteSUCReturnRouteRequestTransmitReport)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tnodeId,\n\t\t\t\t\t`Deleting SUC return route failed: Invalid callback received`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// Custom assigned and priority return routes are no longer valid\n\t\t\t\tthis.setPrioritySUCReturnRouteCached(nodeId, undefined);\n\t\t\t\tthis.setCustomSUCReturnRoutesCached(nodeId, undefined);\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Deleting SUC return route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Returns which custom static routes are currently assigned between the given end nodes.\n\t *\n\t * **Note:** This only considers routes that were assigned using {@link assignCustomReturnRoutes}.\n\t * If another controller has assigned routes in the meantime, this information may be out of date.\n\t */\n\tpublic getCustomReturnRoutesCached(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t): Route[] {\n\t\treturn (\n\t\t\tthis.driver.cacheGet<Route[]>(\n\t\t\t\tcacheKeys.node(nodeId).customReturnRoutes(destinationNodeId),\n\t\t\t) ?? []\n\t\t);\n\t}\n\n\tprivate setCustomReturnRoutesCached(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t\troutes: Route[] | undefined,\n\t): void {\n\t\tthis.driver.cacheSet(\n\t\t\tcacheKeys.node(nodeId).customReturnRoutes(destinationNodeId),\n\t\t\troutes,\n\t\t);\n\t}\n\n\tprivate clearCustomReturnRoutesCached(nodeId: number): void {\n\t\t// This is a bit ugly, but the best we can do right now.\n\t\tfor (let dest = 1; dest <= MAX_NODES; dest++) {\n\t\t\tthis.setCustomReturnRoutesCached(nodeId, dest, undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Instructs the controller to assign static routes between the two given end nodes.\n\t * This will assign up to 4 routes, depending on the network topology (that the controller knows about).\n\t */\n\tpublic async assignReturnRoutes(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t} else if (isLongRangeNodeId(destinationNodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tdestinationNodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\t// Make sure this is not misused by passing the controller's node ID\n\t\tif (destinationNodeId === this.ownNodeId) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`To assign a return route to the SUC (node ID ${destinationNodeId}), assignSUCReturnRoutes() must be used!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Assigning return routes to node ${destinationNodeId}...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tAssignReturnRouteRequestTransmitReport\n\t\t\t>(\n\t\t\t\tnew AssignReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// Custom assigned are no longer valid\n\t\t\t\tthis.setCustomReturnRoutesCached(\n\t\t\t\t\tnodeId,\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\tundefined,\n\t\t\t\t);\n\t\t\t\t// The priority route probably is invalid too now, but it may also point to a random route\n\t\t\t\tif (\n\t\t\t\t\tthis.hasPriorityReturnRouteCached(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\t) !== false\n\t\t\t\t) {\n\t\t\t\t\tthis.setPriorityReturnRouteCached(\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\t\tUNKNOWN_STATE,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Assigning return routes failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Assigns static routes between the two given end nodes. Unlike {@link assignReturnRoutes}, this method assigns\n\t * the given routes instead of having the controller calculate them. At most 4 routes can be assigned. If less are\n\t * specified, the remaining routes are cleared.\n\t *\n\t * **Note:** Calling {@link assignReturnRoutes} or {@link deleteReturnRoutes} will override the custom routes.\n\t */\n\tpublic async assignCustomReturnRoutes(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t\troutes: Route[],\n\t\tpriorityRoute?: Route,\n\t): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t} else if (isLongRangeNodeId(destinationNodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tdestinationNodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\t// Make sure this is not misused by passing the controller's node ID\n\t\tif (destinationNodeId === this.ownNodeId) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`To assign custom return routes to the SUC (node ID ${destinationNodeId}), assignCustomSUCReturnRoutes() must be used!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage:\n\t\t\t\t`Assigning custom return routes to node ${destinationNodeId}...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\tlet result = true;\n\t\tconst MAX_ROUTES = 4;\n\n\t\t// Keep track of which routes have been assigned\n\t\tconst assignedRoutes = new Array(MAX_ROUTES).fill(EMPTY_ROUTE);\n\n\t\tlet priorityRouteIndex = -1;\n\t\t// If a priority route is given, add it to the end of the routes array to mimick what the Z-Wave controller does\n\t\tif (priorityRoute) {\n\t\t\tpriorityRouteIndex = Math.min(MAX_ROUTES - 1, routes.length);\n\t\t\troutes[priorityRouteIndex] = priorityRoute;\n\t\t}\n\n\t\tfor (let i = 0; i < MAX_ROUTES; i++) {\n\t\t\tconst route = routes[i] ?? EMPTY_ROUTE;\n\t\t\tconst isEmpty = isEmptyRoute(route);\n\n\t\t\tconst targetWakeup = !isEmpty\n\t\t\t\t? this.nodes.get(destinationNodeId)?.isFrequentListening\n\t\t\t\t: undefined;\n\n\t\t\tconst cc = new ZWaveProtocolCCAssignReturnRoute({\n\t\t\t\tnodeId,\n\t\t\t\t// Empty routes are marked with a nodeId of 0\n\t\t\t\tdestinationNodeId: isEmpty ? 0 : destinationNodeId,\n\t\t\t\trouteIndex: i,\n\t\t\t\trepeaters: route.repeaters,\n\t\t\t\tdestinationSpeed: route.routeSpeed,\n\t\t\t\tdestinationWakeUp: FLiRS2WakeUpTime(targetWakeup ?? false),\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendZWaveProtocolCC(cc);\n\n\t\t\t\t// Remember that this route has been assigned\n\t\t\t\tif (i !== priorityRouteIndex) assignedRoutes[i] = route;\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage: `Assigning custom return route #${i} failed`,\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\n\t\t\t\tresult = false;\n\t\t\t}\n\t\t}\n\n\t\t// If a priority route was passed, tell the node to use it\n\t\tif (priorityRouteIndex >= 0) {\n\t\t\tconst cc = new ZWaveProtocolCCAssignReturnRoutePriority({\n\t\t\t\tnodeId,\n\t\t\t\ttargetNodeId: destinationNodeId,\n\t\t\t\trouteNumber: priorityRouteIndex,\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tawait this.driver.sendZWaveProtocolCC(cc);\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage: `Marking custom return route as priority failed`,\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\n\t\t\t\tresult = false;\n\t\t\t}\n\t\t}\n\n\t\t// Trim empty routes off the end. We may end up with empty routes in the middle\n\t\t// if an assignment fails.\n\t\twhile (\n\t\t\tassignedRoutes.length > 0\n\t\t\t&& isEmptyRoute(assignedRoutes.at(-1))\n\t\t) {\n\t\t\tassignedRoutes.pop();\n\t\t}\n\n\t\tthis.setCustomReturnRoutesCached(\n\t\t\tnodeId,\n\t\t\tdestinationNodeId,\n\t\t\tassignedRoutes,\n\t\t);\n\t\tif (priorityRoute) {\n\t\t\tthis.setPriorityReturnRouteCached(\n\t\t\t\tnodeId,\n\t\t\t\tdestinationNodeId,\n\t\t\t\tpriorityRoute,\n\t\t\t);\n\t\t} else if (\n\t\t\tthis.hasPriorityReturnRouteCached(nodeId, destinationNodeId)\n\t\t\t\t!== false\n\t\t) {\n\t\t\t// The priority route is probably invalid now, but it may also point to a random route\n\t\t\tthis.setPriorityReturnRouteCached(\n\t\t\t\tnodeId,\n\t\t\t\tdestinationNodeId,\n\t\t\t\tUNKNOWN_STATE,\n\t\t\t);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Instructs the controller to delete all static routes between the given node and all\n\t * other end nodes, including the priority return routes.\n\t */\n\tpublic async deleteReturnRoutes(nodeId: number): Promise<boolean> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Cannot manage routes for nodes using Z-Wave Long Range!`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Deleting all return routes...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tDeleteReturnRouteRequestTransmitReport\n\t\t\t>(\n\t\t\t\tnew DeleteReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// All custom assigned routes are no longer valid\n\t\t\t\tthis.clearPriorityReturnRoutesCached(nodeId);\n\t\t\t\tthis.clearCustomReturnRoutesCached(nodeId);\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Deleting return routes failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Assigns a priority route between two end nodes. This route will always be used for the first transmission attempt.\n\t * @param nodeId The ID of the source node of the route\n\t * @param destinationNodeId The ID of the destination node of the route\n\t * @param repeaters The IDs of the nodes that should be used as repeaters, or an empty array for direct connection\n\t * @param routeSpeed The transmission speed to use for the route\n\t */\n\tpublic async assignPriorityReturnRoute(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t\trepeaters: number[],\n\t\trouteSpeed: ZWaveDataRate,\n\t): Promise<boolean> {\n\t\t// Make sure this is not misused by passing the controller's node ID\n\t\tif (destinationNodeId === this.ownNodeId) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`To assign a priority return route to the SUC (node ID ${destinationNodeId}), assignPrioritySUCReturnRoute() must be used!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage:\n\t\t\t\t`Assigning priority return route to node ${destinationNodeId}...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tAssignReturnRouteRequestTransmitReport\n\t\t\t>(\n\t\t\t\tnew AssignPriorityReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\trepeaters,\n\t\t\t\t\trouteSpeed,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// Update the cached priority route\n\t\t\t\tthis.setPriorityReturnRouteCached(nodeId, destinationNodeId, {\n\t\t\t\t\trepeaters,\n\t\t\t\t\trouteSpeed,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Assigning priority return route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate hasPriorityReturnRouteCached(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t): MaybeUnknown<boolean> {\n\t\tconst ret = this.driver.cacheGet<MaybeUnknown<Route>>(\n\t\t\tcacheKeys.node(nodeId).priorityReturnRoute(destinationNodeId),\n\t\t);\n\t\tif (ret === UNKNOWN_STATE) return UNKNOWN_STATE;\n\t\treturn ret !== undefined;\n\t}\n\n\tprivate setPriorityReturnRouteCached(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t\troute: MaybeUnknown<Route> | undefined,\n\t): void {\n\t\tthis.driver.cacheSet(\n\t\t\tcacheKeys.node(nodeId).priorityReturnRoute(destinationNodeId),\n\t\t\troute,\n\t\t);\n\t}\n\n\tprivate clearPriorityReturnRoutesCached(nodeId: number): void {\n\t\t// This is a bit ugly, but the best we can do right now.\n\t\tfor (let dest = 1; dest <= MAX_NODES; dest++) {\n\t\t\tthis.setPriorityReturnRouteCached(nodeId, dest, undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Returns which priority route is currently assigned between the given end nodes.\n\t *\n\t * **Note:** This is using cached information, since there's no way to query priority routes from a node.\n\t * If another controller has assigned routes in the meantime, this information may be out of date.\n\t */\n\tpublic getPriorityReturnRouteCached(\n\t\tnodeId: number,\n\t\tdestinationNodeId: number,\n\t): MaybeUnknown<Route> | undefined {\n\t\treturn this.driver.cacheGet(\n\t\t\tcacheKeys.node(nodeId).priorityReturnRoute(destinationNodeId),\n\t\t);\n\t}\n\n\t/**\n\t * For the given node, returns all end node destinations and the priority routes to them.\n\t *\n\t * **Note:** This is using cached information, since there's no way to query priority routes from a node.\n\t * If another controller has assigned routes in the meantime, this information may be out of date.\n\t */\n\tpublic getPriorityReturnRoutesCached(\n\t\tnodeId: number,\n\t): Record<number, Route> {\n\t\tconst ret: Record<number, Route> = {};\n\n\t\tconst routes = this.driver.cacheList<Route>(\n\t\t\tcacheKeys.node(nodeId)._priorityReturnRouteBaseKey,\n\t\t);\n\t\tfor (const [key, route] of Object.entries(routes)) {\n\t\t\tconst destination = cacheKeyUtils\n\t\t\t\t.destinationFromPriorityReturnRouteKey(key);\n\t\t\tif (destination !== undefined) ret[destination] = route;\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Assigns a priority route from an end node to the SUC. This route will always be used for the first transmission attempt.\n\t * @param nodeId The ID of the end node for which to assign the route\n\t * @param repeaters The IDs of the nodes that should be used as repeaters, or an empty array for direct connection\n\t * @param routeSpeed The transmission speed to use for the route\n\t */\n\tpublic async assignPrioritySUCReturnRoute(\n\t\tnodeId: number,\n\t\trepeaters: number[],\n\t\trouteSpeed: ZWaveDataRate,\n\t): Promise<boolean> {\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: `Assigning priority SUC return route...`,\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tAssignPrioritySUCReturnRouteRequestTransmitReport\n\t\t\t>(\n\t\t\t\tnew AssignPrioritySUCReturnRouteRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\trepeaters,\n\t\t\t\t\trouteSpeed,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tconst success = this.handleRouteAssignmentTransmitReport(\n\t\t\t\tresult,\n\t\t\t\tnodeId,\n\t\t\t);\n\t\t\tif (success) {\n\t\t\t\t// Update the cached priority route\n\t\t\t\tthis.setPrioritySUCReturnRouteCached(nodeId, {\n\t\t\t\t\trepeaters,\n\t\t\t\t\trouteSpeed,\n\t\t\t\t});\n\t\t\t\t// The command above assigns a full set of new routes, so\n\t\t\t\t// custom SUC return routes are no longer valid\n\t\t\t\tthis.setCustomSUCReturnRoutesCached(nodeId, undefined);\n\t\t\t}\n\t\t\treturn success;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Assigning priority SUC return route failed: ${\n\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\te,\n\t\t\t\t\t)\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate setPrioritySUCReturnRouteCached(\n\t\tnodeId: number,\n\t\troute: Route | undefined,\n\t): void {\n\t\tthis.driver.cacheSet(\n\t\t\tcacheKeys.node(nodeId).prioritySUCReturnRoute,\n\t\t\troute,\n\t\t);\n\t}\n\n\t/**\n\t * Returns which priority route is currently assigned from the given end node to the SUC.\n\t *\n\t * **Note:** This is using cached information, since there's no way to query priority routes from a node.\n\t * If another controller has assigned routes in the meantime, this information may be out of date.\n\t */\n\tpublic getPrioritySUCReturnRouteCached(nodeId: number): Route | undefined {\n\t\treturn this.driver.cacheGet(\n\t\t\tcacheKeys.node(nodeId).prioritySUCReturnRoute,\n\t\t);\n\t}\n\n\tprivate handleRouteAssignmentTransmitReport(\n\t\tmsg: TransmitReport,\n\t\tnodeId: number,\n\t): boolean {\n\t\tswitch (msg.transmitStatus) {\n\t\t\tcase TransmitStatus.OK:\n\t\t\t\treturn true;\n\t\t\tcase TransmitStatus.NoAck:\n\t\t\t\treturn false;\n\t\t\tcase TransmitStatus.NoRoute:\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tnodeId,\n\t\t\t\t\t`Route resolution failed`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\treturn false;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Sets the priority route which will always be used for the first transmission attempt from the controller to the given node.\n\t * @param destinationNodeId The ID of the node that should be reached via the priority route\n\t * @param repeaters The IDs of the nodes that should be used as repeaters, or an empty array for direct connection\n\t * @param routeSpeed The transmission speed to use for the route\n\t */\n\tpublic async setPriorityRoute(\n\t\tdestinationNodeId: number,\n\t\trepeaters: number[],\n\t\trouteSpeed: ZWaveDataRate,\n\t): Promise<boolean> {\n\t\t// 7.xx firmwares (up to at least 7.19.2) have a bug where the response to\n\t\t// SetPriorityRoute is missing the result byte when used with 16-bit node IDs.\n\t\t// So we temporarily switch back to 8-bit node IDs for this message\n\t\tawait this.trySetNodeIDType(NodeIDType.Short);\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`Setting priority route to node ${destinationNodeId}...`,\n\t\t);\n\n\t\tlet ret: boolean;\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetPriorityRouteRequest({\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\trepeaters,\n\t\t\t\t\trouteSpeed,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tret = result.isOK();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Setting priority route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\tret = false;\n\t\t}\n\n\t\t// Switch back to 16-bit node IDs\n\t\tawait this.trySetNodeIDType(NodeIDType.Long);\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Removes the priority route used for the first transmission attempt from the controller to the given node.\n\t * @param destinationNodeId The ID of the node that should be reached via the priority route\n\t */\n\tpublic async removePriorityRoute(\n\t\tdestinationNodeId: number,\n\t): Promise<boolean> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`Removing priority route to node ${destinationNodeId}...`,\n\t\t);\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetPriorityRouteRequest({\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t\t// no repeaters = remove\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\treturn result.isOK();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Removing priority route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the priority route which is currently set for a node.\n\t * If none is set, either the LWR or the NLWR is returned.\n\t * If no route is known yet, this returns `undefined`.\n\t *\n\t * @param destinationNodeId The ID of the node for which the priority route should be returned\n\t */\n\tpublic async getPriorityRoute(destinationNodeId: number): Promise<\n\t\t| {\n\t\t\trouteKind:\n\t\t\t\t| RouteKind.LWR\n\t\t\t\t| RouteKind.NLWR\n\t\t\t\t| RouteKind.Application;\n\t\t\trepeaters: number[];\n\t\t\trouteSpeed: ZWaveDataRate;\n\t\t}\n\t\t| undefined\n\t> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`Retrieving priority route to node ${destinationNodeId}...`,\n\t\t);\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tGetPriorityRouteResponse\n\t\t\t>(\n\t\t\t\tnew GetPriorityRouteRequest({\n\t\t\t\t\tdestinationNodeId,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (result.routeKind === RouteKind.None) return undefined;\n\n\t\t\t// If we do not have any route statistics for the node yet, use this information to\n\t\t\t// to at least partially populate it\n\t\t\tconst node = this.nodes.get(destinationNodeId);\n\t\t\tif (\n\t\t\t\tnode\n\t\t\t\t&& (result.routeKind === RouteKind.LWR\n\t\t\t\t\t|| result.routeKind === RouteKind.NLWR)\n\t\t\t) {\n\t\t\t\tconst routeName = result.routeKind === RouteKind.LWR\n\t\t\t\t\t? \"lwr\"\n\t\t\t\t\t: \"nlwr\";\n\n\t\t\t\tif (!node.statistics[routeName]) {\n\t\t\t\t\tnode.updateStatistics((current) => {\n\t\t\t\t\t\tconst ret = { ...current };\n\t\t\t\t\t\tret[routeName] = {\n\t\t\t\t\t\t\trepeaters: result.repeaters!,\n\t\t\t\t\t\t\tprotocolDataRate:\n\t\t\t\t\t\t\t\t// ZWaveDataRate is a subset of ProtocolDataRate\n\t\t\t\t\t\t\t\tresult\n\t\t\t\t\t\t\t\t\t.routeSpeed as unknown as ProtocolDataRate,\n\t\t\t\t\t\t};\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\trouteKind: result.routeKind,\n\t\t\t\trepeaters: result.repeaters!,\n\t\t\t\trouteSpeed: result.routeSpeed!,\n\t\t\t};\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Retrieving priority route failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Returns a dictionary of all association groups of this node or endpoint and their information.\n\t * If no endpoint is given, the associations of the root device (endpoint 0) are returned.\n\t * This only works AFTER the interview process\n\t */\n\tpublic getAssociationGroups(\n\t\tsource: AssociationAddress,\n\t): ReadonlyMap<number, AssociationGroup> {\n\t\tconst node = this.nodes.getOrThrow(source.nodeId);\n\t\tconst endpoint = node.getEndpointOrThrow(source.endpoint ?? 0);\n\n\t\treturn ccUtils.getAssociationGroups(this.driver, endpoint);\n\t}\n\n\t/**\n\t * Returns all association groups that exist on a node and all its endpoints.\n\t * The returned map uses the endpoint index as keys and its values are maps of group IDs to their definition\n\t */\n\tpublic getAllAssociationGroups(\n\t\tnodeId: number,\n\t): ReadonlyMap<number, ReadonlyMap<number, AssociationGroup>> {\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\treturn ccUtils.getAllAssociationGroups(this.driver, node);\n\t}\n\n\t/**\n\t * Returns all associations (Multi Channel or normal) that are configured on the root device or an endpoint of a node.\n\t * If no endpoint is given, the associations of the root device (endpoint 0) are returned.\n\t */\n\tpublic getAssociations(\n\t\tsource: AssociationAddress,\n\t): ReadonlyMap<number, readonly AssociationAddress[]> {\n\t\tconst node = this.nodes.getOrThrow(source.nodeId);\n\t\tconst endpoint = node.getEndpointOrThrow(source.endpoint ?? 0);\n\n\t\treturn ccUtils.getAssociations(this.driver, endpoint);\n\t}\n\n\t/**\n\t * Returns all associations (Multi Channel or normal) that are configured on a node and all its endpoints.\n\t * The returned map uses the source node+endpoint as keys and its values are a map of association group IDs to target node+endpoint.\n\t */\n\tpublic getAllAssociations(\n\t\tnodeId: number,\n\t): ReadonlyObjectKeyMap<\n\t\tAssociationAddress,\n\t\tReadonlyMap<number, readonly AssociationAddress[]>\n\t> {\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\treturn ccUtils.getAllAssociations(this.driver, node);\n\t}\n\n\t/**\n\t * Checks if a given association is allowed.\n\t */\n\tpublic checkAssociation(\n\t\tsource: AssociationAddress,\n\t\tgroup: number,\n\t\tdestination: AssociationAddress,\n\t): AssociationCheckResult {\n\t\tconst node = this.nodes.getOrThrow(source.nodeId);\n\t\tconst endpoint = node.getEndpointOrThrow(source.endpoint ?? 0);\n\n\t\treturn ccUtils.checkAssociation(\n\t\t\tthis.driver,\n\t\t\tendpoint,\n\t\t\tgroup,\n\t\t\tdestination,\n\t\t);\n\t}\n\n\t/**\n\t * Adds associations to a node or endpoint.\n\t *\n\t * **Note:** This method will throw if:\n\t * * the source node, endpoint or association group does not exist,\n\t * * the source node is a ZWLR node and the destination is not the SIS\n\t * * the destination node is a ZWLR node\n\t * * the association is not allowed for other reasons. In this case, the error's\n\t * `context` property will contain an array with all forbidden destinations, each with an added `checkResult` property\n\t * which contains the reason why the association is forbidden:\n\t * ```ts\n\t * {\n\t * checkResult: AssociationCheckResult;\n\t * nodeId: number;\n\t * endpoint?: number | undefined;\n\t * }[]\n\t * ```\n\t */\n\tpublic async addAssociations(\n\t\tsource: AssociationAddress,\n\t\tgroup: number,\n\t\tdestinations: AssociationAddress[],\n\t): Promise<void> {\n\t\tconst node = this.nodes.getOrThrow(source.nodeId);\n\t\tconst endpoint = node.getEndpointOrThrow(source.endpoint ?? 0);\n\n\t\tawait ccUtils.addAssociations(\n\t\t\tthis.driver,\n\t\t\tendpoint,\n\t\t\tgroup,\n\t\t\tdestinations,\n\t\t);\n\n\t\tif (isLongRangeNodeId(source.nodeId)) return;\n\n\t\t// Nodes need a return route to be able to send commands to other nodes\n\t\tconst destinationNodeIDs = distinct(\n\t\t\tdestinations.map((d) => d.nodeId),\n\t\t\t// Except to the controller itself - this route is already known\n\t\t).filter((id) => id !== this.ownNodeId);\n\t\tfor (const id of destinationNodeIDs) {\n\t\t\tif (id === this._ownNodeId) {\n\t\t\t\tawait this.assignSUCReturnRoutes(source.nodeId);\n\t\t\t} else {\n\t\t\t\tawait this.assignReturnRoutes(source.nodeId, id);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Removes the given associations from a node or endpoint\n\t */\n\tpublic removeAssociations(\n\t\tsource: AssociationAddress,\n\t\tgroup: number,\n\t\tdestinations: AssociationAddress[],\n\t): Promise<void> {\n\t\tconst node = this.nodes.getOrThrow(source.nodeId);\n\t\tconst endpoint = node.getEndpointOrThrow(source.endpoint ?? 0);\n\n\t\treturn ccUtils.removeAssociations(\n\t\t\tthis.driver,\n\t\t\tendpoint,\n\t\t\tgroup,\n\t\t\tdestinations,\n\t\t);\n\t}\n\n\t/**\n\t * Removes a node from all other nodes' associations\n\t * WARNING: It is not recommended to await this method\n\t */\n\tpublic async removeNodeFromAllAssociations(nodeId: number): Promise<void> {\n\t\tconst tasks: Promise<any>[] = [];\n\t\t// Check each endpoint of each node if they have an association to this node\n\t\tfor (const node of this.nodes.values()) {\n\t\t\tif (node.id === this._ownNodeId || node.id === nodeId) continue;\n\t\t\tif (node.interviewStage !== InterviewStage.Complete) continue;\n\n\t\t\tfor (const endpoint of node.getAllEndpoints()) {\n\t\t\t\t// Prefer multi channel associations if that is available\n\t\t\t\tif (\n\t\t\t\t\tendpoint.commandClasses[\n\t\t\t\t\t\t\"Multi Channel Association\"\n\t\t\t\t\t].isSupported()\n\t\t\t\t) {\n\t\t\t\t\tconst existing = MultiChannelAssociationCC\n\t\t\t\t\t\t.getAllDestinationsCached(\n\t\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\t\tendpoint,\n\t\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\t[...existing.values()].some((dests) =>\n\t\t\t\t\t\t\tdests.some((a) => a.nodeId === nodeId)\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\ttasks.push(\n\t\t\t\t\t\t\tendpoint.commandClasses[\n\t\t\t\t\t\t\t\t\"Multi Channel Association\"\n\t\t\t\t\t\t\t].removeDestinations({\n\t\t\t\t\t\t\t\tnodeIds: [nodeId],\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else if (endpoint.commandClasses.Association.isSupported()) {\n\t\t\t\t\tconst existing = AssociationCC.getAllDestinationsCached(\n\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\tendpoint,\n\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\t[...existing.values()].some((dests) =>\n\t\t\t\t\t\t\tdests.some((a) => a.nodeId === nodeId)\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\ttasks.push(\n\t\t\t\t\t\t\tendpoint.commandClasses.Association\n\t\t\t\t\t\t\t\t.removeNodeIdsFromAllGroups(\n\t\t\t\t\t\t\t\t\t[nodeId],\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tawait Promise.all(tasks);\n\t}\n\n\t/**\n\t * Tests if a node is marked as failed in the controller's memory\n\t * @param nodeId The id of the node in question\n\t */\n\tpublic async isFailedNode(nodeId: number): Promise<boolean> {\n\t\tconst result = await this.driver.sendMessage<IsFailedNodeResponse>(\n\t\t\tnew IsFailedNodeRequest({ failedNodeId: nodeId }),\n\t\t);\n\t\treturn result.result;\n\t}\n\n\t/**\n\t * Removes a failed node from the controller's memory. If the process fails, this will throw an exception with the details why.\n\t * @param nodeId The id of the node to remove\n\t */\n\tpublic async removeFailedNode(nodeId: number): Promise<void> {\n\t\tawait this.removeFailedNodeInternal(\n\t\t\tnodeId,\n\t\t\tRemoveNodeReason.RemoveFailed,\n\t\t);\n\t}\n\n\t/** @internal */\n\tpublic async removeFailedNodeInternal(\n\t\tnodeId: number,\n\t\treason: RemoveNodeReason,\n\t): Promise<void> {\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\n\t\t// It is possible that this method is called while the node is still in the process of resetting or leaving the network\n\t\t// Therefore, we ping multiple times in case of success and wait a bit in between\n\t\tlet didFail = false;\n\t\tconst MAX_ATTEMPTS = 3;\n\t\tfor (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n\t\t\tif (await node.ping()) {\n\t\t\t\tif (attempt < MAX_ATTEMPTS) await wait(2000);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tdidFail = true;\n\t\t\tbreak;\n\t\t}\n\t\tif (!didFail) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The node removal process could not be started because the node responded to a ping.`,\n\t\t\t\tZWaveErrorCodes.RemoveFailedNode_Failed,\n\t\t\t);\n\t\t}\n\n\t\tconst result = await this.driver.sendMessage<\n\t\t\tRemoveFailedNodeRequestStatusReport | RemoveFailedNodeResponse\n\t\t>(new RemoveFailedNodeRequest({ failedNodeId: nodeId }));\n\n\t\tif (result instanceof RemoveFailedNodeResponse) {\n\t\t\t// This implicates that the process was unsuccessful.\n\t\t\tlet message =\n\t\t\t\t`The node removal process could not be started due to the following reasons:`;\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.removeStatus\n\t\t\t\t\t& RemoveFailedNodeStartFlags.NotPrimaryController\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage += \"\\n\u00B7 This controller is not the primary controller\";\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.removeStatus\n\t\t\t\t\t& RemoveFailedNodeStartFlags.NodeNotFound\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage +=\n\t\t\t\t\t`\\n\u00B7 Node ${nodeId} is not in the list of failed nodes`;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.removeStatus\n\t\t\t\t\t& RemoveFailedNodeStartFlags.RemoveProcessBusy\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage += `\\n\u00B7 The node removal process is currently busy`;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.removeStatus\n\t\t\t\t\t& RemoveFailedNodeStartFlags.RemoveFailed\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage +=\n\t\t\t\t\t`\\n\u00B7 The controller is busy or the node has responded`;\n\t\t\t}\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.RemoveFailedNode_Failed,\n\t\t\t);\n\t\t} else {\n\t\t\tswitch (result.removeStatus) {\n\t\t\t\tcase RemoveFailedNodeStatus.NodeOK:\n\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t`The node could not be removed because it has responded`,\n\t\t\t\t\t\tZWaveErrorCodes.RemoveFailedNode_NodeOK,\n\t\t\t\t\t);\n\t\t\t\tcase RemoveFailedNodeStatus.NodeNotRemoved:\n\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t`The removal process could not be completed`,\n\t\t\t\t\t\tZWaveErrorCodes.RemoveFailedNode_Failed,\n\t\t\t\t\t);\n\t\t\t\tdefault:\n\t\t\t\t\t// If everything went well, the status is RemoveFailedNodeStatus.NodeRemoved\n\n\t\t\t\t\t// Emit the removed event so the driver and applications can react\n\t\t\t\t\tthis.emit(\"node removed\", this.nodes.get(nodeId)!, reason);\n\t\t\t\t\t// and forget the node\n\t\t\t\t\tthis._nodes.delete(nodeId);\n\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Replace a failed node from the controller's memory. If the process fails, this will throw an exception with the details why.\n\t * @param nodeId The id of the node to replace\n\t * @param options Defines the inclusion strategy to use for the replacement node\n\t */\n\tpublic async replaceFailedNode(\n\t\tnodeId: number,\n\t\toptions: ReplaceNodeOptions = {\n\t\t\tstrategy: InclusionStrategy.Insecure,\n\t\t},\n\t): Promise<boolean> {\n\t\tif (\n\t\t\tthis._inclusionState === InclusionState.Including\n\t\t\t|| this._inclusionState === InclusionState.Excluding\n\t\t\t|| this._inclusionState === InclusionState.Busy\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Leave SmartStart listening mode so we can switch to exclusion mode\n\t\tawait this.pauseSmartStart();\n\n\t\tthis.setInclusionState(InclusionState.Busy);\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`starting replace failed node process...`,\n\t\t);\n\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\tif (await node.ping()) {\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The node replace process could not be started because the node responded to a ping.`,\n\t\t\t\tZWaveErrorCodes.ReplaceFailedNode_Failed,\n\t\t\t);\n\t\t}\n\n\t\tthis._inclusionOptions = options;\n\n\t\tconst result = await this.driver.sendMessage<ReplaceFailedNodeResponse>(\n\t\t\tnew ReplaceFailedNodeRequest({\n\t\t\t\tfailedNodeId: nodeId,\n\t\t\t}),\n\t\t);\n\n\t\tif (!result.isOK()) {\n\t\t\t// This implicates that the process was unsuccessful.\n\t\t\tlet message =\n\t\t\t\t`The node replace process could not be started due to the following reasons:`;\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.replaceStatus\n\t\t\t\t\t& ReplaceFailedNodeStartFlags.NotPrimaryController\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage += \"\\n\u00B7 This controller is not the primary controller\";\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.replaceStatus\n\t\t\t\t\t& ReplaceFailedNodeStartFlags.NodeNotFound\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage +=\n\t\t\t\t\t`\\n\u00B7 Node ${nodeId} is not in the list of failed nodes`;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.replaceStatus\n\t\t\t\t\t& ReplaceFailedNodeStartFlags.ReplaceProcessBusy\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage += `\\n\u00B7 The node replace process is currently busy`;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!!(\n\t\t\t\t\tresult.replaceStatus\n\t\t\t\t\t& ReplaceFailedNodeStartFlags.ReplaceFailed\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmessage +=\n\t\t\t\t\t`\\n\u00B7 The controller is busy or the node has responded`;\n\t\t\t}\n\t\t\tthis.setInclusionState(InclusionState.Idle);\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.ReplaceFailedNode_Failed,\n\t\t\t);\n\t\t} else {\n\t\t\t// Remember which node we're trying to replace\n\t\t\tthis._nodePendingReplace = this.nodes.get(nodeId);\n\t\t\tthis._replaceFailedPromise = createDeferredPromise();\n\t\t\treturn this._replaceFailedPromise;\n\t\t}\n\t}\n\n\t/** Configure the RF region at the Z-Wave API Module */\n\tpublic async setRFRegion(region: RFRegion): Promise<boolean> {\n\t\t// Setting the \"default\" region is not possible. Controllers are supposed to\n\t\t// default to the EU region, so we do just that.\n\t\tif (region === RFRegion[\"Default (EU)\"]) region = RFRegion.Europe;\n\n\t\t// Unless auto-upgrade to LR regions is disabled, try to find a suitable LR replacement region\n\t\tif (this.driver.options.rf?.preferLRRegion !== false) {\n\t\t\tregion = this.tryGetLRCapableRegion(region);\n\t\t}\n\t\treturn this.setRFRegionInternal(region, true);\n\t}\n\n\t/** Configure the RF region at the Z-Wave API Module */\n\tprivate async setRFRegionInternal(\n\t\tregion: RFRegion,\n\t\tsoftReset: boolean = true,\n\t): Promise<boolean> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_SetRFRegionResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(new SerialAPISetup_SetRFRegionRequest({ region }));\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support setting the RF region!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\tif (softReset && result.success) await this.driver.trySoftReset();\n\t\tthis._rfRegion = region;\n\t\treturn result.success;\n\t}\n\n\t/** Request the current RF region configured at the Z-Wave API Module */\n\tpublic async getRFRegion(): Promise<RFRegion> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetRFRegionResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(new SerialAPISetup_GetRFRegionRequest());\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the RF region!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\tthis._rfRegion = result.region;\n\t\treturn result.region;\n\t}\n\n\t/**\n\t * Query the supported regions of the Z-Wave API Module\n\t *\n\t * **Note:** Applications should prefer using {@link getSupportedRFRegions} instead\n\t */\n\tpublic async querySupportedRFRegions(): Promise<RFRegion[]> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetSupportedRegionsResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(new SerialAPISetup_GetSupportedRegionsRequest());\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the supported RF regions!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn result.supportedRegions;\n\t}\n\n\t/**\n\t * Query the supported regions of the Z-Wave API Module\n\t *\n\t * **Note:** Applications should prefer reading the cached value from {@link supportedRFRegions} instead\n\t */\n\tpublic async queryRFRegionInfo(\n\t\tregion: RFRegion,\n\t): Promise<{\n\t\tregion: RFRegion;\n\t\tsupportsZWave: boolean;\n\t\tsupportsLongRange: boolean;\n\t\tincludesRegion?: RFRegion;\n\t}> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetRegionInfoResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(new SerialAPISetup_GetRegionInfoRequest({ region }));\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the RF region info!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn pick(result, [\n\t\t\t\"region\",\n\t\t\t\"supportsZWave\",\n\t\t\t\"supportsLongRange\",\n\t\t\t\"includesRegion\",\n\t\t]);\n\t}\n\n\t/**\n\t * Returns the RF regions supported by this controller, or `undefined` if the information is not known yet.\n\t *\n\t * @param filterSubsets Whether to exclude regions that are subsets of other regions,\n\t * for example `USA` which is a subset of `USA (Long Range)`\n\t */\n\tpublic getSupportedRFRegions(\n\t\tfilterSubsets: boolean = true,\n\t): MaybeNotKnown<readonly RFRegion[]> {\n\t\t// If supported by the firmware, rely on the queried information\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.GetSupportedRegions,\n\t\t\t)\n\t\t) {\n\t\t\tif (this._supportedRegions == NOT_KNOWN) return NOT_KNOWN;\n\t\t\tconst allRegions = new Set(this._supportedRegions.keys());\n\t\t\tif (filterSubsets) {\n\t\t\t\tfor (const region of this._supportedRegions.values()) {\n\t\t\t\t\tif (region.includesRegion != undefined) {\n\t\t\t\t\t\tallRegions.delete(region.includesRegion);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn [...allRegions].sort((a, b) => a - b);\n\t\t}\n\n\t\t// Fallback: Hardcoded list of known supported regions\n\t\tconst ret = new Set([\n\t\t\t// Always supported\n\t\t\tRFRegion.Europe,\n\t\t\tRFRegion.USA,\n\t\t\tRFRegion[\"Australia/New Zealand\"],\n\t\t\tRFRegion[\"Hong Kong\"],\n\t\t\tRFRegion.India,\n\t\t\tRFRegion.Israel,\n\t\t\tRFRegion.Russia,\n\t\t\tRFRegion.China,\n\t\t\tRFRegion.Japan,\n\t\t\tRFRegion.Korea,\n\t\t\tRFRegion[\"Default (EU)\"],\n\t\t]);\n\n\t\tif (this.isLongRangeCapable()) {\n\t\t\t// All LR capable controllers support USA Long Range\n\t\t\tret.add(RFRegion[\"USA (Long Range)\"]);\n\t\t\tif (filterSubsets) ret.delete(RFRegion.USA);\n\n\t\t\t// EU Long Range was added in SDK 7.22 for 800 series chips\n\t\t\t// 7.22.1 adds support for querying the supported regions, so the following\n\t\t\t// is really only necessary for 7.22.0.\n\t\t\tif (\n\t\t\t\ttypeof this._zwaveChipType === \"string\"\n\t\t\t\t&& getChipTypeAndVersion(this._zwaveChipType)?.type === 8\n\t\t\t\t&& this.sdkVersionGte(\"7.22\")\n\t\t\t) {\n\t\t\t\tret.add(RFRegion[\"Europe (Long Range)\"]);\n\t\t\t\tif (filterSubsets) ret.delete(RFRegion.Europe);\n\t\t\t}\n\t\t}\n\n\t\treturn [...ret].sort((a, b) => a - b);\n\t}\n\n\t/** Configure the Powerlevel setting of the Z-Wave API */\n\tpublic async setPowerlevel(\n\t\tpowerlevel: number,\n\t\tmeasured0dBm: number,\n\t): Promise<boolean> {\n\t\tlet request: Message;\n\t\tif (\n\t\t\tthis.supportedSerialAPISetupCommands?.includes(\n\t\t\t\tSerialAPISetupCommand.SetPowerlevel16Bit,\n\t\t\t)\n\t\t) {\n\t\t\trequest = new SerialAPISetup_SetPowerlevel16BitRequest({\n\t\t\t\tpowerlevel,\n\t\t\t\tmeasured0dBm,\n\t\t\t});\n\t\t} else {\n\t\t\trequest = new SerialAPISetup_SetPowerlevelRequest({\n\t\t\t\tpowerlevel,\n\t\t\t\tmeasured0dBm,\n\t\t\t});\n\t\t}\n\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_SetPowerlevelResponse\n\t\t\t| SerialAPISetup_SetPowerlevel16BitResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(request);\n\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support setting the powerlevel!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn result.success;\n\t}\n\n\t/** Request the Powerlevel setting of the Z-Wave API */\n\tpublic async getPowerlevel(): Promise<\n\t\tPick<\n\t\t\tSerialAPISetup_GetPowerlevelResponse,\n\t\t\t\"powerlevel\" | \"measured0dBm\"\n\t\t>\n\t> {\n\t\tlet request: Message;\n\t\tif (\n\t\t\tthis.supportedSerialAPISetupCommands?.includes(\n\t\t\t\tSerialAPISetupCommand.GetPowerlevel16Bit,\n\t\t\t)\n\t\t) {\n\t\t\trequest = new SerialAPISetup_GetPowerlevel16BitRequest();\n\t\t} else {\n\t\t\trequest = new SerialAPISetup_GetPowerlevelRequest();\n\t\t}\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetPowerlevelResponse\n\t\t\t| SerialAPISetup_GetPowerlevel16BitResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(request);\n\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the powerlevel!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn pick(result, [\"powerlevel\", \"measured0dBm\"]);\n\t}\n\n\t/** Configure the maximum TX powerlevel for Z-Wave Long Range */\n\tpublic async setMaxLongRangePowerlevel(\n\t\tlimit: number,\n\t): Promise<boolean> {\n\t\tconst request = new SerialAPISetup_SetLongRangeMaximumTxPowerRequest({\n\t\t\tlimit,\n\t\t});\n\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_SetLongRangeMaximumTxPowerResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(request);\n\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support setting the max. Long Range powerlevel!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\tif (result.success) {\n\t\t\tthis._maxLongRangePowerlevel = limit;\n\t\t}\n\t\treturn result.success;\n\t}\n\n\t/** Request the maximum TX powerlevel setting for Z-Wave Long Range */\n\tpublic async getMaxLongRangePowerlevel(): Promise<number> {\n\t\tconst request = new SerialAPISetup_GetLongRangeMaximumTxPowerRequest();\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetLongRangeMaximumTxPowerResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(request);\n\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the max. Long Range powerlevel!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\tthis._maxLongRangePowerlevel = result.limit;\n\t\treturn result.limit;\n\t}\n\n\t/**\n\t * Configure channel to use for Z-Wave Long Range.\n\t */\n\tpublic async setLongRangeChannel(\n\t\tchannel:\n\t\t\t| LongRangeChannel.A\n\t\t\t| LongRangeChannel.B\n\t\t\t| LongRangeChannel.Auto,\n\t): Promise<boolean> {\n\t\tif (\n\t\t\t!this._supportsLongRangeAutoChannelSelection\n\t\t\t&& channel === LongRangeChannel.Auto\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support automatic Long Range channel selection!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\tconst result = await this.driver.sendMessage<\n\t\t\tSetLongRangeChannelResponse\n\t\t>(\n\t\t\tnew SetLongRangeChannelRequest({ channel }),\n\t\t);\n\n\t\tif (result.success) {\n\t\t\tthis._longRangeChannel = channel;\n\t\t}\n\t\treturn result.success;\n\t}\n\n\t/** Request the channel setting and capabilities for Z-Wave Long Range */\n\tpublic async getLongRangeChannel(): Promise<\n\t\t{ channel: LongRangeChannel; supportsAutoChannelSelection: boolean }\n\t> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\tGetLongRangeChannelResponse\n\t\t>(\n\t\t\tnew GetLongRangeChannelRequest(),\n\t\t);\n\n\t\tconst channel = result.autoChannelSelectionActive\n\t\t\t\t&& result.supportsAutoChannelSelection\n\t\t\t? LongRangeChannel.Auto\n\t\t\t: result.channel;\n\n\t\tthis._longRangeChannel = channel;\n\t\tthis._supportsLongRangeAutoChannelSelection =\n\t\t\tresult.supportsAutoChannelSelection;\n\n\t\treturn {\n\t\t\tchannel,\n\t\t\tsupportsAutoChannelSelection: result.supportsAutoChannelSelection,\n\t\t};\n\t}\n\n\t/**\n\t * @internal\n\t * Configure whether the Z-Wave API should use short (8 bit) or long (16 bit) Node IDs\n\t */\n\tpublic async setNodeIDType(nodeIdType: NodeIDType): Promise<boolean> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`Switching serial API to ${\n\t\t\t\tnodeIdType === NodeIDType.Short ? 8 : 16\n\t\t\t}-bit node IDs...`,\n\t\t);\n\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_SetNodeIDTypeResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(\n\t\t\tnew SerialAPISetup_SetNodeIDTypeRequest({\n\t\t\t\tnodeIdType,\n\t\t\t}),\n\t\t);\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support switching between short and long node IDs!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t} else {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Switching to ${\n\t\t\t\t\tnodeIdType === NodeIDType.Short ? 8 : 16\n\t\t\t\t}-bit node IDs ${result.success ? \"successful\" : \"failed\"}`,\n\t\t\t);\n\n\t\t\tif (result.success) {\n\t\t\t\tthis._nodeIdType = nodeIdType;\n\t\t\t}\n\t\t}\n\t\treturn result.success;\n\t}\n\n\tpublic async trySetNodeIDType(nodeIdType: NodeIDType): Promise<boolean> {\n\t\tif (\n\t\t\tthis.isSerialAPISetupCommandSupported(\n\t\t\t\tSerialAPISetupCommand.SetNodeIDType,\n\t\t\t)\n\t\t) {\n\t\t\ttry {\n\t\t\t\treturn await this.setNodeIDType(nodeIdType);\n\t\t\t} catch {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @internal\n\t * Request the maximum payload that the Z-Wave API Module can accept for transmitting Z-Wave frames. This value depends on the RF Profile\n\t */\n\tpublic async getMaxPayloadSize(): Promise<number> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetMaximumPayloadSizeResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(new SerialAPISetup_GetMaximumPayloadSizeRequest(), {\n\t\t\tsupportCheck: false,\n\t\t});\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the max. payload size!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn result.maxPayloadSize;\n\t}\n\n\t/**\n\t * @internal\n\t * Request the maximum payload that the Z-Wave API Module can accept for transmitting Z-Wave Long Range frames. This value depends on the RF Profile\n\t */\n\tpublic async getMaxPayloadSizeLongRange(): Promise<number> {\n\t\tconst result = await this.driver.sendMessage<\n\t\t\t| SerialAPISetup_GetLongRangeMaximumPayloadSizeResponse\n\t\t\t| SerialAPISetup_CommandUnsupportedResponse\n\t\t>(\n\t\t\tnew SerialAPISetup_GetLongRangeMaximumPayloadSizeRequest(),\n\t\t);\n\t\tif (result instanceof SerialAPISetup_CommandUnsupportedResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support getting the max. long range payload size!`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\t\treturn result.maxPayloadSize;\n\t}\n\n\t/**\n\t * Instructs a node to (re-)discover its neighbors.\n\t *\n\t * **WARNING:** On some controllers, this can cause new SUC return routes to be assigned.\n\t *\n\t * @returns `true` if the update was successful and the new neighbors can be retrieved using\n\t * {@link getNodeNeighbors}. `false` if the update failed.\n\t */\n\tpublic async discoverNodeNeighbors(nodeId: number): Promise<boolean> {\n\t\t// TODO: Consider making this not block the send queue.\n\t\t// However, I haven't actually seen a UpdateStarted callback in the wild,\n\t\t// so we don't know if that would even work.\n\n\t\t// We cannot discover neighbors for the controller\n\t\tif (nodeId === this._ownNodeId) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Discovering neighbors for the controller itself is not possible!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\t// During inclusion, the timeout is mainly required for the node to detect all neighbors\n\t\t// We do the same here, so we just reuse the timeout\n\t\tconst discoveryTimeout = computeNeighborDiscoveryTimeout(\n\t\t\tthis.driver,\n\t\t\t// Controllers take longer, just assume the worst case here\n\t\t\tNodeType.Controller,\n\t\t);\n\n\t\tconst resp = await this.driver.sendMessage<\n\t\t\tRequestNodeNeighborUpdateReport\n\t\t>(\n\t\t\tnew RequestNodeNeighborUpdateRequest({\n\t\t\t\tnodeId,\n\t\t\t\tdiscoveryTimeout,\n\t\t\t}),\n\t\t);\n\t\tconst success =\n\t\t\tresp.updateStatus === NodeNeighborUpdateStatus.UpdateDone;\n\n\t\tif (success) {\n\t\t\t// Not sure why, but Zniffer traces show that a node neighbor update can cause the controller to\n\t\t\t// also do AssignSUCReturnRoute. As a result, we need to invalidate our route cache.\n\t\t\tthis.setCustomSUCReturnRoutesCached(nodeId, undefined);\n\t\t}\n\n\t\treturn success;\n\t}\n\n\t/**\n\t * Returns the known list of neighbors for a node.\n\t *\n\t * Throws when the node is a Long Range node.\n\t */\n\tpublic async getNodeNeighbors(\n\t\tnodeId: number,\n\t\tonlyRepeaters: boolean = false,\n\t): Promise<readonly number[]> {\n\t\tif (isLongRangeNodeId(nodeId)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot request node neighbors for Long Range node ${nodeId}`,\n\t\t\t\tZWaveErrorCodes.Controller_NotSupportedForLongRange,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\tmessage: \"requesting node neighbors...\",\n\t\t\tdirection: \"outbound\",\n\t\t});\n\t\ttry {\n\t\t\tconst resp = await this.driver.sendMessage<GetRoutingInfoResponse>(\n\t\t\t\tnew GetRoutingInfoRequest({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tremoveBadLinks: false,\n\t\t\t\t\tremoveNonRepeaters: onlyRepeaters,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\tmessage: `node neighbors received: ${resp.nodeIds.join(\", \")}`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t\treturn resp.nodeIds;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`requesting the node neighbors failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the known routes the controller will use to communicate with the nodes.\n\t *\n\t * This information is dynamically built using TX status reports and may not be accurate at all times.\n\t * Also, it may not be available immediately after startup or at all if the controller doesn't support this feature.\n\t *\n\t * **Note:** To keep information returned by this method updated, use the information contained in each node's `\"statistics\"` event.\n\t */\n\tpublic getKnownLifelineRoutes(): ReadonlyMap<number, LifelineRoutes> {\n\t\tconst ret = new Map<number, LifelineRoutes>();\n\t\tfor (const node of this.nodes.values()) {\n\t\t\tif (node.isControllerNode) continue;\n\t\t\tret.set(node.id, {\n\t\t\t\tlwr: node.statistics.lwr,\n\t\t\t\tnlwr: node.statistics.nlwr,\n\t\t\t});\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/** Request additional information about the controller/Z-Wave chip */\n\tpublic async getSerialApiInitData(): Promise<SerialApiInitData> {\n\t\tthis.driver.controllerLog.print(\n\t\t\t`querying additional controller information...`,\n\t\t);\n\t\tconst initData = await this.driver.sendMessage<\n\t\t\tGetSerialApiInitDataResponse\n\t\t>(\n\t\t\tnew GetSerialApiInitDataRequest(),\n\t\t);\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received additional controller information:\n Z-Wave API version: ${initData.zwaveApiVersion.version} (${initData.zwaveApiVersion.kind})${\n\t\t\t\tinitData.zwaveChipType\n\t\t\t\t\t? `\n Z-Wave chip type: ${\n\t\t\t\t\t\ttypeof initData.zwaveChipType === \"string\"\n\t\t\t\t\t\t\t? initData.zwaveChipType\n\t\t\t\t\t\t\t: `unknown (type: ${\n\t\t\t\t\t\t\t\tnum2hex(initData.zwaveChipType.type)\n\t\t\t\t\t\t\t}, version: ${\n\t\t\t\t\t\t\t\tnum2hex(initData.zwaveChipType.version)\n\t\t\t\t\t\t\t})`\n\t\t\t\t\t}`\n\t\t\t\t\t: \"\"\n\t\t\t}\n node type ${getEnumMemberName(NodeType, initData.nodeType)}\n controller role: ${initData.isPrimary ? \"primary\" : \"secondary\"}\n controller is the SIS: ${initData.isSIS}\n controller supports timers: ${initData.supportsTimers}\n Z-Wave Classic nodes: ${initData.nodeIds.join(\", \")}`,\n\t\t);\n\n\t\tconst ret: SerialApiInitData = {\n\t\t\t...pick(initData, [\n\t\t\t\t\"zwaveApiVersion\",\n\t\t\t\t\"zwaveChipType\",\n\t\t\t\t\"isPrimary\",\n\t\t\t\t\"isSIS\",\n\t\t\t\t\"nodeType\",\n\t\t\t\t\"supportsTimers\",\n\t\t\t]),\n\t\t\tnodeIds: [...initData.nodeIds],\n\t\t\t// ignore the initVersion, no clue what to do with it\n\t\t};\n\n\t\t// and remember the new info\n\t\tthis._zwaveApiVersion = initData.zwaveApiVersion;\n\t\tthis._zwaveChipType = initData.zwaveChipType;\n\t\tthis._isPrimary = initData.isPrimary;\n\t\tthis._isSIS = initData.isSIS;\n\t\tthis._nodeType = initData.nodeType;\n\t\tthis._supportsTimers = initData.supportsTimers;\n\n\t\treturn ret;\n\t}\n\n\t/** Determines the controller's network role/capabilities */\n\tpublic async getControllerCapabilities(): Promise<ControllerCapabilities> {\n\t\tthis.driver.controllerLog.print(`querying controller capabilities...`);\n\t\tconst result = await this.driver.sendMessage<\n\t\t\tGetControllerCapabilitiesResponse\n\t\t>(\n\t\t\tnew GetControllerCapabilitiesRequest(),\n\t\t\t{ supportCheck: false },\n\t\t);\n\n\t\tconst ret: ControllerCapabilities = {\n\t\t\tisSecondary: result.isSecondary,\n\t\t\tisUsingHomeIdFromOtherNetwork: result.isUsingHomeIdFromOtherNetwork,\n\t\t\tisSISPresent: result.isSISPresent,\n\t\t\twasRealPrimary: result.wasRealPrimary,\n\t\t\tisSUC: result.isStaticUpdateController,\n\t\t\tnoNodesIncluded: result.noNodesIncluded,\n\t\t};\n\n\t\tthis._isSecondary = ret.isSecondary;\n\t\tthis._isUsingHomeIdFromOtherNetwork = ret.isUsingHomeIdFromOtherNetwork;\n\t\tthis._isSISPresent = ret.isSISPresent;\n\t\tthis._wasRealPrimary = ret.wasRealPrimary;\n\t\tthis._isSUC = ret.isSUC;\n\t\tthis._noNodesIncluded = ret.noNodesIncluded;\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t`received controller capabilities:\n controller role: ${getEnumMemberName(ControllerRole, this.role!)}\n is the SUC: ${ret.isSUC}\n started this network: ${!ret.isUsingHomeIdFromOtherNetwork}\n SIS is present: ${ret.isSISPresent}\n was real primary: ${ret.wasRealPrimary}`,\n\t\t);\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * @internal\n\t * Deserializes the controller information and all nodes from the cache.\n\t */\n\tpublic async deserialize(): Promise<void> {\n\t\tif (!this.driver.networkCache) return;\n\t\tconst cache = this.driver.networkCache;\n\n\t\t// Deserialize information for all nodes\n\t\tfor (const node of this.nodes.values()) {\n\t\t\tawait node.deserialize();\n\t\t}\n\n\t\t// Remove nodes which no longer exist from the cache\n\t\t// TODO: Do the same when removing a node\n\t\tfor (const cacheKey of cache.keys()) {\n\t\t\tconst nodeId = cacheKeyUtils.nodeIdFromKey(cacheKey);\n\t\t\tif (nodeId && !this.nodes.has(nodeId)) {\n\t\t\t\tcache.delete(cacheKey);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Turns the Z-Wave radio on or off */\n\tpublic async toggleRF(enabled: boolean): Promise<boolean> {\n\t\ttry {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Turning RF ${enabled ? \"on\" : \"off\"}...`,\n\t\t\t);\n\t\t\tconst ret = await this.driver.sendMessage<SetRFReceiveModeResponse>(\n\t\t\t\tnew SetRFReceiveModeRequest({ enabled }),\n\t\t\t);\n\t\t\treturn ret.isOK();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Error turning RF ${enabled ? \"on\" : \"off\"}: ${\n\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\te,\n\t\t\t\t\t)\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate _nvm: NVMAdapter | undefined;\n\t/** Provides access to the controller's non-volatile memory */\n\tpublic get nvm(): NVMAdapter {\n\t\tif (!this._nvm) {\n\t\t\tif (this.sdkVersionGte(\"7.0\")) {\n\t\t\t\tconst io = new BufferedNVMReader(new SerialNVMIO700(this));\n\t\t\t\tconst nvm3 = new NVM3(io);\n\t\t\t\tthis._nvm = new NVM3Adapter(nvm3);\n\t\t\t} else {\n\t\t\t\tconst io = new BufferedNVMReader(new SerialNVMIO500(this));\n\t\t\t\tconst nvm = new NVM500(io);\n\t\t\t\tthis._nvm = new NVM500Adapter(nvm);\n\t\t\t}\n\t\t}\n\n\t\treturn this._nvm;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Initialize the Firmware Update functionality and determine if the firmware can be updated.\n\t */\n\tprivate async firmwareUpdateNVMInit(): Promise<boolean> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tFirmwareUpdateNVM_InitResponse\n\t\t>(\n\t\t\tnew FirmwareUpdateNVM_InitRequest(),\n\t\t);\n\t\treturn ret.supported;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Set the NEWIMAGE marker in the NVM (to the given value), which is used to signal that a new firmware image is present\n\t */\n\tprivate async firmwareUpdateNVMSetNewImage(\n\t\tvalue: boolean = true,\n\t): Promise<void> {\n\t\tawait this.driver.sendMessage<FirmwareUpdateNVM_SetNewImageResponse>(\n\t\t\tnew FirmwareUpdateNVM_SetNewImageRequest({\n\t\t\t\tnewImage: value,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Return the value of the NEWIMAGE marker in the NVM, which is used to signal that a new firmware image is present\n\t */\n\tprivate async firmwareUpdateNVMGetNewImage(): Promise<boolean> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tFirmwareUpdateNVM_GetNewImageResponse\n\t\t>(\n\t\t\tnew FirmwareUpdateNVM_GetNewImageRequest(),\n\t\t);\n\t\treturn ret.newImage;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Calculates the CRC-16 for the specified block of data in the NVM\n\t */\n\tprivate async firmwareUpdateNVMUpdateCRC16(\n\t\toffset: number,\n\t\tblockLength: number,\n\t\tcrcSeed: number,\n\t): Promise<number> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tFirmwareUpdateNVM_UpdateCRC16Response\n\t\t>(\n\t\t\tnew FirmwareUpdateNVM_UpdateCRC16Request({\n\t\t\t\toffset,\n\t\t\t\tblockLength,\n\t\t\t\tcrcSeed,\n\t\t\t}),\n\t\t);\n\t\treturn ret.crc16;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Writes the given data into the firmware update region of the NVM.\n\t */\n\tprivate async firmwareUpdateNVMWrite(\n\t\toffset: number,\n\t\tbuffer: Uint8Array,\n\t): Promise<void> {\n\t\tawait this.driver.sendMessage<FirmwareUpdateNVM_WriteResponse>(\n\t\t\tnew FirmwareUpdateNVM_WriteRequest({\n\t\t\t\toffset,\n\t\t\t\tbuffer,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Checks if the firmware present in the NVM is valid\n\t */\n\tprivate async firmwareUpdateNVMIsValidCRC16(): Promise<boolean> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tFirmwareUpdateNVM_IsValidCRC16Response\n\t\t>(\n\t\t\tnew FirmwareUpdateNVM_IsValidCRC16Request(),\n\t\t);\n\t\treturn ret.isValid;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Returns information of the controller's external NVM\n\t */\n\tpublic async getNVMId(): Promise<NVMId> {\n\t\tconst ret = await this.driver.sendMessage<GetNVMIdResponse>(\n\t\t\tnew GetNVMIdRequest(),\n\t\t);\n\t\treturn pick(ret, [\"nvmManufacturerId\", \"memoryType\", \"memorySize\"]);\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Reads a byte from the external NVM at the given offset\n\t */\n\tpublic async externalNVMReadByte(offset: number): Promise<number> {\n\t\tconst ret = await this.driver.sendMessage<ExtNVMReadLongByteResponse>(\n\t\t\tnew ExtNVMReadLongByteRequest({ offset }),\n\t\t);\n\t\treturn ret.byte;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Writes a byte to the external NVM at the given offset\n\t * **WARNING:** This function can write in the full NVM address space and is not offset to start at the application area.\n\t * Take care not to accidentally overwrite the protocol NVM area!\n\t *\n\t * @returns `true` when writing succeeded, `false` otherwise\n\t */\n\tpublic async externalNVMWriteByte(\n\t\toffset: number,\n\t\tdata: number,\n\t): Promise<boolean> {\n\t\tconst ret = await this.driver.sendMessage<ExtNVMWriteLongByteResponse>(\n\t\t\tnew ExtNVMWriteLongByteRequest({ offset, byte: data }),\n\t\t);\n\t\treturn ret.success;\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Reads a buffer from the external NVM at the given offset\n\t */\n\tpublic async externalNVMReadBuffer(\n\t\toffset: number,\n\t\tlength: number,\n\t): Promise<Uint8Array> {\n\t\tconst ret = await this.driver.sendMessage<ExtNVMReadLongBufferResponse>(\n\t\t\tnew ExtNVMReadLongBufferRequest({\n\t\t\t\toffset,\n\t\t\t\tlength,\n\t\t\t}),\n\t\t);\n\t\treturn ret.buffer;\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Reads a buffer from the external NVM at the given offset\n\t *\n\t * **Note:** Prefer {@link externalNVMReadBufferExt} if supported, as that command supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMReadBuffer700(\n\t\toffset: number,\n\t\tlength: number,\n\t): Promise<{ buffer: Uint8Array; endOfFile: boolean }> {\n\t\tconst ret = await this.driver.sendMessage<NVMOperationsResponse>(\n\t\t\tnew NVMOperationsReadRequest({\n\t\t\t\toffset,\n\t\t\t\tlength,\n\t\t\t}),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tlet message = \"Could not read from the external NVM\";\n\t\t\tif (ret.status === NVMOperationStatus.Error_OperationInterference) {\n\t\t\t\tmessage += \": interference between read and write operation.\";\n\t\t\t} else if (\n\t\t\t\tret.status === NVMOperationStatus.Error_OperationMismatch\n\t\t\t) {\n\t\t\t\tmessage += \": wrong operation requested.\";\n\t\t\t}\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tbuffer: ret.buffer,\n\t\t\tendOfFile: ret.status === NVMOperationStatus.EndOfFile,\n\t\t};\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Reads a buffer from the external NVM at the given offset\n\t *\n\t * **Note:** If supported, this command should be preferred over {@link externalNVMReadBuffer700} as it supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMReadBufferExt(\n\t\toffset: number,\n\t\tlength: number,\n\t): Promise<{ buffer: Uint8Array; endOfFile: boolean }> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tExtendedNVMOperationsResponse\n\t\t>(\n\t\t\tnew ExtendedNVMOperationsReadRequest({\n\t\t\t\toffset,\n\t\t\t\tlength,\n\t\t\t}),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tlet message = \"Could not read from the external NVM\";\n\t\t\tif (\n\t\t\t\tret.status\n\t\t\t\t\t=== ExtendedNVMOperationStatus.Error_OperationInterference\n\t\t\t) {\n\t\t\t\tmessage += \": interference between read and write operation.\";\n\t\t\t} else if (\n\t\t\t\tret.status\n\t\t\t\t\t=== ExtendedNVMOperationStatus.Error_OperationMismatch\n\t\t\t) {\n\t\t\t\tmessage += \": wrong operation requested.\";\n\t\t\t}\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tbuffer: ret.bufferOrBitmask,\n\t\t\tendOfFile: ret.status === ExtendedNVMOperationStatus.EndOfFile,\n\t\t};\n\t}\n\n\t/**\n\t * **Z-Wave 500 series only**\n\t *\n\t * Writes a buffer to the external NVM at the given offset\n\t * **WARNING:** This function can write in the full NVM address space and is not offset to start at the application area.\n\t * Take care not to accidentally overwrite the protocol NVM area!\n\t *\n\t * @returns `true` when writing succeeded, `false` otherwise\n\t */\n\tpublic async externalNVMWriteBuffer(\n\t\toffset: number,\n\t\tbuffer: Uint8Array,\n\t): Promise<boolean> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tExtNVMWriteLongBufferResponse\n\t\t>(\n\t\t\tnew ExtNVMWriteLongBufferRequest({\n\t\t\t\toffset,\n\t\t\t\tbuffer,\n\t\t\t}),\n\t\t);\n\t\treturn ret.success;\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Writes a buffer to the external NVM at the given offset\n\t *\n\t * **Note:** Prefer {@link externalNVMWriteBufferExt} if supported, as that command supports larger NVMs than 64 KiB.\n\t *\n\t * **WARNING:** This function can write in the full NVM address space and is not offset to start at the application area.\n\t * Take care not to accidentally overwrite the protocol NVM area!\n\t */\n\tpublic async externalNVMWriteBuffer700(\n\t\toffset: number,\n\t\tbuffer: Uint8Array,\n\t): Promise<{ endOfFile: boolean }> {\n\t\tconst ret = await this.driver.sendMessage<NVMOperationsResponse>(\n\t\t\tnew NVMOperationsWriteRequest({\n\t\t\t\toffset,\n\t\t\t\tbuffer,\n\t\t\t}),\n\t\t);\n\n\t\tif (!ret.isOK()) {\n\t\t\tlet message = \"Could not write to the external NVM\";\n\t\t\tif (ret.status === NVMOperationStatus.Error_OperationInterference) {\n\t\t\t\tmessage += \": interference between read and write operation.\";\n\t\t\t} else if (\n\t\t\t\tret.status === NVMOperationStatus.Error_OperationMismatch\n\t\t\t) {\n\t\t\t\tmessage += \": wrong operation requested.\";\n\t\t\t}\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tendOfFile: ret.status === NVMOperationStatus.EndOfFile,\n\t\t};\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Writes a buffer to the external NVM at the given offset\n\t *\n\t * **Note:** If supported, this command should be preferred over {@link externalNVMWriteBuffer700} as it supports larger NVMs than 64 KiB.\n\t *\n\t * **WARNING:** This function can write in the full NVM address space and is not offset to start at the application area.\n\t * Take care not to accidentally overwrite the protocol NVM area!\n\t */\n\tpublic async externalNVMWriteBufferExt(\n\t\toffset: number,\n\t\tbuffer: Uint8Array,\n\t): Promise<{ endOfFile: boolean }> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tExtendedNVMOperationsResponse\n\t\t>(\n\t\t\tnew ExtendedNVMOperationsWriteRequest({\n\t\t\t\toffset,\n\t\t\t\tbuffer,\n\t\t\t}),\n\t\t);\n\n\t\tif (!ret.isOK()) {\n\t\t\tlet message = \"Could not write to the external NVM\";\n\t\t\tif (\n\t\t\t\tret.status\n\t\t\t\t\t=== ExtendedNVMOperationStatus.Error_OperationInterference\n\t\t\t) {\n\t\t\t\tmessage += \": interference between read and write operation.\";\n\t\t\t} else if (\n\t\t\t\tret.status\n\t\t\t\t\t=== ExtendedNVMOperationStatus.Error_OperationMismatch\n\t\t\t) {\n\t\t\t\tmessage += \": wrong operation requested.\";\n\t\t\t} else if (\n\t\t\t\tret.status\n\t\t\t\t\t=== ExtendedNVMOperationStatus.Error_SubCommandNotSupported\n\t\t\t) {\n\t\t\t\tmessage += \": sub-command not supported.\";\n\t\t\t}\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tendOfFile: ret.status === ExtendedNVMOperationStatus.EndOfFile,\n\t\t};\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Opens the controller's external NVM for reading/writing and returns the NVM size\n\t *\n\t * **Note:** Prefer {@link externalNVMOpenExt} if supported, as that command supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMOpen(): Promise<number> {\n\t\tconst ret = await this.driver.sendMessage<NVMOperationsResponse>(\n\t\t\tnew NVMOperationsOpenRequest(),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Failed to open the external NVM\",\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\t\treturn ret.offsetOrSize;\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Opens the controller's external NVM for reading/writing and returns the NVM size and supported operations.\n\t *\n\t * **Note:** If supported, this command should be preferred over {@link externalNVMOpen} as it supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMOpenExt(): Promise<{\n\t\tsize: number;\n\t\tsupportedOperations: ExtendedNVMOperationsCommand[];\n\t}> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tExtendedNVMOperationsResponse\n\t\t>(\n\t\t\tnew ExtendedNVMOperationsOpenRequest(),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Failed to open the external NVM\",\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\t\tconst size = ret.offsetOrSize;\n\t\tconst supportedOperations = parseBitMask(\n\t\t\tret.bufferOrBitmask,\n\t\t\tExtendedNVMOperationsCommand.Open,\n\t\t);\n\t\treturn {\n\t\t\tsize,\n\t\t\tsupportedOperations,\n\t\t};\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Closes the controller's external NVM\n\t *\n\t * **Note:** Prefer {@link externalNVMCloseExt} if supported, as that command supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMClose(): Promise<void> {\n\t\tconst ret = await this.driver.sendMessage<NVMOperationsResponse>(\n\t\t\tnew NVMOperationsCloseRequest(),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Failed to close the external NVM\",\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * **Z-Wave 700+ series only**\n\t *\n\t * Closes the controller's external NVM\n\t *\n\t * **Note:** If supported, this command should be preferred over {@link externalNVMClose} as it supports larger NVMs than 64 KiB.\n\t */\n\tpublic async externalNVMCloseExt(): Promise<void> {\n\t\tconst ret = await this.driver.sendMessage<\n\t\t\tExtendedNVMOperationsResponse\n\t\t>(\n\t\t\tnew ExtendedNVMOperationsCloseRequest(),\n\t\t);\n\t\tif (!ret.isOK()) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Failed to close the external NVM\",\n\t\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a backup of the NVM and returns the raw data as a Buffer. The Z-Wave radio is turned off/on automatically.\n\t * @param onProgress Can be used to monitor the progress of the operation, which may take several seconds up to a few minutes depending on the NVM size\n\t * @returns The raw NVM buffer\n\t */\n\tpublic async backupNVMRaw(\n\t\tonProgress?: (bytesRead: number, total: number) => void,\n\t): Promise<Uint8Array> {\n\t\tthis.driver.controllerLog.print(\"Backing up NVM...\");\n\n\t\t// Turn Z-Wave radio off to avoid having the protocol write to the NVM while dumping it\n\t\tif (!(await this.toggleRF(false))) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Could not turn off the Z-Wave radio before creating NVM backup!\",\n\t\t\t\tZWaveErrorCodes.Controller_ResponseNOK,\n\t\t\t);\n\t\t}\n\n\t\t// Disable watchdog to prevent resets during NVM access\n\t\tawait this.stopWatchdog();\n\n\t\tlet ret: Uint8Array;\n\t\ttry {\n\t\t\tif (this.sdkVersionGte(\"7.0\")) {\n\t\t\t\tret = await this.backupNVMRaw700(onProgress);\n\t\t\t\t// All 7.xx versions so far seem to have a bug where the NVM is not properly closed after reading\n\t\t\t\t// resulting in extremely strange controller behavior after a backup. To work around this, restart the stick if possible\n\t\t\t\tawait this.driver.trySoftReset();\n\t\t\t\t// Soft-resetting will enable the watchdog again\n\t\t\t} else {\n\t\t\t\tret = await this.backupNVMRaw500(onProgress);\n\t\t\t}\n\t\t\tthis.driver.controllerLog.print(\"NVM backup completed\");\n\t\t} finally {\n\t\t\t// Whatever happens, turn Z-Wave radio back on\n\t\t\tawait this.toggleRF(true);\n\t\t}\n\n\t\t// TODO: You can also get away with eliding all the 0xff pages. The NVR also holds the page size of the NVM (NVMP),\n\t\t// so you can figure out which pages you don't have to save or restore. If you do this, you need to make sure to issue a\n\t\t// \"factory reset\" before restoring the NVM - that'll blank out the NVM to 0xffs before initializing it.\n\n\t\treturn ret;\n\t}\n\n\tprivate async backupNVMRaw500(\n\t\tonProgress?: (bytesRead: number, total: number) => void,\n\t): Promise<Uint8Array> {\n\t\tconst size = nvmSizeToBufferSize((await this.getNVMId()).memorySize);\n\t\tif (!size) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Unknown NVM size - cannot backup!\",\n\t\t\t\tZWaveErrorCodes.Controller_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\tconst ret = new Bytes(size);\n\t\tlet offset = 0;\n\t\t// Try reading the maximum size at first, the Serial API should return chunks in a size it supports\n\t\t// For some reason, there is no documentation and no official command for this\n\t\tlet chunkSize: number = Math.min(0xffff, ret.length);\n\t\twhile (offset < ret.length) {\n\t\t\tconst chunk = await this.externalNVMReadBuffer(\n\t\t\t\toffset,\n\t\t\t\tMath.min(chunkSize, ret.length - offset),\n\t\t\t);\n\t\t\tif (chunk.length === 0) {\n\t\t\t\t// Some SDK versions return an empty buffer when trying to read a buffer that is too long\n\t\t\t\t// Fallback to a sane (but maybe slow) size\n\t\t\t\tchunkSize = 48;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tret.set(chunk, offset);\n\t\t\toffset += chunk.length;\n\t\t\tif (chunkSize > chunk.length) chunkSize = chunk.length;\n\n\t\t\t// Report progress for listeners\n\t\t\tif (onProgress) setImmediate(() => onProgress(offset, size));\n\t\t}\n\t\treturn ret;\n\t}\n\n\tprivate async backupNVMRaw700(\n\t\tonProgress?: (bytesRead: number, total: number) => void,\n\t): Promise<Uint8Array> {\n\t\tlet open: () => Promise<number>;\n\t\tlet read: (\n\t\t\toffset: number,\n\t\t\tlength: number,\n\t\t) => Promise<{ buffer: Uint8Array; endOfFile: boolean }>;\n\t\tlet close: () => Promise<void>;\n\n\t\tif (\n\t\t\tthis.supportedFunctionTypes?.includes(\n\t\t\t\tFunctionType.ExtendedNVMOperations,\n\t\t\t)\n\t\t) {\n\t\t\topen = async () => {\n\t\t\t\tconst { size } = await this.externalNVMOpenExt();\n\t\t\t\treturn size;\n\t\t\t};\n\t\t\tread = (offset, length) =>\n\t\t\t\tthis.externalNVMReadBufferExt(offset, length);\n\t\t\tclose = () => this.externalNVMCloseExt();\n\t\t} else {\n\t\t\topen = () => this.externalNVMOpen();\n\t\t\tread = (offset, length) =>\n\t\t\t\tthis.externalNVMReadBuffer700(offset, length);\n\t\t\tclose = () => this.externalNVMClose();\n\t\t}\n\n\t\t// Open NVM for reading\n\t\tconst size = await open();\n\n\t\tconst ret = new Bytes(size);\n\t\tlet offset = 0;\n\t\t// Try reading the maximum size at first, the Serial API should return chunks in a size it supports\n\t\t// For some reason, there is no documentation and no official command for this\n\t\tlet chunkSize: number = Math.min(0xff, ret.length);\n\t\ttry {\n\t\t\twhile (offset < ret.length) {\n\t\t\t\tconst { buffer: chunk, endOfFile } = await read(\n\t\t\t\t\toffset,\n\t\t\t\t\tMath.min(chunkSize, ret.length - offset),\n\t\t\t\t);\n\t\t\t\tif (chunkSize === 0xff && chunk.length === 0) {\n\t\t\t\t\t// Some SDK versions return an empty buffer when trying to read a buffer that is too long\n\t\t\t\t\t// Fallback to a sane (but maybe slow) size\n\t\t\t\t\tchunkSize = 48;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tret.set(chunk, offset);\n\t\t\t\toffset += chunk.length;\n\t\t\t\tif (chunkSize > chunk.length) chunkSize = chunk.length;\n\n\t\t\t\t// Report progress for listeners\n\t\t\t\tif (onProgress) setImmediate(() => onProgress(offset, size));\n\n\t\t\t\tif (endOfFile) break;\n\t\t\t}\n\t\t} finally {\n\t\t\t// Whatever happens, close the NVM\n\t\t\tawait close();\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Restores an NVM backup that was created with `backupNVMRaw`. The Z-Wave radio is turned off/on automatically.\n\t * If the given buffer is in a different NVM format, it is converted automatically. If a conversion is required but not supported, the operation will be aborted.\n\t *\n\t * **WARNING:** If both the source and target NVM use an an unsupported format, they will NOT be checked for compatibility!\n\t *\n\t * **WARNING:** A failure during this process may brick your controller. Use at your own risk!\n\t *\n\t * @param nvmData The NVM backup to be restored\n\t * @param convertProgress Can be used to monitor the progress of the NVM conversion, which may take several seconds up to a few minutes depending on the NVM size\n\t * @param restoreProgress Can be used to monitor the progress of the restore operation, which may take several seconds up to a few minutes depending on the NVM size\n\t */\n\tpublic async restoreNVM(\n\t\tnvmData: Uint8Array,\n\t\tconvertProgress?: (bytesRead: number, total: number) => void,\n\t\trestoreProgress?: (bytesWritten: number, total: number) => void,\n\t): Promise<void> {\n\t\t// Turn Z-Wave radio off to avoid having the protocol write to the NVM while dumping it\n\t\tif (!(await this.toggleRF(false))) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Could not turn off the Z-Wave radio before restoring NVM backup!\",\n\t\t\t\tZWaveErrorCodes.Controller_ResponseNOK,\n\t\t\t);\n\t\t}\n\n\t\t// Disable watchdog to prevent resets during NVM access\n\t\tawait this.stopWatchdog();\n\n\t\t// Restoring a potentially incompatible NVM happens in three steps:\n\t\t// 1. the current NVM is read\n\t\t// 2. the given NVM data is converted to match the current format\n\t\t// 3. the converted data is written to the NVM\n\n\t\ttry {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\"Converting NVM to target format...\",\n\t\t\t);\n\t\t\tlet targetNVM: Uint8Array;\n\t\t\tif (this.sdkVersionGte(\"7.0\")) {\n\t\t\t\ttargetNVM = await this.backupNVMRaw700(convertProgress);\n\t\t\t} else {\n\t\t\t\ttargetNVM = await this.backupNVMRaw500(convertProgress);\n\t\t\t}\n\t\t\tconst convertedNVM = await migrateNVM(nvmData, targetNVM);\n\n\t\t\tthis.driver.controllerLog.print(\"Restoring NVM backup...\");\n\t\t\tif (this.sdkVersionGte(\"7.0\")) {\n\t\t\t\tawait this.restoreNVMRaw700(convertedNVM, restoreProgress);\n\t\t\t} else {\n\t\t\t\tawait this.restoreNVMRaw500(convertedNVM, restoreProgress);\n\t\t\t}\n\t\t\tthis.driver.controllerLog.print(\"NVM backup restored\");\n\t\t} finally {\n\t\t\t// Whatever happens, turn Z-Wave radio back on\n\t\t\tawait this.toggleRF(true);\n\t\t}\n\n\t\t// After restoring an NVM backup, the controller's capabilities may have changed.\n\t\t// Also, we could be talking to different nodes than the cache file contains.\n\t\t// Reset all info about all nodes, so they get re-interviewed.\n\t\tthis._nodes.clear();\n\n\t\t// Normally we'd only need to soft reset the stick, but we also need to re-interview the controller and potentially all nodes.\n\t\t// Just forcing a restart of the driver seems easier.\n\n\t\tawait this.driver.softResetAndRestart(\n\t\t\t\"Restarting driver to activate restored NVM backup...\",\n\t\t\t\"Applying the NVM backup requires a driver restart!\",\n\t\t);\n\t}\n\n\t/**\n\t * Restores an NVM backup that was created with `backupNVMRaw`. The Z-Wave radio is turned off/on automatically.\n\t *\n\t * **WARNING:** The given buffer is NOT checked for compatibility with the current stick. To have Z-Wave JS do that, use the {@link restoreNVM} method instead.\n\t *\n\t * **WARNING:** A failure during this process may brick your controller. Use at your own risk!\n\t * @param nvmData The raw NVM backup to be restored\n\t * @param onProgress Can be used to monitor the progress of the operation, which may take several seconds up to a few minutes depending on the NVM size\n\t */\n\tpublic async restoreNVMRaw(\n\t\tnvmData: Uint8Array,\n\t\tonProgress?: (bytesWritten: number, total: number) => void,\n\t): Promise<void> {\n\t\tthis.driver.controllerLog.print(\"Restoring NVM...\");\n\n\t\t// Turn Z-Wave radio off to avoid having the protocol write to the NVM while dumping it\n\t\tif (!(await this.toggleRF(false))) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Could not turn off the Z-Wave radio before restoring NVM backup!\",\n\t\t\t\tZWaveErrorCodes.Controller_ResponseNOK,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tif (this.sdkVersionGte(\"7.0\")) {\n\t\t\t\tawait this.restoreNVMRaw700(nvmData, onProgress);\n\t\t\t} else {\n\t\t\t\tawait this.restoreNVMRaw500(nvmData, onProgress);\n\t\t\t}\n\t\t\tthis.driver.controllerLog.print(\"NVM backup restored\");\n\t\t} finally {\n\t\t\t// Whatever happens, turn Z-Wave radio back on\n\t\t\tawait this.toggleRF(true);\n\t\t}\n\n\t\t// TODO: You can also get away with eliding all the 0xff pages. The NVR also holds the page size of the NVM (NVMP),\n\t\t// so you can figure out which pages you don't have to save or restore. If you do this, you need to make sure to issue a\n\t\t// \"factory reset\" before restoring the NVM - that'll blank out the NVM to 0xffs before initializing it.\n\n\t\t// After a restored NVM backup, the controller's capabilities may have changed.\n\t\t// Normally we'd only need to soft reset the stick, but we also need to re-interview the controller and potentially all nodes.\n\t\t// Just forcing a restart of the driver seems easier.\n\n\t\t// if (this.driver.options.enableSoftReset) {\n\t\t// \tthis.driver.controllerLog.print(\n\t\t// \t\t\"Activating restored NVM backup...\",\n\t\t// \t);\n\t\t// \tawait this.driver.softReset();\n\t\t// } else {\n\t\t// \tthis.driver.controllerLog.print(\n\t\t// \t\t\"Soft reset not enabled, cannot automatically activate restored NVM backup!\",\n\t\t// \t\t\"warn\",\n\t\t// \t);\n\t\t// }\n\n\t\tthis.driver.controllerLog.print(\n\t\t\t\"Restarting driver to activate restored NVM backup...\",\n\t\t);\n\n\t\tthis.driver.emit(\n\t\t\t\"error\",\n\t\t\tnew ZWaveError(\n\t\t\t\t\"Activating the NVM backup requires a driver restart!\",\n\t\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t\t),\n\t\t);\n\t\tawait this.driver.destroy();\n\t}\n\n\tprivate async restoreNVMRaw500(\n\t\tnvmData: Uint8Array,\n\t\tonProgress?: (bytesWritten: number, total: number) => void,\n\t): Promise<void> {\n\t\tconst size = nvmSizeToBufferSize((await this.getNVMId()).memorySize);\n\t\tif (!size) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Unknown NVM size - cannot restore!\",\n\t\t\t\tZWaveErrorCodes.Controller_NotSupported,\n\t\t\t);\n\t\t} else if (size !== nvmData.length) {\n\t\t\t// This might be a converted NVM buffer which contains only the first relevant part.\n\t\t\t// The first two bytes must point to the last byte in the buffer then\n\t\t\tconst actualSize = 1 + Bytes.view(nvmData).readUInt16BE(0);\n\t\t\tif (actualSize !== nvmData.length) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The given data does not match the NVM size - cannot restore!\",\n\t\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Now we only need to figure out which part of the NVM needs to be overwritten when restoring\n\t\t\tconst firstTwoNVMBytes = Bytes.view(\n\t\t\t\tawait this.externalNVMReadBuffer(0, 2),\n\t\t\t);\n\t\t\tconst oldSize = 1 + firstTwoNVMBytes.readUInt16BE(0);\n\t\t\tif (oldSize > actualSize) {\n\t\t\t\t// Pad the rest with 0xff\n\t\t\t\tnvmData = Bytes.concat([\n\t\t\t\t\tnvmData,\n\t\t\t\t\tBytes.alloc(oldSize - actualSize, 0xff),\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\n\t\t// Figure out the maximum chunk size the Serial API supports\n\t\t// For some reason, there is no documentation and no official command for this\n\t\t// The write requests need 5 bytes more than the read response, so subtract 5 from the returned length\n\t\tconst chunkSize = (await this.externalNVMReadBuffer(0, 0xffff)).length\n\t\t\t- 5;\n\n\t\tfor (let offset = 0; offset < nvmData.length; offset += chunkSize) {\n\t\t\tawait this.externalNVMWriteBuffer(\n\t\t\t\toffset,\n\t\t\t\tnvmData.subarray(offset, offset + chunkSize),\n\t\t\t);\n\t\t\t// Report progress for listeners\n\t\t\tif (onProgress) {\n\t\t\t\tsetImmediate(() => onProgress(offset, nvmData.length));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async restoreNVMRaw700(\n\t\tnvmData: Uint8Array,\n\t\tonProgress?: (bytesWritten: number, total: number) => void,\n\t): Promise<void> {\n\t\tlet open: () => Promise<number>;\n\t\tlet read: (\n\t\t\toffset: number,\n\t\t\tlength: number,\n\t\t) => Promise<{ buffer: Uint8Array; endOfFile: boolean }>;\n\t\tlet write: (\n\t\t\toffset: number,\n\t\t\tbuffer: Uint8Array,\n\t\t) => Promise<{ endOfFile: boolean }>;\n\t\tlet close: () => Promise<void>;\n\n\t\tif (\n\t\t\tthis.supportedFunctionTypes?.includes(\n\t\t\t\tFunctionType.ExtendedNVMOperations,\n\t\t\t)\n\t\t) {\n\t\t\topen = async () => {\n\t\t\t\tconst { size } = await this.externalNVMOpenExt();\n\t\t\t\treturn size;\n\t\t\t};\n\t\t\tread = (offset, length) =>\n\t\t\t\tthis.externalNVMReadBufferExt(offset, length);\n\t\t\twrite = (offset, buffer) =>\n\t\t\t\tthis.externalNVMWriteBufferExt(offset, buffer);\n\t\t\tclose = () => this.externalNVMCloseExt();\n\t\t} else {\n\t\t\topen = () => this.externalNVMOpen();\n\t\t\tread = (offset, length) =>\n\t\t\t\tthis.externalNVMReadBuffer700(offset, length);\n\t\t\twrite = (offset, buffer) =>\n\t\t\t\tthis.externalNVMWriteBuffer700(offset, buffer);\n\t\t\tclose = () => this.externalNVMClose();\n\t\t}\n\n\t\t// Open NVM for reading\n\t\tconst size = await open();\n\n\t\tif (size !== nvmData.length) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The given data does not match the NVM size - cannot restore!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\t// Figure out the maximum chunk size the Serial API supports\n\t\t// For some reason, there is no documentation and no official command for this\n\t\t// The write requests have the same size as the read response - if this yields no\n\t\t// data, default to a sane (but maybe slow) size\n\t\tconst chunkSize = (await read(0, 0xff)).buffer.length || 48;\n\n\t\t// Close NVM and re-open again for writing\n\t\tawait close();\n\t\tawait open();\n\n\t\tfor (let offset = 0; offset < nvmData.length; offset += chunkSize) {\n\t\t\tconst { endOfFile } = await write(\n\t\t\t\toffset,\n\t\t\t\tnvmData.subarray(offset, offset + chunkSize),\n\t\t\t);\n\n\t\t\t// Report progress for listeners\n\t\t\tif (onProgress) setImmediate(() => onProgress(offset, size));\n\n\t\t\tif (endOfFile) break;\n\t\t}\n\t\t// Close NVM\n\t\tawait close();\n\t}\n\n\t/**\n\t * Request the most recent background RSSI levels detected by the controller.\n\t *\n\t * **Note:** This only returns useful values if something was transmitted recently.\n\t */\n\tpublic async getBackgroundRSSI(): Promise<{\n\t\trssiChannel0: RSSI;\n\t\trssiChannel1: RSSI;\n\t\trssiChannel2?: RSSI;\n\t\trssiChannel3?: RSSI;\n\t}> {\n\t\tconst ret = await this.driver.sendMessage<GetBackgroundRSSIResponse>(\n\t\t\tnew GetBackgroundRSSIRequest(),\n\t\t);\n\t\tconst rssi = pick(ret, [\n\t\t\t\"rssiChannel0\",\n\t\t\t\"rssiChannel1\",\n\t\t\t\"rssiChannel2\",\n\t\t\t\"rssiChannel3\",\n\t\t]);\n\n\t\tthis.updateStatistics((current) => {\n\t\t\tconst updated = { ...current };\n\t\t\tupdated.backgroundRSSI = {} as any;\n\n\t\t\t// Average all channels, defaulting to the current measurement\n\t\t\tupdated.backgroundRSSI!.channel0 = {\n\t\t\t\tcurrent: rssi.rssiChannel0,\n\t\t\t\taverage: averageRSSI(\n\t\t\t\t\tcurrent.backgroundRSSI?.channel0.average,\n\t\t\t\t\trssi.rssiChannel0,\n\t\t\t\t\t0.9,\n\t\t\t\t),\n\t\t\t};\n\t\t\tupdated.backgroundRSSI!.channel1 = {\n\t\t\t\tcurrent: rssi.rssiChannel1,\n\t\t\t\taverage: averageRSSI(\n\t\t\t\t\tcurrent.backgroundRSSI?.channel1.average,\n\t\t\t\t\trssi.rssiChannel1,\n\t\t\t\t\t0.9,\n\t\t\t\t),\n\t\t\t};\n\n\t\t\tif (rssi.rssiChannel2 != undefined) {\n\t\t\t\tupdated.backgroundRSSI!.channel2 = {\n\t\t\t\t\tcurrent: rssi.rssiChannel2,\n\t\t\t\t\taverage: averageRSSI(\n\t\t\t\t\t\tcurrent.backgroundRSSI?.channel2?.average,\n\t\t\t\t\t\trssi.rssiChannel2,\n\t\t\t\t\t\t0.9,\n\t\t\t\t\t),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (rssi.rssiChannel3 != undefined) {\n\t\t\t\tupdated.backgroundRSSI!.channel3 = {\n\t\t\t\t\tcurrent: rssi.rssiChannel3,\n\t\t\t\t\taverage: averageRSSI(\n\t\t\t\t\t\tcurrent.backgroundRSSI?.channel3?.average,\n\t\t\t\t\t\trssi.rssiChannel3,\n\t\t\t\t\t\t0.9,\n\t\t\t\t\t),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tupdated.backgroundRSSI!.timestamp = Date.now();\n\n\t\t\treturn updated;\n\t\t});\n\n\t\treturn rssi;\n\t}\n\n\t/**\n\t * Returns whether an OTA firmware update is in progress for any node.\n\t */\n\tpublic isAnyOTAFirmwareUpdateInProgress(): boolean {\n\t\tfor (const node of this._nodes.values()) {\n\t\t\tif (!node.isControllerNode && node.isFirmwareUpdateInProgress()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Retrieves the available firmware updates for the given node from the Z-Wave JS firmware update service.\n\t *\n\t * **Note:** Sleeping nodes need to be woken up for this to work. This method will throw when called for a sleeping node\n\t * which did not wake up within a minute.\n\t *\n\t * **Note:** This requires an API key to be set in the driver options, or passed .\n\t */\n\tpublic async getAvailableFirmwareUpdates(\n\t\tnodeId: number,\n\t\toptions?: GetFirmwareUpdatesOptions,\n\t): Promise<FirmwareUpdateInfo[]> {\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\n\t\tconst { manufacturerId, productType, productId, firmwareVersion } =\n\t\t\tnode;\n\t\t// Be really sure that we have all the information we need\n\t\tif (\n\t\t\ttypeof manufacturerId !== \"number\"\n\t\t\t|| typeof productType !== \"number\"\n\t\t\t|| typeof productId !== \"number\"\n\t\t\t|| typeof firmwareVersion !== \"string\"\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot check for firmware updates for node ${nodeId}: fingerprint or firmware version is unknown!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_MissingInformation,\n\t\t\t);\n\t\t}\n\n\t\t// Now invoke the service\n\t\ttry {\n\t\t\treturn await getAvailableFirmwareUpdates(\n\t\t\t\t{\n\t\t\t\t\tmanufacturerId,\n\t\t\t\t\tproductType,\n\t\t\t\t\tproductId,\n\t\t\t\t\tfirmwareVersion,\n\t\t\t\t\trfRegion: this.rfRegion ?? options?.rfRegion,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tuserAgent: this.driver.getUserAgentStringWithComponents(\n\t\t\t\t\t\toptions?.additionalUserAgentComponents,\n\t\t\t\t\t),\n\t\t\t\t\tapiKey: options?.apiKey\n\t\t\t\t\t\t?? this.driver.options.apiKeys?.firmwareUpdateService,\n\t\t\t\t\tincludePrereleases: options?.includePrereleases,\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (e: any) {\n\t\t\tlet message =\n\t\t\t\t`Cannot check for firmware updates for node ${nodeId}: `;\n\t\t\tif (e.response) {\n\t\t\t\tif (isObject(e.response.data)) {\n\t\t\t\t\tif (typeof e.response.data.error === \"string\") {\n\t\t\t\t\t\tmessage += `${e.response.data.error} `;\n\t\t\t\t\t} else if (typeof e.response.data.message === \"string\") {\n\t\t\t\t\t\tmessage += `${e.response.data.message} `;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmessage += `[${e.response.status} ${e.response.statusText}]`;\n\t\t\t} else if (typeof e.message === \"string\") {\n\t\t\t\tmessage += e.message;\n\t\t\t} else {\n\t\t\t\tmessage += `Failed to download update information!`;\n\t\t\t}\n\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_RequestError,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Ensures that the device ID used to request a firmware update matches the device the firmware update is for */\n\tprivate async ensureFirmwareDeviceIdMatches(\n\t\tnode: ZWaveNode,\n\t\tdeviceId: FirmwareUpdateDeviceID,\n\t): Promise<void> {\n\t\tif (\n\t\t\tdeviceId.rfRegion !== undefined\n\t\t\t&& this.rfRegion !== NOT_KNOWN\n\t\t\t&& deviceId.rfRegion !== this.rfRegion\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot update firmware for node ${node.id}: The firmware update is for a different region!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_DeviceMismatch,\n\t\t\t);\n\t\t}\n\n\t\tconst manufacturerResponse = await node.commandClasses[\n\t\t\t\"Manufacturer Specific\"\n\t\t].get();\n\n\t\tif (!manufacturerResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot check for firmware updates for node ${node.id}: Failed to query fingerprint from the node!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_MissingInformation,\n\t\t\t);\n\t\t}\n\n\t\t// Query the version using both possible commands to ensure we have the full version\n\t\tconst versionResponse = await node.commandClasses.Version.get();\n\t\tif (!versionResponse) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot check for firmware updates for node ${node.id}: Failed to query firmware version from the node!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_MissingInformation,\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tnode.commandClasses.Version.supportsCommand(\n\t\t\t\tVersionCommand.ZWaveSoftwareGet,\n\t\t\t)\n\t\t) {\n\t\t\tconst softwareResponse = await node.commandClasses.Version\n\t\t\t\t.getZWaveSoftware();\n\t\t\tif (!softwareResponse) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`Cannot check for firmware updates for node ${node.id}: Failed to query firmware version from the node!`,\n\t\t\t\t\tZWaveErrorCodes.FWUpdateService_MissingInformation,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst { manufacturerId, productType, productId, firmwareVersion } =\n\t\t\tnode;\n\n\t\tif (\n\t\t\tmanufacturerId !== node.manufacturerId\n\t\t\t|| productType !== node.productType\n\t\t\t|| productId !== node.productId\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot update firmware for node ${node.id}: The firmware update is for a different device!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_DeviceMismatch,\n\t\t\t);\n\t\t} else if (firmwareVersion !== deviceId.firmwareVersion) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot update firmware for node ${node.id}: The update is for a different original firmware version!`,\n\t\t\t\tZWaveErrorCodes.FWUpdateService_DeviceMismatch,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Downloads the desired firmware update(s) from the Z-Wave JS firmware update service and updates the firmware of the given node.\n\t * @param updateInfo The desired entry from the updates array that was returned by {@link getAvailableFirmwareUpdates}.\n\t * Before applying the update, Z-Wave JS will check whether the device IDs, firmware version and region match.\n\t *\n\t * The return value indicates whether the update was successful.\n\t * **WARNING:** This method will throw instead of returning `false` if invalid arguments are passed or downloading files or starting an update fails.\n\t */\n\tpublic async firmwareUpdateOTA(\n\t\tnodeId: number,\n\t\tupdateInfo: FirmwareUpdateInfo,\n\t\toptions?: FirmwareUpdateOptions,\n\t): Promise<FirmwareUpdateResult> {\n\t\t// Don't let two firmware updates happen in parallel\n\t\tif (this.isAnyOTAFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to start the update: A firmware update is already in progress on this network!`;\n\t\t\tthis.driver.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.FirmwareUpdateCC_NetworkBusy,\n\t\t\t);\n\t\t}\n\t\t// Don't allow updating firmware when the controller is currently updating its own firmware\n\t\tif (this.isFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to start the update: The controller is currently being updated!`;\n\t\t\tthis.driver.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.FirmwareUpdateCC_NetworkBusy,\n\t\t\t);\n\t\t}\n\n\t\tconst files = updateInfo.files;\n\t\tconst validateDeviceId = updateInfo.device;\n\n\t\t// We made a breaking change to the method signature. files should never be undefined, unless applications still\n\t\t// use the old signature.\n\t\tif (files?.length === 0) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`At least one update must be provided`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tconst node = this.nodes.getOrThrow(nodeId);\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tnodeId,\n\t\t\t`OTA firmware update started, downloading ${files.length} updates...`,\n\t\t);\n\n\t\tconst loglevel = this.driver.getLogConfig().level;\n\n\t\tconst firmwares: Firmware[] = [];\n\t\tfor (let i = 0; i < files.length; i++) {\n\t\t\tconst update = files[i];\n\t\t\tlet logMessage =\n\t\t\t\t`Downloading firmware update ${i} of ${files.length}...`;\n\t\t\tif (loglevel === \"silly\") {\n\t\t\t\tlogMessage += `\n URL: ${update.url}\n integrity: ${update.integrity}`;\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(nodeId, logMessage);\n\n\t\t\ttry {\n\t\t\t\tconst firmware = await downloadFirmwareUpdate(update);\n\t\t\t\tfirmwares.push(firmware);\n\t\t\t} catch (e: any) {\n\t\t\t\tlet message =\n\t\t\t\t\t`Downloading the firmware update for node ${nodeId} failed:\\n`;\n\t\t\t\tif (isZWaveError(e)) {\n\t\t\t\t\t// Pass \"real\" Z-Wave errors through\n\t\t\t\t\tthrow new ZWaveError(message + e.message, e.code);\n\t\t\t\t} else if (e.response) {\n\t\t\t\t\t// And construct a better error message for HTTP errors\n\t\t\t\t\tif (\n\t\t\t\t\t\tisObject(e.response.data)\n\t\t\t\t\t\t&& typeof e.response.data.message === \"string\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tmessage += `${e.response.data.message} `;\n\t\t\t\t\t}\n\t\t\t\t\tmessage +=\n\t\t\t\t\t\t`[${e.response.status} ${e.response.statusText}]`;\n\t\t\t\t} else if (typeof e.message === \"string\") {\n\t\t\t\t\tmessage += e.message;\n\t\t\t\t} else {\n\t\t\t\t\tmessage += `Failed to download firmware update!`;\n\t\t\t\t}\n\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\tmessage,\n\t\t\t\t\tZWaveErrorCodes.FWUpdateService_RequestError,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Make sure we're not applying the update to the wrong device\n\t\tif (validateDeviceId) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`All updates downloaded, validating device IDs...`,\n\t\t\t);\n\n\t\t\tawait this.ensureFirmwareDeviceIdMatches(node, validateDeviceId);\n\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`Device IDs match, installing firmware updates...`,\n\t\t\t);\n\t\t} else {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tnodeId,\n\t\t\t\t`All updates downloaded, installing...`,\n\t\t\t);\n\t\t}\n\n\t\treturn node.updateFirmware(firmwares, options);\n\t}\n\n\tprivate _firmwareUpdateInProgress: boolean = false;\n\n\t/**\n\t * Returns whether a firmware update is in progress for the controller.\n\t */\n\tpublic isFirmwareUpdateInProgress(): boolean {\n\t\treturn this._firmwareUpdateInProgress;\n\t}\n\n\t/**\n\t * Updates the firmware of the controller using the given firmware file.\n\t *\n\t * The return value indicates whether the update was successful.\n\t * **WARNING:** After a successful update, the Z-Wave driver will destroy itself so it can be restarted.\n\t *\n\t * **WARNING:** A failure during this process may put your controller in recovery mode, rendering it unusable until a correct firmware image is uploaded. Use at your own risk!\n\t */\n\tpublic async firmwareUpdateOTW(\n\t\tdata: Uint8Array,\n\t): Promise<ControllerFirmwareUpdateResult> {\n\t\t// Don't let two firmware updates happen in parallel\n\t\tif (this.isAnyOTAFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to start the update: A firmware update is already in progress on this network!`;\n\t\t\tthis.driver.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.OTW_Update_Busy);\n\t\t}\n\t\t// Don't allow updating firmware when the controller is currently updating its own firmware\n\t\tif (this.isFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to start the update: The controller is currently being updated!`;\n\t\t\tthis.driver.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.OTW_Update_Busy);\n\t\t}\n\n\t\tif (this.driver.isInBootloader() || this.sdkVersionGte(\"7.0\")) {\n\t\t\t// If the controller is stuck in bootloader mode, always use the 700 series update method\n\t\t\treturn this.firmwareUpdateOTW700(data);\n\t\t} else if (\n\t\t\tthis.sdkVersionGte(\"6.50.0\")\n\t\t\t&& this.supportedFunctionTypes?.includes(\n\t\t\t\tFunctionType.FirmwareUpdateNVM,\n\t\t\t)\n\t\t) {\n\t\t\t// This is 500 series\n\t\t\tconst wasUpdated = await this.firmwareUpdateOTW500(data);\n\t\t\tif (wasUpdated.success) {\n\t\t\t\t// After updating the firmware on 500 series sticks, we MUST soft-reset them\n\t\t\t\tawait this.driver.softResetAndRestart(\n\t\t\t\t\t\"Activating new firmware and restarting driver...\",\n\t\t\t\t\t\"Controller firmware updates require a driver restart!\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn wasUpdated;\n\t\t} else {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Firmware updates are not supported on this controller`,\n\t\t\t\tZWaveErrorCodes.Controller_NotSupported,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async firmwareUpdateOTW500(\n\t\tdata: Uint8Array,\n\t): Promise<ControllerFirmwareUpdateResult> {\n\t\tthis._firmwareUpdateInProgress = true;\n\t\tlet turnedRadioOff = false;\n\t\ttry {\n\t\t\tthis.driver.controllerLog.print(\"Beginning firmware update\");\n\n\t\t\tconst canUpdate = await this.firmwareUpdateNVMInit();\n\t\t\tif (!canUpdate) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"OTW update failed: This controller does not support firmware updates\",\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\n\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: ControllerFirmwareUpdateStatus.Error_NotSupported,\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\t// Avoid interruption by incoming messages\n\t\t\tawait this.toggleRF(false);\n\t\t\tturnedRadioOff = true;\n\n\t\t\t// Upload the firmware data\n\t\t\tconst BLOCK_SIZE = 64;\n\t\t\tconst numFragments = Math.ceil(data.length / BLOCK_SIZE);\n\t\t\tfor (let fragment = 0; fragment < numFragments; fragment++) {\n\t\t\t\tconst fragmentData = data.subarray(\n\t\t\t\t\tfragment * BLOCK_SIZE,\n\t\t\t\t\t(fragment + 1) * BLOCK_SIZE,\n\t\t\t\t);\n\t\t\t\tawait this.firmwareUpdateNVMWrite(\n\t\t\t\t\tfragment * BLOCK_SIZE,\n\t\t\t\t\tfragmentData,\n\t\t\t\t);\n\n\t\t\t\t// This progress is technically too low, but we can keep 100% for after CRC checking this way\n\t\t\t\tconst progress: ControllerFirmwareUpdateProgress = {\n\t\t\t\t\tsentFragments: fragment,\n\t\t\t\t\ttotalFragments: numFragments,\n\t\t\t\t\tprogress: roundTo((fragment / numFragments) * 100, 2),\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update progress\", progress);\n\t\t\t}\n\n\t\t\t// Check if a valid image was written\n\t\t\tconst isValidCRC = await this.firmwareUpdateNVMIsValidCRC16();\n\t\t\tif (!isValidCRC) {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"OTW update failed: The firmware image is invalid\",\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\n\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: ControllerFirmwareUpdateStatus.Error_Aborted,\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tthis.emit(\"firmware update progress\", {\n\t\t\t\tsentFragments: numFragments,\n\t\t\t\ttotalFragments: numFragments,\n\t\t\t\tprogress: 100,\n\t\t\t});\n\n\t\t\t// Enable the image\n\t\t\tawait this.firmwareUpdateNVMSetNewImage();\n\n\t\t\tthis.driver.controllerLog.print(\"Firmware update succeeded\");\n\n\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\tsuccess: true,\n\t\t\t\tstatus: ControllerFirmwareUpdateStatus.OK,\n\t\t\t};\n\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tthis._firmwareUpdateInProgress = false;\n\t\t\tif (turnedRadioOff) await this.toggleRF(true);\n\t\t}\n\t}\n\n\tprivate async firmwareUpdateOTW700(\n\t\tdata: Uint8Array,\n\t): Promise<ControllerFirmwareUpdateResult> {\n\t\tthis._firmwareUpdateInProgress = true;\n\t\tlet destroy = false;\n\n\t\ttry {\n\t\t\tif (!this.driver.isInBootloader()) {\n\t\t\t\tawait this.driver.enterBootloader();\n\t\t\t}\n\n\t\t\t// Start the update process\n\t\t\tthis.driver.controllerLog.print(\"Beginning firmware upload\");\n\t\t\tawait this.driver.bootloader.beginUpload();\n\n\t\t\t// Wait for the bootloader to accept fragments\n\t\t\ttry {\n\t\t\t\tawait this.driver.waitForBootloaderChunk(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\tc.type === BootloaderChunkType.Message\n\t\t\t\t\t\t&& c.message === \"begin upload\",\n\t\t\t\t\t5000,\n\t\t\t\t);\n\t\t\t\tawait this.driver.waitForBootloaderChunk(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\tc.type === BootloaderChunkType.FlowControl\n\t\t\t\t\t\t&& c.command === XModemMessageHeaders.C,\n\t\t\t\t\t1000,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"OTW update failed: Expected response not received from the bootloader\",\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: ControllerFirmwareUpdateStatus.Error_Timeout,\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst BLOCK_SIZE = 128;\n\t\t\tif (data.length % BLOCK_SIZE !== 0) {\n\t\t\t\t// Pad the data to a multiple of BLOCK_SIZE\n\t\t\t\tdata = Bytes.concat([\n\t\t\t\t\tdata,\n\t\t\t\t\tnew Bytes(BLOCK_SIZE - (data.length % BLOCK_SIZE)).fill(\n\t\t\t\t\t\t0xff,\n\t\t\t\t\t),\n\t\t\t\t]);\n\t\t\t}\n\t\t\tconst numFragments = Math.ceil(data.length / BLOCK_SIZE);\n\n\t\t\tlet aborted = false;\n\n\t\t\ttransfer: for (\n\t\t\t\tlet fragment = 1;\n\t\t\t\tfragment <= numFragments;\n\t\t\t\tfragment++\n\t\t\t) {\n\t\t\t\tconst fragmentData = data.subarray(\n\t\t\t\t\t(fragment - 1) * BLOCK_SIZE,\n\t\t\t\t\tfragment * BLOCK_SIZE,\n\t\t\t\t);\n\n\t\t\t\tretry: for (let retry = 0; retry < 3; retry++) {\n\t\t\t\t\tawait this.driver.bootloader.uploadFragment(\n\t\t\t\t\t\tfragment,\n\t\t\t\t\t\tfragmentData,\n\t\t\t\t\t);\n\t\t\t\t\tlet result: BootloaderChunk & {\n\t\t\t\t\t\ttype: BootloaderChunkType.FlowControl;\n\t\t\t\t\t};\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = await this.driver.waitForBootloaderChunk(\n\t\t\t\t\t\t\t(c) => c.type === BootloaderChunkType.FlowControl,\n\t\t\t\t\t\t\t1000,\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t\t\"OTW update failed: The bootloader did not acknowledge the start of transfer.\",\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\tstatus:\n\t\t\t\t\t\t\t\tControllerFirmwareUpdateStatus.Error_Timeout,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (result.command) {\n\t\t\t\t\t\tcase XModemMessageHeaders.ACK: {\n\t\t\t\t\t\t\t// The fragment was accepted\n\t\t\t\t\t\t\tconst progress: ControllerFirmwareUpdateProgress = {\n\t\t\t\t\t\t\t\tsentFragments: fragment,\n\t\t\t\t\t\t\t\ttotalFragments: numFragments,\n\t\t\t\t\t\t\t\tprogress: roundTo(\n\t\t\t\t\t\t\t\t\t(fragment / numFragments) * 100,\n\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tthis.emit(\"firmware update progress\", progress);\n\n\t\t\t\t\t\t\t// we've transmitted at least one fragment, so we need to destroy the driver afterwards\n\t\t\t\t\t\t\tdestroy = true;\n\n\t\t\t\t\t\t\tcontinue transfer;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase XModemMessageHeaders.NAK:\n\t\t\t\t\t\t\t// The fragment was rejected, try again\n\t\t\t\t\t\t\tcontinue retry;\n\t\t\t\t\t\tcase XModemMessageHeaders.CAN:\n\t\t\t\t\t\t\t// The bootloader aborted the update. We'll receive the reason afterwards as a message\n\t\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\t\tbreak transfer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"OTW update failed: Maximum retry attempts reached\",\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus:\n\t\t\t\t\t\tControllerFirmwareUpdateStatus.Error_RetryLimitReached,\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tif (aborted) {\n\t\t\t\t// wait for the reason to craft a good error message\n\t\t\t\tconst error = await this.driver\n\t\t\t\t\t.waitForBootloaderChunk<\n\t\t\t\t\t\tBootloaderChunk & { type: BootloaderChunkType.Message }\n\t\t\t\t\t>(\n\t\t\t\t\t\t(c) =>\n\t\t\t\t\t\t\tc.type === BootloaderChunkType.Message\n\t\t\t\t\t\t\t&& c.message.includes(\"error 0x\"),\n\t\t\t\t\t\t1000,\n\t\t\t\t\t)\n\t\t\t\t\t.catch(() => undefined);\n\n\t\t\t\t// wait for the menu screen so it doesn't show up in logs\n\t\t\t\tawait this.driver\n\t\t\t\t\t.waitForBootloaderChunk(\n\t\t\t\t\t\t(c) => c.type === BootloaderChunkType.Menu,\n\t\t\t\t\t\t1000,\n\t\t\t\t\t)\n\t\t\t\t\t.catch(() => undefined);\n\n\t\t\t\tlet message = `OTW update was aborted by the bootloader.`;\n\t\t\t\tif (error) {\n\t\t\t\t\tmessage += ` ${error.message}`;\n\t\t\t\t\t// TODO: parse error code\n\t\t\t\t}\n\t\t\t\tthis.driver.controllerLog.print(message, \"error\");\n\n\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: ControllerFirmwareUpdateStatus.Error_Aborted,\n\t\t\t\t};\n\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\treturn result;\n\t\t\t} else {\n\t\t\t\t// We're done, send EOT and wait for the menu screen\n\t\t\t\tawait this.driver.bootloader.finishUpload();\n\t\t\t\ttry {\n\t\t\t\t\t// The bootloader sends the confirmation and the menu screen very quickly.\n\t\t\t\t\t// Waiting for them separately can cause us to miss the menu screen and\n\t\t\t\t\t// incorrectly assume the update timed out.\n\n\t\t\t\t\tawait Promise.all([\n\t\t\t\t\t\tthis.driver.waitForBootloaderChunk(\n\t\t\t\t\t\t\t(c) =>\n\t\t\t\t\t\t\t\tc.type === BootloaderChunkType.Message\n\t\t\t\t\t\t\t\t&& c.message.includes(\"upload complete\"),\n\t\t\t\t\t\t\t1000,\n\t\t\t\t\t\t),\n\n\t\t\t\t\t\tthis.driver.waitForBootloaderChunk(\n\t\t\t\t\t\t\t(c) => c.type === BootloaderChunkType.Menu,\n\t\t\t\t\t\t\t1000,\n\t\t\t\t\t\t),\n\t\t\t\t\t]);\n\t\t\t\t} catch {\n\t\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\t\"OTW update failed: The bootloader did not acknowledge the end of transfer.\",\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tstatus: ControllerFirmwareUpdateStatus.Error_Timeout,\n\t\t\t\t\t};\n\t\t\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.print(\"Firmware update succeeded\");\n\n\t\t\tconst result: ControllerFirmwareUpdateResult = {\n\t\t\t\tsuccess: true,\n\t\t\t\tstatus: ControllerFirmwareUpdateStatus.OK,\n\t\t\t};\n\t\t\tthis.emit(\"firmware update finished\", result);\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tawait this.driver.leaveBootloader(destroy);\n\t\t\tthis._firmwareUpdateInProgress = false;\n\t\t}\n\t}\n\n\tprivate _currentLearnMode: LearnModeIntent | undefined;\n\tprivate _joinNetworkOptions: JoinNetworkOptions | undefined;\n\n\tpublic async beginJoiningNetwork(\n\t\toptions?: JoinNetworkOptions,\n\t): Promise<JoinNetworkResult> {\n\t\tif (this._currentLearnMode != undefined) {\n\t\t\treturn JoinNetworkResult.Error_Busy;\n\t\t} else if (!this.isLearnModePermitted) {\n\t\t\treturn JoinNetworkResult.Error_NotPermitted;\n\t\t}\n\n\t\t// FIXME: If the join strategy says S0, remove S2 from the NIF before joining\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetLearnModeRequest({\n\t\t\t\t\tintent: LearnModeIntent.Inclusion,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (result.isOK()) {\n\t\t\t\tthis._currentLearnMode = LearnModeIntent.Inclusion;\n\t\t\t\tthis._joinNetworkOptions = options;\n\t\t\t\treturn JoinNetworkResult.OK;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Joining a network failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\n\t\tthis._currentLearnMode = undefined;\n\t\treturn JoinNetworkResult.Error_Failed;\n\t}\n\n\tpublic async stopJoiningNetwork(): Promise<boolean> {\n\t\tif (\n\t\t\tthis._currentLearnMode !== LearnModeIntent.LegacyInclusionExclusion\n\t\t\t// FIXME: ^ only for actual exclusion\n\t\t\t&& this._currentLearnMode !== LearnModeIntent.Inclusion\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetLearnModeRequest({\n\t\t\t\t\t// TODO: We should be using .Stop here for the non-legacy\n\t\t\t\t\t// inclusion/exclusion, but that command results in a\n\t\t\t\t\t// negative response on current firmwares, even though it works.\n\t\t\t\t\t// Using LegacyStop avoids that, but results in an unexpected\n\t\t\t\t\t// LearnModeFailed callback.\n\t\t\t\t\tintent: LearnModeIntent.LegacyStop,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (result.isOK()) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\tthis._joinNetworkOptions = undefined;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Failed to stop joining a network: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic async beginLeavingNetwork(): Promise<LeaveNetworkResult> {\n\t\tif (this._currentLearnMode != undefined) {\n\t\t\treturn LeaveNetworkResult.Error_Busy;\n\t\t} else if (!this.isLearnModePermitted) {\n\t\t\treturn LeaveNetworkResult.Error_NotPermitted;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetLearnModeRequest({\n\t\t\t\t\tintent: LearnModeIntent.NetworkWideExclusion,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (result.isOK()) {\n\t\t\t\tthis._currentLearnMode = LearnModeIntent.NetworkWideExclusion;\n\t\t\t\treturn LeaveNetworkResult.OK;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Leaving the current network failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\n\t\tthis._currentLearnMode = undefined;\n\t\treturn LeaveNetworkResult.Error_Failed;\n\t}\n\n\tpublic async stopLeavingNetwork(): Promise<boolean> {\n\t\tif (\n\t\t\tthis._currentLearnMode !== LearnModeIntent.LegacyInclusionExclusion\n\t\t\t// FIXME: ^ only for actual exclusion\n\t\t\t&& this._currentLearnMode\n\t\t\t\t!== LearnModeIntent.LegacyNetworkWideExclusion\n\t\t\t&& this._currentLearnMode !== LearnModeIntent.DirectExclusion\n\t\t\t&& this._currentLearnMode !== LearnModeIntent.NetworkWideExclusion\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.driver.sendMessage<\n\t\t\t\tMessage & SuccessIndicator\n\t\t\t>(\n\t\t\t\tnew SetLearnModeRequest({\n\t\t\t\t\t// TODO: We should be using .Stop here for the non-legacy\n\t\t\t\t\t// inclusion/exclusion, but that command results in a\n\t\t\t\t\t// negative response on current firmwares, even though it works.\n\t\t\t\t\t// Using LegacyStop avoids that, but results in an unexpected\n\t\t\t\t\t// LearnModeFailed callback.\n\t\t\t\t\tintent: LearnModeIntent.LegacyStop,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (result.isOK()) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t`Failed to stop leaving a network: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Is called when a RemoveNode request is received from the controller.\n\t * Handles and controls the exclusion process.\n\t */\n\tprivate async handleLearnModeCallback(\n\t\tmsg: SetLearnModeCallback,\n\t): Promise<boolean> {\n\t\t// not sure what to do with this message, we're not in learn mode\n\t\tif (this._currentLearnMode == undefined) return false;\n\n\t\t// FIXME: Reset security manager on successful join or leave\n\n\t\tconst wasJoining = this._currentLearnMode === LearnModeIntent.Inclusion\n\t\t\t|| this._currentLearnMode === LearnModeIntent.SmartStart\n\t\t\t|| this._currentLearnMode\n\t\t\t\t=== LearnModeIntent.LegacyNetworkWideInclusion\n\t\t\t|| (this._currentLearnMode\n\t\t\t\t\t=== LearnModeIntent.LegacyInclusionExclusion\n\t\t\t\t// TODO: Secondary controller may also use this to accept controller shift\n\t\t\t\t// Figure out how to detect that.\n\t\t\t\t&& this.role === ControllerRole.Primary);\n\t\tconst wasLeaving =\n\t\t\tthis._currentLearnMode === LearnModeIntent.DirectExclusion\n\t\t\t|| this._currentLearnMode\n\t\t\t\t=== LearnModeIntent.NetworkWideExclusion\n\t\t\t|| this._currentLearnMode\n\t\t\t\t=== LearnModeIntent.LegacyNetworkWideExclusion\n\t\t\t|| (this._currentLearnMode\n\t\t\t\t\t=== LearnModeIntent.LegacyInclusionExclusion\n\t\t\t\t&& this.role !== ControllerRole.Primary);\n\n\t\tif (msg.status === LearnModeStatus.Started) {\n\t\t\t// cool, cool, cool...\n\t\t\treturn true;\n\t\t} else if (msg.status === LearnModeStatus.Failed) {\n\t\t\tif (wasJoining) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\tthis._joinNetworkOptions = undefined;\n\t\t\t\tthis.emit(\"joining network failed\");\n\t\t\t\treturn true;\n\t\t\t} else if (wasLeaving) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\tthis.emit(\"leaving network failed\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (\n\t\t\tmsg.status === LearnModeStatus.Completed\n\t\t\t|| (this._currentLearnMode >= LearnModeIntent.Inclusion\n\t\t\t\t&& msg.status === LearnModeStatus.ProtocolDone)\n\t\t) {\n\t\t\tif (wasJoining) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\tthis.driver[\"_securityManager\"] = undefined;\n\t\t\t\tthis.driver[\"_securityManager2\"] = await SecurityManager2\n\t\t\t\t\t.create();\n\t\t\t\tthis.driver[\"_securityManagerLR\"] = await SecurityManager2\n\t\t\t\t\t.create();\n\t\t\t\tthis._nodes.clear();\n\n\t\t\t\tprocess.nextTick(() => this.afterJoiningNetwork().catch(noop));\n\t\t\t\treturn true;\n\t\t\t} else if (wasLeaving) {\n\t\t\t\tthis._currentLearnMode = undefined;\n\t\t\t\tthis.emit(\"network left\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// not sure what to do with this message\n\t\treturn false;\n\t}\n\n\tprivate async expectSecurityBootstrapS0(\n\t\tbootstrappingNode: ZWaveNode,\n\t): Promise<SecurityBootstrapFailure | undefined> {\n\t\t// When bootstrapping with S0, no other keys are granted\n\t\tfor (const secClass of securityClassOrder) {\n\t\t\tif (secClass !== SecurityClass.S0_Legacy) {\n\t\t\t\tbootstrappingNode.securityClasses.set(secClass, false);\n\t\t\t}\n\t\t}\n\n\t\tconst unGrantSecurityClass = () => {\n\t\t\tthis.driver[\"_securityManager\"] = undefined;\n\t\t\tbootstrappingNode.securityClasses.set(\n\t\t\t\tSecurityClass.S0_Legacy,\n\t\t\t\tfalse,\n\t\t\t);\n\t\t};\n\n\t\tconst abortTimeout = () => {\n\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Security S0 bootstrapping failed: a secure inclusion timer has elapsed`,\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\n\t\t\tunGrantSecurityClass();\n\t\t\treturn SecurityBootstrapFailure.Timeout;\n\t\t};\n\n\t\ttry {\n\t\t\tconst api = bootstrappingNode.commandClasses.Security;\n\n\t\t\t// For the first part of the bootstrapping, a temporary key needs to be used\n\t\t\tthis.driver[\"_securityManager\"] = new SecurityManager({\n\t\t\t\townNodeId: this._ownNodeId!,\n\t\t\t\tnetworkKey: new Uint8Array(16).fill(0),\n\t\t\t\tnonceTimeout: this.driver.options.timeouts.nonce,\n\t\t\t});\n\n\t\t\t// Report the supported schemes\n\t\t\tawait api.reportSecurityScheme(false);\n\n\t\t\t// Expect a NonceGet within 10 seconds\n\t\t\tlet nonceGet = await this.driver.waitForCommand<SecurityCCNonceGet>(\n\t\t\t\t(cc) => cc instanceof SecurityCCNonceGet,\n\t\t\t\t10000,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (nonceGet === \"timeout\") return abortTimeout();\n\n\t\t\t// Send nonce\n\t\t\tawait api.sendNonce();\n\n\t\t\t// Expect NetworkKeySet within 10 seconds\n\t\t\tconst networkKeySet = await this.driver.waitForCommand<\n\t\t\t\tSecurityCCNetworkKeySet\n\t\t\t>(\n\t\t\t\t(cc) => cc instanceof SecurityCCNetworkKeySet,\n\t\t\t\t10000,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (networkKeySet === \"timeout\") return abortTimeout();\n\n\t\t\t// Now that the key is known, we can create the real security manager\n\t\t\tthis.driver[\"_securityManager\"] = new SecurityManager({\n\t\t\t\townNodeId: this._ownNodeId!,\n\t\t\t\tnetworkKey: networkKeySet.networkKey,\n\t\t\t\tnonceTimeout: this.driver.options.timeouts.nonce,\n\t\t\t});\n\n\t\t\t// Request a new nonce to respond, which should be answered within 10 seconds\n\t\t\tlet nonce = await api.withOptions({ reportTimeoutMs: 10000 })\n\t\t\t\t.getNonce();\n\t\t\tif (!nonce) return abortTimeout();\n\n\t\t\t// Verify the key\n\t\t\tawait api.verifyNetworkKey();\n\n\t\t\t// We are a controller, so continue with scheme inherit\n\n\t\t\t// Expect a NonceGet within 10 seconds\n\t\t\tnonceGet = await this.driver.waitForCommand<SecurityCCNonceGet>(\n\t\t\t\t(cc) => cc instanceof SecurityCCNonceGet,\n\t\t\t\t10000,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (nonceGet === \"timeout\") return abortTimeout();\n\n\t\t\t// Send nonce\n\t\t\tawait api.sendNonce();\n\n\t\t\t// Expect SchemeInherit within 10 seconds\n\t\t\tconst schemeInherit = await this.driver.waitForCommand<\n\t\t\t\tSecurityCCSchemeInherit\n\t\t\t>(\n\t\t\t\t(cc) => cc instanceof SecurityCCSchemeInherit,\n\t\t\t\t10000,\n\t\t\t).catch(() => \"timeout\" as const);\n\t\t\tif (schemeInherit === \"timeout\") return abortTimeout();\n\n\t\t\t// Request a new nonce to respond, which should be answered within 10 seconds\n\t\t\tnonce = await api.withOptions({ reportTimeoutMs: 10000 })\n\t\t\t\t.getNonce();\n\t\t\tif (!nonce) return abortTimeout();\n\n\t\t\t// Report the supported schemes. This isn't technically correct, but since\n\t\t\t// S0 won't get any extensions, we can just report the default scheme again\n\t\t\tawait api.reportSecurityScheme(true);\n\n\t\t\t// Remember that the S0 key was granted\n\t\t\tbootstrappingNode.securityClasses.set(\n\t\t\t\tSecurityClass.S0_Legacy,\n\t\t\t\ttrue,\n\t\t\t);\n\n\t\t\t// Store the key\n\t\t\tthis.driver.cacheSet(\n\t\t\t\tcacheKeys.controller.securityKeys(SecurityClass.S0_Legacy),\n\t\t\t\tnetworkKeySet.networkKey,\n\t\t\t);\n\n\t\t\tthis.driver.driverLog.print(\n\t\t\t\t`Security S0 bootstrapping successful`,\n\t\t\t);\n\n\t\t\t// success \uD83C\uDF89\n\t\t} catch (e) {\n\t\t\tlet errorMessage = `Security S0 bootstrapping failed`;\n\t\t\tlet result = SecurityBootstrapFailure.Unknown;\n\t\t\tif (!isZWaveError(e)) {\n\t\t\t\terrorMessage += `: ${e as any}`;\n\t\t\t} else if (e.code === ZWaveErrorCodes.Controller_MessageExpired) {\n\t\t\t\terrorMessage += \": a secure inclusion timer has elapsed.\";\n\t\t\t\tresult = SecurityBootstrapFailure.Timeout;\n\t\t\t} else if (\n\t\t\t\te.code !== ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t&& e.code !== ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t) {\n\t\t\t\terrorMessage += `: ${e.message}`;\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tbootstrappingNode.id,\n\t\t\t\terrorMessage,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tunGrantSecurityClass();\n\n\t\t\treturn result;\n\t\t}\n\t}\n\n\tprivate async expectSecurityBootstrapS2(\n\t\tbootstrappingNode: ZWaveNode,\n\t\trequested: InclusionGrant,\n\t\tuserCallbacks: JoinNetworkUserCallbacks | undefined =\n\t\t\tthis.driver.options.joinNetworkUserCallbacks,\n\t): Promise<\n\t\tSecurityBootstrapFailure | undefined\n\t> {\n\t\tconst api = bootstrappingNode.commandClasses[\"Security 2\"]\n\t\t\t.withOptions({\n\t\t\t\t// Do not wait for Nonce Reports after SET-type commands.\n\t\t\t\t// Timing is critical here\n\t\t\t\ts2VerifyDelivery: false,\n\t\t\t});\n\n\t\tconst unGrantSecurityClasses = () => {\n\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\tbootstrappingNode.securityClasses.set(secClass, false);\n\t\t\t}\n\t\t};\n\n\t\t// FIXME: Abstract this out so it can be reused as primary and secondary\n\t\tconst securityManager = isLongRangeNodeId(this._ownNodeId!)\n\t\t\t? this.driver.securityManagerLR\n\t\t\t: this.driver.securityManager2;\n\n\t\tif (!securityManager) {\n\t\t\t// This should not happen when joining a network.\n\t\t\tunGrantSecurityClasses();\n\t\t\treturn SecurityBootstrapFailure.NoKeysConfigured;\n\t\t}\n\n\t\tconst receivedKeys = new Map<SecurityClass, Uint8Array>();\n\n\t\tconst deleteTempKey = () => {\n\t\t\t// Whatever happens, no further communication needs the temporary key\n\t\t\tsecurityManager.deleteNonce(bootstrappingNode.id);\n\t\t\tsecurityManager.tempKeys.delete(bootstrappingNode.id);\n\t\t};\n\n\t\tlet dskHidden = false;\n\t\tconst applicationHideDSK = () => {\n\t\t\tif (dskHidden) return;\n\t\t\tdskHidden = true;\n\t\t\ttry {\n\t\t\t\tuserCallbacks?.done();\n\t\t\t} catch {\n\t\t\t\t// ignore application-level errors\n\t\t\t}\n\t\t};\n\n\t\tconst abort = async (failType?: KEXFailType): Promise<void> => {\n\t\t\tapplicationHideDSK();\n\t\t\tif (failType != undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tawait api.abortKeyExchange(failType);\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Un-grant S2 security classes we might have granted\n\t\t\tunGrantSecurityClasses();\n\t\t\tdeleteTempKey();\n\t\t};\n\n\t\tconst abortTimeout = async () => {\n\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Security S2 bootstrapping failed: a secure inclusion timer has elapsed`,\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\n\t\t\tawait abort();\n\t\t\treturn SecurityBootstrapFailure.Timeout;\n\t\t};\n\n\t\tconst abortCanceled = async () => {\n\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`The including node canceled the Security S2 bootstrapping.`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\t\t\tawait abort();\n\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t};\n\n\t\ttry {\n\t\t\t// Send with our desired keys\n\t\t\tawait api.requestKeys({\n\t\t\t\trequestedKeys: requested.securityClasses,\n\t\t\t\trequestCSA: false,\n\t\t\t\tsupportedECDHProfiles: [ECDHProfiles.Curve25519],\n\t\t\t\tsupportedKEXSchemes: [KEXSchemes.KEXScheme1],\n\t\t\t});\n\n\t\t\t// Wait for including node to grant keys\n\t\t\tconst kexSet = await this.driver.waitForCommand<\n\t\t\t\tSecurity2CCKEXSet | Security2CCKEXFail\n\t\t\t>(\n\t\t\t\t(cc) =>\n\t\t\t\t\tcc instanceof Security2CCKEXSet\n\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\tinclusionTimeouts.TB2,\n\t\t\t).catch(() => \"timeout\" as const);\n\n\t\t\tif (kexSet === \"timeout\") return abortTimeout();\n\t\t\tif (kexSet instanceof Security2CCKEXFail) {\n\t\t\t\treturn abortCanceled();\n\t\t\t}\n\n\t\t\t// Validate the command\n\t\t\t// Echo flag must be false\n\t\t\tif (kexSet.echo) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: KEX Set unexpectedly has the echo flag set.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoVerify);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t} else if (\n\t\t\t\tkexSet.selectedKEXScheme !== KEXSchemes.KEXScheme1\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Unsupported key exchange scheme.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoSupportedScheme);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t} else if (\n\t\t\t\tkexSet.selectedECDHProfile !== ECDHProfiles.Curve25519\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Unsupported ECDH profile.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoSupportedCurve);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t} else if (kexSet.permitCSA !== false) {\n\t\t\t\t// We do not support CSA at the moment, so it is never requested.\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: CSA granted but not requested.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.BootstrappingCanceled);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t}\n\n\t\t\tconst matchingKeys = kexSet.grantedKeys.filter((k) =>\n\t\t\t\tsecurityClassOrder.includes(k as any)\n\t\t\t\t&& requested.securityClasses.includes(k)\n\t\t\t);\n\t\t\tif (!matchingKeys.length) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: None of the requested security classes are granted.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.NoKeyMatch);\n\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t}\n\n\t\t\tconst highestGranted = getHighestSecurityClass(matchingKeys);\n\t\t\tconst requiresAuthentication =\n\t\t\t\thighestGranted === SecurityClass.S2_AccessControl\n\t\t\t\t|| highestGranted === SecurityClass.S2_Authenticated;\n\n\t\t\t// If authentication is required, use the (static) authenticated ECDH key pair,\n\t\t\t// otherwise generate a new one\n\t\t\tconst keyPair = requiresAuthentication\n\t\t\t\t? this.driver.getLearnModeAuthenticatedKeyPair()\n\t\t\t\t: generateECDHKeyPairSync();\n\t\t\tconst publicKey = extractRawECDHPublicKeySync(keyPair.publicKey);\n\t\t\tconst transmittedPublicKey = Bytes.from(publicKey);\n\t\t\tif (requiresAuthentication) {\n\t\t\t\t// Authentication requires obfuscating the public key\n\t\t\t\ttransmittedPublicKey.writeUInt16BE(0x0000, 0);\n\n\t\t\t\t// Show the DSK to the user\n\t\t\t\tconst dsk = dskToString(publicKey.subarray(0, 16));\n\t\t\t\ttry {\n\t\t\t\t\tuserCallbacks?.showDSK(dsk);\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore application-level errors\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait api.sendPublicKey(transmittedPublicKey, false);\n\n\t\t\t// Wait for including node to send its public key\n\t\t\tconst pubKeyReport = await this.driver.waitForCommand<\n\t\t\t\tSecurity2CCPublicKeyReport | Security2CCKEXFail\n\t\t\t>(\n\t\t\t\t(cc) =>\n\t\t\t\t\tcc instanceof Security2CCPublicKeyReport\n\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\tinclusionTimeouts.TB3,\n\t\t\t).catch(() => \"timeout\" as const);\n\n\t\t\tif (pubKeyReport === \"timeout\") return abortTimeout();\n\t\t\tif (pubKeyReport instanceof Security2CCKEXFail) {\n\t\t\t\treturn abortCanceled();\n\t\t\t}\n\n\t\t\tconst includingNodePubKey = pubKeyReport.publicKey;\n\t\t\tconst sharedSecret = crypto.diffieHellman({\n\t\t\t\tpublicKey: importRawECDHPublicKeySync(includingNodePubKey),\n\t\t\t\tprivateKey: keyPair.privateKey,\n\t\t\t});\n\n\t\t\t// Derive temporary key from ECDH key pair - this will allow us to receive the node's KEX SET commands\n\t\t\tconst tempKeys = await deriveTempKeysAsync(\n\t\t\t\tawait computePRKAsync(\n\t\t\t\t\tsharedSecret,\n\t\t\t\t\tincludingNodePubKey,\n\t\t\t\t\tpublicKey,\n\t\t\t\t),\n\t\t\t);\n\t\t\tsecurityManager.deleteNonce(bootstrappingNode.id);\n\t\t\tsecurityManager.tempKeys.set(bootstrappingNode.id, {\n\t\t\t\tkeyCCM: tempKeys.tempKeyCCM,\n\t\t\t\tpersonalizationString: tempKeys.tempPersonalizationString,\n\t\t\t});\n\n\t\t\t// Wait for the confirmation of the requested keys and\n\t\t\t// retransmit the KEXSet echo every 10 seconds until a response is\n\t\t\t// received or the process timed out.\n\t\t\tconst confirmKeysStartTime = Date.now();\n\t\t\tlet kexReportEcho:\n\t\t\t\t| Security2CCKEXReport\n\t\t\t\t| Security2CCKEXFail\n\t\t\t\t| \"timeout\"\n\t\t\t\t| undefined;\n\t\t\tfor (let i = 0; i <= 25; i++) {\n\t\t\t\ttry {\n\t\t\t\t\tkexReportEcho = await api.withOptions({\n\t\t\t\t\t\treportTimeoutMs: 10000,\n\t\t\t\t\t}).confirmGrantedKeys({\n\t\t\t\t\t\tgrantedKeys: kexSet.grantedKeys,\n\t\t\t\t\t\tpermitCSA: kexSet.permitCSA,\n\t\t\t\t\t\tselectedECDHProfile: kexSet.selectedECDHProfile,\n\t\t\t\t\t\tselectedKEXScheme: kexSet.selectedKEXScheme,\n\t\t\t\t\t\t_reserved: kexSet._reserved,\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t\tif (kexReportEcho != undefined) break;\n\t\t\t\tif (Date.now() - confirmKeysStartTime > 240000) break;\n\t\t\t}\n\n\t\t\tif (!kexReportEcho || kexReportEcho === \"timeout\") {\n\t\t\t\treturn abortTimeout();\n\t\t\t} else if (kexReportEcho instanceof Security2CCKEXFail) {\n\t\t\t\treturn abortCanceled();\n\t\t\t}\n\n\t\t\t// The application no longer needs to show the DSK\n\t\t\tapplicationHideDSK();\n\n\t\t\t// Validate the response\n\t\t\tif (!kexReportEcho.echo) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: KEXReport received without echo flag`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (kexReportEcho.requestCSA !== false) {\n\t\t\t\t// We don't request CSA\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Invalid KEXReport received`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (kexReportEcho._reserved !== 0) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Invalid KEXReport received`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t} else if (\n\t\t\t\t!kexReportEcho.isEncapsulatedWith(\n\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Command received without encryption`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t} else if (\n\t\t\t\tkexReportEcho.requestedKeys.length\n\t\t\t\t\t!== requested.securityClasses.length\n\t\t\t\t|| !kexReportEcho.requestedKeys.every((k) =>\n\t\t\t\t\trequested.securityClasses.includes(k)\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Security S2 bootstrapping failed: Granted key mismatch.`,\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t}\n\n\t\t\tfor (const key of kexSet.grantedKeys) {\n\t\t\t\t// Request network key and wait for including node to respond\n\t\t\t\tconst keyReportPromise = this.driver.waitForCommand<\n\t\t\t\t\tSecurity2CCNetworkKeyReport | Security2CCKEXFail\n\t\t\t\t>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc instanceof Security2CCNetworkKeyReport\n\t\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\t\tinclusionTimeouts.TB4,\n\t\t\t\t).catch(() => \"timeout\" as const);\n\n\t\t\t\tawait api.requestNetworkKey(key);\n\t\t\t\tconst keyReport = await keyReportPromise;\n\n\t\t\t\tif (keyReport === \"timeout\") return abortTimeout();\n\t\t\t\tif (keyReport instanceof Security2CCKEXFail) {\n\t\t\t\t\treturn abortCanceled();\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t!keyReport.isEncapsulatedWith(\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Command received without encryption`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t}\n\n\t\t\t\t// Ensure it was received encrypted with the temporary key\n\t\t\t\tif (\n\t\t\t\t\t!securityManager.hasUsedSecurityClass(\n\t\t\t\t\t\tbootstrappingNode.id,\n\t\t\t\t\t\tSecurityClass.Temporary,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Node used wrong key to communicate.`,\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t}\n\n\t\t\t\tconst securityClass = keyReport.grantedKey;\n\t\t\t\tif (securityClass !== key) {\n\t\t\t\t\t// and that the granted key is the requested key\n\t\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Received key for wrong security class`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.DifferentKey);\n\t\t\t\t\treturn SecurityBootstrapFailure.ParameterMismatch;\n\t\t\t\t}\n\n\t\t\t\t// Store the network key\n\t\t\t\treceivedKeys.set(securityClass, keyReport.networkKey);\n\t\t\t\tawait securityManager.setKeyAsync(\n\t\t\t\t\tsecurityClass,\n\t\t\t\t\tkeyReport.networkKey,\n\t\t\t\t);\n\t\t\t\tif (securityClass === SecurityClass.S0_Legacy) {\n\t\t\t\t\t// TODO: This is awkward to have here\n\t\t\t\t\tthis.driver[\"_securityManager\"] = new SecurityManager({\n\t\t\t\t\t\townNodeId: this._ownNodeId!,\n\t\t\t\t\t\tnetworkKey: keyReport.networkKey,\n\t\t\t\t\t\tnonceTimeout: this.driver.options.timeouts.nonce,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Force nonce synchronization, then verify the network key\n\t\t\t\tsecurityManager.deleteNonce(bootstrappingNode.id);\n\t\t\t\tawait api.withOptions({\n\t\t\t\t\ts2OverrideSecurityClass: securityClass,\n\t\t\t\t}).verifyNetworkKey();\n\n\t\t\t\t// Force nonce synchronization again for the temporary key\n\t\t\t\tsecurityManager.deleteNonce(bootstrappingNode.id);\n\n\t\t\t\t// Wait for including node to send its public key\n\t\t\t\tconst transferEnd = await this.driver.waitForCommand<\n\t\t\t\t\tSecurity2CCTransferEnd | Security2CCKEXFail\n\t\t\t\t>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc instanceof Security2CCTransferEnd\n\t\t\t\t\t\t|| cc instanceof Security2CCKEXFail,\n\t\t\t\t\tinclusionTimeouts.TB5,\n\t\t\t\t).catch(() => \"timeout\" as const);\n\n\t\t\t\tif (transferEnd === \"timeout\") return abortTimeout();\n\t\t\t\tif (transferEnd instanceof Security2CCKEXFail) {\n\t\t\t\t\treturn abortCanceled();\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\t!keyReport.isEncapsulatedWith(\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Command received without encryption`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.S2WrongSecurityLevel;\n\t\t\t\t} else if (\n\t\t\t\t\t!transferEnd.keyVerified || transferEnd.keyRequestComplete\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(bootstrappingNode.id, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Security S2 bootstrapping failed: Invalid TransferEnd received`,\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t\tawait abort(KEXFailType.WrongSecurityLevel);\n\t\t\t\t\treturn SecurityBootstrapFailure.NodeCanceled;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Confirm end of bootstrapping\n\t\t\tawait api.endKeyExchange();\n\n\t\t\t// Remember all security classes we were granted\n\t\t\tfor (const securityClass of securityClassOrder) {\n\t\t\t\tbootstrappingNode.securityClasses.set(\n\t\t\t\t\tsecurityClass,\n\t\t\t\t\tkexSet.grantedKeys.includes(securityClass),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// And store the keys\n\t\t\tfor (const [secClass, key] of receivedKeys) {\n\t\t\t\tthis.driver.cacheSet(\n\t\t\t\t\tcacheKeys.controller.securityKeys(secClass),\n\t\t\t\t\tkey,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.driver.driverLog.print(\n\t\t\t\t`Security S2 bootstrapping successful with these security classes:${\n\t\t\t\t\t[\n\t\t\t\t\t\t...bootstrappingNode.securityClasses.entries(),\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(([, v]) => v)\n\t\t\t\t\t\t.map(([k]) =>\n\t\t\t\t\t\t\t`\\n\u00B7 ${getEnumMemberName(SecurityClass, k)}`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}`,\n\t\t\t);\n\n\t\t\t// success \uD83C\uDF89\n\t\t} catch (e) {\n\t\t\tlet errorMessage =\n\t\t\t\t`Security S2 bootstrapping failed, no S2 security classes were granted`;\n\t\t\tlet result = SecurityBootstrapFailure.Unknown;\n\t\t\tif (!isZWaveError(e)) {\n\t\t\t\terrorMessage += `: ${e as any}`;\n\t\t\t} else if (e.code === ZWaveErrorCodes.Controller_MessageExpired) {\n\t\t\t\terrorMessage += \": a secure inclusion timer has elapsed.\";\n\t\t\t\tresult = SecurityBootstrapFailure.Timeout;\n\t\t\t} else if (\n\t\t\t\te.code !== ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t&& e.code !== ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t) {\n\t\t\t\terrorMessage += `: ${e.message}`;\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tbootstrappingNode.id,\n\t\t\t\terrorMessage,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\t// Remember that we were NOT granted any S2 security classes\n\t\t\tunGrantSecurityClasses();\n\t\t\tbootstrappingNode.removeCC(CommandClasses[\"Security 2\"]);\n\n\t\t\treturn result;\n\t\t} finally {\n\t\t\t// Whatever happens, no further communication needs the temporary key\n\t\t\tdeleteTempKey();\n\t\t}\n\t}\n\n\tprivate async afterJoiningNetwork(): Promise<void> {\n\t\tthis.driver.driverLog.print(\"waiting for security bootstrapping...\");\n\n\t\tconst bootstrapInitStart = Date.now();\n\t\tconst supportedCCs = determineNIF().supportedCCs;\n\t\tconst supportsS0 = supportedCCs.includes(CommandClasses.Security);\n\t\tconst supportsS2 = supportedCCs.includes(CommandClasses[\"Security 2\"]);\n\n\t\tlet initTimeout: number;\n\t\tlet initPredicate: (cc: CCId) => boolean;\n\n\t\t// KEX Get must be received:\n\t\t// - no later than 10..30 seconds after the inclusion if S0 is supported\n\t\t// - no later than 30 seconds after the inclusion if only S2 is supported\n\t\t// For simplicity, we wait the full 30s.\n\t\t// SecurityCCSchemeGet must be received no later than 10 seconds\n\t\t// after the inclusion if S0 is supported\n\t\tif (supportsS0 && supportsS2) {\n\t\t\tinitTimeout = inclusionTimeouts.TB1;\n\t\t\tinitPredicate = (cc) =>\n\t\t\t\tcc instanceof SecurityCCSchemeGet\n\t\t\t\t|| cc instanceof Security2CCKEXGet;\n\t\t} else if (supportsS2) {\n\t\t\tinitTimeout = inclusionTimeouts.TB1;\n\t\t\tinitPredicate = (cc) => cc instanceof Security2CCKEXGet;\n\t\t} else if (supportsS0) {\n\t\t\tinitTimeout = 10000;\n\t\t\tinitPredicate = (cc) => cc instanceof SecurityCCSchemeGet;\n\t\t} else {\n\t\t\tinitTimeout = 0;\n\t\t\tinitPredicate = () => false;\n\t\t}\n\n\t\tconst bootstrapInitPromise = this.driver.waitForCommand<\n\t\t\tSecurity2CCKEXGet | SecurityCCSchemeGet\n\t\t>(initPredicate, initTimeout).catch(() => \"timeout\" as const);\n\n\t\tconst identifySelf = async () => {\n\t\t\t// Update own node ID and other controller flags.\n\t\t\tawait this.identify().catch(noop);\n\n\t\t\t// Notify applications that we're now part of a new network\n\t\t\t// The driver will point the databases to the new home ID\n\t\t\tthis.emit(\"network found\", this._homeId!, this._ownNodeId!);\n\n\t\t\t// Figure out the controller's network role\n\t\t\tawait this.getControllerCapabilities().catch(noop);\n\n\t\t\t// Create new node instances\n\t\t\tconst { nodeIds } = await this.getSerialApiInitData();\n\t\t\tawait this.initNodes(nodeIds, [], () => Promise.resolve());\n\t\t};\n\n\t\t// Do the self-identification while waiting for the bootstrap init command\n\t\tconst [bootstrapInit] = await Promise.all([\n\t\t\tbootstrapInitPromise,\n\t\t\tidentifySelf(),\n\t\t]);\n\n\t\tif (bootstrapInit === \"timeout\") {\n\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\"No security bootstrapping command received, continuing without encryption...\",\n\t\t\t);\n\t\t} else if (bootstrapInit instanceof SecurityCCSchemeGet) {\n\t\t\tconst nodeId = bootstrapInit.nodeId;\n\t\t\tconst bootstrappingNode = this.nodes.get(nodeId);\n\t\t\tif (!bootstrappingNode) {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Received S2 bootstrap initiation from unknown node, ignoring...\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t} else if (Date.now() - bootstrapInitStart > 10000) {\n\t\t\t\t// Received too late, S0 bootstrapping must not continue\n\t\t\t\tthis.driver.controllerLog.print(\n\t\t\t\t\t\"Security S0 bootstrapping command received too late, continuing without encryption...\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// We definitely know that the node supports S0\n\t\t\t\tbootstrappingNode.addCC(CommandClasses.Security, {\n\t\t\t\t\tsecure: true,\n\t\t\t\t\tisSupported: true,\n\t\t\t\t});\n\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage: `Received S0 bootstrap initiation`,\n\t\t\t\t});\n\n\t\t\t\tconst bootstrapResult = await this.expectSecurityBootstrapS0(\n\t\t\t\t\tbootstrappingNode,\n\t\t\t\t);\n\t\t\t\tif (bootstrapResult !== undefined) {\n\t\t\t\t\t// If there was a failure, mark S0 as not supported\n\t\t\t\t\tbootstrappingNode.removeCC(CommandClasses.Security);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (bootstrapInit instanceof Security2CCKEXGet) {\n\t\t\tconst nodeId = bootstrapInit.nodeId as number;\n\t\t\tconst bootstrappingNode = this.nodes.get(nodeId);\n\t\t\tif (!bootstrappingNode) {\n\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Received S2 bootstrap initiation from unknown node, ignoring...\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// We definitely know that the node supports S2\n\t\t\t\tbootstrappingNode.addCC(CommandClasses[\"Security 2\"], {\n\t\t\t\t\tsecure: true,\n\t\t\t\t\tisSupported: true,\n\t\t\t\t});\n\n\t\t\t\tlet grant: InclusionGrant | undefined;\n\n\t\t\t\tswitch (this._joinNetworkOptions?.strategy) {\n\t\t\t\t\t// case JoinNetworkStrategy.Security_S2: {\n\t\t\t\t\t// \tgrant = this._joinNetworkOptions.requested;\n\t\t\t\t\t// \tbreak;\n\t\t\t\t\t// }\n\t\t\t\t\t// case JoinNetworkStrategy.SmartStart:\n\t\t\t\t\tcase JoinNetworkStrategy.Default:\n\t\t\t\t\tdefault: {\n\t\t\t\t\t\t// No options given, just request all keys\n\t\t\t\t\t\tgrant = {\n\t\t\t\t\t\t\tsecurityClasses: [...securityClassOrder],\n\t\t\t\t\t\t\tclientSideAuth: false,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (grant) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Received S2 bootstrap initiation, requesting keys: ${\n\t\t\t\t\t\t\t\tgrant.securityClasses.map((sc) =>\n\t\t\t\t\t\t\t\t\t`\\n\u00B7 ${\n\t\t\t\t\t\t\t\t\t\tgetEnumMemberName(SecurityClass, sc)\n\t\t\t\t\t\t\t\t\t}\\n`\n\t\t\t\t\t\t\t\t).join(\"\")\n\t\t\t\t\t\t\t}\n client-side auth: ${grant.clientSideAuth}`,\n\t\t\t\t\t});\n\n\t\t\t\t\tconst bootstrapResult = await this\n\t\t\t\t\t\t.expectSecurityBootstrapS2(\n\t\t\t\t\t\t\tbootstrappingNode,\n\t\t\t\t\t\t\tgrant,\n\t\t\t\t\t\t);\n\t\t\t\t\tif (bootstrapResult !== undefined) {\n\t\t\t\t\t\t// If there was a failure, mark S2 as not supported\n\t\t\t\t\t\tbootstrappingNode.removeCC(\n\t\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._joinNetworkOptions = undefined;\n\n\t\t// Read protocol information of all nodes\n\t\tfor (const node of this.nodes.values()) {\n\t\t\tif (node.isControllerNode) continue;\n\t\t\tawait node[\"queryProtocolInfo\"]();\n\t\t}\n\n\t\t// Notify applications that joining the network is complete\n\t\tthis.emit(\"network joined\");\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["import_serialapi", "import_Types", "entry", "node", "nodeId", "tai2RemainingMs", "crypto", "ccUtils", "size", "offset", "result"]
7
7
  }