zwave-js 15.3.0 → 15.3.2

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/driver/Driver.ts"],
4
- "sourcesContent": ["import type { JsonlDBOptions } from \"@alcalzone/jsonl-db\";\nimport {\n\ttype CCAPIHost,\n\ttype CCEncodingContext,\n\ttype CCParsingContext,\n\tCRC16CC,\n\tCRC16CCCommandEncapsulation,\n\tCommandClass,\n\ttype FirmwareUpdateResult,\n\tInclusionControllerCCInitiate,\n\tInclusionControllerStep,\n\ttype InterviewContext,\n\ttype InterviewOptions,\n\tInvalidCC,\n\tKEXFailType,\n\tMultiChannelCC,\n\tNoOperationCC,\n\ttype PersistValuesContext,\n\ttype Powerlevel,\n\ttype RefreshValueTimeouts,\n\ttype RefreshValuesContext,\n\ttype SchedulePollOptions,\n\tSecurity2CC,\n\tSecurity2CCCommandsSupportedGet,\n\tSecurity2CCCommandsSupportedReport,\n\tSecurity2CCMessageEncapsulation,\n\tSecurity2CCNonceGet,\n\tSecurity2CCNonceReport,\n\tSecurity2Command,\n\tSecurityCC,\n\tSecurityCCCommandEncapsulation,\n\tSecurityCCCommandEncapsulationNonceGet,\n\tSecurityCCCommandsSupportedGet,\n\tSecurityCCCommandsSupportedReport,\n\tSecurityCCNonceGet,\n\tSecurityCCNonceReport,\n\tSecurityCommand,\n\tSupervisionCC,\n\ttype SupervisionCCGet,\n\tSupervisionCCReport,\n\tTransportServiceCC,\n\tTransportServiceCCFirstSegment,\n\tTransportServiceCCSegmentComplete,\n\tTransportServiceCCSegmentRequest,\n\tTransportServiceCCSegmentWait,\n\ttype TransportServiceCCSubsequentSegment,\n\tTransportServiceTimeouts,\n\ttype UserPreferences,\n\tVersionCommand,\n\tWakeUpCCNoMoreInformation,\n\tWakeUpCCValues,\n\ttype ZWaveProtocolCC,\n\tgetImplementedVersion,\n\tisEncapsulatingCommandClass,\n\tisMultiEncapsulatingCommandClass,\n\tisTransportServiceEncapsulation,\n} from \"@zwave-js/cc\";\nimport { ConfigManager, type DeviceConfig } from \"@zwave-js/config\";\nimport {\n\ttype CCId,\n\tCommandClasses,\n\tControllerLogger,\n\tControllerRole,\n\tControllerStatus,\n\tDuration,\n\tEncapsulationFlags,\n\ttype HostIDs,\n\ttype LogConfig,\n\ttype LogContainer,\n\ttype LogNodeOptions,\n\tMAX_SUPERVISION_SESSION_ID,\n\tMAX_TRANSPORT_SERVICE_SESSION_ID,\n\tMPANState,\n\ttype MaybeNotKnown,\n\tMessagePriority,\n\ttype MessageRecord,\n\ttype MulticastDestination,\n\tNUM_NODEMASK_BYTES,\n\tNodeIDType,\n\tRFRegion,\n\tSPANState,\n\tSecurityClass,\n\tSecurityManager,\n\tSecurityManager2,\n\ttype SendCommandOptions,\n\ttype SendCommandReturnType,\n\ttype SendMessageOptions,\n\ttype SinglecastCC,\n\ttype SupervisionResult,\n\tSupervisionStatus,\n\ttype SupervisionUpdateHandler,\n\tTransactionState,\n\tTransmitOptions,\n\tTransmitStatus,\n\ttype ValueDB,\n\ttype ValueID,\n\ttype ValueMetadata,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tallCCs,\n\tdeserializeCacheValue,\n\tencapsulationCCs,\n\tgenerateECDHKeyPair,\n\tgetCCName,\n\tisEncapsulationCC,\n\tisLongRangeNodeId,\n\tisMissingControllerACK,\n\tisMissingControllerCallback,\n\tisMissingControllerResponse,\n\tisZWaveError,\n\tkeyPairFromRawECDHPrivateKey,\n\tmessageRecordToLines,\n\trandomBytes,\n\tsecurityClassIsS2,\n\tsecurityClassOrder,\n\tserializeCacheValue,\n\tstripUndefined,\n\ttimespan,\n\twasControllerReset,\n} from \"@zwave-js/core\";\nimport {\n\ttype BootloaderChunk,\n\tBootloaderChunkType,\n\ttype CLIChunk,\n\tCLIChunkType,\n\ttype EnumeratedPort,\n\tFunctionType,\n\ttype HasNodeId,\n\tMessage,\n\ttype MessageEncodingContext,\n\tMessageHeaders,\n\ttype MessageParsingContext,\n\tMessageType,\n\ttype SuccessIndicator,\n\tXModemMessageHeaders,\n\ttype ZWaveSerialBindingFactory,\n\tZWaveSerialFrameType,\n\tZWaveSerialMode,\n\ttype ZWaveSerialPortImplementation,\n\ttype ZWaveSerialStream,\n\tZWaveSerialStreamFactory,\n\tgetDefaultPriority,\n\thasNodeId,\n\tisSuccessIndicator,\n\tisZWaveSerialBindingFactory,\n\tisZWaveSerialPortImplementation,\n\twrapLegacySerialBinding,\n} from \"@zwave-js/serial\";\nimport {\n\tApplicationUpdateRequest,\n\ttype CommandRequest,\n\ttype ContainsCC,\n\tEnterBootloaderRequest,\n\tGetControllerVersionRequest,\n\tMAX_SEND_ATTEMPTS,\n\tSendDataAbort,\n\tSendDataBridgeRequest,\n\ttype SendDataMessage,\n\tSendDataMulticastBridgeRequest,\n\tSendDataMulticastRequest,\n\tSendDataRequest,\n\tSendTestFrameRequest,\n\tSendTestFrameTransmitReport,\n\tSerialAPIStartedRequest,\n\tSerialAPIWakeUpReason,\n\tSoftResetRequest,\n\tcontainsCC,\n\tcontainsSerializedCC,\n\thasTXReport,\n\tisAnySendDataResponse,\n\tisCommandRequest,\n\tisSendData,\n\tisSendDataSinglecast,\n\tisSendDataTransmitReport,\n\tisTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tAsyncQueue,\n\tBytes,\n\ttype Interval,\n\ttype Timer,\n\tTypedEventTarget,\n\tareUint8ArraysEqual,\n\tbuffer2hex,\n\tcloneDeep,\n\tcreateWrappingCounter,\n\tgetErrorMessage,\n\tgetenv,\n\tisAbortError,\n\tisUint8Array,\n\tmergeDeep,\n\tnoop,\n\tnum2hex,\n\tpick,\n\tsetInterval,\n\tsetTimer,\n} from \"@zwave-js/shared\";\nimport type {\n\tDatabase,\n\tKeyPair,\n\tReadFile,\n\tReadFileSystemInfo,\n} from \"@zwave-js/shared/bindings\";\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 { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport path from \"pathe\";\nimport { PACKAGE_NAME, PACKAGE_VERSION } from \"../_version.js\";\nimport { ZWaveController } from \"../controller/Controller.js\";\nimport {\n\ttype FoundNode,\n\tInclusionState,\n\tRemoveNodeReason,\n} from \"../controller/Inclusion.js\";\nimport { determineNIF } from \"../controller/NodeInformationFrame.js\";\nimport { DriverLogger } from \"../log/Driver.js\";\nimport type { Endpoint } from \"../node/Endpoint.js\";\nimport type { ZWaveNode } from \"../node/Node.js\";\nimport {\n\tInterviewStage,\n\tNodeStatus,\n\ttype ZWaveNodeEventCallbacks,\n\ttype ZWaveNotificationCallback,\n\tzWaveNodeEvents,\n} from \"../node/_Types.js\";\nimport type { ZWaveNodeBase } from \"../node/mixins/00_Base.js\";\nimport type { NodeWakeup } from \"../node/mixins/30_Wakeup.js\";\nimport type { NodeValues } from \"../node/mixins/40_Values.js\";\nimport type { NodeSchedulePoll } from \"../node/mixins/60_ScheduledPoll.js\";\nimport { reportMissingDeviceConfig } from \"../telemetry/deviceConfig.js\";\nimport {\n\ttype AppInfo,\n\tcompileStatistics,\n\tsendStatistics,\n} from \"../telemetry/statistics.js\";\nimport { Bootloader } from \"./Bootloader.js\";\nimport { DriverMode } from \"./DriverMode.js\";\nimport { EndDeviceCLI } from \"./EndDeviceCLI.js\";\nimport { createMessageGenerator } from \"./MessageGenerators.js\";\nimport {\n\tcacheKeys,\n\tdeserializeNetworkCacheValue,\n\tmigrateLegacyNetworkCache,\n\tserializeNetworkCacheValue,\n} from \"./NetworkCache.js\";\nimport { type SerialAPIQueueItem, TransactionQueue } from \"./Queue.js\";\nimport {\n\ttype SerialAPICommandMachineInput,\n\tcreateSerialAPICommandMachine,\n} from \"./SerialAPICommandMachine.js\";\nimport {\n\ttype TransactionReducer,\n\ttype TransactionReducerResult,\n\tcreateMessageDroppedUnexpectedError,\n\tserialAPICommandErrorToZWaveError,\n} from \"./StateMachineShared.js\";\nimport { TaskScheduler } from \"./Task.js\";\nimport { throttlePresets } from \"./ThrottlePresets.js\";\nimport { Transaction } from \"./Transaction.js\";\nimport {\n\ttype TransportServiceRXMachine,\n\ttype TransportServiceRXMachineInput,\n\tcreateTransportServiceRXMachine,\n} from \"./TransportServiceMachine.js\";\nimport { checkForConfigUpdates, installConfigUpdate } from \"./UpdateConfig.js\";\nimport { mergeUserAgent, userAgentComponentsToString } from \"./UserAgent.js\";\nimport type {\n\tEditableZWaveOptions,\n\tPartialZWaveOptions,\n\tZWaveOptions,\n} from \"./ZWaveOptions.js\";\nimport {\n\ttype OTWFirmwareUpdateProgress,\n\ttype OTWFirmwareUpdateResult,\n\tOTWFirmwareUpdateStatus,\n} from \"./_Types.js\";\nimport { discoverRemoteSerialPorts } from \"./mDNSDiscovery.js\";\n\nexport const libVersion: string = PACKAGE_VERSION;\nexport const libName: string = PACKAGE_NAME;\n\n// This is made with cfonts:\nconst libNameString = `\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\n\u255A\u2550\u2550\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\n \u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\n \u2588\u2588\u2588\u2554\u255D \u255A\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588 \u2588\u2588\u2551 \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\n\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\n`;\n\nconst defaultOptions: ZWaveOptions = {\n\ttimeouts: {\n\t\tack: 1000,\n\t\tbyte: 150,\n\t\t// Ideally we'd want to have this as low as possible, but some\n\t\t// 500 series controllers can take several seconds to respond sometimes.\n\t\tresponse: 10000,\n\t\treport: 1000, // ReportTime timeout SHOULD be set to CommandTime + 1 second\n\t\tnonce: 5000,\n\t\tsendDataAbort: 20000, // If a controller takes over 15s to reach a node, it's probably not going to happen\n\t\tsendDataCallback: 30000, // INS13954 defines this to be 65000 ms, but waiting that long causes issues with reporting devices\n\t\tsendToSleep: 250, // The default should be enough time for applications to react to devices waking up\n\t\tretryJammed: 1000,\n\t\trefreshValue: 5000, // Default should handle most slow devices until we have a better solution\n\t\trefreshValueAfterTransition: 1000, // To account for delays in the device\n\t\tserialAPIStarted: 5000,\n\t},\n\tattempts: {\n\t\topenSerialPort: 10,\n\t\tcontroller: 3,\n\t\tsendData: 3,\n\t\tsendDataJammed: 5,\n\t\tnodeInterview: 5,\n\t},\n\tdisableOptimisticValueUpdate: false,\n\tfeatures: {\n\t\t// By default enable soft reset unless the env variable is set\n\t\tsoftReset: !getenv(\"ZWAVEJS_DISABLE_SOFT_RESET\"),\n\t\t// By default enable the unresponsive controller recovery unless the env variable is set\n\t\tunresponsiveControllerRecovery: !getenv(\n\t\t\t\"ZWAVEJS_DISABLE_UNRESPONSIVE_CONTROLLER_RECOVERY\",\n\t\t),\n\t\t// By default enable the watchdog, unless the env variable is set\n\t\twatchdog: !getenv(\"ZWAVEJS_DISABLE_WATCHDOG\"),\n\t},\n\t// By default, try to recover from bootloader mode\n\tbootloaderMode: \"recover\",\n\tinterview: {\n\t\tqueryAllUserCodes: false,\n\t},\n\tstorage: {\n\t\tcacheDir: typeof process !== \"undefined\"\n\t\t\t? path.join(process.cwd(), \"cache\")\n\t\t\t: \"/cache\",\n\t\tlockDir: getenv(\"ZWAVEJS_LOCK_DIRECTORY\"),\n\t\tthrottle: \"normal\",\n\t},\n\tpreferences: {\n\t\tscales: {},\n\t},\n};\n\n/** Ensures that the options are valid */\nfunction checkOptions(options: ZWaveOptions): void {\n\tif (options.timeouts.ack < 1) {\n\t\tthrow new ZWaveError(\n\t\t\t`The ACK timeout must be positive!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.timeouts.byte < 1) {\n\t\tthrow new ZWaveError(\n\t\t\t`The BYTE timeout must be positive!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.timeouts.response < 500 || options.timeouts.response > 60000) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Response timeout must be between 500 and 60000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.timeouts.report < 500 || options.timeouts.report > 10000) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Report timeout must be between 500 and 10000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.timeouts.nonce < 3000 || options.timeouts.nonce > 20000) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Nonce timeout must be between 3000 and 20000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.timeouts.retryJammed < 10 || options.timeouts.retryJammed > 5000\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The timeout for retrying while jammed must be between 10 and 5000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.timeouts.sendToSleep < 10 || options.timeouts.sendToSleep > 5000\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Send To Sleep timeout must be between 10 and 5000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.timeouts.sendDataCallback < 10000) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Send Data Callback timeout must be at least 10000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.timeouts.sendDataAbort < 5000\n\t\t|| options.timeouts.sendDataAbort\n\t\t\t> options.timeouts.sendDataCallback - 5000\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Send Data Abort Callback timeout must be between 5000 and ${\n\t\t\t\toptions.timeouts.sendDataCallback - 5000\n\t\t\t} milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.timeouts.serialAPIStarted < 1000\n\t\t|| options.timeouts.serialAPIStarted > 30000\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Serial API started timeout must be between 1000 and 30000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.securityKeys != undefined) {\n\t\tconst keys = Object.entries(options.securityKeys);\n\t\tfor (let i = 0; i < keys.length; i++) {\n\t\t\tconst [secClass, key] = keys[i];\n\t\t\tif (key.length !== 16) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`The security key for class ${secClass} must be a buffer with length 16!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (keys.findIndex(([, k]) => areUint8ArraysEqual(k, key)) !== i) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`The security key for class ${secClass} was used multiple times!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\tif (options.attempts.controller < 1 || options.attempts.controller > 3) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Controller attempts must be between 1 and 3!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.attempts.sendData < 1\n\t\t|| options.attempts.sendData > MAX_SEND_ATTEMPTS\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The SendData attempts must be between 1 and ${MAX_SEND_ATTEMPTS}!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.attempts.sendDataJammed < 1\n\t\t|| options.attempts.sendDataJammed > 10\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The SendData attempts while jammed must be between 1 and 10!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.attempts.nodeInterview < 1\n\t\t|| options.attempts.nodeInterview > 10\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Node interview attempts must be between 1 and 10!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\n\tif (options.inclusionUserCallbacks) {\n\t\tif (!isObject(options.inclusionUserCallbacks)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The inclusionUserCallbacks must be an object!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t} else if (\n\t\t\ttypeof options.inclusionUserCallbacks.grantSecurityClasses\n\t\t\t\t!== \"function\"\n\t\t\t|| typeof options.inclusionUserCallbacks.validateDSKAndEnterPIN\n\t\t\t\t!== \"function\"\n\t\t\t|| typeof options.inclusionUserCallbacks.abort !== \"function\"\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The inclusionUserCallbacks must contain the following functions: grantSecurityClasses, validateDSKAndEnterPIN, abort!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (options.joinNetworkUserCallbacks) {\n\t\tif (!isObject(options.joinNetworkUserCallbacks)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The joinNetworkUserCallbacks must be an object!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t} else if (\n\t\t\ttypeof options.joinNetworkUserCallbacks.showDSK\n\t\t\t\t!== \"function\"\n\t\t\t|| typeof options.joinNetworkUserCallbacks.done\n\t\t\t\t!== \"function\"\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The joinNetworkUserCallbacks must contain the following functions: showDSK, done!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (options.rf != undefined) {\n\t\tif (options.rf.region != undefined) {\n\t\t\tif (\n\t\t\t\ttypeof options.rf.region !== \"number\"\n\t\t\t\t|| !(options.rf.region in RFRegion)\n\t\t\t\t|| options.rf.region === RFRegion.Unknown\n\t\t\t) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`${options.rf.region} is not a valid RF region!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (options.rf.txPower != undefined) {\n\t\t\tif (!isObject(options.rf.txPower)) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`rf.txPower must be an object!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t} else if (\n\t\t\t\ttypeof options.rf.txPower.powerlevel !== \"number\"\n\t\t\t\t|| typeof options.rf.txPower.measured0dBm !== \"number\"\n\t\t\t) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`rf.txPower must contain the following numeric properties: powerlevel, measured0dBm!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Function signature for a message handler. The return type signals if the\n * message was handled (`true`) or further handlers should be called (`false`)\n */\nexport type RequestHandler<T extends Message = Message> = (\n\tmsg: T,\n) => boolean | Promise<boolean>;\ninterface RequestHandlerEntry<T extends Message = Message> {\n\tinvoke: RequestHandler<T>;\n\toneTime: boolean;\n}\n\ninterface AwaitedThing<T> {\n\thandler: (thing: T) => void;\n\ttimeout?: Timer;\n\tpredicate: (msg: T) => boolean;\n\trefreshPredicate?: (msg: T) => boolean;\n}\n\ntype AwaitedMessageHeader = AwaitedThing<MessageHeaders>;\ntype AwaitedMessageEntry = AwaitedThing<Message>;\ntype AwaitedCommandEntry = AwaitedThing<CCId>;\ntype AwaitedCLIChunkEntry = AwaitedThing<CLIChunk>;\nexport type AwaitedBootloaderChunkEntry = AwaitedThing<BootloaderChunk>;\n\ninterface TransportServiceSession {\n\tfragmentSize: number;\n\tmachine: TransportServiceRXMachine;\n\ttimeout?: NodeJS.Timeout;\n}\n\ninterface Sessions {\n\t/** A map of all current Transport Service sessions that may still receive updates */\n\ttransportService: Map<number, TransportServiceSession>;\n\t/** A map of all current supervision sessions that may still receive updates */\n\tsupervision: Map<number, SupervisionUpdateHandler>;\n}\n\n// Used to add all node events to the driver event callbacks, but prefixed with \"node \"\ntype PrefixedNodeEvents = {\n\t[\n\t\tK in keyof ZWaveNodeEventCallbacks as K extends string ? `node ${K}`\n\t\t\t: never\n\t]: ZWaveNodeEventCallbacks[K];\n};\n\nconst enum ControllerRecoveryPhase {\n\tNone,\n\tACKTimeout,\n\tACKTimeoutAfterReset,\n\tCallbackTimeout,\n\tCallbackTimeoutAfterReset,\n\tJammed,\n\tJammedAfterReset,\n}\n\nfunction messageIsPing<T extends Message>(\n\tmsg: T,\n): msg is T & ContainsCC<NoOperationCC> {\n\treturn containsCC(msg) && msg.command instanceof NoOperationCC;\n}\n\nfunction assertValidCCs(container: ContainsCC): void {\n\tif (container.command instanceof InvalidCC) {\n\t\tif (typeof container.command.reason === \"number\") {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The message payload failed validation!\",\n\t\t\t\tcontainer.command.reason,\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The message payload is invalid!\",\n\t\t\t\tZWaveErrorCodes.PacketFormat_InvalidPayload,\n\t\t\t\tcontainer.command.reason,\n\t\t\t);\n\t\t}\n\t} else if (containsCC(container.command)) {\n\t\tassertValidCCs(container.command);\n\t}\n}\n\nfunction wrapLegacyFSDriverForCacheMigrationOnly(\n\tlegacy: import(\"@zwave-js/core/traits\").FileSystem,\n): ReadFileSystemInfo & ReadFile {\n\t// This usage only needs readFile and checking if a file exists\n\t// Every other usage will throw!\n\treturn {\n\t\tasync readFile(path) {\n\t\t\tconst text = await legacy.readFile(path, \"utf8\");\n\t\t\treturn Bytes.from(text, \"utf8\");\n\t\t},\n\t\tasync stat(path) {\n\t\t\tif (await legacy.pathExists(path)) {\n\t\t\t\treturn {\n\t\t\t\t\tisDirectory() {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t},\n\t\t\t\t\tisFile() {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t},\n\t\t\t\t\tmtime: new Date(),\n\t\t\t\t\tsize: 0,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tthrow new Error(\"File not found\");\n\t\t\t}\n\t\t},\n\t\treadDir(_path) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew Error(\"Not implemented for the legacy FS driver\"),\n\t\t\t);\n\t\t},\n\t};\n}\n\n// Strongly type the event emitter events\nexport interface DriverEventCallbacks extends PrefixedNodeEvents {\n\t\"driver ready\": () => void;\n\t\"bootloader ready\": () => void;\n\t\"cli ready\": () => void;\n\t\"all nodes ready\": () => void;\n\t\"firmware update progress\": (\n\t\tprogress: OTWFirmwareUpdateProgress,\n\t) => void;\n\t\"firmware update finished\": (\n\t\tresult: OTWFirmwareUpdateResult,\n\t) => void;\n\terror: (err: Error) => void;\n}\n\nexport type DriverEvents = Extract<keyof DriverEventCallbacks, string>;\n\n/**\n * The driver is the core of this library. It controls the serial interface,\n * handles transmission and receipt of messages and manages the network cache.\n * Any action you want to perform on the Z-Wave network must go through a driver\n * instance or its associated nodes.\n */\nexport class Driver extends TypedEventTarget<DriverEventCallbacks>\n\timplements\n\t\tCCAPIHost,\n\t\tInterviewContext,\n\t\tRefreshValuesContext,\n\t\tPersistValuesContext\n{\n\tpublic constructor(\n\t\tprivate port:\n\t\t\t| string\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t| ZWaveSerialPortImplementation\n\t\t\t| ZWaveSerialBindingFactory,\n\t\t...optionsAndPresets: (PartialZWaveOptions | undefined)[]\n\t) {\n\t\tsuper();\n\n\t\t// Ensure the given serial port is valid\n\t\tif (\n\t\t\ttypeof port !== \"string\"\n\t\t\t&& !isZWaveSerialPortImplementation(port)\n\t\t\t&& !isZWaveSerialBindingFactory(port)\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The port must be a string or a valid custom serial port implementation!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t}\n\n\t\t// Deep-Merge all given options/presets\n\t\tconst definedOptionsAndPresets = optionsAndPresets.filter(\n\t\t\t(o): o is PartialZWaveOptions => !!o,\n\t\t);\n\t\tlet mergedOptions: PartialZWaveOptions = {};\n\t\tfor (const preset of definedOptionsAndPresets) {\n\t\t\tmergedOptions = mergeDeep(mergedOptions, preset, true);\n\t\t}\n\t\t// Finally apply the defaults, without overwriting any existing settings\n\t\tthis._options = mergeDeep(\n\t\t\tmergedOptions,\n\t\t\tcloneDeep(defaultOptions),\n\t\t) as ZWaveOptions;\n\n\t\t// And make sure they contain valid values\n\t\tcheckOptions(this._options);\n\t\tif (this._options.userAgent) {\n\t\t\tif (!isObject(this._options.userAgent)) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`The userAgent property must be an object!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.updateUserAgent(this._options.userAgent);\n\t\t}\n\n\t\t// Initialize the cache\n\t\tthis.cacheDir = this._options.storage.cacheDir;\n\n\t\tconst self = this;\n\t\tthis.messageEncodingContext = {\n\t\t\tgetHighestSecurityClass: (nodeId) =>\n\t\t\t\tthis.getHighestSecurityClass(nodeId),\n\t\t\thasSecurityClass: (nodeId, securityClass) =>\n\t\t\t\tthis.hasSecurityClass(nodeId, securityClass),\n\t\t\tsetSecurityClass: (nodeId, securityClass, granted) =>\n\t\t\t\tthis.setSecurityClass(nodeId, securityClass, granted),\n\t\t\tgetDeviceConfig: (nodeId) => this.getDeviceConfig(nodeId),\n\t\t\t// These are evaluated lazily, so we cannot spread messageParsingContext unfortunately\n\t\t\tget securityManager() {\n\t\t\t\treturn self.securityManager;\n\t\t\t},\n\t\t\tget securityManager2() {\n\t\t\t\treturn self.securityManager2;\n\t\t\t},\n\t\t\tget securityManagerLR() {\n\t\t\t\treturn self.securityManagerLR;\n\t\t\t},\n\t\t\tgetSupportedCCVersion: (cc, nodeId, endpointIndex) =>\n\t\t\t\tthis.getSupportedCCVersion(cc, nodeId, endpointIndex),\n\t\t};\n\n\t\tthis._scheduler = new TaskScheduler();\n\t}\n\n\tprivate serialFactory: ZWaveSerialStreamFactory | undefined;\n\t/** The serial port instance */\n\tprivate serial: ZWaveSerialStream | undefined;\n\n\tprivate messageEncodingContext: Omit<\n\t\tMessageEncodingContext,\n\t\tkeyof HostIDs | \"nodeIdType\"\n\t>;\n\n\tprivate getEncodingContext(): MessageEncodingContext & CCEncodingContext {\n\t\treturn {\n\t\t\t...this.messageEncodingContext,\n\t\t\townNodeId: this.controller.ownNodeId!,\n\t\t\thomeId: this.controller.homeId!,\n\t\t\tnodeIdType: this._controller?.nodeIdType ?? NodeIDType.Short,\n\t\t};\n\t}\n\n\tprivate getMessageParsingContext(): MessageParsingContext {\n\t\treturn {\n\t\t\tgetDeviceConfig: (nodeId) => this.getDeviceConfig(nodeId),\n\t\t\tsdkVersion: this._controller?.sdkVersion,\n\t\t\trequestStorage: this._requestStorage,\n\t\t\townNodeId: this._controller?.ownNodeId ?? 0, // Unspecified node ID\n\t\t\thomeId: this._controller?.homeId ?? 0x55555555, // Invalid home ID\n\t\t\tnodeIdType: this._controller?.nodeIdType ?? NodeIDType.Short,\n\t\t};\n\t}\n\n\tprivate getCCParsingContext(): Omit<\n\t\tCCParsingContext,\n\t\t\"sourceNodeId\" | \"frameType\"\n\t> {\n\t\treturn {\n\t\t\t...this.messageEncodingContext,\n\t\t\townNodeId: this.controller.ownNodeId!,\n\t\t\thomeId: this.controller.homeId!,\n\t\t};\n\t}\n\n\t// We have multiple queues to achieve multiple \"layers\" of communication priority:\n\t// The default queue for most messages\n\tprivate queue!: TransactionQueue; // Is initialized in initTransactionQueues()\n\t// An immediate queue for handling queries that need to be handled ASAP, e.g. Nonce Get\n\tprivate immediateQueue!: TransactionQueue; // Is initialized in initTransactionQueues()\n\t// And all of them feed into the serial API queue, which contains commands that will be sent ASAP\n\tprivate serialAPIQueue!: AsyncQueue<SerialAPIQueueItem>; // Is initialized in initControllerAndNodes()\n\n\t/** Gives access to the transaction queues, ordered by priority */\n\tprivate get queues(): TransactionQueue[] {\n\t\treturn [this.immediateQueue, this.queue];\n\t}\n\n\tprivate initTransactionQueues(): void {\n\t\tthis.immediateQueue = new TransactionQueue({\n\t\t\tname: \"immediate\",\n\t\t\tmayStartNextTransaction: (t) => {\n\t\t\t\t// While the controller is unresponsive, only soft resetting is allowed.\n\t\t\t\t// Since we use GetControllerVersionRequest to check if the controller responds after soft-reset,\n\t\t\t\t// allow that too.\n\t\t\t\tif (this.controller.status === ControllerStatus.Unresponsive) {\n\t\t\t\t\treturn t.message instanceof SoftResetRequest\n\t\t\t\t\t\t|| t.message instanceof GetControllerVersionRequest;\n\t\t\t\t}\n\n\t\t\t\t// While the controller is jammed, only soft resetting is allowed\n\t\t\t\tif (this.controller.status === ControllerStatus.Jammed) {\n\t\t\t\t\treturn t.message instanceof SoftResetRequest;\n\t\t\t\t}\n\n\t\t\t\t// All other messages on the immediate queue may always be sent as long as the controller is ready to send\n\t\t\t\treturn !this.queuePaused\n\t\t\t\t\t&& this.controller.status === ControllerStatus.Ready;\n\t\t\t},\n\t\t});\n\t\tthis.queue = new TransactionQueue({\n\t\t\tname: \"normal\",\n\t\t\tmayStartNextTransaction: (t) => this.mayStartTransaction(t),\n\t\t});\n\n\t\tthis._queueIdle = false;\n\n\t\t// Start draining the queues\n\t\tfor (const queue of this.queues) {\n\t\t\tvoid this.drainTransactionQueue(queue);\n\t\t}\n\t}\n\n\tprivate async destroyTransactionQueues(\n\t\treason: string,\n\t\terrorCode?: ZWaveErrorCodes,\n\t): Promise<void> {\n\t\t// The queues might not have been initialized yet\n\t\tfor (const queue of this.queues) {\n\t\t\tif (!queue) return;\n\t\t}\n\n\t\t// Reject pending transactions, but not during integration tests\n\t\tif (getenv(\"NODE_ENV\") !== \"test\") {\n\t\t\tawait this.rejectTransactions(\n\t\t\t\t(_t) => true,\n\t\t\t\treason,\n\t\t\t\terrorCode ?? ZWaveErrorCodes.Driver_TaskRemoved,\n\t\t\t);\n\t\t}\n\n\t\tfor (const queue of this.queues) {\n\t\t\tqueue.abort();\n\t\t}\n\t}\n\n\tprivate _scheduler: TaskScheduler;\n\tpublic get scheduler(): TaskScheduler {\n\t\treturn this._scheduler;\n\t}\n\n\tprivate queuePaused = false;\n\t/** Used to immediately abort ongoing Serial API commands */\n\tprivate abortSerialAPICommand: DeferredPromise<Error> | undefined;\n\n\tprivate initSerialAPIQueue(): void {\n\t\tthis.serialAPIQueue = new AsyncQueue();\n\n\t\t// Start draining the queue\n\t\tvoid this.drainSerialAPIQueue();\n\t}\n\n\tprivate destroySerialAPIQueue(\n\t\treason: string,\n\t\terrorCode?: ZWaveErrorCodes,\n\t): void {\n\t\t// The queue might not have been initialized yet\n\t\tif (!this.serialAPIQueue) return;\n\n\t\tthis.serialAPIQueue.abort();\n\n\t\t// Abort the currently executed serial API command, so the queue does not lock up\n\t\tthis.abortSerialAPICommand?.reject(\n\t\t\tnew ZWaveError(\n\t\t\t\treason,\n\t\t\t\terrorCode ?? ZWaveErrorCodes.Driver_Destroyed,\n\t\t\t),\n\t\t);\n\t}\n\n\t// Keep track of which queues are currently busy\n\tprivate _queuesBusyFlags = 0;\n\tprivate _queueIdle: boolean = false;\n\t/** Whether the queue is currently idle */\n\tpublic get queueIdle(): boolean {\n\t\treturn this._queueIdle;\n\t}\n\tprivate set queueIdle(value: boolean) {\n\t\tif (this._queueIdle !== value) {\n\t\t\tthis.driverLog.print(\n\t\t\t\tvalue ? \"all queues idle\" : \"one or more queues busy\",\n\t\t\t);\n\t\t\tthis._queueIdle = value;\n\t\t\tthis.handleQueueIdleChange(value);\n\t\t}\n\t}\n\n\t/** A map of handlers for all sorts of requests */\n\tprivate requestHandlers = new Map<FunctionType, RequestHandlerEntry[]>();\n\t/** A list of awaited message headers */\n\tprivate awaitedMessageHeaders: AwaitedMessageHeader[] = [];\n\t/** A list of awaited messages */\n\tprivate awaitedMessages: AwaitedMessageEntry[] = [];\n\t/** A list of awaited commands */\n\tprivate awaitedCommands: AwaitedCommandEntry[] = [];\n\t/** A list of awaited chunks from the bootloader */\n\tprivate awaitedBootloaderChunks: AwaitedBootloaderChunkEntry[] = [];\n\t/** A list of awaited chunks from the end device CLI */\n\tprivate awaitedCLIChunks: AwaitedCLIChunkEntry[] = [];\n\n\t/** A map of Node ID -> ongoing sessions */\n\tprivate nodeSessions = new Map<number, Sessions>();\n\tprivate ensureNodeSessions(nodeId: number): Sessions {\n\t\tif (!this.nodeSessions.has(nodeId)) {\n\t\t\tthis.nodeSessions.set(nodeId, {\n\t\t\t\ttransportService: new Map(),\n\t\t\t\tsupervision: new Map(),\n\t\t\t});\n\t\t}\n\t\treturn this.nodeSessions.get(nodeId)!;\n\t}\n\n\tprivate _requestStorage: Map<FunctionType, Record<string, unknown>> =\n\t\tnew Map();\n\t/**\n\t * @internal\n\t * Stores data from Serial API command requests to be used by their responses\n\t */\n\tpublic get requestStorage(): Map<FunctionType, Record<string, unknown>> {\n\t\treturn this._requestStorage;\n\t}\n\n\tpublic readonly cacheDir: string;\n\n\tprivate _valueDB: Database<unknown> | undefined;\n\t/** @internal */\n\tpublic get valueDB(): Database<unknown> | undefined {\n\t\treturn this._valueDB;\n\t}\n\tprivate _metadataDB: Database<ValueMetadata> | undefined;\n\t/** @internal */\n\tpublic get metadataDB(): Database<ValueMetadata> | undefined {\n\t\treturn this._metadataDB;\n\t}\n\tprivate _networkCache: Database<any> | undefined;\n\t/** @internal */\n\tpublic get networkCache(): Database<any> {\n\t\tif (this._networkCache == undefined) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The network cache was not yet initialized!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\treturn this._networkCache;\n\t}\n\n\t// This is set during `start()` and should not be accessed before\n\tprivate _configManager!: ConfigManager;\n\tpublic get configManager(): ConfigManager {\n\t\treturn this._configManager;\n\t}\n\n\tpublic get configVersion(): string {\n\t\treturn (\n\t\t\tthis.configManager?.configVersion\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\t\t\t?? require(\"zwave-js/package.json\")?.dependencies\n\t\t\t\t\t?.[\"@zwave-js/config\"]\n\t\t\t\t?? libVersion\n\t\t);\n\t}\n\n\t// This is set during `start()` and should not be accessed before\n\tprivate _logContainer!: LogContainer;\n\t// This is set during `start()` and should not be accessed before\n\tprivate _driverLog!: DriverLogger;\n\t/** @internal */\n\tpublic get driverLog(): DriverLogger {\n\t\treturn this._driverLog;\n\t}\n\n\t// This is set during `start()` and should not be accessed before\n\tprivate _controllerLog!: ControllerLogger;\n\t/** @internal */\n\tpublic get controllerLog(): ControllerLogger {\n\t\treturn this._controllerLog;\n\t}\n\n\tpublic logNode(\n\t\tnodeId: number,\n\t\tmessage: string,\n\t\tlevel?: LogNodeOptions[\"level\"],\n\t): void;\n\tpublic logNode(nodeId: number, options: LogNodeOptions): void;\n\tpublic logNode(...args: any[]): void {\n\t\t// @ts-expect-error\n\t\tthis._controllerLog.logNode(...args);\n\t}\n\n\tprivate _controller: ZWaveController | undefined;\n\t/** Encapsulates information about the Z-Wave controller and provides access to its nodes */\n\tpublic get controller(): ZWaveController {\n\t\tif (this._controller == undefined) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The controller is not yet ready!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\treturn this._controller;\n\t}\n\n\t/** While in bootloader mode, this encapsulates information about the bootloader and its state */\n\tprivate _bootloader: Bootloader | undefined;\n\tpublic get bootloader(): Bootloader {\n\t\tif (this._bootloader == undefined) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The controller is not in bootloader mode!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\treturn this._bootloader;\n\t}\n\n\tprivate _cli: EndDeviceCLI | undefined;\n\t/** While in end device CLI mode, this encapsulates information about the CLI and its state */\n\tpublic get cli(): EndDeviceCLI {\n\t\tif (this._cli == undefined) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The Z-Wave module is not in CLI mode!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\treturn this._cli;\n\t}\n\n\t/** Determines which kind of Z-Wave application the driver is currently communicating with */\n\tpublic get mode(): DriverMode {\n\t\tif (this._bootloader) return DriverMode.Bootloader;\n\t\tif (this._cli) return DriverMode.CLI;\n\t\tif (this._controller) return DriverMode.SerialAPI;\n\t\treturn DriverMode.Unknown;\n\t}\n\n\tprivate _recoveryPhase: ControllerRecoveryPhase =\n\t\tControllerRecoveryPhase.None;\n\n\tprivate _securityManager: SecurityManager | undefined;\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic get securityManager(): SecurityManager | undefined {\n\t\treturn this._securityManager;\n\t}\n\n\tprivate _securityManager2: SecurityManager2 | undefined;\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic get securityManager2(): SecurityManager2 | undefined {\n\t\treturn this._securityManager2;\n\t}\n\n\tprivate _securityManagerLR: SecurityManager2 | undefined;\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic get securityManagerLR(): SecurityManager2 | undefined {\n\t\treturn this._securityManagerLR;\n\t}\n\n\t/** @internal */\n\tpublic getSecurityManager2(\n\t\tdestination: number | MulticastDestination,\n\t): SecurityManager2 | undefined {\n\t\tconst nodeId = isArray(destination) ? destination[0] : destination;\n\t\tconst isLongRange = isLongRangeNodeId(nodeId);\n\t\treturn isLongRange ? this.securityManagerLR : this.securityManager2;\n\t}\n\n\tprivate _learnModeAuthenticatedKeyPair: KeyPair | undefined;\n\t/** @internal */\n\tpublic async getLearnModeAuthenticatedKeyPair(): Promise<KeyPair> {\n\t\tif (this._learnModeAuthenticatedKeyPair == undefined) {\n\t\t\t// Try restoring from cache\n\t\t\tconst privateKey = this.cacheGet<Uint8Array>(\n\t\t\t\tcacheKeys.controller.privateKey,\n\t\t\t);\n\t\t\tif (privateKey) {\n\t\t\t\tthis._learnModeAuthenticatedKeyPair =\n\t\t\t\t\tawait keyPairFromRawECDHPrivateKey(privateKey);\n\t\t\t} else {\n\t\t\t\t// Not found in cache, create a new one and cache it\n\t\t\t\tthis._learnModeAuthenticatedKeyPair =\n\t\t\t\t\tawait generateECDHKeyPair();\n\t\t\t\tthis.cacheSet(\n\t\t\t\t\tcacheKeys.controller.privateKey,\n\t\t\t\t\tthis._learnModeAuthenticatedKeyPair.privateKey,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn this._learnModeAuthenticatedKeyPair;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications. Use `controller.homeId` instead!\n\t */\n\tpublic get homeId(): number {\n\t\t// This is needed for the ZWaveHost interface\n\t\treturn this.controller.homeId!;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications. Use `controller.ownNodeId` instead!\n\t */\n\tpublic get ownNodeId(): number {\n\t\t// This is needed for the ZWaveHost interface\n\t\treturn this.controller.ownNodeId!;\n\t}\n\n\t/** @internal Used for compatibility with the CCAPIHost interface */\n\tpublic getNode(nodeId: number): ZWaveNode | undefined {\n\t\treturn this.controller.nodes.get(nodeId);\n\t}\n\n\t/** @internal Used for compatibility with the CCAPIHost interface */\n\tpublic getNodeOrThrow(nodeId: number): ZWaveNode {\n\t\treturn this.controller.nodes.getOrThrow(nodeId);\n\t}\n\n\t/** @internal Used for compatibility with the CCAPIHost interface */\n\tpublic getAllNodes(): ZWaveNode[] {\n\t\treturn [...this.controller.nodes.values()];\n\t}\n\n\tpublic tryGetNode(msg: Message): ZWaveNode | undefined {\n\t\tconst nodeId = msg.getNodeId();\n\t\tif (nodeId != undefined) return this.controller.nodes.get(nodeId);\n\t}\n\n\tpublic tryGetEndpoint(cc: CommandClass): Endpoint | undefined {\n\t\tif (cc.isSinglecast()) {\n\t\t\treturn this.controller.nodes\n\t\t\t\t.get(cc.nodeId)\n\t\t\t\t?.getEndpoint(cc.endpointIndex);\n\t\t}\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic getValueDB(nodeId: number): ValueDB {\n\t\t// This is needed for the ZWaveHost interface\n\t\tconst node = this.controller.nodes.getOrThrow(nodeId);\n\t\treturn node.valueDB;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic tryGetValueDB(nodeId: number): ValueDB | undefined {\n\t\t// This is needed for the ZWaveHost interface\n\t\tconst node = this.controller.nodes.get(nodeId);\n\t\treturn node?.valueDB;\n\t}\n\n\tpublic getDeviceConfig(nodeId: number): DeviceConfig | undefined {\n\t\t// This is needed for the ZWaveHost interface\n\t\treturn this.controller.nodes.get(nodeId)?.deviceConfig;\n\t}\n\n\tpublic lookupManufacturer(manufacturerId: number): string | undefined {\n\t\treturn this.configManager.lookupManufacturer(manufacturerId);\n\t}\n\n\tpublic getHighestSecurityClass(\n\t\tnodeId: number,\n\t): MaybeNotKnown<SecurityClass> {\n\t\t// This is needed for the ZWaveHost interface\n\t\tconst node = this.controller.nodes.getOrThrow(nodeId);\n\t\treturn node.getHighestSecurityClass();\n\t}\n\n\tpublic hasSecurityClass(\n\t\tnodeId: number,\n\t\tsecurityClass: SecurityClass,\n\t): MaybeNotKnown<boolean> {\n\t\t// This is needed for the ZWaveHost interface\n\t\tconst node = this.controller.nodes.getOrThrow(nodeId);\n\t\treturn node.hasSecurityClass(securityClass);\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic setSecurityClass(\n\t\tnodeId: number,\n\t\tsecurityClass: SecurityClass,\n\t\tgranted: boolean,\n\t): void {\n\t\t// This is needed for the ZWaveHost interface\n\t\tconst node = this.controller.nodes.getOrThrow(nodeId);\n\t\tnode.setSecurityClass(securityClass, granted);\n\t}\n\n\t/** Updates the logging configuration without having to restart the driver. */\n\tpublic updateLogConfig(config: Partial<LogConfig>): void {\n\t\tthis._logContainer.updateConfiguration(config);\n\t}\n\n\t/** Returns the current logging configuration. */\n\tpublic getLogConfig(): LogConfig {\n\t\treturn this._logContainer.getConfiguration();\n\t}\n\n\t/** Updates the preferred sensor scales to use for node queries */\n\tpublic setPreferredScales(\n\t\tscales: ZWaveOptions[\"preferences\"][\"scales\"],\n\t): void {\n\t\tthis._options.preferences.scales = mergeDeep(\n\t\t\tdefaultOptions.preferences.scales,\n\t\t\tscales,\n\t\t);\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic getUserPreferences(): UserPreferences {\n\t\treturn this._options.preferences;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic getInterviewOptions(): InterviewOptions {\n\t\treturn this._options.interview;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic getRefreshValueTimeouts(): RefreshValueTimeouts {\n\t\treturn {\n\t\t\trefreshValue: this._options.timeouts.refreshValue,\n\t\t\trefreshValueAfterTransition:\n\t\t\t\tthis._options.timeouts.refreshValueAfterTransition,\n\t\t};\n\t}\n\n\t/**\n\t * Enumerates all existing serial ports.\n\t * @param local Whether to include local serial ports\n\t * @param remote Whether to discover remote serial ports using an mDNS query for the `_zwave._tcp` domain\n\t */\n\tpublic static async enumerateSerialPorts({\n\t\tlocal = true,\n\t\tremote = true,\n\t}: {\n\t\tlocal?: boolean;\n\t\tremote?: boolean;\n\t} = {}): Promise<string[]> {\n\t\tconst ret: (EnumeratedPort & { path: string })[] = [];\n\n\t\t// Ideally we'd use the host bindings used by the driver, but we can't access them in a static method\n\n\t\tconst bindings =\n\t\t\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t\t\t// @ts-ignore - For some reason, VSCode does not like this import, although tsc is fine with it\n\t\t\t(await import(\"#default_bindings/serial\")).serial;\n\t\tif (local && typeof bindings.list === \"function\") {\n\t\t\tfor (const port of await bindings.list()) {\n\t\t\t\tif (port.type === \"custom\") continue;\n\t\t\t\tret.push(port);\n\t\t\t}\n\t\t}\n\t\tif (remote) {\n\t\t\tconst ports = await discoverRemoteSerialPorts();\n\t\t\tif (ports) {\n\t\t\t\tret.push(...ports.map((p) => ({\n\t\t\t\t\ttype: \"socket\" as const,\n\t\t\t\t\tpath: p.port,\n\t\t\t\t})));\n\t\t\t}\n\t\t}\n\n\t\tconst portOrder: EnumeratedPort[\"type\"][] = [\"link\", \"socket\", \"tty\"];\n\n\t\tret.sort((a, b) => {\n\t\t\tconst typeA = portOrder.indexOf(a.type);\n\t\t\tconst typeB = portOrder.indexOf(b.type);\n\t\t\tif (typeA !== typeB) return typeA - typeB;\n\t\t\treturn a.path.localeCompare(b.path);\n\t\t});\n\n\t\treturn distinct(ret.map((p) => p.path));\n\t}\n\n\t/** Updates a subset of the driver options on the fly */\n\tpublic updateOptions(options: EditableZWaveOptions): void {\n\t\t// This code is called from user code, so we need to make sure no options were passed\n\t\t// which we are not able to update on the fly\n\t\tconst safeOptions = pick(options, [\n\t\t\t\"disableOptimisticValueUpdate\",\n\t\t\t\"emitValueUpdateAfterSetValue\",\n\t\t\t\"inclusionUserCallbacks\",\n\t\t\t\"joinNetworkUserCallbacks\",\n\t\t\t\"interview\",\n\t\t\t\"preferences\",\n\t\t\t\"vendor\",\n\t\t]);\n\n\t\t// Create a new deep-merged copy of the options so we can check them for validity\n\t\t// without affecting our own options.\n\t\t// The following options are potentially unsafe to clone, so just preserve them:\n\t\t// - logConfig\n\t\t// - host (could contain classes)\n\t\tconst { logConfig, host, ...rest } = this._options;\n\t\tconst newOptions = mergeDeep(\n\t\t\tcloneDeep(rest),\n\t\t\tsafeOptions,\n\t\t\ttrue,\n\t\t) as ZWaveOptions;\n\t\tnewOptions.logConfig = logConfig;\n\t\tnewOptions.host = host;\n\t\tcheckOptions(newOptions);\n\n\t\tif (options.userAgent && !isObject(options.userAgent)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The userAgent property must be an object!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t}\n\n\t\t// All good, update the options\n\t\tthis._options = newOptions;\n\n\t\tif (options.logConfig) {\n\t\t\tthis.updateLogConfig(options.logConfig);\n\t\t}\n\n\t\tif (options.userAgent) {\n\t\t\tthis.updateUserAgent(options.userAgent);\n\t\t}\n\t}\n\n\tprivate _options: ZWaveOptions;\n\tpublic get options(): Readonly<ZWaveOptions> {\n\t\treturn this._options;\n\t}\n\n\t/**\n\t * The host bindings used to access file system etc.\n\t */\n\t// This is set during `start()` and should not be accessed before\n\tprivate bindings!: Required<NonNullable<ZWaveOptions[\"host\"]>>;\n\n\tprivate _wasStarted: boolean = false;\n\tprivate _isOpen: boolean = false;\n\n\t/** Start the driver */\n\tpublic async start(): Promise<void> {\n\t\t// avoid starting twice\n\t\tif (this.wasDestroyed) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The driver was destroyed. Create a new instance and start that one.\",\n\t\t\t\tZWaveErrorCodes.Driver_Destroyed,\n\t\t\t);\n\t\t}\n\t\tif (this._wasStarted) return Promise.resolve();\n\t\tthis._wasStarted = true;\n\n\t\t// Populate default bindings. This has to happen asynchronously, so the driver does not have a hard dependency\n\t\t// on Node.js internals\n\t\tthis.bindings = {\n\t\t\tfs: this._options.host?.fs\n\t\t\t\t?? (await import(\"#default_bindings/fs\")).fs,\n\t\t\tserial: this._options.host?.serial\n\t\t\t\t?? (await import(\"#default_bindings/serial\")).serial,\n\t\t\tdb: this._options.host?.db\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t\t\t\t// @ts-ignore - For some reason, VSCode does not like this import, although tsc is fine with it\n\t\t\t\t?? (await import(\"#default_bindings/db\")).db,\n\t\t\tlog: this._options.host?.log\n\t\t\t\t?? (await import(\"#default_bindings/log\")).log,\n\t\t};\n\n\t\t// Initialize logging\n\t\tthis._logContainer = this.bindings.log(this._options.logConfig);\n\t\tthis._driverLog = new DriverLogger(this, this._logContainer);\n\t\tthis._controllerLog = new ControllerLogger(this._logContainer);\n\n\t\t// Initialize config manager\n\t\tthis._configManager = new ConfigManager({\n\t\t\tbindings: this.bindings.fs,\n\t\t\tlogContainer: this._logContainer,\n\t\t\tdeviceConfigPriorityDir:\n\t\t\t\tthis._options.storage.deviceConfigPriorityDir,\n\t\t\tdeviceConfigExternalDir:\n\t\t\t\tthis._options.storage.deviceConfigExternalDir,\n\t\t});\n\n\t\tconst spOpenPromise = createDeferredPromise();\n\n\t\t// Log which version is running\n\t\tthis.driverLog.print(libNameString, \"info\");\n\t\tthis.driverLog.print(`version ${libVersion}`, \"info\");\n\t\tthis.driverLog.print(\"\", \"info\");\n\n\t\tthis.driverLog.print(\"starting driver...\");\n\n\t\t// Open the serial port\n\t\tlet binding: ZWaveSerialBindingFactory;\n\t\tif (typeof this.port === \"string\") {\n\t\t\tif (\n\t\t\t\ttypeof this.bindings.serial.createFactoryByPath === \"function\"\n\t\t\t) {\n\t\t\t\tthis.driverLog.print(`opening serial port ${this.port}`);\n\t\t\t\tbinding = await this.bindings.serial.createFactoryByPath(\n\t\t\t\t\tthis.port,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tspOpenPromise.reject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\"This platform does not support creating a serial connection by path\",\n\t\t\t\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tvoid this.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (isZWaveSerialPortImplementation(this.port)) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"opening serial port using the provided custom implementation\",\n\t\t\t);\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"This is deprecated! Switch to the factory pattern instead.\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tbinding = wrapLegacySerialBinding(this.port);\n\t\t} else {\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"opening serial port using the provided custom factory\",\n\t\t\t);\n\t\t\tbinding = this.port;\n\t\t}\n\t\tthis.serialFactory = new ZWaveSerialStreamFactory(\n\t\t\tbinding,\n\t\t\tthis._logContainer,\n\t\t);\n\n\t\t// IMPORTANT: Test code expects the open promise to be created and returned synchronously\n\t\t// Everything async (including opening the serial port) must happen in the setImmediate callback\n\n\t\t// asynchronously open the serial port\n\t\tsetImmediate(async () => {\n\t\t\ttry {\n\t\t\t\tawait this.openSerialport();\n\t\t\t} catch (e) {\n\t\t\t\tspOpenPromise.reject(e as Error);\n\t\t\t\tvoid this.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.driverLog.print(\"serial port opened\");\n\t\t\tthis._isOpen = true;\n\t\t\tspOpenPromise.resolve();\n\n\t\t\t// Start the task scheduler\n\t\t\tthis._scheduler.start();\n\n\t\t\tif (\n\t\t\t\ttypeof this._options.testingHooks?.onSerialPortOpen\n\t\t\t\t\t=== \"function\"\n\t\t\t) {\n\t\t\t\tawait this._options.testingHooks.onSerialPortOpen(this.serial!);\n\t\t\t}\n\n\t\t\t// Perform initialization sequence\n\t\t\tif (this._options.testingHooks?.skipFirmwareIdentification) {\n\t\t\t\t// No identification desired, just send a NAK and assume it's a\n\t\t\t\t// Serial API controller\n\t\t\t\tawait this.writeHeader(MessageHeaders.NAK);\n\t\t\t\tif (getenv(\"NODE_ENV\") !== \"test\") {\n\t\t\t\t\tawait wait(1000);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst mode = await this.detectMode();\n\t\t\t\tif (mode === DriverMode.CLI) {\n\t\t\t\t\tthis.emit(\"cli ready\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (mode === DriverMode.Bootloader) {\n\t\t\t\t\tif (this._options.bootloaderMode === \"stay\") {\n\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\"Controller is in bootloader mode. Staying in bootloader as requested.\",\n\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.emit(\"bootloader ready\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Controller is in bootloader, attempting to recover...\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tawait this.leaveBootloaderInternal();\n\n\t\t\t\t\t// Wait a short time again. If we're in bootloader mode again, we're stuck\n\t\t\t\t\tawait wait(1000);\n\n\t\t\t\t\t// FIXME: Leaving the bootloader may end up in the CLI\n\n\t\t\t\t\tif (this._bootloader) {\n\t\t\t\t\t\tif (this._options.bootloaderMode === \"allow\") {\n\t\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\t\"Failed to recover from bootloader. Staying in bootloader mode as requested.\",\n\t\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tthis.emit(\"bootloader ready\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// bootloaderMode === \"recover\"\n\t\t\t\t\t\t\tvoid this.destroyWithMessage(\n\t\t\t\t\t\t\t\t\"Failed to recover from bootloader. Please flash a new firmware to continue...\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Try to create the cache directory. This can fail, in which case we should expose a good error message\n\t\t\ttry {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\tif (this._options.storage.driver) {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\tawait this._options.storage.driver.ensureDir(this.cacheDir);\n\t\t\t\t} else {\n\t\t\t\t\tawait this.bindings.fs.ensureDir(this.cacheDir);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tlet message: string;\n\t\t\t\tif (\n\t\t\t\t\t/\\.yarn[/\\\\]cache[/\\\\]zwave-js/i.test(\n\t\t\t\t\t\tgetErrorMessage(e, true),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tmessage =\n\t\t\t\t\t\t`Failed to create the cache directory ${this.cacheDir}. When using Yarn PnP, you need to change the location with the \"storage.cacheDir\" driver option.`;\n\t\t\t\t} else {\n\t\t\t\t\tmessage =\n\t\t\t\t\t\t`Failed to create the cache directory ${this.cacheDir}. Please make sure that it is writable or change the location with the \"storage.cacheDir\" driver option.`;\n\t\t\t\t}\n\n\t\t\t\tvoid this.destroyWithMessage(message);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Load the necessary configuration\n\t\t\tif (this._options.testingHooks?.loadConfiguration !== false) {\n\t\t\t\tthis.driverLog.print(\"loading configuration...\");\n\t\t\t\ttry {\n\t\t\t\t\tawait this.configManager.loadAll();\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst message = `Failed to load the configuration: ${\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\tvoid this.destroyWithMessage(message);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.driverLog.print(\"beginning interview...\");\n\t\t\ttry {\n\t\t\t\tawait this.initializeControllerAndNodes();\n\t\t\t} catch (e) {\n\t\t\t\tlet message: string;\n\t\t\t\tif (\n\t\t\t\t\tisZWaveError(e)\n\t\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t) {\n\t\t\t\t\tmessage =\n\t\t\t\t\t\t`Failed to initialize the driver, no response from the controller. Are you sure this is a Z-Wave controller?`;\n\t\t\t\t} else {\n\t\t\t\t\tmessage = `Failed to initialize the driver: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`;\n\t\t\t\t}\n\t\t\t\tthis.driverLog.print(message, \"error\");\n\t\t\t\tthis.emit(\n\t\t\t\t\t\"error\",\n\t\t\t\t\tnew ZWaveError(message, ZWaveErrorCodes.Driver_Failed),\n\t\t\t\t);\n\t\t\t\tvoid this.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\t\t});\n\n\t\treturn spOpenPromise;\n\t}\n\n\tprivate async detectMode(): Promise<DriverMode> {\n\t\t// We re-use the NAK that should be used to reset the communication on\n\t\t// Serial API startup to detect which kind of application we are talking to\n\n\t\tconst incomingNAK = this.waitForMessageHeader(\n\t\t\t(h) => h === MessageHeaders.NAK,\n\t\t\t500,\n\t\t)\n\t\t\t.then(() => true)\n\t\t\t.catch(() => false);\n\t\tawait this.writeHeader(MessageHeaders.NAK);\n\n\t\t// The response to this NAK helps determine whether the Z-Wave module is...\n\t\t// ...stuck in the bootloader,\n\t\t// ...running a SoC end device firmware with CLI\n\t\t// ...or a \"normal\" Serial API\n\n\t\tif (await incomingNAK) {\n\t\t\t// This is possibly a CLI. It should respond with a prompt after we\n\t\t\t// send a newline.\n\t\t\tawait this.writeSerial(Bytes.from(\"\\n\", \"ascii\"));\n\t\t}\n\n\t\t// If there was no NAK, it may be a bootloader, but it may also be a CLI\n\t\t// on a device that just started. In this case it can happen that the\n\t\t// NAK is not answered, but a CLI prompt is received.\n\t\t// In this case, the CLI is also detected by the serial parsers.\n\n\t\t// In any case, wait another 500ms to give the parsers time to detect\n\t\t// either the booloader or the CLI\n\t\tawait wait(500);\n\n\t\tif (this._cli) return DriverMode.CLI;\n\t\tif (this._bootloader) return DriverMode.Bootloader;\n\n\t\treturn DriverMode.SerialAPI;\n\t}\n\n\tprivate _controllerInterviewed: boolean = false;\n\tprivate _nodesReady = new Set<number>();\n\tprivate _nodesReadyEventEmitted: boolean = false;\n\tprivate _isOpeningSerialPort: boolean = false;\n\n\tprivate async openSerialport(): Promise<void> {\n\t\tlet lastError: unknown;\n\t\t// After a reset, the serial port may need a few seconds until we can open it - try a few times\n\t\tthis._isOpeningSerialPort = true;\n\t\tfor (\n\t\t\tlet attempt = 1;\n\t\t\tattempt <= this._options.attempts.openSerialPort;\n\t\t\tattempt++\n\t\t) {\n\t\t\ttry {\n\t\t\t\tthis.serial = await this.serialFactory!.createStream();\n\t\t\t\t// Start reading from the serial port\n\t\t\t\tvoid this.handleSerialData(this.serial);\n\n\t\t\t\t// There are some situations where the serial port closes unexpectedly\n\t\t\t\t// after just a few milliseconds, e.g. when reconnecting to a TCP serial port.\n\t\t\t\t// Wait a bit to see if this happens.\n\t\t\t\tif (getenv(\"NODE_ENV\") !== \"test\") {\n\t\t\t\t\tawait wait(250);\n\t\t\t\t}\n\n\t\t\t\tif (this.serial.isOpen) {\n\t\t\t\t\t// It is still open, we're done\n\t\t\t\t\tthis._isOpeningSerialPort = false;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tlastError = e;\n\t\t\t}\n\t\t\tif (attempt < this._options.attempts.openSerialPort) {\n\t\t\t\tawait wait(1000);\n\t\t\t}\n\t\t}\n\n\t\tthis._isOpeningSerialPort = false;\n\n\t\tconst message = `Failed to open the serial port: ${\n\t\t\tgetErrorMessage(\n\t\t\t\tlastError,\n\t\t\t)\n\t\t}`;\n\t\tthis.driverLog.print(message, \"error\");\n\n\t\tthrow new ZWaveError(message, ZWaveErrorCodes.Driver_Failed);\n\t}\n\n\t/** Indicates whether all nodes are ready, i.e. the \"all nodes ready\" event has been emitted */\n\tpublic get allNodesReady(): boolean {\n\t\treturn this._nodesReadyEventEmitted;\n\t}\n\n\tprivate getJsonlDBOptions(): JsonlDBOptions<any> {\n\t\tconst options: JsonlDBOptions<any> = {\n\t\t\tignoreReadErrors: true,\n\t\t\t...throttlePresets[this._options.storage.throttle],\n\t\t};\n\t\tif (this._options.storage.lockDir) {\n\t\t\toptions.lockfile = {\n\t\t\t\tdirectory: this._options.storage.lockDir,\n\t\t\t};\n\t\t}\n\t\treturn options;\n\t}\n\n\tprivate async initNetworkCache(homeId: number): Promise<void> {\n\t\tconst options = this.getJsonlDBOptions();\n\n\t\tconst networkCacheFile = path.join(\n\t\t\tthis.cacheDir,\n\t\t\t`${homeId.toString(16)}.jsonl`,\n\t\t);\n\t\tthis._networkCache = this.bindings.db.createInstance(networkCacheFile, {\n\t\t\t...options,\n\t\t\tserializer: serializeNetworkCacheValue,\n\t\t\treviver: deserializeNetworkCacheValue,\n\t\t});\n\t\tawait this._networkCache.open();\n\n\t\tif (getenv(\"NO_CACHE\") === \"true\") {\n\t\t\t// Since the network cache is append-only, we need to\n\t\t\t// clear it if the cache should be ignored\n\t\t\tthis._networkCache.clear();\n\t\t}\n\t}\n\n\tprivate async initValueDBs(homeId: number): Promise<void> {\n\t\tconst options = this.getJsonlDBOptions();\n\n\t\tconst valueDBFile = path.join(\n\t\t\tthis.cacheDir,\n\t\t\t`${homeId.toString(16)}.values.jsonl`,\n\t\t);\n\t\tthis._valueDB = this.bindings.db.createInstance(valueDBFile, {\n\t\t\t...options,\n\t\t\tenableTimestamps: true,\n\t\t\treviver: (_key, value) => deserializeCacheValue(value),\n\t\t\tserializer: (_key, value) => serializeCacheValue(value),\n\t\t});\n\t\tawait this._valueDB.open();\n\n\t\tconst metadataDBFile = path.join(\n\t\t\tthis.cacheDir,\n\t\t\t`${homeId.toString(16)}.metadata.jsonl`,\n\t\t);\n\t\tthis._metadataDB = this.bindings.db.createInstance(\n\t\t\tmetadataDBFile,\n\t\t\toptions,\n\t\t);\n\t\tawait this._metadataDB.open();\n\n\t\tif (getenv(\"NO_CACHE\") === \"true\") {\n\t\t\t// Since value/metadata DBs are append-only, we need to\n\t\t\t// clear them if the cache should be ignored\n\t\t\tthis._valueDB.clear();\n\t\t\tthis._metadataDB.clear();\n\t\t}\n\t}\n\n\tprivate async performCacheMigration(): Promise<void> {\n\t\tif (\n\t\t\t!this._controller\n\t\t\t|| !this.controller.homeId\n\t\t\t|| !this._networkCache\n\t\t\t|| !this._valueDB\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// In v9, the network cache was switched from a json file to use a Jsonl-DB\n\t\t// Therefore the legacy cache file must be migrated to the new format\n\t\tif (this._networkCache.size === 0) {\n\t\t\t// version the cache format, so migrations in the future are easier\n\t\t\tthis._networkCache.set(\"cacheFormat\", 1);\n\n\t\t\ttry {\n\t\t\t\tawait migrateLegacyNetworkCache(\n\t\t\t\t\tthis.controller.homeId,\n\t\t\t\t\tthis._networkCache,\n\t\t\t\t\tthis._valueDB,\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\tthis._options.storage.driver\n\t\t\t\t\t\t? wrapLegacyFSDriverForCacheMigrationOnly(\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\t\t\tthis._options.storage.driver,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t: this.bindings.fs,\n\t\t\t\t\tthis.cacheDir,\n\t\t\t\t);\n\n\t\t\t\t// Go through the value DB and remove all keys referencing commandClass -1, which used to be a\n\t\t\t\t// hacky way to store non-CC specific values\n\t\t\t\tfor (const key of this._valueDB.keys()) {\n\t\t\t\t\tif (-1 === key.indexOf(`,\"commandClass\":-1,`)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthis._valueDB.delete(key);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tconst message =\n\t\t\t\t\t`Migrating the legacy cache file to jsonl failed: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`;\n\t\t\t\tthis.driverLog.print(message, \"error\");\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Initializes the variables for controller and nodes,\n\t * adds event handlers and starts the interview process.\n\t */\n\tprivate async initializeControllerAndNodes(): Promise<void> {\n\t\tif (this._controller) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The controller was already initialized!\",\n\t\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t\t);\n\t\t}\n\n\t\tthis._controller = new ZWaveController(this);\n\t\tthis._controller\n\t\t\t.on(\"node found\", this.onNodeFound.bind(this))\n\t\t\t.on(\"node added\", this.onNodeAdded.bind(this))\n\t\t\t.on(\"node removed\", this.onNodeRemoved.bind(this))\n\t\t\t.on(\n\t\t\t\t\"status changed\",\n\t\t\t\tthis.onControllerStatusChanged.bind(this),\n\t\t\t)\n\t\t\t.on(\"network found\", this.onNetworkFound.bind(this))\n\t\t\t.on(\"network joined\", this.onNetworkJoined.bind(this))\n\t\t\t.on(\"network left\", this.onNetworkLeft.bind(this));\n\n\t\t// Create and start all queues after creating the controller instance\n\t\tthis.initTransactionQueues();\n\t\tthis.initSerialAPIQueue();\n\n\t\tif (!this._options.testingHooks?.skipControllerIdentification) {\n\t\t\t// Determine what the controller can do\n\t\t\tconst { nodeIds } = await this.controller.queryCapabilities();\n\n\t\t\t// Configure the radio\n\t\t\tawait this.controller.queryAndConfigureRF();\n\n\t\t\t// Soft-reset the stick if possible.\n\t\t\t// On 700+ series, we'll also learn about whether the stick supports\n\t\t\t// Z-Wave Long Range in the current region.\n\t\t\tconst maySoftReset = this.maySoftReset();\n\t\t\tif (this._options.features.softReset && !maySoftReset) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t`Soft reset is enabled through config, but this stick does not support it.`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tthis._options.features.softReset = false;\n\t\t\t}\n\n\t\t\tif (maySoftReset) {\n\t\t\t\tawait this.softResetInternal(false);\n\t\t\t}\n\n\t\t\tlet lrNodeIds: readonly number[] | undefined;\n\t\t\tif (this.controller.supportsLongRange) {\n\t\t\t\t// If the controller supports ZWLR, we need to query the node IDs again\n\t\t\t\t// to get the full list of nodes\n\t\t\t\tlrNodeIds = (await this.controller.queryLongRangeCapabilities())\n\t\t\t\t\t.lrNodeIds;\n\t\t\t}\n\n\t\t\t// If the controller supports ZWLR in the current region, switch to\n\t\t\t// 16-bit node IDs. Otherwise, make sure that it is actually using 8-bit node IDs.\n\t\t\tawait this.controller.trySetNodeIDType(\n\t\t\t\tthis.controller.supportsLongRange\n\t\t\t\t\t? NodeIDType.Long\n\t\t\t\t\t: NodeIDType.Short,\n\t\t\t);\n\n\t\t\t// Now that we know the node ID type, we can identify the controller\n\t\t\tawait this.controller.identify();\n\n\t\t\t// Perform additional configuration\n\t\t\tawait this.controller.configure();\n\n\t\t\t// now that we know the home ID, we can open the databases\n\t\t\tawait this.initNetworkCache(this.controller.homeId!);\n\t\t\tawait this.initValueDBs(this.controller.homeId!);\n\t\t\tawait this.performCacheMigration();\n\n\t\t\t// Initialize all nodes and restore the data from cache\n\t\t\tawait this.controller.initNodes(\n\t\t\t\tnodeIds,\n\t\t\t\tlrNodeIds ?? [],\n\t\t\t\tasync () => {\n\t\t\t\t\t// Try to restore the network information from the cache\n\t\t\t\t\tif (getenv(\"NO_CACHE\") !== \"true\") {\n\t\t\t\t\t\t// FIXME: This entire thing is a pretty convoluted way of looking up the device config\n\t\t\t\t\t\tawait this.restoreNetworkStructureFromCache();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// For controllers with proprietary implementations, interview them too\n\t\t\tawait this.controller.interviewProprietary();\n\n\t\t\tthis.controllerLog.print(\"Interview completed\");\n\n\t\t\tif (this.controller.role === ControllerRole.Primary) {\n\t\t\t\t// Auto-enable smart start inclusion\n\t\t\t\tthis.controller.autoProvisionSmartStart();\n\t\t\t}\n\t\t} else {\n\t\t\t// When skipping the controller identification, set the flags to consider the controller a primary\n\t\t\tthis.controller[\"_wasRealPrimary\"] = true;\n\t\t\tthis.controller[\"_isSUC\"] = true;\n\t\t\tthis.controller[\"_isSISPresent\"] = true;\n\t\t\tthis.controller[\"_sucNodeId\"] = 1;\n\t\t}\n\n\t\tif (this.controller.role === ControllerRole.Primary) {\n\t\t\t// Set up the S0 security manager. We can only do that after the controller\n\t\t\t// interview because we need to know the controller node id.\n\t\t\tconst S0Key = this._options.securityKeys?.S0_Legacy;\n\t\t\tif (S0Key) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Network key for S0 configured, enabling S0 security manager...\",\n\t\t\t\t);\n\t\t\t\tthis._securityManager = new SecurityManager({\n\t\t\t\t\tnetworkKey: S0Key,\n\t\t\t\t\townNodeId: this._controller.ownNodeId!,\n\t\t\t\t\tnonceTimeout: this._options.timeouts.nonce,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"No network key for S0 configured, communication with secure (S0) devices won't work!\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// The S2 security manager could be initialized earlier, but we do it here for consistency\n\t\t\tif (\n\t\t\t\tthis._options.securityKeys\n\t\t\t\t// Only set it up if we have security keys for at least one S2 security class\n\t\t\t\t&& Object.keys(this._options.securityKeys).some(\n\t\t\t\t\t(key) =>\n\t\t\t\t\t\tkey.startsWith(\"S2_\")\n\t\t\t\t\t\t&& key in SecurityClass\n\t\t\t\t\t\t&& securityClassIsS2((SecurityClass as any)[key]),\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"At least one network key for S2 configured, enabling S2 security manager...\",\n\t\t\t\t);\n\t\t\t\tthis._securityManager2 = await SecurityManager2.create();\n\t\t\t\t// Set up all keys\n\t\t\t\tfor (\n\t\t\t\t\tconst secClass of [\n\t\t\t\t\t\t\"S2_Unauthenticated\",\n\t\t\t\t\t\t\"S2_Authenticated\",\n\t\t\t\t\t\t\"S2_AccessControl\",\n\t\t\t\t\t\t\"S0_Legacy\",\n\t\t\t\t\t] as const\n\t\t\t\t) {\n\t\t\t\t\tconst key = this._options.securityKeys[secClass];\n\t\t\t\t\tif (key) {\n\t\t\t\t\t\tawait this._securityManager2.setKey(\n\t\t\t\t\t\t\tSecurityClass[secClass],\n\t\t\t\t\t\t\tkey,\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.driverLog.print(\n\t\t\t\t\t\"No network key for S2 configured, communication with secure (S2) devices won't work!\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tthis._options.securityKeysLongRange?.S2_AccessControl\n\t\t\t\t|| this._options.securityKeysLongRange?.S2_Authenticated\n\t\t\t) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"At least one network key for Z-Wave Long Range configured, enabling security manager...\",\n\t\t\t\t);\n\t\t\t\tthis._securityManagerLR = await SecurityManager2.create();\n\t\t\t\tif (this._options.securityKeysLongRange?.S2_AccessControl) {\n\t\t\t\t\tawait this._securityManagerLR.setKey(\n\t\t\t\t\t\tSecurityClass.S2_AccessControl,\n\t\t\t\t\t\tthis._options.securityKeysLongRange.S2_AccessControl,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (this._options.securityKeysLongRange?.S2_Authenticated) {\n\t\t\t\t\tawait this._securityManagerLR.setKey(\n\t\t\t\t\t\tSecurityClass.S2_Authenticated,\n\t\t\t\t\t\tthis._options.securityKeysLongRange.S2_Authenticated,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"No network key for Z-Wave Long Range configured, communication won't work!\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\t// Secondary controller - load security keys from cache.\n\t\t\t// Either LR or S2+S0, not both\n\t\t\t// FIXME: The fallback code duplicates the logic from the primary controller above. Find a nicer solution.\n\t\t\tif (isLongRangeNodeId(this.controller.ownNodeId!)) {\n\t\t\t\tconst securityKeysLongRange = [\n\t\t\t\t\tSecurityClass.S2_AccessControl,\n\t\t\t\t\tSecurityClass.S2_Authenticated,\n\t\t\t\t].map(\n\t\t\t\t\t(sc) => ([\n\t\t\t\t\t\tsc,\n\t\t\t\t\t\tthis.cacheGet<Uint8Array>(\n\t\t\t\t\t\t\tcacheKeys.controller.securityKeysLongRange(sc),\n\t\t\t\t\t\t),\n\t\t\t\t\t] as [SecurityClass, Uint8Array | undefined]),\n\t\t\t\t).filter((v): v is [SecurityClass, Uint8Array] =>\n\t\t\t\t\tv[1] != undefined\n\t\t\t\t);\n\t\t\t\tif (securityKeysLongRange.length) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"At least one network key for Z-Wave Long Range found in cache, enabling security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManagerLR = await SecurityManager2.create();\n\t\t\t\t\tfor (const [sc, key] of securityKeysLongRange) {\n\t\t\t\t\t\tawait this._securityManagerLR.setKey(sc, key);\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tthis._options.securityKeysLongRange?.S2_AccessControl\n\t\t\t\t\t|| this._options.securityKeysLongRange?.S2_Authenticated\n\t\t\t\t) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Fallback to configured network keys for Z-Wave Long Range, enabling security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManagerLR = await SecurityManager2.create();\n\t\t\t\t\tif (this._options.securityKeysLongRange?.S2_AccessControl) {\n\t\t\t\t\t\tawait this._securityManagerLR.setKey(\n\t\t\t\t\t\t\tSecurityClass.S2_AccessControl,\n\t\t\t\t\t\t\tthis._options.securityKeysLongRange\n\t\t\t\t\t\t\t\t.S2_AccessControl,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (this._options.securityKeysLongRange?.S2_Authenticated) {\n\t\t\t\t\t\tawait this._securityManagerLR.setKey(\n\t\t\t\t\t\t\tSecurityClass.S2_Authenticated,\n\t\t\t\t\t\t\tthis._options.securityKeysLongRange\n\t\t\t\t\t\t\t\t.S2_Authenticated,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"No network key for Z-Wave Long Range configured, communication won't work!\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst s0Key = this.cacheGet<Uint8Array>(\n\t\t\t\t\tcacheKeys.controller.securityKeys(SecurityClass.S0_Legacy),\n\t\t\t\t);\n\t\t\t\tif (s0Key) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Network key for S0 found in cache, enabling S0 security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManager = new SecurityManager({\n\t\t\t\t\t\tnetworkKey: s0Key,\n\t\t\t\t\t\townNodeId: this._controller.ownNodeId!,\n\t\t\t\t\t\tnonceTimeout: this._options.timeouts.nonce,\n\t\t\t\t\t});\n\t\t\t\t} else if (this._options.securityKeys?.S0_Legacy) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Fallback to configured S0 network key, enabling S0 security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManager = new SecurityManager({\n\t\t\t\t\t\tnetworkKey: this._options.securityKeys.S0_Legacy,\n\t\t\t\t\t\townNodeId: this._controller.ownNodeId!,\n\t\t\t\t\t\tnonceTimeout: this._options.timeouts.nonce,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"No network key for S0 found in cache, communication with secure (S0) devices won't work!\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst securityKeys = securityClassOrder.map(\n\t\t\t\t\t(sc) => ([\n\t\t\t\t\t\tsc,\n\t\t\t\t\t\tthis.cacheGet<Uint8Array>(\n\t\t\t\t\t\t\tcacheKeys.controller.securityKeys(sc),\n\t\t\t\t\t\t),\n\t\t\t\t\t] as [SecurityClass, Uint8Array | undefined]),\n\t\t\t\t).filter((v): v is [SecurityClass, Uint8Array] =>\n\t\t\t\t\tv[1] != undefined\n\t\t\t\t);\n\t\t\t\tif (securityKeys.length) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"At least one network key for S2 found in cache, enabling S2 security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManager2 = await SecurityManager2.create();\n\t\t\t\t\tfor (const [sc, key] of securityKeys) {\n\t\t\t\t\t\tawait this._securityManager2.setKey(sc, key);\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tthis._options.securityKeys\n\t\t\t\t\t&& Object.keys(this._options.securityKeys).some(\n\t\t\t\t\t\t(key) =>\n\t\t\t\t\t\t\tkey.startsWith(\"S2_\")\n\t\t\t\t\t\t\t&& key in SecurityClass\n\t\t\t\t\t\t\t&& securityClassIsS2((SecurityClass as any)[key]),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Fallback to configured network keys for S2, enabling S2 security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManager2 = await SecurityManager2.create();\n\t\t\t\t\t// Set up all keys\n\t\t\t\t\tfor (\n\t\t\t\t\t\tconst secClass of [\n\t\t\t\t\t\t\t\"S2_Unauthenticated\",\n\t\t\t\t\t\t\t\"S2_Authenticated\",\n\t\t\t\t\t\t\t\"S2_AccessControl\",\n\t\t\t\t\t\t\t\"S0_Legacy\",\n\t\t\t\t\t\t] as const\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst key = this._options.securityKeys[secClass];\n\t\t\t\t\t\tif (key) {\n\t\t\t\t\t\t\tawait this._securityManager2.setKey(\n\t\t\t\t\t\t\t\tSecurityClass[secClass],\n\t\t\t\t\t\t\t\tkey,\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 {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"No network key for S2 found in cache, communication with secure (S2) devices won't work!\",\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\n\t\t// in any case we need to emit the driver ready event here\n\t\tthis._controllerInterviewed = true;\n\t\tthis.driverLog.print(\"driver ready\");\n\t\tthis.emit(\"driver ready\");\n\n\t\t// Add event handlers for the nodes\n\t\tfor (const node of this._controller.nodes.values()) {\n\t\t\tthis.addNodeEventHandlers(node);\n\t\t}\n\n\t\tif (this.controller.role === ControllerRole.Primary) {\n\t\t\t// Before interviewing nodes reset our knowledge about their ready state\n\t\t\tthis._nodesReady.clear();\n\t\t\tthis._nodesReadyEventEmitted = false;\n\n\t\t\tif (!this._options.testingHooks?.skipNodeInterview) {\n\t\t\t\t// Now interview all nodes\n\t\t\t\t// First complete the controller interview\n\t\t\t\tconst controllerNode = this._controller.nodes.get(\n\t\t\t\t\tthis._controller.ownNodeId!,\n\t\t\t\t)!;\n\t\t\t\tawait this.interviewNodeInternal(controllerNode);\n\t\t\t\t// The controller node is always alive\n\t\t\t\tcontrollerNode.markAsAlive();\n\n\t\t\t\t// Then do all the nodes in parallel, but prioritize nodes that are more likely to be ready\n\t\t\t\tconst nodeInterviewOrder = [...this._controller.nodes.values()]\n\t\t\t\t\t.filter((n) => n.id !== this._controller!.ownNodeId)\n\t\t\t\t\t.sort((a, b) =>\n\t\t\t\t\t\t// Fully-interviewed devices first (need the least amount of communication now)\n\t\t\t\t\t\t(b.interviewStage - a.interviewStage)\n\t\t\t\t\t\t// Always listening -> FLiRS -> sleeping\n\t\t\t\t\t\t|| (\n\t\t\t\t\t\t\t(b.isListening ? 2 : b.isFrequentListening ? 1 : 0)\n\t\t\t\t\t\t\t- (a.isListening\n\t\t\t\t\t\t\t\t? 2\n\t\t\t\t\t\t\t\t: a.isFrequentListening\n\t\t\t\t\t\t\t\t? 1\n\t\t\t\t\t\t\t\t: 0)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Then by last seen, more recently first\n\t\t\t\t\t\t|| (\n\t\t\t\t\t\t\t(b.lastSeen?.getTime() ?? 0)\n\t\t\t\t\t\t\t- (a.lastSeen?.getTime() ?? 0)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Lastly ascending by node ID\n\t\t\t\t\t\t|| (a.id - b.id)\n\t\t\t\t\t);\n\n\t\t\t\tif (nodeInterviewOrder.length) {\n\t\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t\t`Interviewing nodes and/or determining their status: ${\n\t\t\t\t\t\t\tnodeInterviewOrder.map((n) => n.id).join(\", \")\n\t\t\t\t\t\t}`,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const node of nodeInterviewOrder) {\n\t\t\t\t\t\tif (node.canSleep) {\n\t\t\t\t\t\t\t// A node that can sleep should be assumed to be sleeping after resuming from cache\n\t\t\t\t\t\t\tnode.markAsAsleep();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvoid (async () => {\n\t\t\t\t\t\t\t// Continue the interview if necessary. If that is not necessary, at least\n\t\t\t\t\t\t\t// determine the node's status\n\t\t\t\t\t\t\tif (node.interviewStage < InterviewStage.Complete) {\n\t\t\t\t\t\t\t\tawait this.interviewNodeInternal(node);\n\t\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\t\tnode.isListening || node.isFrequentListening\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// Ping non-sleeping nodes to determine their status\n\t\t\t\t\t\t\t\tawait node.ping();\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\t\t\t}\n\t\t} else {\n\t\t\tif (!this._options.testingHooks?.skipNodeInterview) {\n\t\t\t\t// We're a secondary controller. Just determine if nodes are ready and do the interview at another time.\n\n\t\t\t\t// First complete the controller \"interview\"\n\t\t\t\tconst controllerNode = this._controller.nodes.get(\n\t\t\t\t\tthis._controller.ownNodeId!,\n\t\t\t\t)!;\n\t\t\t\tawait this.interviewNodeInternal(controllerNode);\n\t\t\t\t// The controller node is always alive\n\t\t\t\tcontrollerNode.markAsAlive();\n\n\t\t\t\t// Query the protocol information from the controller\n\t\t\t\tfor (const node of this._controller.nodes.values()) {\n\t\t\t\t\tif (node.isControllerNode) continue;\n\t\t\t\t\tawait node[\"queryProtocolInfo\"]();\n\t\t\t\t}\n\n\t\t\t\t// Then ping (frequently) listening nodes to determine their status\n\t\t\t\tconst nodeInterviewOrder = [...this._controller.nodes.values()]\n\t\t\t\t\t.filter((n) => n.id !== this._controller!.ownNodeId)\n\t\t\t\t\t.filter((n) => n.isListening || n.isFrequentListening)\n\t\t\t\t\t.sort((a, b) =>\n\t\t\t\t\t\t// Always listening -> FLiRS\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\t(b.isListening ? 1 : 0)\n\t\t\t\t\t\t\t- (a.isListening ? 1 : 0)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Then by last seen, more recently first\n\t\t\t\t\t\t|| (\n\t\t\t\t\t\t\t(b.lastSeen?.getTime() ?? 0)\n\t\t\t\t\t\t\t- (a.lastSeen?.getTime() ?? 0)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Lastly ascending by node ID\n\t\t\t\t\t\t|| (a.id - b.id)\n\t\t\t\t\t);\n\n\t\t\t\tif (nodeInterviewOrder.length) {\n\t\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t\t`Determining node status: ${\n\t\t\t\t\t\t\tnodeInterviewOrder.map((n) => n.id).join(\", \")\n\t\t\t\t\t\t}`,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const node of nodeInterviewOrder) {\n\t\t\t\t\t\tvoid node.ping();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If we only have sleeping nodes or a controller-only network, the send\n\t\t// thread is idle before the driver gets marked ready, the idle tasks won't be triggered.\n\t\t// So do it manually.\n\t\tthis.handleQueueIdleChange(this.queueIdle);\n\t}\n\n\tprivate autoRefreshNodeValueTimers = new Map<number, Interval>();\n\tprivate retryNodeInterviewTimeouts = new Map<number, Timer>();\n\t/**\n\t * @internal\n\t * Starts or resumes the interview of a Z-Wave node. It is advised to NOT\n\t * await this method as it can take a very long time (minutes to hours)!\n\t *\n\t * WARNING: Do not call this method from application code. To refresh the information\n\t * for a specific node, use `node.refreshInfo()` instead\n\t */\n\tpublic async interviewNodeInternal(node: ZWaveNode): Promise<void> {\n\t\tif (node.interviewStage === InterviewStage.Complete) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Avoid having multiple restart timeouts active\n\t\tif (this.retryNodeInterviewTimeouts.has(node.id)) {\n\t\t\tthis.retryNodeInterviewTimeouts.get(node.id)?.clear();\n\t\t\tthis.retryNodeInterviewTimeouts.delete(node.id);\n\t\t}\n\n\t\t// Drop all pending messages that come from a previous interview attempt\n\t\tawait this.rejectTransactions(\n\t\t\t(t) =>\n\t\t\t\tt.message.getNodeId() === node.id\n\t\t\t\t&& (t.priority === MessagePriority.NodeQuery\n\t\t\t\t\t|| t.tag === \"interview\"),\n\t\t\t\"The interview was restarted\",\n\t\t\tZWaveErrorCodes.Controller_InterviewRestarted,\n\t\t);\n\n\t\tconst maxInterviewAttempts = this._options.attempts.nodeInterview;\n\n\t\ttry {\n\t\t\tif (!(await node.interviewInternal())) {\n\t\t\t\t// Find out if we may retry the interview\n\t\t\t\tif (node.status === NodeStatus.Dead) {\n\t\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t`Interview attempt (${node.interviewAttempts}/${maxInterviewAttempts}) failed, node is dead.`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tnode.emit(\"interview failed\", node, {\n\t\t\t\t\t\terrorMessage: \"The node is dead\",\n\t\t\t\t\t\tisFinal: true,\n\t\t\t\t\t});\n\t\t\t\t} else if (node.interviewAttempts < maxInterviewAttempts) {\n\t\t\t\t\t// This is most likely because the node is unable to handle our load of requests now. Give it some time\n\t\t\t\t\tconst retryTimeout = Math.min(\n\t\t\t\t\t\t30000,\n\t\t\t\t\t\tnode.interviewAttempts * 5000,\n\t\t\t\t\t);\n\t\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t`Interview attempt ${node.interviewAttempts}/${maxInterviewAttempts} failed, retrying in ${retryTimeout} ms...`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tnode.emit(\"interview failed\", node, {\n\t\t\t\t\t\terrorMessage:\n\t\t\t\t\t\t\t`Attempt ${node.interviewAttempts}/${maxInterviewAttempts} failed`,\n\t\t\t\t\t\tisFinal: false,\n\t\t\t\t\t\tattempt: node.interviewAttempts,\n\t\t\t\t\t\tmaxAttempts: maxInterviewAttempts,\n\t\t\t\t\t});\n\t\t\t\t\t// Schedule the retry and remember the timeout instance\n\t\t\t\t\tthis.retryNodeInterviewTimeouts.set(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\tsetTimer(() => {\n\t\t\t\t\t\t\tthis.retryNodeInterviewTimeouts.delete(node.id);\n\t\t\t\t\t\t\tvoid this.interviewNodeInternal(node);\n\t\t\t\t\t\t}, retryTimeout).unref(),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t`Failed all interview attempts, giving up.`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tnode.emit(\"interview failed\", node, {\n\t\t\t\t\t\terrorMessage: `Maximum interview attempts reached`,\n\t\t\t\t\t\tisFinal: true,\n\t\t\t\t\t\tattempt: maxInterviewAttempts,\n\t\t\t\t\t\tmaxAttempts: maxInterviewAttempts,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\tnode.manufacturerId != undefined\n\t\t\t\t&& node.productType != undefined\n\t\t\t\t&& node.productId != undefined\n\t\t\t\t&& node.firmwareVersion != undefined\n\t\t\t\t&& !node.deviceConfig\n\t\t\t\t&& process.env.NODE_ENV !== \"test\"\n\t\t\t) {\n\t\t\t\t// The interview succeeded, but we don't have a device config for this node.\n\t\t\t\t// Report it, so we can add a config file\n\n\t\t\t\tvoid reportMissingDeviceConfig(this, node as any).catch(noop);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (isZWaveError(e)) {\n\t\t\t\tif (\n\t\t\t\t\te.code === ZWaveErrorCodes.Driver_NotReady\n\t\t\t\t\t|| e.code === ZWaveErrorCodes.Controller_NodeRemoved\n\t\t\t\t) {\n\t\t\t\t\t// This only happens when a node is removed during the interview - we don't log this\n\t\t\t\t\treturn;\n\t\t\t\t} else if (\n\t\t\t\t\te.code === ZWaveErrorCodes.Controller_InterviewRestarted\n\t\t\t\t) {\n\t\t\t\t\t// The interview was restarted by a user - we don't log this\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\tnode.id,\n\t\t\t\t\t`Error during node interview: ${e.message}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Adds the necessary event handlers for a node instance */\n\tprivate addNodeEventHandlers(node: ZWaveNode): void {\n\t\tnode.on(\"wake up\", this.onNodeWakeUp.bind(this))\n\t\t\t.on(\"sleep\", this.onNodeSleep.bind(this))\n\t\t\t.on(\"alive\", this.onNodeAlive.bind(this))\n\t\t\t.on(\"dead\", this.onNodeDead.bind(this))\n\t\t\t.on(\"interview completed\", this.onNodeInterviewCompleted.bind(this))\n\t\t\t.on(\"ready\", this.onNodeReady.bind(this))\n\t\t\t.on(\n\t\t\t\t\"firmware update finished\",\n\t\t\t\tthis.onNodeFirmwareUpdated.bind(this),\n\t\t\t)\n\t\t\t.on(\"notification\", this.onNodeNotification.bind(this));\n\n\t\t// Add forwarders for all node events\n\t\tfor (const event of zWaveNodeEvents) {\n\t\t\tnode.on(event, (...args: any[]) => {\n\t\t\t\t// @ts-expect-error We made sure that args matches\n\t\t\t\tthis.emit(`node ${event}`, ...args);\n\t\t\t});\n\t\t}\n\t}\n\n\t/** Removes a node's event handlers that were added with addNodeEventHandlers */\n\tprivate removeNodeEventHandlers(node: ZWaveNode): void {\n\t\tnode.removeAllListeners();\n\t}\n\n\t/** Is called when a node wakes up */\n\tprivate onNodeWakeUp(node: ZWaveNode, oldStatus: NodeStatus): void {\n\t\tthis.controllerLog.logNode(\n\t\t\tnode.id,\n\t\t\t`The node is ${\n\t\t\t\toldStatus === NodeStatus.Unknown ? \"\" : \"now \"\n\t\t\t}awake.`,\n\t\t);\n\n\t\t// Make sure to handle the pending messages as quickly as possible\n\t\tif (oldStatus === NodeStatus.Asleep) {\n\t\t\tvoid this.reduceQueues(({ message }) => {\n\t\t\t\t// Ignore messages that are not for this node\n\t\t\t\tif (message.getNodeId() !== node.id) return { type: \"keep\" };\n\t\t\t\t// Resolve pings, so we don't need to send them (we know the node is awake)\n\t\t\t\tif (messageIsPing(message)) {\n\t\t\t\t\treturn { type: \"resolve\", message: undefined };\n\t\t\t\t}\n\t\t\t\t// Re-queue all other transactions for this node, so they get added in front of the others\n\t\t\t\treturn { type: \"requeue\" };\n\t\t\t});\n\t\t}\n\n\t\t// Start the timer for sending the node to sleep again\n\t\tthis.debounceSendNodeToSleep(node);\n\t}\n\n\t/** Is called when a node goes to sleep */\n\tprivate onNodeSleep(node: ZWaveNode, oldStatus: NodeStatus): void {\n\t\tthis.controllerLog.logNode(\n\t\t\tnode.id,\n\t\t\t`The node is ${\n\t\t\t\toldStatus === NodeStatus.Unknown ? \"\" : \"now \"\n\t\t\t}asleep.`,\n\t\t);\n\n\t\t// Move all its pending messages to the WakeupQueue\n\t\t// This clears the current transaction and continues sending the next messages\n\t\tthis.moveMessagesToWakeupQueue(node.id);\n\t}\n\n\t/** Is called when a previously dead node starts communicating again */\n\tprivate onNodeAlive(node: ZWaveNode, oldStatus: NodeStatus): void {\n\t\tthis.controllerLog.logNode(\n\t\t\tnode.id,\n\t\t\t`The node is ${\n\t\t\t\toldStatus === NodeStatus.Unknown ? \"\" : \"now \"\n\t\t\t}alive.`,\n\t\t);\n\t\tif (\n\t\t\toldStatus === NodeStatus.Dead\n\t\t\t&& node.interviewStage !== InterviewStage.Complete\n\t\t\t&& !this._options.testingHooks?.skipNodeInterview\n\t\t) {\n\t\t\tvoid this.interviewNodeInternal(node);\n\t\t}\n\t}\n\n\t/** Is called when a node is marked as dead */\n\tprivate onNodeDead(node: ZWaveNode, oldStatus: NodeStatus): void {\n\t\tthis.controllerLog.logNode(\n\t\t\tnode.id,\n\t\t\t`The node is ${\n\t\t\t\toldStatus === NodeStatus.Unknown ? \"\" : \"now \"\n\t\t\t}dead.`,\n\t\t);\n\n\t\t// This could mean that we need to ignore it in the all nodes ready check,\n\t\t// so perform the check again\n\t\tthis.checkAllNodesReady();\n\t}\n\n\t/** Is called when a node is ready to be used */\n\tprivate onNodeReady(node: ZWaveNode): void {\n\t\tthis._nodesReady.add(node.id);\n\t\tthis.controllerLog.logNode(node.id, \"The node is ready to be used\");\n\n\t\t// Regularly check if values of non-sleeping nodes need to be refreshed per the specs\n\t\t// For sleeping nodes this is done on wakeup\n\t\tif (this.autoRefreshNodeValueTimers.has(node.id)) {\n\t\t\tthis.autoRefreshNodeValueTimers.get(node.id)?.clear();\n\t\t\tthis.autoRefreshNodeValueTimers.delete(node.id);\n\t\t}\n\t\tif (!node.canSleep) {\n\t\t\t// Randomize the interval so we don't get a flood of queries for all listening nodes\n\t\t\tconst intervalMinutes = 50 + Math.random() * 20;\n\t\t\tthis.autoRefreshNodeValueTimers.set(\n\t\t\t\tnode.id,\n\t\t\t\tsetInterval(() => {\n\t\t\t\t\tvoid node.autoRefreshValues().catch(() => {\n\t\t\t\t\t\t// ignore errors\n\t\t\t\t\t});\n\t\t\t\t}, timespan.minutes(intervalMinutes)).unref(),\n\t\t\t);\n\t\t}\n\n\t\tthis.checkAllNodesReady();\n\t}\n\n\t/** Checks if all nodes are ready and emits the \"all nodes ready\" event if they are */\n\tprivate checkAllNodesReady(): void {\n\t\t// Only emit \"all nodes ready\" once\n\t\tif (this._nodesReadyEventEmitted) return;\n\n\t\tfor (const [id, node] of this.controller.nodes) {\n\t\t\t// Ignore dead nodes or the all nodes ready event will never be emitted without physical user interaction\n\t\t\tif (node.status === NodeStatus.Dead) continue;\n\n\t\t\tif (!this._nodesReady.has(id)) return;\n\t\t}\n\t\t// All nodes are ready\n\t\tthis.controllerLog.print(\"All nodes are ready to be used\");\n\t\tthis.emit(\"all nodes ready\");\n\t\tthis._nodesReadyEventEmitted = true;\n\n\t\t// We know we have all data, this is the time to send statistics (when enabled)\n\t\tvoid this.compileAndSendStatistics().catch(() => {\n\t\t\t/* ignore */\n\t\t});\n\t}\n\n\tprivate _statisticsEnabled: boolean = false;\n\t/** Whether reporting usage statistics is currently enabled */\n\tpublic get statisticsEnabled(): boolean {\n\t\treturn this._statisticsEnabled;\n\t}\n\n\tprivate statisticsAppInfo:\n\t\t| Pick<AppInfo, \"applicationName\" | \"applicationVersion\">\n\t\t| undefined;\n\n\tprivate userAgentComponents = new Map<string, string>();\n\n\t/**\n\t * Updates individual components of the user agent. Versions for individual applications can be added or removed.\n\t * @param components An object with application/module/component names and their versions. Set a version to `null` or `undefined` explicitly to remove it from the user agent.\n\t */\n\tpublic updateUserAgent(\n\t\tcomponents: Record<string, string | null | undefined>,\n\t): void {\n\t\tthis.userAgentComponents = mergeUserAgent(\n\t\t\tthis.userAgentComponents,\n\t\t\tcomponents,\n\t\t);\n\t\tthis._userAgent = this.getEffectiveUserAgentString(\n\t\t\tthis.userAgentComponents,\n\t\t);\n\t}\n\n\t/**\n\t * Returns the effective user agent string for the given components.\n\t * The driver name and version is automatically prepended and the statisticsAppInfo data is automatically appended if no components were given.\n\t */\n\tprivate getEffectiveUserAgentString(\n\t\tcomponents: Map<string, string>,\n\t): string {\n\t\tconst effectiveComponents = new Map([\n\t\t\t[libName, libVersion],\n\t\t\t...components,\n\t\t]);\n\t\tif (\n\t\t\teffectiveComponents.size === 1\n\t\t\t&& this.statisticsAppInfo\n\t\t\t&& this.statisticsAppInfo.applicationName !== \"node-zwave-js\"\n\t\t\t// node-zwave-js was renamed to just zwave-js in v15\n\t\t\t&& this.statisticsAppInfo.applicationName !== \"zwave-js\"\n\t\t) {\n\t\t\teffectiveComponents.set(\n\t\t\t\tthis.statisticsAppInfo.applicationName,\n\t\t\t\tthis.statisticsAppInfo.applicationVersion,\n\t\t\t);\n\t\t}\n\t\treturn userAgentComponentsToString(effectiveComponents);\n\t}\n\n\tprivate _userAgent: string = `zwave-js/${libVersion}`;\n\t/** Returns the user agent string used for service requests */\n\tpublic get userAgent(): string {\n\t\treturn this._userAgent;\n\t}\n\n\t/** Returns the user agent string combined with the additional components (if given) */\n\tpublic getUserAgentStringWithComponents(\n\t\tcomponents?: Record<string, string | null | undefined>,\n\t): string {\n\t\tif (!components || Object.keys(components).length === 0) {\n\t\t\treturn this._userAgent;\n\t\t}\n\n\t\tconst merged = mergeUserAgent(\n\t\t\tthis.userAgentComponents,\n\t\t\tcomponents,\n\t\t\tfalse,\n\t\t);\n\t\treturn this.getEffectiveUserAgentString(merged);\n\t}\n\n\t/**\n\t * Enable sending usage statistics. Although this does not include any sensitive information, we expect that you\n\t * inform your users before enabling statistics.\n\t */\n\tpublic enableStatistics(\n\t\tappInfo: Pick<AppInfo, \"applicationName\" | \"applicationVersion\">,\n\t): void {\n\t\tif (this._statisticsEnabled) return;\n\n\t\tif (\n\t\t\t!isObject(appInfo)\n\t\t\t|| typeof appInfo.applicationName !== \"string\"\n\t\t\t|| typeof appInfo.applicationVersion !== \"string\"\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The application statistics must be an object with two string properties \"applicationName\" and \"applicationVersion\"!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t} else if (appInfo.applicationName.length > 100) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The applicationName for statistics must be maximum 100 characters long!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t} else if (appInfo.applicationVersion.length > 100) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The applicationVersion for statistics must be maximum 100 characters long!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t}\n\n\t\tthis._statisticsEnabled = true;\n\t\tthis.statisticsAppInfo = appInfo;\n\n\t\t// If we're already ready, send statistics\n\t\tif (this._nodesReadyEventEmitted) {\n\t\t\tvoid this.compileAndSendStatistics().catch(() => {\n\t\t\t\t/* ignore */\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Disable sending usage statistics\n\t */\n\tpublic disableStatistics(): void {\n\t\tthis._statisticsEnabled = false;\n\t\tthis.statisticsAppInfo = undefined;\n\t\tthis.statisticsTimeout?.clear();\n\t\tthis.statisticsTimeout = undefined;\n\t}\n\n\t/** @internal */\n\t// eslint-disable-next-line @typescript-eslint/require-await\n\tpublic async getUUID(): Promise<string> {\n\t\t// To anonymously identify a network, we create a unique ID and use it to salt the Home ID\n\t\tif (!this._valueDB!.has(\"uuid\")) {\n\t\t\tthis._valueDB!.set(\n\t\t\t\t\"uuid\",\n\t\t\t\tBytes.view(randomBytes(32)).toString(\"hex\"),\n\t\t\t);\n\t\t}\n\t\tconst ret = this._valueDB!.get(\"uuid\") as string;\n\t\treturn ret;\n\t}\n\n\tprivate statisticsTimeout: Timer | undefined;\n\tprivate async compileAndSendStatistics(): Promise<void> {\n\t\t// Don't send anything if statistics are not enabled\n\t\tif (!this.statisticsEnabled || !this.statisticsAppInfo) return;\n\n\t\tthis.statisticsTimeout?.clear();\n\t\tthis.statisticsTimeout = undefined;\n\n\t\tlet success: number | boolean = false;\n\t\ttry {\n\t\t\tconst statistics = await compileStatistics(this, {\n\t\t\t\tdriverVersion: libVersion,\n\t\t\t\t...this.statisticsAppInfo,\n\t\t\t\tnodeVersion: process.versions.node,\n\t\t\t\tos: process.platform,\n\t\t\t\tarch: process.arch,\n\t\t\t});\n\t\t\tsuccess = await sendStatistics(statistics);\n\t\t} catch {\n\t\t\t// Didn't work - try again in a few hours\n\t\t\tsuccess = false;\n\t\t} finally {\n\t\t\tif (typeof success === \"number\") {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t`Sending usage statistics was rate limited - next attempt scheduled in ${success} seconds.`,\n\t\t\t\t\t\"verbose\",\n\t\t\t\t);\n\t\t\t\t// Wait at most 6 hours to try again\n\t\t\t\tconst retryMs = Math.max(\n\t\t\t\t\ttimespan.minutes(1),\n\t\t\t\t\tMath.min(success * 1000, timespan.hours(6)),\n\t\t\t\t);\n\t\t\t\tthis.statisticsTimeout = setTimer(() => {\n\t\t\t\t\tvoid this.compileAndSendStatistics();\n\t\t\t\t}, retryMs).unref();\n\t\t\t} else {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\tsuccess\n\t\t\t\t\t\t? `Usage statistics sent - next transmission scheduled in 23 hours.`\n\t\t\t\t\t\t: `Failed to send usage statistics - next transmission scheduled in 6 hours.`,\n\t\t\t\t\t\"verbose\",\n\t\t\t\t);\n\t\t\t\tthis.statisticsTimeout = setTimer(() => {\n\t\t\t\t\tvoid this.compileAndSendStatistics();\n\t\t\t\t}, timespan.hours(success ? 23 : 6)).unref();\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Is called when a node interview is completed */\n\tprivate onNodeInterviewCompleted(node: ZWaveNode): void {\n\t\tthis.debounceSendNodeToSleep(node);\n\t}\n\n\t/** This is called when a new node was found and is being added to the network */\n\tprivate onNodeFound(node: FoundNode): void {\n\t\t// GH#7692: In some cases, an old node's info may be left in the value DB,\n\t\t// causing incorrect behavior during interview.\n\n\t\t// Delete from value and metadata DBs\n\t\tconst prefix = `{\"nodeId\":${node.id},`;\n\t\tfor (const key of this.valueDB!.keys()) {\n\t\t\tif (key.startsWith(prefix)) {\n\t\t\t\tthis.valueDB!.delete(key);\n\t\t\t}\n\t\t}\n\t\tfor (const key of this.metadataDB!.keys()) {\n\t\t\tif (key.startsWith(prefix)) {\n\t\t\t\tthis.metadataDB!.delete(key);\n\t\t\t}\n\t\t}\n\n\t\tthis.cachePurge(cacheKeys.node(node.id)._baseKey);\n\t}\n\n\t/** This is called when a new node has been added to the network */\n\tprivate onNodeAdded(node: ZWaveNode): void {\n\t\tthis.addNodeEventHandlers(node);\n\n\t\tif (this._options.interview?.disableOnNodeAdded) return;\n\t\tif (this._options.testingHooks?.skipNodeInterview) return;\n\n\t\t// Interview the node\n\t\t// don't await the interview, because it may take a very long time\n\t\t// if a node is asleep\n\t\tvoid this.interviewNodeInternal(node);\n\t}\n\n\t/** This is called when a node was removed from the network */\n\tprivate onNodeRemoved(node: ZWaveNode, reason: RemoveNodeReason): void {\n\t\t// Remove all listeners and timers\n\t\tthis.removeNodeEventHandlers(node);\n\t\tif (this.sendNodeToSleepTimers.has(node.id)) {\n\t\t\tthis.sendNodeToSleepTimers.get(node.id)?.clear();\n\t\t\tthis.sendNodeToSleepTimers.delete(node.id);\n\t\t}\n\t\tif (this.retryNodeInterviewTimeouts.has(node.id)) {\n\t\t\tthis.retryNodeInterviewTimeouts.get(node.id)?.clear();\n\t\t\tthis.retryNodeInterviewTimeouts.delete(node.id);\n\t\t}\n\t\tif (this.autoRefreshNodeValueTimers.has(node.id)) {\n\t\t\tthis.autoRefreshNodeValueTimers.get(node.id)?.clear();\n\t\t\tthis.autoRefreshNodeValueTimers.delete(node.id);\n\t\t}\n\n\t\t// purge node values from the DB\n\t\tnode.valueDB.clear();\n\t\tthis.cachePurge(cacheKeys.node(node.id)._baseKey);\n\n\t\t// Remove the node from all security manager instances\n\t\tthis.securityManager?.deleteAllNoncesForReceiver(node.id);\n\t\tthis.securityManager2?.deleteNonce(node.id);\n\t\tthis.securityManagerLR?.deleteNonce(node.id);\n\n\t\tvoid this.rejectAllTransactionsForNode(\n\t\t\tnode.id,\n\t\t\t\"The node was removed from the network\",\n\t\t\tZWaveErrorCodes.Controller_NodeRemoved,\n\t\t);\n\n\t\tconst replaced = reason === RemoveNodeReason.Replaced\n\t\t\t|| reason === RemoveNodeReason.ProxyReplaced;\n\t\tif (!replaced) {\n\t\t\t// Asynchronously remove the node from all possible associations, ignore potential errors\n\t\t\t// but only if the node is not getting replaced, because the removal will interfere with\n\t\t\t// bootstrapping the new node\n\t\t\tthis.controller\n\t\t\t\t.removeNodeFromAllAssociations(node.id)\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Failed to remove node ${node.id} from all associations: ${err.message}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t}\n\n\t\t// And clean up all remaining resources used by the node\n\t\tnode.destroy();\n\n\t\t// If this was a failed node it could mean that all nodes are now ready\n\t\tthis.checkAllNodesReady();\n\t}\n\n\tprivate onControllerStatusChanged(_status: ControllerStatus): void {\n\t\tthis.triggerQueues();\n\t}\n\n\tprivate async onNetworkFound(\n\t\thomeId: number,\n\t\t_ownNodeId: number,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Joined network with home ID ${\n\t\t\t\t\tnum2hex(homeId)\n\t\t\t\t}, switching to new network cache...`,\n\t\t\t);\n\t\t\tawait this.recreateNetworkCacheAndValueDBs();\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Recreating the network cache and value DBs failed: ${\n\t\t\t\t\tgetErrorMessage(e)\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate onNetworkJoined(): void {\n\t\tthis.driverLog.print(`Finished joining network`);\n\t}\n\n\tprivate async onNetworkLeft(): Promise<void> {\n\t\ttry {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Left the previous network, switching network cache to new home ID ${\n\t\t\t\t\tnum2hex(this.controller.homeId)\n\t\t\t\t}...`,\n\t\t\t);\n\t\t\tawait this.recreateNetworkCacheAndValueDBs();\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Recreating the network cache and value DBs failed: ${\n\t\t\t\t\tgetErrorMessage(e)\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async recreateNetworkCacheAndValueDBs(): Promise<void> {\n\t\tawait this._networkCache?.close();\n\t\tawait this._valueDB?.close();\n\t\tawait this._metadataDB?.close();\n\n\t\t// Reopen with the new home ID\n\t\tawait this.initNetworkCache(this.controller.homeId!);\n\t\tawait this.initValueDBs(this.controller.homeId!);\n\t\tawait this.performCacheMigration();\n\t}\n\n\t/**\n\t * Returns the time in seconds to actually wait after a firmware upgrade, depending on what the device said.\n\t * This number will always be a bit greater than the advertised duration, because devices have been found to take longer to actually reboot.\n\t */\n\tpublic getConservativeWaitTimeAfterFirmwareUpdate(\n\t\tadvertisedWaitTime: number | undefined,\n\t): number {\n\t\t// Wait the specified time plus a bit, so the device is actually ready to use\n\t\tif (!advertisedWaitTime) {\n\t\t\t// Wait at least 5 seconds\n\t\t\treturn 5;\n\t\t} else if (advertisedWaitTime < 20) {\n\t\t\treturn advertisedWaitTime + 5;\n\t\t} else if (advertisedWaitTime < 60) {\n\t\t\treturn advertisedWaitTime + 10;\n\t\t} else {\n\t\t\treturn advertisedWaitTime + 30;\n\t\t}\n\t}\n\n\t/** This is called when the firmware on one of a node's firmware targets was updated */\n\tprivate async onNodeFirmwareUpdated(\n\t\tnode: ZWaveNode,\n\t\tresult: FirmwareUpdateResult,\n\t): Promise<void> {\n\t\tconst { success, reInterview } = result;\n\n\t\t// Nothing to do for non-successful updates\n\t\tif (!success) return;\n\n\t\t// TODO: Add support for delayed activation\n\n\t\t// Reset nonces etc. to prevent false-positive duplicates after the update\n\t\tthis.securityManager?.deleteAllNoncesForReceiver(node.id);\n\t\tthis.securityManager2?.deleteNonce(node.id);\n\t\tthis.securityManagerLR?.deleteNonce(node.id);\n\n\t\t// waitTime should always be defined, but just to be sure\n\t\tconst waitTime = result.waitTime ?? 5;\n\n\t\tif (reInterview) {\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tnode.id,\n\t\t\t\t`Firmware updated, scheduling interview in ${waitTime} seconds...`,\n\t\t\t);\n\t\t\t// We reuse the retryNodeInterviewTimeouts here because they serve a similar purpose\n\t\t\tthis.retryNodeInterviewTimeouts.set(\n\t\t\t\tnode.id,\n\t\t\t\tsetTimer(() => {\n\t\t\t\t\tthis.retryNodeInterviewTimeouts.delete(node.id);\n\t\t\t\t\tvoid node.refreshInfo({\n\t\t\t\t\t\t// After a firmware update, we need to refresh the node info\n\t\t\t\t\t\twaitForWakeup: false,\n\t\t\t\t\t});\n\t\t\t\t}, waitTime * 1000).unref(),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tnode.id,\n\t\t\t\t`Firmware updated. No restart or re-interview required. Refreshing version information in ${waitTime} seconds...`,\n\t\t\t);\n\n\t\t\tawait wait(waitTime * 1000, true);\n\n\t\t\ttry {\n\t\t\t\tconst versionAPI = node.commandClasses.Version;\n\t\t\t\tawait versionAPI.get();\n\t\t\t\tif (\n\t\t\t\t\tversionAPI.supportsCommand(VersionCommand.CapabilitiesGet)\n\t\t\t\t) {\n\t\t\t\t\tawait versionAPI.getCapabilities();\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\tversionAPI.supportsCommand(VersionCommand.ZWaveSoftwareGet)\n\t\t\t\t) {\n\t\t\t\t\tawait versionAPI.getZWaveSoftware();\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// ignore\n\t\t\t}\n\n\t\t\t// No need to keep the node awake longer than necessary\n\t\t\tnode.keepAwake = false;\n\t\t\tthis.debounceSendNodeToSleep(node);\n\t\t}\n\t}\n\n\t/** This is called when a node emits a `\"notification\"` event */\n\tprivate onNodeNotification: ZWaveNotificationCallback = (\n\t\tendpoint,\n\t\tccId,\n\t\tccArgs,\n\t) => {\n\t\tlet prefix: string;\n\t\tlet details: string[];\n\t\tif (ccId === CommandClasses.Notification) {\n\t\t\tconst msg: MessageRecord = {\n\t\t\t\ttype: ccArgs.label,\n\t\t\t\tevent: ccArgs.eventLabel,\n\t\t\t};\n\t\t\tif (ccArgs.parameters) {\n\t\t\t\tif (isUint8Array(ccArgs.parameters)) {\n\t\t\t\t\tmsg.parameters = buffer2hex(ccArgs.parameters);\n\t\t\t\t} else if (Duration.isDuration(ccArgs.parameters)) {\n\t\t\t\t\tmsg.duration = ccArgs.parameters.toString();\n\t\t\t\t} else if (isObject(ccArgs.parameters)) {\n\t\t\t\t\tObject.assign(msg, ccArgs.parameters);\n\t\t\t\t}\n\t\t\t}\n\t\t\tprefix = \"[Notification]\";\n\t\t\tdetails = messageRecordToLines(msg);\n\t\t} else if (ccId === CommandClasses[\"Entry Control\"]) {\n\t\t\tprefix = \"[Notification] Entry Control\";\n\t\t\tdetails = messageRecordToLines({\n\t\t\t\t\"event type\": ccArgs.eventTypeLabel,\n\t\t\t\t\"data type\": ccArgs.dataTypeLabel,\n\t\t\t});\n\t\t} else if (ccId === CommandClasses[\"Multilevel Switch\"]) {\n\t\t\tprefix = \"[Notification] Multilevel Switch\";\n\t\t\tdetails = messageRecordToLines(\n\t\t\t\tstripUndefined({\n\t\t\t\t\t\"event type\": ccArgs.eventTypeLabel,\n\t\t\t\t\tdirection: ccArgs.direction,\n\t\t\t\t}),\n\t\t\t);\n\t\t} /*if (ccId === CommandClasses.Powerlevel)*/ else {\n\t\t\t// Don't bother logging this\n\t\t\treturn;\n\t\t}\n\n\t\tthis.controllerLog.logNode(endpoint.nodeId, {\n\t\t\tendpoint: endpoint.index,\n\t\t\tmessage: [prefix, ...details.map((d) => ` ${d}`)].join(\"\\n\"),\n\t\t});\n\t};\n\n\t/** Checks if there are any pending messages for the given node */\n\tprivate hasPendingMessages(\n\t\tnode: ZWaveNodeBase & NodeSchedulePoll,\n\t): boolean {\n\t\t// First check if there are messages in the queue\n\t\tif (\n\t\t\tthis.hasPendingTransactions(\n\t\t\t\t(t) => t.message.getNodeId() === node.id,\n\t\t\t)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Then check if there are scheduled polls\n\t\treturn node.hasScheduledPolls();\n\t}\n\n\t/** Checks if there are any pending transactions that match the given predicate */\n\tpublic hasPendingTransactions(\n\t\tpredicate: (t: Transaction) => boolean,\n\t): boolean {\n\t\t// Queue is not an array\n\t\t// eslint-disable-next-line unicorn/prefer-array-some\n\t\tif (!!this.queue.find((t) => predicate(t))) return true;\n\t\treturn this.queues.some(\n\t\t\t(q) => q.currentTransaction && predicate(q.currentTransaction),\n\t\t);\n\t}\n\n\t/**\n\t * Retrieves the maximum version of a command class the given endpoint supports.\n\t * Returns 0 when the CC is not supported. Also returns 0 when the node was not found.\n\t * Falls back to querying the root endpoint if an endpoint was not found on the node\n\t *\n\t * @param cc The command class whose version should be retrieved\n\t * @param nodeId The node for which the CC version should be retrieved\n\t * @param endpointIndex The endpoint in question\n\t */\n\tpublic getSupportedCCVersion(\n\t\tcc: CommandClasses,\n\t\tnodeId: number,\n\t\tendpointIndex: number = 0,\n\t): number {\n\t\tif (!this._controller?.nodes.has(nodeId)) {\n\t\t\treturn 0;\n\t\t}\n\t\tconst node = this.controller.nodes.get(nodeId)!;\n\t\tconst endpoint = node.getEndpoint(endpointIndex);\n\t\tif (endpoint) return endpoint.getCCVersion(cc);\n\t\t// We sometimes receive messages from an endpoint, but can't find that endpoint.\n\t\t// In that case fall back to the root endpoint to determine the supported version.\n\t\treturn node.getCCVersion(cc);\n\t}\n\n\t/**\n\t * Retrieves the maximum version of a command class that can be used to communicate with a node.\n\t * Returns the highest implemented version if the node's CC version is unknown.\n\t * Returns `undefined` for CCs that are not implemented in this library yet.\n\t *\n\t * @param cc The command class whose version should be retrieved\n\t * @param nodeId The node for which the CC version should be retrieved\n\t * @param endpointIndex The endpoint for which the CC version should be retrieved\n\t */\n\tpublic getSafeCCVersion(\n\t\tcc: CommandClasses,\n\t\tnodeId: number,\n\t\tendpointIndex: number = 0,\n\t): number | undefined {\n\t\tconst implementedVersion = getImplementedVersion(cc);\n\t\tif (\n\t\t\timplementedVersion === 0\n\t\t\t|| implementedVersion === Number.POSITIVE_INFINITY\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst supportedVersion = this.getSupportedCCVersion(\n\t\t\tcc,\n\t\t\tnodeId,\n\t\t\tendpointIndex,\n\t\t);\n\t\tif (supportedVersion === 0) {\n\t\t\t// Unknown, use the highest implemented version\n\t\t\treturn implementedVersion;\n\t\t}\n\n\t\treturn Math.min(supportedVersion, implementedVersion);\n\t}\n\n\t/**\n\t * Determines whether a CC must be secure for a given node and endpoint.\n\t *\n\t * @param ccId The command class in question\n\t * @param nodeId The node for which the CC security should be determined\n\t * @param endpointIndex The endpoint for which the CC security should be determined\n\t */\n\tisCCSecure(\n\t\tccId: CommandClasses,\n\t\tnodeId: number,\n\t\tendpointIndex: number = 0,\n\t): boolean {\n\t\t// This is obvious\n\t\tif (\n\t\t\tccId === CommandClasses.Security\n\t\t\t|| ccId === CommandClasses[\"Security 2\"]\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst node = this.controller.nodes.get(nodeId);\n\t\t// Node is unknown, don't use secure communication\n\t\tif (!node) return false;\n\n\t\tconst endpoint = node.getEndpoint(endpointIndex);\n\n\t\tconst securityClass = node.getHighestSecurityClass();\n\t\t// Node is not secure, don't use secure communication\n\t\tif (\n\t\t\tsecurityClass === undefined || securityClass === SecurityClass.None\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Special case for Basic CC, which we sometimes hide:\n\t\t// A securely included node MAY support the Basic Command Class at the highest security level but it\n\t\t// MUST NOT support the Basic Command Class at any lower security level or non-securely.\n\t\tconst isBasicCC = ccId === CommandClasses.Basic;\n\n\t\t// Security S2 specs also mandate that all non-securely supported CCs MUST also be supported securely\n\t\t// so we can just shortcut if the node is using S2\n\t\tif (securityClassIsS2(securityClass)) {\n\t\t\t// Use secure communication if the CC is supported. This avoids silly things like S2-encapsulated pings\n\t\t\treturn (\n\t\t\t\t!!this.getSecurityManager2(nodeId)\n\t\t\t\t&& (isBasicCC || (endpoint ?? node).supportsCC(ccId))\n\t\t\t);\n\t\t}\n\n\t\t// Security S0 can be a little more complicated, with secure and non-secure endpoints\n\t\tif (securityClass === SecurityClass.S0_Legacy) {\n\t\t\t// Therefore actually check if the CC is marked as secure\n\t\t\treturn (\n\t\t\t\t!!this.securityManager\n\t\t\t\t&& (isBasicCC || (endpoint ?? node).isCCSecure(ccId))\n\t\t\t);\n\t\t}\n\n\t\t// We shouldn't be here\n\t\treturn false;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications.\n\t * Needed for compatibility with CCAPIHost\n\t */\n\tpublic schedulePoll(\n\t\tnodeId: number,\n\t\tvalueId: ValueID,\n\t\toptions: SchedulePollOptions,\n\t): boolean {\n\t\tconst node = this.controller.nodes.getOrThrow(nodeId);\n\t\treturn node.schedulePoll(valueId, options);\n\t}\n\n\tprivate isSoftResetting: boolean = false;\n\n\tprivate maySoftReset(): boolean {\n\t\t// 700+ series controllers have no problems with soft reset and MUST even be soft reset in some cases\n\t\tif (this._controller?.sdkVersionGt(\"7.0\")) return true;\n\n\t\t// Blacklist some sticks that are known to not support soft reset\n\t\tconst { manufacturerId, productType, productId } = this.controller;\n\n\t\t// Z-Wave.me UZB1\n\t\tif (\n\t\t\tmanufacturerId === 0x0115\n\t\t\t&& productType === 0x0000\n\t\t\t&& productId === 0x0000\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Z-Wave.me UZB\n\t\tif (\n\t\t\tmanufacturerId === 0x0115\n\t\t\t&& productType === 0x0400\n\t\t\t&& productId === 0x0001\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Vision Gen5 USB Stick\n\t\tif (\n\t\t\tmanufacturerId === 0x0109\n\t\t\t&& productType === 0x1001\n\t\t\t&& productId === 0x0201\n\t\t\t// firmware version 15.1 (GH#3730)\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// No clear indication, make the result depend on the config option\n\t\treturn !!this._options.features.softReset;\n\t}\n\n\t/**\n\t * Soft-resets the controller if the feature is enabled\n\t */\n\tpublic async trySoftReset(): Promise<void> {\n\t\tif (this.maySoftReset()) {\n\t\t\tawait this.softReset();\n\t\t} else {\n\t\t\tconst message =\n\t\t\t\t`The controller should not or cannot be soft reset, skipping API call.`;\n\t\t\tthis.controllerLog.print(message, \"warn\");\n\t\t}\n\t}\n\n\t/**\n\t * Instruct the controller to soft-reset.\n\t *\n\t * **Warning:** USB modules will reconnect, meaning that they might get a new address.\n\t *\n\t * **Warning:** This call will throw if soft-reset is not enabled.\n\t */\n\tpublic async softReset(): Promise<void> {\n\t\tif (!this.maySoftReset()) {\n\t\t\tconst message =\n\t\t\t\t`The controller does not support soft reset or the soft reset feature has been disabled with a config option or the ZWAVEJS_DISABLE_SOFT_RESET environment variable.`;\n\t\t\tthis.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.Driver_FeatureDisabled,\n\t\t\t);\n\t\t}\n\n\t\tif (this._controller?.isAnyOTAFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to soft reset controller: A firmware update is in progress on this network.`;\n\t\t\tthis.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\treturn this.softResetInternal(true);\n\t}\n\n\tprivate async softResetInternal(destroyOnError: boolean): Promise<void> {\n\t\tthis.controllerLog.print(\"Performing soft reset...\");\n\n\t\ttry {\n\t\t\tthis.isSoftResetting = true;\n\t\t\tawait this.sendMessage(new SoftResetRequest(), {\n\t\t\t\tsupportCheck: false,\n\t\t\t\tpauseSendThread: true,\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tthis.controllerLog.print(\n\t\t\t\t`Soft reset failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\t// Don't continue if the controller is unresponsive\n\t\t\tif (isMissingControllerACK(e)) {\n\t\t\t\tthis.isSoftResetting = false;\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\n\t\tif (this._controller) {\n\t\t\t// Soft-reset resets the node ID type back to 8 bit\n\t\t\tthis._controller[\"_nodeIdType\"] = NodeIDType.Short;\n\n\t\t\t// Soft-resetting disables any ongoing inclusion, so we need to reset\n\t\t\t// the state that is tracked in the controller\n\t\t\tthis._controller.setInclusionState(InclusionState.Idle);\n\t\t}\n\n\t\t// Make sure we're able to communicate with the controller again\n\t\tif (!(await this.ensureSerialAPI())) {\n\t\t\tif (destroyOnError) {\n\t\t\t\tawait this.destroy();\n\t\t\t} else {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The Serial API did not respond after soft-reset\",\n\t\t\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthis.isSoftResetting = false;\n\n\t\t// This is a bit hacky, but what the heck...\n\t\tif (!this._enteringBootloader) {\n\t\t\t// Start the watchdog again, unless disabled\n\t\t\tif (this.options.features.watchdog) {\n\t\t\t\tvoid this._controller?.startWatchdog();\n\t\t\t}\n\n\t\t\t// If desired, re-configure the controller to use 16 bit node IDs\n\t\t\tvoid this._controller?.trySetNodeIDType(NodeIDType.Long);\n\n\t\t\t// Resume sending\n\t\t\tthis.unpauseSendQueue();\n\t\t}\n\t}\n\n\t/** Soft-reset the Z-Wave module and restart the driver instance */\n\tpublic async softResetAndRestart(): Promise<void> {\n\t\tthis.controllerLog.print(\"Performing soft reset...\");\n\n\t\ttry {\n\t\t\tthis.isSoftResetting = true;\n\t\t\tawait this.sendMessage(new SoftResetRequest(), {\n\t\t\t\tsupportCheck: false,\n\t\t\t\tpauseSendThread: true,\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tthis.controllerLog.print(\n\t\t\t\t`Soft reset failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\n\t\t// Make sure we're able to communicate with the controller again\n\t\tif (!(await this.ensureSerialAPI())) {\n\t\t\tawait this.destroyWithMessage(\n\t\t\t\t\"The Serial API did not respond after soft-reset\",\n\t\t\t);\n\t\t}\n\n\t\tthis.isSoftResetting = false;\n\n\t\t// Clean up and interview the controller again\n\t\tawait this.destroyController();\n\t\tvoid this.initializeControllerAndNodes();\n\t}\n\n\t/**\n\t * Checks whether recovering an unresponsive controller is enabled\n\t * and whether the driver is in a state where it makes sense.\n\t */\n\tprivate mayRecoverUnresponsiveController(): boolean {\n\t\tif (!this._options.features.unresponsiveControllerRecovery) {\n\t\t\treturn false;\n\t\t}\n\t\t// Only recover after we know the controller has been responsive\n\t\treturn this._controllerInterviewed;\n\t}\n\n\tprivate async ensureSerialAPI(): Promise<boolean> {\n\t\t// Wait 1.5 seconds after reset to ensure that the module is ready for communication again\n\t\t// Z-Wave 700 sticks are relatively fast, so we also wait for the Serial API started command\n\t\t// to bail early\n\t\tthis.controllerLog.print(\"Waiting for the controller to reconnect...\");\n\t\tlet waitResult = await this.waitForMessage<SerialAPIStartedRequest>(\n\t\t\t(msg) => msg.functionType === FunctionType.SerialAPIStarted,\n\t\t\t1500,\n\t\t).catch(() => false as const);\n\n\t\tif (waitResult) {\n\t\t\t// Serial API did start\n\t\t\tthis.controllerLog.print(\"reconnected and restarted\");\n\t\t\tif (this._controller) {\n\t\t\t\tthis._controller[\"_supportsLongRange\"] =\n\t\t\t\t\twaitResult.supportsLongRange;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t// If the controller disconnected the serial port during the soft reset, we need to re-open it\n\t\tif (!this.serial!.isOpen) {\n\t\t\tthis.controllerLog.print(\"Re-opening serial port...\");\n\t\t\ttry {\n\t\t\t\tawait this.openSerialport();\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// Wait the configured amount of time for the Serial API started command to be received\n\t\tthis.controllerLog.print(\"Waiting for the Serial API to start...\");\n\t\twaitResult = await this.waitForMessage<SerialAPIStartedRequest>(\n\t\t\t(msg) => {\n\t\t\t\treturn msg.functionType === FunctionType.SerialAPIStarted;\n\t\t\t},\n\t\t\tthis._options.timeouts.serialAPIStarted,\n\t\t).catch(() => false as const);\n\n\t\tif (waitResult) {\n\t\t\t// Serial API did start, maybe do something with the information?\n\t\t\tthis.controllerLog.print(\"Serial API started\");\n\t\t\tif (this._controller) {\n\t\t\t\tthis._controller[\"_supportsLongRange\"] =\n\t\t\t\t\twaitResult.supportsLongRange;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tthis.controllerLog.print(\n\t\t\t\"Did not receive notification that Serial API has started, checking if it responds...\",\n\t\t);\n\n\t\t// We don't need to use any specific command here. However we're going to use this one in the interview\n\t\t// anyways, so we might aswell use it here too\n\t\tconst pollController = async () => {\n\t\t\ttry {\n\t\t\t\t// And resume sending - this requires us to unpause the send thread\n\t\t\t\tthis.unpauseSendQueue();\n\t\t\t\tawait this.sendMessage(new GetControllerVersionRequest(), {\n\t\t\t\t\tsupportCheck: false,\n\t\t\t\t\tpriority: MessagePriority.ControllerImmediate,\n\t\t\t\t});\n\t\t\t\tthis.pauseSendQueue();\n\t\t\t\tthis.controllerLog.print(\"Serial API responded\");\n\t\t\t\treturn true;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\t\t// Poll the controller with increasing backoff delay\n\t\tif (await pollController()) return true;\n\t\tfor (const backoff of [2, 5, 10, 15]) {\n\t\t\tthis.controllerLog.print(\n\t\t\t\t`Serial API did not respond, trying again in ${backoff} seconds...`,\n\t\t\t);\n\t\t\tawait wait(backoff * 1000);\n\t\t\tif (await pollController()) return true;\n\t\t}\n\n\t\tthis.controllerLog.print(\n\t\t\t\"Serial API did not respond, giving up\",\n\t\t\t\"error\",\n\t\t);\n\t\treturn false;\n\t}\n\n\tprivate _ensureCLIReadyPromise: DeferredPromise<boolean> | undefined;\n\tprivate async ensureCLIReady(): Promise<boolean> {\n\t\t// Ensure this is only called once and all subsequent calls block\n\t\tif (this._ensureCLIReadyPromise) return this._ensureCLIReadyPromise;\n\t\tthis._ensureCLIReadyPromise = createDeferredPromise();\n\n\t\t// Try to detect the available CLI commands and wait long enough for the communication to succeed\n\t\t// Wait 1.5 seconds after reset to ensure that the module is ready for communication again\n\t\t// Z-Wave 700 sticks are relatively fast, so we also wait for the Serial API started command\n\t\t// to bail early\n\t\tthis.controllerLog.print(\"Waiting for the CLI to be ready...\");\n\n\t\t// After booting, the CLI can take a while to respond to commands\n\t\t// Try up to 3 times to detect the available commands\n\t\tawait wait(250);\n\t\tfor (let i = 0;; i++) {\n\t\t\ttry {\n\t\t\t\tawait this.cli.detectCommands();\n\t\t\t\tthis.controllerLog.print(\"CLI started\");\n\t\t\t\tthis._ensureCLIReadyPromise?.resolve(true);\n\t\t\t\tthis._ensureCLIReadyPromise = undefined;\n\t\t\t\treturn true;\n\t\t\t} catch {\n\t\t\t\tif (i === 2) {\n\t\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t\t\"CLI did not respond, giving up\",\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._ensureCLIReadyPromise?.resolve(false);\n\t\t\t\t\tthis._ensureCLIReadyPromise = undefined;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tawait wait(1000);\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 *\n\t * The returned Promise resolves when the hard reset has been performed.\n\t * It does not wait for the initialization process which is started afterwards.\n\t */\n\tpublic async hardReset(): Promise<void> {\n\t\tthis.ensureReady(true);\n\n\t\tif (this.controller.isAnyOTAFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to hard reset controller: A firmware update is in progress on this network.`;\n\t\t\tthis.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\t// Preserve the private key for the authenticated learn mode ECDH key pair\n\t\tconst oldPrivateKey = this.cacheGet<Uint8Array>(\n\t\t\tcacheKeys.controller.privateKey,\n\t\t);\n\n\t\t// Drop all scheduled tasks - they don't make sense after a hard reset\n\t\tawait this.scheduler.removeTasks(\n\t\t\t() => true,\n\t\t\tnew ZWaveError(\n\t\t\t\t\"The controller is being hard-reset\",\n\t\t\t\tZWaveErrorCodes.Driver_TaskRemoved,\n\t\t\t),\n\t\t);\n\n\t\t// Update the controller NIF prior to hard resetting\n\t\tawait this.controller.setControllerNIF();\n\t\tawait this.controller.hardReset();\n\n\t\t// Clean up\n\t\tawait this.destroyController();\n\t\tvoid this.initializeControllerAndNodes();\n\n\t\t// Save the key pair in the new cache again\n\t\tif (oldPrivateKey) {\n\t\t\tthis.once(\"driver ready\", () => {\n\t\t\t\tthis.cacheSet(cacheKeys.controller.privateKey, oldPrivateKey);\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Instructs the Z-Wave API to shut down in order to safely remove the power.\n\t * This will destroy the driver instance if it succeeds.\n\t */\n\tpublic async shutdown(): Promise<boolean> {\n\t\tthis.ensureReady(true);\n\n\t\t// Not a good idea to abort firmware updates this way\n\t\tif (this.controller.isAnyOTAFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to shut down controller: A firmware update is in progress on this network.`;\n\t\t\tthis.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 result = await this.controller.shutdown();\n\t\ttry {\n\t\t\tif (result) await this.destroy();\n\t\t} finally {\n\t\t\treturn result;\n\t\t}\n\t}\n\n\tprivate _destroyPromise: DeferredPromise<void> | undefined;\n\tprivate get wasDestroyed(): boolean {\n\t\treturn !!this._destroyPromise;\n\t}\n\n\t/**\n\t * Ensures that the driver is ready to communicate (serial port open and not destroyed).\n\t * If desired, also checks that the controller interview has been completed.\n\t */\n\tprivate ensureReady(includingController: boolean = false): void {\n\t\tif (!this._wasStarted || !this._isOpen || this.wasDestroyed) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The driver is not ready or has been destroyed\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\tif (includingController && !this._controllerInterviewed) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The controller is not ready yet\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\tif (this._bootloader) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Cannot do this while in bootloader mode\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Indicates whether the driver is ready, i.e. the \"driver ready\" event was emitted */\n\tpublic get ready(): boolean {\n\t\treturn (\n\t\t\tthis._wasStarted\n\t\t\t&& this._isOpen\n\t\t\t&& !this.wasDestroyed\n\t\t\t&& this._controllerInterviewed\n\t\t);\n\t}\n\n\tprivate async destroyWithMessage(message: string): Promise<void> {\n\t\tthis.driverLog.print(message, \"error\");\n\n\t\tconst error = new ZWaveError(\n\t\t\tmessage,\n\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t);\n\t\tthis.emit(\"error\", error);\n\n\t\tawait this.destroy();\n\t}\n\n\t/**\n\t * Terminates the driver instance and closes the underlying serial connection.\n\t * Must be called under any circumstances.\n\t */\n\tpublic async destroy(): Promise<void> {\n\t\t// Ensure this is only called once and all subsequent calls block\n\t\tif (this._destroyPromise) return this._destroyPromise;\n\t\tthis._destroyPromise = createDeferredPromise();\n\n\t\tthis.driverLog.print(\"destroying driver instance...\");\n\n\t\t// First stop the scheduler, all queues and close the serial port, so nothing happens anymore\n\t\tawait this._scheduler.stop();\n\n\t\tawait this.destroyTransactionQueues(\n\t\t\t\"driver instance destroyed\",\n\t\t\tZWaveErrorCodes.Driver_Destroyed,\n\t\t);\n\n\t\tthis.destroySerialAPIQueue(\n\t\t\t\"driver instance destroyed\",\n\t\t\tZWaveErrorCodes.Driver_Destroyed,\n\t\t);\n\n\t\tif (this.serial != undefined) {\n\t\t\t// Avoid spewing errors if the port was in the middle of receiving something\n\t\t\tif (this.serial.isOpen) await this.serial.close();\n\t\t\tthis.serial = undefined;\n\t\t}\n\n\t\tawait this.destroyController();\n\n\t\tthis.driverLog.print(`driver instance destroyed`);\n\n\t\t// destroy loggers as the very last thing\n\t\tthis._logContainer.destroy();\n\n\t\tthis._destroyPromise.resolve();\n\t}\n\n\t/** Cleanly destroy the controller instance, but not the entire driver */\n\t// FIXME: Too much overlap with destroy()\n\tprivate async destroyController(): Promise<void> {\n\t\t// Avoid re-transmissions etc. communicating with other applications\n\t\t// or the bootloader\n\t\tawait this.scheduler.removeTasks(\n\t\t\t() => true,\n\t\t\tnew ZWaveError(\n\t\t\t\t\"The controller instance is being destroyed\",\n\t\t\t\tZWaveErrorCodes.Driver_TaskRemoved,\n\t\t\t),\n\t\t);\n\n\t\tawait this.destroyTransactionQueues(\n\t\t\t\"The controller instance is being destroyed\",\n\t\t\tZWaveErrorCodes.Driver_TaskRemoved,\n\t\t);\n\n\t\tthis.destroySerialAPIQueue(\n\t\t\t\"The controller instance is being destroyed\",\n\t\t\tZWaveErrorCodes.Driver_TaskRemoved,\n\t\t);\n\n\t\tthis.requestHandlers.clear();\n\n\t\t// Attempt to close the value DBs and network cache\n\t\tawait this.closeDatabases();\n\n\t\t// Remove all timeouts\n\t\tthis.clearAllTimeouts();\n\n\t\t// Destroy all nodes and the controller\n\t\tif (this._controller) {\n\t\t\tthis._controller.destroy();\n\t\t\tthis._controller = undefined;\n\t\t}\n\n\t\tthis._controllerInterviewed = false;\n\t\tthis._nodesReady.clear();\n\t\tthis._nodesReadyEventEmitted = false;\n\t}\n\n\tprivate async closeDatabases(): Promise<void> {\n\t\ttry {\n\t\t\tawait this._valueDB?.close();\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Closing the value DB failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\tawait this._metadataDB?.close();\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Closing the metadata DB failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\tawait this._networkCache?.close();\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Closing the network cache failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate clearAllTimeouts() {\n\t\tfor (\n\t\t\tconst timeout of [\n\t\t\t\tthis._powerlevelTestNodeContext?.timeout,\n\t\t\t]\n\t\t) {\n\t\t\tif (timeout) clearTimeout(timeout);\n\t\t}\n\t\tfor (\n\t\t\tconst timeout of [\n\t\t\t\t...this.retryNodeInterviewTimeouts.values(),\n\t\t\t\t...this.autoRefreshNodeValueTimers.values(),\n\t\t\t\tthis.statisticsTimeout,\n\t\t\t\tthis.pollBackgroundRSSITimer,\n\t\t\t\t...this.sendNodeToSleepTimers.values(),\n\t\t\t\t...this.awaitedCommands.map((c) => c.timeout),\n\t\t\t\t...this.awaitedMessages.map((m) => m.timeout),\n\t\t\t\t...this.awaitedMessageHeaders.map((h) => h.timeout),\n\t\t\t\t...this.awaitedBootloaderChunks.map((b) => b.timeout),\n\t\t\t\t...this.awaitedCLIChunks.map((c) => c.timeout),\n\t\t\t]\n\t\t) {\n\t\t\ttimeout?.clear();\n\t\t}\n\t}\n\n\tprivate async handleSerialData(serial: ZWaveSerialStream): Promise<void> {\n\t\ttry {\n\t\t\tfor await (const frame of serial.readable) {\n\t\t\t\tsetImmediate(() => {\n\t\t\t\t\tif (frame.type === ZWaveSerialFrameType.SerialAPI) {\n\t\t\t\t\t\tvoid this.serialport_onData(frame.data);\n\t\t\t\t\t} else if (frame.type === ZWaveSerialFrameType.Bootloader) {\n\t\t\t\t\t\tvoid this.serialport_onBootloaderData(frame.data);\n\t\t\t\t\t} else if (frame.type === ZWaveSerialFrameType.CLI) {\n\t\t\t\t\t\tvoid this.serialport_onCLIData(frame.data);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Handle discarded data?\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (isAbortError(e)) {\n\t\t\t\treturn;\n\t\t\t} else if (\n\t\t\t\tisZWaveError(e) && e.code === ZWaveErrorCodes.Driver_Failed\n\t\t\t) {\n\t\t\t\t// A disconnection while soft resetting is to be expected.\n\t\t\t\t// The soft reset method will handle reopening\n\t\t\t\tif (this.isSoftResetting || this._isOpeningSerialPort) return;\n\n\t\t\t\tvoid this.destroyWithMessage(e.message);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Is called when the serial port has received a single-byte message or a complete message buffer\n\t */\n\tprivate async serialport_onData(\n\t\tdata:\n\t\t\t| Uint8Array\n\t\t\t| MessageHeaders.ACK\n\t\t\t| MessageHeaders.CAN\n\t\t\t| MessageHeaders.NAK,\n\t): Promise<void> {\n\t\tif (typeof data === \"number\") {\n\t\t\tswitch (data) {\n\t\t\t\tcase MessageHeaders.ACK:\n\t\t\t\tcase MessageHeaders.NAK:\n\t\t\t\tcase MessageHeaders.CAN: {\n\t\t\t\t\t// check if someone is waiting for this\n\t\t\t\t\tfor (const entry of this.awaitedMessageHeaders) {\n\t\t\t\t\t\tif (entry.predicate(data)) {\n\t\t\t\t\t\t\tentry.handler(data);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._cli = undefined;\n\t\tthis._bootloader = undefined;\n\n\t\tlet msg: Message | undefined;\n\t\ttry {\n\t\t\t// Parse the message while remembering potential decoding errors in embedded CCs\n\t\t\t// This way we can log the invalid CC contents\n\t\t\tmsg = Message.parse(\n\t\t\t\tdata,\n\t\t\t\tthis.getMessageParsingContext(),\n\t\t\t);\n\n\t\t\t// Parse embedded CCs\n\t\t\tif (isCommandRequest(msg) && containsSerializedCC(msg)) {\n\t\t\t\tmsg.command = await CommandClass.parse(\n\t\t\t\t\tmsg.serializedCC,\n\t\t\t\t\t{\n\t\t\t\t\t\t...this.getCCParsingContext(),\n\t\t\t\t\t\tsourceNodeId: msg.getNodeId()!,\n\t\t\t\t\t\tframeType: msg.frameType,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\t// Whether successful or not, a message from a node should update last seen\n\t\t\t\tconst node = this.tryGetNode(msg);\n\t\t\t\tif (node) node.lastSeen = new Date();\n\n\t\t\t\t// Ensure there are no errors\n\t\t\t\tassertValidCCs(msg as ContainsCC);\n\t\t\t}\n\t\t\t// And update statistics\n\t\t\tif (!!this._controller) {\n\t\t\t\tif (containsCC(msg)) {\n\t\t\t\t\tthis.tryGetNode(msg)?.incrementStatistics(\"commandsRX\");\n\t\t\t\t} else {\n\t\t\t\t\tthis._controller.incrementStatistics(\"messagesRX\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t// all good, send ACK\n\t\t\tawait this.writeHeader(MessageHeaders.ACK);\n\t\t} catch (e: any) {\n\t\t\ttry {\n\t\t\t\tif (await this.handleSecurityS2DecodeError(e, msg)) {\n\t\t\t\t\t// TODO\n\t\t\t\t} else {\n\t\t\t\t\tconst response = this.handleDecodeError(e, data, msg);\n\t\t\t\t\tif (response) await this.writeHeader(response);\n\t\t\t\t\tif (!!this._controller) {\n\t\t\t\t\t\tif (containsCC(msg)) {\n\t\t\t\t\t\t\tthis.tryGetNode(msg)?.incrementStatistics(\n\t\t\t\t\t\t\t\t\"commandsDroppedRX\",\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t// Figure out if the command was received with supervision encapsulation\n\t\t\t\t\t\t\tconst supervisionSessionId = SupervisionCC\n\t\t\t\t\t\t\t\t.getSessionId(msg.command);\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tsupervisionSessionId !== undefined\n\t\t\t\t\t\t\t\t&& msg.command instanceof InvalidCC\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// If it was, we need to notify the sender that we couldn't decode the command\n\t\t\t\t\t\t\t\tconst node = this.tryGetNode(msg);\n\t\t\t\t\t\t\t\tif (node) {\n\t\t\t\t\t\t\t\t\tconst endpoint = node.getEndpoint(\n\t\t\t\t\t\t\t\t\t\tmsg.command.endpointIndex,\n\t\t\t\t\t\t\t\t\t) ?? node;\n\t\t\t\t\t\t\t\t\tconst encapsulationFlags =\n\t\t\t\t\t\t\t\t\t\tmsg.command.encapsulationFlags;\n\t\t\t\t\t\t\t\t\tawait endpoint\n\t\t\t\t\t\t\t\t\t\t.createAPI(\n\t\t\t\t\t\t\t\t\t\t\tCommandClasses.Supervision,\n\t\t\t\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t.sendReport({\n\t\t\t\t\t\t\t\t\t\t\tsessionId: supervisionSessionId,\n\t\t\t\t\t\t\t\t\t\t\tmoreUpdatesFollow: false,\n\t\t\t\t\t\t\t\t\t\t\tstatus: SupervisionStatus.NoSupport,\n\t\t\t\t\t\t\t\t\t\t\trequestWakeUpOnDemand: this\n\t\t\t\t\t\t\t\t\t\t\t\t.shouldRequestWakeupOnDemand(\n\t\t\t\t\t\t\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\tencapsulationFlags,\n\t\t\t\t\t\t\t\t\t\t\tlowPriority: this\n\t\t\t\t\t\t\t\t\t\t\t\t.shouldUseLowPriorityForSupervisionReport(\n\t\t\t\t\t\t\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\t\t\t\t\t\t\tencapsulationFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\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\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis._controller.incrementStatistics(\n\t\t\t\t\t\t\t\t\"messagesDroppedRX\",\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\t\t\t} catch (ee) {\n\t\t\t\tif (ee instanceof Error) {\n\t\t\t\t\tif (/serial port is not open/.test(ee.message)) {\n\t\t\t\t\t\tthis.emit(\"error\", ee);\n\t\t\t\t\t\tvoid this.destroy();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// Print something, so we know what is wrong\n\t\t\t\t\tthis._driverLog.print(ee.stack ?? ee.message, \"error\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Don't keep handling the message\n\t\t\tmsg = undefined;\n\t\t}\n\n\t\t// If we receive a CC from a node while the controller is not ready yet,\n\t\t// we can't do anything with it, but logging it may assume that it can access the controller.\n\t\t// To prevent this problem, we just ignore CCs until the controller is ready\n\t\tif (!this._controller && containsCC(msg)) return;\n\n\t\t// If the message could be decoded, forward it to the send thread\n\t\tif (msg) {\n\t\t\tlet wasMessageLogged = false;\n\t\t\tif (isCommandRequest(msg) && containsCC(msg)) {\n\t\t\t\t// SecurityCCCommandEncapsulationNonceGet is two commands in one, but\n\t\t\t\t// we're not set up to handle things like this. Reply to the nonce get\n\t\t\t\t// and handle the encapsulation part normally\n\t\t\t\tif (\n\t\t\t\t\tmsg.command\n\t\t\t\t\t\tinstanceof SecurityCCCommandEncapsulationNonceGet\n\t\t\t\t) {\n\t\t\t\t\tconst node = this.tryGetNode(msg);\n\t\t\t\t\tif (node) {\n\t\t\t\t\t\tvoid this.handleSecurityNonceGet(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Transport Service commands must be handled before assembling partial CCs\n\t\t\t\tif (isTransportServiceEncapsulation(msg.command)) {\n\t\t\t\t\t// Log Transport Service commands before doing anything else\n\t\t\t\t\tthis.driverLog.logMessage(msg, {\n\t\t\t\t\t\tsecondaryTags: [\"partial\"],\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t});\n\t\t\t\t\twasMessageLogged = true;\n\n\t\t\t\t\tvoid this.handleTransportServiceCommand(msg.command).catch(\n\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t// Don't care about errors in incoming transport service commands\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Assemble partial CCs on the driver level. Only forward complete messages to the send thread machine\n\t\t\t\tif (!(await this.assemblePartialCCs(msg))) {\n\t\t\t\t\t// Check if a message timer needs to be refreshed.\n\t\t\t\t\tfor (const entry of this.awaitedMessages) {\n\t\t\t\t\t\tif (entry.refreshPredicate?.(msg)) {\n\t\t\t\t\t\t\tentry.timeout?.refresh();\n\t\t\t\t\t\t\t// Since this is a partial message there may be no clear 1:1 match.\n\t\t\t\t\t\t\t// Therefore we loop through all awaited messages\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Make sure we are allowed to handle this command\n\t\t\t\tif (\n\t\t\t\t\tthis.isSecurityLevelTooLow(msg.command)\n\t\t\t\t\t|| this.shouldDiscardCC(msg.command)\n\t\t\t\t) {\n\t\t\t\t\tif (!wasMessageLogged) {\n\t\t\t\t\t\tthis.driverLog.logMessage(msg, {\n\t\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\t\tsecondaryTags: [\"discarded\"],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// When we have a complete CC, save its values\n\t\t\t\ttry {\n\t\t\t\t\tthis.persistCCValues(msg.command);\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// Indicate invalid payloads with a special CC type\n\t\t\t\t\tif (\n\t\t\t\t\t\tisZWaveError(e)\n\t\t\t\t\t\t&& e.code\n\t\t\t\t\t\t\t=== ZWaveErrorCodes.PacketFormat_InvalidPayload\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t`dropping CC with invalid values${\n\t\t\t\t\t\t\t\ttypeof e.context === \"string\"\n\t\t\t\t\t\t\t\t\t? ` (Reason: ${e.context})`\n\t\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\t// TODO: We may need to do the S2 MOS dance here - or we can deal with it when the next valid CC arrives\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Transport Service CC can be eliminated from the encapsulation stack, since it is always the outermost CC\n\t\t\t\tif (isTransportServiceEncapsulation(msg.command)) {\n\t\t\t\t\tmsg.command = msg.command.encapsulated;\n\t\t\t\t\t// Now we do want to log the command again, so we can see what was inside\n\t\t\t\t\twasMessageLogged = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!wasMessageLogged) {\n\t\t\t\ttry {\n\t\t\t\t\tthis.driverLog.logMessage(msg, {\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t});\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// We shouldn't throw just because logging a message fails\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Logging a message failed: ${getErrorMessage(e)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// // Check if this message is unsolicited by passing it to the Serial API command interpreter if possible\n\t\t\t// if (\n\t\t\t// \tthis.serialAPIInterpreter?.status === InterpreterStatus.Running\n\t\t\t// ) {\n\t\t\t// \tthis.serialAPIInterpreter.send({\n\t\t\t// \t\ttype: \"message\",\n\t\t\t// \t\tmessage: msg,\n\t\t\t// \t});\n\t\t\t// } else {\n\t\t\tvoid this.handleUnsolicitedMessage(msg);\n\t\t\t// }\n\t\t}\n\t}\n\n\t/** Handles a decoding error and returns the desired reply to the stick */\n\tprivate handleDecodeError(\n\t\te: Error,\n\t\tdata: Uint8Array,\n\t\tmsg: Message | undefined,\n\t): MessageHeaders | undefined {\n\t\tif (isZWaveError(e)) {\n\t\t\tswitch (e.code) {\n\t\t\t\tcase ZWaveErrorCodes.PacketFormat_Invalid:\n\t\t\t\tcase ZWaveErrorCodes.PacketFormat_Checksum:\n\t\t\t\tcase ZWaveErrorCodes.PacketFormat_Truncated:\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Dropping message because it contains invalid data`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\treturn MessageHeaders.NAK;\n\n\t\t\t\tcase ZWaveErrorCodes.Deserialization_NotImplemented:\n\t\t\t\tcase ZWaveErrorCodes.CC_NotImplemented:\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Dropping message because it could not be deserialized: ${e.message}`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\treturn MessageHeaders.ACK;\n\n\t\t\t\tcase ZWaveErrorCodes.Driver_NotReady:\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Dropping message because the driver is not ready to handle it yet.`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\treturn MessageHeaders.ACK;\n\n\t\t\t\tcase ZWaveErrorCodes.PacketFormat_InvalidPayload:\n\t\t\t\t\tif (msg) {\n\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t`Dropping message with invalid payload`,\n\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tthis.driverLog.logMessage(msg, {\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} catch (e) {\n\t\t\t\t\t\t\t// We shouldn't throw just because logging a message fails\n\t\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\t`Logging a message failed: ${\n\t\t\t\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\t\t\t\te,\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}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t`Dropping message with invalid payload${\n\t\t\t\t\t\t\t\ttypeof e.context === \"string\"\n\t\t\t\t\t\t\t\t\t? ` (Reason: ${e.context})`\n\t\t\t\t\t\t\t\t\t: \"\"\n\t\t\t\t\t\t\t}:\\n${buffer2hex(data)}`,\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\treturn MessageHeaders.ACK;\n\n\t\t\t\tcase ZWaveErrorCodes.Driver_NoSecurity:\n\t\t\t\tcase ZWaveErrorCodes.Security2CC_NotInitialized:\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Dropping message because network keys are not set or the driver is not yet ready to receive secure messages.`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\treturn MessageHeaders.ACK;\n\n\t\t\t\tcase ZWaveErrorCodes.Controller_NodeNotFound:\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Dropping message because ${\n\t\t\t\t\t\t\ttypeof e.context === \"number\"\n\t\t\t\t\t\t\t\t? `node ${e.context}`\n\t\t\t\t\t\t\t\t: \"the node\"\n\t\t\t\t\t\t} does not exist.`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\treturn MessageHeaders.ACK;\n\t\t\t}\n\t\t} else {\n\t\t\tif (/database is not open/.test(e.message)) {\n\t\t\t\t// The JSONL-DB is not open yet\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t`Dropping message because the driver is not ready to handle it yet.`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\treturn MessageHeaders.ACK;\n\t\t\t}\n\t\t}\n\t\t// Pass all other errors through\n\t\tthrow e;\n\t}\n\n\tprivate mustReplyWithSecurityS2MOS(\n\t\tmsg: ContainsCC & CommandRequest,\n\t): boolean {\n\t\t// We're looking for a singlecast S2-encapsulated request\n\t\tif (msg.frameType !== \"singlecast\") return false;\n\t\tconst encapS2 = msg.command.getEncapsulatingCC(\n\t\t\tCommandClasses[\"Security 2\"],\n\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t) as Security2CCMessageEncapsulation | undefined;\n\t\tif (!encapS2) return false;\n\n\t\t// With the MGRP extension present\n\t\tconst node = this.tryGetNode(msg);\n\t\tif (!node) return false;\n\t\tconst groupId = encapS2.getMulticastGroupId();\n\t\tif (groupId == undefined) return false;\n\t\tconst securityManager = this.getSecurityManager2(node.id);\n\t\tif (\n\t\t\t// but where we don't have an MPAN stored\n\t\t\tsecurityManager?.getPeerMPAN(\n\t\t\t\tmsg.command.nodeId as number,\n\t\t\t\tgroupId,\n\t\t\t).type !== MPANState.MPAN\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate async handleSecurityS2DecodeError(\n\t\te: Error,\n\t\tmsg: Message | undefined,\n\t): Promise<boolean> {\n\t\tif (!isZWaveError(e)) return false;\n\t\tif (\n\t\t\t(e.code === ZWaveErrorCodes.Security2CC_NoSPAN\n\t\t\t\t|| e.code === ZWaveErrorCodes.Security2CC_CannotDecode)\n\t\t\t&& containsCC(msg)\n\t\t) {\n\t\t\t// Decoding the command failed because no SPAN has been established with the other node\n\t\t\tconst nodeId = msg.getNodeId()!;\n\t\t\t// If the node isn't known, ignore this error\n\t\t\tconst node = this._controller?.nodes.get(nodeId);\n\t\t\tif (!node) return false;\n\n\t\t\t// Before we can send anything, ACK the command\n\t\t\tawait this.writeHeader(MessageHeaders.ACK);\n\n\t\t\tthis.driverLog.logMessage(msg, { direction: \"inbound\" });\n\t\t\tnode.incrementStatistics(\"commandsDroppedRX\");\n\n\t\t\t// We might receive this before the node has been interviewed. If that case, we need to mark Security S2 as\n\t\t\t// supported or we won't ever be able to communicate with the node\n\t\t\tif (node.interviewStage < InterviewStage.NodeInfo) {\n\t\t\t\tnode.addCC(CommandClasses[\"Security 2\"], {\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// Ensure that we're not flooding the queue with unnecessary NonceReports\n\t\t\tconst isS2NonceReport = (t: Transaction) =>\n\t\t\t\tt.message.getNodeId() === nodeId\n\t\t\t\t&& containsCC(t.message)\n\t\t\t\t&& t.message.command instanceof Security2CCNonceReport;\n\n\t\t\tconst message: string =\n\t\t\t\te.code === ZWaveErrorCodes.Security2CC_CannotDecode\n\t\t\t\t\t? \"Message authentication failed\"\n\t\t\t\t\t: \"No SPAN is established yet\";\n\n\t\t\tif (this.controller.bootstrappingS2NodeId === nodeId) {\n\t\t\t\t// The node is currently being bootstrapped.\n\t\t\t\tconst securityManager = this.getSecurityManager2(nodeId);\n\t\t\t\tif (securityManager?.tempKeys.has(nodeId)) {\n\t\t\t\t\t// The DSK has been verified, so we should be able to decode this command.\n\t\t\t\t\t// If this is the first attempt, we need to request a nonce first\n\t\t\t\t\tif (\n\t\t\t\t\t\tsecurityManager.getSPANState(nodeId).type\n\t\t\t\t\t\t\t=== SPANState.None\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`${message}, cannot decode command. Requesting a nonce...`,\n\t\t\t\t\t\t\tlevel: \"verbose\",\n\t\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Send the node our nonce\n\t\t\t\t\t\tnode.commandClasses[\"Security 2\"]\n\t\t\t\t\t\t\t.sendNonce()\n\t\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\t\t// Ignore errors\n\t\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Us repeatedly not being able to decode the command means we need to abort the bootstrapping process\n\t\t\t\t\t\t// because the PIN is wrong\n\t\t\t\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`${message}, cannot decode command. Aborting the S2 bootstrapping process...`,\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tthis.controller.cancelSecureBootstrapS2(\n\t\t\t\t\t\t\tKEXFailType.BootstrappingCanceled,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Ignoring KEXSet because the DSK has not been verified yet`,\n\t\t\t\t\t\tlevel: \"verbose\",\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (!this.hasPendingTransactions(isS2NonceReport)) {\n\t\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`${message}, cannot decode command. Requesting a nonce...`,\n\t\t\t\t\tlevel: \"verbose\",\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t});\n\t\t\t\t// Send the node our nonce, and use the chance to re-sync the MPAN if necessary\n\t\t\t\tconst s2MulticastOutOfSync = isCommandRequest(msg)\n\t\t\t\t\t&& this.mustReplyWithSecurityS2MOS(msg);\n\n\t\t\t\tnode.commandClasses[\"Security 2\"]\n\t\t\t\t\t.withOptions({ s2MulticastOutOfSync })\n\t\t\t\t\t.sendNonce()\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t// Ignore errors\n\t\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage: `${message}, cannot decode command.`,\n\t\t\t\t\tlevel: \"verbose\",\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} else if (\n\t\t\t(e.code === ZWaveErrorCodes.Security2CC_NoMPAN\n\t\t\t\t|| e.code === ZWaveErrorCodes.Security2CC_CannotDecodeMulticast)\n\t\t\t&& containsCC(msg)\n\t\t) {\n\t\t\t// Decoding the command failed because the MPAN used by the other node\n\t\t\t// is not known to us yet\n\t\t\tconst nodeId = msg.getNodeId()!;\n\t\t\t// If the node isn't known, ignore this error\n\t\t\tconst node = this._controller?.nodes.get(nodeId);\n\t\t\tif (!node) return false;\n\n\t\t\t// Before we can send anything, ACK the command\n\t\t\tawait this.writeHeader(MessageHeaders.ACK);\n\n\t\t\tthis.driverLog.logMessage(msg, { direction: \"inbound\" });\n\t\t\tnode.incrementStatistics(\"commandsDroppedRX\");\n\n\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Cannot decode S2 multicast command, since MPAN is not known yet. Will attempt re-sync after the next singlecast.`,\n\t\t\t\tlevel: \"verbose\",\n\t\t\t});\n\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Checks if a transaction failed because a node didn't respond in time */\n\tprivate isMissingNodeACK(\n\t\ttransaction: Transaction,\n\t\te: ZWaveError,\n\t): transaction is Transaction & {\n\t\tmessage: SendDataRequest | SendDataBridgeRequest;\n\t} {\n\t\treturn (\n\t\t\t// If the node does not acknowledge our request, it is either asleep or dead\n\t\t\te.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t&& (transaction.message instanceof SendDataRequest\n\t\t\t\t|| transaction.message instanceof SendDataBridgeRequest)\n\t\t);\n\t}\n\n\t/**\n\t * @internal\n\t * Handles the case that a node failed to respond in time.\n\t * Returns `true` if the transaction failure was handled, `false` if it needs to be rejected.\n\t */\n\tpublic handleMissingNodeACK(\n\t\ttransaction: Transaction & {\n\t\t\tmessage: HasNodeId;\n\t\t},\n\t\terror: ZWaveError,\n\t): boolean {\n\t\tconst node = this.tryGetNode(transaction.message);\n\t\tif (!node) return false; // This should never happen, but whatever\n\n\t\tconst messagePart1 = isSendData(transaction.message)\n\t\t\t? `The node did not respond after ${transaction.message.maxSendAttempts} attempts`\n\t\t\t: `The node did not respond`;\n\n\t\tif (!transaction.changeNodeStatusOnTimeout) {\n\t\t\t// The sender of this transaction doesn't want it to change the status of the node\n\t\t\treturn false;\n\t\t} else if (node.canSleep) {\n\t\t\tif (node.status === NodeStatus.Asleep) {\n\t\t\t\t// We already moved the messages to the wakeup queue before. If we end up here, this means a command\n\t\t\t\t// was sent that may be sent to potentially asleep nodes - including pings.\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tnode.id,\n\t\t\t\t`${messagePart1}. It is probably asleep, moving its messages to the wakeup queue.`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\n\t\t\t// There is no longer a reference to the current transaction. If it should be moved to the wakeup queue,\n\t\t\t// it temporarily needs to be added to the queue again.\n\t\t\tconst handled = this.mayMoveToWakeupQueue(transaction);\n\t\t\tif (handled) {\n\t\t\t\tthis.queue.add(transaction);\n\t\t\t}\n\n\t\t\t// Mark the node as asleep. This will move the messages to the wakeup queue\n\t\t\tnode.markAsAsleep();\n\n\t\t\treturn handled;\n\t\t} else {\n\t\t\tconst errorMsg = `${messagePart1}, it is presumed dead`;\n\t\t\tthis.controllerLog.logNode(node.id, errorMsg, \"warn\");\n\n\t\t\tnode.markAsDead();\n\n\t\t\t// There is no longer a reference to the current transaction on the queue, so we need to reject it separately.\n\t\t\ttransaction.setProgress({\n\t\t\t\tstate: TransactionState.Failed,\n\t\t\t\treason: errorMsg,\n\t\t\t});\n\n\t\t\ttransaction.abort(error);\n\t\t\tvoid this.rejectAllTransactionsForNode(node.id, errorMsg);\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Handles the case that the controller didn't acknowledge a command in time\n\t * Returns `true` if the transaction failure was handled, `false` if it needs to be rejected.\n\t */\n\tprivate handleMissingControllerACK(\n\t\ttransaction: Transaction,\n\t\terror: ZWaveError & {\n\t\t\tcode: ZWaveErrorCodes.Controller_Timeout;\n\t\t\tcontext: \"ACK\";\n\t\t},\n\t): boolean {\n\t\tif (!this._controller || !this.mayRecoverUnresponsiveController()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst recoverByReopeningSerialport = async () => {\n\t\t\tif (!this.serial) return;\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"Attempting to recover unresponsive controller by reopening the serial port...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tif (this.serial.isOpen) await this.serial.close();\n\t\t\tawait wait(1000);\n\t\t\tawait this.openSerialport();\n\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"Serial port reopened. Returning to normal operation and hoping for the best...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\n\t\t\t// We don't know if this worked\n\t\t\t// Go back to normal operation and hope for the best.\n\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t};\n\n\t\tif (\n\t\t\t(this._controller.status !== ControllerStatus.Unresponsive\n\t\t\t\t&& !this.maySoftReset())\n\t\t\t|| this._recoveryPhase\n\t\t\t\t=== ControllerRecoveryPhase.ACKTimeoutAfterReset\n\t\t) {\n\t\t\t// Either we can/could not do a soft reset or the controller is still timing out afterwards\n\t\t\tvoid recoverByReopeningSerialport().catch(noop);\n\n\t\t\treturn true;\n\t\t} else if (this._controller.status !== ControllerStatus.Unresponsive) {\n\t\t\t// The controller was responsive before this transaction failed.\n\t\t\t// Mark it as unresponsive and try to soft-reset it.\n\t\t\tthis.controller.setStatus(\n\t\t\t\tControllerStatus.Unresponsive,\n\t\t\t);\n\n\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.ACKTimeout;\n\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"Attempting to recover unresponsive controller by restarting it...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\n\t\t\t// Execute the soft-reset asynchronously\n\t\t\tvoid this.softReset().then(() => {\n\t\t\t\t// The controller responded. It is no longer unresponsive\n\n\t\t\t\t// Re-queue the transaction, so it can get handled next.\n\t\t\t\t// Its message generator may have finished, so reset that too.\n\t\t\t\ttransaction.reset();\n\t\t\t\tthis.getQueueForTransaction(transaction).add(\n\t\t\t\t\ttransaction.clone(),\n\t\t\t\t);\n\n\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t\t}).catch(() => {\n\t\t\t\t// Soft-reset failed. Reject the transaction\n\t\t\t\tthis.rejectTransaction(transaction, error);\n\n\t\t\t\t// and reopen the serial port\n\t\t\t\treturn recoverByReopeningSerialport();\n\t\t\t});\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\t// Not sure what to do here\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Handles the case that the controller didn't send the callback for a SendData in time\n\t * Returns `true` if the transaction failure was handled, `false` if it needs to be rejected.\n\t */\n\tprivate handleMissingSendDataResponseOrCallback(\n\t\ttransaction: Transaction,\n\t\terror: ZWaveError & {\n\t\t\tcode: ZWaveErrorCodes.Controller_Timeout;\n\t\t\tcontext: \"callback\" | \"response\";\n\t\t},\n\t): boolean {\n\t\tif (!this._controller || !this.mayRecoverUnresponsiveController()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (\n\t\t\t// The SendData response can time out on older controllers trying to reach a dead node.\n\t\t\t// In this case, we do not want to reset the controller, but just mark the node as dead.\n\t\t\terror.context === \"response\"\n\t\t\t// Also do this if the callback is timing out even after restarting the controller\n\t\t\t|| this._recoveryPhase\n\t\t\t\t=== ControllerRecoveryPhase.CallbackTimeoutAfterReset\n\t\t) {\n\t\t\tconst node = this.tryGetNode(transaction.message);\n\t\t\tif (!node) return false; // This should never happen, but whatever\n\n\t\t\t// The controller is still timing out transmitting after a soft reset, don't try again.\n\t\t\t// Real-world experience has shown that for older controllers this situation can be caused by unresponsive nodes.\n\n\t\t\t// The following is essentially a copy of handleMissingNodeACK, but with updated error messages\n\t\t\tconst messagePart1 =\n\t\t\t\t\"The node is causing the controller to become unresponsive\";\n\n\t\t\tlet handled: boolean;\n\n\t\t\tif (node.canSleep) {\n\t\t\t\tif (node.status === NodeStatus.Asleep) {\n\t\t\t\t\t// We already moved the messages to the wakeup queue before. If we end up here, this means a command\n\t\t\t\t\t// was sent that may be sent to potentially asleep nodes - including pings.\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\tnode.id,\n\t\t\t\t\t`${messagePart1}. It is probably asleep, moving its messages to the wakeup queue.`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\n\t\t\t\t// There is no longer a reference to the current transaction. If it should be moved to the wakeup queue,\n\t\t\t\t// it temporarily needs to be added to the queue again.\n\t\t\t\thandled = this.mayMoveToWakeupQueue(transaction);\n\t\t\t\tif (handled) {\n\t\t\t\t\tthis.queue.add(transaction);\n\t\t\t\t}\n\n\t\t\t\t// Mark the node as asleep. This will move the messages to the wakeup queue\n\t\t\t\tnode.markAsAsleep();\n\t\t\t} else {\n\t\t\t\tconst errorMsg = `${messagePart1}, it is presumed dead`;\n\t\t\t\tthis.controllerLog.logNode(node.id, errorMsg, \"warn\");\n\n\t\t\t\tnode.markAsDead();\n\n\t\t\t\t// There is no longer a reference to the current transaction on the queue, so we need to reject it separately.\n\t\t\t\ttransaction.setProgress({\n\t\t\t\t\tstate: TransactionState.Failed,\n\t\t\t\t\treason: errorMsg,\n\t\t\t\t});\n\n\t\t\t\ttransaction.abort(error);\n\t\t\t\tvoid this.rejectAllTransactionsForNode(node.id, errorMsg);\n\n\t\t\t\thandled = true;\n\t\t\t}\n\n\t\t\t// If the controller is still timing out, reset it once more\n\t\t\tif (\n\t\t\t\tthis._recoveryPhase\n\t\t\t\t\t=== ControllerRecoveryPhase.CallbackTimeoutAfterReset\n\t\t\t) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Attempting to recover controller again...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tvoid this.softResetInternal(true).catch(() => {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Automatic controller recovery failed. Returning to normal operation and hoping for the best.\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}).finally(() => {\n\t\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn handled;\n\t\t} else if (this._controller.status !== ControllerStatus.Unresponsive) {\n\t\t\t// The controller was responsive before this transaction failed.\n\n\t\t\tif (this.maySoftReset()) {\n\t\t\t\t// Mark it as unresponsive and try to soft-reset it.\n\t\t\t\tthis.controller.setStatus(\n\t\t\t\t\tControllerStatus.Unresponsive,\n\t\t\t\t);\n\n\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.CallbackTimeout;\n\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Controller missed Send Data callback. Attempting to recover...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\n\t\t\t\t// Execute the soft-reset asynchronously\n\t\t\t\tvoid this.softResetInternal(true).then(() => {\n\t\t\t\t\t// The controller responded. It is no longer unresponsive.\n\n\t\t\t\t\t// Re-queue the transaction, so it can get handled next.\n\t\t\t\t\t// Its message generator may have finished, so reset that too.\n\t\t\t\t\ttransaction.reset();\n\t\t\t\t\tthis.getQueueForTransaction(transaction).add(\n\t\t\t\t\t\ttransaction.clone(),\n\t\t\t\t\t);\n\n\t\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\t\tthis._recoveryPhase =\n\t\t\t\t\t\tControllerRecoveryPhase.CallbackTimeoutAfterReset;\n\t\t\t\t}).catch(() => {\n\t\t\t\t\t// Soft-reset failed. Just reject the transaction\n\t\t\t\t\tthis.rejectTransaction(transaction, error);\n\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Automatic controller recovery failed. Returning to normal operation and hoping for the best.\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Controller missed Send Data callback. Cannot recover automatically because the soft reset feature is unsupported or disabled. Returning to normal operation and hoping for the best...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tthis.rejectTransaction(transaction, error);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\t// Not sure what to do here\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Handles the case that the controller locks up and fails to transmit continuously\n\t */\n\tprivate handleJammedController(\n\t\ttransaction: Transaction,\n\t\terror: ZWaveError,\n\t): boolean {\n\t\tif (!this._controller || !this.mayRecoverUnresponsiveController()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (\n\t\t\t// Transmits still fail even after restarting the controller\n\t\t\tthis._recoveryPhase\n\t\t\t\t=== ControllerRecoveryPhase.JammedAfterReset\n\t\t) {\n\t\t\t// Maybe this isn't actually the controller being jammed. Give up on this command.\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"Automatic controller recovery failed. Returning to normal operation and hoping for the best.\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t\tthis._controller.setStatus(ControllerStatus.Ready);\n\n\t\t\treturn false;\n\t\t} else if (this._controller.status === ControllerStatus.Jammed) {\n\t\t\t// The controller failed to transmit continuously. Try to soft-reset it if we can.\n\t\t\tif (this.controller.sdkVersionLt(\"7.0\")) {\n\t\t\t\t// The workaround only makes sense on 700/800 series\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Cannot recover jammed controller automatically. Returning to normal operation and hoping for the best...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\tthis.rejectTransaction(transaction, error);\n\t\t\t} else if (this.maySoftReset()) {\n\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.Jammed;\n\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Attempting to recover jammed controller...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\n\t\t\t\t// Execute the soft-reset asynchronously\n\t\t\t\tvoid this.softReset().then(() => {\n\t\t\t\t\t// The controller responded. It is no longer unresponsive.\n\n\t\t\t\t\t// Re-queue the transaction, so it can get handled next.\n\t\t\t\t\t// Its message generator may have finished, so reset that too.\n\t\t\t\t\ttransaction.reset();\n\t\t\t\t\tthis.getQueueForTransaction(transaction).add(\n\t\t\t\t\t\ttransaction.clone(),\n\t\t\t\t\t);\n\n\t\t\t\t\tthis._recoveryPhase =\n\t\t\t\t\t\tControllerRecoveryPhase.JammedAfterReset;\n\t\t\t\t}).catch(() => {\n\t\t\t\t\t// Soft-reset failed. Just reject the transaction\n\t\t\t\t\tthis.rejectTransaction(transaction, error);\n\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Automatic controller recovery failed. Returning to normal operation and hoping for the best.\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Cannot recover jammed controller automatically because the soft reset feature is unsupported or disabled. Returning to normal operation and hoping for the best...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\tthis.rejectTransaction(transaction, error);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\t// Not sure what to do here\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate shouldRequestWakeupOnDemand(node: ZWaveNode): boolean {\n\t\treturn (\n\t\t\t!!node.supportsWakeUpOnDemand\n\t\t\t&& node.status === NodeStatus.Asleep\n\t\t\t&& this.hasPendingTransactions(\n\t\t\t\t(t) =>\n\t\t\t\t\tt.requestWakeUpOnDemand\n\t\t\t\t\t&& t.message.getNodeId() === node.id,\n\t\t\t)\n\t\t);\n\t}\n\n\tprivate partialCCSessions = new Map<string, CommandClass[]>();\n\tprivate getPartialCCSession(\n\t\tcommand: CommandClass,\n\t\tcreateIfMissing: false,\n\t): { partialSessionKey: string; session?: CommandClass[] } | undefined;\n\tprivate getPartialCCSession(\n\t\tcommand: CommandClass,\n\t\tcreateIfMissing: true,\n\t): { partialSessionKey: string; session: CommandClass[] } | undefined;\n\tprivate getPartialCCSession(\n\t\tcommand: CommandClass,\n\t\tcreateIfMissing: boolean,\n\t): { partialSessionKey: string; session?: CommandClass[] } | undefined {\n\t\tconst sessionId = command.getPartialCCSessionId();\n\n\t\tif (sessionId) {\n\t\t\t// This CC belongs to a partial session\n\t\t\tconst partialSessionKey = JSON.stringify({\n\t\t\t\tnodeId: command.nodeId,\n\t\t\t\tccId: command.ccId,\n\t\t\t\tccCommand: command.ccCommand!,\n\t\t\t\t...sessionId,\n\t\t\t});\n\t\t\tif (\n\t\t\t\tcreateIfMissing\n\t\t\t\t&& !this.partialCCSessions.has(partialSessionKey)\n\t\t\t) {\n\t\t\t\tthis.partialCCSessions.set(partialSessionKey, []);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tpartialSessionKey,\n\t\t\t\tsession: this.partialCCSessions.get(partialSessionKey),\n\t\t\t};\n\t\t}\n\t}\n\t/**\n\t * Assembles partial CCs of in a message body. Returns `true` when the message is complete and can be handled further.\n\t * If the message expects another partial one, this returns `false`.\n\t */\n\tprivate async assemblePartialCCs(\n\t\tmsg: CommandRequest & ContainsCC,\n\t): Promise<boolean> {\n\t\tlet command: CommandClass | undefined = msg.command;\n\t\t// We search for the every CC that provides us with a session ID\n\t\t// There might be newly-completed CCs that contain a partial CC,\n\t\t// so investigate the entire CC encapsulation stack.\n\t\twhile (true) {\n\t\t\tconst { partialSessionKey, session } =\n\t\t\t\tthis.getPartialCCSession(command, true) ?? {};\n\t\t\tif (session) {\n\t\t\t\t// This CC belongs to a partial session\n\t\t\t\tif (command.expectMoreMessages(session)) {\n\t\t\t\t\t// this is not the final one, store it\n\t\t\t\t\tsession.push(command);\n\t\t\t\t\tif (!isTransportServiceEncapsulation(msg.command)) {\n\t\t\t\t\t\t// and don't handle the command now\n\t\t\t\t\t\tthis.driverLog.logMessage(msg, {\n\t\t\t\t\t\t\tsecondaryTags: [\"partial\"],\n\t\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t} else {\n\t\t\t\t\t// this is the final one, merge the previous responses\n\t\t\t\t\tthis.partialCCSessions.delete(partialSessionKey!);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait command.mergePartialCCs(session, {\n\t\t\t\t\t\t\t...this.getCCParsingContext(),\n\t\t\t\t\t\t\tsourceNodeId: msg.command.nodeId as number,\n\t\t\t\t\t\t\tframeType: msg.frameType,\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Ensure there are no errors\n\t\t\t\t\t\tassertValidCCs(msg);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (isZWaveError(e)) {\n\t\t\t\t\t\t\tswitch (e.code) {\n\t\t\t\t\t\t\t\tcase ZWaveErrorCodes\n\t\t\t\t\t\t\t\t\t.Deserialization_NotImplemented:\n\t\t\t\t\t\t\t\tcase ZWaveErrorCodes.CC_NotImplemented:\n\t\t\t\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\t\t\t`Dropping message because it could not be deserialized: ${e.message}`,\n\t\t\t\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t// Don't continue handling this message\n\t\t\t\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t\t\t\tcase ZWaveErrorCodes\n\t\t\t\t\t\t\t\t\t.PacketFormat_InvalidPayload:\n\t\t\t\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\t\t\t`Could not assemble partial CCs because the payload is invalid. Dropping them.`,\n\t\t\t\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t// Don't continue handling this message\n\t\t\t\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t\t\t\tcase ZWaveErrorCodes.Driver_NotReady:\n\t\t\t\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\t\t\t`Could not assemble partial CCs because the driver is not ready yet. Dropping them`,\n\t\t\t\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t// Don't continue handling this message\n\t\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\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t\t// Assembling this CC was successful - but it might contain another partial CC\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// No partial CC, just continue\n\t\t\t}\n\n\t\t\t// If this is an encapsulating CC, we need to look one level deeper\n\t\t\tif (isEncapsulatingCommandClass(command)) {\n\t\t\t\tcommand = command.encapsulated;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/** Is called when a Transport Service command is received */\n\tprivate async handleTransportServiceCommand(\n\t\tcommand:\n\t\t\t| TransportServiceCCFirstSegment\n\t\t\t| TransportServiceCCSubsequentSegment,\n\t): Promise<void> {\n\t\tconst nodeSessions = this.ensureNodeSessions(command.nodeId);\n\n\t\t// TODO: Figure out how to know which timeout is the correct one. For now use the larger one\n\t\tconst missingSegmentTimeout =\n\t\t\tTransportServiceTimeouts.requestMissingSegmentR2;\n\n\t\tconst advanceTransportServiceSession = async (\n\t\t\tsession: TransportServiceSession,\n\t\t\tinput: TransportServiceRXMachineInput,\n\t\t): Promise<void> => {\n\t\t\tconst machine = session.machine;\n\n\t\t\t// Figure out what needs to be done for this input\n\t\t\tconst transition = machine.next(input);\n\t\t\tif (transition) {\n\t\t\t\tmachine.transition(transition.newState);\n\n\t\t\t\tif (machine.state.value === \"receive\") {\n\t\t\t\t\t// We received a segment in the normal flow. Restart the timer\n\t\t\t\t\tstartMissingSegmentTimeout(session);\n\t\t\t\t} else if (machine.state.value === \"requestMissing\") {\n\t\t\t\t\t// A segment is missing. Request it and restart the timeout\n\t\t\t\t\tthis.controllerLog.logNode(command.nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Transport Service RX session #${command.sessionId}: Segment with offset ${machine.state.offset} missing - requesting it...`,\n\t\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t});\n\t\t\t\t\tconst cc = new TransportServiceCCSegmentRequest({\n\t\t\t\t\t\tnodeId: command.nodeId,\n\t\t\t\t\t\tsessionId: command.sessionId,\n\t\t\t\t\t\tdatagramOffset: machine.state.offset,\n\t\t\t\t\t});\n\t\t\t\t\tawait this.sendCommand(cc, {\n\t\t\t\t\t\tmaxSendAttempts: 1,\n\t\t\t\t\t\tpriority: MessagePriority.Immediate,\n\t\t\t\t\t});\n\n\t\t\t\t\tstartMissingSegmentTimeout(session);\n\t\t\t\t} else if (machine.state.value === \"failure\") {\n\t\t\t\t\tthis.controllerLog.logNode(command.nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Transport Service RX session #${command.sessionId} failed`,\n\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\t});\n\t\t\t\t\t// TODO: Update statistics\n\t\t\t\t\tnodeSessions.transportService.delete(command.sessionId);\n\t\t\t\t\tif (session.timeout) {\n\t\t\t\t\t\tclearTimeout(session.timeout);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (machine.state.value === \"success\") {\n\t\t\t\t// This state may happen without a transition if we received the last segment before\n\t\t\t\t// but the SegmentComplete message got lost\n\t\t\t\tthis.controllerLog.logNode(command.nodeId, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Transport Service RX session #${command.sessionId} complete`,\n\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\t\t\t\tif (session.timeout) {\n\t\t\t\t\tclearTimeout(session.timeout);\n\t\t\t\t}\n\n\t\t\t\tconst cc = new TransportServiceCCSegmentComplete({\n\t\t\t\t\tnodeId: command.nodeId,\n\t\t\t\t\tsessionId: command.sessionId,\n\t\t\t\t});\n\t\t\t\tawait this.sendCommand(cc, {\n\t\t\t\t\tmaxSendAttempts: 1,\n\t\t\t\t\tpriority: MessagePriority.Immediate,\n\t\t\t\t}).catch(noop);\n\t\t\t}\n\t\t};\n\n\t\tfunction startMissingSegmentTimeout(\n\t\t\tsession: TransportServiceSession,\n\t\t): void {\n\t\t\tif (session.timeout) {\n\t\t\t\tclearTimeout(session.timeout);\n\t\t\t}\n\n\t\t\tsession.timeout = setTimeout(() => {\n\t\t\t\tsession.timeout = undefined;\n\t\t\t\tvoid advanceTransportServiceSession(session, {\n\t\t\t\t\tvalue: \"timeout\",\n\t\t\t\t});\n\t\t\t}, missingSegmentTimeout);\n\t\t}\n\n\t\tif (command instanceof TransportServiceCCFirstSegment) {\n\t\t\t// This is the first message in a sequence. Create or re-initialize the session\n\t\t\t// We don't delete finished sessions when the last message is received in order to be able to\n\t\t\t// handle when the SegmentComplete message gets lost. As soon as the node initializes a new session,\n\t\t\t// we do know that the previous one is finished.\n\t\t\tnodeSessions.transportService.clear();\n\n\t\t\tthis.controllerLog.logNode(command.nodeId, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Beginning Transport Service RX session #${command.sessionId}...`,\n\t\t\t\tlevel: \"debug\",\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\n\t\t\tconst machine = createTransportServiceRXMachine(\n\t\t\t\tcommand.datagramSize,\n\t\t\t\tcommand.partialDatagram.length,\n\t\t\t);\n\n\t\t\tconst session: TransportServiceSession = {\n\t\t\t\tfragmentSize: command.partialDatagram.length,\n\t\t\t\tmachine,\n\t\t\t};\n\t\t\tnodeSessions.transportService.set(command.sessionId, session);\n\n\t\t\t// Time out waiting for subsequent segments\n\t\t\tstartMissingSegmentTimeout(session);\n\t\t} else {\n\t\t\t// This is a subsequent message in a sequence. Continue executing the state machine\n\t\t\tconst transportSession = nodeSessions.transportService.get(\n\t\t\t\tcommand.sessionId,\n\t\t\t);\n\t\t\tif (transportSession) {\n\t\t\t\tawait advanceTransportServiceSession(transportSession, {\n\t\t\t\t\tvalue: \"segment\",\n\t\t\t\t\toffset: command.datagramOffset,\n\t\t\t\t\tlength: command.partialDatagram.length,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// This belongs to a session we don't know... tell the sending node to try again\n\t\t\t\tconst cc = new TransportServiceCCSegmentWait({\n\t\t\t\t\tnodeId: command.nodeId,\n\t\t\t\t\tpendingSegments: 0,\n\t\t\t\t});\n\t\t\t\tawait this.sendCommand(cc, {\n\t\t\t\t\tmaxSendAttempts: 1,\n\t\t\t\t\tpriority: MessagePriority.Immediate,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Is called when a message is received that does not belong to any ongoing transactions\n\t * @param msg The decoded message\n\t */\n\tprivate async handleUnsolicitedMessage(msg: Message): Promise<void> {\n\t\t// FIXME: Rename this - msg might not be unsolicited\n\t\t// This is a message we might have registered handlers for\n\t\ttry {\n\t\t\tif (msg.type === MessageType.Request) {\n\t\t\t\tawait this.handleRequest(msg);\n\t\t\t} else {\n\t\t\t\tawait this.handleResponse(msg);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Driver_NotReady\n\t\t\t) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t`Cannot handle message because the driver is not ready to handle it yet.`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Is called when the Serial API restart unexpectedly.\n\t */\n\tprivate async handleSerialAPIStartedUnexpectedly(\n\t\tmsg: SerialAPIStartedRequest,\n\t): Promise<boolean> {\n\t\t// Normally, the soft reset command includes waiting for this message.\n\t\t// If we end up here, it is unexpected.\n\n\t\tswitch (msg.wakeUpReason) {\n\t\t\t// All wakeup reasons that indicate a reset of the Serial API\n\t\t\t// need to be handled here, so we interpret node IDs correctly.\n\t\t\tcase SerialAPIWakeUpReason.Reset:\n\t\t\tcase SerialAPIWakeUpReason.WatchdogReset:\n\t\t\tcase SerialAPIWakeUpReason.SoftwareReset:\n\t\t\tcase SerialAPIWakeUpReason.EmergencyWatchdogReset:\n\t\t\tcase SerialAPIWakeUpReason.BrownoutCircuit: {\n\t\t\t\t// The Serial API restarted unexpectedly\n\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t`Serial API restarted unexpectedly.`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\n\t\t\t\t// In this situation, we may be executing a Serial API command, which will never complete.\n\t\t\t\t// Abort it, so it can be retried\n\t\t\t\tif (this.abortSerialAPICommand) {\n\t\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t\t`Currently active command will be retried...`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tthis.abortSerialAPICommand.reject(\n\t\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\t\"The Serial API restarted unexpectedly\",\n\t\t\t\t\t\t\tZWaveErrorCodes.Controller_Reset,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Restart the watchdog unless disabled\n\t\t\t\tif (this.options.features.watchdog) {\n\t\t\t\t\tawait this._controller?.startWatchdog();\n\t\t\t\t}\n\n\t\t\t\tif (this._controller?.nodeIdType === NodeIDType.Long) {\n\t\t\t\t\t// We previously used 16 bit node IDs, but the controller was reset.\n\t\t\t\t\t// Remember this and try to go back to 16 bit.\n\t\t\t\t\tthis._controller.nodeIdType = NodeIDType.Short;\n\t\t\t\t\tawait this._controller.trySetNodeIDType(NodeIDType.Long);\n\t\t\t\t}\n\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\t}\n\t\t}\n\n\t\treturn false; // Not handled\n\t}\n\n\t/**\n\t * Registers a handler for messages that are not handled by the driver as part of a message exchange.\n\t * The handler function needs to return a boolean indicating if the message has been handled.\n\t * Registered handlers are called in sequence until a handler returns `true`.\n\t *\n\t * @param fnType The function type to register the handler for\n\t * @param handler The request handler callback\n\t * @param oneTime Whether the handler should be removed after its first successful invocation\n\t */\n\tpublic registerRequestHandler<T extends Message>(\n\t\tfnType: FunctionType,\n\t\thandler: RequestHandler<T>,\n\t\toneTime: boolean = false,\n\t): void {\n\t\tconst handlers: RequestHandlerEntry<T>[] = this.requestHandlers.has(\n\t\t\t\tfnType,\n\t\t\t)\n\t\t\t? this.requestHandlers.get(fnType)!\n\t\t\t: [];\n\t\tconst entry: RequestHandlerEntry<T> = { invoke: handler, oneTime };\n\t\thandlers.push(entry);\n\t\tthis.driverLog.print(\n\t\t\t`added${oneTime ? \" one-time\" : \"\"} request handler for ${\n\t\t\t\tFunctionType[fnType]\n\t\t\t} (${num2hex(fnType)})...\n${handlers.length} registered`,\n\t\t);\n\t\tthis.requestHandlers.set(fnType, handlers as RequestHandlerEntry[]);\n\t}\n\n\t/**\n\t * Unregisters a message handler that has been added with `registerRequestHandler`\n\t * @param fnType The function type to unregister the handler for\n\t * @param handler The previously registered request handler callback\n\t */\n\tpublic unregisterRequestHandler(\n\t\tfnType: FunctionType,\n\t\thandler: RequestHandler,\n\t): void {\n\t\tconst handlers = this.requestHandlers.has(fnType)\n\t\t\t? this.requestHandlers.get(fnType)!\n\t\t\t: [];\n\t\tfor (let i = 0, entry = handlers[i]; i < handlers.length; i++) {\n\t\t\t// remove the handler if it was found\n\t\t\tif (entry.invoke === handler) {\n\t\t\t\thandlers.splice(i, 1);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tthis.driverLog.print(\n\t\t\t`removed request handler for ${FunctionType[fnType]} (${fnType})...\n${handlers.length} left`,\n\t\t);\n\t\tthis.requestHandlers.set(fnType, handlers);\n\t}\n\n\t/**\n\t * Checks whether a CC has a lower than expected security level and needs to be discarded\n\t */\n\tprivate isSecurityLevelTooLow(cc: CommandClass): boolean {\n\t\t// With Security S0, some commands may be accepted without encryption, some require it\n\t\t// With Security S2, a node MUST support its command classes only when communication is using its\n\t\t// highest Security Class granted during security bootstrapping.\n\n\t\t// We already discard lower S2 keys when decrypting, so all that's left here to check is if the\n\t\t// CC is encrypted at all.\n\n\t\tconst node = this._controller?.nodes.get(cc.nodeId as number);\n\t\tif (!node) {\n\t\t\t// Node does not exist, don't accept the CC\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tcc.nodeId as number,\n\t\t\t\t`is unknown - discarding received command...`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Transport Service has a special handler\n\t\tif (cc instanceof TransportServiceCC) return false;\n\t\t// CRC16 belongs outside of Security encapsulation\n\t\tif (cc instanceof CRC16CCCommandEncapsulation) {\n\t\t\treturn this.isSecurityLevelTooLow(cc.encapsulated);\n\t\t}\n\n\t\tconst secClass = node.getHighestSecurityClass();\n\t\tif (\n\t\t\tsecClass === SecurityClass.None\n\t\t\t|| secClass === SecurityClass.Temporary\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst expectedSecurityCC = securityClassIsS2(secClass)\n\t\t\t? CommandClasses[\"Security 2\"]\n\t\t\t: secClass === SecurityClass.S0_Legacy\n\t\t\t? CommandClasses.Security\n\t\t\t: undefined;\n\n\t\tconst isCCConsideredSecure = (\n\t\t\tcmd: CommandClass,\n\t\t): MaybeNotKnown<boolean> => {\n\t\t\t// Some CCs are always accepted, regardless of security class\n\t\t\tif (cmd instanceof SecurityCC) {\n\t\t\t\tswitch (cmd.ccCommand) {\n\t\t\t\t\t// Cannot be sent encapsulated:\n\t\t\t\t\tcase SecurityCommand.NonceGet:\n\t\t\t\t\tcase SecurityCommand.NonceReport:\n\t\t\t\t\tcase SecurityCommand.SchemeGet:\n\t\t\t\t\tcase SecurityCommand.SchemeReport:\n\t\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tif (cmd instanceof SecurityCCCommandEncapsulation) {\n\t\t\t\t\t// CommandsSupportedReport is always accepted to be able to learn security classes and interview nodes\n\t\t\t\t\t// CommandsSupportedGet is always accepted, so others can learn our security classes\n\t\t\t\t\tif (\n\t\t\t\t\t\tcmd.encapsulated\n\t\t\t\t\t\t\tinstanceof SecurityCCCommandsSupportedReport\n\t\t\t\t\t\t|| cmd.encapsulated\n\t\t\t\t\t\t\tinstanceof SecurityCCCommandsSupportedGet\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Other S0 commands are only accepted if S0 is the highest security class\n\t\t\t\t\treturn secClass === SecurityClass.S0_Legacy;\n\t\t\t\t}\n\t\t\t} else if (cmd instanceof Security2CC) {\n\t\t\t\tif (cmd instanceof Security2CCMessageEncapsulation) {\n\t\t\t\t\t// CommandsSupportedReport is always accepted to be able to learn security classes and interview nodes\n\t\t\t\t\tif (\n\t\t\t\t\t\tcmd.encapsulated\n\t\t\t\t\t\t\tinstanceof Security2CCCommandsSupportedReport\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// CommandsSupportedGet is always accepted, so others can learn our security classes\n\t\t\t\t\tif (\n\t\t\t\t\t\tcmd.encapsulated\n\t\t\t\t\t\t\tinstanceof Security2CCCommandsSupportedGet\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Multicast commands are always accepted\n\t\t\t\t\tif (cmd.getMulticastGroupId() != undefined) return true;\n\n\t\t\t\t\t// This shouldn't happen, but better be sure\n\t\t\t\t\tif (cmd.securityClass == undefined) return false;\n\n\t\t\t\t\t// All other commands are only accepted if the highest security class is used\n\t\t\t\t\treturn cmd.securityClass === secClass;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn cmd.ccId === expectedSecurityCC;\n\t\t};\n\n\t\tlet requiresSecurity = securityClassIsS2(secClass);\n\t\tconst isSecure = isCCConsideredSecure(cc);\n\n\t\twhile (true) {\n\t\t\tif (isEncapsulatingCommandClass(cc)) {\n\t\t\t\tcc = cc.encapsulated;\n\t\t\t} else if (isMultiEncapsulatingCommandClass(cc)) {\n\t\t\t\trequiresSecurity ||= cc.encapsulated.some((cmd) =>\n\t\t\t\t\tnode.isCCSecure(cmd.ccId)\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\trequiresSecurity ||= node.isCCSecure(cc.ccId)\n\t\t\t\t\t&& cc.ccId !== CommandClasses.Security\n\t\t\t\t\t&& cc.ccId !== CommandClasses[\"Security 2\"];\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (requiresSecurity && !isSecure) {\n\t\t\t// none found, don't accept the CC\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tcc.nodeId as number,\n\t\t\t\t`command was received at a lower security level than expected - discarding it...`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/** Checks whether a CC should be discarded */\n\tprivate shouldDiscardCC(cc: CommandClass): boolean {\n\t\tif (isEncapsulatingCommandClass(cc)) {\n\t\t\treturn this.shouldDiscardCC(cc.encapsulated);\n\t\t}\n\n\t\tconst node = this._controller?.nodes.get(cc.nodeId as number);\n\t\t// We should have checked this before, but better be safe than sorry\n\t\tif (!node) {\n\t\t\t// Node does not exist, don't accept the CC\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tcc.nodeId as number,\n\t\t\t\t`is unknown - discarding received command...`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\tif (\n\t\t\tcc.constructor.name.endsWith(\"Get\")\n\t\t\t&& (cc.frameType === \"multicast\" || cc.frameType === \"broadcast\")\n\t\t) {\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tcc.nodeId as number,\n\t\t\t\t`received GET-type command via ${cc.frameType} - discarding...`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Do not accept Meter CC and/or Multilevel Sensor CC if the node does not support them\n\t\t// https://github.com/zwave-js/zwave-js/issues/5510\n\t\t// TODO: Consider expanding this to all CCs and not only reports\n\t\tif (\n\t\t\tcc.ccId === CommandClasses.Meter\n\t\t\t|| cc.ccId === CommandClasses[\"Multilevel Sensor\"]\n\t\t) {\n\t\t\tconst endpoint = node.getEndpoint(cc.endpointIndex) ?? node;\n\t\t\tif (\n\t\t\t\t!endpoint.supportsCC(cc.ccId) && !endpoint.controlsCC(cc.ccId)\n\t\t\t) {\n\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\tcc.nodeId as number,\n\t\t\t\t\t`${\n\t\t\t\t\t\tcc.endpointIndex > 0\n\t\t\t\t\t\t\t? `Endpoint ${cc.endpointIndex} `\n\t\t\t\t\t\t\t: \"\"\n\t\t\t\t\t}does not support CC ${\n\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\tcc.ccId,\n\t\t\t\t\t\t)\n\t\t\t\t\t} - discarding received command...`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Is called when a Response-type message was received\n\t */\n\tprivate handleResponse(msg: Message): Promise<void> {\n\t\t// Check if we have a dynamic handler waiting for this message\n\t\tfor (const entry of this.awaitedMessages) {\n\t\t\tif (entry.predicate(msg)) {\n\t\t\t\t// We do\n\t\t\t\tentry.handler(msg);\n\t\t\t\treturn Promise.resolve();\n\t\t\t}\n\t\t}\n\n\t\tthis.driverLog.transactionResponse(msg, undefined, \"unexpected\");\n\t\tthis.driverLog.print(\"unexpected response, discarding...\", \"warn\");\n\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * Is called when a Request-type message was received\n\t */\n\tprivate async handleRequest(msg: Message): Promise<void> {\n\t\tlet handlers: RequestHandlerEntry[] | undefined;\n\n\t\tif (hasNodeId(msg) || containsCC(msg)) {\n\t\t\tconst node = this.tryGetNode(msg);\n\t\t\tif (node) {\n\t\t\t\t// We have received an unsolicited message from a dead node, bring it back to life\n\t\t\t\tif (node.status === NodeStatus.Dead) {\n\t\t\t\t\tnode.markAsAlive();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check if we have a dynamic handler waiting for this message\n\t\tfor (const entry of this.awaitedMessages) {\n\t\t\tif (entry.predicate(msg)) {\n\t\t\t\t// We do\n\t\t\t\tentry.handler(msg);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (isCommandRequest(msg) && containsCC(msg)) {\n\t\t\tconst nodeId = msg.getNodeId()!;\n\n\t\t\t// It could also be that this is the node's response for a CC that we sent, but where the ACK is delayed\n\t\t\tconst currentMessage = this.queue.currentTransaction\n\t\t\t\t?.getCurrentMessage();\n\t\t\tif (\n\t\t\t\tcurrentMessage\n\t\t\t\t&& currentMessage.expectsNodeUpdate()\n\t\t\t\t&& currentMessage.isExpectedNodeUpdate(msg)\n\t\t\t) {\n\t\t\t\t// The message we're currently sending is still in progress but expects this message in response.\n\t\t\t\t// Remember the message there.\n\t\t\t\tthis.controllerLog.logNode(msg.getNodeId()!, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`received expected response prematurely, remembering it...`,\n\t\t\t\t\tlevel: \"verbose\",\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\t\t\t\tcurrentMessage.prematureNodeUpdate = msg;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// For further actions, we are only interested in the innermost CC\n\t\t\tthis.unwrapCommands(msg);\n\n\t\t\t// cannot handle ApplicationCommandRequests without a controller\n\t\t\tif (this._controller == undefined) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t` the controller is not ready yet, discarding...`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t} else if (!this.controller.nodes.has(nodeId)) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t` the node is unknown or not initialized yet, discarding...`,\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\tconst node = this.controller.nodes.get(nodeId)!;\n\t\t\tconst nodeSessions = this.nodeSessions.get(nodeId);\n\t\t\t// Check if we need to handle the command ourselves\n\n\t\t\t// Some Security-related commands make sense to be handled in the driver\n\t\t\tif (msg.command instanceof SecurityCCNonceGet) {\n\t\t\t\treturn this.handleSecurityNonceGet(node);\n\t\t\t}\n\t\t\tif (msg.command instanceof SecurityCCNonceReport) {\n\t\t\t\treturn this.handleSecurityNonceReport(node, msg.command);\n\t\t\t}\n\t\t\tif (msg.command instanceof SecurityCCCommandsSupportedGet) {\n\t\t\t\treturn this.handleSecurityCommandsSupportedGet(\n\t\t\t\t\tnode,\n\t\t\t\t\tmsg.command,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (msg.command instanceof Security2CCNonceGet) {\n\t\t\t\treturn this.handleSecurity2NonceGet(node);\n\t\t\t}\n\t\t\tif (msg.command instanceof Security2CCNonceReport) {\n\t\t\t\treturn this.handleSecurity2NonceReport(node, msg.command);\n\t\t\t}\n\t\t\tif (msg.command instanceof Security2CCCommandsSupportedGet) {\n\t\t\t\treturn this.handleSecurity2CommandsSupportedGet(\n\t\t\t\t\tnode,\n\t\t\t\t\tmsg.command,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tmsg.command.ccId === CommandClasses.Supervision\n\t\t\t\t&& msg.command instanceof SupervisionCCReport\n\t\t\t\t&& nodeSessions?.supervision.has(msg.command.sessionId)\n\t\t\t) {\n\t\t\t\t// Supervision commands are handled here\n\t\t\t\tthis.controllerLog.logNode(msg.command.nodeId, {\n\t\t\t\t\tmessage: `Received update for a Supervision session`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\n\t\t\t\t// Call the update handler\n\t\t\t\tnodeSessions.supervision.get(msg.command.sessionId)!({\n\t\t\t\t\tstatus: msg.command.status,\n\t\t\t\t\tremainingDuration: msg.command.duration,\n\t\t\t\t} as SupervisionResult);\n\t\t\t\t// If this was a final report, remove the handler\n\t\t\t\tif (!msg.command.moreUpdatesFollow) {\n\t\t\t\t\tnodeSessions.supervision.delete(msg.command.sessionId);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Figure out if the command was received with supervision encapsulation and we need to respond accordingly\n\t\t\tconst supervisionSessionId = SupervisionCC.getSessionId(\n\t\t\t\tmsg.command,\n\t\t\t);\n\t\t\t// Figure out if this is an S2 multicast followup for a group that is out of sync\n\t\t\tconst s2MulticastOutOfSync = this.mustReplyWithSecurityS2MOS(\n\t\t\t\tmsg,\n\t\t\t);\n\n\t\t\tconst encapsulationFlags = msg.command.encapsulationFlags;\n\n\t\t\tlet reply: (\n\t\t\t\tstatus:\n\t\t\t\t\t| SupervisionStatus.Success\n\t\t\t\t\t| SupervisionStatus.Fail\n\t\t\t\t\t| SupervisionStatus.NoSupport,\n\t\t\t) => Promise<void>;\n\t\t\tif (supervisionSessionId != undefined) {\n\t\t\t\t// The command was supervised, and we must respond with a Supervision Report\n\t\t\t\tconst endpoint = node.getEndpoint(msg.command.endpointIndex)\n\t\t\t\t\t?? node;\n\t\t\t\treply = (status) =>\n\t\t\t\t\tendpoint\n\t\t\t\t\t\t.createAPI(CommandClasses.Supervision, false)\n\t\t\t\t\t\t.withOptions({ s2MulticastOutOfSync })\n\t\t\t\t\t\t.sendReport({\n\t\t\t\t\t\t\tsessionId: supervisionSessionId,\n\t\t\t\t\t\t\tmoreUpdatesFollow: false,\n\t\t\t\t\t\t\tstatus,\n\t\t\t\t\t\t\trequestWakeUpOnDemand: this\n\t\t\t\t\t\t\t\t.shouldRequestWakeupOnDemand(node),\n\t\t\t\t\t\t\tencapsulationFlags,\n\t\t\t\t\t\t\tlowPriority: this\n\t\t\t\t\t\t\t\t.shouldUseLowPriorityForSupervisionReport(\n\t\t\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\t\t\tencapsulationFlags,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Unsupervised, reply is a no-op\n\t\t\t\treply = () => Promise.resolve();\n\t\t\t}\n\n\t\t\tconst trySupervised = async (\n\t\t\t\taction: () => void | Promise<void>,\n\t\t\t): Promise<void> => {\n\t\t\t\ttry {\n\t\t\t\t\tawait action();\n\t\t\t\t\tawait reply(SupervisionStatus.Success);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tlet handled = false;\n\t\t\t\t\tif (isZWaveError(e)) {\n\t\t\t\t\t\tif (e.code === ZWaveErrorCodes.CC_OperationFailed) {\n\t\t\t\t\t\t\t// The sending node tried to do something that didn't work\n\t\t\t\t\t\t\tawait reply(SupervisionStatus.Fail);\n\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t} else if (e.code === ZWaveErrorCodes.CC_NotSupported) {\n\t\t\t\t\t\t\t// The sending node sent a command we could not handle\n\t\t\t\t\t\t\tawait reply(SupervisionStatus.NoSupport);\n\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!handled) {\n\t\t\t\t\t\t// Something unexpected happened.\n\t\t\t\t\t\t// Report failure, then re-throw the error, so it can be handled accordingly\n\t\t\t\t\t\tawait reply(SupervisionStatus.Fail);\n\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// DO NOT force-add support for the Supervision CC here. Some devices only support Supervision when sending,\n\t\t\t// so we need to trust the information we already have.\n\n\t\t\t// In the case where the command was unsupervised and we need to send a MOS, do it as soon as possible\n\t\t\tif (supervisionSessionId == undefined && s2MulticastOutOfSync) {\n\t\t\t\t// If the command was NOT received using Supervision,\n\t\t\t\t// we need to respond with an MOS nonce. Otherwise we'll set the flag\n\t\t\t\t// on the Supervision Report\n\t\t\t\tnode.commandClasses[\"Security 2\"].sendMOS().catch(() => {\n\t\t\t\t\t// Ignore errors\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// check if someone is waiting for this command\n\t\t\tfor (const entry of this.awaitedCommands) {\n\t\t\t\tif (entry.predicate(msg.command)) {\n\t\t\t\t\t// there is!\n\t\t\t\t\tentry.handler(msg.command);\n\n\t\t\t\t\t// and possibly reply to a supervised command\n\t\t\t\t\tawait reply(SupervisionStatus.Success);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Some S2 commands contain only extensions. Those are handled by the CC implementation.\n\t\t\tif (\n\t\t\t\tmsg.command instanceof Security2CCMessageEncapsulation\n\t\t\t\t&& msg.command.encapsulated == undefined\n\t\t\t) {\n\t\t\t\t// possibly reply to a supervised command\n\t\t\t\tawait reply(SupervisionStatus.Success);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Inclusion controller commands are handled by the controller class\n\t\t\tif (msg.command instanceof InclusionControllerCCInitiate) {\n\t\t\t\tconst command = msg.command;\n\t\t\t\tif (\n\t\t\t\t\tmsg.command.step === InclusionControllerStep.ProxyInclusion\n\t\t\t\t) {\n\t\t\t\t\tawait trySupervised(() =>\n\t\t\t\t\t\tthis.controller\n\t\t\t\t\t\t\t.handleInclusionControllerCCInitiateProxyInclusion(\n\t\t\t\t\t\t\t\tcommand,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t} else if (\n\t\t\t\t\tmsg.command.step\n\t\t\t\t\t\t=== InclusionControllerStep.ProxyInclusionReplace\n\t\t\t\t) {\n\t\t\t\t\tawait trySupervised(() =>\n\t\t\t\t\t\tthis.controller\n\t\t\t\t\t\t\t.handleInclusionControllerCCInitiateReplace(\n\t\t\t\t\t\t\t\tcommand,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// No one is waiting, dispatch the command to the node itself\n\t\t\tawait trySupervised(() => node.handleCommand(msg.command));\n\n\t\t\treturn;\n\t\t} else if (msg instanceof ApplicationUpdateRequest) {\n\t\t\t// Make sure we're ready to handle this command\n\t\t\tthis.ensureReady(true);\n\t\t\treturn this.controller.handleApplicationUpdateRequest(msg);\n\t\t} else if (msg instanceof SerialAPIStartedRequest) {\n\t\t\tif (await this.handleSerialAPIStartedUnexpectedly(msg)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\tif (\n\t\t\t\tmsg.functionType >= FunctionType.Proprietary_F0\n\t\t\t\t&& msg.functionType <= FunctionType.Proprietary_FE\n\t\t\t\t&& await this._controller\n\t\t\t\t\t?.handleUnsolictedProprietaryCommand(msg)\n\t\t\t) {\n\t\t\t\t// Proprietary command was handled\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// TODO: This deserves a nicer formatting\n\t\t\tthis.driverLog.print(\n\t\t\t\t`handling request ${\n\t\t\t\t\tFunctionType[msg.functionType]\n\t\t\t\t} (${msg.functionType})`,\n\t\t\t);\n\t\t\thandlers = this.requestHandlers.get(msg.functionType);\n\t\t}\n\n\t\tif (handlers != undefined && handlers.length > 0) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t` ${handlers.length} handler${\n\t\t\t\t\thandlers.length !== 1 ? \"s\" : \"\"\n\t\t\t\t} registered!`,\n\t\t\t);\n\t\t\t// loop through all handlers and find the first one that returns true to indicate that it handled the message\n\t\t\tfor (let i = 0; i < handlers.length; i++) {\n\t\t\t\tthis.driverLog.print(` invoking handler #${i}`);\n\t\t\t\t// Invoke the handler and remember its result\n\t\t\t\tconst handler = handlers[i];\n\t\t\t\tlet handlerResult = handler.invoke(msg);\n\t\t\t\tif (handlerResult instanceof Promise) {\n\t\t\t\t\thandlerResult = await handlerResult;\n\t\t\t\t}\n\t\t\t\tif (handlerResult) {\n\t\t\t\t\tthis.driverLog.print(` the message was handled`);\n\t\t\t\t\tif (handler.oneTime) {\n\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\" one-time handler was successfully called, removing it...\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\thandlers.splice(i, 1);\n\t\t\t\t\t}\n\t\t\t\t\t// don't invoke any more handlers\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.driverLog.print(\" no handlers registered!\", \"warn\");\n\t\t}\n\t}\n\n\tprivate hasLoggedNoNetworkKey = false;\n\n\tprivate async handleSecurityNonceGet(\n\t\tnode: ZWaveNode,\n\t): Promise<void> {\n\t\t// Only reply if secure communication is set up\n\t\tif (!this.securityManager) {\n\t\t\tif (!this.hasLoggedNoNetworkKey) {\n\t\t\t\tthis.hasLoggedNoNetworkKey = true;\n\t\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`cannot reply to NonceGet because no network key was configured!`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// When a node asks us for a nonce, it must support Security CC\n\t\tnode.addCC(CommandClasses.Security, {\n\t\t\tisSupported: true,\n\t\t\tversion: 1,\n\t\t\t// Security CC is always secure\n\t\t\tsecure: true,\n\t\t});\n\n\t\t// Ensure that we're not flooding the queue with unnecessary NonceReports (GH#1059)\n\t\tconst isNonceReport = (t: Transaction) =>\n\t\t\tt.message.getNodeId() === node.id\n\t\t\t&& containsCC(t.message)\n\t\t\t&& t.message.command instanceof SecurityCCNonceReport;\n\n\t\tif (this.hasPendingTransactions(isNonceReport)) {\n\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t\"in the process of replying to a NonceGet, won't send another NonceReport\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// Delete all previous nonces we sent the node, since they should no longer be used\n\t\tthis.securityManager.deleteAllNoncesForReceiver(node.id);\n\n\t\t// Now send the current nonce\n\t\ttry {\n\t\t\tawait node.commandClasses.Security.sendNonce();\n\t\t} catch (e) {\n\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\tmessage: `failed to send nonce: ${getErrorMessage(e)}`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Is called when a nonce report is received that does not belong to any transaction.\n\t * The received nonce reports are stored as \"free\" nonces\n\t */\n\tprivate handleSecurityNonceReport(\n\t\tnode: ZWaveNode,\n\t\tcommand: SecurityCCNonceReport,\n\t): void {\n\t\tconst secMan = this.securityManager;\n\t\tif (!secMan) return;\n\n\t\tsecMan.setNonce(\n\t\t\t{\n\t\t\t\tissuer: node.id,\n\t\t\t\tnonceId: secMan.getNonceId(command.nonce),\n\t\t\t},\n\t\t\t{\n\t\t\t\tnonce: command.nonce,\n\t\t\t\treceiver: this.controller.ownNodeId!,\n\t\t\t},\n\t\t\t{ free: true },\n\t\t);\n\t}\n\n\tprivate async handleSecurityCommandsSupportedGet(\n\t\tnode: ZWaveNode,\n\t\tcommand: SecurityCCCommandsSupportedGet,\n\t): Promise<void> {\n\t\tconst endpoint = node.getEndpoint(command.endpointIndex) ?? node;\n\n\t\tif (this.getHighestSecurityClass(node.id) === SecurityClass.S0_Legacy) {\n\t\t\tconst { supportedCCs } = determineNIF();\n\t\t\tawait endpoint.commandClasses.Security.reportSupportedCommands(\n\t\t\t\tsupportedCCs,\n\t\t\t\t// We don't report controlled CCs\n\t\t\t\t[],\n\t\t\t);\n\t\t} else {\n\t\t\t// S0 is not the highest class. Return an empty list\n\t\t\tawait endpoint.commandClasses.Security.reportSupportedCommands(\n\t\t\t\t[],\n\t\t\t\t[],\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Handles a nonce request for S2 */\n\tprivate async handleSecurity2NonceGet(\n\t\tnode: ZWaveNode,\n\t): Promise<void> {\n\t\t// Only reply if secure communication is set up\n\t\tif (!this.getSecurityManager2(node.id)) {\n\t\t\tif (!this.hasLoggedNoNetworkKey) {\n\t\t\t\tthis.hasLoggedNoNetworkKey = true;\n\t\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`cannot reply to NonceGet (S2) because no network key was configured!`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// When a node asks us for a nonce, it must support Security 2 CC\n\t\tnode.addCC(CommandClasses[\"Security 2\"], {\n\t\t\tisSupported: true,\n\t\t\tversion: 1,\n\t\t\t// Security 2 CC is always secure\n\t\t\tsecure: true,\n\t\t});\n\n\t\t// Ensure that we're not flooding the queue with unnecessary NonceReports (GH#1059)\n\t\tconst isNonceReport = (t: Transaction) =>\n\t\t\tt.message.getNodeId() === node.id\n\t\t\t&& containsCC(t.message)\n\t\t\t&& t.message.command instanceof Security2CCNonceReport;\n\n\t\tif (this.hasPendingTransactions(isNonceReport)) {\n\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t\"in the process of replying to a NonceGet, won't send another NonceReport\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait node.commandClasses[\"Security 2\"].sendNonce();\n\t\t} catch (e) {\n\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\tmessage: `failed to send nonce: ${getErrorMessage(e)}`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Is called when a nonce report is received that does not belong to any transaction.\n\t */\n\tprivate handleSecurity2NonceReport(\n\t\tnode: ZWaveNode,\n\t\t_command: Security2CCNonceReport,\n\t): void {\n\t\t// const secMan = this.securityManager2;\n\t\t// if (!secMan) return;\n\n\t\t// This has the potential of resetting our SPAN state in the middle of a transaction which may expect it to be valid\n\t\t// So we probably shouldn't react here, and instead handle the NonceReport we'll get in response to the next command we send\n\n\t\t// if (command.SOS && command.receiverEI) {\n\t\t// \t// The node couldn't decrypt the last command we sent it. Invalidate\n\t\t// \t// the shared SPAN, since it did the same\n\t\t// \tsecMan.storeRemoteEI(node.id, command.receiverEI);\n\t\t// }\n\n\t\t// Since we landed here, this is not in response to any command we sent\n\t\tthis.controllerLog.logNode(node.id, {\n\t\t\tmessage:\n\t\t\t\t`received S2 nonce without an active transaction, not sure what to do with it`,\n\t\t\tlevel: \"warn\",\n\t\t\tdirection: \"inbound\",\n\t\t});\n\t}\n\n\tprivate async handleSecurity2CommandsSupportedGet(\n\t\tnode: ZWaveNode,\n\t\tcommand: Security2CCCommandsSupportedGet,\n\t): Promise<void> {\n\t\tconst endpoint = node.getEndpoint(command.endpointIndex) ?? node;\n\n\t\tconst highestSecurityClass = this.getHighestSecurityClass(node.id);\n\t\tconst actualSecurityClass = (\n\t\t\tcommand.getEncapsulatingCC(\n\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t) as Security2CCMessageEncapsulation | undefined\n\t\t)?.securityClass;\n\n\t\tif (\n\t\t\thighestSecurityClass !== undefined\n\t\t\t&& highestSecurityClass === actualSecurityClass\n\t\t) {\n\t\t\t// The command was received using the highest security class. Return the list of supported CCs\n\n\t\t\tconst implementedCCs = allCCs.filter((cc) =>\n\t\t\t\tgetImplementedVersion(cc) > 0\n\t\t\t);\n\n\t\t\t// Encapsulation CCs are always supported\n\t\t\tconst implementedEncapsulationCCs = encapsulationCCs.filter(\n\t\t\t\t(cc) =>\n\t\t\t\t\timplementedCCs.includes(cc)\n\t\t\t\t\t// A node MUST advertise support for Multi Channel Command Class only if it implements End Points.\n\t\t\t\t\t// A node able to communicate using the Multi Channel encapsulation but implementing no End Point\n\t\t\t\t\t// MUST NOT advertise support for the Multi Channel Command Class.\n\t\t\t\t\t// --> We do not implement end points\n\t\t\t\t\t&& cc !== CommandClasses[\"Multi Channel\"],\n\t\t\t);\n\n\t\t\tconst supportedCCs = new Set([\n\t\t\t\t// DT:00.11.0004.1\n\t\t\t\t// All Root Devices or nodes MUST support:\n\t\t\t\t// - Association, version 2\n\t\t\t\t// - Association Group Information\n\t\t\t\t// - Device Reset Locally\n\t\t\t\t// - Firmware Update Meta Data, version 5\n\t\t\t\t// - Indicator, version 3\n\t\t\t\t// - Manufacturer Specific\n\t\t\t\t// - Multi Channel Association, version 3\n\t\t\t\t// - Powerlevel\n\t\t\t\t// - Security 2\n\t\t\t\t// - Supervision\n\t\t\t\t// - Transport Service, version 2\n\t\t\t\t// - Version, version 2\n\t\t\t\t// - Z-Wave Plus Info, version 2\n\t\t\t\tCommandClasses.Association,\n\t\t\t\tCommandClasses[\"Association Group Information\"],\n\t\t\t\tCommandClasses[\"Device Reset Locally\"],\n\t\t\t\tCommandClasses[\"Firmware Update Meta Data\"],\n\t\t\t\tCommandClasses.Indicator,\n\t\t\t\tCommandClasses[\"Manufacturer Specific\"],\n\t\t\t\tCommandClasses[\"Multi Channel Association\"],\n\t\t\t\tCommandClasses.Powerlevel,\n\t\t\t\tCommandClasses.Version,\n\t\t\t\tCommandClasses[\"Z-Wave Plus Info\"],\n\n\t\t\t\t// Generic Controller device type has no additional support requirements,\n\t\t\t\t// but we also support the following command classes:\n\t\t\t\tCommandClasses[\"Inclusion Controller\"],\n\n\t\t\t\t// plus encapsulation CCs, which are part of the above requirement\n\t\t\t\t...implementedEncapsulationCCs.filter(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\t// CC:009F.01.0E.11.00F\n\t\t\t\t\t\t// The Security 0 and Security 2 Command Class MUST NOT be advertised in this command\n\t\t\t\t\t\t// The Transport Service Command Class MUST NOT be advertised in this command.\n\t\t\t\t\t\tcc !== CommandClasses.Security\n\t\t\t\t\t\t&& cc !== CommandClasses[\"Security 2\"]\n\t\t\t\t\t\t&& cc !== CommandClasses[\"Transport Service\"],\n\t\t\t\t),\n\t\t\t]);\n\n\t\t\t// Commands that are always in the NIF should not appear in the\n\t\t\t// S2 commands supported report\n\t\t\tconst commandsInNIF = new Set(determineNIF().supportedCCs);\n\t\t\tconst supportedCommandsNotInNIF = [...supportedCCs].filter((cc) =>\n\t\t\t\t!commandsInNIF.has(cc)\n\t\t\t);\n\n\t\t\tawait endpoint.commandClasses[\"Security 2\"].reportSupportedCommands(\n\t\t\t\tsupportedCommandsNotInNIF,\n\t\t\t);\n\t\t} else if (securityClassIsS2(actualSecurityClass)) {\n\t\t\t// The command was received using a lower security class. Return an empty list\n\t\t\tawait endpoint.commandClasses[\"Security 2\"]\n\t\t\t\t.withOptions({\n\t\t\t\t\ts2OverrideSecurityClass: actualSecurityClass,\n\t\t\t\t})\n\t\t\t\t.reportSupportedCommands([]);\n\t\t} else {\n\t\t\t// Do not respond\n\t\t}\n\t}\n\n\t/**\n\t * Returns the next callback ID. Callback IDs are used to correlate requests\n\t * to the controller/nodes with its response\n\t */\n\tpublic readonly getNextCallbackId = createWrappingCounter(0xff);\n\n\tprivate readonly supervisionSessionIDs = new Map<number, () => number>();\n\t/**\n\t * Returns the next session ID for Supervision CC\n\t */\n\tpublic getNextSupervisionSessionId(nodeId: number): number {\n\t\tif (!this.supervisionSessionIDs.has(nodeId)) {\n\t\t\tthis.supervisionSessionIDs.set(\n\t\t\t\tnodeId,\n\t\t\t\tcreateWrappingCounter(MAX_SUPERVISION_SESSION_ID, true),\n\t\t\t);\n\t\t}\n\t\treturn this.supervisionSessionIDs.get(nodeId)!();\n\t}\n\n\t/**\n\t * Returns the next session ID for Transport Service CC\n\t */\n\tpublic readonly getNextTransportServiceSessionId = createWrappingCounter(\n\t\tMAX_TRANSPORT_SERVICE_SESSION_ID,\n\t\ttrue,\n\t);\n\n\tprivate encapsulateCommands(\n\t\tcmd: CommandClass,\n\t\toptions: Omit<SendCommandOptions, keyof SendMessageOptions> = {},\n\t): CommandClass {\n\t\t// The encapsulation order (from outside to inside) is as follows:\n\t\t// 5. Any one of the following combinations:\n\t\t// a. Security (S0 or S2) followed by transport service\n\t\t// b. Transport Service\n\t\t// c. Security (S0 or S2)\n\t\t// d. CRC16\n\t\t// b and d are mutually exclusive, security is not\n\t\t// 4. Multi Channel\n\t\t// 3. Supervision\n\t\t// 2. Multi Command\n\t\t// 1. Encapsulated Command Class (payload), e.g. Basic Set\n\n\t\t// TODO: 2.\n\n\t\t// 3.\n\t\tif (SupervisionCC.requiresEncapsulation(cmd)) {\n\t\t\tcmd = SupervisionCC.encapsulate(\n\t\t\t\tcmd,\n\t\t\t\tthis.getNextSupervisionSessionId(cmd.nodeId as number),\n\t\t\t);\n\t\t}\n\n\t\t// 4.\n\t\tif (MultiChannelCC.requiresEncapsulation(cmd)) {\n\t\t\tconst multiChannelCCVersion = this.getSupportedCCVersion(\n\t\t\t\tCommandClasses[\"Multi Channel\"],\n\t\t\t\tcmd.nodeId as number,\n\t\t\t);\n\n\t\t\tcmd = multiChannelCCVersion === 1\n\t\t\t\t? MultiChannelCC.encapsulateV1(cmd)\n\t\t\t\t: MultiChannelCC.encapsulate(cmd);\n\t\t}\n\n\t\t// 5.\n\t\tif (CRC16CC.requiresEncapsulation(cmd)) {\n\t\t\tcmd = CRC16CC.encapsulate(cmd);\n\t\t} else {\n\t\t\t// The command must be S2-encapsulated, if ...\n\t\t\tlet maybeS2 = false;\n\t\t\tconst node = cmd.getNode(this);\n\t\t\tif (node?.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\t\t// ... the node supports S2 and has a valid security class\n\t\t\t\tconst nodeSecClass = node.getHighestSecurityClass();\n\t\t\t\tconst securityManager = this.getSecurityManager2(node.id);\n\t\t\t\tmaybeS2 = securityClassIsS2(nodeSecClass)\n\t\t\t\t\t|| !!securityManager?.tempKeys.has(node.id);\n\t\t\t} else if (options.s2MulticastGroupId != undefined) {\n\t\t\t\t// ... or we're dealing with S2 multicast\n\t\t\t\tmaybeS2 = true;\n\t\t\t}\n\t\t\tif (maybeS2 && Security2CC.requiresEncapsulation(cmd)) {\n\t\t\t\tcmd = Security2CC.encapsulate(\n\t\t\t\t\tcmd,\n\t\t\t\t\tthis.ownNodeId,\n\t\t\t\t\tthis,\n\t\t\t\t\t{\n\t\t\t\t\t\tsecurityClass: options.s2OverrideSecurityClass,\n\t\t\t\t\t\tmulticastOutOfSync: !!options.s2MulticastOutOfSync,\n\t\t\t\t\t\tmulticastGroupId: options.s2MulticastGroupId,\n\t\t\t\t\t\tverifyDelivery: options.s2VerifyDelivery,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// This check will return false for S2-encapsulated commands\n\t\t\tif (SecurityCC.requiresEncapsulation(cmd)) {\n\t\t\t\tcmd = SecurityCC.encapsulate(\n\t\t\t\t\tthis.ownNodeId,\n\t\t\t\t\tthis.securityManager!,\n\t\t\t\t\tcmd,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn cmd;\n\t}\n\n\tpublic unwrapCommands(msg: Message & ContainsCC): void {\n\t\t// Unwrap encapsulating CCs until we get to the core\n\t\twhile (\n\t\t\tisEncapsulatingCommandClass(msg.command)\n\t\t\t|| isMultiEncapsulatingCommandClass(msg.command)\n\t\t) {\n\t\t\tconst unwrapped = msg.command.encapsulated;\n\t\t\tif (isArray(unwrapped)) {\n\t\t\t\t// Multi Command CC cannot be further unwrapped. Preserve the encapsulation flags though.\n\t\t\t\tfor (const cmd of unwrapped) {\n\t\t\t\t\tcmd.toggleEncapsulationFlag(\n\t\t\t\t\t\tmsg.command.encapsulationFlags,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Copy the encapsulation flags and add the current encapsulation\n\t\t\tunwrapped.encapsulationFlags = msg.command.encapsulationFlags;\n\t\t\tswitch (msg.command.ccId) {\n\t\t\t\tcase CommandClasses.Supervision:\n\t\t\t\t\tunwrapped.toggleEncapsulationFlag(\n\t\t\t\t\t\tEncapsulationFlags.Supervision,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase CommandClasses[\"Security 2\"]:\n\t\t\t\tcase CommandClasses.Security:\n\t\t\t\t\tunwrapped.toggleEncapsulationFlag(\n\t\t\t\t\t\tEncapsulationFlags.Security,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase CommandClasses[\"CRC-16 Encapsulation\"]:\n\t\t\t\t\tunwrapped.toggleEncapsulationFlag(\n\t\t\t\t\t\tEncapsulationFlags.CRC16,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tmsg.command = unwrapped;\n\t\t}\n\t}\n\n\tprivate shouldPersistCCValues(cc: CommandClass): boolean {\n\t\t// Always persist encapsulation CCs, otherwise interviews don't work.\n\t\tif (isEncapsulationCC(cc.ccId)) return true;\n\n\t\t// Do not persist values for a node or endpoint that does not exist\n\t\tconst endpoint = this.tryGetEndpoint(cc);\n\t\tconst node = endpoint?.tryGetNode();\n\t\tif (!node) return false;\n\n\t\t// Do not persist values for a CC that was force-removed via config\n\t\tif (endpoint?.wasCCRemovedViaConfig(cc.ccId)) return false;\n\n\t\t// Do not persist values for a CC that's being mapped to another endpoint.\n\t\t// FIXME: This duplicates logic in Node.ts -> handleCommand\n\t\tconst compatConfig = node?.deviceConfig?.compat;\n\t\tif (\n\t\t\tcc.endpointIndex === 0\n\t\t\t&& cc.constructor.name.endsWith(\"Report\")\n\t\t\t&& node.getEndpointCount() >= 1\n\t\t\t// Only map reports from the root device to an endpoint if we know which one\n\t\t\t&& compatConfig?.mapRootReportsToEndpoint != undefined\n\t\t) {\n\t\t\tconst targetEndpoint = node.getEndpoint(\n\t\t\t\tcompatConfig.mapRootReportsToEndpoint,\n\t\t\t);\n\t\t\tif (targetEndpoint?.supportsCC(cc.ccId)) return false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/** Persists the values contained in a Command Class in the corresponding nodes's value DB */\n\tprivate persistCCValues(cc: CommandClass) {\n\t\tif (this.shouldPersistCCValues(cc)) {\n\t\t\tcc.persistValues(this);\n\t\t}\n\n\t\tif (isEncapsulatingCommandClass(cc)) {\n\t\t\tthis.persistCCValues(cc.encapsulated);\n\t\t} else if (isMultiEncapsulatingCommandClass(cc)) {\n\t\t\tfor (const encapsulated of cc.encapsulated) {\n\t\t\t\tthis.persistCCValues(encapsulated);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Gets called whenever any Serial API command succeeded or a SendData command had a negative callback.\n\t */\n\tprivate handleSerialAPICommandResult(\n\t\tmsg: Message,\n\t\toptions: SendMessageOptions,\n\t\tresult: Message | undefined,\n\t): void {\n\t\t// Update statistics\n\t\tconst node = this.tryGetNode(msg);\n\t\tlet success = true;\n\t\tif (isSendData(msg) || hasNodeId(msg)) {\n\t\t\t// This shouldn't happen, but just in case\n\t\t\tif (!node) return;\n\n\t\t\t// If this is a transmit report, use it to update statistics\n\t\t\tif (isTransmitReport(result)) {\n\t\t\t\tif (!result.isOK()) {\n\t\t\t\t\tsuccess = false;\n\t\t\t\t\tnode.incrementStatistics(\"commandsDroppedTX\");\n\t\t\t\t} else {\n\t\t\t\t\tnode.incrementStatistics(\"commandsTX\");\n\t\t\t\t\tnode.updateRTT(msg);\n\t\t\t\t\t// Update last seen state\n\t\t\t\t\tnode.lastSeen = new Date();\n\t\t\t\t}\n\n\t\t\t\t// Notify listeners about the status report if one was received\n\t\t\t\tif (hasTXReport(result)) {\n\t\t\t\t\toptions.onTXReport?.(result.txReport);\n\t\t\t\t\tnode.updateRouteStatistics(result.txReport);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Track and potentially update the status of the node when communication succeeds\n\t\t\tif (success) {\n\t\t\t\tif (node.canSleep) {\n\t\t\t\t\t// Do not update the node status when we only responded to a nonce/supervision request\n\t\t\t\t\tif (options.priority !== MessagePriority.Immediate) {\n\t\t\t\t\t\t// If the node is not meant to be kept awake, try to send it back to sleep\n\t\t\t\t\t\tif (!node.keepAwake) {\n\t\t\t\t\t\t\tsetImmediate(() =>\n\t\t\t\t\t\t\t\tthis.debounceSendNodeToSleep(node)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// The node must be awake because it answered\n\t\t\t\t\t\tnode.markAsAwake();\n\t\t\t\t\t}\n\t\t\t\t} else if (node.status !== NodeStatus.Alive) {\n\t\t\t\t\t// The node status was unknown or dead - in either case it must be alive because it answered\n\t\t\t\t\tnode.markAsAlive();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis._controller?.incrementStatistics(\"messagesTX\");\n\t\t}\n\t}\n\n\tprivate shouldUseLowPriorityForSupervisionReport(\n\t\ttargetNode: ZWaveNode,\n\t\tencapsulationFlags: EncapsulationFlags,\n\t): boolean {\n\t\t// To avoid S2 collisions, we reduce the priority of Supervision reports\n\t\t// when they are S2-encapsulated, and another S2-encapsulated transaction is in\n\t\t// progress for the same node\n\n\t\t// Use Immediate priority if there is no other transaction for this node in progress\n\t\tconst currentNormalMsg = this.queue.currentTransaction?.message;\n\t\tif (currentNormalMsg?.getNodeId() !== targetNode.id) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!containsCC(currentNormalMsg)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Use Immediate priority if the node isn't using Security S2\n\t\tif (!securityClassIsS2(targetNode.getHighestSecurityClass())) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Use Immediate priority unless both messages are S2-encapsulated\n\t\tconst currentMsgIsSecure = currentNormalMsg.command\n\t\t\tinstanceof Security2CCMessageEncapsulation;\n\t\tconst reportIsSecure = !!(\n\t\t\tencapsulationFlags & EncapsulationFlags.Security\n\t\t);\n\t\tif (!currentMsgIsSecure || !reportIsSecure) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// This has potential for a conflict, use low priority\n\t\tthis.controllerLog.logNode(targetNode.id, {\n\t\t\tmessage:\n\t\t\t\t\"S2 collision detected, reducing priority for Supervision report\",\n\t\t\tlevel: \"debug\",\n\t\t});\n\t\treturn true;\n\t}\n\n\tprivate mayStartTransaction(transaction: Transaction): boolean {\n\t\t// We may not send anything on the normal queue if the send thread is paused\n\t\t// or the controller is unresponsive\n\t\tif (\n\t\t\tthis.queuePaused\n\t\t\t|| this.controller.status === ControllerStatus.Unresponsive\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst message = transaction.message;\n\t\tconst targetNode = message.tryGetNode(this);\n\n\t\t// Messages to the controller may always be sent...\n\t\tif (!targetNode) return true;\n\n\t\t// The transaction queue is sorted automatically. If the first message is for a sleeping node, all messages in the queue are.\n\t\t// There are a few exceptions:\n\t\t// 1. Pings may be used to determine whether a node is really asleep.\n\t\t// 2. Responses to nonce requests must be sent independent of the node status, because some sleeping nodes may try to send us encrypted messages.\n\t\t// If we don't send them, they block the send queue\n\t\t// 3. Nodes that can sleep but do not support wakeup: https://github.com/zwave-js/zwave-js/discussions/1537\n\t\t// We need to try and send messages to them even if they are asleep, because we might never hear from them\n\n\t\t// 2. is handled by putting the message into the immediate queue\n\n\t\t// Pings may always be sent\n\t\tif (messageIsPing(message)) return true;\n\n\t\treturn (\n\t\t\ttargetNode.status !== NodeStatus.Asleep\n\t\t\t|| (!targetNode.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t\t\t&& targetNode.interviewStage >= InterviewStage.NodeInfo)\n\t\t);\n\t}\n\n\tprivate markQueueBusy(queue: TransactionQueue, busy: boolean): void {\n\t\tconst index = this.queues.indexOf(queue);\n\t\tif (busy) {\n\t\t\tthis._queuesBusyFlags |= 1 << index;\n\t\t} else {\n\t\t\tthis._queuesBusyFlags &= ~(1 << index);\n\t\t}\n\t\tthis.queueIdle = this._queuesBusyFlags === 0;\n\t}\n\n\tprivate async drainTransactionQueue(\n\t\tqueue: TransactionQueue,\n\t): Promise<void> {\n\t\tlet setIdleTimer: NodeJS.Immediate | undefined;\n\t\tfor await (const transaction of queue) {\n\t\t\tif (setIdleTimer) {\n\t\t\t\tclearImmediate(setIdleTimer);\n\t\t\t\tsetIdleTimer = undefined;\n\t\t\t}\n\t\t\tthis.markQueueBusy(queue, true);\n\n\t\t\tlet error: ZWaveError | undefined;\n\t\t\ttry {\n\t\t\t\tawait this.executeTransaction(transaction);\n\t\t\t} catch (e) {\n\t\t\t\terror = e as ZWaveError;\n\t\t\t} finally {\n\t\t\t\tqueue.finalizeTransaction();\n\t\t\t}\n\n\t\t\t// Handle errors after clearing the current transaction.\n\t\t\t// Otherwise, it will get considered the active transaction and cause an unnecessary SendDataAbort\n\t\t\tif (error) {\n\t\t\t\tthis.handleFailedTransaction(transaction, error);\n\t\t\t}\n\n\t\t\tsetIdleTimer = setImmediate(() => {\n\t\t\t\tthis.markQueueBusy(queue, false);\n\t\t\t});\n\t\t}\n\t}\n\n\t/** Steps through the message generator of a transaction. Throws an error if the transaction should fail. */\n\tprivate async executeTransaction(transaction: Transaction): Promise<void> {\n\t\tlet prevResult: Message | undefined;\n\t\tlet msg: Message | undefined;\n\n\t\ttransaction.start();\n\t\ttransaction.setProgress({ state: TransactionState.Active });\n\n\t\tconst maxJammedAttempts =\n\t\t\tthis._recoveryPhase === ControllerRecoveryPhase.JammedAfterReset\n\t\t\t\t// After attempting soft-reset, only try sending once\n\t\t\t\t? 1\n\t\t\t\t: this.options.attempts.sendDataJammed;\n\n\t\t// Step through the transaction as long as it gives us a next message\n\t\twhile ((msg = await transaction.generateNextMessage(prevResult))) {\n\t\t\t// Keep track of how often the controller failed to send a command, to prevent ending up in an infinite loop\n\t\t\tlet jammedAttempts = 0;\n\t\t\tlet queueAttempts = 0;\n\t\t\tattemptMessage: for (let attemptNumber = 1;; attemptNumber++) {\n\t\t\t\ttry {\n\t\t\t\t\tprevResult = await this.queueSerialAPICommand(\n\t\t\t\t\t\tmsg,\n\t\t\t\t\t\ttransaction.stack,\n\t\t\t\t\t);\n\t\t\t\t\tif (isTransmitReport(prevResult)) {\n\t\t\t\t\t\t// Figure out if the controller is jammed. If it is, wait a second and try again.\n\t\t\t\t\t\t// https://github.com/zwave-js/zwave-js/issues/6199\n\t\t\t\t\t\t// In some cases, the transmit status can be Fail, even after transmitting for a couple of seconds.\n\t\t\t\t\t\t// Not sure what causes this, but it doesn't mean that the controller is jammed.\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tprevResult.transmitStatus === TransmitStatus.Fail\n\t\t\t\t\t\t\t&& \"txReport\" in prevResult\n\t\t\t\t\t\t\t// Ensure the controller didn't actually transmit\n\t\t\t\t\t\t\t&& prevResult.txReport?.txTicks === 0\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tjammedAttempts++;\n\t\t\t\t\t\t\tif (jammedAttempts < maxJammedAttempts) {\n\t\t\t\t\t\t\t\t// The controller is jammed. Wait a bit, then try again.\n\t\t\t\t\t\t\t\tthis.controller.setStatus(\n\t\t\t\t\t\t\t\t\tControllerStatus.Jammed,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tawait wait(\n\t\t\t\t\t\t\t\t\tthis.options.timeouts.retryJammed,\n\t\t\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tcontinue attemptMessage;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Reject the transaction so we can attempt to recover\n\t\t\t\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\t\t\t`Failed to send the command after ${jammedAttempts} attempts`,\n\t\t\t\t\t\t\t\t\tZWaveErrorCodes.Controller_MessageDropped,\n\t\t\t\t\t\t\t\t\tprevResult,\n\t\t\t\t\t\t\t\t\ttransaction.stack,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tthis.controller.status === ControllerStatus.Jammed\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// A command could be sent, so the controller is no longer jammed\n\t\t\t\t\t\t\tthis.controller.setStatus(ControllerStatus.Ready);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!prevResult.isOK()) {\n\t\t\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\t\t\"The node did not acknowledge the command\",\n\t\t\t\t\t\t\t\tZWaveErrorCodes.Controller_CallbackNOK,\n\t\t\t\t\t\t\t\tprevResult,\n\t\t\t\t\t\t\t\ttransaction.stack,\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\t// We got a result - it will be passed to the next iteration\n\t\t\t\t\tbreak attemptMessage;\n\t\t\t\t} catch (e: any) {\n\t\t\t\t\tlet zwError: ZWaveError;\n\t\t\t\t\tlet waitDurationMs = 0;\n\n\t\t\t\t\tif (!isZWaveError(e)) {\n\t\t\t\t\t\tzwError = createMessageDroppedUnexpectedError(e);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tisSendData(msg) && isMissingControllerCallback(e)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// The controller is unresponsive. Reject the transaction, so we can attempt to recover\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t} else if (isMissingControllerACK(e)) {\n\t\t\t\t\t\t\t// The controller is unresponsive. Reject the transaction, so we can attempt to recover\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t} else if (wasControllerReset(e)) {\n\t\t\t\t\t\t\t// The controller was reset unexpectedly. Reject the transaction, so we can attempt to recover\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\tisAnySendDataResponse(e.context)\n\t\t\t\t\t\t\t&& !e.context.wasSent\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// If a SendData command could not be queued, try again after a short delay\n\t\t\t\t\t\t\tqueueAttempts++;\n\t\t\t\t\t\t\tif (queueAttempts < 3) {\n\t\t\t\t\t\t\t\twaitDurationMs = 500;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\te.code === ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// We gave up on this command, so don't retry it\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tthis.mayRetrySerialAPICommand(\n\t\t\t\t\t\t\t\tmsg,\n\t\t\t\t\t\t\t\t// Ignore the number of attempts while jammed or where queuing failed\n\t\t\t\t\t\t\t\tattemptNumber - jammedAttempts - queueAttempts,\n\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tif (waitDurationMs) {\n\t\t\t\t\t\t\t\tawait wait(waitDurationMs, true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Retry the command\n\t\t\t\t\t\t\tcontinue attemptMessage;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tzwError = e;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Sending the command failed, reject the transaction\n\t\t\t\t\tthrow zwError;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// This transaction completed successfully, try the next one\n\t\ttransaction.setProgress({ state: TransactionState.Completed });\n\t}\n\n\t/**\n\t * Provides access to the result Promise for the currently executed serial API command\n\t */\n\tprivate _currentSerialAPICommandPromise:\n\t\t| DeferredPromise<Message | undefined>\n\t\t| undefined;\n\n\t/** Handles sequencing of queued Serial API commands */\n\tprivate async drainSerialAPIQueue(): Promise<void> {\n\t\tfor await (const item of this.serialAPIQueue) {\n\t\t\tconst { msg, transactionSource, result } = item;\n\t\t\tthis._currentSerialAPICommandPromise = result;\n\n\t\t\t// Attempt the command multiple times if necessary\n\t\t\tattempts: for (let attempt = 1;; attempt++) {\n\t\t\t\ttry {\n\t\t\t\t\tconst ret = await this.executeSerialAPICommand(\n\t\t\t\t\t\tmsg,\n\t\t\t\t\t\ttransactionSource,\n\t\t\t\t\t);\n\t\t\t\t\tresult.resolve(ret);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tisZWaveError(e)\n\t\t\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t\t\t&& e.context === \"CAN\"\n\t\t\t\t\t\t&& attempt < 3\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Retry up to 3 times if there are serial collisions\n\t\t\t\t\t\tawait wait(100);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// In all other cases, reject the transaction\n\t\t\t\t\tresult.reject(e as Error);\n\t\t\t\t} finally {\n\t\t\t\t\tthis._currentSerialAPICommandPromise = undefined;\n\t\t\t\t}\n\t\t\t\tbreak attempts;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate triggerQueues(): void {\n\t\tfor (const queue of this.queues) {\n\t\t\tqueue.trigger();\n\t\t}\n\t}\n\n\t/** Puts a message on the serial API queue and returns or throws the command result */\n\tprivate queueSerialAPICommand(\n\t\tmsg: Message,\n\t\ttransactionSource?: string,\n\t): Promise<Message | undefined> {\n\t\tconst result = createDeferredPromise<Message | undefined>();\n\t\tthis.serialAPIQueue.add({\n\t\t\tmsg,\n\t\t\ttransactionSource,\n\t\t\tresult,\n\t\t\t[Symbol.dispose]: () => {\n\t\t\t\t// Is called when the queue is aborted\n\t\t\t\tresult.reject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\"The message has been removed from the queue\",\n\t\t\t\t\t\tZWaveErrorCodes.Controller_MessageDropped,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\ttransactionSource,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t},\n\t\t});\n\n\t\treturn result;\n\t}\n\n\tprivate mayRetrySerialAPICommand(\n\t\tmsg: Message,\n\t\tattemptNumber: number,\n\t\terror: ZWaveError,\n\t): boolean {\n\t\t// Only retry Send Data, nothing else\n\t\tif (!isSendData(msg)) return false;\n\n\t\t// Don't try to resend SendData commands when the response times out\n\t\tif (\n\t\t\terror.code === ZWaveErrorCodes.Controller_Timeout\n\t\t\t&& error.context === \"response\"\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Don't try to resend multicast messages if they were already transmitted.\n\t\t// One or more nodes might have already reacted\n\t\tif (\n\t\t\t(msg instanceof SendDataMulticastRequest\n\t\t\t\t|| msg instanceof SendDataMulticastBridgeRequest)\n\t\t\t&& error.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn attemptNumber < msg.maxSendAttempts;\n\t}\n\n\t/**\n\t * Executes a Serial API command and returns or throws the result.\n\t * This method should not be called outside of {@link drainSerialAPIQueue}.\n\t */\n\tprivate async executeSerialAPICommand(\n\t\tmsg: Message,\n\t\ttransactionSource?: string,\n\t): Promise<Message | undefined> {\n\t\t// Give the command a callback ID if it needs one\n\t\tif (msg.needsCallbackId() && !msg.hasCallbackId()) {\n\t\t\tmsg.callbackId = this.getNextCallbackId();\n\t\t}\n\n\t\t// Work around an issue in the 700 series firmware where the ACK after a soft-reset has a random high nibble.\n\t\t// This was broken in 7.19, not fixed so far\n\t\tif (\n\t\t\tmsg.functionType === FunctionType.SoftReset\n\t\t\t&& this.controller.sdkVersionGte(\"7.19.0\")\n\t\t) {\n\t\t\tthis.serial?.ignoreAckHighNibbleOnce();\n\t\t}\n\n\t\tconst machine = createSerialAPICommandMachine(msg);\n\t\tthis.abortSerialAPICommand = createDeferredPromise();\n\t\tconst abortController = new AbortController();\n\n\t\tlet nextInput: SerialAPICommandMachineInput | undefined = {\n\t\t\tvalue: \"start\",\n\t\t};\n\n\t\ttry {\n\t\t\twhile (!machine.done) {\n\t\t\t\tif (nextInput == undefined) {\n\t\t\t\t\t// We should not be in a situation where we have no input for the state machine\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Serial API Command machine is in an invalid state: no input provided\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst transition = machine.next(nextInput);\n\t\t\t\tif (transition == undefined) {\n\t\t\t\t\t// We should not be in a situation where the state machine does not transition\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Serial API Command machine is in an invalid state: no transition taken\",\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// The input was used\n\t\t\t\tnextInput = undefined;\n\n\t\t\t\t// Transition to the new state\n\t\t\t\tmachine.transition(transition.newState);\n\n\t\t\t\t// Now check what needs to be done in the new state\n\t\t\t\tswitch (machine.state.value) {\n\t\t\t\t\tcase \"initial\":\n\t\t\t\t\t\t// This should never happen\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\"Serial API Command machine is in an invalid state: transitioned to initial state\",\n\t\t\t\t\t\t);\n\n\t\t\t\t\tcase \"sending\": {\n\t\t\t\t\t\tthis.driverLog.logMessage(msg, {\n\t\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Mark the message as sent immediately before actually sending\n\t\t\t\t\t\tmsg.markAsSent();\n\t\t\t\t\t\tconst data = await msg.serialize(\n\t\t\t\t\t\t\tthis.getEncodingContext(),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait this.writeSerial(data);\n\t\t\t\t\t\tnextInput = { value: \"message sent\" };\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"waitingForACK\": {\n\t\t\t\t\t\tconst controlFlow = await this.waitForMessageHeader(\n\t\t\t\t\t\t\t() => true,\n\t\t\t\t\t\t\tthis.options.timeouts.ack,\n\t\t\t\t\t\t).catch(() => \"timeout\" as const);\n\n\t\t\t\t\t\tif (controlFlow === \"timeout\") {\n\t\t\t\t\t\t\tnextInput = { value: \"timeout\" };\n\t\t\t\t\t\t} else if (controlFlow === MessageHeaders.ACK) {\n\t\t\t\t\t\t\tnextInput = { value: \"ACK\" };\n\t\t\t\t\t\t} else if (controlFlow === MessageHeaders.CAN) {\n\t\t\t\t\t\t\tnextInput = { value: \"CAN\" };\n\t\t\t\t\t\t} else if (controlFlow === MessageHeaders.NAK) {\n\t\t\t\t\t\t\tnextInput = { value: \"NAK\" };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"waitingForResponse\": {\n\t\t\t\t\t\tconst response = await Promise.race([\n\t\t\t\t\t\t\tthis.abortSerialAPICommand?.catch((e) =>\n\t\t\t\t\t\t\t\te as Error\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tthis.waitForMessage(\n\t\t\t\t\t\t\t\t(resp) => msg.isExpectedResponse(resp),\n\t\t\t\t\t\t\t\tmsg.getResponseTimeout()\n\t\t\t\t\t\t\t\t\t?? this.options.timeouts.response,\n\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\tabortController.signal,\n\t\t\t\t\t\t\t).catch(() => \"timeout\" as const),\n\t\t\t\t\t\t]);\n\n\t\t\t\t\t\tif (response instanceof Error) {\n\t\t\t\t\t\t\t// The command was aborted from the outside\n\t\t\t\t\t\t\t// Remove the pending wait entry\n\t\t\t\t\t\t\tabortController.abort();\n\t\t\t\t\t\t\tthrow response;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (response === \"timeout\") {\n\t\t\t\t\t\t\tif (isSendData(msg)) {\n\t\t\t\t\t\t\t\t// Timed out SendData commands must be aborted\n\t\t\t\t\t\t\t\tvoid this.abortSendData();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tnextInput = { value: \"timeout\" };\n\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\tisSuccessIndicator(response) && !response.isOK()\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tnextInput = { value: \"response NOK\", response };\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextInput = { value: \"response\", response };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"waitingForCallback\": {\n\t\t\t\t\t\tlet sendDataAbortTimeout: Timer | undefined;\n\t\t\t\t\t\tif (isSendData(msg)) {\n\t\t\t\t\t\t\t// We abort timed out SendData commands before the callback times out\n\t\t\t\t\t\t\tsendDataAbortTimeout = setTimer(() => {\n\t\t\t\t\t\t\t\tvoid this.abortSendData();\n\t\t\t\t\t\t\t}, this.options.timeouts.sendDataAbort).unref();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst callback = await Promise.race([\n\t\t\t\t\t\t\tthis.abortSerialAPICommand?.catch((e) =>\n\t\t\t\t\t\t\t\te as Error\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tthis.waitForMessage(\n\t\t\t\t\t\t\t\t(resp) => msg.isExpectedCallback(resp),\n\t\t\t\t\t\t\t\tmsg.getCallbackTimeout()\n\t\t\t\t\t\t\t\t\t?? this.options.timeouts.sendDataCallback,\n\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\tabortController.signal,\n\t\t\t\t\t\t\t).catch(() => \"timeout\" as const),\n\t\t\t\t\t\t]);\n\n\t\t\t\t\t\tsendDataAbortTimeout?.clear();\n\n\t\t\t\t\t\tif (callback instanceof Error) {\n\t\t\t\t\t\t\t// The command was aborted from the outside\n\t\t\t\t\t\t\t// Remove the pending wait entry\n\t\t\t\t\t\t\tabortController.abort();\n\t\t\t\t\t\t\tthrow callback;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (callback === \"timeout\") {\n\t\t\t\t\t\t\tnextInput = { value: \"timeout\" };\n\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\tisSuccessIndicator(callback) && !callback.isOK()\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tnextInput = { value: \"callback NOK\", callback };\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextInput = { value: \"callback\", callback };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"success\": {\n\t\t\t\t\t\treturn machine.state.result;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"failure\": {\n\t\t\t\t\t\tconst { reason, result } = machine.state;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\treason === \"callback NOK\"\n\t\t\t\t\t\t\t&& (isSendData(msg) || isTransmitReport(result))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// For messages that were sent to a node, a NOK callback still contains useful info we need to evaluate\n\t\t\t\t\t\t\t// ... so we treat it as a result\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow serialAPICommandErrorToZWaveError(\n\t\t\t\t\t\t\t\treason,\n\t\t\t\t\t\t\t\tmsg,\n\t\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\t\ttransactionSource,\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\t\t\t}\n\t\t} finally {\n\t\t\tthis.abortSerialAPICommand = undefined;\n\t\t}\n\t}\n\n\tprivate getQueueForTransaction(t: Transaction): TransactionQueue {\n\t\tif (\n\t\t\tt.priority === MessagePriority.Immediate\n\t\t\t|| t.priority === MessagePriority.ControllerImmediate\n\t\t) {\n\t\t\treturn this.immediateQueue;\n\t\t} else {\n\t\t\treturn this.queue;\n\t\t}\n\t}\n\n\t/**\n\t * Sends a message to the Z-Wave stick.\n\t * @param msg The message to send\n\t * @param options (optional) Options regarding the message transmission\n\t */\n\tpublic async sendMessage<TResponse extends Message = Message>(\n\t\tmsg: Message,\n\t\toptions: SendMessageOptions = {},\n\t): Promise<TResponse> {\n\t\tthis.ensureReady();\n\n\t\tlet node: ZWaveNode | undefined;\n\t\tif (hasNodeId(msg) || containsCC(msg)) {\n\t\t\tnode = this.tryGetNode(msg);\n\t\t}\n\n\t\tif (options.priority == undefined) {\n\t\t\toptions.priority = getDefaultPriority(msg);\n\t\t}\n\t\tif (options.priority == undefined) {\n\t\t\tconst className = msg.constructor.name;\n\t\t\tconst msgTypeName = FunctionType[msg.functionType];\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`No default priority has been defined for ${className} (${msgTypeName}), so you have to provide one for your message`,\n\t\t\t\tZWaveErrorCodes.Driver_NoPriority,\n\t\t\t);\n\t\t}\n\n\t\tif (options.supportCheck == undefined) options.supportCheck = true;\n\t\tif (\n\t\t\toptions.supportCheck\n\t\t\t&& this._controller != undefined\n\t\t\t&& !this._controller.isFunctionSupported(msg.functionType)\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support the ${\n\t\t\t\t\tFunctionType[msg.functionType]\n\t\t\t\t} function`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\t// When sending a message to a node that is known to be sleeping,\n\t\t// the priority must be WakeUp, so the message gets deprioritized\n\t\t// in comparison with messages to awake nodes.\n\t\t// However there are a few exceptions...\n\t\tif (\n\t\t\t!!node\n\t\t\t// Pings can be used to check if a node is really asleep, so they should be sent regardless\n\t\t\t&& !messageIsPing(msg)\n\t\t\t// Nodes that can sleep and support the Wake Up CC should have their messages queued for wakeup\n\t\t\t&& node.canSleep\n\t\t\t&& (node.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t\t\t// If we don't know the Wake Up support yet, also change the priority or RequestNodeInfoRequests will get stuck\n\t\t\t\t// in front of the queue\n\t\t\t\t|| node.interviewStage < InterviewStage.NodeInfo)\n\t\t\t// If we move multicasts to the wakeup queue, it is unlikely\n\t\t\t// that there is ever a points where all targets are awake\n\t\t\t&& !(msg instanceof SendDataMulticastRequest)\n\t\t\t&& !(msg instanceof SendDataMulticastBridgeRequest)\n\t\t\t// Nonces and responses to Supervision Get have to be sent immediately\n\t\t\t&& options.priority !== MessagePriority.Immediate\n\t\t) {\n\t\t\tif (options.priority === MessagePriority.NodeQuery) {\n\t\t\t\t// Remember that this transaction was part of an interview\n\t\t\t\toptions.tag = \"interview\";\n\t\t\t}\n\t\t\toptions.priority = MessagePriority.WakeUp;\n\t\t}\n\n\t\t// Create the transaction\n\t\tconst { generator, resultPromise } = createMessageGenerator(\n\t\t\tthis,\n\t\t\tthis.getEncodingContext(),\n\t\t\tmsg,\n\t\t\t(msg, _result) => {\n\t\t\t\tthis.handleSerialAPICommandResult(msg, options, _result);\n\t\t\t},\n\t\t);\n\t\tconst transaction = new Transaction(this, {\n\t\t\tmessage: msg,\n\t\t\tpriority: options.priority,\n\t\t\tparts: generator,\n\t\t\tpromise: resultPromise,\n\t\t\tlistener: options.onProgress,\n\t\t});\n\n\t\t// Configure its options\n\t\tif (options.changeNodeStatusOnMissingACK != undefined) {\n\t\t\ttransaction.changeNodeStatusOnTimeout =\n\t\t\t\toptions.changeNodeStatusOnMissingACK;\n\t\t}\n\t\tif (options.pauseSendThread === true) {\n\t\t\ttransaction.pauseSendThread = true;\n\t\t}\n\t\ttransaction.requestWakeUpOnDemand = !!options.requestWakeUpOnDemand;\n\t\ttransaction.tag = options.tag;\n\n\t\t// And queue it\n\t\tthis.getQueueForTransaction(transaction).add(transaction);\n\t\ttransaction.setProgress({ state: TransactionState.Queued });\n\n\t\t// If the transaction should expire, start the timeout\n\t\tlet expirationTimeout: Timer | undefined;\n\t\tif (options.expire) {\n\t\t\texpirationTimeout = setTimer(() => {\n\t\t\t\tvoid this.reduceQueues((t, _source) => {\n\t\t\t\t\tif (t === transaction) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttype: \"reject\",\n\t\t\t\t\t\t\tmessage: `The message has expired`,\n\t\t\t\t\t\t\tcode: ZWaveErrorCodes.Controller_MessageExpired,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\treturn { type: \"keep\" };\n\t\t\t\t});\n\t\t\t}, options.expire).unref();\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = (await resultPromise) as TResponse;\n\n\t\t\t// If this was a successful non-nonce message to a sleeping node, make sure it goes to sleep again\n\t\t\tlet maybeSendToSleep: boolean;\n\t\t\tif (isSendData(msg)) {\n\t\t\t\t// For SendData messages, make sure the message is not a nonce\n\t\t\t\tmaybeSendToSleep =\n\t\t\t\t\toptions.priority !== MessagePriority.Immediate\n\t\t\t\t\t// And that the result is either a response from the node\n\t\t\t\t\t// or a transmit report indicating success\n\t\t\t\t\t&& result\n\t\t\t\t\t&& (result.functionType\n\t\t\t\t\t\t\t=== FunctionType.BridgeApplicationCommand\n\t\t\t\t\t\t|| result.functionType\n\t\t\t\t\t\t\t=== FunctionType.ApplicationCommand\n\t\t\t\t\t\t|| (isSendDataTransmitReport(result) && result.isOK()));\n\t\t\t} else {\n\t\t\t\t// For other messages to the node, just check for successful completion. If the callback is not OK,\n\t\t\t\t// we might not be able to communicate with the node. Sending another message is not a good idea.\n\t\t\t\tmaybeSendToSleep = hasNodeId(msg)\n\t\t\t\t\t&& result\n\t\t\t\t\t&& isSuccessIndicator(result)\n\t\t\t\t\t&& result.isOK();\n\t\t\t}\n\n\t\t\tif (maybeSendToSleep && node && node.canSleep && !node.keepAwake) {\n\t\t\t\tsetImmediate(() => this.debounceSendNodeToSleep(node));\n\t\t\t}\n\n\t\t\t// Set the transaction progress to completed before resolving the Promise\n\t\t\ttransaction.setProgress({ state: TransactionState.Completed });\n\n\t\t\treturn result;\n\t\t} catch (e) {\n\t\t\tif (isZWaveError(e)) {\n\t\t\t\tif (\n\t\t\t\t\t// If a controller command failed (that is not SendData), pass the response/callback through\n\t\t\t\t\t(e.code === ZWaveErrorCodes.Controller_ResponseNOK\n\t\t\t\t\t\t|| e.code === ZWaveErrorCodes.Controller_CallbackNOK)\n\t\t\t\t\t&& e.context instanceof Message\n\t\t\t\t\t// We need to check the function type here because context can be the transmit reports\n\t\t\t\t\t&& e.context.functionType !== FunctionType.SendData\n\t\t\t\t\t&& e.context.functionType !== FunctionType.SendDataMulticast\n\t\t\t\t\t&& e.context.functionType !== FunctionType.SendDataBridge\n\t\t\t\t\t&& e.context.functionType\n\t\t\t\t\t\t!== FunctionType.SendDataMulticastBridge\n\t\t\t\t) {\n\t\t\t\t\tthis._controller?.incrementStatistics(\"messagesDroppedTX\");\n\t\t\t\t\treturn e.context as TResponse;\n\t\t\t\t} else if (e.code === ZWaveErrorCodes.Controller_NodeTimeout) {\n\t\t\t\t\t// If the node failed to respond in time, remember this for the statistics\n\t\t\t\t\tnode?.incrementStatistics(\"timeoutResponse\");\n\t\t\t\t}\n\t\t\t\t// Enrich errors with the transaction's stack instead of the internal stack\n\t\t\t\tif (!e.transactionSource) {\n\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\te.message,\n\t\t\t\t\t\te.code,\n\t\t\t\t\t\te.context,\n\t\t\t\t\t\ttransaction.stack,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\t// The transaction was handled, so it can no longer expire\n\t\t\texpirationTimeout?.clear();\n\t\t}\n\t}\n\n\t/** Wraps a CC in the correct SendData message to use for sending */\n\tpublic createSendDataMessage(\n\t\tcommand: CommandClass,\n\t\toptions: Omit<SendCommandOptions, keyof SendMessageOptions> = {},\n\t): SendDataMessage & ContainsCC {\n\t\t// Automatically encapsulate commands before sending\n\t\tif (options.autoEncapsulate !== false) {\n\t\t\tcommand = this.encapsulateCommands(command, options);\n\t\t}\n\n\t\tlet msg: SendDataMessage;\n\t\tif (command.isSinglecast() || command.isBroadcast()) {\n\t\t\tif (\n\t\t\t\tthis.controller.isFunctionSupported(FunctionType.SendDataBridge)\n\t\t\t) {\n\t\t\t\t// Prioritize Bridge commands when they are supported\n\t\t\t\tmsg = new SendDataBridgeRequest({\n\t\t\t\t\tsourceNodeId: this.ownNodeId,\n\t\t\t\t\tcommand,\n\t\t\t\t\tmaxSendAttempts: this._options.attempts.sendData,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tmsg = new SendDataRequest({\n\t\t\t\t\tcommand,\n\t\t\t\t\tmaxSendAttempts: this._options.attempts.sendData,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (command.isMulticast()) {\n\t\t\tif (\n\t\t\t\tthis.controller.isFunctionSupported(\n\t\t\t\t\tFunctionType.SendDataMulticastBridge,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\t// Prioritize Bridge commands when they are supported\n\t\t\t\tmsg = new SendDataMulticastBridgeRequest({\n\t\t\t\t\tsourceNodeId: this.ownNodeId,\n\t\t\t\t\tcommand,\n\t\t\t\t\tmaxSendAttempts: this._options.attempts.sendData,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tmsg = new SendDataMulticastRequest({\n\t\t\t\t\tcommand,\n\t\t\t\t\tmaxSendAttempts: this._options.attempts.sendData,\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`A CC must either be singlecast or multicast`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\t\t// Specify the number of send attempts for the request\n\t\tif (options.maxSendAttempts != undefined) {\n\t\t\tmsg.maxSendAttempts = options.maxSendAttempts;\n\t\t}\n\n\t\t// Specify transmit options for the request\n\t\tif (options.transmitOptions != undefined) {\n\t\t\tmsg.transmitOptions = options.transmitOptions;\n\t\t}\n\n\t\tif (!!options.reportTimeoutMs) {\n\t\t\tmsg.nodeUpdateTimeout = options.reportTimeoutMs;\n\t\t}\n\n\t\treturn msg as SendDataMessage & ContainsCC;\n\t}\n\n\t/**\n\t * Sends a command to a Z-Wave node. If the node returns a command in response, that command will be the return value.\n\t * If the command expects no response **or** the response times out, nothing will be returned\n\t * @param command The command to send. It will be encapsulated in a SendData[Multicast]Request.\n\t * @param options (optional) Options regarding the message transmission\n\t */\n\tprivate async sendCommandInternal<\n\t\tTResponse extends CCId = CCId,\n\t>(\n\t\tcommand: CommandClass,\n\t\toptions: Omit<\n\t\t\tSendCommandOptions,\n\t\t\t\"requestStatusUpdates\" | \"onUpdate\"\n\t\t> = {},\n\t): Promise<TResponse | undefined> {\n\t\tconst msg = this.createSendDataMessage(command, options);\n\t\ttry {\n\t\t\tconst resp = await this.sendMessage(msg, options);\n\n\t\t\t// And unwrap the response if there was any\n\t\t\tif (containsCC(resp)) {\n\t\t\t\tthis.unwrapCommands(resp);\n\t\t\t\treturn resp.command as unknown as TResponse;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\t// A timeout always has to be expected. In this case return nothing.\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t) {\n\t\t\t\tif (command.isSinglecast()) {\n\t\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\t\tcommand.nodeId,\n\t\t\t\t\t\te.message,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// We don't want to swallow any other errors\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Sends a supervised command to a Z-Wave node. When status updates are requested, the passed callback will be executed for every non-final update.\n\t * @param command The command to send\n\t * @param options (optional) Options regarding the message transmission\n\t */\n\tprivate async sendSupervisedCommand(\n\t\tcommand: SinglecastCC<CommandClass>,\n\t\toptions: SendCommandOptions & { useSupervision?: \"auto\" } = {\n\t\t\trequestStatusUpdates: false,\n\t\t},\n\t): Promise<SupervisionResult | undefined> {\n\t\t// Create the encapsulating CC so we have a session ID\n\t\tconst sessionId = this.getNextSupervisionSessionId(command.nodeId);\n\t\tcommand = SupervisionCC.encapsulate(\n\t\t\tcommand,\n\t\t\tsessionId,\n\t\t\toptions.requestStatusUpdates,\n\t\t);\n\n\t\tconst resp = await this.sendCommandInternal<SupervisionCCReport>(\n\t\t\tcommand,\n\t\t\toptions,\n\t\t);\n\t\tif (!resp) return;\n\n\t\t// If future updates are expected, listen for them\n\t\tif (options.requestStatusUpdates && resp.moreUpdatesFollow) {\n\t\t\tthis.ensureNodeSessions(command.nodeId).supervision.set(\n\t\t\t\t(command as SupervisionCCGet).sessionId,\n\t\t\t\toptions.onUpdate,\n\t\t\t);\n\t\t}\n\t\t// In any case, return the status\n\t\treturn resp.toSupervisionResult();\n\t}\n\n\t/**\n\t * Sends a command to a Z-Wave node. The return value depends on several factors:\n\t * * If the node returns a command in response, that command will be the return value.\n\t * * If the command is a SET-type command and Supervision CC can and should be used, a {@link SupervisionResult} will be returned.\n\t * * If the command expects no response **or** the response times out, nothing will be returned.\n\t *\n\t * @param command The command to send. It will be encapsulated in a SendData[Multicast]Request.\n\t * @param options (optional) Options regarding the message transmission\n\t */\n\tpublic async sendCommand<\n\t\tTResponse extends CCId | undefined = undefined,\n\t>(\n\t\tcommand: CommandClass,\n\t\toptions?: SendCommandOptions,\n\t): Promise<SendCommandReturnType<TResponse>> {\n\t\tif (options?.encapsulationFlags != undefined) {\n\t\t\tcommand.encapsulationFlags = options.encapsulationFlags;\n\t\t}\n\n\t\t// Use security encapsulation for CCs that are only supported securely, and multicast commands\n\t\tif (\n\t\t\tthis.isCCSecure(\n\t\t\t\tcommand.ccId,\n\t\t\t\tcommand.nodeId as number,\n\t\t\t\tcommand.endpointIndex,\n\t\t\t)\n\t\t\t|| options?.s2MulticastGroupId != undefined\n\t\t) {\n\t\t\tcommand.toggleEncapsulationFlag(EncapsulationFlags.Security, true);\n\t\t}\n\n\t\t// Only use supervision if...\n\t\tif (\n\t\t\t// ... not disabled\n\t\t\toptions?.useSupervision !== false\n\t\t\t// ... and it is legal for the command\n\t\t\t&& SupervisionCC.mayUseSupervision(this, command)\n\t\t) {\n\t\t\tconst result = await this.sendSupervisedCommand(command, options);\n\t\t\tif (result?.status !== SupervisionStatus.NoSupport) {\n\t\t\t\t// @ts-expect-error TS doesn't know we've narrowed the return type to match\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\t// The node should support supervision but it doesn't for this command. Remember this\n\t\t\tSupervisionCC.setCCSupportedWithSupervision(\n\t\t\t\tthis,\n\t\t\t\tcommand.getEndpoint(this)!,\n\t\t\t\tcommand.ccId,\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\t// And retry the command without supervision\n\t\t}\n\n\t\t// Fall back to non-supervised commands\n\t\tconst result = await this.sendCommandInternal(command, options);\n\n\t\t// When sending S2 multicast commands to supporting nodes, the singlecast followups\n\t\t// may use supervision. In this case, the multicast message generator returns a\n\t\t// synthetic SupervisionCCReport.\n\t\t// sendCommand is supposed to return a SupervisionResult though.\n\t\tif (\n\t\t\toptions?.s2MulticastGroupId != undefined\n\t\t\t&& result instanceof SupervisionCCReport\n\t\t) {\n\t\t\t// @ts-expect-error TS doesn't know we've narrowed the return type to match\n\t\t\treturn result.toSupervisionResult();\n\t\t}\n\n\t\t// @ts-expect-error TS doesn't know we've narrowed the return type to match\n\t\treturn result;\n\t}\n\n\t/** @internal */\n\tpublic async sendZWaveProtocolCC(\n\t\tcommand: ZWaveProtocolCC,\n\t\toptions: Pick<\n\t\t\tSendCommandOptions,\n\t\t\t\"changeNodeStatusOnMissingACK\" | \"maxSendAttempts\"\n\t\t> = {},\n\t): Promise<void> {\n\t\tawait this.sendCommandInternal(command, {\n\t\t\tpriority: MessagePriority.Controller,\n\t\t\t// No shenanigans, just send the raw command\n\t\t\tautoEncapsulate: false,\n\t\t\tuseSupervision: false,\n\t\t\tchangeNodeStatusOnMissingACK: options.changeNodeStatusOnMissingACK\n\t\t\t\t?? false,\n\t\t\tmaxSendAttempts: options.maxSendAttempts || 1,\n\t\t\ttransmitOptions: TransmitOptions.AutoRoute | TransmitOptions.ACK,\n\t\t});\n\t}\n\n\tprivate async abortSendData(): Promise<void> {\n\t\ttry {\n\t\t\tconst abort = new SendDataAbort();\n\t\t\tawait this.writeSerial(\n\t\t\t\tawait abort.serialize(this.getEncodingContext()),\n\t\t\t);\n\t\t\tthis.driverLog.logMessage(abort, {\n\t\t\t\tdirection: \"outbound\",\n\t\t\t});\n\n\t\t\t// We're bypassing the serial API machine, so we need to wait for the ACK ourselves\n\t\t\t// This could also cause a NAK or CAN, but we don't really care\n\t\t\tawait this.waitForMessageHeader(() => true, 500).catch(noop);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\t/**\n\t * Sends a low-level message like ACK, NAK or CAN immediately\n\t * @param header The low-level message to send\n\t */\n\tprivate writeHeader(header: MessageHeaders): Promise<void> {\n\t\t// ACK, CAN, NAK\n\t\treturn this.writeSerial(Uint8Array.from([header]));\n\t}\n\n\t/** Sends a raw datagram to the serialport (if that is open) */\n\tprivate async writeSerial(data: Uint8Array): Promise<void> {\n\t\treturn this.serial?.writeAsync(data);\n\t}\n\n\t/**\n\t * Waits until a matching message header is received or a timeout has elapsed. Returns the received message.\n\t *\n\t * @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected\n\t * @param predicate A predicate function to test all incoming message headers.\n\t */\n\tpublic waitForMessageHeader(\n\t\tpredicate: (header: MessageHeaders) => boolean,\n\t\ttimeout: number,\n\t): Promise<MessageHeaders> {\n\t\treturn new Promise<MessageHeaders>((resolve, reject) => {\n\t\t\tconst promise = createDeferredPromise<MessageHeaders>();\n\t\t\tconst entry: AwaitedMessageHeader = {\n\t\t\t\tpredicate,\n\t\t\t\thandler: (msg) => promise.resolve(msg),\n\t\t\t\ttimeout: undefined,\n\t\t\t};\n\t\t\tthis.awaitedMessageHeaders.push(entry);\n\t\t\tconst removeEntry = () => {\n\t\t\t\tentry.timeout?.clear();\n\t\t\t\tconst index = this.awaitedMessageHeaders.indexOf(entry);\n\t\t\t\tif (index !== -1) this.awaitedMessageHeaders.splice(index, 1);\n\t\t\t};\n\t\t\t// When the timeout elapses, remove the wait entry and reject the returned Promise\n\t\t\tentry.timeout = setTimer(() => {\n\t\t\t\tremoveEntry();\n\t\t\t\treject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`Received no matching serial frame within the provided timeout!`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_Timeout,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}, timeout);\n\t\t\t// When the promise is resolved, remove the wait entry and resolve the returned Promise\n\t\t\tvoid promise.then((cc) => {\n\t\t\t\tremoveEntry();\n\t\t\t\tresolve(cc);\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Waits until an unsolicited serial message is received or a timeout has elapsed. Returns the received message.\n\t *\n\t * **Note:** To wait for a certain CommandClass, better use {@link waitForCommand}.\n\t * @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected\n\t * @param predicate A predicate function to test all incoming messages.\n\t * @param refreshPredicate A predicate function to test partial messages. If this returns `true` for a message, the timer will be restarted.\n\t */\n\tpublic waitForMessage<T extends Message>(\n\t\tpredicate: (msg: Message) => boolean,\n\t\ttimeout: number,\n\t\trefreshPredicate?: (msg: Message) => boolean,\n\t\tabortSignal?: AbortSignal,\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst promise = createDeferredPromise<Message>();\n\t\t\tconst entry: AwaitedMessageEntry = {\n\t\t\t\tpredicate,\n\t\t\t\trefreshPredicate,\n\t\t\t\thandler: (msg) => promise.resolve(msg),\n\t\t\t\ttimeout: undefined,\n\t\t\t};\n\t\t\tthis.awaitedMessages.push(entry);\n\t\t\tconst removeEntry = () => {\n\t\t\t\tentry.timeout?.clear();\n\t\t\t\tconst index = this.awaitedMessages.indexOf(entry);\n\t\t\t\tif (index !== -1) this.awaitedMessages.splice(index, 1);\n\t\t\t};\n\t\t\t// When the timeout elapses, remove the wait entry and reject the returned Promise\n\t\t\tentry.timeout = setTimer(() => {\n\t\t\t\tremoveEntry();\n\t\t\t\treject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`Received no matching message within the provided timeout!`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_Timeout,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}, timeout);\n\t\t\t// When the promise is resolved, remove the wait entry and resolve the returned Promise\n\t\t\tvoid promise.then((cc) => {\n\t\t\t\tremoveEntry();\n\t\t\t\tresolve(cc as T);\n\t\t\t});\n\t\t\t// When the abort signal is used, silently remove the wait entry\n\t\t\tabortSignal?.addEventListener(\"abort\", () => {\n\t\t\t\tremoveEntry();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Waits until a CommandClass is received or a timeout has elapsed. Returns the received command.\n\t * @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected\n\t * @param predicate A predicate function to test all incoming command classes\n\t */\n\tpublic waitForCommand<T extends CCId>(\n\t\tpredicate: (cc: CCId) => boolean,\n\t\ttimeout: number,\n\t\tabortSignal?: AbortSignal,\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst promise = createDeferredPromise<CCId>();\n\t\t\tconst entry: AwaitedCommandEntry = {\n\t\t\t\tpredicate,\n\t\t\t\thandler: (cc) => promise.resolve(cc),\n\t\t\t\ttimeout: undefined,\n\t\t\t};\n\t\t\tthis.awaitedCommands.push(entry);\n\t\t\tconst removeEntry = () => {\n\t\t\t\tentry.timeout?.clear();\n\t\t\t\tconst index = this.awaitedCommands.indexOf(entry);\n\t\t\t\tif (index !== -1) this.awaitedCommands.splice(index, 1);\n\t\t\t};\n\t\t\t// When the timeout elapses, remove the wait entry and reject the returned Promise\n\t\t\tentry.timeout = setTimer(() => {\n\t\t\t\tremoveEntry();\n\t\t\t\treject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`Received no matching command within the provided timeout!`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_NodeTimeout,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}, timeout);\n\t\t\t// When the promise is resolved, remove the wait entry and resolve the returned Promise\n\t\t\tvoid promise.then((cc) => {\n\t\t\t\tremoveEntry();\n\t\t\t\tresolve(cc as T);\n\t\t\t});\n\t\t\t// When the abort signal is used, silently remove the wait entry\n\t\t\tabortSignal?.addEventListener(\"abort\", () => {\n\t\t\t\tremoveEntry();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Calls the given handler function every time a CommandClass is received that matches the given predicate.\n\t * @param predicate A predicate function to test all incoming command classes\n\t */\n\tpublic registerCommandHandler<T extends CCId>(\n\t\tpredicate: (cc: CCId) => boolean,\n\t\thandler: (cc: T) => void,\n\t): {\n\t\tunregister: () => void;\n\t} {\n\t\tconst entry: AwaitedCommandEntry = {\n\t\t\tpredicate,\n\t\t\thandler: (cc) => handler(cc as T),\n\t\t\ttimeout: undefined,\n\t\t};\n\t\tthis.awaitedCommands.push(entry);\n\t\tconst removeEntry = () => {\n\t\t\tentry.timeout?.clear();\n\t\t\tconst index = this.awaitedCommands.indexOf(entry);\n\t\t\tif (index !== -1) this.awaitedCommands.splice(index, 1);\n\t\t};\n\n\t\treturn {\n\t\t\tunregister: removeEntry,\n\t\t};\n\t}\n\n\tprivate handleFailedTransaction(\n\t\ttransaction: Transaction,\n\t\terror: ZWaveError,\n\t): void {\n\t\t// If a node failed to respond in time, it might be sleeping\n\t\tif (this.isMissingNodeACK(transaction, error)) {\n\t\t\tif (this.handleMissingNodeACK(transaction as any, error)) return;\n\t\t} else if (isMissingControllerACK(error)) {\n\t\t\tif (this.handleMissingControllerACK(transaction, error)) return;\n\t\t} else if (\n\t\t\t// 700/800 series controllers can be jammed due to a bug,\n\t\t\t// which a soft-reset is supposed to work around\n\t\t\tisSendData(transaction.message)\n\t\t\t&& this.controller.status === ControllerStatus.Jammed\n\t\t) {\n\t\t\tif (this.handleJammedController(transaction, error)) return;\n\t\t} else if (\n\t\t\tisSendData(transaction.message)\n\t\t\t&& (isMissingControllerResponse(error)\n\t\t\t\t|| isMissingControllerCallback(error))\n\t\t) {\n\t\t\tif (\n\t\t\t\tthis.handleMissingSendDataResponseOrCallback(transaction, error)\n\t\t\t) return;\n\t\t} else if (wasControllerReset(error)) {\n\t\t\t// The controller was reset in the middle of a transaction.\n\t\t\t// Re-queue the transaction, so it can get handled again\n\t\t\t// Its message generator may have finished, so reset that too.\n\t\t\ttransaction.reset();\n\t\t\tthis.getQueueForTransaction(transaction).add(\n\t\t\t\ttransaction.clone(),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.rejectTransaction(transaction, error);\n\t}\n\n\tprivate rejectTransaction(\n\t\ttransaction: Transaction,\n\t\terror: ZWaveError,\n\t): void {\n\t\ttransaction.setProgress({\n\t\t\tstate: TransactionState.Failed,\n\t\t\treason: error.message,\n\t\t});\n\n\t\ttransaction.abort(error);\n\t}\n\n\tprivate resolveTransaction(\n\t\ttransaction: Transaction,\n\t\tresult?: Message,\n\t): void {\n\t\ttransaction.abort(result);\n\t}\n\n\t/** Checks if a message is allowed to go into the wakeup queue */\n\tprivate mayMoveToWakeupQueue(transaction: Transaction): boolean {\n\t\tconst msg = transaction.message;\n\t\tswitch (true) {\n\t\t\t// Pings, nonces and Supervision Reports will block the send queue until wakeup, so they must be dropped\n\t\t\tcase messageIsPing(msg):\n\t\t\tcase transaction.priority === MessagePriority.Immediate:\n\t\t\t// We also don't want to immediately send the node to sleep when it wakes up\n\t\t\tcase containsCC(msg)\n\t\t\t\t&& msg.command instanceof WakeUpCCNoMoreInformation:\n\t\t\t// compat queries because they will be recreated when the node wakes up\n\t\t\tcase transaction.tag === \"compat\":\n\t\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/** Moves all messages for a given node into the wakeup queue */\n\tprivate moveMessagesToWakeupQueue(nodeId: number): void {\n\t\tconst reject: TransactionReducerResult = {\n\t\t\ttype: \"reject\",\n\t\t\tmessage: `The node is asleep`,\n\t\t\tcode: ZWaveErrorCodes.Controller_MessageDropped,\n\t\t};\n\t\tconst requeue: TransactionReducerResult = {\n\t\t\ttype: \"requeue\",\n\t\t\tpriority: MessagePriority.WakeUp,\n\t\t\t// Reset the transaction so it doesn't simply resolve to `undefined` when we attempt to continue it\n\t\t\treset: true,\n\t\t};\n\t\tconst requeueAndTagAsInterview: TransactionReducerResult = {\n\t\t\t...requeue,\n\t\t\ttag: \"interview\",\n\t\t};\n\n\t\tvoid this.reduceQueues((transaction, _source) => {\n\t\t\tconst msg = transaction.message;\n\t\t\tif (msg.getNodeId() !== nodeId) return { type: \"keep\" };\n\t\t\t// Drop all messages that are not allowed in the wakeup queue\n\t\t\t// For all other messages, change the priority to wakeup\n\t\t\treturn this.mayMoveToWakeupQueue(transaction)\n\t\t\t\t? transaction.priority === MessagePriority.NodeQuery\n\t\t\t\t\t? requeueAndTagAsInterview\n\t\t\t\t\t: requeue\n\t\t\t\t: reject;\n\t\t});\n\t}\n\n\t/**\n\t * @internal\n\t * Rejects all pending transactions that match a predicate and removes them from the send queue\n\t */\n\tpublic rejectTransactions(\n\t\tpredicate: (t: Transaction) => boolean,\n\t\terrorMsg: string = `The message has been removed from the queue`,\n\t\terrorCode: ZWaveErrorCodes = ZWaveErrorCodes.Controller_MessageDropped,\n\t): Promise<void> {\n\t\treturn this.reduceQueues((transaction, _source) => {\n\t\t\tif (predicate(transaction)) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"reject\",\n\t\t\t\t\tmessage: errorMsg,\n\t\t\t\t\tcode: errorCode,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\treturn { type: \"keep\" };\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * @internal\n\t * Rejects all pending transactions for a node and removes them from the send queue\n\t */\n\tpublic rejectAllTransactionsForNode(\n\t\tnodeId: number,\n\t\terrorMsg: string = `The node is dead`,\n\t\terrorCode: ZWaveErrorCodes = ZWaveErrorCodes.Controller_MessageDropped,\n\t): Promise<void> {\n\t\treturn this.rejectTransactions(\n\t\t\t(t) => t.message.getNodeId() === nodeId,\n\t\t\terrorMsg,\n\t\t\terrorCode,\n\t\t);\n\t}\n\n\t/**\n\t * Pauses the send queue, avoiding commands to be sent to the controller\n\t */\n\tprivate pauseSendQueue(): void {\n\t\tthis.queuePaused = true;\n\t}\n\n\t/**\n\t * Unpauses the send queue, allowing commands to be sent to the controller again\n\t */\n\tprivate unpauseSendQueue(): void {\n\t\tthis.queuePaused = false;\n\t\tthis.triggerQueues();\n\t}\n\n\tprivate reduceQueues(reducer: TransactionReducer): Promise<void> {\n\t\t// This function MUST not be async, because this can introduce a\n\t\t// race condition caused by the microtick delay\n\t\treturn Promise\n\t\t\t.all(this.queues.map((queue) => this.reduceQueue(queue, reducer)))\n\t\t\t.then(noop);\n\t}\n\n\tprivate reduceQueue(\n\t\tqueue: TransactionQueue,\n\t\treducer: TransactionReducer,\n\t): Promise<void> {\n\t\tconst dropQueued: Transaction[] = [];\n\t\tlet stopActive: Transaction | undefined;\n\t\tconst requeue: Transaction[] = [];\n\n\t\tconst reduceTransaction: (\n\t\t\t...args: Parameters<TransactionReducer>\n\t\t) => void = (transaction, source) => {\n\t\t\tconst reducerResult = reducer(transaction, source);\n\t\t\tswitch (reducerResult.type) {\n\t\t\t\tcase \"drop\":\n\t\t\t\t\tif (source === \"queue\") {\n\t\t\t\t\t\tdropQueued.push(transaction);\n\n\t\t\t\t\t\t// This will silently drop the transaction, so awaiting it will never resolve.\n\t\t\t\t\t\t// At least notify the listeners about it.\n\t\t\t\t\t\ttransaction.setProgress({\n\t\t\t\t\t\t\tstate: TransactionState.Failed,\n\t\t\t\t\t\t\treason: \"The message was dropped\",\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstopActive = transaction;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"requeue\":\n\t\t\t\t\tif (reducerResult.priority != undefined) {\n\t\t\t\t\t\ttransaction.priority = reducerResult.priority;\n\t\t\t\t\t}\n\t\t\t\t\tif (reducerResult.tag != undefined) {\n\t\t\t\t\t\ttransaction.tag = reducerResult.tag;\n\t\t\t\t\t}\n\t\t\t\t\tif (reducerResult.reset) {\n\t\t\t\t\t\ttransaction.reset();\n\t\t\t\t\t}\n\t\t\t\t\tif (source === \"active\") stopActive = transaction;\n\t\t\t\t\trequeue.push(transaction);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"resolve\":\n\t\t\t\t\tthis.resolveTransaction(transaction, reducerResult.message);\n\t\t\t\t\tif (source === \"queue\") {\n\t\t\t\t\t\tdropQueued.push(transaction);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstopActive = transaction;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"reject\":\n\t\t\t\t\tthis.rejectTransaction(\n\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\treducerResult.message,\n\t\t\t\t\t\t\treducerResult.code,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\ttransaction.stack,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tif (source === \"queue\") {\n\t\t\t\t\t\tdropQueued.push(transaction);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstopActive = transaction;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t};\n\n\t\tfor (const transaction of queue.transactions) {\n\t\t\treduceTransaction(transaction, \"queue\");\n\t\t}\n\n\t\tif (queue.currentTransaction) {\n\t\t\treduceTransaction(queue.currentTransaction, \"active\");\n\t\t}\n\n\t\t// Now we know what to do with the transactions\n\t\tqueue.remove(...dropQueued, ...requeue);\n\t\tconst requeued = requeue.map((t) => t.clone());\n\t\tqueue.add(...requeued);\n\n\t\t// Notify listeners about re-queued transactions\n\t\tfor (const t of requeued) {\n\t\t\tt.setProgress({ state: TransactionState.Queued });\n\t\t}\n\n\t\t// Abort ongoing SendData messages that should be dropped\n\t\tif (isSendData(stopActive?.message)) {\n\t\t\treturn this.abortSendData();\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\n\t/** @internal */\n\tpublic resolvePendingPings(nodeId: number): void {\n\t\t// When a previously sleeping node sends a NIF after a ping was sent to it, but not acknowledged yet,\n\t\t// the node is awake, but the ping would fail. Resolve pending pings, so communication can continue.\n\t\tfor (const { currentTransaction } of this.queues) {\n\t\t\tif (!currentTransaction) continue;\n\t\t\tconst msg = currentTransaction.parts.current;\n\t\t\tif (!!msg && messageIsPing(msg) && msg.getNodeId() === nodeId) {\n\t\t\t\t// The pending transaction is a ping. Short-circuit its message generator by throwing something that's not an error\n\t\t\t\tcurrentTransaction.abort(undefined);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Helper function to read and convert potentially existing values from the network cache\n\t */\n\tpublic cacheGet<T>(\n\t\tcacheKey: string,\n\t\toptions?: {\n\t\t\treviver?: (value: any) => T;\n\t\t},\n\t): T | undefined {\n\t\tlet ret = this.networkCache.get(cacheKey);\n\t\tif (ret !== undefined && typeof options?.reviver === \"function\") {\n\t\t\ttry {\n\t\t\t\tret = options.reviver(ret);\n\t\t\t} catch {\n\t\t\t\t// ignore, invalid entry\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/**\n\t * @internal\n\t * Helper function to convert values and write them to the network cache\n\t */\n\tpublic cacheSet<T>(\n\t\tcacheKey: string,\n\t\tvalue: T | undefined,\n\t\toptions?: {\n\t\t\tserializer?: (value: T) => any;\n\t\t},\n\t): void {\n\t\tif (value !== undefined && typeof options?.serializer === \"function\") {\n\t\t\tvalue = options.serializer(value);\n\t\t}\n\n\t\tif (value === undefined) {\n\t\t\tthis.networkCache.delete(cacheKey);\n\t\t} else {\n\t\t\tthis.networkCache.set(cacheKey, value);\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Helper function to find multiple existing values from the network cache\n\t */\n\tpublic cacheList<T>(\n\t\tprefix: string,\n\t\toptions?: {\n\t\t\treviver?: (value: any) => T;\n\t\t},\n\t): Record<string, T> {\n\t\tconst ret: Record<string, T> = {};\n\t\tfor (const entry of this.networkCache.entries()) {\n\t\t\tconst key = entry[0];\n\t\t\tif (!key.startsWith(prefix)) continue;\n\t\t\tlet value = entry[1];\n\t\t\tif (value === undefined) continue;\n\t\t\tif (typeof options?.reviver === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\tvalue = options.reviver(value);\n\t\t\t\t} catch {\n\t\t\t\t\t// invalid entry\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tret[key] = value;\n\t\t}\n\t\treturn ret;\n\t}\n\n\tprivate cachePurge(prefix: string): void {\n\t\tfor (const key of this.networkCache.keys()) {\n\t\t\tif (key.startsWith(prefix)) {\n\t\t\t\tthis.networkCache.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Restores a previously stored Z-Wave network state from cache to speed up the startup process\n\t */\n\tpublic async restoreNetworkStructureFromCache(): Promise<void> {\n\t\tif (\n\t\t\t!this._controller\n\t\t\t|| !this.controller.homeId\n\t\t\t|| !this._networkCache\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._networkCache.size <= 1) {\n\t\t\t// If the size is 0 or 1, the cache is empty, so we cannot restore it\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Cache file for homeId ${\n\t\t\t\t\tnum2hex(\n\t\t\t\t\t\tthis.controller.homeId,\n\t\t\t\t\t)\n\t\t\t\t} found, attempting to restore the network from cache...`,\n\t\t\t);\n\t\t\tawait this.controller.deserialize();\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Restoring the network from cache was successful!`,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tconst message = `Restoring the network from cache failed: ${\n\t\t\t\tgetErrorMessage(\n\t\t\t\t\te,\n\t\t\t\t\ttrue,\n\t\t\t\t)\n\t\t\t}`;\n\t\t\tthis.emit(\n\t\t\t\t\"error\",\n\t\t\t\tnew ZWaveError(message, ZWaveErrorCodes.Driver_InvalidCache),\n\t\t\t);\n\t\t\tthis.driverLog.print(message, \"error\");\n\t\t}\n\t}\n\n\tprivate sendNodeToSleepTimers = new Map<number, Timer>();\n\t/**\n\t * @internal\n\t * Marks a node for a later sleep command. Every call refreshes the period until the node actually goes to sleep\n\t */\n\tpublic debounceSendNodeToSleep(\n\t\tnode: ZWaveNodeBase & NodeSchedulePoll & NodeValues & NodeWakeup,\n\t): void {\n\t\t// TODO: This should be a single command to the send thread\n\t\t// Delete old timers if any exist\n\t\tif (this.sendNodeToSleepTimers.has(node.id)) {\n\t\t\tthis.sendNodeToSleepTimers.get(node.id)?.clear();\n\t\t}\n\n\t\t// Sends a node to sleep if it has no more messages.\n\t\tconst sendNodeToSleep = (\n\t\t\tnode: ZWaveNodeBase & NodeSchedulePoll & NodeWakeup,\n\t\t): void => {\n\t\t\tthis.sendNodeToSleepTimers.delete(node.id);\n\t\t\tif (!this.hasPendingMessages(node)) {\n\t\t\t\tvoid node.sendNoMoreInformation().catch(() => {\n\t\t\t\t\t/* ignore errors */\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\n\t\t// If a sleeping node has no messages pending (and supports the Wake Up CC), we may send it back to sleep\n\t\tif (\n\t\t\tnode.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t\t&& !this.hasPendingMessages(node)\n\t\t) {\n\t\t\tconst wakeUpInterval = node.getValue<number>(\n\t\t\t\tWakeUpCCValues.wakeUpInterval.id,\n\t\t\t);\n\t\t\t// GH#2179: when a device only wakes up manually, don't send it back to sleep\n\t\t\t// Best case, the user wanted to interact with it.\n\t\t\t// Worst case, the device won't ACK this and cause a delay\n\t\t\tif (wakeUpInterval !== 0) {\n\t\t\t\tthis.sendNodeToSleepTimers.set(\n\t\t\t\t\tnode.id,\n\t\t\t\t\tsetTimer(\n\t\t\t\t\t\t() => sendNodeToSleep(node),\n\t\t\t\t\t\tthis.options.timeouts.sendToSleep,\n\t\t\t\t\t).unref(),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Computes the maximum net CC payload size for the given CC or SendDataRequest */\n\tpublic computeNetCCPayloadSize(\n\t\tcommandOrMsg:\n\t\t\t| CommandClass\n\t\t\t| (SendDataRequest | SendDataBridgeRequest) & ContainsCC,\n\t\tignoreEncapsulation: boolean = false,\n\t): number {\n\t\t// Recreate the correct encapsulation structure\n\t\tlet msg: (SendDataRequest | SendDataBridgeRequest) & ContainsCC;\n\t\tif (isSendDataSinglecast(commandOrMsg)) {\n\t\t\tmsg = commandOrMsg;\n\t\t} else {\n\t\t\tconst SendDataConstructor = this.getSendDataSinglecastConstructor();\n\t\t\tmsg = new SendDataConstructor({\n\t\t\t\tsourceNodeId: this.ownNodeId,\n\t\t\t\tcommand: commandOrMsg,\n\t\t\t}) as (SendDataRequest | SendDataBridgeRequest) & ContainsCC;\n\t\t}\n\t\tif (!ignoreEncapsulation) {\n\t\t\tmsg.command = this.encapsulateCommands(\n\t\t\t\tmsg.command,\n\t\t\t) as SinglecastCC<CommandClass>;\n\t\t}\n\t\treturn msg.command.getMaxPayloadLength(this.getMaxPayloadLength(msg));\n\t}\n\n\t/** Computes the maximum payload size that can be transmitted with the given message */\n\tpublic getMaxPayloadLength(msg: SendDataMessage): number {\n\t\tconst nodeId = msg.getNodeId();\n\n\t\t// For ZWLR, the result is simply the maximum payload size\n\t\tif (\n\t\t\tnodeId != undefined\n\t\t\t&& isLongRangeNodeId(nodeId)\n\t\t\t&& this._controller?.maxPayloadSizeLR\n\t\t) {\n\t\t\treturn this._controller.maxPayloadSizeLR;\n\t\t}\n\n\t\t// For ZW Classic, it depends on the frame type and transmit options\n\t\tconst maxExplorerPayloadSinglecast = this._controller?.maxPayloadSize\n\t\t\t?? 46;\n\t\tif (isSendDataSinglecast(msg)) {\n\t\t\t// From INS13954-7, chapter 4.3.3.1.5\n\t\t\tif (msg.transmitOptions & TransmitOptions.Explore) {\n\t\t\t\treturn maxExplorerPayloadSinglecast;\n\t\t\t}\n\t\t\tif (msg.transmitOptions & TransmitOptions.AutoRoute) {\n\t\t\t\treturn maxExplorerPayloadSinglecast + 2;\n\t\t\t}\n\t\t\treturn maxExplorerPayloadSinglecast + 8;\n\t\t} else {\n\t\t\t// Multicast needs space for the nodes bitmask\n\t\t\tconst maxExplorerPayloadMulticast = maxExplorerPayloadSinglecast\n\t\t\t\t- NUM_NODEMASK_BYTES;\n\n\t\t\t// From INS13954-13, chapter 4.3.3.6\n\t\t\tif (msg.transmitOptions & TransmitOptions.ACK) {\n\t\t\t\tif (msg.transmitOptions & TransmitOptions.Explore) {\n\t\t\t\t\treturn maxExplorerPayloadMulticast;\n\t\t\t\t}\n\t\t\t\tif (msg.transmitOptions & TransmitOptions.AutoRoute) {\n\t\t\t\t\treturn maxExplorerPayloadMulticast + 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn maxExplorerPayloadMulticast + 8;\n\t\t}\n\t}\n\n\tpublic async exceedsMaxPayloadLength(\n\t\tmsg: SendDataMessage,\n\t): Promise<boolean> {\n\t\tconst serializedCC = await msg.serializeCC(\n\t\t\tthis.getEncodingContext(),\n\t\t);\n\t\treturn serializedCC.length > this.getMaxPayloadLength(msg);\n\t}\n\n\t/** Determines time in milliseconds to wait for a report from a node */\n\tpublic getReportTimeout(msg: Message): number {\n\t\tconst node = this.tryGetNode(msg);\n\n\t\treturn (\n\t\t\t// If there's a message-specific timeout, use that\n\t\t\tmsg.nodeUpdateTimeout\n\t\t\t\t// If the node has a compat flag to override the timeout, use that\n\t\t\t\t?? node?.deviceConfig?.compat?.reportTimeout\n\t\t\t\t// otherwise use the driver option\n\t\t\t\t?? this._options.timeouts.report\n\t\t);\n\t}\n\n\t/** Returns the preferred constructor to use for singlecast SendData commands */\n\tpublic getSendDataSinglecastConstructor():\n\t\t| typeof SendDataRequest\n\t\t| typeof SendDataBridgeRequest\n\t{\n\t\treturn this._controller?.isFunctionSupported(\n\t\t\t\tFunctionType.SendDataBridge,\n\t\t\t)\n\t\t\t? SendDataBridgeRequest\n\t\t\t: SendDataRequest;\n\t}\n\n\t/** Returns the preferred constructor to use for multicast SendData commands */\n\tpublic getSendDataMulticastConstructor():\n\t\t| typeof SendDataMulticastRequest\n\t\t| typeof SendDataMulticastBridgeRequest\n\t{\n\t\treturn this._controller?.isFunctionSupported(\n\t\t\t\tFunctionType.SendDataMulticastBridge,\n\t\t\t)\n\t\t\t? SendDataMulticastBridgeRequest\n\t\t\t: SendDataMulticastRequest;\n\t}\n\n\t/**\n\t * Checks whether there is a compatible update for the currently installed config package.\n\t * Returns the new version if there is an update, `undefined` otherwise.\n\t */\n\tpublic async checkForConfigUpdates(\n\t\tsilent: boolean = false,\n\t): Promise<string | undefined> {\n\t\tthis.ensureReady();\n\n\t\ttry {\n\t\t\tif (!silent) {\n\t\t\t\tthis.driverLog.print(\"Checking for configuration updates...\");\n\t\t\t}\n\t\t\tconst ret = await checkForConfigUpdates(this.configVersion);\n\t\t\tif (ret) {\n\t\t\t\tif (!silent) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Configuration update available: ${ret}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (!silent) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"No configuration update available...\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(getErrorMessage(e), \"error\");\n\t\t}\n\t}\n\n\tprivate _installConfigUpdatePromise: Promise<boolean> | undefined;\n\n\t/**\n\t * Installs an update for the embedded configuration DB if there is a compatible one.\n\t * Returns `true` when an update was installed, `false` otherwise.\n\t *\n\t * **Note:** Bugfixes and changes to device configuration generally require a restart or re-interview to take effect.\n\t */\n\tpublic async installConfigUpdate(): Promise<boolean> {\n\t\tthis.ensureReady();\n\n\t\tif (this._installConfigUpdatePromise) {\n\t\t\treturn this._installConfigUpdatePromise;\n\t\t}\n\t\tthis._installConfigUpdatePromise = this.installConfigUpdateInternal();\n\n\t\ttry {\n\t\t\treturn await this._installConfigUpdatePromise;\n\t\t} finally {\n\t\t\tthis._installConfigUpdatePromise = undefined;\n\t\t}\n\t}\n\n\tprivate async installConfigUpdateInternal(): Promise<boolean> {\n\t\tconst newVersion = await this.checkForConfigUpdates(true);\n\t\tif (!newVersion) return false;\n\n\t\tconst extConfigDir = this.configManager.externalConfigDir;\n\t\tif (!this.configManager.useExternalConfig || !extConfigDir) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Cannot update configuration DB update - external config directory is not set`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driverLog.print(\n\t\t\t`Installing version ${newVersion} of configuration DB...`,\n\t\t);\n\t\ttry {\n\t\t\tawait installConfigUpdate(\n\t\t\t\tthis.bindings.fs,\n\t\t\t\tnewVersion,\n\t\t\t\t{ configDir: extConfigDir },\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(getErrorMessage(e), \"error\");\n\t\t\treturn false;\n\t\t}\n\t\tthis.driverLog.print(\n\t\t\t`Configuration DB updated to version ${newVersion}, activating...`,\n\t\t);\n\n\t\t// Reload the config files\n\t\tawait this.configManager.loadAll();\n\n\t\t// Now try to apply them to all known devices\n\t\tif (this._controller) {\n\t\t\tfor (const node of this._controller.nodes.values()) {\n\t\t\t\tif (node.ready) await node[\"loadDeviceConfig\"]();\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t// region OTW Firmware Updates\n\n\tprivate _otwFirmwareUpdateInProgress: boolean = false;\n\n\t/**\n\t * Returns whether a firmware update is in progress for the Z-Wave module.\n\t */\n\tpublic isOTWFirmwareUpdateInProgress(): boolean {\n\t\treturn this._otwFirmwareUpdateInProgress;\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<OTWFirmwareUpdateResult> {\n\t\t// Don't interrupt ongoing OTA firmware updates\n\t\tif (this._controller?.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.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.OTW_Update_Busy);\n\t\t}\n\n\t\t// Don't allow updating firmware when the controller is currently updating its own firmware\n\t\tif (this.isOTWFirmwareUpdateInProgress()) {\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.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.OTW_Update_Busy);\n\t\t}\n\n\t\t// When in bootloader mode, we can use the 700 series update method\n\t\tif (this.mode === DriverMode.Bootloader) {\n\t\t\treturn this.firmwareUpdateOTW700(data);\n\t\t} else if (this.mode === DriverMode.SerialAPI) {\n\t\t\tif (this.controller.sdkVersionGte(\"7.0\")) {\n\t\t\t\t// This is at least a 700 series controller, so we can use the 700 series update method\n\t\t\t\treturn this.firmwareUpdateOTW700(data);\n\t\t\t} else if (\n\t\t\t\tthis.controller.sdkVersionGte(\"6.50.0\")\n\t\t\t\t&& this.controller.supportedFunctionTypes\n\t\t\t\t\t?.includes(FunctionType.FirmwareUpdateNVM)\n\t\t\t) {\n\t\t\t\t// This is a 500 series controller, use the 500 series update method\n\t\t\t\tconst wasUpdated = await this.firmwareUpdateOTW500(data);\n\t\t\t\tif (wasUpdated.success) {\n\t\t\t\t\t// After updating the firmware on 500 series sticks, we MUST soft-reset them\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Activating new firmware and restarting driver...\",\n\t\t\t\t\t);\n\t\t\t\t\tawait this.softResetAndRestart();\n\t\t\t\t}\n\t\t\t\treturn wasUpdated;\n\t\t\t}\n\t\t} else if (this.mode === DriverMode.CLI) {\n\t\t\t// If the CLI has an option to enter bootloader, we can use the 700 series update method,\n\t\t\t// since it tries to execute that.\n\t\t\treturn this.firmwareUpdateOTW700(data);\n\t\t}\n\n\t\tthrow new ZWaveError(\n\t\t\t`Firmware updates are not supported on this Z-Wave module`,\n\t\t\tZWaveErrorCodes.Controller_NotSupported,\n\t\t);\n\t}\n\n\tprivate async firmwareUpdateOTW500(\n\t\tdata: Uint8Array,\n\t): Promise<OTWFirmwareUpdateResult> {\n\t\tthis._otwFirmwareUpdateInProgress = true;\n\t\tlet turnedRadioOff = false;\n\t\ttry {\n\t\t\tthis.controllerLog.print(\"Beginning firmware update\");\n\n\t\t\tconst canUpdate = await this.controller.firmwareUpdateNVMInit();\n\t\t\tif (!canUpdate) {\n\t\t\t\tthis.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.controller.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.controller.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: OTWFirmwareUpdateProgress = {\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.controller\n\t\t\t\t.firmwareUpdateNVMIsValidCRC16();\n\t\t\tif (!isValidCRC) {\n\t\t\t\tthis.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.controller.firmwareUpdateNVMSetNewImage();\n\n\t\t\tthis.controllerLog.print(\"Firmware update succeeded\");\n\n\t\t\tconst result: OTWFirmwareUpdateResult = {\n\t\t\t\tsuccess: true,\n\t\t\t\tstatus: OTWFirmwareUpdateStatus.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._otwFirmwareUpdateInProgress = false;\n\t\t\tif (turnedRadioOff) await this.controller.toggleRF(true);\n\t\t}\n\t}\n\n\tprivate async firmwareUpdateOTW700(\n\t\tdata: Uint8Array,\n\t): Promise<OTWFirmwareUpdateResult> {\n\t\tthis._otwFirmwareUpdateInProgress = true;\n\n\t\ttry {\n\t\t\tawait this.enterBootloader();\n\n\t\t\t// Start the update process\n\t\t\tthis.controllerLog.print(\"Beginning firmware upload\");\n\t\t\tawait this.bootloader.beginUpload();\n\n\t\t\t// Wait for the bootloader to accept fragments\n\t\t\ttry {\n\t\t\t\tawait this.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.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.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.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.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.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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: OTWFirmwareUpdateProgress = {\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\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.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.waitForBootloaderChunk<\n\t\t\t\t\tBootloaderChunk & { type: BootloaderChunkType.Message }\n\t\t\t\t>(\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.includes(\"error 0x\"),\n\t\t\t\t\t1000,\n\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.waitForBootloaderChunk(\n\t\t\t\t\t(c) => c.type === BootloaderChunkType.Menu,\n\t\t\t\t\t1000,\n\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.controllerLog.print(message, \"error\");\n\n\t\t\t\tconst result: OTWFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.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.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.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.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.controllerLog.print(\"Firmware update succeeded\");\n\n\t\t\tconst result: OTWFirmwareUpdateResult = {\n\t\t\t\tsuccess: true,\n\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.leaveBootloader();\n\t\t\tthis._otwFirmwareUpdateInProgress = false;\n\t\t}\n\t}\n\n\t// region Bootloader\n\n\tprivate _enteringBootloader: boolean = false;\n\tprivate _enterBootloaderPromise: DeferredPromise<void> | undefined;\n\n\tpublic async enterBootloader(): Promise<void> {\n\t\tif (this._bootloader) return;\n\n\t\tthis.controllerLog.print(\"Entering bootloader...\");\n\t\tthis._enteringBootloader = true;\n\t\ttry {\n\t\t\tif (this.mode === DriverMode.SerialAPI) {\n\t\t\t\tawait this.enterBootloaderFromSerialAPI();\n\t\t\t} else if (this.mode === DriverMode.CLI) {\n\t\t\t\tawait this.enterBootloaderFromCLI();\n\t\t\t}\n\n\t\t\t// Wait if the menu shows up\n\t\t\tthis._enterBootloaderPromise = createDeferredPromise();\n\t\t\tconst success = await Promise.race([\n\t\t\t\tthis._enterBootloaderPromise.then(() => true),\n\t\t\t\twait(5000, true).then(() => false),\n\t\t\t]);\n\t\t\tif (success) {\n\t\t\t\tthis.controllerLog.print(\"Entered bootloader\");\n\t\t\t} else {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"Failed to enter bootloader\",\n\t\t\t\t\tZWaveErrorCodes.Controller_Timeout,\n\t\t\t\t);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._enteringBootloader = false;\n\t\t}\n\t}\n\n\tprivate async enterBootloaderFromSerialAPI(): Promise<void> {\n\t\t// Get the encoding context before destroying the controller\n\t\tconst ctx = this.getEncodingContext();\n\n\t\tawait this.destroyController();\n\n\t\t// It would be nicer to not hardcode the command here, but since we're switching stream parsers\n\t\t// mid-command - thus ignoring the ACK, we can't really use the existing communication machinery\n\t\tconst msg = new EnterBootloaderRequest();\n\t\tconst promise = this.writeSerial(await msg.serialize(ctx));\n\t\tthis.serial!.mode = ZWaveSerialMode.Bootloader;\n\t\tawait promise;\n\t}\n\n\tprivate async enterBootloaderFromCLI(): Promise<void> {\n\t\t// The CLI first responds with a success message, so we have to reset the\n\t\tawait this.cli.executeCommand(\"bootloader\");\n\t\t// serial mode after the command is complete\n\t\tthis.serial!.mode = undefined;\n\t}\n\n\tprivate leaveBootloaderInternal(): Promise<void> {\n\t\tconst promise = this.bootloader.runApplication();\n\t\t// Reset the known serial mode.\n\t\t// We might end up in serial API, CLI or bootloader mode afterwards.\n\t\tthis.serial!.mode = undefined;\n\t\tthis._bootloader = undefined;\n\t\treturn promise;\n\t}\n\n\t/**\n\t * Leaves the bootloader by running the application.\n\t */\n\tpublic async leaveBootloader(): Promise<void> {\n\t\tthis.controllerLog.print(\"Leaving bootloader...\");\n\t\tawait this.leaveBootloaderInternal();\n\n\t\t// We may end up in a Serial API or CLI application. CLI is detected\n\t\t// automatically, Serial API should send a SerialAPIStartedRequest.\n\t\t// Give both a second to respond.\n\t\tconst isSerialAPI = await this.waitForMessage<SerialAPIStartedRequest>(\n\t\t\t(msg) => msg.functionType === FunctionType.SerialAPIStarted,\n\t\t\t1000,\n\t\t)\n\t\t\t.then(() => true as const)\n\t\t\t.catch(() => false as const);\n\n\t\tif (isSerialAPI) {\n\t\t\t// Re-initialize the controller\n\t\t\tawait this.initializeControllerAndNodes();\n\t\t\treturn;\n\t\t} else if (this.mode === DriverMode.CLI) {\n\t\t\tawait this.ensureCLIReady();\n\t\t\treturn;\n\t\t}\n\n\t\t// FIXME: Do we need the pause thing still?\n\t\tthis.unpauseSendQueue();\n\n\t\tawait this.ensureSerialAPI();\n\t}\n\n\tprivate serialport_onBootloaderData(data: BootloaderChunk): void {\n\t\tswitch (data.type) {\n\t\t\tcase BootloaderChunkType.Message: {\n\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t`[BOOTLOADER] ${data.message}`,\n\t\t\t\t\t\"verbose\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase BootloaderChunkType.FlowControl: {\n\t\t\t\tif (data.command === XModemMessageHeaders.C) {\n\t\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t\t`[BOOTLOADER] awaiting input...`,\n\t\t\t\t\t\t\"verbose\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Check if there is a handler waiting for this chunk\n\t\tfor (const entry of this.awaitedBootloaderChunks) {\n\t\t\tif (entry.predicate(data)) {\n\t\t\t\t// there is!\n\t\t\t\tentry.handler(data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!this._bootloader && data.type === BootloaderChunkType.Menu) {\n\t\t\t// We just entered the bootloader\n\t\t\tthis._controller?.destroy();\n\t\t\tthis._controller = undefined;\n\t\t\tthis._cli = undefined;\n\n\t\t\tthis.controllerLog.print(\n\t\t\t\t`[BOOTLOADER] version ${data.version}`,\n\t\t\t\t\"verbose\",\n\t\t\t);\n\n\t\t\tthis._bootloader = new Bootloader(\n\t\t\t\tthis.writeSerial.bind(this),\n\t\t\t\tdata.version,\n\t\t\t\tdata.options,\n\t\t\t);\n\t\t\tif (this._enterBootloaderPromise) {\n\t\t\t\tthis._enterBootloaderPromise.resolve();\n\t\t\t\tthis._enterBootloaderPromise = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Waits until a specific chunk is received from the bootloader or a timeout has elapsed. Returns the received chunk.\n\t * @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected\n\t * @param predicate A predicate function to test all incoming chunks\n\t */\n\tpublic waitForBootloaderChunk<T extends BootloaderChunk>(\n\t\tpredicate: (chunk: BootloaderChunk) => boolean,\n\t\ttimeout: number,\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst promise = createDeferredPromise<BootloaderChunk>();\n\t\t\tconst entry: AwaitedBootloaderChunkEntry = {\n\t\t\t\tpredicate,\n\t\t\t\thandler: (chunk) => promise.resolve(chunk),\n\t\t\t\ttimeout: undefined,\n\t\t\t};\n\t\t\tthis.awaitedBootloaderChunks.push(entry);\n\t\t\tconst removeEntry = () => {\n\t\t\t\tentry.timeout?.clear();\n\t\t\t\tconst index = this.awaitedBootloaderChunks.indexOf(entry);\n\t\t\t\tif (index !== -1) this.awaitedBootloaderChunks.splice(index, 1);\n\t\t\t};\n\t\t\t// When the timeout elapses, remove the wait entry and reject the returned Promise\n\t\t\tentry.timeout = setTimer(() => {\n\t\t\t\tremoveEntry();\n\t\t\t\treject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`Received no matching chunk within the provided timeout!`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_Timeout,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}, timeout);\n\t\t\t// When the promise is resolved, remove the wait entry and resolve the returned Promise\n\t\t\tvoid promise.then((chunk) => {\n\t\t\t\tremoveEntry();\n\t\t\t\tresolve(chunk as T);\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Waits until a specific chunk is received from the end device CLI or a timeout has elapsed. Returns the received chunk.\n\t * @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected\n\t * @param predicate A predicate function to test all incoming chunks\n\t */\n\tpublic waitForCLIChunk<T extends CLIChunk>(\n\t\tpredicate: (chunk: CLIChunk) => boolean,\n\t\ttimeout: number,\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst promise = createDeferredPromise<CLIChunk>();\n\t\t\tconst entry: AwaitedCLIChunkEntry = {\n\t\t\t\tpredicate,\n\t\t\t\thandler: (chunk) => promise.resolve(chunk),\n\t\t\t\ttimeout: undefined,\n\t\t\t};\n\t\t\tthis.awaitedCLIChunks.push(entry);\n\t\t\tconst removeEntry = () => {\n\t\t\t\tentry.timeout?.clear();\n\t\t\t\tconst index = this.awaitedCLIChunks.indexOf(entry);\n\t\t\t\tif (index !== -1) this.awaitedCLIChunks.splice(index, 1);\n\t\t\t};\n\t\t\t// When the timeout elapses, remove the wait entry and reject the returned Promise\n\t\t\tentry.timeout = setTimer(() => {\n\t\t\t\tremoveEntry();\n\t\t\t\treject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`Received no matching chunk within the provided timeout!`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_Timeout,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}, timeout);\n\t\t\t// When the promise is resolved, remove the wait entry and resolve the returned Promise\n\t\t\tvoid promise.then((chunk) => {\n\t\t\t\tremoveEntry();\n\t\t\t\tresolve(chunk as T);\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async serialport_onCLIData(data: CLIChunk): Promise<void> {\n\t\t// Check if there is a handler waiting for this chunk\n\t\tfor (const entry of this.awaitedCLIChunks) {\n\t\t\tif (entry.predicate(data)) {\n\t\t\t\t// there is!\n\t\t\t\tentry.handler(data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!this._cli && data.type === CLIChunkType.Prompt) {\n\t\t\t// We just detected the CLI\n\t\t\tthis._controller?.destroy();\n\t\t\tthis._controller = undefined;\n\t\t\tthis._bootloader = undefined;\n\n\t\t\tthis._cli = new EndDeviceCLI(\n\t\t\t\tthis.writeSerial.bind(this),\n\t\t\t\t(timeout) =>\n\t\t\t\t\tthis.waitForCLIChunk(\n\t\t\t\t\t\t(c) => c.type === CLIChunkType.Message,\n\t\t\t\t\t\ttimeout ?? 1000,\n\t\t\t\t\t)\n\t\t\t\t\t\t.then((c) =>\n\t\t\t\t\t\t\t(c as CLIChunk & { type: CLIChunkType.Message })\n\t\t\t\t\t\t\t\t.message\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.catch(() => undefined),\n\t\t\t);\n\n\t\t\tif (!(await this.ensureCLIReady())) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"Failed to detect available CLI commands\",\n\t\t\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate pollBackgroundRSSITimer: Timer | undefined;\n\tprivate lastBackgroundRSSITimestamp = 0;\n\n\tprivate handleQueueIdleChange(idle: boolean): void {\n\t\tif (!this.ready) return;\n\t\tif (\n\t\t\tthis.controller.isFunctionSupported(FunctionType.GetBackgroundRSSI)\n\t\t) {\n\t\t\t// When the send thread stays idle for 5 seconds, poll the background RSSI, but at most every 30s\n\t\t\tif (idle) {\n\t\t\t\tconst timeout = Math.max(\n\t\t\t\t\t// Wait at least 5s\n\t\t\t\t\t5000,\n\t\t\t\t\t// and up to 30s if we recently queried the RSSI\n\t\t\t\t\t30_000 - (Date.now() - this.lastBackgroundRSSITimestamp),\n\t\t\t\t);\n\t\t\t\tthis.pollBackgroundRSSITimer = setTimer(async () => {\n\t\t\t\t\t// Due to the timeout, the driver might have been destroyed in the meantime\n\t\t\t\t\tif (!this.ready) return;\n\n\t\t\t\t\tthis.lastBackgroundRSSITimestamp = Date.now();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.controller.getBackgroundRSSI();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// ignore errors\n\t\t\t\t\t}\n\t\t\t\t}, timeout).unref();\n\t\t\t} else {\n\t\t\t\tthis.pollBackgroundRSSITimer?.clear();\n\t\t\t\tthis.pollBackgroundRSSITimer = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _powerlevelTestNodeContext: {\n\t\ttestNodeId: number;\n\t\ttimeout: NodeJS.Timeout | undefined;\n\t\tacknowledgedFrames: number;\n\t} | undefined;\n\n\t/**\n\t * @internal\n\t * Begins a powerlevel test for the given node using NOP power frames\n\t */\n\tpublic async sendNOPPowerFrames(\n\t\ttestNodeId: number,\n\t\tpowerlevel: Powerlevel,\n\t\tframeCount: number,\n\t): Promise<number> {\n\t\tif (this._powerlevelTestNodeContext) {\n\t\t\t// Cancel the previous test\n\t\t\tclearTimeout(this._powerlevelTestNodeContext.timeout);\n\t\t\tthis._powerlevelTestNodeContext = undefined;\n\t\t}\n\n\t\tif (frameCount < 1) return 0;\n\n\t\tconst ret = createDeferredPromise<number>();\n\n\t\tconst context = {\n\t\t\ttestNodeId,\n\t\t\tacknowledgedFrames: 0,\n\t\t\t// This is set below after defining sendFrame\n\t\t\ttimeout: undefined as NodeJS.Timeout | undefined,\n\t\t};\n\t\tthis._powerlevelTestNodeContext = context;\n\n\t\t// We're expected to send these pretty quickly (260 frames in 25s for the CTT test)\n\t\tconst interval = 50;\n\n\t\tconst sendFrame = async () => {\n\t\t\tconst result = await this.sendTestFrame(testNodeId, powerlevel);\n\t\t\tif (result === TransmitStatus.OK) {\n\t\t\t\tcontext.acknowledgedFrames++;\n\t\t\t}\n\t\t\tframeCount--;\n\n\t\t\tif (frameCount > 0) {\n\t\t\t\tcontext.timeout = setTimeout(sendFrame, interval);\n\t\t\t} else {\n\t\t\t\tcontext.timeout = undefined;\n\t\t\t\tret.resolve(context.acknowledgedFrames);\n\t\t\t}\n\t\t};\n\n\t\tcontext.timeout = setTimeout(sendFrame, interval);\n\n\t\treturn ret;\n\t}\n\n\t/** Sends a NOP Power frame to the given node and returns the transmit status if the frame was sent */\n\tpublic async sendTestFrame(\n\t\tnodeId: number,\n\t\tpowerlevel: Powerlevel,\n\t): Promise<TransmitStatus | undefined> {\n\t\tconst result = await this.sendMessage<\n\t\t\tMessage & SuccessIndicator\n\t\t>(\n\t\t\tnew SendTestFrameRequest({\n\t\t\t\ttestNodeId: nodeId,\n\t\t\t\tpowerlevel,\n\t\t\t}),\n\t\t);\n\n\t\tif (result instanceof SendTestFrameTransmitReport) {\n\t\t\treturn result.transmitStatus;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Returns the status of a potentially ongoing NOP power test\n\t */\n\tpublic getNOPPowerTestStatus(): {\n\t\ttestNodeId: number;\n\t\tinProgress: boolean;\n\t\tacknowledgedFrames: number;\n\t} | undefined {\n\t\tif (this._powerlevelTestNodeContext) {\n\t\t\treturn {\n\t\t\t\tinProgress: !!this._powerlevelTestNodeContext.timeout,\n\t\t\t\ttestNodeId: this._powerlevelTestNodeContext.testNodeId,\n\t\t\t\tacknowledgedFrames:\n\t\t\t\t\tthis._powerlevelTestNodeContext.acknowledgedFrames,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Resets the S2 singlecast encryption state (SPAN) for the given node, which forces\n\t * a re-synchronization on the next communication attempt.\n\t */\n\tpublic resetSPAN(nodeId: number): void {\n\t\tthis.getSecurityManager2(nodeId)?.deleteNonce(nodeId);\n\t}\n\n\t/**\n\t * Resets the S2 singlecast encryption state (SPAN) for all nodes, which forces\n\t * a re-synchronization on the next communication attempt.\n\t */\n\tpublic resetAllSPANs(): void {\n\t\tfor (const nodeId of this.controller.nodes.keys()) {\n\t\t\tthis.resetSPAN(nodeId);\n\t\t}\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA;;;;;;;AAAA,gBAuDO;AACP,oBAAiD;AACjD,kBA6DO;AACP,oBA2BO;AACP,uBA2BO;AACP,oBAoBO;AAOP,oBAAyB;AACzB,mBAAqB;AACrB,8BAGO;AACP,kBAAwB;AACxB,wBAAkC;AAClC,mBAAiB;AACjB,qBAA8C;AAC9C,wBAAgC;AAChC,uBAIO;AACP,kCAA6B;AAC7B,oBAA6B;AAG7B,mBAMO;AAKP,0BAA0C;AAC1C,wBAIO;AACP,wBAA2B;AAC3B,wBAA2B;AAC3B,0BAA6B;AAC7B,+BAAuC;AACvC,0BAKO;AACP,mBAA0D;AAC1D,qCAGO;AACP,gCAKO;AACP,kBAA8B;AAC9B,6BAAgC;AAChC,yBAA4B;AAC5B,qCAIO;AACP,0BAA2D;AAC3D,uBAA4D;AAM5D,IAAAA,gBAIO;AACP,2BAA0C;AAEnC,MAAM,aAAqB;AAC3B,MAAM,UAAkB;AAG/B,MAAM,gBAAgB;;;;;;;;AAStB,MAAM,iBAA+B;EACpC,UAAU;IACT,KAAK;IACL,MAAM;;;IAGN,UAAU;IACV,QAAQ;;IACR,OAAO;IACP,eAAe;;IACf,kBAAkB;;IAClB,aAAa;;IACb,aAAa;IACb,cAAc;;IACd,6BAA6B;;IAC7B,kBAAkB;;EAEnB,UAAU;IACT,gBAAgB;IAChB,YAAY;IACZ,UAAU;IACV,gBAAgB;IAChB,eAAe;;EAEhB,8BAA8B;EAC9B,UAAU;;IAET,WAAW,KAAC,sBAAO,4BAA4B;;IAE/C,gCAAgC,KAAC,sBAChC,kDAAkD;;IAGnD,UAAU,KAAC,sBAAO,0BAA0B;;;EAG7C,gBAAgB;EAChB,WAAW;IACV,mBAAmB;;EAEpB,SAAS;IACR,UAAU,OAAO,YAAY,cAC1B,aAAAC,QAAK,KAAK,QAAQ,IAAG,GAAI,OAAO,IAChC;IACH,aAAS,sBAAO,wBAAwB;IACxC,UAAU;;EAEX,aAAa;IACZ,QAAQ,CAAA;;;AAKV,SAAS,aAAa,SAAqB;AAC1C,MAAI,QAAQ,SAAS,MAAM,GAAG;AAC7B,UAAM,IAAI,uBACT,qCACA,4BAAgB,qBAAqB;EAEvC;AACA,MAAI,QAAQ,SAAS,OAAO,GAAG;AAC9B,UAAM,IAAI,uBACT,sCACA,4BAAgB,qBAAqB;EAEvC;AACA,MAAI,QAAQ,SAAS,WAAW,OAAO,QAAQ,SAAS,WAAW,KAAO;AACzE,UAAM,IAAI,uBACT,oEACA,4BAAgB,qBAAqB;EAEvC;AACA,MAAI,QAAQ,SAAS,SAAS,OAAO,QAAQ,SAAS,SAAS,KAAO;AACrE,UAAM,IAAI,uBACT,kEACA,4BAAgB,qBAAqB;EAEvC;AACA,MAAI,QAAQ,SAAS,QAAQ,OAAQ,QAAQ,SAAS,QAAQ,KAAO;AACpE,UAAM,IAAI,uBACT,kEACA,4BAAgB,qBAAqB;EAEvC;AACA,MACC,QAAQ,SAAS,cAAc,MAAM,QAAQ,SAAS,cAAc,KACnE;AACD,UAAM,IAAI,uBACT,mFACA,4BAAgB,qBAAqB;EAEvC;AACA,MACC,QAAQ,SAAS,cAAc,MAAM,QAAQ,SAAS,cAAc,KACnE;AACD,UAAM,IAAI,uBACT,uEACA,4BAAgB,qBAAqB;EAEvC;AACA,MAAI,QAAQ,SAAS,mBAAmB,KAAO;AAC9C,UAAM,IAAI,uBACT,uEACA,4BAAgB,qBAAqB;EAEvC;AACA,MACC,QAAQ,SAAS,gBAAgB,OAC9B,QAAQ,SAAS,gBACjB,QAAQ,SAAS,mBAAmB,KACtC;AACD,UAAM,IAAI,uBACT,iEACC,QAAQ,SAAS,mBAAmB,GACrC,kBACA,4BAAgB,qBAAqB;EAEvC;AACA,MACC,QAAQ,SAAS,mBAAmB,OACjC,QAAQ,SAAS,mBAAmB,KACtC;AACD,UAAM,IAAI,uBACT,+EACA,4BAAgB,qBAAqB;EAEvC;AACA,MAAI,QAAQ,gBAAgB,QAAW;AACtC,UAAM,OAAO,OAAO,QAAQ,QAAQ,YAAY;AAChD,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACrC,YAAM,CAAC,UAAU,GAAG,IAAI,KAAK,CAAC;AAC9B,UAAI,IAAI,WAAW,IAAI;AACtB,cAAM,IAAI,uBACT,8BAA8B,QAAQ,qCACtC,4BAAgB,qBAAqB;MAEvC;AACA,UAAI,KAAK,UAAU,CAAC,CAAC,EAAE,CAAC,UAAM,mCAAoB,GAAG,GAAG,CAAC,MAAM,GAAG;AACjE,cAAM,IAAI,uBACT,8BAA8B,QAAQ,6BACtC,4BAAgB,qBAAqB;MAEvC;IACD;EACD;AACA,MAAI,QAAQ,SAAS,aAAa,KAAK,QAAQ,SAAS,aAAa,GAAG;AACvE,UAAM,IAAI,uBACT,oDACA,4BAAgB,qBAAqB;EAEvC;AACA,MACC,QAAQ,SAAS,WAAW,KACzB,QAAQ,SAAS,WAAW,oCAC9B;AACD,UAAM,IAAI,uBACT,+CAA+C,kCAAiB,KAChE,4BAAgB,qBAAqB;EAEvC;AACA,MACC,QAAQ,SAAS,iBAAiB,KAC/B,QAAQ,SAAS,iBAAiB,IACpC;AACD,UAAM,IAAI,uBACT,gEACA,4BAAgB,qBAAqB;EAEvC;AACA,MACC,QAAQ,SAAS,gBAAgB,KAC9B,QAAQ,SAAS,gBAAgB,IACnC;AACD,UAAM,IAAI,uBACT,yDACA,4BAAgB,qBAAqB;EAEvC;AAEA,MAAI,QAAQ,wBAAwB;AACnC,QAAI,KAAC,4BAAS,QAAQ,sBAAsB,GAAG;AAC9C,YAAM,IAAI,uBACT,iDACA,4BAAgB,qBAAqB;IAEvC,WACC,OAAO,QAAQ,uBAAuB,yBACjC,cACF,OAAO,QAAQ,uBAAuB,2BACpC,cACF,OAAO,QAAQ,uBAAuB,UAAU,YAClD;AACD,YAAM,IAAI,uBACT,yHACA,4BAAgB,qBAAqB;IAEvC;EACD;AAEA,MAAI,QAAQ,0BAA0B;AACrC,QAAI,KAAC,4BAAS,QAAQ,wBAAwB,GAAG;AAChD,YAAM,IAAI,uBACT,mDACA,4BAAgB,qBAAqB;IAEvC,WACC,OAAO,QAAQ,yBAAyB,YACnC,cACF,OAAO,QAAQ,yBAAyB,SACtC,YACJ;AACD,YAAM,IAAI,uBACT,qFACA,4BAAgB,qBAAqB;IAEvC;EACD;AAEA,MAAI,QAAQ,MAAM,QAAW;AAC5B,QAAI,QAAQ,GAAG,UAAU,QAAW;AACnC,UACC,OAAO,QAAQ,GAAG,WAAW,YAC1B,EAAE,QAAQ,GAAG,UAAU,yBACvB,QAAQ,GAAG,WAAW,qBAAS,SACjC;AACD,cAAM,IAAI,uBACT,GAAG,QAAQ,GAAG,MAAM,8BACpB,4BAAgB,qBAAqB;MAEvC;IACD;AAEA,QAAI,QAAQ,GAAG,WAAW,QAAW;AACpC,UAAI,KAAC,4BAAS,QAAQ,GAAG,OAAO,GAAG;AAClC,cAAM,IAAI,uBACT,iCACA,4BAAgB,qBAAqB;MAEvC,WACC,OAAO,QAAQ,GAAG,QAAQ,eAAe,YACtC,OAAO,QAAQ,GAAG,QAAQ,iBAAiB,UAC7C;AACD,cAAM,IAAI,uBACT,uFACA,4BAAgB,qBAAqB;MAEvC;IACD;EACD;AACD;AApMS;AA8PT,SAAS,cACR,KAAM;AAEN,aAAO,6BAAW,GAAG,KAAK,IAAI,mBAAmB;AAClD;AAJS;AAMT,SAAS,eAAe,WAAqB;AAC5C,MAAI,UAAU,mBAAmB,qBAAW;AAC3C,QAAI,OAAO,UAAU,QAAQ,WAAW,UAAU;AACjD,YAAM,IAAI,uBACT,0CACA,UAAU,QAAQ,MAAM;IAE1B,OAAO;AACN,YAAM,IAAI,uBACT,mCACA,4BAAgB,6BAChB,UAAU,QAAQ,MAAM;IAE1B;EACD,eAAW,6BAAW,UAAU,OAAO,GAAG;AACzC,mBAAe,UAAU,OAAO;EACjC;AACD;AAjBS;AAmBT,SAAS,wCACR,QAAkD;AAIlD,SAAO;IACN,MAAM,SAASA,OAAI;AAClB,YAAM,OAAO,MAAM,OAAO,SAASA,OAAM,MAAM;AAC/C,aAAO,oBAAM,KAAK,MAAM,MAAM;IAC/B;IACA,MAAM,KAAKA,OAAI;AACd,UAAI,MAAM,OAAO,WAAWA,KAAI,GAAG;AAClC,eAAO;UACN,cAAW;AACV,mBAAO;UACR;UACA,SAAM;AACL,mBAAO;UACR;UACA,OAAO,oBAAI,KAAI;UACf,MAAM;;MAER,OAAO;AACN,cAAM,IAAI,MAAM,gBAAgB;MACjC;IACD;IACA,QAAQ,OAAK;AACZ,aAAO,QAAQ,OACd,IAAI,MAAM,0CAA0C,CAAC;IAEvD;;AAEF;AAhCS;AAyDH,MAAO,eAAe,+BAAsC;EA5qBlE,OA4qBkE;;;EAQxD;EADT,YACS,SAKL,mBAAsD;AAEzD,UAAK;AAPG,SAAA,OAAA;AAUR,QACC,OAAO,SAAS,YACb,KAAC,+CAAgC,IAAI,KACrC,KAAC,2CAA4B,IAAI,GACnC;AACD,YAAM,IAAI,uBACT,2EACA,4BAAgB,qBAAqB;IAEvC;AAGA,UAAM,2BAA2B,kBAAkB,OAClD,CAAC,MAAgC,CAAC,CAAC,CAAC;AAErC,QAAI,gBAAqC,CAAA;AACzC,eAAW,UAAU,0BAA0B;AAC9C,0BAAgB,yBAAU,eAAe,QAAQ,IAAI;IACtD;AAEA,SAAK,eAAW,yBACf,mBACA,yBAAU,cAAc,CAAC;AAI1B,iBAAa,KAAK,QAAQ;AAC1B,QAAI,KAAK,SAAS,WAAW;AAC5B,UAAI,KAAC,4BAAS,KAAK,SAAS,SAAS,GAAG;AACvC,cAAM,IAAI,uBACT,6CACA,4BAAgB,qBAAqB;MAEvC;AAEA,WAAK,gBAAgB,KAAK,SAAS,SAAS;IAC7C;AAGA,SAAK,WAAW,KAAK,SAAS,QAAQ;AAEtC,UAAM,OAAO;AACb,SAAK,yBAAyB;MAC7B,yBAAyB,wBAAC,WACzB,KAAK,wBAAwB,MAAM,GADX;MAEzB,kBAAkB,wBAAC,QAAQ,kBAC1B,KAAK,iBAAiB,QAAQ,aAAa,GAD1B;MAElB,kBAAkB,wBAAC,QAAQ,eAAe,YACzC,KAAK,iBAAiB,QAAQ,eAAe,OAAO,GADnC;MAElB,iBAAiB,wBAAC,WAAW,KAAK,gBAAgB,MAAM,GAAvC;;MAEjB,IAAI,kBAAe;AAClB,eAAO,KAAK;MACb;MACA,IAAI,mBAAgB;AACnB,eAAO,KAAK;MACb;MACA,IAAI,oBAAiB;AACpB,eAAO,KAAK;MACb;MACA,uBAAuB,wBAAC,IAAI,QAAQ,kBACnC,KAAK,sBAAsB,IAAI,QAAQ,aAAa,GAD9B;;AAIxB,SAAK,aAAa,IAAI,0BAAa;EACpC;EAEQ;;EAEA;EAEA;EAKA,qBAAkB;AACzB,WAAO;MACN,GAAG,KAAK;MACR,WAAW,KAAK,WAAW;MAC3B,QAAQ,KAAK,WAAW;MACxB,YAAY,KAAK,aAAa,cAAc,uBAAW;;EAEzD;EAEQ,2BAAwB;AAC/B,WAAO;MACN,iBAAiB,wBAAC,WAAW,KAAK,gBAAgB,MAAM,GAAvC;MACjB,YAAY,KAAK,aAAa;MAC9B,gBAAgB,KAAK;MACrB,WAAW,KAAK,aAAa,aAAa;;MAC1C,QAAQ,KAAK,aAAa,UAAU;;MACpC,YAAY,KAAK,aAAa,cAAc,uBAAW;;EAEzD;EAEQ,sBAAmB;AAI1B,WAAO;MACN,GAAG,KAAK;MACR,WAAW,KAAK,WAAW;MAC3B,QAAQ,KAAK,WAAW;;EAE1B;;;EAIQ;;;EAEA;;;EAEA;;;EAGR,IAAY,SAAM;AACjB,WAAO,CAAC,KAAK,gBAAgB,KAAK,KAAK;EACxC;EAEQ,wBAAqB;AAC5B,SAAK,iBAAiB,IAAI,8BAAiB;MAC1C,MAAM;MACN,yBAAyB,wBAAC,MAAK;AAI9B,YAAI,KAAK,WAAW,WAAW,6BAAiB,cAAc;AAC7D,iBAAO,EAAE,mBAAmB,qCACxB,EAAE,mBAAmB;QAC1B;AAGA,YAAI,KAAK,WAAW,WAAW,6BAAiB,QAAQ;AACvD,iBAAO,EAAE,mBAAmB;QAC7B;AAGA,eAAO,CAAC,KAAK,eACT,KAAK,WAAW,WAAW,6BAAiB;MACjD,GAjByB;KAkBzB;AACD,SAAK,QAAQ,IAAI,8BAAiB;MACjC,MAAM;MACN,yBAAyB,wBAAC,MAAM,KAAK,oBAAoB,CAAC,GAAjC;KACzB;AAED,SAAK,aAAa;AAGlB,eAAW,SAAS,KAAK,QAAQ;AAChC,WAAK,KAAK,sBAAsB,KAAK;IACtC;EACD;EAEQ,MAAM,yBACb,QACA,WAA2B;AAG3B,eAAW,SAAS,KAAK,QAAQ;AAChC,UAAI,CAAC;AAAO;IACb;AAGA,YAAI,sBAAO,UAAU,MAAM,QAAQ;AAClC,YAAM,KAAK,mBACV,CAAC,OAAO,MACR,QACA,aAAa,4BAAgB,kBAAkB;IAEjD;AAEA,eAAW,SAAS,KAAK,QAAQ;AAChC,YAAM,MAAK;IACZ;EACD;EAEQ;EACR,IAAW,YAAS;AACnB,WAAO,KAAK;EACb;EAEQ,cAAc;;EAEd;EAEA,qBAAkB;AACzB,SAAK,iBAAiB,IAAI,yBAAU;AAGpC,SAAK,KAAK,oBAAmB;EAC9B;EAEQ,sBACP,QACA,WAA2B;AAG3B,QAAI,CAAC,KAAK;AAAgB;AAE1B,SAAK,eAAe,MAAK;AAGzB,SAAK,uBAAuB,OAC3B,IAAI,uBACH,QACA,aAAa,4BAAgB,gBAAgB,CAC7C;EAEH;;EAGQ,mBAAmB;EACnB,aAAsB;;EAE9B,IAAW,YAAS;AACnB,WAAO,KAAK;EACb;EACA,IAAY,UAAU,OAAc;AACnC,QAAI,KAAK,eAAe,OAAO;AAC9B,WAAK,UAAU,MACd,QAAQ,oBAAoB,yBAAyB;AAEtD,WAAK,aAAa;AAClB,WAAK,sBAAsB,KAAK;IACjC;EACD;;EAGQ,kBAAkB,oBAAI,IAAG;;EAEzB,wBAAgD,CAAA;;EAEhD,kBAAyC,CAAA;;EAEzC,kBAAyC,CAAA;;EAEzC,0BAAyD,CAAA;;EAEzD,mBAA2C,CAAA;;EAG3C,eAAe,oBAAI,IAAG;EACtB,mBAAmB,QAAc;AACxC,QAAI,CAAC,KAAK,aAAa,IAAI,MAAM,GAAG;AACnC,WAAK,aAAa,IAAI,QAAQ;QAC7B,kBAAkB,oBAAI,IAAG;QACzB,aAAa,oBAAI,IAAG;OACpB;IACF;AACA,WAAO,KAAK,aAAa,IAAI,MAAM;EACpC;EAEQ,kBACP,oBAAI,IAAG;;;;;EAKR,IAAW,iBAAc;AACxB,WAAO,KAAK;EACb;EAEgB;EAER;;EAER,IAAW,UAAO;AACjB,WAAO,KAAK;EACb;EACQ;;EAER,IAAW,aAAU;AACpB,WAAO,KAAK;EACb;EACQ;;EAER,IAAW,eAAY;AACtB,QAAI,KAAK,iBAAiB,QAAW;AACpC,YAAM,IAAI,uBACT,8CACA,4BAAgB,eAAe;IAEjC;AACA,WAAO,KAAK;EACb;;EAGQ;EACR,IAAW,gBAAa;AACvB,WAAO,KAAK;EACb;EAEA,IAAW,gBAAa;AACvB,WACC,KAAK,eAAe,iBAEhB,QAAQ,uBAAuB,GAAG,eACjC,kBAAkB,KACnB;EAEN;;EAGQ;;EAEA;;EAER,IAAW,YAAS;AACnB,WAAO,KAAK;EACb;;EAGQ;;EAER,IAAW,gBAAa;AACvB,WAAO,KAAK;EACb;EAQO,WAAW,MAAW;AAE5B,SAAK,eAAe,QAAQ,GAAG,IAAI;EACpC;EAEQ;;EAER,IAAW,aAAU;AACpB,QAAI,KAAK,eAAe,QAAW;AAClC,YAAM,IAAI,uBACT,oCACA,4BAAgB,eAAe;IAEjC;AACA,WAAO,KAAK;EACb;;EAGQ;EACR,IAAW,aAAU;AACpB,QAAI,KAAK,eAAe,QAAW;AAClC,YAAM,IAAI,uBACT,6CACA,4BAAgB,eAAe;IAEjC;AACA,WAAO,KAAK;EACb;EAEQ;;EAER,IAAW,MAAG;AACb,QAAI,KAAK,QAAQ,QAAW;AAC3B,YAAM,IAAI,uBACT,yCACA,4BAAgB,eAAe;IAEjC;AACA,WAAO,KAAK;EACb;;EAGA,IAAW,OAAI;AACd,QAAI,KAAK;AAAa,aAAO,6BAAW;AACxC,QAAI,KAAK;AAAM,aAAO,6BAAW;AACjC,QAAI,KAAK;AAAa,aAAO,6BAAW;AACxC,WAAO,6BAAW;EACnB;EAEQ,iBAAc;EAGd;;;;;;EAMR,IAAW,kBAAe;AACzB,WAAO,KAAK;EACb;EAEQ;;;;;;EAMR,IAAW,mBAAgB;AAC1B,WAAO,KAAK;EACb;EAEQ;;;;;;EAMR,IAAW,oBAAiB;AAC3B,WAAO,KAAK;EACb;;EAGO,oBACN,aAA0C;AAE1C,UAAM,aAAS,2BAAQ,WAAW,IAAI,YAAY,CAAC,IAAI;AACvD,UAAM,kBAAc,+BAAkB,MAAM;AAC5C,WAAO,cAAc,KAAK,oBAAoB,KAAK;EACpD;EAEQ;;EAED,MAAM,mCAAgC;AAC5C,QAAI,KAAK,kCAAkC,QAAW;AAErD,YAAM,aAAa,KAAK,SACvB,8BAAU,WAAW,UAAU;AAEhC,UAAI,YAAY;AACf,aAAK,iCACJ,UAAM,0CAA6B,UAAU;MAC/C,OAAO;AAEN,aAAK,iCACJ,UAAM,iCAAmB;AAC1B,aAAK,SACJ,8BAAU,WAAW,YACrB,KAAK,+BAA+B,UAAU;MAEhD;IACD;AACA,WAAO,KAAK;EACb;;;;;;EAOA,IAAW,SAAM;AAEhB,WAAO,KAAK,WAAW;EACxB;;;;;;EAOA,IAAW,YAAS;AAEnB,WAAO,KAAK,WAAW;EACxB;;EAGO,QAAQ,QAAc;AAC5B,WAAO,KAAK,WAAW,MAAM,IAAI,MAAM;EACxC;;EAGO,eAAe,QAAc;AACnC,WAAO,KAAK,WAAW,MAAM,WAAW,MAAM;EAC/C;;EAGO,cAAW;AACjB,WAAO,CAAC,GAAG,KAAK,WAAW,MAAM,OAAM,CAAE;EAC1C;EAEO,WAAW,KAAY;AAC7B,UAAM,SAAS,IAAI,UAAS;AAC5B,QAAI,UAAU;AAAW,aAAO,KAAK,WAAW,MAAM,IAAI,MAAM;EACjE;EAEO,eAAe,IAAgB;AACrC,QAAI,GAAG,aAAY,GAAI;AACtB,aAAO,KAAK,WAAW,MACrB,IAAI,GAAG,MAAM,GACZ,YAAY,GAAG,aAAa;IAChC;EACD;;;;;;EAOO,WAAW,QAAc;AAE/B,UAAM,OAAO,KAAK,WAAW,MAAM,WAAW,MAAM;AACpD,WAAO,KAAK;EACb;;;;;;EAOO,cAAc,QAAc;AAElC,UAAM,OAAO,KAAK,WAAW,MAAM,IAAI,MAAM;AAC7C,WAAO,MAAM;EACd;EAEO,gBAAgB,QAAc;AAEpC,WAAO,KAAK,WAAW,MAAM,IAAI,MAAM,GAAG;EAC3C;EAEO,mBAAmB,gBAAsB;AAC/C,WAAO,KAAK,cAAc,mBAAmB,cAAc;EAC5D;EAEO,wBACN,QAAc;AAGd,UAAM,OAAO,KAAK,WAAW,MAAM,WAAW,MAAM;AACpD,WAAO,KAAK,wBAAuB;EACpC;EAEO,iBACN,QACA,eAA4B;AAG5B,UAAM,OAAO,KAAK,WAAW,MAAM,WAAW,MAAM;AACpD,WAAO,KAAK,iBAAiB,aAAa;EAC3C;;;;;;EAOO,iBACN,QACA,eACA,SAAgB;AAGhB,UAAM,OAAO,KAAK,WAAW,MAAM,WAAW,MAAM;AACpD,SAAK,iBAAiB,eAAe,OAAO;EAC7C;;EAGO,gBAAgB,QAA0B;AAChD,SAAK,cAAc,oBAAoB,MAAM;EAC9C;;EAGO,eAAY;AAClB,WAAO,KAAK,cAAc,iBAAgB;EAC3C;;EAGO,mBACN,QAA6C;AAE7C,SAAK,SAAS,YAAY,aAAS,yBAClC,eAAe,YAAY,QAC3B,MAAM;EAER;;;;;;EAOO,qBAAkB;AACxB,WAAO,KAAK,SAAS;EACtB;;;;;;EAOO,sBAAmB;AACzB,WAAO,KAAK,SAAS;EACtB;;;;;;EAOO,0BAAuB;AAC7B,WAAO;MACN,cAAc,KAAK,SAAS,SAAS;MACrC,6BACC,KAAK,SAAS,SAAS;;EAE1B;;;;;;EAOO,aAAa,qBAAqB,EACxC,QAAQ,MACR,SAAS,KAAI,IAIV,CAAA,GAAE;AACL,UAAM,MAA6C,CAAA;AAInD,UAAM;;;OAGJ,MAAM,OAAO,0BAA0B,GAAG;;AAC5C,QAAI,SAAS,OAAO,SAAS,SAAS,YAAY;AACjD,iBAAW,QAAQ,MAAM,SAAS,KAAI,GAAI;AACzC,YAAI,KAAK,SAAS;AAAU;AAC5B,YAAI,KAAK,IAAI;MACd;IACD;AACA,QAAI,QAAQ;AACX,YAAM,QAAQ,UAAM,gDAAyB;AAC7C,UAAI,OAAO;AACV,YAAI,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO;UAC7B,MAAM;UACN,MAAM,EAAE;UACP,CAAC;MACJ;IACD;AAEA,UAAM,YAAsC,CAAC,QAAQ,UAAU,KAAK;AAEpE,QAAI,KAAK,CAAC,GAAG,MAAK;AACjB,YAAM,QAAQ,UAAU,QAAQ,EAAE,IAAI;AACtC,YAAM,QAAQ,UAAU,QAAQ,EAAE,IAAI;AACtC,UAAI,UAAU;AAAO,eAAO,QAAQ;AACpC,aAAO,EAAE,KAAK,cAAc,EAAE,IAAI;IACnC,CAAC;AAED,eAAO,wBAAS,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;EACvC;;EAGO,cAAc,SAA6B;AAGjD,UAAM,kBAAc,oBAAK,SAAS;MACjC;MACA;MACA;MACA;MACA;MACA;MACA;KACA;AAOD,UAAM,EAAE,WAAW,MAAM,GAAG,KAAI,IAAK,KAAK;AAC1C,UAAM,iBAAa,6BAClB,yBAAU,IAAI,GACd,aACA,IAAI;AAEL,eAAW,YAAY;AACvB,eAAW,OAAO;AAClB,iBAAa,UAAU;AAEvB,QAAI,QAAQ,aAAa,KAAC,4BAAS,QAAQ,SAAS,GAAG;AACtD,YAAM,IAAI,uBACT,6CACA,4BAAgB,qBAAqB;IAEvC;AAGA,SAAK,WAAW;AAEhB,QAAI,QAAQ,WAAW;AACtB,WAAK,gBAAgB,QAAQ,SAAS;IACvC;AAEA,QAAI,QAAQ,WAAW;AACtB,WAAK,gBAAgB,QAAQ,SAAS;IACvC;EACD;EAEQ;EACR,IAAW,UAAO;AACjB,WAAO,KAAK;EACb;;;;;EAMQ;EAEA,cAAuB;EACvB,UAAmB;;EAGpB,MAAM,QAAK;AAEjB,QAAI,KAAK,cAAc;AACtB,YAAM,IAAI,uBACT,uEACA,4BAAgB,gBAAgB;IAElC;AACA,QAAI,KAAK;AAAa,aAAO,QAAQ,QAAO;AAC5C,SAAK,cAAc;AAInB,SAAK,WAAW;MACf,IAAI,KAAK,SAAS,MAAM,OACnB,MAAM,OAAO,sBAAsB,GAAG;MAC3C,QAAQ,KAAK,SAAS,MAAM,WACvB,MAAM,OAAO,0BAA0B,GAAG;MAC/C,IAAI,KAAK,SAAS,MAAM,OAGnB,MAAM,OAAO,sBAAsB,GAAG;MAC3C,KAAK,KAAK,SAAS,MAAM,QACpB,MAAM,OAAO,uBAAuB,GAAG;;AAI7C,SAAK,gBAAgB,KAAK,SAAS,IAAI,KAAK,SAAS,SAAS;AAC9D,SAAK,aAAa,IAAI,2BAAa,MAAM,KAAK,aAAa;AAC3D,SAAK,iBAAiB,IAAI,6BAAiB,KAAK,aAAa;AAG7D,SAAK,iBAAiB,IAAI,4BAAc;MACvC,UAAU,KAAK,SAAS;MACxB,cAAc,KAAK;MACnB,yBACC,KAAK,SAAS,QAAQ;MACvB,yBACC,KAAK,SAAS,QAAQ;KACvB;AAED,UAAM,oBAAgB,+CAAqB;AAG3C,SAAK,UAAU,MAAM,eAAe,MAAM;AAC1C,SAAK,UAAU,MAAM,WAAW,UAAU,IAAI,MAAM;AACpD,SAAK,UAAU,MAAM,IAAI,MAAM;AAE/B,SAAK,UAAU,MAAM,oBAAoB;AAGzC,QAAI;AACJ,QAAI,OAAO,KAAK,SAAS,UAAU;AAClC,UACC,OAAO,KAAK,SAAS,OAAO,wBAAwB,YACnD;AACD,aAAK,UAAU,MAAM,uBAAuB,KAAK,IAAI,EAAE;AACvD,kBAAU,MAAM,KAAK,SAAS,OAAO,oBACpC,KAAK,IAAI;MAEX,OAAO;AACN,sBAAc,OACb,IAAI,uBACH,uEACA,4BAAgB,aAAa,CAC7B;AAEF,aAAK,KAAK,QAAO;AACjB;MACD;IACD,eAAW,+CAAgC,KAAK,IAAI,GAAG;AACtD,WAAK,UAAU,MACd,8DAA8D;AAE/D,WAAK,UAAU,MACd,8DACA,MAAM;AAEP,oBAAU,uCAAwB,KAAK,IAAI;IAC5C,OAAO;AACN,WAAK,UAAU,MACd,uDAAuD;AAExD,gBAAU,KAAK;IAChB;AACA,SAAK,gBAAgB,IAAI,uCACxB,SACA,KAAK,aAAa;AAOnB,iBAAa,YAAW;AACvB,UAAI;AACH,cAAM,KAAK,eAAc;MAC1B,SAAS,GAAG;AACX,sBAAc,OAAO,CAAU;AAC/B,aAAK,KAAK,QAAO;AACjB;MACD;AAEA,WAAK,UAAU,MAAM,oBAAoB;AACzC,WAAK,UAAU;AACf,oBAAc,QAAO;AAGrB,WAAK,WAAW,MAAK;AAErB,UACC,OAAO,KAAK,SAAS,cAAc,qBAC9B,YACJ;AACD,cAAM,KAAK,SAAS,aAAa,iBAAiB,KAAK,MAAO;MAC/D;AAGA,UAAI,KAAK,SAAS,cAAc,4BAA4B;AAG3D,cAAM,KAAK,YAAY,6BAAe,GAAG;AACzC,gBAAI,sBAAO,UAAU,MAAM,QAAQ;AAClC,oBAAM,mBAAK,GAAI;QAChB;MACD,OAAO;AACN,cAAM,OAAO,MAAM,KAAK,WAAU;AAClC,YAAI,SAAS,6BAAW,KAAK;AAC5B,eAAK,KAAK,WAAW;AACrB;QACD;AAEA,YAAI,SAAS,6BAAW,YAAY;AACnC,cAAI,KAAK,SAAS,mBAAmB,QAAQ;AAC5C,iBAAK,UAAU,MACd,yEACA,MAAM;AAEP,iBAAK,KAAK,kBAAkB;AAC5B;UACD;AAEA,eAAK,UAAU,MACd,yDACA,MAAM;AAEP,gBAAM,KAAK,wBAAuB;AAGlC,oBAAM,mBAAK,GAAI;AAIf,cAAI,KAAK,aAAa;AACrB,gBAAI,KAAK,SAAS,mBAAmB,SAAS;AAC7C,mBAAK,UAAU,MACd,+EACA,MAAM;AAEP,mBAAK,KAAK,kBAAkB;YAC7B,OAAO;AAEN,mBAAK,KAAK,mBACT,+EAA+E;YAEjF;AAEA;UACD;QACD;MACD;AAGA,UAAI;AAEH,YAAI,KAAK,SAAS,QAAQ,QAAQ;AAEjC,gBAAM,KAAK,SAAS,QAAQ,OAAO,UAAU,KAAK,QAAQ;QAC3D,OAAO;AACN,gBAAM,KAAK,SAAS,GAAG,UAAU,KAAK,QAAQ;QAC/C;MACD,SAAS,GAAG;AACX,YAAI;AACJ,YACC,iCAAiC,SAChC,+BAAgB,GAAG,IAAI,CAAC,GAExB;AACD,oBACC,wCAAwC,KAAK,QAAQ;QACvD,OAAO;AACN,oBACC,wCAAwC,KAAK,QAAQ;QACvD;AAEA,aAAK,KAAK,mBAAmB,OAAO;AACpC;MACD;AAGA,UAAI,KAAK,SAAS,cAAc,sBAAsB,OAAO;AAC5D,aAAK,UAAU,MAAM,0BAA0B;AAC/C,YAAI;AACH,gBAAM,KAAK,cAAc,QAAO;QACjC,SAAS,GAAG;AACX,gBAAM,UAAU,yCACf,+BACC,CAAC,CAEH;AACA,eAAK,KAAK,mBAAmB,OAAO;AACpC;QACD;MACD;AAEA,WAAK,UAAU,MAAM,wBAAwB;AAC7C,UAAI;AACH,cAAM,KAAK,6BAA4B;MACxC,SAAS,GAAG;AACX,YAAI;AACJ,gBACC,0BAAa,CAAC,KACX,EAAE,SAAS,4BAAgB,2BAC7B;AACD,oBACC;QACF,OAAO;AACN,oBAAU,wCACT,+BACC,GACA,IAAI,CAEN;QACD;AACA,aAAK,UAAU,MAAM,SAAS,OAAO;AACrC,aAAK,KACJ,SACA,IAAI,uBAAW,SAAS,4BAAgB,aAAa,CAAC;AAEvD,aAAK,KAAK,QAAO;AACjB;MACD;IACD,CAAC;AAED,WAAO;EACR;EAEQ,MAAM,aAAU;AAIvB,UAAM,cAAc,KAAK,qBACxB,CAAC,MAAM,MAAM,6BAAe,KAC5B,GAAG,EAEF,KAAK,MAAM,IAAI,EACf,MAAM,MAAM,KAAK;AACnB,UAAM,KAAK,YAAY,6BAAe,GAAG;AAOzC,QAAI,MAAM,aAAa;AAGtB,YAAM,KAAK,YAAY,oBAAM,KAAK,MAAM,OAAO,CAAC;IACjD;AASA,cAAM,mBAAK,GAAG;AAEd,QAAI,KAAK;AAAM,aAAO,6BAAW;AACjC,QAAI,KAAK;AAAa,aAAO,6BAAW;AAExC,WAAO,6BAAW;EACnB;EAEQ,yBAAkC;EAClC,cAAc,oBAAI,IAAG;EACrB,0BAAmC;EACnC,uBAAgC;EAEhC,MAAM,iBAAc;AAC3B,QAAI;AAEJ,SAAK,uBAAuB;AAC5B,aACK,UAAU,GACd,WAAW,KAAK,SAAS,SAAS,gBAClC,WACC;AACD,UAAI;AACH,aAAK,SAAS,MAAM,KAAK,cAAe,aAAY;AAEpD,aAAK,KAAK,iBAAiB,KAAK,MAAM;AAKtC,gBAAI,sBAAO,UAAU,MAAM,QAAQ;AAClC,oBAAM,mBAAK,GAAG;QACf;AAEA,YAAI,KAAK,OAAO,QAAQ;AAEvB,eAAK,uBAAuB;AAC5B;QACD;MACD,SAAS,GAAG;AACX,oBAAY;MACb;AACA,UAAI,UAAU,KAAK,SAAS,SAAS,gBAAgB;AACpD,kBAAM,mBAAK,GAAI;MAChB;IACD;AAEA,SAAK,uBAAuB;AAE5B,UAAM,UAAU,uCACf,+BACC,SAAS,CAEX;AACA,SAAK,UAAU,MAAM,SAAS,OAAO;AAErC,UAAM,IAAI,uBAAW,SAAS,4BAAgB,aAAa;EAC5D;;EAGA,IAAW,gBAAa;AACvB,WAAO,KAAK;EACb;EAEQ,oBAAiB;AACxB,UAAM,UAA+B;MACpC,kBAAkB;MAClB,GAAG,uCAAgB,KAAK,SAAS,QAAQ,QAAQ;;AAElD,QAAI,KAAK,SAAS,QAAQ,SAAS;AAClC,cAAQ,WAAW;QAClB,WAAW,KAAK,SAAS,QAAQ;;IAEnC;AACA,WAAO;EACR;EAEQ,MAAM,iBAAiB,QAAc;AAC5C,UAAM,UAAU,KAAK,kBAAiB;AAEtC,UAAM,mBAAmB,aAAAA,QAAK,KAC7B,KAAK,UACL,GAAG,OAAO,SAAS,EAAE,CAAC,QAAQ;AAE/B,SAAK,gBAAgB,KAAK,SAAS,GAAG,eAAe,kBAAkB;MACtE,GAAG;MACH,YAAY;MACZ,SAAS;KACT;AACD,UAAM,KAAK,cAAc,KAAI;AAE7B,YAAI,sBAAO,UAAU,MAAM,QAAQ;AAGlC,WAAK,cAAc,MAAK;IACzB;EACD;EAEQ,MAAM,aAAa,QAAc;AACxC,UAAM,UAAU,KAAK,kBAAiB;AAEtC,UAAM,cAAc,aAAAA,QAAK,KACxB,KAAK,UACL,GAAG,OAAO,SAAS,EAAE,CAAC,eAAe;AAEtC,SAAK,WAAW,KAAK,SAAS,GAAG,eAAe,aAAa;MAC5D,GAAG;MACH,kBAAkB;MAClB,SAAS,wBAAC,MAAM,cAAU,mCAAsB,KAAK,GAA5C;MACT,YAAY,wBAAC,MAAM,cAAU,iCAAoB,KAAK,GAA1C;KACZ;AACD,UAAM,KAAK,SAAS,KAAI;AAExB,UAAM,iBAAiB,aAAAA,QAAK,KAC3B,KAAK,UACL,GAAG,OAAO,SAAS,EAAE,CAAC,iBAAiB;AAExC,SAAK,cAAc,KAAK,SAAS,GAAG,eACnC,gBACA,OAAO;AAER,UAAM,KAAK,YAAY,KAAI;AAE3B,YAAI,sBAAO,UAAU,MAAM,QAAQ;AAGlC,WAAK,SAAS,MAAK;AACnB,WAAK,YAAY,MAAK;IACvB;EACD;EAEQ,MAAM,wBAAqB;AAClC,QACC,CAAC,KAAK,eACH,CAAC,KAAK,WAAW,UACjB,CAAC,KAAK,iBACN,CAAC,KAAK,UACR;AACD;IACD;AAIA,QAAI,KAAK,cAAc,SAAS,GAAG;AAElC,WAAK,cAAc,IAAI,eAAe,CAAC;AAEvC,UAAI;AACH,kBAAM;UACL,KAAK,WAAW;UAChB,KAAK;UACL,KAAK;;UAEL,KAAK,SAAS,QAAQ,SACnB;;YAED,KAAK,SAAS,QAAQ;UAAM,IAE3B,KAAK,SAAS;UACjB,KAAK;QAAQ;AAKd,mBAAW,OAAO,KAAK,SAAS,KAAI,GAAI;AACvC,cAAI,OAAO,IAAI,QAAQ,qBAAqB,GAAG;AAC9C;UACD;AACA,eAAK,SAAS,OAAO,GAAG;QACzB;MACD,SAAS,GAAG;AACX,cAAM,UACL,wDACC,+BACC,GACA,IAAI,CAEN;AACD,aAAK,UAAU,MAAM,SAAS,OAAO;MACtC;IACD;EACD;;;;;EAMQ,MAAM,+BAA4B;AACzC,QAAI,KAAK,aAAa;AACrB,YAAM,IAAI,uBACT,2CACA,4BAAgB,aAAa;IAE/B;AAEA,SAAK,cAAc,IAAI,kCAAgB,IAAI;AAC3C,SAAK,YACH,GAAG,cAAc,KAAK,YAAY,KAAK,IAAI,CAAC,EAC5C,GAAG,cAAc,KAAK,YAAY,KAAK,IAAI,CAAC,EAC5C,GAAG,gBAAgB,KAAK,cAAc,KAAK,IAAI,CAAC,EAChD,GACA,kBACA,KAAK,0BAA0B,KAAK,IAAI,CAAC,EAEzC,GAAG,iBAAiB,KAAK,eAAe,KAAK,IAAI,CAAC,EAClD,GAAG,kBAAkB,KAAK,gBAAgB,KAAK,IAAI,CAAC,EACpD,GAAG,gBAAgB,KAAK,cAAc,KAAK,IAAI,CAAC;AAGlD,SAAK,sBAAqB;AAC1B,SAAK,mBAAkB;AAEvB,QAAI,CAAC,KAAK,SAAS,cAAc,8BAA8B;AAE9D,YAAM,EAAE,QAAO,IAAK,MAAM,KAAK,WAAW,kBAAiB;AAG3D,YAAM,KAAK,WAAW,oBAAmB;AAKzC,YAAM,eAAe,KAAK,aAAY;AACtC,UAAI,KAAK,SAAS,SAAS,aAAa,CAAC,cAAc;AACtD,aAAK,UAAU,MACd,6EACA,MAAM;AAEP,aAAK,SAAS,SAAS,YAAY;MACpC;AAEA,UAAI,cAAc;AACjB,cAAM,KAAK,kBAAkB,KAAK;MACnC;AAEA,UAAI;AACJ,UAAI,KAAK,WAAW,mBAAmB;AAGtC,qBAAa,MAAM,KAAK,WAAW,2BAA0B,GAC3D;MACH;AAIA,YAAM,KAAK,WAAW,iBACrB,KAAK,WAAW,oBACb,uBAAW,OACX,uBAAW,KAAK;AAIpB,YAAM,KAAK,WAAW,SAAQ;AAG9B,YAAM,KAAK,WAAW,UAAS;AAG/B,YAAM,KAAK,iBAAiB,KAAK,WAAW,MAAO;AACnD,YAAM,KAAK,aAAa,KAAK,WAAW,MAAO;AAC/C,YAAM,KAAK,sBAAqB;AAGhC,YAAM,KAAK,WAAW,UACrB,SACA,aAAa,CAAA,GACb,YAAW;AAEV,gBAAI,sBAAO,UAAU,MAAM,QAAQ;AAElC,gBAAM,KAAK,iCAAgC;QAC5C;MACD,CAAC;AAIF,YAAM,KAAK,WAAW,qBAAoB;AAE1C,WAAK,cAAc,MAAM,qBAAqB;AAE9C,UAAI,KAAK,WAAW,SAAS,2BAAe,SAAS;AAEpD,aAAK,WAAW,wBAAuB;MACxC;IACD,OAAO;AAEN,WAAK,WAAW,iBAAiB,IAAI;AACrC,WAAK,WAAW,QAAQ,IAAI;AAC5B,WAAK,WAAW,eAAe,IAAI;AACnC,WAAK,WAAW,YAAY,IAAI;IACjC;AAEA,QAAI,KAAK,WAAW,SAAS,2BAAe,SAAS;AAGpD,YAAM,QAAQ,KAAK,SAAS,cAAc;AAC1C,UAAI,OAAO;AACV,aAAK,UAAU,MACd,gEAAgE;AAEjE,aAAK,mBAAmB,IAAI,4BAAgB;UAC3C,YAAY;UACZ,WAAW,KAAK,YAAY;UAC5B,cAAc,KAAK,SAAS,SAAS;SACrC;MACF,OAAO;AACN,aAAK,UAAU,MACd,wFACA,MAAM;MAER;AAGA,UACC,KAAK,SAAS,gBAEX,OAAO,KAAK,KAAK,SAAS,YAAY,EAAE,KAC1C,CAAC,QACA,IAAI,WAAW,KAAK,KACjB,OAAO,iCACP,+BAAmB,0BAAsB,GAAG,CAAC,CAAC,GAElD;AACD,aAAK,UAAU,MACd,6EAA6E;AAE9E,aAAK,oBAAoB,MAAM,6BAAiB,OAAM;AAEtD,mBACO,YAAY;UACjB;UACA;UACA;UACA;WAEA;AACD,gBAAM,MAAM,KAAK,SAAS,aAAa,QAAQ;AAC/C,cAAI,KAAK;AACR,kBAAM,KAAK,kBAAkB,OAC5B,0BAAc,QAAQ,GACtB,GAAG;UAEL;QACD;MACD,OAAO;AACN,aAAK,UAAU,MACd,wFACA,MAAM;MAER;AAEA,UACC,KAAK,SAAS,uBAAuB,oBAClC,KAAK,SAAS,uBAAuB,kBACvC;AACD,aAAK,UAAU,MACd,yFAAyF;AAE1F,aAAK,qBAAqB,MAAM,6BAAiB,OAAM;AACvD,YAAI,KAAK,SAAS,uBAAuB,kBAAkB;AAC1D,gBAAM,KAAK,mBAAmB,OAC7B,0BAAc,kBACd,KAAK,SAAS,sBAAsB,gBAAgB;QAEtD;AACA,YAAI,KAAK,SAAS,uBAAuB,kBAAkB;AAC1D,gBAAM,KAAK,mBAAmB,OAC7B,0BAAc,kBACd,KAAK,SAAS,sBAAsB,gBAAgB;QAEtD;MACD,OAAO;AACN,aAAK,UAAU,MACd,8EACA,MAAM;MAER;IACD,OAAO;AAIN,cAAI,+BAAkB,KAAK,WAAW,SAAU,GAAG;AAClD,cAAM,wBAAwB;UAC7B,0BAAc;UACd,0BAAc;UACb,IACD,CAAC,OAAQ;UACR;UACA,KAAK,SACJ,8BAAU,WAAW,sBAAsB,EAAE,CAAC;SAEH,EAC5C,OAAO,CAAC,MACT,EAAE,CAAC,KAAK,MAAS;AAElB,YAAI,sBAAsB,QAAQ;AACjC,eAAK,UAAU,MACd,6FAA6F;AAE9F,eAAK,qBAAqB,MAAM,6BAAiB,OAAM;AACvD,qBAAW,CAAC,IAAI,GAAG,KAAK,uBAAuB;AAC9C,kBAAM,KAAK,mBAAmB,OAAO,IAAI,GAAG;UAC7C;QACD,WACC,KAAK,SAAS,uBAAuB,oBAClC,KAAK,SAAS,uBAAuB,kBACvC;AACD,eAAK,UAAU,MACd,yFAAyF;AAE1F,eAAK,qBAAqB,MAAM,6BAAiB,OAAM;AACvD,cAAI,KAAK,SAAS,uBAAuB,kBAAkB;AAC1D,kBAAM,KAAK,mBAAmB,OAC7B,0BAAc,kBACd,KAAK,SAAS,sBACZ,gBAAgB;UAEpB;AACA,cAAI,KAAK,SAAS,uBAAuB,kBAAkB;AAC1D,kBAAM,KAAK,mBAAmB,OAC7B,0BAAc,kBACd,KAAK,SAAS,sBACZ,gBAAgB;UAEpB;QACD,OAAO;AACN,eAAK,UAAU,MACd,8EACA,MAAM;QAER;MACD,OAAO;AACN,cAAM,QAAQ,KAAK,SAClB,8BAAU,WAAW,aAAa,0BAAc,SAAS,CAAC;AAE3D,YAAI,OAAO;AACV,eAAK,UAAU,MACd,oEAAoE;AAErE,eAAK,mBAAmB,IAAI,4BAAgB;YAC3C,YAAY;YACZ,WAAW,KAAK,YAAY;YAC5B,cAAc,KAAK,SAAS,SAAS;WACrC;QACF,WAAW,KAAK,SAAS,cAAc,WAAW;AACjD,eAAK,UAAU,MACd,wEAAwE;AAEzE,eAAK,mBAAmB,IAAI,4BAAgB;YAC3C,YAAY,KAAK,SAAS,aAAa;YACvC,WAAW,KAAK,YAAY;YAC5B,cAAc,KAAK,SAAS,SAAS;WACrC;QACF,OAAO;AACN,eAAK,UAAU,MACd,4FACA,MAAM;QAER;AACA,cAAM,eAAe,+BAAmB,IACvC,CAAC,OAAQ;UACR;UACA,KAAK,SACJ,8BAAU,WAAW,aAAa,EAAE,CAAC;SAEM,EAC5C,OAAO,CAAC,MACT,EAAE,CAAC,KAAK,MAAS;AAElB,YAAI,aAAa,QAAQ;AACxB,eAAK,UAAU,MACd,iFAAiF;AAElF,eAAK,oBAAoB,MAAM,6BAAiB,OAAM;AACtD,qBAAW,CAAC,IAAI,GAAG,KAAK,cAAc;AACrC,kBAAM,KAAK,kBAAkB,OAAO,IAAI,GAAG;UAC5C;QACD,WACC,KAAK,SAAS,gBACX,OAAO,KAAK,KAAK,SAAS,YAAY,EAAE,KAC1C,CAAC,QACA,IAAI,WAAW,KAAK,KACjB,OAAO,iCACP,+BAAmB,0BAAsB,GAAG,CAAC,CAAC,GAElD;AACD,eAAK,UAAU,MACd,6EAA6E;AAE9E,eAAK,oBAAoB,MAAM,6BAAiB,OAAM;AAEtD,qBACO,YAAY;YACjB;YACA;YACA;YACA;aAEA;AACD,kBAAM,MAAM,KAAK,SAAS,aAAa,QAAQ;AAC/C,gBAAI,KAAK;AACR,oBAAM,KAAK,kBAAkB,OAC5B,0BAAc,QAAQ,GACtB,GAAG;YAEL;UACD;QACD,OAAO;AACN,eAAK,UAAU,MACd,4FACA,MAAM;QAER;MACD;IACD;AAGA,SAAK,yBAAyB;AAC9B,SAAK,UAAU,MAAM,cAAc;AACnC,SAAK,KAAK,cAAc;AAGxB,eAAW,QAAQ,KAAK,YAAY,MAAM,OAAM,GAAI;AACnD,WAAK,qBAAqB,IAAI;IAC/B;AAEA,QAAI,KAAK,WAAW,SAAS,2BAAe,SAAS;AAEpD,WAAK,YAAY,MAAK;AACtB,WAAK,0BAA0B;AAE/B,UAAI,CAAC,KAAK,SAAS,cAAc,mBAAmB;AAGnD,cAAM,iBAAiB,KAAK,YAAY,MAAM,IAC7C,KAAK,YAAY,SAAU;AAE5B,cAAM,KAAK,sBAAsB,cAAc;AAE/C,uBAAe,YAAW;AAG1B,cAAM,qBAAqB,CAAC,GAAG,KAAK,YAAY,MAAM,OAAM,CAAE,EAC5D,OAAO,CAAC,MAAM,EAAE,OAAO,KAAK,YAAa,SAAS,EAClD,KAAK,CAAC,GAAG;;UAER,EAAE,iBAAiB,EAAE,mBAGpB,EAAE,cAAc,IAAI,EAAE,sBAAsB,IAAI,MAC9C,EAAE,cACF,IACA,EAAE,sBACF,IACA,OAIF,EAAE,UAAU,QAAO,KAAM,MACvB,EAAE,UAAU,QAAO,KAAM,MAGzB,EAAE,KAAK,EAAE;SAAG;AAGlB,YAAI,mBAAmB,QAAQ;AAC9B,eAAK,cAAc,MAClB,uDACC,mBAAmB,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,IAAI,CAC9C,EAAE;AAEH,qBAAW,QAAQ,oBAAoB;AACtC,gBAAI,KAAK,UAAU;AAElB,mBAAK,aAAY;YAClB;AAEA,kBAAM,YAAW;AAGhB,kBAAI,KAAK,iBAAiB,4BAAe,UAAU;AAClD,sBAAM,KAAK,sBAAsB,IAAI;cACtC,WACC,KAAK,eAAe,KAAK,qBACxB;AAED,sBAAM,KAAK,KAAI;cAChB;YACD,GAAE;UACH;QACD;MACD;IACD,OAAO;AACN,UAAI,CAAC,KAAK,SAAS,cAAc,mBAAmB;AAInD,cAAM,iBAAiB,KAAK,YAAY,MAAM,IAC7C,KAAK,YAAY,SAAU;AAE5B,cAAM,KAAK,sBAAsB,cAAc;AAE/C,uBAAe,YAAW;AAG1B,mBAAW,QAAQ,KAAK,YAAY,MAAM,OAAM,GAAI;AACnD,cAAI,KAAK;AAAkB;AAC3B,gBAAM,KAAK,mBAAmB,EAAC;QAChC;AAGA,cAAM,qBAAqB,CAAC,GAAG,KAAK,YAAY,MAAM,OAAM,CAAE,EAC5D,OAAO,CAAC,MAAM,EAAE,OAAO,KAAK,YAAa,SAAS,EAClD,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,mBAAmB,EACpD,KAAK,CAAC,GAAG;;WAGP,EAAE,cAAc,IAAI,MAClB,EAAE,cAAc,IAAI,OAItB,EAAE,UAAU,QAAO,KAAM,MACvB,EAAE,UAAU,QAAO,KAAM,MAGzB,EAAE,KAAK,EAAE;SAAG;AAGlB,YAAI,mBAAmB,QAAQ;AAC9B,eAAK,cAAc,MAClB,4BACC,mBAAmB,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,IAAI,CAC9C,EAAE;AAEH,qBAAW,QAAQ,oBAAoB;AACtC,iBAAK,KAAK,KAAI;UACf;QACD;MACD;IACD;AAKA,SAAK,sBAAsB,KAAK,SAAS;EAC1C;EAEQ,6BAA6B,oBAAI,IAAG;EACpC,6BAA6B,oBAAI,IAAG;;;;;;;;;EASrC,MAAM,sBAAsB,MAAe;AACjD,QAAI,KAAK,mBAAmB,4BAAe,UAAU;AACpD;IACD;AAGA,QAAI,KAAK,2BAA2B,IAAI,KAAK,EAAE,GAAG;AACjD,WAAK,2BAA2B,IAAI,KAAK,EAAE,GAAG,MAAK;AACnD,WAAK,2BAA2B,OAAO,KAAK,EAAE;IAC/C;AAGA,UAAM,KAAK,mBACV,CAAC,MACA,EAAE,QAAQ,UAAS,MAAO,KAAK,OAC3B,EAAE,aAAa,4BAAgB,aAC/B,EAAE,QAAQ,cACf,+BACA,4BAAgB,6BAA6B;AAG9C,UAAM,uBAAuB,KAAK,SAAS,SAAS;AAEpD,QAAI;AACH,UAAI,CAAE,MAAM,KAAK,kBAAiB,GAAK;AAEtC,YAAI,KAAK,WAAW,wBAAW,MAAM;AACpC,eAAK,cAAc,QAClB,KAAK,IACL,sBAAsB,KAAK,iBAAiB,IAAI,oBAAoB,2BACpE,MAAM;AAEP,eAAK,KAAK,oBAAoB,MAAM;YACnC,cAAc;YACd,SAAS;WACT;QACF,WAAW,KAAK,oBAAoB,sBAAsB;AAEzD,gBAAM,eAAe,KAAK,IACzB,KACA,KAAK,oBAAoB,GAAI;AAE9B,eAAK,cAAc,QAClB,KAAK,IACL,qBAAqB,KAAK,iBAAiB,IAAI,oBAAoB,wBAAwB,YAAY,UACvG,MAAM;AAEP,eAAK,KAAK,oBAAoB,MAAM;YACnC,cACC,WAAW,KAAK,iBAAiB,IAAI,oBAAoB;YAC1D,SAAS;YACT,SAAS,KAAK;YACd,aAAa;WACb;AAED,eAAK,2BAA2B,IAC/B,KAAK,QACL,wBAAS,MAAK;AACb,iBAAK,2BAA2B,OAAO,KAAK,EAAE;AAC9C,iBAAK,KAAK,sBAAsB,IAAI;UACrC,GAAG,YAAY,EAAE,MAAK,CAAE;QAE1B,OAAO;AACN,eAAK,cAAc,QAClB,KAAK,IACL,6CACA,MAAM;AAEP,eAAK,KAAK,oBAAoB,MAAM;YACnC,cAAc;YACd,SAAS;YACT,SAAS;YACT,aAAa;WACb;QACF;MACD,WACC,KAAK,kBAAkB,UACpB,KAAK,eAAe,UACpB,KAAK,aAAa,UAClB,KAAK,mBAAmB,UACxB,CAAC,KAAK,gBACN,QAAQ,IAAI,aAAa,QAC3B;AAID,iBAAK,+CAA0B,MAAM,IAAW,EAAE,MAAM,kBAAI;MAC7D;IACD,SAAS,GAAG;AACX,cAAI,0BAAa,CAAC,GAAG;AACpB,YACC,EAAE,SAAS,4BAAgB,mBACxB,EAAE,SAAS,4BAAgB,wBAC7B;AAED;QACD,WACC,EAAE,SAAS,4BAAgB,+BAC1B;AAED;QACD;AACA,aAAK,cAAc,QAClB,KAAK,IACL,gCAAgC,EAAE,OAAO,IACzC,OAAO;MAET,OAAO;AACN,cAAM;MACP;IACD;EACD;;EAGQ,qBAAqB,MAAe;AAC3C,SAAK,GAAG,WAAW,KAAK,aAAa,KAAK,IAAI,CAAC,EAC7C,GAAG,SAAS,KAAK,YAAY,KAAK,IAAI,CAAC,EACvC,GAAG,SAAS,KAAK,YAAY,KAAK,IAAI,CAAC,EACvC,GAAG,QAAQ,KAAK,WAAW,KAAK,IAAI,CAAC,EACrC,GAAG,uBAAuB,KAAK,yBAAyB,KAAK,IAAI,CAAC,EAClE,GAAG,SAAS,KAAK,YAAY,KAAK,IAAI,CAAC,EACvC,GACA,4BACA,KAAK,sBAAsB,KAAK,IAAI,CAAC,EAErC,GAAG,gBAAgB,KAAK,mBAAmB,KAAK,IAAI,CAAC;AAGvD,eAAW,SAAS,8BAAiB;AACpC,WAAK,GAAG,OAAO,IAAI,SAAe;AAEjC,aAAK,KAAK,QAAQ,KAAK,IAAI,GAAG,IAAI;MACnC,CAAC;IACF;EACD;;EAGQ,wBAAwB,MAAe;AAC9C,SAAK,mBAAkB;EACxB;;EAGQ,aAAa,MAAiB,WAAqB;AAC1D,SAAK,cAAc,QAClB,KAAK,IACL,eACC,cAAc,wBAAW,UAAU,KAAK,MACzC,QAAQ;AAIT,QAAI,cAAc,wBAAW,QAAQ;AACpC,WAAK,KAAK,aAAa,CAAC,EAAE,QAAO,MAAM;AAEtC,YAAI,QAAQ,UAAS,MAAO,KAAK;AAAI,iBAAO,EAAE,MAAM,OAAM;AAE1D,YAAI,cAAc,OAAO,GAAG;AAC3B,iBAAO,EAAE,MAAM,WAAW,SAAS,OAAS;QAC7C;AAEA,eAAO,EAAE,MAAM,UAAS;MACzB,CAAC;IACF;AAGA,SAAK,wBAAwB,IAAI;EAClC;;EAGQ,YAAY,MAAiB,WAAqB;AACzD,SAAK,cAAc,QAClB,KAAK,IACL,eACC,cAAc,wBAAW,UAAU,KAAK,MACzC,SAAS;AAKV,SAAK,0BAA0B,KAAK,EAAE;EACvC;;EAGQ,YAAY,MAAiB,WAAqB;AACzD,SAAK,cAAc,QAClB,KAAK,IACL,eACC,cAAc,wBAAW,UAAU,KAAK,MACzC,QAAQ;AAET,QACC,cAAc,wBAAW,QACtB,KAAK,mBAAmB,4BAAe,YACvC,CAAC,KAAK,SAAS,cAAc,mBAC/B;AACD,WAAK,KAAK,sBAAsB,IAAI;IACrC;EACD;;EAGQ,WAAW,MAAiB,WAAqB;AACxD,SAAK,cAAc,QAClB,KAAK,IACL,eACC,cAAc,wBAAW,UAAU,KAAK,MACzC,OAAO;AAKR,SAAK,mBAAkB;EACxB;;EAGQ,YAAY,MAAe;AAClC,SAAK,YAAY,IAAI,KAAK,EAAE;AAC5B,SAAK,cAAc,QAAQ,KAAK,IAAI,8BAA8B;AAIlE,QAAI,KAAK,2BAA2B,IAAI,KAAK,EAAE,GAAG;AACjD,WAAK,2BAA2B,IAAI,KAAK,EAAE,GAAG,MAAK;AACnD,WAAK,2BAA2B,OAAO,KAAK,EAAE;IAC/C;AACA,QAAI,CAAC,KAAK,UAAU;AAEnB,YAAM,kBAAkB,KAAK,KAAK,OAAM,IAAK;AAC7C,WAAK,2BAA2B,IAC/B,KAAK,QACL,2BAAY,MAAK;AAChB,aAAK,KAAK,kBAAiB,EAAG,MAAM,MAAK;QAEzC,CAAC;MACF,GAAG,qBAAS,QAAQ,eAAe,CAAC,EAAE,MAAK,CAAE;IAE/C;AAEA,SAAK,mBAAkB;EACxB;;EAGQ,qBAAkB;AAEzB,QAAI,KAAK;AAAyB;AAElC,eAAW,CAAC,IAAI,IAAI,KAAK,KAAK,WAAW,OAAO;AAE/C,UAAI,KAAK,WAAW,wBAAW;AAAM;AAErC,UAAI,CAAC,KAAK,YAAY,IAAI,EAAE;AAAG;IAChC;AAEA,SAAK,cAAc,MAAM,gCAAgC;AACzD,SAAK,KAAK,iBAAiB;AAC3B,SAAK,0BAA0B;AAG/B,SAAK,KAAK,yBAAwB,EAAG,MAAM,MAAK;IAEhD,CAAC;EACF;EAEQ,qBAA8B;;EAEtC,IAAW,oBAAiB;AAC3B,WAAO,KAAK;EACb;EAEQ;EAIA,sBAAsB,oBAAI,IAAG;;;;;EAM9B,gBACN,YAAqD;AAErD,SAAK,0BAAsB,iCAC1B,KAAK,qBACL,UAAU;AAEX,SAAK,aAAa,KAAK,4BACtB,KAAK,mBAAmB;EAE1B;;;;;EAMQ,4BACP,YAA+B;AAE/B,UAAM,sBAAsB,IAAI,IAAI;MACnC,CAAC,SAAS,UAAU;MACpB,GAAG;KACH;AACD,QACC,oBAAoB,SAAS,KAC1B,KAAK,qBACL,KAAK,kBAAkB,oBAAoB,mBAE3C,KAAK,kBAAkB,oBAAoB,YAC7C;AACD,0BAAoB,IACnB,KAAK,kBAAkB,iBACvB,KAAK,kBAAkB,kBAAkB;IAE3C;AACA,eAAO,8CAA4B,mBAAmB;EACvD;EAEQ,aAAqB,YAAY,UAAU;;EAEnD,IAAW,YAAS;AACnB,WAAO,KAAK;EACb;;EAGO,iCACN,YAAsD;AAEtD,QAAI,CAAC,cAAc,OAAO,KAAK,UAAU,EAAE,WAAW,GAAG;AACxD,aAAO,KAAK;IACb;AAEA,UAAM,aAAS,iCACd,KAAK,qBACL,YACA,KAAK;AAEN,WAAO,KAAK,4BAA4B,MAAM;EAC/C;;;;;EAMO,iBACN,SAAgE;AAEhE,QAAI,KAAK;AAAoB;AAE7B,QACC,KAAC,4BAAS,OAAO,KACd,OAAO,QAAQ,oBAAoB,YACnC,OAAO,QAAQ,uBAAuB,UACxC;AACD,YAAM,IAAI,uBACT,uHACA,4BAAgB,qBAAqB;IAEvC,WAAW,QAAQ,gBAAgB,SAAS,KAAK;AAChD,YAAM,IAAI,uBACT,2EACA,4BAAgB,qBAAqB;IAEvC,WAAW,QAAQ,mBAAmB,SAAS,KAAK;AACnD,YAAM,IAAI,uBACT,8EACA,4BAAgB,qBAAqB;IAEvC;AAEA,SAAK,qBAAqB;AAC1B,SAAK,oBAAoB;AAGzB,QAAI,KAAK,yBAAyB;AACjC,WAAK,KAAK,yBAAwB,EAAG,MAAM,MAAK;MAEhD,CAAC;IACF;EACD;;;;EAKO,oBAAiB;AACvB,SAAK,qBAAqB;AAC1B,SAAK,oBAAoB;AACzB,SAAK,mBAAmB,MAAK;AAC7B,SAAK,oBAAoB;EAC1B;;;EAIO,MAAM,UAAO;AAEnB,QAAI,CAAC,KAAK,SAAU,IAAI,MAAM,GAAG;AAChC,WAAK,SAAU,IACd,QACA,oBAAM,SAAK,yBAAY,EAAE,CAAC,EAAE,SAAS,KAAK,CAAC;IAE7C;AACA,UAAM,MAAM,KAAK,SAAU,IAAI,MAAM;AACrC,WAAO;EACR;EAEQ;EACA,MAAM,2BAAwB;AAErC,QAAI,CAAC,KAAK,qBAAqB,CAAC,KAAK;AAAmB;AAExD,SAAK,mBAAmB,MAAK;AAC7B,SAAK,oBAAoB;AAEzB,QAAI,UAA4B;AAChC,QAAI;AACH,YAAM,aAAa,UAAM,qCAAkB,MAAM;QAChD,eAAe;QACf,GAAG,KAAK;QACR,aAAa,QAAQ,SAAS;QAC9B,IAAI,QAAQ;QACZ,MAAM,QAAQ;OACd;AACD,gBAAU,UAAM,kCAAe,UAAU;IAC1C,QAAQ;AAEP,gBAAU;IACX;AACC,UAAI,OAAO,YAAY,UAAU;AAChC,aAAK,UAAU,MACd,yEAAyE,OAAO,aAChF,SAAS;AAGV,cAAM,UAAU,KAAK,IACpB,qBAAS,QAAQ,CAAC,GAClB,KAAK,IAAI,UAAU,KAAM,qBAAS,MAAM,CAAC,CAAC,CAAC;AAE5C,aAAK,wBAAoB,wBAAS,MAAK;AACtC,eAAK,KAAK,yBAAwB;QACnC,GAAG,OAAO,EAAE,MAAK;MAClB,OAAO;AACN,aAAK,UAAU,MACd,UACG,qEACA,6EACH,SAAS;AAEV,aAAK,wBAAoB,wBAAS,MAAK;AACtC,eAAK,KAAK,yBAAwB;QACnC,GAAG,qBAAS,MAAM,UAAU,KAAK,CAAC,CAAC,EAAE,MAAK;MAC3C;IACD;EACD;;EAGQ,yBAAyB,MAAe;AAC/C,SAAK,wBAAwB,IAAI;EAClC;;EAGQ,YAAY,MAAe;AAKlC,UAAM,SAAS,aAAa,KAAK,EAAE;AACnC,eAAW,OAAO,KAAK,QAAS,KAAI,GAAI;AACvC,UAAI,IAAI,WAAW,MAAM,GAAG;AAC3B,aAAK,QAAS,OAAO,GAAG;MACzB;IACD;AACA,eAAW,OAAO,KAAK,WAAY,KAAI,GAAI;AAC1C,UAAI,IAAI,WAAW,MAAM,GAAG;AAC3B,aAAK,WAAY,OAAO,GAAG;MAC5B;IACD;AAEA,SAAK,WAAW,8BAAU,KAAK,KAAK,EAAE,EAAE,QAAQ;EACjD;;EAGQ,YAAY,MAAe;AAClC,SAAK,qBAAqB,IAAI;AAE9B,QAAI,KAAK,SAAS,WAAW;AAAoB;AACjD,QAAI,KAAK,SAAS,cAAc;AAAmB;AAKnD,SAAK,KAAK,sBAAsB,IAAI;EACrC;;EAGQ,cAAc,MAAiB,QAAwB;AAE9D,SAAK,wBAAwB,IAAI;AACjC,QAAI,KAAK,sBAAsB,IAAI,KAAK,EAAE,GAAG;AAC5C,WAAK,sBAAsB,IAAI,KAAK,EAAE,GAAG,MAAK;AAC9C,WAAK,sBAAsB,OAAO,KAAK,EAAE;IAC1C;AACA,QAAI,KAAK,2BAA2B,IAAI,KAAK,EAAE,GAAG;AACjD,WAAK,2BAA2B,IAAI,KAAK,EAAE,GAAG,MAAK;AACnD,WAAK,2BAA2B,OAAO,KAAK,EAAE;IAC/C;AACA,QAAI,KAAK,2BAA2B,IAAI,KAAK,EAAE,GAAG;AACjD,WAAK,2BAA2B,IAAI,KAAK,EAAE,GAAG,MAAK;AACnD,WAAK,2BAA2B,OAAO,KAAK,EAAE;IAC/C;AAGA,SAAK,QAAQ,MAAK;AAClB,SAAK,WAAW,8BAAU,KAAK,KAAK,EAAE,EAAE,QAAQ;AAGhD,SAAK,iBAAiB,2BAA2B,KAAK,EAAE;AACxD,SAAK,kBAAkB,YAAY,KAAK,EAAE;AAC1C,SAAK,mBAAmB,YAAY,KAAK,EAAE;AAE3C,SAAK,KAAK,6BACT,KAAK,IACL,yCACA,4BAAgB,sBAAsB;AAGvC,UAAM,WAAW,WAAW,kCAAiB,YACzC,WAAW,kCAAiB;AAChC,QAAI,CAAC,UAAU;AAId,WAAK,WACH,8BAA8B,KAAK,EAAE,EACrC,MAAM,CAAC,QAAO;AACd,aAAK,UAAU,MACd,yBAAyB,KAAK,EAAE,2BAA2B,IAAI,OAAO,IACtE,OAAO;MAET,CAAC;IACH;AAGA,SAAK,QAAO;AAGZ,SAAK,mBAAkB;EACxB;EAEQ,0BAA0B,SAAyB;AAC1D,SAAK,cAAa;EACnB;EAEQ,MAAM,eACb,QACA,YAAkB;AAElB,QAAI;AACH,WAAK,UAAU,MACd,mCACC,uBAAQ,MAAM,CACf,qCAAqC;AAEtC,YAAM,KAAK,gCAA+B;IAC3C,SAAS,GAAG;AACX,WAAK,UAAU,MACd,0DACC,+BAAgB,CAAC,CAClB,IACA,OAAO;IAET;EACD;EAEQ,kBAAe;AACtB,SAAK,UAAU,MAAM,0BAA0B;EAChD;EAEQ,MAAM,gBAAa;AAC1B,QAAI;AACH,WAAK,UAAU,MACd,yEACC,uBAAQ,KAAK,WAAW,MAAM,CAC/B,KAAK;AAEN,YAAM,KAAK,gCAA+B;IAC3C,SAAS,GAAG;AACX,WAAK,UAAU,MACd,0DACC,+BAAgB,CAAC,CAClB,IACA,OAAO;IAET;EACD;EAEQ,MAAM,kCAA+B;AAC5C,UAAM,KAAK,eAAe,MAAK;AAC/B,UAAM,KAAK,UAAU,MAAK;AAC1B,UAAM,KAAK,aAAa,MAAK;AAG7B,UAAM,KAAK,iBAAiB,KAAK,WAAW,MAAO;AACnD,UAAM,KAAK,aAAa,KAAK,WAAW,MAAO;AAC/C,UAAM,KAAK,sBAAqB;EACjC;;;;;EAMO,2CACN,oBAAsC;AAGtC,QAAI,CAAC,oBAAoB;AAExB,aAAO;IACR,WAAW,qBAAqB,IAAI;AACnC,aAAO,qBAAqB;IAC7B,WAAW,qBAAqB,IAAI;AACnC,aAAO,qBAAqB;IAC7B,OAAO;AACN,aAAO,qBAAqB;IAC7B;EACD;;EAGQ,MAAM,sBACb,MACA,QAA4B;AAE5B,UAAM,EAAE,SAAS,YAAW,IAAK;AAGjC,QAAI,CAAC;AAAS;AAKd,SAAK,iBAAiB,2BAA2B,KAAK,EAAE;AACxD,SAAK,kBAAkB,YAAY,KAAK,EAAE;AAC1C,SAAK,mBAAmB,YAAY,KAAK,EAAE;AAG3C,UAAM,WAAW,OAAO,YAAY;AAEpC,QAAI,aAAa;AAChB,WAAK,cAAc,QAClB,KAAK,IACL,6CAA6C,QAAQ,aAAa;AAGnE,WAAK,2BAA2B,IAC/B,KAAK,QACL,wBAAS,MAAK;AACb,aAAK,2BAA2B,OAAO,KAAK,EAAE;AAC9C,aAAK,KAAK,YAAY;;UAErB,eAAe;SACf;MACF,GAAG,WAAW,GAAI,EAAE,MAAK,CAAE;IAE7B,OAAO;AACN,WAAK,cAAc,QAClB,KAAK,IACL,4FAA4F,QAAQ,aAAa;AAGlH,gBAAM,mBAAK,WAAW,KAAM,IAAI;AAEhC,UAAI;AACH,cAAM,aAAa,KAAK,eAAe;AACvC,cAAM,WAAW,IAAG;AACpB,YACC,WAAW,gBAAgB,yBAAe,eAAe,GACxD;AACD,gBAAM,WAAW,gBAAe;QACjC;AACA,YACC,WAAW,gBAAgB,yBAAe,gBAAgB,GACzD;AACD,gBAAM,WAAW,iBAAgB;QAClC;MACD,QAAQ;MAER;AAGA,WAAK,YAAY;AACjB,WAAK,wBAAwB,IAAI;IAClC;EACD;;EAGQ,qBAAgD,wBACvD,UACA,MACA,WACG;AACH,QAAI;AACJ,QAAI;AACJ,QAAI,SAAS,2BAAe,cAAc;AACzC,YAAM,MAAqB;QAC1B,MAAM,OAAO;QACb,OAAO,OAAO;;AAEf,UAAI,OAAO,YAAY;AACtB,gBAAI,4BAAa,OAAO,UAAU,GAAG;AACpC,cAAI,iBAAa,0BAAW,OAAO,UAAU;QAC9C,WAAW,qBAAS,WAAW,OAAO,UAAU,GAAG;AAClD,cAAI,WAAW,OAAO,WAAW,SAAQ;QAC1C,eAAW,4BAAS,OAAO,UAAU,GAAG;AACvC,iBAAO,OAAO,KAAK,OAAO,UAAU;QACrC;MACD;AACA,eAAS;AACT,oBAAU,kCAAqB,GAAG;IACnC,WAAW,SAAS,2BAAe,eAAe,GAAG;AACpD,eAAS;AACT,oBAAU,kCAAqB;QAC9B,cAAc,OAAO;QACrB,aAAa,OAAO;OACpB;IACF,WAAW,SAAS,2BAAe,mBAAmB,GAAG;AACxD,eAAS;AACT,oBAAU,sCACT,4BAAe;QACd,cAAc,OAAO;QACrB,WAAW,OAAO;OAClB,CAAC;IAEJ,OAAmD;AAElD;IACD;AAEA,SAAK,cAAc,QAAQ,SAAS,QAAQ;MAC3C,UAAU,SAAS;MACnB,SAAS,CAAC,QAAQ,GAAG,QAAQ,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI;KAC5D;EACF,GA9CwD;;EAiDhD,mBACP,MAAsC;AAGtC,QACC,KAAK,uBACJ,CAAC,MAAM,EAAE,QAAQ,UAAS,MAAO,KAAK,EAAE,GAExC;AACD,aAAO;IACR;AAGA,WAAO,KAAK,kBAAiB;EAC9B;;EAGO,uBACN,WAAsC;AAItC,QAAI,CAAC,CAAC,KAAK,MAAM,KAAK,CAAC,MAAM,UAAU,CAAC,CAAC;AAAG,aAAO;AACnD,WAAO,KAAK,OAAO,KAClB,CAAC,MAAM,EAAE,sBAAsB,UAAU,EAAE,kBAAkB,CAAC;EAEhE;;;;;;;;;;EAWO,sBACN,IACA,QACA,gBAAwB,GAAC;AAEzB,QAAI,CAAC,KAAK,aAAa,MAAM,IAAI,MAAM,GAAG;AACzC,aAAO;IACR;AACA,UAAM,OAAO,KAAK,WAAW,MAAM,IAAI,MAAM;AAC7C,UAAM,WAAW,KAAK,YAAY,aAAa;AAC/C,QAAI;AAAU,aAAO,SAAS,aAAa,EAAE;AAG7C,WAAO,KAAK,aAAa,EAAE;EAC5B;;;;;;;;;;EAWO,iBACN,IACA,QACA,gBAAwB,GAAC;AAEzB,UAAM,yBAAqB,iCAAsB,EAAE;AACnD,QACC,uBAAuB,KACpB,uBAAuB,OAAO,mBAChC;AACD,aAAO;IACR;AACA,UAAM,mBAAmB,KAAK,sBAC7B,IACA,QACA,aAAa;AAEd,QAAI,qBAAqB,GAAG;AAE3B,aAAO;IACR;AAEA,WAAO,KAAK,IAAI,kBAAkB,kBAAkB;EACrD;;;;;;;;EASA,WACC,MACA,QACA,gBAAwB,GAAC;AAGzB,QACC,SAAS,2BAAe,YACrB,SAAS,2BAAe,YAAY,GACtC;AACD,aAAO;IACR;AAEA,UAAM,OAAO,KAAK,WAAW,MAAM,IAAI,MAAM;AAE7C,QAAI,CAAC;AAAM,aAAO;AAElB,UAAM,WAAW,KAAK,YAAY,aAAa;AAE/C,UAAM,gBAAgB,KAAK,wBAAuB;AAElD,QACC,kBAAkB,UAAa,kBAAkB,0BAAc,MAC9D;AACD,aAAO;IACR;AAKA,UAAM,YAAY,SAAS,2BAAe;AAI1C,YAAI,+BAAkB,aAAa,GAAG;AAErC,aACC,CAAC,CAAC,KAAK,oBAAoB,MAAM,MAC7B,cAAc,YAAY,MAAM,WAAW,IAAI;IAErD;AAGA,QAAI,kBAAkB,0BAAc,WAAW;AAE9C,aACC,CAAC,CAAC,KAAK,oBACH,cAAc,YAAY,MAAM,WAAW,IAAI;IAErD;AAGA,WAAO;EACR;;;;;;;EAQO,aACN,QACA,SACA,SAA4B;AAE5B,UAAM,OAAO,KAAK,WAAW,MAAM,WAAW,MAAM;AACpD,WAAO,KAAK,aAAa,SAAS,OAAO;EAC1C;EAEQ,kBAA2B;EAE3B,eAAY;AAEnB,QAAI,KAAK,aAAa,aAAa,KAAK;AAAG,aAAO;AAGlD,UAAM,EAAE,gBAAgB,aAAa,UAAS,IAAK,KAAK;AAGxD,QACC,mBAAmB,OAChB,gBAAgB,KAChB,cAAc,GAChB;AACD,aAAO;IACR;AAGA,QACC,mBAAmB,OAChB,gBAAgB,QAChB,cAAc,GAChB;AACD,aAAO;IACR;AAGA,QACC,mBAAmB,OAChB,gBAAgB,QAChB,cAAc,KAEhB;AACD,aAAO;IACR;AAGA,WAAO,CAAC,CAAC,KAAK,SAAS,SAAS;EACjC;;;;EAKO,MAAM,eAAY;AACxB,QAAI,KAAK,aAAY,GAAI;AACxB,YAAM,KAAK,UAAS;IACrB,OAAO;AACN,YAAM,UACL;AACD,WAAK,cAAc,MAAM,SAAS,MAAM;IACzC;EACD;;;;;;;;EASO,MAAM,YAAS;AACrB,QAAI,CAAC,KAAK,aAAY,GAAI;AACzB,YAAM,UACL;AACD,WAAK,cAAc,MAAM,SAAS,OAAO;AACzC,YAAM,IAAI,uBACT,SACA,4BAAgB,sBAAsB;IAExC;AAEA,QAAI,KAAK,aAAa,iCAAgC,GAAI;AACzD,YAAM,UACL;AACD,WAAK,cAAc,MAAM,SAAS,OAAO;AACzC,YAAM,IAAI,uBACT,SACA,4BAAgB,4BAA4B;IAE9C;AAEA,WAAO,KAAK,kBAAkB,IAAI;EACnC;EAEQ,MAAM,kBAAkB,gBAAuB;AACtD,SAAK,cAAc,MAAM,0BAA0B;AAEnD,QAAI;AACH,WAAK,kBAAkB;AACvB,YAAM,KAAK,YAAY,IAAI,kCAAgB,GAAI;QAC9C,cAAc;QACd,iBAAiB;OACjB;IACF,SAAS,GAAG;AACX,WAAK,cAAc,MAClB,0BAAsB,+BAAgB,CAAC,CAAC,IACxC,OAAO;AAGR,cAAI,oCAAuB,CAAC,GAAG;AAC9B,aAAK,kBAAkB;AACvB,cAAM;MACP;IACD;AAEA,QAAI,KAAK,aAAa;AAErB,WAAK,YAAY,aAAa,IAAI,uBAAW;AAI7C,WAAK,YAAY,kBAAkB,gCAAe,IAAI;IACvD;AAGA,QAAI,CAAE,MAAM,KAAK,gBAAe,GAAK;AACpC,UAAI,gBAAgB;AACnB,cAAM,KAAK,QAAO;MACnB,OAAO;AACN,cAAM,IAAI,uBACT,mDACA,4BAAgB,aAAa;MAE/B;IACD;AAEA,SAAK,kBAAkB;AAGvB,QAAI,CAAC,KAAK,qBAAqB;AAE9B,UAAI,KAAK,QAAQ,SAAS,UAAU;AACnC,aAAK,KAAK,aAAa,cAAa;MACrC;AAGA,WAAK,KAAK,aAAa,iBAAiB,uBAAW,IAAI;AAGvD,WAAK,iBAAgB;IACtB;EACD;;EAGO,MAAM,sBAAmB;AAC/B,SAAK,cAAc,MAAM,0BAA0B;AAEnD,QAAI;AACH,WAAK,kBAAkB;AACvB,YAAM,KAAK,YAAY,IAAI,kCAAgB,GAAI;QAC9C,cAAc;QACd,iBAAiB;OACjB;IACF,SAAS,GAAG;AACX,WAAK,cAAc,MAClB,0BAAsB,+BAAgB,CAAC,CAAC,IACxC,OAAO;IAET;AAGA,QAAI,CAAE,MAAM,KAAK,gBAAe,GAAK;AACpC,YAAM,KAAK,mBACV,iDAAiD;IAEnD;AAEA,SAAK,kBAAkB;AAGvB,UAAM,KAAK,kBAAiB;AAC5B,SAAK,KAAK,6BAA4B;EACvC;;;;;EAMQ,mCAAgC;AACvC,QAAI,CAAC,KAAK,SAAS,SAAS,gCAAgC;AAC3D,aAAO;IACR;AAEA,WAAO,KAAK;EACb;EAEQ,MAAM,kBAAe;AAI5B,SAAK,cAAc,MAAM,4CAA4C;AACrE,QAAI,aAAa,MAAM,KAAK,eAC3B,CAAC,QAAQ,IAAI,iBAAiB,2BAAa,kBAC3C,IAAI,EACH,MAAM,MAAM,KAAc;AAE5B,QAAI,YAAY;AAEf,WAAK,cAAc,MAAM,2BAA2B;AACpD,UAAI,KAAK,aAAa;AACrB,aAAK,YAAY,oBAAoB,IACpC,WAAW;MACb;AACA,aAAO;IACR;AAGA,QAAI,CAAC,KAAK,OAAQ,QAAQ;AACzB,WAAK,cAAc,MAAM,2BAA2B;AACpD,UAAI;AACH,cAAM,KAAK,eAAc;MAC1B,QAAQ;AACP,eAAO;MACR;IACD;AAGA,SAAK,cAAc,MAAM,wCAAwC;AACjE,iBAAa,MAAM,KAAK,eACvB,CAAC,QAAO;AACP,aAAO,IAAI,iBAAiB,2BAAa;IAC1C,GACA,KAAK,SAAS,SAAS,gBAAgB,EACtC,MAAM,MAAM,KAAc;AAE5B,QAAI,YAAY;AAEf,WAAK,cAAc,MAAM,oBAAoB;AAC7C,UAAI,KAAK,aAAa;AACrB,aAAK,YAAY,oBAAoB,IACpC,WAAW;MACb;AACA,aAAO;IACR;AAEA,SAAK,cAAc,MAClB,sFAAsF;AAKvF,UAAM,iBAAiB,mCAAW;AACjC,UAAI;AAEH,aAAK,iBAAgB;AACrB,cAAM,KAAK,YAAY,IAAI,6CAA2B,GAAI;UACzD,cAAc;UACd,UAAU,4BAAgB;SAC1B;AACD,aAAK,eAAc;AACnB,aAAK,cAAc,MAAM,sBAAsB;AAC/C,eAAO;MACR,QAAQ;AACP,eAAO;MACR;IACD,GAduB;AAgBvB,QAAI,MAAM,eAAc;AAAI,aAAO;AACnC,eAAW,WAAW,CAAC,GAAG,GAAG,IAAI,EAAE,GAAG;AACrC,WAAK,cAAc,MAClB,+CAA+C,OAAO,aAAa;AAEpE,gBAAM,mBAAK,UAAU,GAAI;AACzB,UAAI,MAAM,eAAc;AAAI,eAAO;IACpC;AAEA,SAAK,cAAc,MAClB,yCACA,OAAO;AAER,WAAO;EACR;EAEQ;EACA,MAAM,iBAAc;AAE3B,QAAI,KAAK;AAAwB,aAAO,KAAK;AAC7C,SAAK,6BAAyB,+CAAqB;AAMnD,SAAK,cAAc,MAAM,oCAAoC;AAI7D,cAAM,mBAAK,GAAG;AACd,aAAS,IAAI,KAAI,KAAK;AACrB,UAAI;AACH,cAAM,KAAK,IAAI,eAAc;AAC7B,aAAK,cAAc,MAAM,aAAa;AACtC,aAAK,wBAAwB,QAAQ,IAAI;AACzC,aAAK,yBAAyB;AAC9B,eAAO;MACR,QAAQ;AACP,YAAI,MAAM,GAAG;AACZ,eAAK,cAAc,MAClB,kCACA,OAAO;AAER,eAAK,wBAAwB,QAAQ,KAAK;AAC1C,eAAK,yBAAyB;AAC9B,iBAAO;QACR;AACA,kBAAM,mBAAK,GAAI;MAChB;IACD;EACD;;;;;;;EAQO,MAAM,YAAS;AACrB,SAAK,YAAY,IAAI;AAErB,QAAI,KAAK,WAAW,iCAAgC,GAAI;AACvD,YAAM,UACL;AACD,WAAK,cAAc,MAAM,SAAS,OAAO;AACzC,YAAM,IAAI,uBACT,SACA,4BAAgB,4BAA4B;IAE9C;AAGA,UAAM,gBAAgB,KAAK,SAC1B,8BAAU,WAAW,UAAU;AAIhC,UAAM,KAAK,UAAU,YACpB,MAAM,MACN,IAAI,uBACH,sCACA,4BAAgB,kBAAkB,CAClC;AAIF,UAAM,KAAK,WAAW,iBAAgB;AACtC,UAAM,KAAK,WAAW,UAAS;AAG/B,UAAM,KAAK,kBAAiB;AAC5B,SAAK,KAAK,6BAA4B;AAGtC,QAAI,eAAe;AAClB,WAAK,KAAK,gBAAgB,MAAK;AAC9B,aAAK,SAAS,8BAAU,WAAW,YAAY,aAAa;MAC7D,CAAC;IACF;EACD;;;;;EAMO,MAAM,WAAQ;AACpB,SAAK,YAAY,IAAI;AAGrB,QAAI,KAAK,WAAW,iCAAgC,GAAI;AACvD,YAAM,UACL;AACD,WAAK,cAAc,MAAM,SAAS,OAAO;AACzC,YAAM,IAAI,uBACT,SACA,4BAAgB,4BAA4B;IAE9C;AAEA,UAAM,SAAS,MAAM,KAAK,WAAW,SAAQ;AAC7C,QAAI;AACH,UAAI;AAAQ,cAAM,KAAK,QAAO;IAC/B;AACC,aAAO;IACR;EACD;EAEQ;EACR,IAAY,eAAY;AACvB,WAAO,CAAC,CAAC,KAAK;EACf;;;;;EAMQ,YAAY,sBAA+B,OAAK;AACvD,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,WAAW,KAAK,cAAc;AAC5D,YAAM,IAAI,uBACT,iDACA,4BAAgB,eAAe;IAEjC;AACA,QAAI,uBAAuB,CAAC,KAAK,wBAAwB;AACxD,YAAM,IAAI,uBACT,mCACA,4BAAgB,eAAe;IAEjC;AACA,QAAI,KAAK,aAAa;AACrB,YAAM,IAAI,uBACT,2CACA,4BAAgB,eAAe;IAEjC;EACD;;EAGA,IAAW,QAAK;AACf,WACC,KAAK,eACF,KAAK,WACL,CAAC,KAAK,gBACN,KAAK;EAEV;EAEQ,MAAM,mBAAmB,SAAe;AAC/C,SAAK,UAAU,MAAM,SAAS,OAAO;AAErC,UAAM,QAAQ,IAAI,uBACjB,SACA,4BAAgB,aAAa;AAE9B,SAAK,KAAK,SAAS,KAAK;AAExB,UAAM,KAAK,QAAO;EACnB;;;;;EAMO,MAAM,UAAO;AAEnB,QAAI,KAAK;AAAiB,aAAO,KAAK;AACtC,SAAK,sBAAkB,+CAAqB;AAE5C,SAAK,UAAU,MAAM,+BAA+B;AAGpD,UAAM,KAAK,WAAW,KAAI;AAE1B,UAAM,KAAK,yBACV,6BACA,4BAAgB,gBAAgB;AAGjC,SAAK,sBACJ,6BACA,4BAAgB,gBAAgB;AAGjC,QAAI,KAAK,UAAU,QAAW;AAE7B,UAAI,KAAK,OAAO;AAAQ,cAAM,KAAK,OAAO,MAAK;AAC/C,WAAK,SAAS;IACf;AAEA,UAAM,KAAK,kBAAiB;AAE5B,SAAK,UAAU,MAAM,2BAA2B;AAGhD,SAAK,cAAc,QAAO;AAE1B,SAAK,gBAAgB,QAAO;EAC7B;;;EAIQ,MAAM,oBAAiB;AAG9B,UAAM,KAAK,UAAU,YACpB,MAAM,MACN,IAAI,uBACH,8CACA,4BAAgB,kBAAkB,CAClC;AAGF,UAAM,KAAK,yBACV,8CACA,4BAAgB,kBAAkB;AAGnC,SAAK,sBACJ,8CACA,4BAAgB,kBAAkB;AAGnC,SAAK,gBAAgB,MAAK;AAG1B,UAAM,KAAK,eAAc;AAGzB,SAAK,iBAAgB;AAGrB,QAAI,KAAK,aAAa;AACrB,WAAK,YAAY,QAAO;AACxB,WAAK,cAAc;IACpB;AAEA,SAAK,yBAAyB;AAC9B,SAAK,YAAY,MAAK;AACtB,SAAK,0BAA0B;EAChC;EAEQ,MAAM,iBAAc;AAC3B,QAAI;AACH,YAAM,KAAK,UAAU,MAAK;IAC3B,SAAS,GAAG;AACX,WAAK,UAAU,MACd,oCAAgC,+BAAgB,CAAC,CAAC,IAClD,OAAO;IAET;AACA,QAAI;AACH,YAAM,KAAK,aAAa,MAAK;IAC9B,SAAS,GAAG;AACX,WAAK,UAAU,MACd,uCAAmC,+BAAgB,CAAC,CAAC,IACrD,OAAO;IAET;AACA,QAAI;AACH,YAAM,KAAK,eAAe,MAAK;IAChC,SAAS,GAAG;AACX,WAAK,UAAU,MACd,yCAAqC,+BAAgB,CAAC,CAAC,IACvD,OAAO;IAET;EACD;EAEQ,mBAAgB;AACvB,eACO,WAAW;MAChB,KAAK,4BAA4B;OAEjC;AACD,UAAI;AAAS,qBAAa,OAAO;IAClC;AACA,eACO,WAAW;MAChB,GAAG,KAAK,2BAA2B,OAAM;MACzC,GAAG,KAAK,2BAA2B,OAAM;MACzC,KAAK;MACL,KAAK;MACL,GAAG,KAAK,sBAAsB,OAAM;MACpC,GAAG,KAAK,gBAAgB,IAAI,CAAC,MAAM,EAAE,OAAO;MAC5C,GAAG,KAAK,gBAAgB,IAAI,CAAC,MAAM,EAAE,OAAO;MAC5C,GAAG,KAAK,sBAAsB,IAAI,CAAC,MAAM,EAAE,OAAO;MAClD,GAAG,KAAK,wBAAwB,IAAI,CAAC,MAAM,EAAE,OAAO;MACpD,GAAG,KAAK,iBAAiB,IAAI,CAAC,MAAM,EAAE,OAAO;OAE7C;AACD,eAAS,MAAK;IACf;EACD;EAEQ,MAAM,iBAAiB,QAAyB;AACvD,QAAI;AACH,uBAAiB,SAAS,OAAO,UAAU;AAC1C,qBAAa,MAAK;AACjB,cAAI,MAAM,SAAS,mCAAqB,WAAW;AAClD,iBAAK,KAAK,kBAAkB,MAAM,IAAI;UACvC,WAAW,MAAM,SAAS,mCAAqB,YAAY;AAC1D,iBAAK,KAAK,4BAA4B,MAAM,IAAI;UACjD,WAAW,MAAM,SAAS,mCAAqB,KAAK;AACnD,iBAAK,KAAK,qBAAqB,MAAM,IAAI;UAC1C,OAAO;UAEP;QACD,CAAC;MACF;IACD,SAAS,GAAG;AACX,cAAI,4BAAa,CAAC,GAAG;AACpB;MACD,eACC,0BAAa,CAAC,KAAK,EAAE,SAAS,4BAAgB,eAC7C;AAGD,YAAI,KAAK,mBAAmB,KAAK;AAAsB;AAEvD,aAAK,KAAK,mBAAmB,EAAE,OAAO;AACtC;MACD;AACA,YAAM;IACP;EACD;;;;EAKQ,MAAM,kBACb,MAIqB;AAErB,QAAI,OAAO,SAAS,UAAU;AAC7B,cAAQ,MAAM;QACb,KAAK,6BAAe;QACpB,KAAK,6BAAe;QACpB,KAAK,6BAAe,KAAK;AAExB,qBAAW,SAAS,KAAK,uBAAuB;AAC/C,gBAAI,MAAM,UAAU,IAAI,GAAG;AAC1B,oBAAM,QAAQ,IAAI;AAClB;YACD;UACD;AACA;QACD;MACD;IACD;AAEA,SAAK,OAAO;AACZ,SAAK,cAAc;AAEnB,QAAI;AACJ,QAAI;AAGH,YAAM,sBAAQ,MACb,MACA,KAAK,yBAAwB,CAAE;AAIhC,cAAI,mCAAiB,GAAG,SAAK,uCAAqB,GAAG,GAAG;AACvD,YAAI,UAAU,MAAM,uBAAa,MAChC,IAAI,cACJ;UACC,GAAG,KAAK,oBAAmB;UAC3B,cAAc,IAAI,UAAS;UAC3B,WAAW,IAAI;SACf;AAIF,cAAM,OAAO,KAAK,WAAW,GAAG;AAChC,YAAI;AAAM,eAAK,WAAW,oBAAI,KAAI;AAGlC,uBAAe,GAAiB;MACjC;AAEA,UAAI,CAAC,CAAC,KAAK,aAAa;AACvB,gBAAI,6BAAW,GAAG,GAAG;AACpB,eAAK,WAAW,GAAG,GAAG,oBAAoB,YAAY;QACvD,OAAO;AACN,eAAK,YAAY,oBAAoB,YAAY;QAClD;MACD;AAEA,YAAM,KAAK,YAAY,6BAAe,GAAG;IAC1C,SAAS,GAAQ;AAChB,UAAI;AACH,YAAI,MAAM,KAAK,4BAA4B,GAAG,GAAG,GAAG;QAEpD,OAAO;AACN,gBAAM,WAAW,KAAK,kBAAkB,GAAG,MAAM,GAAG;AACpD,cAAI;AAAU,kBAAM,KAAK,YAAY,QAAQ;AAC7C,cAAI,CAAC,CAAC,KAAK,aAAa;AACvB,oBAAI,6BAAW,GAAG,GAAG;AACpB,mBAAK,WAAW,GAAG,GAAG,oBACrB,mBAAmB;AAIpB,oBAAM,uBAAuB,wBAC3B,aAAa,IAAI,OAAO;AAC1B,kBACC,yBAAyB,UACtB,IAAI,mBAAmB,qBACzB;AAED,sBAAM,OAAO,KAAK,WAAW,GAAG;AAChC,oBAAI,MAAM;AACT,wBAAM,WAAW,KAAK,YACrB,IAAI,QAAQ,aAAa,KACrB;AACL,wBAAM,qBACL,IAAI,QAAQ;AACb,wBAAM,SACJ,UACA,2BAAe,aACf,KAAK,EAEL,WAAW;oBACX,WAAW;oBACX,mBAAmB;oBACnB,QAAQ,8BAAkB;oBAC1B,uBAAuB,KACrB,4BACA,IAAI;oBAEN;oBACA,aAAa,KACX,yCACA,MACA,kBAAkB;mBAEpB;gBACH;AACA;cACD;YACD,OAAO;AACN,mBAAK,YAAY,oBAChB,mBAAmB;YAErB;UACD;QACD;MACD,SAAS,IAAI;AACZ,YAAI,cAAc,OAAO;AACxB,cAAI,0BAA0B,KAAK,GAAG,OAAO,GAAG;AAC/C,iBAAK,KAAK,SAAS,EAAE;AACrB,iBAAK,KAAK,QAAO;AACjB;UACD;AAEA,eAAK,WAAW,MAAM,GAAG,SAAS,GAAG,SAAS,OAAO;QACtD;MACD;AAEA,YAAM;IACP;AAKA,QAAI,CAAC,KAAK,mBAAe,6BAAW,GAAG;AAAG;AAG1C,QAAI,KAAK;AACR,UAAI,mBAAmB;AACvB,cAAI,mCAAiB,GAAG,SAAK,6BAAW,GAAG,GAAG;AAI7C,YACC,IAAI,mBACQ,kDACX;AACD,gBAAM,OAAO,KAAK,WAAW,GAAG;AAChC,cAAI,MAAM;AACT,iBAAK,KAAK,uBAAuB,IAAI;UACtC;QACD;AAGA,gBAAI,2CAAgC,IAAI,OAAO,GAAG;AAEjD,eAAK,UAAU,WAAW,KAAK;YAC9B,eAAe,CAAC,SAAS;YACzB,WAAW;WACX;AACD,6BAAmB;AAEnB,eAAK,KAAK,8BAA8B,IAAI,OAAO,EAAE,MACpD,MAAK;UAEL,CAAC;QAEH;AAGA,YAAI,CAAE,MAAM,KAAK,mBAAmB,GAAG,GAAI;AAE1C,qBAAW,SAAS,KAAK,iBAAiB;AACzC,gBAAI,MAAM,mBAAmB,GAAG,GAAG;AAClC,oBAAM,SAAS,QAAO;YAGvB;UACD;AACA;QACD;AAGA,YACC,KAAK,sBAAsB,IAAI,OAAO,KACnC,KAAK,gBAAgB,IAAI,OAAO,GAClC;AACD,cAAI,CAAC,kBAAkB;AACtB,iBAAK,UAAU,WAAW,KAAK;cAC9B,WAAW;cACX,eAAe,CAAC,WAAW;aAC3B;UACF;AACA;QACD;AAGA,YAAI;AACH,eAAK,gBAAgB,IAAI,OAAO;QACjC,SAAS,GAAG;AAEX,kBACC,0BAAa,CAAC,KACX,EAAE,SACA,4BAAgB,6BACpB;AACD,iBAAK,UAAU,MACd,kCACC,OAAO,EAAE,YAAY,WAClB,aAAa,EAAE,OAAO,MACtB,EACJ,IACA,MAAM;AAGP;UACD,OAAO;AACN,kBAAM;UACP;QACD;AAGA,gBAAI,2CAAgC,IAAI,OAAO,GAAG;AACjD,cAAI,UAAU,IAAI,QAAQ;AAE1B,6BAAmB;QACpB;MACD;AAEA,UAAI,CAAC,kBAAkB;AACtB,YAAI;AACH,eAAK,UAAU,WAAW,KAAK;YAC9B,WAAW;WACX;QACF,SAAS,GAAG;AAEX,eAAK,UAAU,MACd,iCAA6B,+BAAgB,CAAC,CAAC,EAAE;QAEnD;MACD;AAWA,WAAK,KAAK,yBAAyB,GAAG;IAEvC;EACD;;EAGQ,kBACP,GACA,MACA,KAAwB;AAExB,YAAI,0BAAa,CAAC,GAAG;AACpB,cAAQ,EAAE,MAAM;QACf,KAAK,4BAAgB;QACrB,KAAK,4BAAgB;QACrB,KAAK,4BAAgB;AACpB,eAAK,UAAU,MACd,qDACA,MAAM;AAEP,iBAAO,6BAAe;QAEvB,KAAK,4BAAgB;QACrB,KAAK,4BAAgB;AACpB,eAAK,UAAU,MACd,0DAA0D,EAAE,OAAO,IACnE,MAAM;AAEP,iBAAO,6BAAe;QAEvB,KAAK,4BAAgB;AACpB,eAAK,UAAU,MACd,sEACA,MAAM;AAEP,iBAAO,6BAAe;QAEvB,KAAK,4BAAgB;AACpB,cAAI,KAAK;AACR,iBAAK,UAAU,MACd,yCACA,MAAM;AAEP,gBAAI;AACH,mBAAK,UAAU,WAAW,KAAK;gBAC9B,WAAW;eACX;YACF,SAASC,IAAG;AAEX,mBAAK,UAAU,MACd,iCACC,+BACCA,EAAC,CAEH,EAAE;YAEJ;UACD,OAAO;AACN,iBAAK,UAAU,MACd,wCACC,OAAO,EAAE,YAAY,WAClB,aAAa,EAAE,OAAO,MACtB,EACJ;MAAM,0BAAW,IAAI,CAAC,IACtB,MAAM;UAER;AACA,iBAAO,6BAAe;QAEvB,KAAK,4BAAgB;QACrB,KAAK,4BAAgB;AACpB,eAAK,UAAU,MACd,gHACA,MAAM;AAEP,iBAAO,6BAAe;QAEvB,KAAK,4BAAgB;AACpB,eAAK,UAAU,MACd,4BACC,OAAO,EAAE,YAAY,WAClB,QAAQ,EAAE,OAAO,KACjB,UACJ,oBACA,MAAM;AAEP,iBAAO,6BAAe;MACxB;IACD,OAAO;AACN,UAAI,uBAAuB,KAAK,EAAE,OAAO,GAAG;AAE3C,aAAK,UAAU,MACd,sEACA,MAAM;AAEP,eAAO,6BAAe;MACvB;IACD;AAEA,UAAM;EACP;EAEQ,2BACP,KAAgC;AAGhC,QAAI,IAAI,cAAc;AAAc,aAAO;AAC3C,UAAM,UAAU,IAAI,QAAQ,mBAC3B,2BAAe,YAAY,GAC3B,2BAAiB,oBAAoB;AAEtC,QAAI,CAAC;AAAS,aAAO;AAGrB,UAAM,OAAO,KAAK,WAAW,GAAG;AAChC,QAAI,CAAC;AAAM,aAAO;AAClB,UAAM,UAAU,QAAQ,oBAAmB;AAC3C,QAAI,WAAW;AAAW,aAAO;AACjC,UAAM,kBAAkB,KAAK,oBAAoB,KAAK,EAAE;AACxD;;MAEC,iBAAiB,YAChB,IAAI,QAAQ,QACZ,OAAO,EACN,SAAS,sBAAU;MACpB;AACD,aAAO;IACR;AACA,WAAO;EACR;EAEQ,MAAM,4BACb,GACA,KAAwB;AAExB,QAAI,KAAC,0BAAa,CAAC;AAAG,aAAO;AAC7B,SACE,EAAE,SAAS,4BAAgB,sBACxB,EAAE,SAAS,4BAAgB,iCAC5B,6BAAW,GAAG,GAChB;AAED,YAAM,SAAS,IAAI,UAAS;AAE5B,YAAM,OAAO,KAAK,aAAa,MAAM,IAAI,MAAM;AAC/C,UAAI,CAAC;AAAM,eAAO;AAGlB,YAAM,KAAK,YAAY,6BAAe,GAAG;AAEzC,WAAK,UAAU,WAAW,KAAK,EAAE,WAAW,UAAS,CAAE;AACvD,WAAK,oBAAoB,mBAAmB;AAI5C,UAAI,KAAK,iBAAiB,4BAAe,UAAU;AAClD,aAAK,MAAM,2BAAe,YAAY,GAAG;UACxC,aAAa;UACb,SAAS;SACT;MACF;AAGA,YAAM,kBAAkB,wBAAC,MACxB,EAAE,QAAQ,UAAS,MAAO,cACvB,6BAAW,EAAE,OAAO,KACpB,EAAE,QAAQ,mBAAmB,kCAHT;AAKxB,YAAM,UACL,EAAE,SAAS,4BAAgB,2BACxB,kCACA;AAEJ,UAAI,KAAK,WAAW,0BAA0B,QAAQ;AAErD,cAAM,kBAAkB,KAAK,oBAAoB,MAAM;AACvD,YAAI,iBAAiB,SAAS,IAAI,MAAM,GAAG;AAG1C,cACC,gBAAgB,aAAa,MAAM,EAAE,SAChC,sBAAU,MACd;AACD,iBAAK,cAAc,QAAQ,QAAQ;cAClC,SACC,GAAG,OAAO;cACX,OAAO;cACP,WAAW;aACX;AAED,iBAAK,eAAe,YAAY,EAC9B,UAAS,EACT,MAAM,MAAK;YAEZ,CAAC;UACH,OAAO;AAGN,iBAAK,cAAc,QAAQ,QAAQ;cAClC,SACC,GAAG,OAAO;cACX,OAAO;cACP,WAAW;aACX;AACD,iBAAK,WAAW,wBACf,sBAAY,qBAAqB;UAEnC;QACD,OAAO;AACN,eAAK,cAAc,QAAQ,QAAQ;YAClC,SACC;YACD,OAAO;YACP,WAAW;WACX;QACF;MACD,WAAW,CAAC,KAAK,uBAAuB,eAAe,GAAG;AACzD,aAAK,cAAc,QAAQ,QAAQ;UAClC,SACC,GAAG,OAAO;UACX,OAAO;UACP,WAAW;SACX;AAED,cAAM,2BAAuB,mCAAiB,GAAG,KAC7C,KAAK,2BAA2B,GAAG;AAEvC,aAAK,eAAe,YAAY,EAC9B,YAAY,EAAE,qBAAoB,CAAE,EACpC,UAAS,EACT,MAAM,MAAK;QAEZ,CAAC;MACH,OAAO;AACN,aAAK,cAAc,QAAQ,QAAQ;UAClC,SAAS,GAAG,OAAO;UACnB,OAAO;UACP,WAAW;SACX;MACF;AAEA,aAAO;IACR,YACE,EAAE,SAAS,4BAAgB,sBACxB,EAAE,SAAS,4BAAgB,0CAC5B,6BAAW,GAAG,GAChB;AAGD,YAAM,SAAS,IAAI,UAAS;AAE5B,YAAM,OAAO,KAAK,aAAa,MAAM,IAAI,MAAM;AAC/C,UAAI,CAAC;AAAM,eAAO;AAGlB,YAAM,KAAK,YAAY,6BAAe,GAAG;AAEzC,WAAK,UAAU,WAAW,KAAK,EAAE,WAAW,UAAS,CAAE;AACvD,WAAK,oBAAoB,mBAAmB;AAE5C,WAAK,cAAc,QAAQ,QAAQ;QAClC,SACC;QACD,OAAO;OACP;AAED,aAAO;IACR;AACA,WAAO;EACR;;EAGQ,iBACP,aACA,GAAa;AAIb;;MAEC,EAAE,SAAS,4BAAgB,2BACvB,YAAY,mBAAmB,oCAC/B,YAAY,mBAAmB;;EAErC;;;;;;EAOO,qBACN,aAGA,OAAiB;AAEjB,UAAM,OAAO,KAAK,WAAW,YAAY,OAAO;AAChD,QAAI,CAAC;AAAM,aAAO;AAElB,UAAM,mBAAe,6BAAW,YAAY,OAAO,IAChD,kCAAkC,YAAY,QAAQ,eAAe,cACrE;AAEH,QAAI,CAAC,YAAY,2BAA2B;AAE3C,aAAO;IACR,WAAW,KAAK,UAAU;AACzB,UAAI,KAAK,WAAW,wBAAW,QAAQ;AAGtC,eAAO;MACR;AACA,WAAK,cAAc,QAClB,KAAK,IACL,GAAG,YAAY,qEACf,MAAM;AAKP,YAAM,UAAU,KAAK,qBAAqB,WAAW;AACrD,UAAI,SAAS;AACZ,aAAK,MAAM,IAAI,WAAW;MAC3B;AAGA,WAAK,aAAY;AAEjB,aAAO;IACR,OAAO;AACN,YAAM,WAAW,GAAG,YAAY;AAChC,WAAK,cAAc,QAAQ,KAAK,IAAI,UAAU,MAAM;AAEpD,WAAK,WAAU;AAGf,kBAAY,YAAY;QACvB,OAAO,6BAAiB;QACxB,QAAQ;OACR;AAED,kBAAY,MAAM,KAAK;AACvB,WAAK,KAAK,6BAA6B,KAAK,IAAI,QAAQ;AAExD,aAAO;IACR;EACD;;;;;;EAOQ,2BACP,aACA,OAGC;AAED,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,iCAAgC,GAAI;AAClE,aAAO;IACR;AAEA,UAAM,+BAA+B,mCAAW;AAC/C,UAAI,CAAC,KAAK;AAAQ;AAClB,WAAK,UAAU,MACd,iFACA,MAAM;AAEP,UAAI,KAAK,OAAO;AAAQ,cAAM,KAAK,OAAO,MAAK;AAC/C,gBAAM,mBAAK,GAAI;AACf,YAAM,KAAK,eAAc;AAEzB,WAAK,UAAU,MACd,kFACA,MAAM;AAKP,WAAK,aAAa,UAAU,6BAAiB,KAAK;AAClD,WAAK,iBAAc;IACpB,GAnBqC;AAqBrC,QACE,KAAK,YAAY,WAAW,6BAAiB,gBAC1C,CAAC,KAAK,aAAY,KACnB,KAAK,sBAEP;AAED,WAAK,6BAA4B,EAAG,MAAM,kBAAI;AAE9C,aAAO;IACR,WAAW,KAAK,YAAY,WAAW,6BAAiB,cAAc;AAGrE,WAAK,WAAW,UACf,6BAAiB,YAAY;AAG9B,WAAK,iBAAc;AAEnB,WAAK,UAAU,MACd,qEACA,MAAM;AAIP,WAAK,KAAK,UAAS,EAAG,KAAK,MAAK;AAK/B,oBAAY,MAAK;AACjB,aAAK,uBAAuB,WAAW,EAAE,IACxC,YAAY,MAAK,CAAE;AAGpB,aAAK,aAAa,UAAU,6BAAiB,KAAK;AAClD,aAAK,iBAAc;MACpB,CAAC,EAAE,MAAM,MAAK;AAEb,aAAK,kBAAkB,aAAa,KAAK;AAGzC,eAAO,6BAA4B;MACpC,CAAC;AAED,aAAO;IACR,OAAO;AAEN,aAAO;IACR;EACD;;;;;;EAOQ,wCACP,aACA,OAGC;AAED,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,iCAAgC,GAAI;AAClE,aAAO;IACR;AAEA;;;MAGC,MAAM,YAAY,cAEf,KAAK;MAEP;AACD,YAAM,OAAO,KAAK,WAAW,YAAY,OAAO;AAChD,UAAI,CAAC;AAAM,eAAO;AAMlB,YAAM,eACL;AAED,UAAI;AAEJ,UAAI,KAAK,UAAU;AAClB,YAAI,KAAK,WAAW,wBAAW,QAAQ;AAGtC,iBAAO;QACR;AACA,aAAK,cAAc,QAClB,KAAK,IACL,GAAG,YAAY,qEACf,MAAM;AAKP,kBAAU,KAAK,qBAAqB,WAAW;AAC/C,YAAI,SAAS;AACZ,eAAK,MAAM,IAAI,WAAW;QAC3B;AAGA,aAAK,aAAY;MAClB,OAAO;AACN,cAAM,WAAW,GAAG,YAAY;AAChC,aAAK,cAAc,QAAQ,KAAK,IAAI,UAAU,MAAM;AAEpD,aAAK,WAAU;AAGf,oBAAY,YAAY;UACvB,OAAO,6BAAiB;UACxB,QAAQ;SACR;AAED,oBAAY,MAAM,KAAK;AACvB,aAAK,KAAK,6BAA6B,KAAK,IAAI,QAAQ;AAExD,kBAAU;MACX;AAGA,UACC,KAAK,sBAEJ;AACD,aAAK,UAAU,MACd,6CACA,MAAM;AAEP,aAAK,KAAK,kBAAkB,IAAI,EAAE,MAAM,MAAK;AAC5C,eAAK,UAAU,MACd,gGACA,MAAM;QAER,CAAC,EAAE,QAAQ,MAAK;AACf,eAAK,iBAAc;AACnB,eAAK,aAAa,UAAU,6BAAiB,KAAK;QACnD,CAAC;MACF;AAEA,aAAO;IACR,WAAW,KAAK,YAAY,WAAW,6BAAiB,cAAc;AAGrE,UAAI,KAAK,aAAY,GAAI;AAExB,aAAK,WAAW,UACf,6BAAiB,YAAY;AAG9B,aAAK,iBAAc;AAEnB,aAAK,UAAU,MACd,kEACA,MAAM;AAIP,aAAK,KAAK,kBAAkB,IAAI,EAAE,KAAK,MAAK;AAK3C,sBAAY,MAAK;AACjB,eAAK,uBAAuB,WAAW,EAAE,IACxC,YAAY,MAAK,CAAE;AAGpB,eAAK,aAAa,UAAU,6BAAiB,KAAK;AAClD,eAAK;QAEN,CAAC,EAAE,MAAM,MAAK;AAEb,eAAK,kBAAkB,aAAa,KAAK;AAEzC,eAAK,UAAU,MACd,gGACA,MAAM;AAEP,eAAK,iBAAc;AACnB,eAAK,aAAa,UAAU,6BAAiB,KAAK;QACnD,CAAC;MACF,OAAO;AACN,aAAK,UAAU,MACd,0LACA,MAAM;AAEP,aAAK,kBAAkB,aAAa,KAAK;MAC1C;AAEA,aAAO;IACR,OAAO;AAEN,aAAO;IACR;EACD;;;;;EAMQ,uBACP,aACA,OAAiB;AAEjB,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,iCAAgC,GAAI;AAClE,aAAO;IACR;AAEA;;MAEC,KAAK;MAEJ;AAED,WAAK,UAAU,MACd,gGACA,MAAM;AAEP,WAAK,iBAAc;AACnB,WAAK,YAAY,UAAU,6BAAiB,KAAK;AAEjD,aAAO;IACR,WAAW,KAAK,YAAY,WAAW,6BAAiB,QAAQ;AAE/D,UAAI,KAAK,WAAW,aAAa,KAAK,GAAG;AAExC,aAAK,UAAU,MACd,4GACA,MAAM;AAEP,aAAK,aAAa,UAAU,6BAAiB,KAAK;AAClD,aAAK,kBAAkB,aAAa,KAAK;MAC1C,WAAW,KAAK,aAAY,GAAI;AAC/B,aAAK,iBAAc;AAEnB,aAAK,UAAU,MACd,8CACA,MAAM;AAIP,aAAK,KAAK,UAAS,EAAG,KAAK,MAAK;AAK/B,sBAAY,MAAK;AACjB,eAAK,uBAAuB,WAAW,EAAE,IACxC,YAAY,MAAK,CAAE;AAGpB,eAAK;QAEN,CAAC,EAAE,MAAM,MAAK;AAEb,eAAK,kBAAkB,aAAa,KAAK;AAEzC,eAAK,UAAU,MACd,gGACA,MAAM;AAEP,eAAK,iBAAc;AACnB,eAAK,aAAa,UAAU,6BAAiB,KAAK;QACnD,CAAC;MACF,OAAO;AACN,aAAK,UAAU,MACd,sKACA,MAAM;AAEP,aAAK,aAAa,UAAU,6BAAiB,KAAK;AAClD,aAAK,kBAAkB,aAAa,KAAK;MAC1C;AAEA,aAAO;IACR,OAAO;AAEN,aAAO;IACR;EACD;EAEQ,4BAA4B,MAAe;AAClD,WACC,CAAC,CAAC,KAAK,0BACJ,KAAK,WAAW,wBAAW,UAC3B,KAAK,uBACP,CAAC,MACA,EAAE,yBACC,EAAE,QAAQ,UAAS,MAAO,KAAK,EAAE;EAGxC;EAEQ,oBAAoB,oBAAI,IAAG;EAS3B,oBACP,SACA,iBAAwB;AAExB,UAAM,YAAY,QAAQ,sBAAqB;AAE/C,QAAI,WAAW;AAEd,YAAM,oBAAoB,KAAK,UAAU;QACxC,QAAQ,QAAQ;QAChB,MAAM,QAAQ;QACd,WAAW,QAAQ;QACnB,GAAG;OACH;AACD,UACC,mBACG,CAAC,KAAK,kBAAkB,IAAI,iBAAiB,GAC/C;AACD,aAAK,kBAAkB,IAAI,mBAAmB,CAAA,CAAE;MACjD;AACA,aAAO;QACN;QACA,SAAS,KAAK,kBAAkB,IAAI,iBAAiB;;IAEvD;EACD;;;;;EAKQ,MAAM,mBACb,KAAgC;AAEhC,QAAI,UAAoC,IAAI;AAI5C,WAAO,MAAM;AACZ,YAAM,EAAE,mBAAmB,QAAO,IACjC,KAAK,oBAAoB,SAAS,IAAI,KAAK,CAAA;AAC5C,UAAI,SAAS;AAEZ,YAAI,QAAQ,mBAAmB,OAAO,GAAG;AAExC,kBAAQ,KAAK,OAAO;AACpB,cAAI,KAAC,2CAAgC,IAAI,OAAO,GAAG;AAElD,iBAAK,UAAU,WAAW,KAAK;cAC9B,eAAe,CAAC,SAAS;cACzB,WAAW;aACX;UACF;AACA,iBAAO;QACR,OAAO;AAEN,eAAK,kBAAkB,OAAO,iBAAkB;AAChD,cAAI;AACH,kBAAM,QAAQ,gBAAgB,SAAS;cACtC,GAAG,KAAK,oBAAmB;cAC3B,cAAc,IAAI,QAAQ;cAC1B,WAAW,IAAI;aACf;AAED,2BAAe,GAAG;UACnB,SAAS,GAAG;AACX,oBAAI,0BAAa,CAAC,GAAG;AACpB,sBAAQ,EAAE,MAAM;gBACf,KAAK,4BACH;gBACF,KAAK,4BAAgB;AACpB,uBAAK,UAAU,MACd,0DAA0D,EAAE,OAAO,IACnE,MAAM;AAGP,yBAAO;gBAER,KAAK,4BACH;AACD,uBAAK,UAAU,MACd,iFACA,MAAM;AAGP,yBAAO;gBAER,KAAK,4BAAgB;AACpB,uBAAK,UAAU,MACd,qFACA,MAAM;AAGP,yBAAO;cACT;YACD;AACA,kBAAM;UACP;QAED;MACD,OAAO;MAEP;AAGA,cAAI,uCAA4B,OAAO,GAAG;AACzC,kBAAU,QAAQ;MACnB,OAAO;AACN;MACD;IACD;AACA,WAAO;EACR;;EAGQ,MAAM,8BACb,SAEsC;AAEtC,UAAM,eAAe,KAAK,mBAAmB,QAAQ,MAAM;AAG3D,UAAM,wBACL,mCAAyB;AAE1B,UAAM,iCAAiC,8BACtC,SACA,UACkB;AAClB,YAAM,UAAU,QAAQ;AAGxB,YAAM,aAAa,QAAQ,KAAK,KAAK;AACrC,UAAI,YAAY;AACf,gBAAQ,WAAW,WAAW,QAAQ;AAEtC,YAAI,QAAQ,MAAM,UAAU,WAAW;AAEtC,qCAA2B,OAAO;QACnC,WAAW,QAAQ,MAAM,UAAU,kBAAkB;AAEpD,eAAK,cAAc,QAAQ,QAAQ,QAAQ;YAC1C,SACC,iCAAiC,QAAQ,SAAS,yBAAyB,QAAQ,MAAM,MAAM;YAChG,OAAO;YACP,WAAW;WACX;AACD,gBAAM,KAAK,IAAI,2CAAiC;YAC/C,QAAQ,QAAQ;YAChB,WAAW,QAAQ;YACnB,gBAAgB,QAAQ,MAAM;WAC9B;AACD,gBAAM,KAAK,YAAY,IAAI;YAC1B,iBAAiB;YACjB,UAAU,4BAAgB;WAC1B;AAED,qCAA2B,OAAO;QACnC,WAAW,QAAQ,MAAM,UAAU,WAAW;AAC7C,eAAK,cAAc,QAAQ,QAAQ,QAAQ;YAC1C,SACC,iCAAiC,QAAQ,SAAS;YACnD,OAAO;YACP,WAAW;WACX;AAED,uBAAa,iBAAiB,OAAO,QAAQ,SAAS;AACtD,cAAI,QAAQ,SAAS;AACpB,yBAAa,QAAQ,OAAO;UAC7B;QACD;MACD;AAEA,UAAI,QAAQ,MAAM,UAAU,WAAW;AAGtC,aAAK,cAAc,QAAQ,QAAQ,QAAQ;UAC1C,SACC,iCAAiC,QAAQ,SAAS;UACnD,OAAO;UACP,WAAW;SACX;AACD,YAAI,QAAQ,SAAS;AACpB,uBAAa,QAAQ,OAAO;QAC7B;AAEA,cAAM,KAAK,IAAI,4CAAkC;UAChD,QAAQ,QAAQ;UAChB,WAAW,QAAQ;SACnB;AACD,cAAM,KAAK,YAAY,IAAI;UAC1B,iBAAiB;UACjB,UAAU,4BAAgB;SAC1B,EAAE,MAAM,kBAAI;MACd;IACD,GAtEuC;AAwEvC,aAAS,2BACR,SAAgC;AAEhC,UAAI,QAAQ,SAAS;AACpB,qBAAa,QAAQ,OAAO;MAC7B;AAEA,cAAQ,UAAU,WAAW,MAAK;AACjC,gBAAQ,UAAU;AAClB,aAAK,+BAA+B,SAAS;UAC5C,OAAO;SACP;MACF,GAAG,qBAAqB;IACzB;AAbS;AAeT,QAAI,mBAAmB,0CAAgC;AAKtD,mBAAa,iBAAiB,MAAK;AAEnC,WAAK,cAAc,QAAQ,QAAQ,QAAQ;QAC1C,SACC,2CAA2C,QAAQ,SAAS;QAC7D,OAAO;QACP,WAAW;OACX;AAED,YAAM,cAAU,gEACf,QAAQ,cACR,QAAQ,gBAAgB,MAAM;AAG/B,YAAM,UAAmC;QACxC,cAAc,QAAQ,gBAAgB;QACtC;;AAED,mBAAa,iBAAiB,IAAI,QAAQ,WAAW,OAAO;AAG5D,iCAA2B,OAAO;IACnC,OAAO;AAEN,YAAM,mBAAmB,aAAa,iBAAiB,IACtD,QAAQ,SAAS;AAElB,UAAI,kBAAkB;AACrB,cAAM,+BAA+B,kBAAkB;UACtD,OAAO;UACP,QAAQ,QAAQ;UAChB,QAAQ,QAAQ,gBAAgB;SAChC;MACF,OAAO;AAEN,cAAM,KAAK,IAAI,wCAA8B;UAC5C,QAAQ,QAAQ;UAChB,iBAAiB;SACjB;AACD,cAAM,KAAK,YAAY,IAAI;UAC1B,iBAAiB;UACjB,UAAU,4BAAgB;SAC1B;MACF;IACD;EACD;;;;;EAMQ,MAAM,yBAAyB,KAAY;AAGlD,QAAI;AACH,UAAI,IAAI,SAAS,0BAAY,SAAS;AACrC,cAAM,KAAK,cAAc,GAAG;MAC7B,OAAO;AACN,cAAM,KAAK,eAAe,GAAG;MAC9B;IACD,SAAS,GAAG;AACX,cACC,0BAAa,CAAC,KACX,EAAE,SAAS,4BAAgB,iBAC7B;AACD,aAAK,UAAU,MACd,2EACA,MAAM;MAER,OAAO;AACN,cAAM;MACP;IACD;EACD;;;;EAKQ,MAAM,mCACb,KAA4B;AAK5B,YAAQ,IAAI,cAAc;;;MAGzB,KAAK,uCAAsB;MAC3B,KAAK,uCAAsB;MAC3B,KAAK,uCAAsB;MAC3B,KAAK,uCAAsB;MAC3B,KAAK,uCAAsB,iBAAiB;AAE3C,aAAK,cAAc,MAClB,sCACA,MAAM;AAKP,YAAI,KAAK,uBAAuB;AAC/B,eAAK,cAAc,MAClB,+CACA,MAAM;AAEP,eAAK,sBAAsB,OAC1B,IAAI,uBACH,yCACA,4BAAgB,gBAAgB,CAChC;QAEH;AAGA,YAAI,KAAK,QAAQ,SAAS,UAAU;AACnC,gBAAM,KAAK,aAAa,cAAa;QACtC;AAEA,YAAI,KAAK,aAAa,eAAe,uBAAW,MAAM;AAGrD,eAAK,YAAY,aAAa,uBAAW;AACzC,gBAAM,KAAK,YAAY,iBAAiB,uBAAW,IAAI;QACxD;AAEA,eAAO;MACR;IACD;AAEA,WAAO;EACR;;;;;;;;;;EAWO,uBACN,QACA,SACA,UAAmB,OAAK;AAExB,UAAM,WAAqC,KAAK,gBAAgB,IAC9D,MAAM,IAEL,KAAK,gBAAgB,IAAI,MAAM,IAC/B,CAAA;AACH,UAAM,QAAgC,EAAE,QAAQ,SAAS,QAAO;AAChE,aAAS,KAAK,KAAK;AACnB,SAAK,UAAU,MACd,QAAQ,UAAU,cAAc,EAAE,wBACjC,2BAAa,MAAM,CACpB,SAAK,uBAAQ,MAAM,CAAC;EACrB,SAAS,MAAM,aAAa;AAE5B,SAAK,gBAAgB,IAAI,QAAQ,QAAiC;EACnE;;;;;;EAOO,yBACN,QACA,SAAuB;AAEvB,UAAM,WAAW,KAAK,gBAAgB,IAAI,MAAM,IAC7C,KAAK,gBAAgB,IAAI,MAAM,IAC/B,CAAA;AACH,aAAS,IAAI,GAAG,QAAQ,SAAS,CAAC,GAAG,IAAI,SAAS,QAAQ,KAAK;AAE9D,UAAI,MAAM,WAAW,SAAS;AAC7B,iBAAS,OAAO,GAAG,CAAC;AACpB;MACD;IACD;AACA,SAAK,UAAU,MACd,+BAA+B,2BAAa,MAAM,CAAC,KAAK,MAAM;EAC/D,SAAS,MAAM,OAAO;AAEtB,SAAK,gBAAgB,IAAI,QAAQ,QAAQ;EAC1C;;;;EAKQ,sBAAsB,IAAgB;AAQ7C,UAAM,OAAO,KAAK,aAAa,MAAM,IAAI,GAAG,MAAgB;AAC5D,QAAI,CAAC,MAAM;AAEV,WAAK,cAAc,QAClB,GAAG,QACH,+CACA,MAAM;AAEP,aAAO;IACR;AAGA,QAAI,cAAc;AAAoB,aAAO;AAE7C,QAAI,cAAc,uCAA6B;AAC9C,aAAO,KAAK,sBAAsB,GAAG,YAAY;IAClD;AAEA,UAAM,WAAW,KAAK,wBAAuB;AAC7C,QACC,aAAa,0BAAc,QACxB,aAAa,0BAAc,WAC7B;AACD,aAAO;IACR;AAEA,UAAM,yBAAqB,+BAAkB,QAAQ,IAClD,2BAAe,YAAY,IAC3B,aAAa,0BAAc,YAC3B,2BAAe,WACf;AAEH,UAAM,uBAAuB,wBAC5B,QAC2B;AAE3B,UAAI,eAAe,sBAAY;AAC9B,gBAAQ,IAAI,WAAW;;UAEtB,KAAK,0BAAgB;UACrB,KAAK,0BAAgB;UACrB,KAAK,0BAAgB;UACrB,KAAK,0BAAgB;AACpB,mBAAO;QACT;AAEA,YAAI,eAAe,0CAAgC;AAGlD,cACC,IAAI,wBACQ,+CACT,IAAI,wBACK,0CACX;AACD,mBAAO;UACR;AAGA,iBAAO,aAAa,0BAAc;QACnC;MACD,WAAW,eAAe,uBAAa;AACtC,YAAI,eAAe,2CAAiC;AAEnD,cACC,IAAI,wBACQ,8CACX;AACD,mBAAO;UACR;AAGA,cACC,IAAI,wBACQ,2CACX;AACD,mBAAO;UACR;AAGA,cAAI,IAAI,oBAAmB,KAAM;AAAW,mBAAO;AAGnD,cAAI,IAAI,iBAAiB;AAAW,mBAAO;AAG3C,iBAAO,IAAI,kBAAkB;QAC9B;MACD;AAEA,aAAO,IAAI,SAAS;IACrB,GA3D6B;AA6D7B,QAAI,uBAAmB,+BAAkB,QAAQ;AACjD,UAAM,WAAW,qBAAqB,EAAE;AAExC,WAAO,MAAM;AACZ,cAAI,uCAA4B,EAAE,GAAG;AACpC,aAAK,GAAG;MACT,eAAW,4CAAiC,EAAE,GAAG;AAChD,6BAAqB,GAAG,aAAa,KAAK,CAAC,QAC1C,KAAK,WAAW,IAAI,IAAI,CAAC;AAE1B;MACD,OAAO;AACN,6BAAqB,KAAK,WAAW,GAAG,IAAI,KACxC,GAAG,SAAS,2BAAe,YAC3B,GAAG,SAAS,2BAAe,YAAY;AAE3C;MACD;IACD;AACA,QAAI,oBAAoB,CAAC,UAAU;AAElC,WAAK,cAAc,QAClB,GAAG,QACH,mFACA,MAAM;AAEP,aAAO;IACR;AAEA,WAAO;EACR;;EAGQ,gBAAgB,IAAgB;AACvC,YAAI,uCAA4B,EAAE,GAAG;AACpC,aAAO,KAAK,gBAAgB,GAAG,YAAY;IAC5C;AAEA,UAAM,OAAO,KAAK,aAAa,MAAM,IAAI,GAAG,MAAgB;AAE5D,QAAI,CAAC,MAAM;AAEV,WAAK,cAAc,QAClB,GAAG,QACH,+CACA,MAAM;AAEP,aAAO;IACR;AAEA,QACC,GAAG,YAAY,KAAK,SAAS,KAAK,MAC9B,GAAG,cAAc,eAAe,GAAG,cAAc,cACpD;AACD,WAAK,cAAc,QAClB,GAAG,QACH,iCAAiC,GAAG,SAAS,oBAC7C,MAAM;AAEP,aAAO;IACR;AAKA,QACC,GAAG,SAAS,2BAAe,SACxB,GAAG,SAAS,2BAAe,mBAAmB,GAChD;AACD,YAAM,WAAW,KAAK,YAAY,GAAG,aAAa,KAAK;AACvD,UACC,CAAC,SAAS,WAAW,GAAG,IAAI,KAAK,CAAC,SAAS,WAAW,GAAG,IAAI,GAC5D;AACD,aAAK,cAAc,QAClB,GAAG,QACH,GACC,GAAG,gBAAgB,IAChB,YAAY,GAAG,aAAa,MAC5B,EACJ,2BACC,uBACC,GAAG,IAAI,CAET,qCACA,MAAM;AAEP,eAAO;MACR;IACD;AAEA,WAAO;EACR;;;;EAKQ,eAAe,KAAY;AAElC,eAAW,SAAS,KAAK,iBAAiB;AACzC,UAAI,MAAM,UAAU,GAAG,GAAG;AAEzB,cAAM,QAAQ,GAAG;AACjB,eAAO,QAAQ,QAAO;MACvB;IACD;AAEA,SAAK,UAAU,oBAAoB,KAAK,QAAW,YAAY;AAC/D,SAAK,UAAU,MAAM,sCAAsC,MAAM;AAEjE,WAAO,QAAQ,QAAO;EACvB;;;;EAKQ,MAAM,cAAc,KAAY;AACvC,QAAI;AAEJ,YAAI,yBAAU,GAAG,SAAK,6BAAW,GAAG,GAAG;AACtC,YAAM,OAAO,KAAK,WAAW,GAAG;AAChC,UAAI,MAAM;AAET,YAAI,KAAK,WAAW,wBAAW,MAAM;AACpC,eAAK,YAAW;QACjB;MACD;IACD;AAGA,eAAW,SAAS,KAAK,iBAAiB;AACzC,UAAI,MAAM,UAAU,GAAG,GAAG;AAEzB,cAAM,QAAQ,GAAG;AACjB;MACD;IACD;AAEA,YAAI,mCAAiB,GAAG,SAAK,6BAAW,GAAG,GAAG;AAC7C,YAAM,SAAS,IAAI,UAAS;AAG5B,YAAM,iBAAiB,KAAK,MAAM,oBAC/B,kBAAiB;AACpB,UACC,kBACG,eAAe,kBAAiB,KAChC,eAAe,qBAAqB,GAAG,GACzC;AAGD,aAAK,cAAc,QAAQ,IAAI,UAAS,GAAK;UAC5C,SACC;UACD,OAAO;UACP,WAAW;SACX;AACD,uBAAe,sBAAsB;AACrC;MACD;AAGA,WAAK,eAAe,GAAG;AAGvB,UAAI,KAAK,eAAe,QAAW;AAClC,aAAK,UAAU,MACd,oDACA,MAAM;AAEP;MACD,WAAW,CAAC,KAAK,WAAW,MAAM,IAAI,MAAM,GAAG;AAC9C,aAAK,UAAU,MACd,+DACA,MAAM;AAEP;MACD;AAEA,YAAM,OAAO,KAAK,WAAW,MAAM,IAAI,MAAM;AAC7C,YAAM,eAAe,KAAK,aAAa,IAAI,MAAM;AAIjD,UAAI,IAAI,mBAAmB,8BAAoB;AAC9C,eAAO,KAAK,uBAAuB,IAAI;MACxC;AACA,UAAI,IAAI,mBAAmB,iCAAuB;AACjD,eAAO,KAAK,0BAA0B,MAAM,IAAI,OAAO;MACxD;AACA,UAAI,IAAI,mBAAmB,0CAAgC;AAC1D,eAAO,KAAK,mCACX,MACA,IAAI,OAAO;MAEb;AAEA,UAAI,IAAI,mBAAmB,+BAAqB;AAC/C,eAAO,KAAK,wBAAwB,IAAI;MACzC;AACA,UAAI,IAAI,mBAAmB,kCAAwB;AAClD,eAAO,KAAK,2BAA2B,MAAM,IAAI,OAAO;MACzD;AACA,UAAI,IAAI,mBAAmB,2CAAiC;AAC3D,eAAO,KAAK,oCACX,MACA,IAAI,OAAO;MAEb;AAEA,UACC,IAAI,QAAQ,SAAS,2BAAe,eACjC,IAAI,mBAAmB,iCACvB,cAAc,YAAY,IAAI,IAAI,QAAQ,SAAS,GACrD;AAED,aAAK,cAAc,QAAQ,IAAI,QAAQ,QAAQ;UAC9C,SAAS;UACT,WAAW;SACX;AAGD,qBAAa,YAAY,IAAI,IAAI,QAAQ,SAAS,EAAG;UACpD,QAAQ,IAAI,QAAQ;UACpB,mBAAmB,IAAI,QAAQ;SACV;AAEtB,YAAI,CAAC,IAAI,QAAQ,mBAAmB;AACnC,uBAAa,YAAY,OAAO,IAAI,QAAQ,SAAS;QACtD;AAEA;MACD;AAGA,YAAM,uBAAuB,wBAAc,aAC1C,IAAI,OAAO;AAGZ,YAAM,uBAAuB,KAAK,2BACjC,GAAG;AAGJ,YAAM,qBAAqB,IAAI,QAAQ;AAEvC,UAAI;AAMJ,UAAI,wBAAwB,QAAW;AAEtC,cAAM,WAAW,KAAK,YAAY,IAAI,QAAQ,aAAa,KACvD;AACJ,gBAAQ,wBAAC,WACR,SACE,UAAU,2BAAe,aAAa,KAAK,EAC3C,YAAY,EAAE,qBAAoB,CAAE,EACpC,WAAW;UACX,WAAW;UACX,mBAAmB;UACnB;UACA,uBAAuB,KACrB,4BAA4B,IAAI;UAClC;UACA,aAAa,KACX,yCACA,MACA,kBAAkB;SAEpB,GAhBK;MAiBT,OAAO;AAEN,gBAAQ,6BAAM,QAAQ,QAAO,GAArB;MACT;AAEA,YAAM,gBAAgB,8BACrB,WACkB;AAClB,YAAI;AACH,gBAAM,OAAM;AACZ,gBAAM,MAAM,8BAAkB,OAAO;QACtC,SAAS,GAAG;AACX,cAAI,UAAU;AACd,kBAAI,0BAAa,CAAC,GAAG;AACpB,gBAAI,EAAE,SAAS,4BAAgB,oBAAoB;AAElD,oBAAM,MAAM,8BAAkB,IAAI;AAClC,wBAAU;YACX,WAAW,EAAE,SAAS,4BAAgB,iBAAiB;AAEtD,oBAAM,MAAM,8BAAkB,SAAS;AACvC,wBAAU;YACX;UACD;AAEA,cAAI,CAAC,SAAS;AAGb,kBAAM,MAAM,8BAAkB,IAAI;AAElC,kBAAM;UACP;QACD;MACD,GA5BsB;AAkCtB,UAAI,wBAAwB,UAAa,sBAAsB;AAI9D,aAAK,eAAe,YAAY,EAAE,QAAO,EAAG,MAAM,MAAK;QAEvD,CAAC;MACF;AAGA,iBAAW,SAAS,KAAK,iBAAiB;AACzC,YAAI,MAAM,UAAU,IAAI,OAAO,GAAG;AAEjC,gBAAM,QAAQ,IAAI,OAAO;AAGzB,gBAAM,MAAM,8BAAkB,OAAO;AACrC;QACD;MACD;AAGA,UACC,IAAI,mBAAmB,6CACpB,IAAI,QAAQ,gBAAgB,QAC9B;AAED,cAAM,MAAM,8BAAkB,OAAO;AACrC;MACD;AAGA,UAAI,IAAI,mBAAmB,yCAA+B;AACzD,cAAM,UAAU,IAAI;AACpB,YACC,IAAI,QAAQ,SAAS,kCAAwB,gBAC5C;AACD,gBAAM,cAAc,MACnB,KAAK,WACH,kDACA,OAAO,CACP;AAEH;QACD,WACC,IAAI,QAAQ,SACP,kCAAwB,uBAC5B;AACD,gBAAM,cAAc,MACnB,KAAK,WACH,2CACA,OAAO,CACP;QAEJ;MACD;AAGA,YAAM,cAAc,MAAM,KAAK,cAAc,IAAI,OAAO,CAAC;AAEzD;IACD,WAAW,eAAe,2CAA0B;AAEnD,WAAK,YAAY,IAAI;AACrB,aAAO,KAAK,WAAW,+BAA+B,GAAG;IAC1D,WAAW,eAAe,0CAAyB;AAClD,UAAI,MAAM,KAAK,mCAAmC,GAAG,GAAG;AACvD;MACD;IACD,OAAO;AACN,UACC,IAAI,gBAAgB,2BAAa,kBAC9B,IAAI,gBAAgB,2BAAa,kBACjC,MAAM,KAAK,aACX,mCAAmC,GAAG,GACxC;AAED;MACD;AAGA,WAAK,UAAU,MACd,oBACC,2BAAa,IAAI,YAAY,CAC9B,KAAK,IAAI,YAAY,GAAG;AAEzB,iBAAW,KAAK,gBAAgB,IAAI,IAAI,YAAY;IACrD;AAEA,QAAI,YAAY,UAAa,SAAS,SAAS,GAAG;AACjD,WAAK,UAAU,MACd,KAAK,SAAS,MAAM,WACnB,SAAS,WAAW,IAAI,MAAM,EAC/B,cAAc;AAGf,eAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,aAAK,UAAU,MAAM,uBAAuB,CAAC,EAAE;AAE/C,cAAM,UAAU,SAAS,CAAC;AAC1B,YAAI,gBAAgB,QAAQ,OAAO,GAAG;AACtC,YAAI,yBAAyB,SAAS;AACrC,0BAAgB,MAAM;QACvB;AACA,YAAI,eAAe;AAClB,eAAK,UAAU,MAAM,6BAA6B;AAClD,cAAI,QAAQ,SAAS;AACpB,iBAAK,UAAU,MACd,4DAA4D;AAE7D,qBAAS,OAAO,GAAG,CAAC;UACrB;AAEA;QACD;MACD;IACD,OAAO;AACN,WAAK,UAAU,MAAM,6BAA6B,MAAM;IACzD;EACD;EAEQ,wBAAwB;EAExB,MAAM,uBACb,MAAe;AAGf,QAAI,CAAC,KAAK,iBAAiB;AAC1B,UAAI,CAAC,KAAK,uBAAuB;AAChC,aAAK,wBAAwB;AAC7B,aAAK,cAAc,QAAQ,KAAK,IAAI;UACnC,SACC;UACD,WAAW;UACX,OAAO;SACP;MACF;AACA;IACD;AAGA,SAAK,MAAM,2BAAe,UAAU;MACnC,aAAa;MACb,SAAS;;MAET,QAAQ;KACR;AAGD,UAAM,gBAAgB,wBAAC,MACtB,EAAE,QAAQ,UAAS,MAAO,KAAK,UAC5B,6BAAW,EAAE,OAAO,KACpB,EAAE,QAAQ,mBAAmB,iCAHX;AAKtB,QAAI,KAAK,uBAAuB,aAAa,GAAG;AAC/C,WAAK,cAAc,QAAQ,KAAK,IAAI;QACnC,SACC;QACD,OAAO;OACP;AACD;IACD;AAGA,SAAK,gBAAgB,2BAA2B,KAAK,EAAE;AAGvD,QAAI;AACH,YAAM,KAAK,eAAe,SAAS,UAAS;IAC7C,SAAS,GAAG;AACX,WAAK,cAAc,QAAQ,KAAK,IAAI;QACnC,SAAS,6BAAyB,+BAAgB,CAAC,CAAC;QACpD,WAAW;OACX;IACF;EACD;;;;;EAMQ,0BACP,MACA,SAA8B;AAE9B,UAAM,SAAS,KAAK;AACpB,QAAI,CAAC;AAAQ;AAEb,WAAO,SACN;MACC,QAAQ,KAAK;MACb,SAAS,OAAO,WAAW,QAAQ,KAAK;OAEzC;MACC,OAAO,QAAQ;MACf,UAAU,KAAK,WAAW;OAE3B,EAAE,MAAM,KAAI,CAAE;EAEhB;EAEQ,MAAM,mCACb,MACA,SAAuC;AAEvC,UAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAE5D,QAAI,KAAK,wBAAwB,KAAK,EAAE,MAAM,0BAAc,WAAW;AACtE,YAAM,EAAE,aAAY,QAAK,0CAAY;AACrC,YAAM,SAAS,eAAe,SAAS;QACtC;;QAEA,CAAA;MAAE;IAEJ,OAAO;AAEN,YAAM,SAAS,eAAe,SAAS,wBACtC,CAAA,GACA,CAAA,CAAE;IAEJ;EACD;;EAGQ,MAAM,wBACb,MAAe;AAGf,QAAI,CAAC,KAAK,oBAAoB,KAAK,EAAE,GAAG;AACvC,UAAI,CAAC,KAAK,uBAAuB;AAChC,aAAK,wBAAwB;AAC7B,aAAK,cAAc,QAAQ,KAAK,IAAI;UACnC,SACC;UACD,WAAW;UACX,OAAO;SACP;MACF;AACA;IACD;AAGA,SAAK,MAAM,2BAAe,YAAY,GAAG;MACxC,aAAa;MACb,SAAS;;MAET,QAAQ;KACR;AAGD,UAAM,gBAAgB,wBAAC,MACtB,EAAE,QAAQ,UAAS,MAAO,KAAK,UAC5B,6BAAW,EAAE,OAAO,KACpB,EAAE,QAAQ,mBAAmB,kCAHX;AAKtB,QAAI,KAAK,uBAAuB,aAAa,GAAG;AAC/C,WAAK,cAAc,QAAQ,KAAK,IAAI;QACnC,SACC;QACD,OAAO;OACP;AACD;IACD;AAEA,QAAI;AACH,YAAM,KAAK,eAAe,YAAY,EAAE,UAAS;IAClD,SAAS,GAAG;AACX,WAAK,cAAc,QAAQ,KAAK,IAAI;QACnC,SAAS,6BAAyB,+BAAgB,CAAC,CAAC;QACpD,WAAW;OACX;IACF;EACD;;;;EAKQ,2BACP,MACA,UAAgC;AAehC,SAAK,cAAc,QAAQ,KAAK,IAAI;MACnC,SACC;MACD,OAAO;MACP,WAAW;KACX;EACF;EAEQ,MAAM,oCACb,MACA,SAAwC;AAExC,UAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAE5D,UAAM,uBAAuB,KAAK,wBAAwB,KAAK,EAAE;AACjE,UAAM,sBACL,QAAQ,mBACP,2BAAe,YAAY,GAC3B,2BAAiB,oBAAoB,GAEpC;AAEH,QACC,yBAAyB,UACtB,yBAAyB,qBAC3B;AAGD,YAAM,iBAAiB,mBAAO,OAAO,CAAC,WACrC,iCAAsB,EAAE,IAAI,CAAC;AAI9B,YAAM,8BAA8B,6BAAiB,OACpD,CAAC,OACA,eAAe,SAAS,EAAE,KAKvB,OAAO,2BAAe,eAAe,CAAC;AAG3C,YAAM,eAAe,oBAAI,IAAI;;;;;;;;;;;;;;;;QAgB5B,2BAAe;QACf,2BAAe,+BAA+B;QAC9C,2BAAe,sBAAsB;QACrC,2BAAe,2BAA2B;QAC1C,2BAAe;QACf,2BAAe,uBAAuB;QACtC,2BAAe,2BAA2B;QAC1C,2BAAe;QACf,2BAAe;QACf,2BAAe,kBAAkB;;;QAIjC,2BAAe,sBAAsB;;QAGrC,GAAG,4BAA4B,OAC9B,CAAC;;;;UAIA,OAAO,2BAAe,YACnB,OAAO,2BAAe,YAAY,KAClC,OAAO,2BAAe,mBAAmB;SAAC;OAE/C;AAID,YAAM,gBAAgB,IAAI,QAAI,0CAAY,EAAG,YAAY;AACzD,YAAM,4BAA4B,CAAC,GAAG,YAAY,EAAE,OAAO,CAAC,OAC3D,CAAC,cAAc,IAAI,EAAE,CAAC;AAGvB,YAAM,SAAS,eAAe,YAAY,EAAE,wBAC3C,yBAAyB;IAE3B,eAAW,+BAAkB,mBAAmB,GAAG;AAElD,YAAM,SAAS,eAAe,YAAY,EACxC,YAAY;QACZ,yBAAyB;OACzB,EACA,wBAAwB,CAAA,CAAE;IAC7B,OAAO;IAEP;EACD;;;;;EAMgB,wBAAoB,qCAAsB,GAAI;EAE7C,wBAAwB,oBAAI,IAAG;;;;EAIzC,4BAA4B,QAAc;AAChD,QAAI,CAAC,KAAK,sBAAsB,IAAI,MAAM,GAAG;AAC5C,WAAK,sBAAsB,IAC1B,YACA,qCAAsB,wCAA4B,IAAI,CAAC;IAEzD;AACA,WAAO,KAAK,sBAAsB,IAAI,MAAM,EAAE;EAC/C;;;;EAKgB,uCAAmC,qCAClD,8CACA,IAAI;EAGG,oBACP,KACA,UAA8D,CAAA,GAAE;AAiBhE,QAAI,wBAAc,sBAAsB,GAAG,GAAG;AAC7C,YAAM,wBAAc,YACnB,KACA,KAAK,4BAA4B,IAAI,MAAgB,CAAC;IAExD;AAGA,QAAI,yBAAe,sBAAsB,GAAG,GAAG;AAC9C,YAAM,wBAAwB,KAAK,sBAClC,2BAAe,eAAe,GAC9B,IAAI,MAAgB;AAGrB,YAAM,0BAA0B,IAC7B,yBAAe,cAAc,GAAG,IAChC,yBAAe,YAAY,GAAG;IAClC;AAGA,QAAI,kBAAQ,sBAAsB,GAAG,GAAG;AACvC,YAAM,kBAAQ,YAAY,GAAG;IAC9B,OAAO;AAEN,UAAI,UAAU;AACd,YAAM,OAAO,IAAI,QAAQ,IAAI;AAC7B,UAAI,MAAM,WAAW,2BAAe,YAAY,CAAC,GAAG;AAEnD,cAAM,eAAe,KAAK,wBAAuB;AACjD,cAAM,kBAAkB,KAAK,oBAAoB,KAAK,EAAE;AACxD,sBAAU,+BAAkB,YAAY,KACpC,CAAC,CAAC,iBAAiB,SAAS,IAAI,KAAK,EAAE;MAC5C,WAAW,QAAQ,sBAAsB,QAAW;AAEnD,kBAAU;MACX;AACA,UAAI,WAAW,sBAAY,sBAAsB,GAAG,GAAG;AACtD,cAAM,sBAAY,YACjB,KACA,KAAK,WACL,MACA;UACC,eAAe,QAAQ;UACvB,oBAAoB,CAAC,CAAC,QAAQ;UAC9B,kBAAkB,QAAQ;UAC1B,gBAAgB,QAAQ;SACxB;MAEH;AAGA,UAAI,qBAAW,sBAAsB,GAAG,GAAG;AAC1C,cAAM,qBAAW,YAChB,KAAK,WACL,KAAK,iBACL,GAAG;MAEL;IACD;AACA,WAAO;EACR;EAEO,eAAe,KAAyB;AAE9C,eACC,uCAA4B,IAAI,OAAO,SACpC,4CAAiC,IAAI,OAAO,GAC9C;AACD,YAAM,YAAY,IAAI,QAAQ;AAC9B,cAAI,2BAAQ,SAAS,GAAG;AAEvB,mBAAW,OAAO,WAAW;AAC5B,cAAI,wBACH,IAAI,QAAQ,oBACZ,IAAI;QAEN;AACA;MACD;AAGA,gBAAU,qBAAqB,IAAI,QAAQ;AAC3C,cAAQ,IAAI,QAAQ,MAAM;QACzB,KAAK,2BAAe;AACnB,oBAAU,wBACT,+BAAmB,aACnB,IAAI;AAEL;QACD,KAAK,2BAAe,YAAY;QAChC,KAAK,2BAAe;AACnB,oBAAU,wBACT,+BAAmB,UACnB,IAAI;AAEL;QACD,KAAK,2BAAe,sBAAsB;AACzC,oBAAU,wBACT,+BAAmB,OACnB,IAAI;AAEL;MACF;AAEA,UAAI,UAAU;IACf;EACD;EAEQ,sBAAsB,IAAgB;AAE7C,YAAI,+BAAkB,GAAG,IAAI;AAAG,aAAO;AAGvC,UAAM,WAAW,KAAK,eAAe,EAAE;AACvC,UAAM,OAAO,UAAU,WAAU;AACjC,QAAI,CAAC;AAAM,aAAO;AAGlB,QAAI,UAAU,sBAAsB,GAAG,IAAI;AAAG,aAAO;AAIrD,UAAM,eAAe,MAAM,cAAc;AACzC,QACC,GAAG,kBAAkB,KAClB,GAAG,YAAY,KAAK,SAAS,QAAQ,KACrC,KAAK,iBAAgB,KAAM,KAE3B,cAAc,4BAA4B,QAC5C;AACD,YAAM,iBAAiB,KAAK,YAC3B,aAAa,wBAAwB;AAEtC,UAAI,gBAAgB,WAAW,GAAG,IAAI;AAAG,eAAO;IACjD;AAEA,WAAO;EACR;;EAGQ,gBAAgB,IAAgB;AACvC,QAAI,KAAK,sBAAsB,EAAE,GAAG;AACnC,SAAG,cAAc,IAAI;IACtB;AAEA,YAAI,uCAA4B,EAAE,GAAG;AACpC,WAAK,gBAAgB,GAAG,YAAY;IACrC,eAAW,4CAAiC,EAAE,GAAG;AAChD,iBAAW,gBAAgB,GAAG,cAAc;AAC3C,aAAK,gBAAgB,YAAY;MAClC;IACD;EACD;;;;EAKQ,6BACP,KACA,SACA,QAA2B;AAG3B,UAAM,OAAO,KAAK,WAAW,GAAG;AAChC,QAAI,UAAU;AACd,YAAI,6BAAW,GAAG,SAAK,yBAAU,GAAG,GAAG;AAEtC,UAAI,CAAC;AAAM;AAGX,cAAI,mCAAiB,MAAM,GAAG;AAC7B,YAAI,CAAC,OAAO,KAAI,GAAI;AACnB,oBAAU;AACV,eAAK,oBAAoB,mBAAmB;QAC7C,OAAO;AACN,eAAK,oBAAoB,YAAY;AACrC,eAAK,UAAU,GAAG;AAElB,eAAK,WAAW,oBAAI,KAAI;QACzB;AAGA,gBAAI,8BAAY,MAAM,GAAG;AACxB,kBAAQ,aAAa,OAAO,QAAQ;AACpC,eAAK,sBAAsB,OAAO,QAAQ;QAC3C;MACD;AAGA,UAAI,SAAS;AACZ,YAAI,KAAK,UAAU;AAElB,cAAI,QAAQ,aAAa,4BAAgB,WAAW;AAEnD,gBAAI,CAAC,KAAK,WAAW;AACpB,2BAAa,MACZ,KAAK,wBAAwB,IAAI,CAAC;YAEpC;AAEA,iBAAK,YAAW;UACjB;QACD,WAAW,KAAK,WAAW,wBAAW,OAAO;AAE5C,eAAK,YAAW;QACjB;MACD;IACD,OAAO;AACN,WAAK,aAAa,oBAAoB,YAAY;IACnD;EACD;EAEQ,yCACP,YACA,oBAAsC;AAOtC,UAAM,mBAAmB,KAAK,MAAM,oBAAoB;AACxD,QAAI,kBAAkB,UAAS,MAAO,WAAW,IAAI;AACpD,aAAO;IACR;AACA,QAAI,KAAC,6BAAW,gBAAgB,GAAG;AAClC,aAAO;IACR;AAGA,QAAI,KAAC,+BAAkB,WAAW,wBAAuB,CAAE,GAAG;AAC7D,aAAO;IACR;AAGA,UAAM,qBAAqB,iBAAiB,mBAChC;AACZ,UAAM,iBAAiB,CAAC,EACvB,qBAAqB,+BAAmB;AAEzC,QAAI,CAAC,sBAAsB,CAAC,gBAAgB;AAC3C,aAAO;IACR;AAGA,SAAK,cAAc,QAAQ,WAAW,IAAI;MACzC,SACC;MACD,OAAO;KACP;AACD,WAAO;EACR;EAEQ,oBAAoB,aAAwB;AAGnD,QACC,KAAK,eACF,KAAK,WAAW,WAAW,6BAAiB,cAC9C;AACD,aAAO;IACR;AAEA,UAAM,UAAU,YAAY;AAC5B,UAAM,aAAa,QAAQ,WAAW,IAAI;AAG1C,QAAI,CAAC;AAAY,aAAO;AAaxB,QAAI,cAAc,OAAO;AAAG,aAAO;AAEnC,WACC,WAAW,WAAW,wBAAW,UAC7B,CAAC,WAAW,WAAW,2BAAe,SAAS,CAAC,KAChD,WAAW,kBAAkB,4BAAe;EAElD;EAEQ,cAAc,OAAyB,MAAa;AAC3D,UAAM,QAAQ,KAAK,OAAO,QAAQ,KAAK;AACvC,QAAI,MAAM;AACT,WAAK,oBAAoB,KAAK;IAC/B,OAAO;AACN,WAAK,oBAAoB,EAAE,KAAK;IACjC;AACA,SAAK,YAAY,KAAK,qBAAqB;EAC5C;EAEQ,MAAM,sBACb,OAAuB;AAEvB,QAAI;AACJ,qBAAiB,eAAe,OAAO;AACtC,UAAI,cAAc;AACjB,uBAAe,YAAY;AAC3B,uBAAe;MAChB;AACA,WAAK,cAAc,OAAO,IAAI;AAE9B,UAAI;AACJ,UAAI;AACH,cAAM,KAAK,mBAAmB,WAAW;MAC1C,SAAS,GAAG;AACX,gBAAQ;MACT;AACC,cAAM,oBAAmB;MAC1B;AAIA,UAAI,OAAO;AACV,aAAK,wBAAwB,aAAa,KAAK;MAChD;AAEA,qBAAe,aAAa,MAAK;AAChC,aAAK,cAAc,OAAO,KAAK;MAChC,CAAC;IACF;EACD;;EAGQ,MAAM,mBAAmB,aAAwB;AACxD,QAAI;AACJ,QAAI;AAEJ,gBAAY,MAAK;AACjB,gBAAY,YAAY,EAAE,OAAO,6BAAiB,OAAM,CAAE;AAE1D,UAAM,oBACL,KAAK,mBAAc,IAEhB,IACA,KAAK,QAAQ,SAAS;AAG1B,WAAQ,MAAM,MAAM,YAAY,oBAAoB,UAAU,GAAI;AAEjE,UAAI,iBAAiB;AACrB,UAAI,gBAAgB;AACpB,qBAAgB,UAAS,gBAAgB,KAAI,iBAAiB;AAC7D,YAAI;AACH,uBAAa,MAAM,KAAK,sBACvB,KACA,YAAY,KAAK;AAElB,kBAAI,mCAAiB,UAAU,GAAG;AAKjC,gBACC,WAAW,mBAAmB,2BAAe,QAC1C,cAAc,cAEd,WAAW,UAAU,YAAY,GACnC;AACD;AACA,kBAAI,iBAAiB,mBAAmB;AAEvC,qBAAK,WAAW,UACf,6BAAiB,MAAM;AAExB,0BAAM,mBACL,KAAK,QAAQ,SAAS,aACtB,IAAI;AAGL,yBAAS;cACV,OAAO;AAEN,sBAAM,IAAI,uBACT,oCAAoC,cAAc,aAClD,4BAAgB,2BAChB,YACA,YAAY,KAAK;cAEnB;YACD;AAEA,gBACC,KAAK,WAAW,WAAW,6BAAiB,QAC3C;AAED,mBAAK,WAAW,UAAU,6BAAiB,KAAK;YACjD;AAEA,gBAAI,CAAC,WAAW,KAAI,GAAI;AACvB,oBAAM,IAAI,uBACT,4CACA,4BAAgB,wBAChB,YACA,YAAY,KAAK;YAEnB;UACD;AAEA,gBAAM;QACP,SAAS,GAAQ;AAChB,cAAI;AACJ,cAAI,iBAAiB;AAErB,cAAI,KAAC,0BAAa,CAAC,GAAG;AACrB,0BAAU,+DAAoC,CAAC;UAChD,OAAO;AACN,oBACC,6BAAW,GAAG,SAAK,yCAA4B,CAAC,GAC/C;AAED,oBAAM;YACP,eAAW,oCAAuB,CAAC,GAAG;AAErC,oBAAM;YACP,eAAW,gCAAmB,CAAC,GAAG;AAEjC,oBAAM;YACP,eACC,wCAAsB,EAAE,OAAO,KAC5B,CAAC,EAAE,QAAQ,SACb;AAED;AACA,kBAAI,gBAAgB,GAAG;AACtB,iCAAiB;cAClB,OAAO;AACN,sBAAM;cACP;YACD,WACC,EAAE,SAAS,4BAAgB,2BAC1B;AAED,oBAAM;YACP;AAEA,gBACC,KAAK;cACJ;;cAEA,gBAAgB,iBAAiB;cACjC;YAAC,GAED;AACD,kBAAI,gBAAgB;AACnB,0BAAM,mBAAK,gBAAgB,IAAI;cAChC;AAEA,uBAAS;YACV;AAEA,sBAAU;UACX;AAGA,gBAAM;QACP;MACD;IACD;AAGA,gBAAY,YAAY,EAAE,OAAO,6BAAiB,UAAS,CAAE;EAC9D;;;;EAKQ;;EAKA,MAAM,sBAAmB;AAChC,qBAAiB,QAAQ,KAAK,gBAAgB;AAC7C,YAAM,EAAE,KAAK,mBAAmB,OAAM,IAAK;AAC3C,WAAK,kCAAkC;AAGvC,eAAU,UAAS,UAAU,KAAI,WAAW;AAC3C,YAAI;AACH,gBAAM,MAAM,MAAM,KAAK,wBACtB,KACA,iBAAiB;AAElB,iBAAO,QAAQ,GAAG;QACnB,SAAS,GAAG;AACX,kBACC,0BAAa,CAAC,KACX,EAAE,SAAS,4BAAgB,6BAC3B,EAAE,YAAY,SACd,UAAU,GACZ;AAED,sBAAM,mBAAK,GAAG;AACd;UACD;AAGA,iBAAO,OAAO,CAAU;QACzB;AACC,eAAK,kCAAkC;QACxC;AACA,cAAM;MACP;IACD;EACD;EAEQ,gBAAa;AACpB,eAAW,SAAS,KAAK,QAAQ;AAChC,YAAM,QAAO;IACd;EACD;;EAGQ,sBACP,KACA,mBAA0B;AAE1B,UAAM,aAAS,+CAAqB;AACpC,SAAK,eAAe,IAAI;MACvB;MACA;MACA;MACA,CAAC,OAAO,OAAO,GAAG,MAAK;AAEtB,eAAO,OACN,IAAI,uBACH,+CACA,4BAAgB,2BAChB,QACA,iBAAiB,CACjB;MAEH;KACA;AAED,WAAO;EACR;EAEQ,yBACP,KACA,eACA,OAAiB;AAGjB,QAAI,KAAC,6BAAW,GAAG;AAAG,aAAO;AAG7B,QACC,MAAM,SAAS,4BAAgB,sBAC5B,MAAM,YAAY,YACpB;AACD,aAAO;IACR;AAIA,SACE,eAAe,6CACZ,eAAe,oDAChB,MAAM,SAAS,4BAAgB,wBACjC;AACD,aAAO;IACR;AAEA,WAAO,gBAAgB,IAAI;EAC5B;;;;;EAMQ,MAAM,wBACb,KACA,mBAA0B;AAG1B,QAAI,IAAI,gBAAe,KAAM,CAAC,IAAI,cAAa,GAAI;AAClD,UAAI,aAAa,KAAK,kBAAiB;IACxC;AAIA,QACC,IAAI,iBAAiB,2BAAa,aAC/B,KAAK,WAAW,cAAc,QAAQ,GACxC;AACD,WAAK,QAAQ,wBAAuB;IACrC;AAEA,UAAM,cAAU,8DAA8B,GAAG;AACjD,SAAK,4BAAwB,+CAAqB;AAClD,UAAM,kBAAkB,IAAI,gBAAe;AAE3C,QAAI,YAAsD;MACzD,OAAO;;AAGR,QAAI;AACH,aAAO,CAAC,QAAQ,MAAM;AACrB,YAAI,aAAa,QAAW;AAE3B,gBAAM,IAAI,MACT,sEAAsE;QAExE;AACA,cAAM,aAAa,QAAQ,KAAK,SAAS;AACzC,YAAI,cAAc,QAAW;AAE5B,gBAAM,IAAI,MACT,wEAAwE;QAE1E;AAGA,oBAAY;AAGZ,gBAAQ,WAAW,WAAW,QAAQ;AAGtC,gBAAQ,QAAQ,MAAM,OAAO;UAC5B,KAAK;AAEJ,kBAAM,IAAI,MACT,kFAAkF;UAGpF,KAAK,WAAW;AACf,iBAAK,UAAU,WAAW,KAAK;cAC9B,WAAW;aACX;AAGD,gBAAI,WAAU;AACd,kBAAM,OAAO,MAAM,IAAI,UACtB,KAAK,mBAAkB,CAAE;AAE1B,kBAAM,KAAK,YAAY,IAAI;AAC3B,wBAAY,EAAE,OAAO,eAAc;AACnC;UACD;UAEA,KAAK,iBAAiB;AACrB,kBAAM,cAAc,MAAM,KAAK,qBAC9B,MAAM,MACN,KAAK,QAAQ,SAAS,GAAG,EACxB,MAAM,MAAM,SAAkB;AAEhC,gBAAI,gBAAgB,WAAW;AAC9B,0BAAY,EAAE,OAAO,UAAS;YAC/B,WAAW,gBAAgB,6BAAe,KAAK;AAC9C,0BAAY,EAAE,OAAO,MAAK;YAC3B,WAAW,gBAAgB,6BAAe,KAAK;AAC9C,0BAAY,EAAE,OAAO,MAAK;YAC3B,WAAW,gBAAgB,6BAAe,KAAK;AAC9C,0BAAY,EAAE,OAAO,MAAK;YAC3B;AAEA;UACD;UAEA,KAAK,sBAAsB;AAC1B,kBAAM,WAAW,MAAM,QAAQ,KAAK;cACnC,KAAK,uBAAuB,MAAM,CAAC,MAClC,CAAU;cAEX,KAAK,eACJ,CAAC,SAAS,IAAI,mBAAmB,IAAI,GACrC,IAAI,mBAAkB,KAClB,KAAK,QAAQ,SAAS,UAC1B,QACA,gBAAgB,MAAM,EACrB,MAAM,MAAM,SAAkB;aAChC;AAED,gBAAI,oBAAoB,OAAO;AAG9B,8BAAgB,MAAK;AACrB,oBAAM;YACP;AAEA,gBAAI,aAAa,WAAW;AAC3B,sBAAI,6BAAW,GAAG,GAAG;AAEpB,qBAAK,KAAK,cAAa;cACxB;AAEA,0BAAY,EAAE,OAAO,UAAS;YAC/B,eACC,kCAAmB,QAAQ,KAAK,CAAC,SAAS,KAAI,GAC7C;AACD,0BAAY,EAAE,OAAO,gBAAgB,SAAQ;YAC9C,OAAO;AACN,0BAAY,EAAE,OAAO,YAAY,SAAQ;YAC1C;AAEA;UACD;UAEA,KAAK,sBAAsB;AAC1B,gBAAI;AACJ,oBAAI,6BAAW,GAAG,GAAG;AAEpB,yCAAuB,wBAAS,MAAK;AACpC,qBAAK,KAAK,cAAa;cACxB,GAAG,KAAK,QAAQ,SAAS,aAAa,EAAE,MAAK;YAC9C;AAEA,kBAAM,WAAW,MAAM,QAAQ,KAAK;cACnC,KAAK,uBAAuB,MAAM,CAAC,MAClC,CAAU;cAEX,KAAK,eACJ,CAAC,SAAS,IAAI,mBAAmB,IAAI,GACrC,IAAI,mBAAkB,KAClB,KAAK,QAAQ,SAAS,kBAC1B,QACA,gBAAgB,MAAM,EACrB,MAAM,MAAM,SAAkB;aAChC;AAED,kCAAsB,MAAK;AAE3B,gBAAI,oBAAoB,OAAO;AAG9B,8BAAgB,MAAK;AACrB,oBAAM;YACP;AAEA,gBAAI,aAAa,WAAW;AAC3B,0BAAY,EAAE,OAAO,UAAS;YAC/B,eACC,kCAAmB,QAAQ,KAAK,CAAC,SAAS,KAAI,GAC7C;AACD,0BAAY,EAAE,OAAO,gBAAgB,SAAQ;YAC9C,OAAO;AACN,0BAAY,EAAE,OAAO,YAAY,SAAQ;YAC1C;AAEA;UACD;UAEA,KAAK,WAAW;AACf,mBAAO,QAAQ,MAAM;UACtB;UAEA,KAAK,WAAW;AACf,kBAAM,EAAE,QAAQ,OAAM,IAAK,QAAQ;AACnC,gBACC,WAAW,uBACP,6BAAW,GAAG,SAAK,mCAAiB,MAAM,IAC7C;AAGD,qBAAO;YACR,OAAO;AACN,wBAAM,6DACL,QACA,KACA,QACA,iBAAiB;YAEnB;UACD;QACD;MACD;IACD;AACC,WAAK,wBAAwB;IAC9B;EACD;EAEQ,uBAAuB,GAAc;AAC5C,QACC,EAAE,aAAa,4BAAgB,aAC5B,EAAE,aAAa,4BAAgB,qBACjC;AACD,aAAO,KAAK;IACb,OAAO;AACN,aAAO,KAAK;IACb;EACD;;;;;;EAOO,MAAM,YACZ,KACA,UAA8B,CAAA,GAAE;AAEhC,SAAK,YAAW;AAEhB,QAAI;AACJ,YAAI,yBAAU,GAAG,SAAK,6BAAW,GAAG,GAAG;AACtC,aAAO,KAAK,WAAW,GAAG;IAC3B;AAEA,QAAI,QAAQ,YAAY,QAAW;AAClC,cAAQ,eAAW,kCAAmB,GAAG;IAC1C;AACA,QAAI,QAAQ,YAAY,QAAW;AAClC,YAAM,YAAY,IAAI,YAAY;AAClC,YAAM,cAAc,2BAAa,IAAI,YAAY;AACjD,YAAM,IAAI,uBACT,4CAA4C,SAAS,KAAK,WAAW,kDACrE,4BAAgB,iBAAiB;IAEnC;AAEA,QAAI,QAAQ,gBAAgB;AAAW,cAAQ,eAAe;AAC9D,QACC,QAAQ,gBACL,KAAK,eAAe,UACpB,CAAC,KAAK,YAAY,oBAAoB,IAAI,YAAY,GACxD;AACD,YAAM,IAAI,uBACT,sCACC,2BAAa,IAAI,YAAY,CAC9B,aACA,4BAAgB,mBAAmB;IAErC;AAMA,QACC,CAAC,CAAC,QAEC,CAAC,cAAc,GAAG,KAElB,KAAK,aACJ,KAAK,WAAW,2BAAe,SAAS,CAAC,KAGzC,KAAK,iBAAiB,4BAAe,aAGtC,EAAE,eAAe,8CACjB,EAAE,eAAe,oDAEjB,QAAQ,aAAa,4BAAgB,WACvC;AACD,UAAI,QAAQ,aAAa,4BAAgB,WAAW;AAEnD,gBAAQ,MAAM;MACf;AACA,cAAQ,WAAW,4BAAgB;IACpC;AAGA,UAAM,EAAE,WAAW,cAAa,QAAK,iDACpC,MACA,KAAK,mBAAkB,GACvB,KACA,CAACC,MAAK,YAAW;AAChB,WAAK,6BAA6BA,MAAK,SAAS,OAAO;IACxD,CAAC;AAEF,UAAM,cAAc,IAAI,+BAAY,MAAM;MACzC,SAAS;MACT,UAAU,QAAQ;MAClB,OAAO;MACP,SAAS;MACT,UAAU,QAAQ;KAClB;AAGD,QAAI,QAAQ,gCAAgC,QAAW;AACtD,kBAAY,4BACX,QAAQ;IACV;AACA,QAAI,QAAQ,oBAAoB,MAAM;AACrC,kBAAY,kBAAkB;IAC/B;AACA,gBAAY,wBAAwB,CAAC,CAAC,QAAQ;AAC9C,gBAAY,MAAM,QAAQ;AAG1B,SAAK,uBAAuB,WAAW,EAAE,IAAI,WAAW;AACxD,gBAAY,YAAY,EAAE,OAAO,6BAAiB,OAAM,CAAE;AAG1D,QAAI;AACJ,QAAI,QAAQ,QAAQ;AACnB,8BAAoB,wBAAS,MAAK;AACjC,aAAK,KAAK,aAAa,CAAC,GAAG,YAAW;AACrC,cAAI,MAAM,aAAa;AACtB,mBAAO;cACN,MAAM;cACN,SAAS;cACT,MAAM,4BAAgB;;UAExB;AACA,iBAAO,EAAE,MAAM,OAAM;QACtB,CAAC;MACF,GAAG,QAAQ,MAAM,EAAE,MAAK;IACzB;AAEA,QAAI;AACH,YAAM,SAAU,MAAM;AAGtB,UAAI;AACJ,cAAI,6BAAW,GAAG,GAAG;AAEpB,2BACC,QAAQ,aAAa,4BAAgB,aAGlC,WACC,OAAO,iBACL,2BAAa,4BACf,OAAO,iBACL,2BAAa,0BACd,2CAAyB,MAAM,KAAK,OAAO,KAAI;MACtD,OAAO;AAGN,+BAAmB,yBAAU,GAAG,KAC5B,cACA,kCAAmB,MAAM,KACzB,OAAO,KAAI;MAChB;AAEA,UAAI,oBAAoB,QAAQ,KAAK,YAAY,CAAC,KAAK,WAAW;AACjE,qBAAa,MAAM,KAAK,wBAAwB,IAAI,CAAC;MACtD;AAGA,kBAAY,YAAY,EAAE,OAAO,6BAAiB,UAAS,CAAE;AAE7D,aAAO;IACR,SAAS,GAAG;AACX,cAAI,0BAAa,CAAC,GAAG;AACpB;;WAEE,EAAE,SAAS,4BAAgB,0BACxB,EAAE,SAAS,4BAAgB,2BAC5B,EAAE,mBAAmB,yBAErB,EAAE,QAAQ,iBAAiB,2BAAa,YACxC,EAAE,QAAQ,iBAAiB,2BAAa,qBACxC,EAAE,QAAQ,iBAAiB,2BAAa,kBACxC,EAAE,QAAQ,iBACR,2BAAa;UACjB;AACD,eAAK,aAAa,oBAAoB,mBAAmB;AACzD,iBAAO,EAAE;QACV,WAAW,EAAE,SAAS,4BAAgB,wBAAwB;AAE7D,gBAAM,oBAAoB,iBAAiB;QAC5C;AAEA,YAAI,CAAC,EAAE,mBAAmB;AACzB,gBAAM,IAAI,uBACT,EAAE,SACF,EAAE,MACF,EAAE,SACF,YAAY,KAAK;QAEnB;MACD;AACA,YAAM;IACP;AAEC,yBAAmB,MAAK;IACzB;EACD;;EAGO,sBACN,SACA,UAA8D,CAAA,GAAE;AAGhE,QAAI,QAAQ,oBAAoB,OAAO;AACtC,gBAAU,KAAK,oBAAoB,SAAS,OAAO;IACpD;AAEA,QAAI;AACJ,QAAI,QAAQ,aAAY,KAAM,QAAQ,YAAW,GAAI;AACpD,UACC,KAAK,WAAW,oBAAoB,2BAAa,cAAc,GAC9D;AAED,cAAM,IAAI,uCAAsB;UAC/B,cAAc,KAAK;UACnB;UACA,iBAAiB,KAAK,SAAS,SAAS;SACxC;MACF,OAAO;AACN,cAAM,IAAI,iCAAgB;UACzB;UACA,iBAAiB,KAAK,SAAS,SAAS;SACxC;MACF;IACD,WAAW,QAAQ,YAAW,GAAI;AACjC,UACC,KAAK,WAAW,oBACf,2BAAa,uBAAuB,GAEpC;AAED,cAAM,IAAI,gDAA+B;UACxC,cAAc,KAAK;UACnB;UACA,iBAAiB,KAAK,SAAS,SAAS;SACxC;MACF,OAAO;AACN,cAAM,IAAI,0CAAyB;UAClC;UACA,iBAAiB,KAAK,SAAS,SAAS;SACxC;MACF;IACD,OAAO;AACN,YAAM,IAAI,uBACT,+CACA,4BAAgB,gBAAgB;IAElC;AAEA,QAAI,QAAQ,mBAAmB,QAAW;AACzC,UAAI,kBAAkB,QAAQ;IAC/B;AAGA,QAAI,QAAQ,mBAAmB,QAAW;AACzC,UAAI,kBAAkB,QAAQ;IAC/B;AAEA,QAAI,CAAC,CAAC,QAAQ,iBAAiB;AAC9B,UAAI,oBAAoB,QAAQ;IACjC;AAEA,WAAO;EACR;;;;;;;EAQQ,MAAM,oBAGb,SACA,UAGI,CAAA,GAAE;AAEN,UAAM,MAAM,KAAK,sBAAsB,SAAS,OAAO;AACvD,QAAI;AACH,YAAM,OAAO,MAAM,KAAK,YAAY,KAAK,OAAO;AAGhD,cAAI,6BAAW,IAAI,GAAG;AACrB,aAAK,eAAe,IAAI;AACxB,eAAO,KAAK;MACb;IACD,SAAS,GAAG;AAEX,cACC,0BAAa,CAAC,KACX,EAAE,SAAS,4BAAgB,wBAC7B;AACD,YAAI,QAAQ,aAAY,GAAI;AAC3B,eAAK,cAAc,QAClB,QAAQ,QACR,EAAE,SACF,MAAM;QAER;MACD,OAAO;AAEN,cAAM;MACP;IACD;EACD;;;;;;EAOQ,MAAM,sBACb,SACA,UAA4D;IAC3D,sBAAsB;KACtB;AAGD,UAAM,YAAY,KAAK,4BAA4B,QAAQ,MAAM;AACjE,cAAU,wBAAc,YACvB,SACA,WACA,QAAQ,oBAAoB;AAG7B,UAAM,OAAO,MAAM,KAAK,oBACvB,SACA,OAAO;AAER,QAAI,CAAC;AAAM;AAGX,QAAI,QAAQ,wBAAwB,KAAK,mBAAmB;AAC3D,WAAK,mBAAmB,QAAQ,MAAM,EAAE,YAAY,IAClD,QAA6B,WAC9B,QAAQ,QAAQ;IAElB;AAEA,WAAO,KAAK,oBAAmB;EAChC;;;;;;;;;;EAWO,MAAM,YAGZ,SACA,SAA4B;AAE5B,QAAI,SAAS,sBAAsB,QAAW;AAC7C,cAAQ,qBAAqB,QAAQ;IACtC;AAGA,QACC,KAAK,WACJ,QAAQ,MACR,QAAQ,QACR,QAAQ,aAAa,KAEnB,SAAS,sBAAsB,QACjC;AACD,cAAQ,wBAAwB,+BAAmB,UAAU,IAAI;IAClE;AAGA;;MAEC,SAAS,mBAAmB,SAEzB,wBAAc,kBAAkB,MAAM,OAAO;MAC/C;AACD,YAAMC,UAAS,MAAM,KAAK,sBAAsB,SAAS,OAAO;AAChE,UAAIA,SAAQ,WAAW,8BAAkB,WAAW;AAEnD,eAAOA;MACR;AAGA,8BAAc,8BACb,MACA,QAAQ,YAAY,IAAI,GACxB,QAAQ,MACR,KAAK;IAGP;AAGA,UAAM,SAAS,MAAM,KAAK,oBAAoB,SAAS,OAAO;AAM9D,QACC,SAAS,sBAAsB,UAC5B,kBAAkB,+BACpB;AAED,aAAO,OAAO,oBAAmB;IAClC;AAGA,WAAO;EACR;;EAGO,MAAM,oBACZ,SACA,UAGI,CAAA,GAAE;AAEN,UAAM,KAAK,oBAAoB,SAAS;MACvC,UAAU,4BAAgB;;MAE1B,iBAAiB;MACjB,gBAAgB;MAChB,8BAA8B,QAAQ,gCAClC;MACJ,iBAAiB,QAAQ,mBAAmB;MAC5C,iBAAiB,4BAAgB,YAAY,4BAAgB;KAC7D;EACF;EAEQ,MAAM,gBAAa;AAC1B,QAAI;AACH,YAAM,QAAQ,IAAI,+BAAa;AAC/B,YAAM,KAAK,YACV,MAAM,MAAM,UAAU,KAAK,mBAAkB,CAAE,CAAC;AAEjD,WAAK,UAAU,WAAW,OAAO;QAChC,WAAW;OACX;AAID,YAAM,KAAK,qBAAqB,MAAM,MAAM,GAAG,EAAE,MAAM,kBAAI;IAC5D,QAAQ;IAER;EACD;;;;;EAMQ,YAAY,QAAsB;AAEzC,WAAO,KAAK,YAAY,WAAW,KAAK,CAAC,MAAM,CAAC,CAAC;EAClD;;EAGQ,MAAM,YAAY,MAAgB;AACzC,WAAO,KAAK,QAAQ,WAAW,IAAI;EACpC;;;;;;;EAQO,qBACN,WACA,SAAe;AAEf,WAAO,IAAI,QAAwB,CAAC,SAAS,WAAU;AACtD,YAAM,cAAU,+CAAqB;AACrC,YAAM,QAA8B;QACnC;QACA,SAAS,wBAAC,QAAQ,QAAQ,QAAQ,GAAG,GAA5B;QACT,SAAS;;AAEV,WAAK,sBAAsB,KAAK,KAAK;AACrC,YAAM,cAAc,6BAAK;AACxB,cAAM,SAAS,MAAK;AACpB,cAAM,QAAQ,KAAK,sBAAsB,QAAQ,KAAK;AACtD,YAAI,UAAU;AAAI,eAAK,sBAAsB,OAAO,OAAO,CAAC;MAC7D,GAJoB;AAMpB,YAAM,cAAU,wBAAS,MAAK;AAC7B,oBAAW;AACX,eACC,IAAI,uBACH,kEACA,4BAAgB,kBAAkB,CAClC;MAEH,GAAG,OAAO;AAEV,WAAK,QAAQ,KAAK,CAAC,OAAM;AACxB,oBAAW;AACX,gBAAQ,EAAE;MACX,CAAC;IACF,CAAC;EACF;;;;;;;;;EAUO,eACN,WACA,SACA,kBACA,aAAyB;AAEzB,WAAO,IAAI,QAAW,CAAC,SAAS,WAAU;AACzC,YAAM,cAAU,+CAAqB;AACrC,YAAM,QAA6B;QAClC;QACA;QACA,SAAS,wBAAC,QAAQ,QAAQ,QAAQ,GAAG,GAA5B;QACT,SAAS;;AAEV,WAAK,gBAAgB,KAAK,KAAK;AAC/B,YAAM,cAAc,6BAAK;AACxB,cAAM,SAAS,MAAK;AACpB,cAAM,QAAQ,KAAK,gBAAgB,QAAQ,KAAK;AAChD,YAAI,UAAU;AAAI,eAAK,gBAAgB,OAAO,OAAO,CAAC;MACvD,GAJoB;AAMpB,YAAM,cAAU,wBAAS,MAAK;AAC7B,oBAAW;AACX,eACC,IAAI,uBACH,6DACA,4BAAgB,kBAAkB,CAClC;MAEH,GAAG,OAAO;AAEV,WAAK,QAAQ,KAAK,CAAC,OAAM;AACxB,oBAAW;AACX,gBAAQ,EAAO;MAChB,CAAC;AAED,mBAAa,iBAAiB,SAAS,MAAK;AAC3C,oBAAW;MACZ,CAAC;IACF,CAAC;EACF;;;;;;EAOO,eACN,WACA,SACA,aAAyB;AAEzB,WAAO,IAAI,QAAW,CAAC,SAAS,WAAU;AACzC,YAAM,cAAU,+CAAqB;AACrC,YAAM,QAA6B;QAClC;QACA,SAAS,wBAAC,OAAO,QAAQ,QAAQ,EAAE,GAA1B;QACT,SAAS;;AAEV,WAAK,gBAAgB,KAAK,KAAK;AAC/B,YAAM,cAAc,6BAAK;AACxB,cAAM,SAAS,MAAK;AACpB,cAAM,QAAQ,KAAK,gBAAgB,QAAQ,KAAK;AAChD,YAAI,UAAU;AAAI,eAAK,gBAAgB,OAAO,OAAO,CAAC;MACvD,GAJoB;AAMpB,YAAM,cAAU,wBAAS,MAAK;AAC7B,oBAAW;AACX,eACC,IAAI,uBACH,6DACA,4BAAgB,sBAAsB,CACtC;MAEH,GAAG,OAAO;AAEV,WAAK,QAAQ,KAAK,CAAC,OAAM;AACxB,oBAAW;AACX,gBAAQ,EAAO;MAChB,CAAC;AAED,mBAAa,iBAAiB,SAAS,MAAK;AAC3C,oBAAW;MACZ,CAAC;IACF,CAAC;EACF;;;;;EAMO,uBACN,WACA,SAAwB;AAIxB,UAAM,QAA6B;MAClC;MACA,SAAS,wBAAC,OAAO,QAAQ,EAAO,GAAvB;MACT,SAAS;;AAEV,SAAK,gBAAgB,KAAK,KAAK;AAC/B,UAAM,cAAc,6BAAK;AACxB,YAAM,SAAS,MAAK;AACpB,YAAM,QAAQ,KAAK,gBAAgB,QAAQ,KAAK;AAChD,UAAI,UAAU;AAAI,aAAK,gBAAgB,OAAO,OAAO,CAAC;IACvD,GAJoB;AAMpB,WAAO;MACN,YAAY;;EAEd;EAEQ,wBACP,aACA,OAAiB;AAGjB,QAAI,KAAK,iBAAiB,aAAa,KAAK,GAAG;AAC9C,UAAI,KAAK,qBAAqB,aAAoB,KAAK;AAAG;IAC3D,eAAW,oCAAuB,KAAK,GAAG;AACzC,UAAI,KAAK,2BAA2B,aAAa,KAAK;AAAG;IAC1D;;;UAGC,6BAAW,YAAY,OAAO,KAC3B,KAAK,WAAW,WAAW,6BAAiB;MAC9C;AACD,UAAI,KAAK,uBAAuB,aAAa,KAAK;AAAG;IACtD,eACC,6BAAW,YAAY,OAAO,UAC1B,yCAA4B,KAAK,SACjC,yCAA4B,KAAK,IACpC;AACD,UACC,KAAK,wCAAwC,aAAa,KAAK;AAC9D;IACH,eAAW,gCAAmB,KAAK,GAAG;AAIrC,kBAAY,MAAK;AACjB,WAAK,uBAAuB,WAAW,EAAE,IACxC,YAAY,MAAK,CAAE;AAEpB;IACD;AAEA,SAAK,kBAAkB,aAAa,KAAK;EAC1C;EAEQ,kBACP,aACA,OAAiB;AAEjB,gBAAY,YAAY;MACvB,OAAO,6BAAiB;MACxB,QAAQ,MAAM;KACd;AAED,gBAAY,MAAM,KAAK;EACxB;EAEQ,mBACP,aACA,QAAgB;AAEhB,gBAAY,MAAM,MAAM;EACzB;;EAGQ,qBAAqB,aAAwB;AACpD,UAAM,MAAM,YAAY;AACxB,YAAQ,MAAM;;MAEb,KAAK,cAAc,GAAG;MACtB,KAAK,YAAY,aAAa,4BAAgB;;MAE9C,UAAK,6BAAW,GAAG,KACf,IAAI,mBAAmB;;MAE3B,KAAK,YAAY,QAAQ;AACxB,eAAO;IACT;AAEA,WAAO;EACR;;EAGQ,0BAA0B,QAAc;AAC/C,UAAM,SAAmC;MACxC,MAAM;MACN,SAAS;MACT,MAAM,4BAAgB;;AAEvB,UAAM,UAAoC;MACzC,MAAM;MACN,UAAU,4BAAgB;;MAE1B,OAAO;;AAER,UAAM,2BAAqD;MAC1D,GAAG;MACH,KAAK;;AAGN,SAAK,KAAK,aAAa,CAAC,aAAa,YAAW;AAC/C,YAAM,MAAM,YAAY;AACxB,UAAI,IAAI,UAAS,MAAO;AAAQ,eAAO,EAAE,MAAM,OAAM;AAGrD,aAAO,KAAK,qBAAqB,WAAW,IACzC,YAAY,aAAa,4BAAgB,YACxC,2BACA,UACD;IACJ,CAAC;EACF;;;;;EAMO,mBACN,WACA,WAAmB,+CACnB,YAA6B,4BAAgB,2BAAyB;AAEtE,WAAO,KAAK,aAAa,CAAC,aAAa,YAAW;AACjD,UAAI,UAAU,WAAW,GAAG;AAC3B,eAAO;UACN,MAAM;UACN,SAAS;UACT,MAAM;;MAER,OAAO;AACN,eAAO,EAAE,MAAM,OAAM;MACtB;IACD,CAAC;EACF;;;;;EAMO,6BACN,QACA,WAAmB,oBACnB,YAA6B,4BAAgB,2BAAyB;AAEtE,WAAO,KAAK,mBACX,CAAC,MAAM,EAAE,QAAQ,UAAS,MAAO,QACjC,UACA,SAAS;EAEX;;;;EAKQ,iBAAc;AACrB,SAAK,cAAc;EACpB;;;;EAKQ,mBAAgB;AACvB,SAAK,cAAc;AACnB,SAAK,cAAa;EACnB;EAEQ,aAAa,SAA2B;AAG/C,WAAO,QACL,IAAI,KAAK,OAAO,IAAI,CAAC,UAAU,KAAK,YAAY,OAAO,OAAO,CAAC,CAAC,EAChE,KAAK,kBAAI;EACZ;EAEQ,YACP,OACA,SAA2B;AAE3B,UAAM,aAA4B,CAAA;AAClC,QAAI;AACJ,UAAM,UAAyB,CAAA;AAE/B,UAAM,oBAEM,wBAAC,aAAa,WAAU;AACnC,YAAM,gBAAgB,QAAQ,aAAa,MAAM;AACjD,cAAQ,cAAc,MAAM;QAC3B,KAAK;AACJ,cAAI,WAAW,SAAS;AACvB,uBAAW,KAAK,WAAW;AAI3B,wBAAY,YAAY;cACvB,OAAO,6BAAiB;cACxB,QAAQ;aACR;UACF,OAAO;AACN,yBAAa;UACd;AACA;QACD,KAAK;AACJ,cAAI,cAAc,YAAY,QAAW;AACxC,wBAAY,WAAW,cAAc;UACtC;AACA,cAAI,cAAc,OAAO,QAAW;AACnC,wBAAY,MAAM,cAAc;UACjC;AACA,cAAI,cAAc,OAAO;AACxB,wBAAY,MAAK;UAClB;AACA,cAAI,WAAW;AAAU,yBAAa;AACtC,kBAAQ,KAAK,WAAW;AACxB;QACD,KAAK;AACJ,eAAK,mBAAmB,aAAa,cAAc,OAAO;AAC1D,cAAI,WAAW,SAAS;AACvB,uBAAW,KAAK,WAAW;UAC5B,OAAO;AACN,yBAAa;UACd;AACA;QACD,KAAK;AACJ,eAAK,kBACJ,aACA,IAAI,uBACH,cAAc,SACd,cAAc,MACd,QACA,YAAY,KAAK,CACjB;AAEF,cAAI,WAAW,SAAS;AACvB,uBAAW,KAAK,WAAW;UAC5B,OAAO;AACN,yBAAa;UACd;AACA;MACF;IACD,GAvDY;AAyDZ,eAAW,eAAe,MAAM,cAAc;AAC7C,wBAAkB,aAAa,OAAO;IACvC;AAEA,QAAI,MAAM,oBAAoB;AAC7B,wBAAkB,MAAM,oBAAoB,QAAQ;IACrD;AAGA,UAAM,OAAO,GAAG,YAAY,GAAG,OAAO;AACtC,UAAM,WAAW,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAK,CAAE;AAC7C,UAAM,IAAI,GAAG,QAAQ;AAGrB,eAAW,KAAK,UAAU;AACzB,QAAE,YAAY,EAAE,OAAO,6BAAiB,OAAM,CAAE;IACjD;AAGA,YAAI,6BAAW,YAAY,OAAO,GAAG;AACpC,aAAO,KAAK,cAAa;IAC1B;AACA,WAAO,QAAQ,QAAO;EACvB;;EAGO,oBAAoB,QAAc;AAGxC,eAAW,EAAE,mBAAkB,KAAM,KAAK,QAAQ;AACjD,UAAI,CAAC;AAAoB;AACzB,YAAM,MAAM,mBAAmB,MAAM;AACrC,UAAI,CAAC,CAAC,OAAO,cAAc,GAAG,KAAK,IAAI,UAAS,MAAO,QAAQ;AAE9D,2BAAmB,MAAM,MAAS;MACnC;IACD;EACD;;;;;EAMO,SACN,UACA,SAEC;AAED,QAAI,MAAM,KAAK,aAAa,IAAI,QAAQ;AACxC,QAAI,QAAQ,UAAa,OAAO,SAAS,YAAY,YAAY;AAChE,UAAI;AACH,cAAM,QAAQ,QAAQ,GAAG;MAC1B,QAAQ;MAER;IACD;AACA,WAAO;EACR;;;;;EAMO,SACN,UACA,OACA,SAEC;AAED,QAAI,UAAU,UAAa,OAAO,SAAS,eAAe,YAAY;AACrE,cAAQ,QAAQ,WAAW,KAAK;IACjC;AAEA,QAAI,UAAU,QAAW;AACxB,WAAK,aAAa,OAAO,QAAQ;IAClC,OAAO;AACN,WAAK,aAAa,IAAI,UAAU,KAAK;IACtC;EACD;;;;;EAMO,UACN,QACA,SAEC;AAED,UAAM,MAAyB,CAAA;AAC/B,eAAW,SAAS,KAAK,aAAa,QAAO,GAAI;AAChD,YAAM,MAAM,MAAM,CAAC;AACnB,UAAI,CAAC,IAAI,WAAW,MAAM;AAAG;AAC7B,UAAI,QAAQ,MAAM,CAAC;AACnB,UAAI,UAAU;AAAW;AACzB,UAAI,OAAO,SAAS,YAAY,YAAY;AAC3C,YAAI;AACH,kBAAQ,QAAQ,QAAQ,KAAK;QAC9B,QAAQ;AAEP;QACD;MACD;AACA,UAAI,GAAG,IAAI;IACZ;AACA,WAAO;EACR;EAEQ,WAAW,QAAc;AAChC,eAAW,OAAO,KAAK,aAAa,KAAI,GAAI;AAC3C,UAAI,IAAI,WAAW,MAAM,GAAG;AAC3B,aAAK,aAAa,OAAO,GAAG;MAC7B;IACD;EACD;;;;EAKO,MAAM,mCAAgC;AAC5C,QACC,CAAC,KAAK,eACH,CAAC,KAAK,WAAW,UACjB,CAAC,KAAK,eACR;AACD;IACD;AAEA,QAAI,KAAK,cAAc,QAAQ,GAAG;AAEjC;IACD;AAEA,QAAI;AACH,WAAK,UAAU,MACd,6BACC,uBACC,KAAK,WAAW,MAAM,CAExB,yDAAyD;AAE1D,YAAM,KAAK,WAAW,YAAW;AACjC,WAAK,UAAU,MACd,kDAAkD;IAEpD,SAAS,GAAG;AACX,YAAM,UAAU,gDACf,+BACC,GACA,IAAI,CAEN;AACA,WAAK,KACJ,SACA,IAAI,uBAAW,SAAS,4BAAgB,mBAAmB,CAAC;AAE7D,WAAK,UAAU,MAAM,SAAS,OAAO;IACtC;EACD;EAEQ,wBAAwB,oBAAI,IAAG;;;;;EAKhC,wBACN,MAAgE;AAIhE,QAAI,KAAK,sBAAsB,IAAI,KAAK,EAAE,GAAG;AAC5C,WAAK,sBAAsB,IAAI,KAAK,EAAE,GAAG,MAAK;IAC/C;AAGA,UAAM,kBAAkB,wBACvBC,UACS;AACT,WAAK,sBAAsB,OAAOA,MAAK,EAAE;AACzC,UAAI,CAAC,KAAK,mBAAmBA,KAAI,GAAG;AACnC,aAAKA,MAAK,sBAAqB,EAAG,MAAM,MAAK;QAE7C,CAAC;MACF;IACD,GATwB;AAYxB,QACC,KAAK,WAAW,2BAAe,SAAS,CAAC,KACtC,CAAC,KAAK,mBAAmB,IAAI,GAC/B;AACD,YAAM,iBAAiB,KAAK,SAC3B,yBAAe,eAAe,EAAE;AAKjC,UAAI,mBAAmB,GAAG;AACzB,aAAK,sBAAsB,IAC1B,KAAK,QACL,wBACC,MAAM,gBAAgB,IAAI,GAC1B,KAAK,QAAQ,SAAS,WAAW,EAChC,MAAK,CAAE;MAEX;IACD;EACD;;EAGO,wBACN,cAGA,sBAA+B,OAAK;AAGpC,QAAI;AACJ,YAAI,uCAAqB,YAAY,GAAG;AACvC,YAAM;IACP,OAAO;AACN,YAAM,sBAAsB,KAAK,iCAAgC;AACjE,YAAM,IAAI,oBAAoB;QAC7B,cAAc,KAAK;QACnB,SAAS;OACT;IACF;AACA,QAAI,CAAC,qBAAqB;AACzB,UAAI,UAAU,KAAK,oBAClB,IAAI,OAAO;IAEb;AACA,WAAO,IAAI,QAAQ,oBAAoB,KAAK,oBAAoB,GAAG,CAAC;EACrE;;EAGO,oBAAoB,KAAoB;AAC9C,UAAM,SAAS,IAAI,UAAS;AAG5B,QACC,UAAU,cACP,+BAAkB,MAAM,KACxB,KAAK,aAAa,kBACpB;AACD,aAAO,KAAK,YAAY;IACzB;AAGA,UAAM,+BAA+B,KAAK,aAAa,kBACnD;AACJ,YAAI,uCAAqB,GAAG,GAAG;AAE9B,UAAI,IAAI,kBAAkB,4BAAgB,SAAS;AAClD,eAAO;MACR;AACA,UAAI,IAAI,kBAAkB,4BAAgB,WAAW;AACpD,eAAO,+BAA+B;MACvC;AACA,aAAO,+BAA+B;IACvC,OAAO;AAEN,YAAM,8BAA8B,+BACjC;AAGH,UAAI,IAAI,kBAAkB,4BAAgB,KAAK;AAC9C,YAAI,IAAI,kBAAkB,4BAAgB,SAAS;AAClD,iBAAO;QACR;AACA,YAAI,IAAI,kBAAkB,4BAAgB,WAAW;AACpD,iBAAO,8BAA8B;QACtC;MACD;AACA,aAAO,8BAA8B;IACtC;EACD;EAEO,MAAM,wBACZ,KAAoB;AAEpB,UAAM,eAAe,MAAM,IAAI,YAC9B,KAAK,mBAAkB,CAAE;AAE1B,WAAO,aAAa,SAAS,KAAK,oBAAoB,GAAG;EAC1D;;EAGO,iBAAiB,KAAY;AACnC,UAAM,OAAO,KAAK,WAAW,GAAG;AAEhC;;MAEC,IAAI,qBAEA,MAAM,cAAc,QAAQ,iBAE5B,KAAK,SAAS,SAAS;;EAE7B;;EAGO,mCAAgC;AAItC,WAAO,KAAK,aAAa,oBACvB,2BAAa,cAAc,IAE1B,yCACA;EACJ;;EAGO,kCAA+B;AAIrC,WAAO,KAAK,aAAa,oBACvB,2BAAa,uBAAuB,IAEnC,kDACA;EACJ;;;;;EAMO,MAAM,sBACZ,SAAkB,OAAK;AAEvB,SAAK,YAAW;AAEhB,QAAI;AACH,UAAI,CAAC,QAAQ;AACZ,aAAK,UAAU,MAAM,uCAAuC;MAC7D;AACA,YAAM,MAAM,UAAM,2CAAsB,KAAK,aAAa;AAC1D,UAAI,KAAK;AACR,YAAI,CAAC,QAAQ;AACZ,eAAK,UAAU,MACd,mCAAmC,GAAG,EAAE;QAE1C;MACD,OAAO;AACN,YAAI,CAAC,QAAQ;AACZ,eAAK,UAAU,MACd,sCAAsC;QAExC;MACD;AACA,aAAO;IACR,SAAS,GAAG;AACX,WAAK,UAAU,UAAM,+BAAgB,CAAC,GAAG,OAAO;IACjD;EACD;EAEQ;;;;;;;EAQD,MAAM,sBAAmB;AAC/B,SAAK,YAAW;AAEhB,QAAI,KAAK,6BAA6B;AACrC,aAAO,KAAK;IACb;AACA,SAAK,8BAA8B,KAAK,4BAA2B;AAEnE,QAAI;AACH,aAAO,MAAM,KAAK;IACnB;AACC,WAAK,8BAA8B;IACpC;EACD;EAEQ,MAAM,8BAA2B;AACxC,UAAM,aAAa,MAAM,KAAK,sBAAsB,IAAI;AACxD,QAAI,CAAC;AAAY,aAAO;AAExB,UAAM,eAAe,KAAK,cAAc;AACxC,QAAI,CAAC,KAAK,cAAc,qBAAqB,CAAC,cAAc;AAC3D,WAAK,UAAU,MACd,gFACA,OAAO;AAER,aAAO;IACR;AAEA,SAAK,UAAU,MACd,sBAAsB,UAAU,yBAAyB;AAE1D,QAAI;AACH,gBAAM,yCACL,KAAK,SAAS,IACd,YACA,EAAE,WAAW,aAAY,CAAE;IAE7B,SAAS,GAAG;AACX,WAAK,UAAU,UAAM,+BAAgB,CAAC,GAAG,OAAO;AAChD,aAAO;IACR;AACA,SAAK,UAAU,MACd,uCAAuC,UAAU,iBAAiB;AAInE,UAAM,KAAK,cAAc,QAAO;AAGhC,QAAI,KAAK,aAAa;AACrB,iBAAW,QAAQ,KAAK,YAAY,MAAM,OAAM,GAAI;AACnD,YAAI,KAAK;AAAO,gBAAM,KAAK,kBAAkB,EAAC;MAC/C;IACD;AAEA,WAAO;EACR;;EAIQ,+BAAwC;;;;EAKzC,gCAA6B;AACnC,WAAO,KAAK;EACb;;;;;;;;;EAUO,MAAM,kBACZ,MAAgB;AAGhB,QAAI,KAAK,aAAa,iCAAgC,GAAI;AACzD,YAAM,UACL;AACD,WAAK,cAAc,MAAM,SAAS,OAAO;AACzC,YAAM,IAAI,uBAAW,SAAS,4BAAgB,eAAe;IAC9D;AAGA,QAAI,KAAK,8BAA6B,GAAI;AACzC,YAAM,UACL;AACD,WAAK,cAAc,MAAM,SAAS,OAAO;AACzC,YAAM,IAAI,uBAAW,SAAS,4BAAgB,eAAe;IAC9D;AAGA,QAAI,KAAK,SAAS,6BAAW,YAAY;AACxC,aAAO,KAAK,qBAAqB,IAAI;IACtC,WAAW,KAAK,SAAS,6BAAW,WAAW;AAC9C,UAAI,KAAK,WAAW,cAAc,KAAK,GAAG;AAEzC,eAAO,KAAK,qBAAqB,IAAI;MACtC,WACC,KAAK,WAAW,cAAc,QAAQ,KACnC,KAAK,WAAW,wBAChB,SAAS,2BAAa,iBAAiB,GACzC;AAED,cAAM,aAAa,MAAM,KAAK,qBAAqB,IAAI;AACvD,YAAI,WAAW,SAAS;AAEvB,eAAK,UAAU,MACd,kDAAkD;AAEnD,gBAAM,KAAK,oBAAmB;QAC/B;AACA,eAAO;MACR;IACD,WAAW,KAAK,SAAS,6BAAW,KAAK;AAGxC,aAAO,KAAK,qBAAqB,IAAI;IACtC;AAEA,UAAM,IAAI,uBACT,4DACA,4BAAgB,uBAAuB;EAEzC;EAEQ,MAAM,qBACb,MAAgB;AAEhB,SAAK,+BAA+B;AACpC,QAAI,iBAAiB;AACrB,QAAI;AACH,WAAK,cAAc,MAAM,2BAA2B;AAEpD,YAAM,YAAY,MAAM,KAAK,WAAW,sBAAqB;AAC7D,UAAI,CAAC,WAAW;AACf,aAAK,cAAc,MAClB,wEACA,OAAO;AAGR,cAAMD,UAAkC;UACvC,SAAS;UACT,QAAQ,sCAAwB;;AAEjC,aAAK,KAAK,4BAA4BA,OAAM;AAC5C,eAAOA;MACR;AAGA,YAAM,KAAK,WAAW,SAAS,KAAK;AACpC,uBAAiB;AAGjB,YAAM,aAAa;AACnB,YAAM,eAAe,KAAK,KAAK,KAAK,SAAS,UAAU;AACvD,eAAS,WAAW,GAAG,WAAW,cAAc,YAAY;AAC3D,cAAM,eAAe,KAAK,SACzB,WAAW,aACV,WAAW,KAAK,UAAU;AAE5B,cAAM,KAAK,WAAW,uBACrB,WAAW,YACX,YAAY;AAIb,cAAM,WAAsC;UAC3C,eAAe;UACf,gBAAgB;UAChB,cAAU,qBAAS,WAAW,eAAgB,KAAK,CAAC;;AAErD,aAAK,KAAK,4BAA4B,QAAQ;MAC/C;AAGA,YAAM,aAAa,MAAM,KAAK,WAC5B,8BAA6B;AAC/B,UAAI,CAAC,YAAY;AAChB,aAAK,cAAc,MAClB,oDACA,OAAO;AAGR,cAAMA,UAAkC;UACvC,SAAS;UACT,QAAQ,sCAAwB;;AAEjC,aAAK,KAAK,4BAA4BA,OAAM;AAC5C,eAAOA;MACR;AAEA,WAAK,KAAK,4BAA4B;QACrC,eAAe;QACf,gBAAgB;QAChB,UAAU;OACV;AAGD,YAAM,KAAK,WAAW,6BAA4B;AAElD,WAAK,cAAc,MAAM,2BAA2B;AAEpD,YAAM,SAAkC;QACvC,SAAS;QACT,QAAQ,sCAAwB;;AAEjC,WAAK,KAAK,4BAA4B,MAAM;AAC5C,aAAO;IACR;AACC,WAAK,+BAA+B;AACpC,UAAI;AAAgB,cAAM,KAAK,WAAW,SAAS,IAAI;IACxD;EACD;EAEQ,MAAM,qBACb,MAAgB;AAEhB,SAAK,+BAA+B;AAEpC,QAAI;AACH,YAAM,KAAK,gBAAe;AAG1B,WAAK,cAAc,MAAM,2BAA2B;AACpD,YAAM,KAAK,WAAW,YAAW;AAGjC,UAAI;AACH,cAAM,KAAK,uBACV,CAAC,MACA,EAAE,SAAS,kCAAoB,WAC5B,EAAE,YAAY,gBAClB,GAAI;AAEL,cAAM,KAAK,uBACV,CAAC,MACA,EAAE,SAAS,kCAAoB,eAC5B,EAAE,YAAY,mCAAqB,GACvC,GAAI;MAEN,QAAQ;AACP,aAAK,cAAc,MAClB,yEACA,OAAO;AAER,cAAMA,UAAkC;UACvC,SAAS;UACT,QAAQ,sCAAwB;;AAEjC,aAAK,KAAK,4BAA4BA,OAAM;AAC5C,eAAOA;MACR;AAEA,YAAM,aAAa;AACnB,UAAI,KAAK,SAAS,eAAe,GAAG;AAEnC,eAAO,oBAAM,OAAO;UACnB;UACA,IAAI,oBAAM,aAAc,KAAK,SAAS,UAAW,EAAE,KAClD,GAAI;SAEL;MACF;AACA,YAAM,eAAe,KAAK,KAAK,KAAK,SAAS,UAAU;AAEvD,UAAI,UAAU;AAEd,eAAU,UACL,WAAW,GACf,YAAY,cACZ,YACC;AACD,cAAM,eAAe,KAAK,UACxB,WAAW,KAAK,YACjB,WAAW,UAAU;AAGtB,cAAO,UAAS,QAAQ,GAAG,QAAQ,GAAG,SAAS;AAC9C,gBAAM,KAAK,WAAW,eACrB,UACA,YAAY;AAEb,cAAIA;AAGJ,cAAI;AACH,YAAAA,UAAS,MAAM,KAAK,uBACnB,CAAC,MAAM,EAAE,SAAS,kCAAoB,aACtC,GAAI;UAEN,QAAQ;AACP,iBAAK,cAAc,MAClB,gFACA,OAAO;AAGR,kBAAMA,UAAkC;cACvC,SAAS;cACT,QAAQ,sCAAwB;;AAEjC,iBAAK,KAAK,4BAA4BA,OAAM;AAC5C,mBAAOA;UACR;AAEA,kBAAQA,QAAO,SAAS;YACvB,KAAK,mCAAqB,KAAK;AAE9B,oBAAM,WAAsC;gBAC3C,eAAe;gBACf,gBAAgB;gBAChB,cAAU,qBACR,WAAW,eAAgB,KAC5B,CAAC;;AAGH,mBAAK,KAAK,4BAA4B,QAAQ;AAC9C,uBAAS;YACV;YACA,KAAK,mCAAqB;AAEzB,uBAAS;YACV,KAAK,mCAAqB;AAEzB,wBAAU;AACV,oBAAM;UACR;QACD;AAEA,aAAK,cAAc,MAClB,qDACA,OAAO;AAER,cAAMA,UAAkC;UACvC,SAAS;UACT,QAAQ,sCAAwB;;AAEjC,aAAK,KAAK,4BAA4BA,OAAM;AAC5C,eAAOA;MACR;AAEA,UAAI,SAAS;AAEZ,cAAM,QAAQ,MAAM,KAAK,uBAGxB,CAAC,MACA,EAAE,SAAS,kCAAoB,WAC5B,EAAE,QAAQ,SAAS,UAAU,GACjC,GAAI,EAEH,MAAM,MAAM,MAAS;AAGvB,cAAM,KAAK,uBACV,CAAC,MAAM,EAAE,SAAS,kCAAoB,MACtC,GAAI,EAEH,MAAM,MAAM,MAAS;AAEvB,YAAI,UAAU;AACd,YAAI,OAAO;AACV,qBAAW,IAAI,MAAM,OAAO;QAE7B;AACA,aAAK,cAAc,MAAM,SAAS,OAAO;AAEzC,cAAMA,UAAkC;UACvC,SAAS;UACT,QAAQ,sCAAwB;;AAEjC,aAAK,KAAK,4BAA4BA,OAAM;AAC5C,eAAOA;MACR,OAAO;AAEN,cAAM,KAAK,WAAW,aAAY;AAClC,YAAI;AAKH,gBAAM,QAAQ,IAAI;YACjB,KAAK,uBACJ,CAAC,MACA,EAAE,SAAS,kCAAoB,WAC5B,EAAE,QAAQ,SAAS,iBAAiB,GACxC,GAAI;YAGL,KAAK,uBACJ,CAAC,MAAM,EAAE,SAAS,kCAAoB,MACtC,GAAI;WAEL;QACF,QAAQ;AACP,eAAK,cAAc,MAClB,8EACA,OAAO;AAER,gBAAMA,UAAkC;YACvC,SAAS;YACT,QAAQ,sCAAwB;;AAEjC,eAAK,KAAK,4BAA4BA,OAAM;AAC5C,iBAAOA;QACR;MACD;AAEA,WAAK,cAAc,MAAM,2BAA2B;AAEpD,YAAM,SAAkC;QACvC,SAAS;QACT,QAAQ,sCAAwB;;AAEjC,WAAK,KAAK,4BAA4B,MAAM;AAC5C,aAAO;IACR;AACC,YAAM,KAAK,gBAAe;AAC1B,WAAK,+BAA+B;IACrC;EACD;;EAIQ,sBAA+B;EAC/B;EAED,MAAM,kBAAe;AAC3B,QAAI,KAAK;AAAa;AAEtB,SAAK,cAAc,MAAM,wBAAwB;AACjD,SAAK,sBAAsB;AAC3B,QAAI;AACH,UAAI,KAAK,SAAS,6BAAW,WAAW;AACvC,cAAM,KAAK,6BAA4B;MACxC,WAAW,KAAK,SAAS,6BAAW,KAAK;AACxC,cAAM,KAAK,uBAAsB;MAClC;AAGA,WAAK,8BAA0B,+CAAqB;AACpD,YAAM,UAAU,MAAM,QAAQ,KAAK;QAClC,KAAK,wBAAwB,KAAK,MAAM,IAAI;YAC5C,mBAAK,KAAM,IAAI,EAAE,KAAK,MAAM,KAAK;OACjC;AACD,UAAI,SAAS;AACZ,aAAK,cAAc,MAAM,oBAAoB;MAC9C,OAAO;AACN,cAAM,IAAI,uBACT,8BACA,4BAAgB,kBAAkB;MAEpC;IACD;AACC,WAAK,sBAAsB;IAC5B;EACD;EAEQ,MAAM,+BAA4B;AAEzC,UAAM,MAAM,KAAK,mBAAkB;AAEnC,UAAM,KAAK,kBAAiB;AAI5B,UAAM,MAAM,IAAI,wCAAsB;AACtC,UAAM,UAAU,KAAK,YAAY,MAAM,IAAI,UAAU,GAAG,CAAC;AACzD,SAAK,OAAQ,OAAO,8BAAgB;AACpC,UAAM;EACP;EAEQ,MAAM,yBAAsB;AAEnC,UAAM,KAAK,IAAI,eAAe,YAAY;AAE1C,SAAK,OAAQ,OAAO;EACrB;EAEQ,0BAAuB;AAC9B,UAAM,UAAU,KAAK,WAAW,eAAc;AAG9C,SAAK,OAAQ,OAAO;AACpB,SAAK,cAAc;AACnB,WAAO;EACR;;;;EAKO,MAAM,kBAAe;AAC3B,SAAK,cAAc,MAAM,uBAAuB;AAChD,UAAM,KAAK,wBAAuB;AAKlC,UAAM,cAAc,MAAM,KAAK,eAC9B,CAAC,QAAQ,IAAI,iBAAiB,2BAAa,kBAC3C,GAAI,EAEH,KAAK,MAAM,IAAa,EACxB,MAAM,MAAM,KAAc;AAE5B,QAAI,aAAa;AAEhB,YAAM,KAAK,6BAA4B;AACvC;IACD,WAAW,KAAK,SAAS,6BAAW,KAAK;AACxC,YAAM,KAAK,eAAc;AACzB;IACD;AAGA,SAAK,iBAAgB;AAErB,UAAM,KAAK,gBAAe;EAC3B;EAEQ,4BAA4B,MAAqB;AACxD,YAAQ,KAAK,MAAM;MAClB,KAAK,kCAAoB,SAAS;AACjC,aAAK,cAAc,MAClB,gBAAgB,KAAK,OAAO,IAC5B,SAAS;AAEV;MACD;MACA,KAAK,kCAAoB,aAAa;AACrC,YAAI,KAAK,YAAY,mCAAqB,GAAG;AAC5C,eAAK,cAAc,MAClB,kCACA,SAAS;QAEX;AACA;MACD;IACD;AAGA,eAAW,SAAS,KAAK,yBAAyB;AACjD,UAAI,MAAM,UAAU,IAAI,GAAG;AAE1B,cAAM,QAAQ,IAAI;AAClB;MACD;IACD;AAEA,QAAI,CAAC,KAAK,eAAe,KAAK,SAAS,kCAAoB,MAAM;AAEhE,WAAK,aAAa,QAAO;AACzB,WAAK,cAAc;AACnB,WAAK,OAAO;AAEZ,WAAK,cAAc,MAClB,wBAAwB,KAAK,OAAO,IACpC,SAAS;AAGV,WAAK,cAAc,IAAI,6BACtB,KAAK,YAAY,KAAK,IAAI,GAC1B,KAAK,SACL,KAAK,OAAO;AAEb,UAAI,KAAK,yBAAyB;AACjC,aAAK,wBAAwB,QAAO;AACpC,aAAK,0BAA0B;MAChC;IACD;EACD;;;;;;EAOO,uBACN,WACA,SAAe;AAEf,WAAO,IAAI,QAAW,CAAC,SAAS,WAAU;AACzC,YAAM,cAAU,+CAAqB;AACrC,YAAM,QAAqC;QAC1C;QACA,SAAS,wBAAC,UAAU,QAAQ,QAAQ,KAAK,GAAhC;QACT,SAAS;;AAEV,WAAK,wBAAwB,KAAK,KAAK;AACvC,YAAM,cAAc,6BAAK;AACxB,cAAM,SAAS,MAAK;AACpB,cAAM,QAAQ,KAAK,wBAAwB,QAAQ,KAAK;AACxD,YAAI,UAAU;AAAI,eAAK,wBAAwB,OAAO,OAAO,CAAC;MAC/D,GAJoB;AAMpB,YAAM,cAAU,wBAAS,MAAK;AAC7B,oBAAW;AACX,eACC,IAAI,uBACH,2DACA,4BAAgB,kBAAkB,CAClC;MAEH,GAAG,OAAO;AAEV,WAAK,QAAQ,KAAK,CAAC,UAAS;AAC3B,oBAAW;AACX,gBAAQ,KAAU;MACnB,CAAC;IACF,CAAC;EACF;;;;;;EAOO,gBACN,WACA,SAAe;AAEf,WAAO,IAAI,QAAW,CAAC,SAAS,WAAU;AACzC,YAAM,cAAU,+CAAqB;AACrC,YAAM,QAA8B;QACnC;QACA,SAAS,wBAAC,UAAU,QAAQ,QAAQ,KAAK,GAAhC;QACT,SAAS;;AAEV,WAAK,iBAAiB,KAAK,KAAK;AAChC,YAAM,cAAc,6BAAK;AACxB,cAAM,SAAS,MAAK;AACpB,cAAM,QAAQ,KAAK,iBAAiB,QAAQ,KAAK;AACjD,YAAI,UAAU;AAAI,eAAK,iBAAiB,OAAO,OAAO,CAAC;MACxD,GAJoB;AAMpB,YAAM,cAAU,wBAAS,MAAK;AAC7B,oBAAW;AACX,eACC,IAAI,uBACH,2DACA,4BAAgB,kBAAkB,CAClC;MAEH,GAAG,OAAO;AAEV,WAAK,QAAQ,KAAK,CAAC,UAAS;AAC3B,oBAAW;AACX,gBAAQ,KAAU;MACnB,CAAC;IACF,CAAC;EACF;EAEQ,MAAM,qBAAqB,MAAc;AAEhD,eAAW,SAAS,KAAK,kBAAkB;AAC1C,UAAI,MAAM,UAAU,IAAI,GAAG;AAE1B,cAAM,QAAQ,IAAI;AAClB;MACD;IACD;AAEA,QAAI,CAAC,KAAK,QAAQ,KAAK,SAAS,2BAAa,QAAQ;AAEpD,WAAK,aAAa,QAAO;AACzB,WAAK,cAAc;AACnB,WAAK,cAAc;AAEnB,WAAK,OAAO,IAAI,iCACf,KAAK,YAAY,KAAK,IAAI,GAC1B,CAAC,YACA,KAAK,gBACJ,CAAC,MAAM,EAAE,SAAS,2BAAa,SAC/B,WAAW,GAAI,EAEd,KAAK,CAAC,MACL,EACC,OAAO,EAET,MAAM,MAAM,MAAS,CAAC;AAG1B,UAAI,CAAE,MAAM,KAAK,eAAc,GAAK;AACnC,cAAM,IAAI,uBACT,2CACA,4BAAgB,aAAa;MAE/B;IACD;EACD;EAEQ;EACA,8BAA8B;EAE9B,sBAAsB,MAAa;AAC1C,QAAI,CAAC,KAAK;AAAO;AACjB,QACC,KAAK,WAAW,oBAAoB,2BAAa,iBAAiB,GACjE;AAED,UAAI,MAAM;AACT,cAAM,UAAU,KAAK;;UAEpB;;UAEA,OAAU,KAAK,IAAG,IAAK,KAAK;QAA4B;AAEzD,aAAK,8BAA0B,wBAAS,YAAW;AAElD,cAAI,CAAC,KAAK;AAAO;AAEjB,eAAK,8BAA8B,KAAK,IAAG;AAC3C,cAAI;AACH,kBAAM,KAAK,WAAW,kBAAiB;UACxC,QAAQ;UAER;QACD,GAAG,OAAO,EAAE,MAAK;MAClB,OAAO;AACN,aAAK,yBAAyB,MAAK;AACnC,aAAK,0BAA0B;MAChC;IACD;EACD;EAEQ;;;;;EAUD,MAAM,mBACZ,YACA,YACA,YAAkB;AAElB,QAAI,KAAK,4BAA4B;AAEpC,mBAAa,KAAK,2BAA2B,OAAO;AACpD,WAAK,6BAA6B;IACnC;AAEA,QAAI,aAAa;AAAG,aAAO;AAE3B,UAAM,UAAM,+CAAqB;AAEjC,UAAM,UAAU;MACf;MACA,oBAAoB;;MAEpB,SAAS;;AAEV,SAAK,6BAA6B;AAGlC,UAAM,WAAW;AAEjB,UAAM,YAAY,mCAAW;AAC5B,YAAM,SAAS,MAAM,KAAK,cAAc,YAAY,UAAU;AAC9D,UAAI,WAAW,2BAAe,IAAI;AACjC,gBAAQ;MACT;AACA;AAEA,UAAI,aAAa,GAAG;AACnB,gBAAQ,UAAU,WAAW,WAAW,QAAQ;MACjD,OAAO;AACN,gBAAQ,UAAU;AAClB,YAAI,QAAQ,QAAQ,kBAAkB;MACvC;IACD,GAbkB;AAelB,YAAQ,UAAU,WAAW,WAAW,QAAQ;AAEhD,WAAO;EACR;;EAGO,MAAM,cACZ,QACA,YAAsB;AAEtB,UAAM,SAAS,MAAM,KAAK,YAGzB,IAAI,sCAAqB;MACxB,YAAY;MACZ;KACA,CAAC;AAGH,QAAI,kBAAkB,8CAA6B;AAClD,aAAO,OAAO;IACf;EACD;;;;;EAMO,wBAAqB;AAK3B,QAAI,KAAK,4BAA4B;AACpC,aAAO;QACN,YAAY,CAAC,CAAC,KAAK,2BAA2B;QAC9C,YAAY,KAAK,2BAA2B;QAC5C,oBACC,KAAK,2BAA2B;;IAEnC;EACD;;;;;EAMO,UAAU,QAAc;AAC9B,SAAK,oBAAoB,MAAM,GAAG,YAAY,MAAM;EACrD;;;;;EAMO,gBAAa;AACnB,eAAW,UAAU,KAAK,WAAW,MAAM,KAAI,GAAI;AAClD,WAAK,UAAU,MAAM;IACtB;EACD;;",
4
+ "sourcesContent": ["import type { JsonlDBOptions } from \"@alcalzone/jsonl-db\";\nimport {\n\ttype CCAPIHost,\n\ttype CCEncodingContext,\n\ttype CCParsingContext,\n\tCRC16CC,\n\tCRC16CCCommandEncapsulation,\n\tCommandClass,\n\ttype FirmwareUpdateResult,\n\tInclusionControllerCCInitiate,\n\tInclusionControllerStep,\n\ttype InterviewContext,\n\ttype InterviewOptions,\n\tInvalidCC,\n\tKEXFailType,\n\tMultiChannelCC,\n\tNoOperationCC,\n\ttype PersistValuesContext,\n\ttype Powerlevel,\n\ttype RefreshValueTimeouts,\n\ttype RefreshValuesContext,\n\ttype SchedulePollOptions,\n\tSecurity2CC,\n\tSecurity2CCCommandsSupportedGet,\n\tSecurity2CCCommandsSupportedReport,\n\tSecurity2CCMessageEncapsulation,\n\tSecurity2CCNonceGet,\n\tSecurity2CCNonceReport,\n\tSecurity2Command,\n\tSecurityCC,\n\tSecurityCCCommandEncapsulation,\n\tSecurityCCCommandEncapsulationNonceGet,\n\tSecurityCCCommandsSupportedGet,\n\tSecurityCCCommandsSupportedReport,\n\tSecurityCCNonceGet,\n\tSecurityCCNonceReport,\n\tSecurityCommand,\n\tSupervisionCC,\n\ttype SupervisionCCGet,\n\tSupervisionCCReport,\n\tTransportServiceCC,\n\tTransportServiceCCFirstSegment,\n\tTransportServiceCCSegmentComplete,\n\tTransportServiceCCSegmentRequest,\n\tTransportServiceCCSegmentWait,\n\ttype TransportServiceCCSubsequentSegment,\n\tTransportServiceTimeouts,\n\ttype UserPreferences,\n\tVersionCommand,\n\tWakeUpCCNoMoreInformation,\n\tWakeUpCCValues,\n\ttype ZWaveProtocolCC,\n\tgetImplementedVersion,\n\tisEncapsulatingCommandClass,\n\tisMultiEncapsulatingCommandClass,\n\tisTransportServiceEncapsulation,\n\tregisterCCs,\n} from \"@zwave-js/cc\";\nimport { ConfigManager, type DeviceConfig } from \"@zwave-js/config\";\nimport {\n\ttype CCId,\n\tCommandClasses,\n\tControllerLogger,\n\tControllerRole,\n\tControllerStatus,\n\tDuration,\n\tEncapsulationFlags,\n\ttype HostIDs,\n\ttype LogConfig,\n\ttype LogContainer,\n\ttype LogNodeOptions,\n\tMAX_SUPERVISION_SESSION_ID,\n\tMAX_TRANSPORT_SERVICE_SESSION_ID,\n\tMPANState,\n\ttype MaybeNotKnown,\n\tMessagePriority,\n\ttype MessageRecord,\n\ttype MulticastDestination,\n\tNUM_NODEMASK_BYTES,\n\tNodeIDType,\n\tRFRegion,\n\tSPANState,\n\tSecurityClass,\n\tSecurityManager,\n\tSecurityManager2,\n\ttype SendCommandOptions,\n\ttype SendCommandReturnType,\n\ttype SendMessageOptions,\n\ttype SinglecastCC,\n\ttype SupervisionResult,\n\tSupervisionStatus,\n\ttype SupervisionUpdateHandler,\n\tTransactionState,\n\tTransmitOptions,\n\tTransmitStatus,\n\ttype ValueDB,\n\ttype ValueID,\n\ttype ValueMetadata,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tallCCs,\n\tdeserializeCacheValue,\n\tencapsulationCCs,\n\tgenerateECDHKeyPair,\n\tgetCCName,\n\tisEncapsulationCC,\n\tisLongRangeNodeId,\n\tisMissingControllerACK,\n\tisMissingControllerCallback,\n\tisMissingControllerResponse,\n\tisZWaveError,\n\tkeyPairFromRawECDHPrivateKey,\n\tmessageRecordToLines,\n\trandomBytes,\n\tsecurityClassIsS2,\n\tsecurityClassOrder,\n\tserializeCacheValue,\n\tstripUndefined,\n\ttimespan,\n\twasControllerReset,\n} from \"@zwave-js/core\";\nimport {\n\ttype BootloaderChunk,\n\tBootloaderChunkType,\n\ttype CLIChunk,\n\tCLIChunkType,\n\ttype EnumeratedPort,\n\tFunctionType,\n\ttype HasNodeId,\n\tMessage,\n\ttype MessageEncodingContext,\n\tMessageHeaders,\n\ttype MessageParsingContext,\n\tMessageType,\n\ttype SuccessIndicator,\n\tXModemMessageHeaders,\n\ttype ZWaveSerialBindingFactory,\n\tZWaveSerialFrameType,\n\tZWaveSerialMode,\n\ttype ZWaveSerialPortImplementation,\n\ttype ZWaveSerialStream,\n\tZWaveSerialStreamFactory,\n\tgetDefaultPriority,\n\thasNodeId,\n\tisSuccessIndicator,\n\tisZWaveSerialBindingFactory,\n\tisZWaveSerialPortImplementation,\n\twrapLegacySerialBinding,\n} from \"@zwave-js/serial\";\nimport {\n\tApplicationUpdateRequest,\n\ttype CommandRequest,\n\ttype ContainsCC,\n\tEnterBootloaderRequest,\n\tGetControllerVersionRequest,\n\tMAX_SEND_ATTEMPTS,\n\tSendDataAbort,\n\tSendDataBridgeRequest,\n\ttype SendDataMessage,\n\tSendDataMulticastBridgeRequest,\n\tSendDataMulticastRequest,\n\tSendDataRequest,\n\tSendTestFrameRequest,\n\tSendTestFrameTransmitReport,\n\tSerialAPIStartedRequest,\n\tSerialAPIWakeUpReason,\n\tSoftResetRequest,\n\tcontainsCC,\n\tcontainsSerializedCC,\n\thasTXReport,\n\tisAnySendDataResponse,\n\tisCommandRequest,\n\tisSendData,\n\tisSendDataSinglecast,\n\tisSendDataTransmitReport,\n\tisTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tAsyncQueue,\n\tBytes,\n\ttype Interval,\n\ttype Timer,\n\tTypedEventTarget,\n\tareUint8ArraysEqual,\n\tbuffer2hex,\n\tcloneDeep,\n\tcreateWrappingCounter,\n\tgetErrorMessage,\n\tgetenv,\n\tisAbortError,\n\tisUint8Array,\n\tmergeDeep,\n\tnoop,\n\tnum2hex,\n\tpick,\n\tsetInterval,\n\tsetTimer,\n} from \"@zwave-js/shared\";\nimport type {\n\tDatabase,\n\tKeyPair,\n\tReadFile,\n\tReadFileSystemInfo,\n} from \"@zwave-js/shared/bindings\";\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 { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport path from \"pathe\";\nimport { PACKAGE_NAME, PACKAGE_VERSION } from \"../_version.js\";\nimport { ZWaveController } from \"../controller/Controller.js\";\nimport {\n\ttype FoundNode,\n\tInclusionState,\n\tRemoveNodeReason,\n} from \"../controller/Inclusion.js\";\nimport { determineNIF } from \"../controller/NodeInformationFrame.js\";\nimport { DriverLogger } from \"../log/Driver.js\";\nimport type { Endpoint } from \"../node/Endpoint.js\";\nimport type { ZWaveNode } from \"../node/Node.js\";\nimport {\n\tInterviewStage,\n\tNodeStatus,\n\ttype ZWaveNodeEventCallbacks,\n\ttype ZWaveNotificationCallback,\n\tzWaveNodeEvents,\n} from \"../node/_Types.js\";\nimport type { ZWaveNodeBase } from \"../node/mixins/00_Base.js\";\nimport type { NodeWakeup } from \"../node/mixins/30_Wakeup.js\";\nimport type { NodeValues } from \"../node/mixins/40_Values.js\";\nimport type { NodeSchedulePoll } from \"../node/mixins/60_ScheduledPoll.js\";\nimport { reportMissingDeviceConfig } from \"../telemetry/deviceConfig.js\";\nimport {\n\ttype AppInfo,\n\tcompileStatistics,\n\tsendStatistics,\n} from \"../telemetry/statistics.js\";\nimport { Bootloader } from \"./Bootloader.js\";\nimport { DriverMode } from \"./DriverMode.js\";\nimport { EndDeviceCLI } from \"./EndDeviceCLI.js\";\nimport { createMessageGenerator } from \"./MessageGenerators.js\";\nimport {\n\tcacheKeys,\n\tdeserializeNetworkCacheValue,\n\tmigrateLegacyNetworkCache,\n\tserializeNetworkCacheValue,\n} from \"./NetworkCache.js\";\nimport { type SerialAPIQueueItem, TransactionQueue } from \"./Queue.js\";\nimport {\n\ttype SerialAPICommandMachineInput,\n\tcreateSerialAPICommandMachine,\n} from \"./SerialAPICommandMachine.js\";\nimport {\n\ttype TransactionReducer,\n\ttype TransactionReducerResult,\n\tcreateMessageDroppedUnexpectedError,\n\tserialAPICommandErrorToZWaveError,\n} from \"./StateMachineShared.js\";\nimport { TaskScheduler } from \"./Task.js\";\nimport { throttlePresets } from \"./ThrottlePresets.js\";\nimport { Transaction } from \"./Transaction.js\";\nimport {\n\ttype TransportServiceRXMachine,\n\ttype TransportServiceRXMachineInput,\n\tcreateTransportServiceRXMachine,\n} from \"./TransportServiceMachine.js\";\nimport { checkForConfigUpdates, installConfigUpdate } from \"./UpdateConfig.js\";\nimport { mergeUserAgent, userAgentComponentsToString } from \"./UserAgent.js\";\nimport type {\n\tEditableZWaveOptions,\n\tPartialZWaveOptions,\n\tZWaveOptions,\n} from \"./ZWaveOptions.js\";\nimport {\n\ttype OTWFirmwareUpdateProgress,\n\ttype OTWFirmwareUpdateResult,\n\tOTWFirmwareUpdateStatus,\n} from \"./_Types.js\";\nimport { discoverRemoteSerialPorts } from \"./mDNSDiscovery.js\";\n\n// Force-load all Command Classes:\nregisterCCs();\n\nexport const libVersion: string = PACKAGE_VERSION;\nexport const libName: string = PACKAGE_NAME;\n\n// This is made with cfonts:\nconst libNameString = `\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\n\u255A\u2550\u2550\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\n \u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\n \u2588\u2588\u2588\u2554\u255D \u255A\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588 \u2588\u2588\u2551 \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\n\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\n`;\n\nconst defaultOptions: ZWaveOptions = {\n\ttimeouts: {\n\t\tack: 1000,\n\t\tbyte: 150,\n\t\t// Ideally we'd want to have this as low as possible, but some\n\t\t// 500 series controllers can take several seconds to respond sometimes.\n\t\tresponse: 10000,\n\t\treport: 1000, // ReportTime timeout SHOULD be set to CommandTime + 1 second\n\t\tnonce: 5000,\n\t\tsendDataAbort: 20000, // If a controller takes over 15s to reach a node, it's probably not going to happen\n\t\tsendDataCallback: 30000, // INS13954 defines this to be 65000 ms, but waiting that long causes issues with reporting devices\n\t\tsendToSleep: 250, // The default should be enough time for applications to react to devices waking up\n\t\tretryJammed: 1000,\n\t\trefreshValue: 5000, // Default should handle most slow devices until we have a better solution\n\t\trefreshValueAfterTransition: 1000, // To account for delays in the device\n\t\tserialAPIStarted: 5000,\n\t},\n\tattempts: {\n\t\topenSerialPort: 10,\n\t\tcontroller: 3,\n\t\tsendData: 3,\n\t\tsendDataJammed: 5,\n\t\tnodeInterview: 5,\n\t},\n\tdisableOptimisticValueUpdate: false,\n\tfeatures: {\n\t\t// By default enable soft reset unless the env variable is set\n\t\tsoftReset: !getenv(\"ZWAVEJS_DISABLE_SOFT_RESET\"),\n\t\t// By default enable the unresponsive controller recovery unless the env variable is set\n\t\tunresponsiveControllerRecovery: !getenv(\n\t\t\t\"ZWAVEJS_DISABLE_UNRESPONSIVE_CONTROLLER_RECOVERY\",\n\t\t),\n\t\t// By default enable the watchdog, unless the env variable is set\n\t\twatchdog: !getenv(\"ZWAVEJS_DISABLE_WATCHDOG\"),\n\t},\n\t// By default, try to recover from bootloader mode\n\tbootloaderMode: \"recover\",\n\tinterview: {\n\t\tqueryAllUserCodes: false,\n\t},\n\tstorage: {\n\t\tcacheDir: typeof process !== \"undefined\"\n\t\t\t? path.join(process.cwd(), \"cache\")\n\t\t\t: \"/cache\",\n\t\tlockDir: getenv(\"ZWAVEJS_LOCK_DIRECTORY\"),\n\t\tthrottle: \"normal\",\n\t},\n\tpreferences: {\n\t\tscales: {},\n\t},\n};\n\n/** Ensures that the options are valid */\nfunction checkOptions(options: ZWaveOptions): void {\n\tif (options.timeouts.ack < 1) {\n\t\tthrow new ZWaveError(\n\t\t\t`The ACK timeout must be positive!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.timeouts.byte < 1) {\n\t\tthrow new ZWaveError(\n\t\t\t`The BYTE timeout must be positive!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.timeouts.response < 500 || options.timeouts.response > 60000) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Response timeout must be between 500 and 60000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.timeouts.report < 500 || options.timeouts.report > 10000) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Report timeout must be between 500 and 10000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.timeouts.nonce < 3000 || options.timeouts.nonce > 20000) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Nonce timeout must be between 3000 and 20000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.timeouts.retryJammed < 10 || options.timeouts.retryJammed > 5000\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The timeout for retrying while jammed must be between 10 and 5000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.timeouts.sendToSleep < 10 || options.timeouts.sendToSleep > 5000\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Send To Sleep timeout must be between 10 and 5000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.timeouts.sendDataCallback < 10000) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Send Data Callback timeout must be at least 10000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.timeouts.sendDataAbort < 5000\n\t\t|| options.timeouts.sendDataAbort\n\t\t\t> options.timeouts.sendDataCallback - 5000\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Send Data Abort Callback timeout must be between 5000 and ${\n\t\t\t\toptions.timeouts.sendDataCallback - 5000\n\t\t\t} milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.timeouts.serialAPIStarted < 1000\n\t\t|| options.timeouts.serialAPIStarted > 30000\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Serial API started timeout must be between 1000 and 30000 milliseconds!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (options.securityKeys != undefined) {\n\t\tconst keys = Object.entries(options.securityKeys);\n\t\tfor (let i = 0; i < keys.length; i++) {\n\t\t\tconst [secClass, key] = keys[i];\n\t\t\tif (key.length !== 16) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`The security key for class ${secClass} must be a buffer with length 16!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (keys.findIndex(([, k]) => areUint8ArraysEqual(k, key)) !== i) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`The security key for class ${secClass} was used multiple times!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\tif (options.attempts.controller < 1 || options.attempts.controller > 3) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Controller attempts must be between 1 and 3!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.attempts.sendData < 1\n\t\t|| options.attempts.sendData > MAX_SEND_ATTEMPTS\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The SendData attempts must be between 1 and ${MAX_SEND_ATTEMPTS}!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.attempts.sendDataJammed < 1\n\t\t|| options.attempts.sendDataJammed > 10\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The SendData attempts while jammed must be between 1 and 10!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\tif (\n\t\toptions.attempts.nodeInterview < 1\n\t\t|| options.attempts.nodeInterview > 10\n\t) {\n\t\tthrow new ZWaveError(\n\t\t\t`The Node interview attempts must be between 1 and 10!`,\n\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t);\n\t}\n\n\tif (options.inclusionUserCallbacks) {\n\t\tif (!isObject(options.inclusionUserCallbacks)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The inclusionUserCallbacks must be an object!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t} else if (\n\t\t\ttypeof options.inclusionUserCallbacks.grantSecurityClasses\n\t\t\t\t!== \"function\"\n\t\t\t|| typeof options.inclusionUserCallbacks.validateDSKAndEnterPIN\n\t\t\t\t!== \"function\"\n\t\t\t|| typeof options.inclusionUserCallbacks.abort !== \"function\"\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The inclusionUserCallbacks must contain the following functions: grantSecurityClasses, validateDSKAndEnterPIN, abort!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (options.joinNetworkUserCallbacks) {\n\t\tif (!isObject(options.joinNetworkUserCallbacks)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The joinNetworkUserCallbacks must be an object!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t} else if (\n\t\t\ttypeof options.joinNetworkUserCallbacks.showDSK\n\t\t\t\t!== \"function\"\n\t\t\t|| typeof options.joinNetworkUserCallbacks.done\n\t\t\t\t!== \"function\"\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The joinNetworkUserCallbacks must contain the following functions: showDSK, done!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (options.rf != undefined) {\n\t\tif (options.rf.region != undefined) {\n\t\t\tif (\n\t\t\t\ttypeof options.rf.region !== \"number\"\n\t\t\t\t|| !(options.rf.region in RFRegion)\n\t\t\t\t|| options.rf.region === RFRegion.Unknown\n\t\t\t) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`${options.rf.region} is not a valid RF region!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (options.rf.txPower != undefined) {\n\t\t\tif (!isObject(options.rf.txPower)) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`rf.txPower must be an object!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t} else if (\n\t\t\t\ttypeof options.rf.txPower.powerlevel !== \"number\"\n\t\t\t\t|| typeof options.rf.txPower.measured0dBm !== \"number\"\n\t\t\t) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`rf.txPower must contain the following numeric properties: powerlevel, measured0dBm!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Function signature for a message handler. The return type signals if the\n * message was handled (`true`) or further handlers should be called (`false`)\n */\nexport type RequestHandler<T extends Message = Message> = (\n\tmsg: T,\n) => boolean | Promise<boolean>;\ninterface RequestHandlerEntry<T extends Message = Message> {\n\tinvoke: RequestHandler<T>;\n\toneTime: boolean;\n}\n\ninterface AwaitedThing<T> {\n\thandler: (thing: T) => void;\n\ttimeout?: Timer;\n\tpredicate: (msg: T) => boolean;\n\trefreshPredicate?: (msg: T) => boolean;\n}\n\ntype AwaitedMessageHeader = AwaitedThing<MessageHeaders>;\ntype AwaitedMessageEntry = AwaitedThing<Message>;\ntype AwaitedCommandEntry = AwaitedThing<CCId>;\ntype AwaitedCLIChunkEntry = AwaitedThing<CLIChunk>;\nexport type AwaitedBootloaderChunkEntry = AwaitedThing<BootloaderChunk>;\n\ninterface TransportServiceSession {\n\tfragmentSize: number;\n\tmachine: TransportServiceRXMachine;\n\ttimeout?: NodeJS.Timeout;\n}\n\ninterface Sessions {\n\t/** A map of all current Transport Service sessions that may still receive updates */\n\ttransportService: Map<number, TransportServiceSession>;\n\t/** A map of all current supervision sessions that may still receive updates */\n\tsupervision: Map<number, SupervisionUpdateHandler>;\n}\n\n// Used to add all node events to the driver event callbacks, but prefixed with \"node \"\ntype PrefixedNodeEvents = {\n\t[\n\t\tK in keyof ZWaveNodeEventCallbacks as K extends string ? `node ${K}`\n\t\t\t: never\n\t]: ZWaveNodeEventCallbacks[K];\n};\n\nconst enum ControllerRecoveryPhase {\n\tNone,\n\tACKTimeout,\n\tACKTimeoutAfterReset,\n\tCallbackTimeout,\n\tCallbackTimeoutAfterReset,\n\tJammed,\n\tJammedAfterReset,\n}\n\nfunction messageIsPing<T extends Message>(\n\tmsg: T,\n): msg is T & ContainsCC<NoOperationCC> {\n\treturn containsCC(msg) && msg.command instanceof NoOperationCC;\n}\n\nfunction assertValidCCs(container: ContainsCC): void {\n\tif (container.command instanceof InvalidCC) {\n\t\tif (typeof container.command.reason === \"number\") {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The message payload failed validation!\",\n\t\t\t\tcontainer.command.reason,\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The message payload is invalid!\",\n\t\t\t\tZWaveErrorCodes.PacketFormat_InvalidPayload,\n\t\t\t\tcontainer.command.reason,\n\t\t\t);\n\t\t}\n\t} else if (containsCC(container.command)) {\n\t\tassertValidCCs(container.command);\n\t}\n}\n\nfunction wrapLegacyFSDriverForCacheMigrationOnly(\n\tlegacy: import(\"@zwave-js/core/traits\").FileSystem,\n): ReadFileSystemInfo & ReadFile {\n\t// This usage only needs readFile and checking if a file exists\n\t// Every other usage will throw!\n\treturn {\n\t\tasync readFile(path) {\n\t\t\tconst text = await legacy.readFile(path, \"utf8\");\n\t\t\treturn Bytes.from(text, \"utf8\");\n\t\t},\n\t\tasync stat(path) {\n\t\t\tif (await legacy.pathExists(path)) {\n\t\t\t\treturn {\n\t\t\t\t\tisDirectory() {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t},\n\t\t\t\t\tisFile() {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t},\n\t\t\t\t\tmtime: new Date(),\n\t\t\t\t\tsize: 0,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tthrow new Error(\"File not found\");\n\t\t\t}\n\t\t},\n\t\treadDir(_path) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew Error(\"Not implemented for the legacy FS driver\"),\n\t\t\t);\n\t\t},\n\t};\n}\n\n// Strongly type the event emitter events\nexport interface DriverEventCallbacks extends PrefixedNodeEvents {\n\t\"driver ready\": () => void;\n\t\"bootloader ready\": () => void;\n\t\"cli ready\": () => void;\n\t\"all nodes ready\": () => void;\n\t\"firmware update progress\": (\n\t\tprogress: OTWFirmwareUpdateProgress,\n\t) => void;\n\t\"firmware update finished\": (\n\t\tresult: OTWFirmwareUpdateResult,\n\t) => void;\n\terror: (err: Error) => void;\n}\n\nexport type DriverEvents = Extract<keyof DriverEventCallbacks, string>;\n\n/**\n * The driver is the core of this library. It controls the serial interface,\n * handles transmission and receipt of messages and manages the network cache.\n * Any action you want to perform on the Z-Wave network must go through a driver\n * instance or its associated nodes.\n */\nexport class Driver extends TypedEventTarget<DriverEventCallbacks>\n\timplements\n\t\tCCAPIHost,\n\t\tInterviewContext,\n\t\tRefreshValuesContext,\n\t\tPersistValuesContext\n{\n\tpublic constructor(\n\t\tprivate port:\n\t\t\t| string\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t| ZWaveSerialPortImplementation\n\t\t\t| ZWaveSerialBindingFactory,\n\t\t...optionsAndPresets: (PartialZWaveOptions | undefined)[]\n\t) {\n\t\tsuper();\n\n\t\t// Ensure the given serial port is valid\n\t\tif (\n\t\t\ttypeof port !== \"string\"\n\t\t\t&& !isZWaveSerialPortImplementation(port)\n\t\t\t&& !isZWaveSerialBindingFactory(port)\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The port must be a string or a valid custom serial port implementation!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t}\n\n\t\t// Deep-Merge all given options/presets\n\t\tconst definedOptionsAndPresets = optionsAndPresets.filter(\n\t\t\t(o): o is PartialZWaveOptions => !!o,\n\t\t);\n\t\tlet mergedOptions: PartialZWaveOptions = {};\n\t\tfor (const preset of definedOptionsAndPresets) {\n\t\t\tmergedOptions = mergeDeep(mergedOptions, preset, true);\n\t\t}\n\t\t// Finally apply the defaults, without overwriting any existing settings\n\t\tthis._options = mergeDeep(\n\t\t\tmergedOptions,\n\t\t\tcloneDeep(defaultOptions),\n\t\t) as ZWaveOptions;\n\n\t\t// And make sure they contain valid values\n\t\tcheckOptions(this._options);\n\t\tif (this._options.userAgent) {\n\t\t\tif (!isObject(this._options.userAgent)) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t`The userAgent property must be an object!`,\n\t\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.updateUserAgent(this._options.userAgent);\n\t\t}\n\n\t\t// Initialize the cache\n\t\tthis.cacheDir = this._options.storage.cacheDir;\n\n\t\tconst self = this;\n\t\tthis.messageEncodingContext = {\n\t\t\tgetHighestSecurityClass: (nodeId) =>\n\t\t\t\tthis.getHighestSecurityClass(nodeId),\n\t\t\thasSecurityClass: (nodeId, securityClass) =>\n\t\t\t\tthis.hasSecurityClass(nodeId, securityClass),\n\t\t\tsetSecurityClass: (nodeId, securityClass, granted) =>\n\t\t\t\tthis.setSecurityClass(nodeId, securityClass, granted),\n\t\t\tgetDeviceConfig: (nodeId) => this.getDeviceConfig(nodeId),\n\t\t\t// These are evaluated lazily, so we cannot spread messageParsingContext unfortunately\n\t\t\tget securityManager() {\n\t\t\t\treturn self.securityManager;\n\t\t\t},\n\t\t\tget securityManager2() {\n\t\t\t\treturn self.securityManager2;\n\t\t\t},\n\t\t\tget securityManagerLR() {\n\t\t\t\treturn self.securityManagerLR;\n\t\t\t},\n\t\t\tgetSupportedCCVersion: (cc, nodeId, endpointIndex) =>\n\t\t\t\tthis.getSupportedCCVersion(cc, nodeId, endpointIndex),\n\t\t};\n\n\t\tthis._scheduler = new TaskScheduler();\n\t}\n\n\tprivate serialFactory: ZWaveSerialStreamFactory | undefined;\n\t/** The serial port instance */\n\tprivate serial: ZWaveSerialStream | undefined;\n\n\tprivate messageEncodingContext: Omit<\n\t\tMessageEncodingContext,\n\t\tkeyof HostIDs | \"nodeIdType\"\n\t>;\n\n\tprivate getEncodingContext(): MessageEncodingContext & CCEncodingContext {\n\t\treturn {\n\t\t\t...this.messageEncodingContext,\n\t\t\townNodeId: this.controller.ownNodeId!,\n\t\t\thomeId: this.controller.homeId!,\n\t\t\tnodeIdType: this._controller?.nodeIdType ?? NodeIDType.Short,\n\t\t};\n\t}\n\n\tprivate getMessageParsingContext(): MessageParsingContext {\n\t\treturn {\n\t\t\tgetDeviceConfig: (nodeId) => this.getDeviceConfig(nodeId),\n\t\t\tsdkVersion: this._controller?.sdkVersion,\n\t\t\trequestStorage: this._requestStorage,\n\t\t\townNodeId: this._controller?.ownNodeId ?? 0, // Unspecified node ID\n\t\t\thomeId: this._controller?.homeId ?? 0x55555555, // Invalid home ID\n\t\t\tnodeIdType: this._controller?.nodeIdType ?? NodeIDType.Short,\n\t\t};\n\t}\n\n\tprivate getCCParsingContext(): Omit<\n\t\tCCParsingContext,\n\t\t\"sourceNodeId\" | \"frameType\"\n\t> {\n\t\treturn {\n\t\t\t...this.messageEncodingContext,\n\t\t\townNodeId: this.controller.ownNodeId!,\n\t\t\thomeId: this.controller.homeId!,\n\t\t};\n\t}\n\n\t// We have multiple queues to achieve multiple \"layers\" of communication priority:\n\t// The default queue for most messages\n\tprivate queue!: TransactionQueue; // Is initialized in initTransactionQueues()\n\t// An immediate queue for handling queries that need to be handled ASAP, e.g. Nonce Get\n\tprivate immediateQueue!: TransactionQueue; // Is initialized in initTransactionQueues()\n\t// And all of them feed into the serial API queue, which contains commands that will be sent ASAP\n\tprivate serialAPIQueue!: AsyncQueue<SerialAPIQueueItem>; // Is initialized in initControllerAndNodes()\n\n\t/** Gives access to the transaction queues, ordered by priority */\n\tprivate get queues(): TransactionQueue[] {\n\t\treturn [this.immediateQueue, this.queue];\n\t}\n\n\tprivate initTransactionQueues(): void {\n\t\tthis.immediateQueue = new TransactionQueue({\n\t\t\tname: \"immediate\",\n\t\t\tmayStartNextTransaction: (t) => {\n\t\t\t\t// While the controller is unresponsive, only soft resetting is allowed.\n\t\t\t\t// Since we use GetControllerVersionRequest to check if the controller responds after soft-reset,\n\t\t\t\t// allow that too.\n\t\t\t\tif (this.controller.status === ControllerStatus.Unresponsive) {\n\t\t\t\t\treturn t.message instanceof SoftResetRequest\n\t\t\t\t\t\t|| t.message instanceof GetControllerVersionRequest;\n\t\t\t\t}\n\n\t\t\t\t// While the controller is jammed, only soft resetting is allowed\n\t\t\t\tif (this.controller.status === ControllerStatus.Jammed) {\n\t\t\t\t\treturn t.message instanceof SoftResetRequest;\n\t\t\t\t}\n\n\t\t\t\t// All other messages on the immediate queue may always be sent as long as the controller is ready to send\n\t\t\t\treturn !this.queuePaused\n\t\t\t\t\t&& this.controller.status === ControllerStatus.Ready;\n\t\t\t},\n\t\t});\n\t\tthis.queue = new TransactionQueue({\n\t\t\tname: \"normal\",\n\t\t\tmayStartNextTransaction: (t) => this.mayStartTransaction(t),\n\t\t});\n\n\t\tthis._queueIdle = false;\n\n\t\t// Start draining the queues\n\t\tfor (const queue of this.queues) {\n\t\t\tvoid this.drainTransactionQueue(queue);\n\t\t}\n\t}\n\n\tprivate async destroyTransactionQueues(\n\t\treason: string,\n\t\terrorCode?: ZWaveErrorCodes,\n\t): Promise<void> {\n\t\t// The queues might not have been initialized yet\n\t\tfor (const queue of this.queues) {\n\t\t\tif (!queue) return;\n\t\t}\n\n\t\t// Reject pending transactions, but not during integration tests\n\t\tif (getenv(\"NODE_ENV\") !== \"test\") {\n\t\t\tawait this.rejectTransactions(\n\t\t\t\t(_t) => true,\n\t\t\t\treason,\n\t\t\t\terrorCode ?? ZWaveErrorCodes.Driver_TaskRemoved,\n\t\t\t);\n\t\t}\n\n\t\tfor (const queue of this.queues) {\n\t\t\tqueue.abort();\n\t\t}\n\t}\n\n\tprivate _scheduler: TaskScheduler;\n\tpublic get scheduler(): TaskScheduler {\n\t\treturn this._scheduler;\n\t}\n\n\tprivate queuePaused = false;\n\t/** Used to immediately abort ongoing Serial API commands */\n\tprivate abortSerialAPICommand: DeferredPromise<Error> | undefined;\n\n\tprivate initSerialAPIQueue(): void {\n\t\tthis.serialAPIQueue = new AsyncQueue();\n\n\t\t// Start draining the queue\n\t\tvoid this.drainSerialAPIQueue();\n\t}\n\n\tprivate destroySerialAPIQueue(\n\t\treason: string,\n\t\terrorCode?: ZWaveErrorCodes,\n\t): void {\n\t\t// The queue might not have been initialized yet\n\t\tif (!this.serialAPIQueue) return;\n\n\t\tthis.serialAPIQueue.abort();\n\n\t\t// Abort the currently executed serial API command, so the queue does not lock up\n\t\tthis.abortSerialAPICommand?.reject(\n\t\t\tnew ZWaveError(\n\t\t\t\treason,\n\t\t\t\terrorCode ?? ZWaveErrorCodes.Driver_Destroyed,\n\t\t\t),\n\t\t);\n\t}\n\n\t// Keep track of which queues are currently busy\n\tprivate _queuesBusyFlags = 0;\n\tprivate _queueIdle: boolean = false;\n\t/** Whether the queue is currently idle */\n\tpublic get queueIdle(): boolean {\n\t\treturn this._queueIdle;\n\t}\n\tprivate set queueIdle(value: boolean) {\n\t\tif (this._queueIdle !== value) {\n\t\t\tthis.driverLog.print(\n\t\t\t\tvalue ? \"all queues idle\" : \"one or more queues busy\",\n\t\t\t);\n\t\t\tthis._queueIdle = value;\n\t\t\tthis.handleQueueIdleChange(value);\n\t\t}\n\t}\n\n\t/** A map of handlers for all sorts of requests */\n\tprivate requestHandlers = new Map<FunctionType, RequestHandlerEntry[]>();\n\t/** A list of awaited message headers */\n\tprivate awaitedMessageHeaders: AwaitedMessageHeader[] = [];\n\t/** A list of awaited messages */\n\tprivate awaitedMessages: AwaitedMessageEntry[] = [];\n\t/** A list of awaited commands */\n\tprivate awaitedCommands: AwaitedCommandEntry[] = [];\n\t/** A list of awaited chunks from the bootloader */\n\tprivate awaitedBootloaderChunks: AwaitedBootloaderChunkEntry[] = [];\n\t/** A list of awaited chunks from the end device CLI */\n\tprivate awaitedCLIChunks: AwaitedCLIChunkEntry[] = [];\n\n\t/** A map of Node ID -> ongoing sessions */\n\tprivate nodeSessions = new Map<number, Sessions>();\n\tprivate ensureNodeSessions(nodeId: number): Sessions {\n\t\tif (!this.nodeSessions.has(nodeId)) {\n\t\t\tthis.nodeSessions.set(nodeId, {\n\t\t\t\ttransportService: new Map(),\n\t\t\t\tsupervision: new Map(),\n\t\t\t});\n\t\t}\n\t\treturn this.nodeSessions.get(nodeId)!;\n\t}\n\n\tprivate _requestStorage: Map<FunctionType, Record<string, unknown>> =\n\t\tnew Map();\n\t/**\n\t * @internal\n\t * Stores data from Serial API command requests to be used by their responses\n\t */\n\tpublic get requestStorage(): Map<FunctionType, Record<string, unknown>> {\n\t\treturn this._requestStorage;\n\t}\n\n\tpublic readonly cacheDir: string;\n\n\tprivate _valueDB: Database<unknown> | undefined;\n\t/** @internal */\n\tpublic get valueDB(): Database<unknown> | undefined {\n\t\treturn this._valueDB;\n\t}\n\tprivate _metadataDB: Database<ValueMetadata> | undefined;\n\t/** @internal */\n\tpublic get metadataDB(): Database<ValueMetadata> | undefined {\n\t\treturn this._metadataDB;\n\t}\n\tprivate _networkCache: Database<any> | undefined;\n\t/** @internal */\n\tpublic get networkCache(): Database<any> {\n\t\tif (this._networkCache == undefined) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The network cache was not yet initialized!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\treturn this._networkCache;\n\t}\n\n\t// This is set during `start()` and should not be accessed before\n\tprivate _configManager!: ConfigManager;\n\tpublic get configManager(): ConfigManager {\n\t\treturn this._configManager;\n\t}\n\n\tpublic get configVersion(): string {\n\t\treturn (\n\t\t\tthis.configManager?.configVersion\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\t\t\t?? require(\"zwave-js/package.json\")?.dependencies\n\t\t\t\t\t?.[\"@zwave-js/config\"]\n\t\t\t\t?? libVersion\n\t\t);\n\t}\n\n\t// This is set during `start()` and should not be accessed before\n\tprivate _logContainer!: LogContainer;\n\t// This is set during `start()` and should not be accessed before\n\tprivate _driverLog!: DriverLogger;\n\t/** @internal */\n\tpublic get driverLog(): DriverLogger {\n\t\treturn this._driverLog;\n\t}\n\n\t// This is set during `start()` and should not be accessed before\n\tprivate _controllerLog!: ControllerLogger;\n\t/** @internal */\n\tpublic get controllerLog(): ControllerLogger {\n\t\treturn this._controllerLog;\n\t}\n\n\tpublic logNode(\n\t\tnodeId: number,\n\t\tmessage: string,\n\t\tlevel?: LogNodeOptions[\"level\"],\n\t): void;\n\tpublic logNode(nodeId: number, options: LogNodeOptions): void;\n\tpublic logNode(...args: any[]): void {\n\t\t// @ts-expect-error\n\t\tthis._controllerLog.logNode(...args);\n\t}\n\n\tprivate _controller: ZWaveController | undefined;\n\t/** Encapsulates information about the Z-Wave controller and provides access to its nodes */\n\tpublic get controller(): ZWaveController {\n\t\tif (this._controller == undefined) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The controller is not yet ready!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\treturn this._controller;\n\t}\n\n\t/** While in bootloader mode, this encapsulates information about the bootloader and its state */\n\tprivate _bootloader: Bootloader | undefined;\n\tpublic get bootloader(): Bootloader {\n\t\tif (this._bootloader == undefined) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The controller is not in bootloader mode!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\treturn this._bootloader;\n\t}\n\n\tprivate _cli: EndDeviceCLI | undefined;\n\t/** While in end device CLI mode, this encapsulates information about the CLI and its state */\n\tpublic get cli(): EndDeviceCLI {\n\t\tif (this._cli == undefined) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The Z-Wave module is not in CLI mode!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\treturn this._cli;\n\t}\n\n\t/** Determines which kind of Z-Wave application the driver is currently communicating with */\n\tpublic get mode(): DriverMode {\n\t\tif (this._bootloader) return DriverMode.Bootloader;\n\t\tif (this._cli) return DriverMode.CLI;\n\t\tif (this._controller) return DriverMode.SerialAPI;\n\t\treturn DriverMode.Unknown;\n\t}\n\n\tprivate _recoveryPhase: ControllerRecoveryPhase =\n\t\tControllerRecoveryPhase.None;\n\n\tprivate _securityManager: SecurityManager | undefined;\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic get securityManager(): SecurityManager | undefined {\n\t\treturn this._securityManager;\n\t}\n\n\tprivate _securityManager2: SecurityManager2 | undefined;\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic get securityManager2(): SecurityManager2 | undefined {\n\t\treturn this._securityManager2;\n\t}\n\n\tprivate _securityManagerLR: SecurityManager2 | undefined;\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic get securityManagerLR(): SecurityManager2 | undefined {\n\t\treturn this._securityManagerLR;\n\t}\n\n\t/** @internal */\n\tpublic getSecurityManager2(\n\t\tdestination: number | MulticastDestination,\n\t): SecurityManager2 | undefined {\n\t\tconst nodeId = isArray(destination) ? destination[0] : destination;\n\t\tconst isLongRange = isLongRangeNodeId(nodeId);\n\t\treturn isLongRange ? this.securityManagerLR : this.securityManager2;\n\t}\n\n\tprivate _learnModeAuthenticatedKeyPair: KeyPair | undefined;\n\t/** @internal */\n\tpublic async getLearnModeAuthenticatedKeyPair(): Promise<KeyPair> {\n\t\tif (this._learnModeAuthenticatedKeyPair == undefined) {\n\t\t\t// Try restoring from cache\n\t\t\tconst privateKey = this.cacheGet<Uint8Array>(\n\t\t\t\tcacheKeys.controller.privateKey,\n\t\t\t);\n\t\t\tif (privateKey) {\n\t\t\t\tthis._learnModeAuthenticatedKeyPair =\n\t\t\t\t\tawait keyPairFromRawECDHPrivateKey(privateKey);\n\t\t\t} else {\n\t\t\t\t// Not found in cache, create a new one and cache it\n\t\t\t\tthis._learnModeAuthenticatedKeyPair =\n\t\t\t\t\tawait generateECDHKeyPair();\n\t\t\t\tthis.cacheSet(\n\t\t\t\t\tcacheKeys.controller.privateKey,\n\t\t\t\t\tthis._learnModeAuthenticatedKeyPair.privateKey,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn this._learnModeAuthenticatedKeyPair;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications. Use `controller.homeId` instead!\n\t */\n\tpublic get homeId(): number {\n\t\t// This is needed for the ZWaveHost interface\n\t\treturn this.controller.homeId!;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications. Use `controller.ownNodeId` instead!\n\t */\n\tpublic get ownNodeId(): number {\n\t\t// This is needed for the ZWaveHost interface\n\t\treturn this.controller.ownNodeId!;\n\t}\n\n\t/** @internal Used for compatibility with the CCAPIHost interface */\n\tpublic getNode(nodeId: number): ZWaveNode | undefined {\n\t\treturn this.controller.nodes.get(nodeId);\n\t}\n\n\t/** @internal Used for compatibility with the CCAPIHost interface */\n\tpublic getNodeOrThrow(nodeId: number): ZWaveNode {\n\t\treturn this.controller.nodes.getOrThrow(nodeId);\n\t}\n\n\t/** @internal Used for compatibility with the CCAPIHost interface */\n\tpublic getAllNodes(): ZWaveNode[] {\n\t\treturn [...this.controller.nodes.values()];\n\t}\n\n\tpublic tryGetNode(msg: Message): ZWaveNode | undefined {\n\t\tconst nodeId = msg.getNodeId();\n\t\tif (nodeId != undefined) return this.controller.nodes.get(nodeId);\n\t}\n\n\tpublic tryGetEndpoint(cc: CommandClass): Endpoint | undefined {\n\t\tif (cc.isSinglecast()) {\n\t\t\treturn this.controller.nodes\n\t\t\t\t.get(cc.nodeId)\n\t\t\t\t?.getEndpoint(cc.endpointIndex);\n\t\t}\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic getValueDB(nodeId: number): ValueDB {\n\t\t// This is needed for the ZWaveHost interface\n\t\tconst node = this.controller.nodes.getOrThrow(nodeId);\n\t\treturn node.valueDB;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic tryGetValueDB(nodeId: number): ValueDB | undefined {\n\t\t// This is needed for the ZWaveHost interface\n\t\tconst node = this.controller.nodes.get(nodeId);\n\t\treturn node?.valueDB;\n\t}\n\n\tpublic getDeviceConfig(nodeId: number): DeviceConfig | undefined {\n\t\t// This is needed for the ZWaveHost interface\n\t\treturn this.controller.nodes.get(nodeId)?.deviceConfig;\n\t}\n\n\tpublic lookupManufacturer(manufacturerId: number): string | undefined {\n\t\treturn this.configManager.lookupManufacturer(manufacturerId);\n\t}\n\n\tpublic getHighestSecurityClass(\n\t\tnodeId: number,\n\t): MaybeNotKnown<SecurityClass> {\n\t\t// This is needed for the ZWaveHost interface\n\t\tconst node = this.controller.nodes.getOrThrow(nodeId);\n\t\treturn node.getHighestSecurityClass();\n\t}\n\n\tpublic hasSecurityClass(\n\t\tnodeId: number,\n\t\tsecurityClass: SecurityClass,\n\t): MaybeNotKnown<boolean> {\n\t\t// This is needed for the ZWaveHost interface\n\t\tconst node = this.controller.nodes.getOrThrow(nodeId);\n\t\treturn node.hasSecurityClass(securityClass);\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic setSecurityClass(\n\t\tnodeId: number,\n\t\tsecurityClass: SecurityClass,\n\t\tgranted: boolean,\n\t): void {\n\t\t// This is needed for the ZWaveHost interface\n\t\tconst node = this.controller.nodes.getOrThrow(nodeId);\n\t\tnode.setSecurityClass(securityClass, granted);\n\t}\n\n\t/** Updates the logging configuration without having to restart the driver. */\n\tpublic updateLogConfig(config: Partial<LogConfig>): void {\n\t\tthis._logContainer.updateConfiguration(config);\n\t}\n\n\t/** Returns the current logging configuration. */\n\tpublic getLogConfig(): LogConfig {\n\t\treturn this._logContainer.getConfiguration();\n\t}\n\n\t/** Updates the preferred sensor scales to use for node queries */\n\tpublic setPreferredScales(\n\t\tscales: ZWaveOptions[\"preferences\"][\"scales\"],\n\t): void {\n\t\tthis._options.preferences.scales = mergeDeep(\n\t\t\tdefaultOptions.preferences.scales,\n\t\t\tscales,\n\t\t);\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic getUserPreferences(): UserPreferences {\n\t\treturn this._options.preferences;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic getInterviewOptions(): InterviewOptions {\n\t\treturn this._options.interview;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications\n\t */\n\tpublic getRefreshValueTimeouts(): RefreshValueTimeouts {\n\t\treturn {\n\t\t\trefreshValue: this._options.timeouts.refreshValue,\n\t\t\trefreshValueAfterTransition:\n\t\t\t\tthis._options.timeouts.refreshValueAfterTransition,\n\t\t};\n\t}\n\n\t/**\n\t * Enumerates all existing serial ports.\n\t * @param local Whether to include local serial ports\n\t * @param remote Whether to discover remote serial ports using an mDNS query for the `_zwave._tcp` domain\n\t */\n\tpublic static async enumerateSerialPorts({\n\t\tlocal = true,\n\t\tremote = true,\n\t}: {\n\t\tlocal?: boolean;\n\t\tremote?: boolean;\n\t} = {}): Promise<string[]> {\n\t\tconst ret: (EnumeratedPort & { path: string })[] = [];\n\n\t\t// Ideally we'd use the host bindings used by the driver, but we can't access them in a static method\n\n\t\tconst bindings =\n\t\t\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t\t\t// @ts-ignore - For some reason, VSCode does not like this import, although tsc is fine with it\n\t\t\t(await import(\"#default_bindings/serial\")).serial;\n\t\tif (local && typeof bindings.list === \"function\") {\n\t\t\tfor (const port of await bindings.list()) {\n\t\t\t\tif (port.type === \"custom\") continue;\n\t\t\t\tret.push(port);\n\t\t\t}\n\t\t}\n\t\tif (remote) {\n\t\t\tconst ports = await discoverRemoteSerialPorts();\n\t\t\tif (ports) {\n\t\t\t\tret.push(...ports.map((p) => ({\n\t\t\t\t\ttype: \"socket\" as const,\n\t\t\t\t\tpath: p.port,\n\t\t\t\t})));\n\t\t\t}\n\t\t}\n\n\t\tconst portOrder: EnumeratedPort[\"type\"][] = [\"link\", \"socket\", \"tty\"];\n\n\t\tret.sort((a, b) => {\n\t\t\tconst typeA = portOrder.indexOf(a.type);\n\t\t\tconst typeB = portOrder.indexOf(b.type);\n\t\t\tif (typeA !== typeB) return typeA - typeB;\n\t\t\treturn a.path.localeCompare(b.path);\n\t\t});\n\n\t\treturn distinct(ret.map((p) => p.path));\n\t}\n\n\t/** Updates a subset of the driver options on the fly */\n\tpublic updateOptions(options: EditableZWaveOptions): void {\n\t\t// This code is called from user code, so we need to make sure no options were passed\n\t\t// which we are not able to update on the fly\n\t\tconst safeOptions = pick(options, [\n\t\t\t\"disableOptimisticValueUpdate\",\n\t\t\t\"emitValueUpdateAfterSetValue\",\n\t\t\t\"inclusionUserCallbacks\",\n\t\t\t\"joinNetworkUserCallbacks\",\n\t\t\t\"interview\",\n\t\t\t\"preferences\",\n\t\t\t\"vendor\",\n\t\t]);\n\n\t\t// Create a new deep-merged copy of the options so we can check them for validity\n\t\t// without affecting our own options.\n\t\t// The following options are potentially unsafe to clone, so just preserve them:\n\t\t// - logConfig\n\t\t// - host (could contain classes)\n\t\tconst { logConfig, host, ...rest } = this._options;\n\t\tconst newOptions = mergeDeep(\n\t\t\tcloneDeep(rest),\n\t\t\tsafeOptions,\n\t\t\ttrue,\n\t\t) as ZWaveOptions;\n\t\tnewOptions.logConfig = logConfig;\n\t\tnewOptions.host = host;\n\t\tcheckOptions(newOptions);\n\n\t\tif (options.userAgent && !isObject(options.userAgent)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The userAgent property must be an object!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t}\n\n\t\t// All good, update the options\n\t\tthis._options = newOptions;\n\n\t\tif (options.logConfig) {\n\t\t\tthis.updateLogConfig(options.logConfig);\n\t\t}\n\n\t\tif (options.userAgent) {\n\t\t\tthis.updateUserAgent(options.userAgent);\n\t\t}\n\t}\n\n\tprivate _options: ZWaveOptions;\n\tpublic get options(): Readonly<ZWaveOptions> {\n\t\treturn this._options;\n\t}\n\n\t/**\n\t * The host bindings used to access file system etc.\n\t */\n\t// This is set during `start()` and should not be accessed before\n\tprivate bindings!: Required<NonNullable<ZWaveOptions[\"host\"]>>;\n\n\tprivate _wasStarted: boolean = false;\n\tprivate _isOpen: boolean = false;\n\n\t/** Start the driver */\n\tpublic async start(): Promise<void> {\n\t\t// avoid starting twice\n\t\tif (this.wasDestroyed) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The driver was destroyed. Create a new instance and start that one.\",\n\t\t\t\tZWaveErrorCodes.Driver_Destroyed,\n\t\t\t);\n\t\t}\n\t\tif (this._wasStarted) return Promise.resolve();\n\t\tthis._wasStarted = true;\n\n\t\t// Populate default bindings. This has to happen asynchronously, so the driver does not have a hard dependency\n\t\t// on Node.js internals\n\t\tthis.bindings = {\n\t\t\tfs: this._options.host?.fs\n\t\t\t\t?? (await import(\"#default_bindings/fs\")).fs,\n\t\t\tserial: this._options.host?.serial\n\t\t\t\t?? (await import(\"#default_bindings/serial\")).serial,\n\t\t\tdb: this._options.host?.db\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t\t\t\t// @ts-ignore - For some reason, VSCode does not like this import, although tsc is fine with it\n\t\t\t\t?? (await import(\"#default_bindings/db\")).db,\n\t\t\tlog: this._options.host?.log\n\t\t\t\t?? (await import(\"#default_bindings/log\")).log,\n\t\t};\n\n\t\t// Initialize logging\n\t\tthis._logContainer = this.bindings.log(this._options.logConfig);\n\t\tthis._driverLog = new DriverLogger(this, this._logContainer);\n\t\tthis._controllerLog = new ControllerLogger(this._logContainer);\n\n\t\t// Initialize config manager\n\t\tthis._configManager = new ConfigManager({\n\t\t\tbindings: this.bindings.fs,\n\t\t\tlogContainer: this._logContainer,\n\t\t\tdeviceConfigPriorityDir:\n\t\t\t\tthis._options.storage.deviceConfigPriorityDir,\n\t\t\tdeviceConfigExternalDir:\n\t\t\t\tthis._options.storage.deviceConfigExternalDir,\n\t\t});\n\n\t\tconst spOpenPromise = createDeferredPromise();\n\n\t\t// Log which version is running\n\t\tthis.driverLog.print(libNameString, \"info\");\n\t\tthis.driverLog.print(`version ${libVersion}`, \"info\");\n\t\tthis.driverLog.print(\"\", \"info\");\n\n\t\tthis.driverLog.print(\"starting driver...\");\n\n\t\t// Open the serial port\n\t\tlet binding: ZWaveSerialBindingFactory;\n\t\tif (typeof this.port === \"string\") {\n\t\t\tif (\n\t\t\t\ttypeof this.bindings.serial.createFactoryByPath === \"function\"\n\t\t\t) {\n\t\t\t\tthis.driverLog.print(`opening serial port ${this.port}`);\n\t\t\t\tbinding = await this.bindings.serial.createFactoryByPath(\n\t\t\t\t\tthis.port,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tspOpenPromise.reject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\"This platform does not support creating a serial connection by path\",\n\t\t\t\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tvoid this.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (isZWaveSerialPortImplementation(this.port)) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"opening serial port using the provided custom implementation\",\n\t\t\t);\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"This is deprecated! Switch to the factory pattern instead.\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tbinding = wrapLegacySerialBinding(this.port);\n\t\t} else {\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"opening serial port using the provided custom factory\",\n\t\t\t);\n\t\t\tbinding = this.port;\n\t\t}\n\t\tthis.serialFactory = new ZWaveSerialStreamFactory(\n\t\t\tbinding,\n\t\t\tthis._logContainer,\n\t\t);\n\n\t\t// IMPORTANT: Test code expects the open promise to be created and returned synchronously\n\t\t// Everything async (including opening the serial port) must happen in the setImmediate callback\n\n\t\t// asynchronously open the serial port\n\t\tsetImmediate(async () => {\n\t\t\ttry {\n\t\t\t\tawait this.openSerialport();\n\t\t\t} catch (e) {\n\t\t\t\tspOpenPromise.reject(e as Error);\n\t\t\t\tvoid this.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.driverLog.print(\"serial port opened\");\n\t\t\tthis._isOpen = true;\n\t\t\tspOpenPromise.resolve();\n\n\t\t\t// Start the task scheduler\n\t\t\tthis._scheduler.start();\n\n\t\t\tif (\n\t\t\t\ttypeof this._options.testingHooks?.onSerialPortOpen\n\t\t\t\t\t=== \"function\"\n\t\t\t) {\n\t\t\t\tawait this._options.testingHooks.onSerialPortOpen(this.serial!);\n\t\t\t}\n\n\t\t\t// Perform initialization sequence\n\t\t\tif (this._options.testingHooks?.skipFirmwareIdentification) {\n\t\t\t\t// No identification desired, just send a NAK and assume it's a\n\t\t\t\t// Serial API controller\n\t\t\t\tawait this.writeHeader(MessageHeaders.NAK);\n\t\t\t\tif (getenv(\"NODE_ENV\") !== \"test\") {\n\t\t\t\t\tawait wait(1000);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst mode = await this.detectMode();\n\t\t\t\tif (mode === DriverMode.CLI) {\n\t\t\t\t\tthis.emit(\"cli ready\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (mode === DriverMode.Bootloader) {\n\t\t\t\t\tif (this._options.bootloaderMode === \"stay\") {\n\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\"Controller is in bootloader mode. Staying in bootloader as requested.\",\n\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.emit(\"bootloader ready\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Controller is in bootloader, attempting to recover...\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tawait this.leaveBootloaderInternal();\n\n\t\t\t\t\t// Wait a short time again. If we're in bootloader mode again, we're stuck\n\t\t\t\t\tawait wait(1000);\n\n\t\t\t\t\t// FIXME: Leaving the bootloader may end up in the CLI\n\n\t\t\t\t\tif (this._bootloader) {\n\t\t\t\t\t\tif (this._options.bootloaderMode === \"allow\") {\n\t\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\t\"Failed to recover from bootloader. Staying in bootloader mode as requested.\",\n\t\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tthis.emit(\"bootloader ready\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// bootloaderMode === \"recover\"\n\t\t\t\t\t\t\tvoid this.destroyWithMessage(\n\t\t\t\t\t\t\t\t\"Failed to recover from bootloader. Please flash a new firmware to continue...\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Try to create the cache directory. This can fail, in which case we should expose a good error message\n\t\t\ttry {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\tif (this._options.storage.driver) {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\tawait this._options.storage.driver.ensureDir(this.cacheDir);\n\t\t\t\t} else {\n\t\t\t\t\tawait this.bindings.fs.ensureDir(this.cacheDir);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tlet message: string;\n\t\t\t\tif (\n\t\t\t\t\t/\\.yarn[/\\\\]cache[/\\\\]zwave-js/i.test(\n\t\t\t\t\t\tgetErrorMessage(e, true),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tmessage =\n\t\t\t\t\t\t`Failed to create the cache directory ${this.cacheDir}. When using Yarn PnP, you need to change the location with the \"storage.cacheDir\" driver option.`;\n\t\t\t\t} else {\n\t\t\t\t\tmessage =\n\t\t\t\t\t\t`Failed to create the cache directory ${this.cacheDir}. Please make sure that it is writable or change the location with the \"storage.cacheDir\" driver option.`;\n\t\t\t\t}\n\n\t\t\t\tvoid this.destroyWithMessage(message);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Load the necessary configuration\n\t\t\tif (this._options.testingHooks?.loadConfiguration !== false) {\n\t\t\t\tthis.driverLog.print(\"loading configuration...\");\n\t\t\t\ttry {\n\t\t\t\t\tawait this.configManager.loadAll();\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst message = `Failed to load the configuration: ${\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\tvoid this.destroyWithMessage(message);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.driverLog.print(\"beginning interview...\");\n\t\t\ttry {\n\t\t\t\tawait this.initializeControllerAndNodes();\n\t\t\t} catch (e) {\n\t\t\t\tlet message: string;\n\t\t\t\tif (\n\t\t\t\t\tisZWaveError(e)\n\t\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t) {\n\t\t\t\t\tmessage =\n\t\t\t\t\t\t`Failed to initialize the driver, no response from the controller. Are you sure this is a Z-Wave controller?`;\n\t\t\t\t} else {\n\t\t\t\t\tmessage = `Failed to initialize the driver: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`;\n\t\t\t\t}\n\t\t\t\tthis.driverLog.print(message, \"error\");\n\t\t\t\tthis.emit(\n\t\t\t\t\t\"error\",\n\t\t\t\t\tnew ZWaveError(message, ZWaveErrorCodes.Driver_Failed),\n\t\t\t\t);\n\t\t\t\tvoid this.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\t\t});\n\n\t\treturn spOpenPromise;\n\t}\n\n\tprivate async detectMode(): Promise<DriverMode> {\n\t\t// We re-use the NAK that should be used to reset the communication on\n\t\t// Serial API startup to detect which kind of application we are talking to\n\n\t\tconst incomingNAK = this.waitForMessageHeader(\n\t\t\t(h) => h === MessageHeaders.NAK,\n\t\t\t500,\n\t\t)\n\t\t\t.then(() => true)\n\t\t\t.catch(() => false);\n\t\tawait this.writeHeader(MessageHeaders.NAK);\n\n\t\t// The response to this NAK helps determine whether the Z-Wave module is...\n\t\t// ...stuck in the bootloader,\n\t\t// ...running a SoC end device firmware with CLI\n\t\t// ...or a \"normal\" Serial API\n\n\t\tif (await incomingNAK) {\n\t\t\t// This is possibly a CLI. It should respond with a prompt after we\n\t\t\t// send a newline.\n\t\t\tawait this.writeSerial(Bytes.from(\"\\n\", \"ascii\"));\n\t\t}\n\n\t\t// If there was no NAK, it may be a bootloader, but it may also be a CLI\n\t\t// on a device that just started. In this case it can happen that the\n\t\t// NAK is not answered, but a CLI prompt is received.\n\t\t// In this case, the CLI is also detected by the serial parsers.\n\n\t\t// In any case, wait another 500ms to give the parsers time to detect\n\t\t// either the booloader or the CLI\n\t\tawait wait(500);\n\n\t\tif (this._cli) return DriverMode.CLI;\n\t\tif (this._bootloader) return DriverMode.Bootloader;\n\n\t\treturn DriverMode.SerialAPI;\n\t}\n\n\tprivate _controllerInterviewed: boolean = false;\n\tprivate _nodesReady = new Set<number>();\n\tprivate _nodesReadyEventEmitted: boolean = false;\n\tprivate _isOpeningSerialPort: boolean = false;\n\n\tprivate async openSerialport(): Promise<void> {\n\t\tlet lastError: unknown;\n\t\t// After a reset, the serial port may need a few seconds until we can open it - try a few times\n\t\tthis._isOpeningSerialPort = true;\n\t\tfor (\n\t\t\tlet attempt = 1;\n\t\t\tattempt <= this._options.attempts.openSerialPort;\n\t\t\tattempt++\n\t\t) {\n\t\t\ttry {\n\t\t\t\tthis.serial = await this.serialFactory!.createStream();\n\t\t\t\t// Start reading from the serial port\n\t\t\t\tvoid this.handleSerialData(this.serial);\n\n\t\t\t\t// There are some situations where the serial port closes unexpectedly\n\t\t\t\t// after just a few milliseconds, e.g. when reconnecting to a TCP serial port.\n\t\t\t\t// Wait a bit to see if this happens.\n\t\t\t\tif (getenv(\"NODE_ENV\") !== \"test\") {\n\t\t\t\t\tawait wait(250);\n\t\t\t\t}\n\n\t\t\t\tif (this.serial.isOpen) {\n\t\t\t\t\t// It is still open, we're done\n\t\t\t\t\tthis._isOpeningSerialPort = false;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tlastError = e;\n\t\t\t}\n\t\t\tif (attempt < this._options.attempts.openSerialPort) {\n\t\t\t\tawait wait(1000);\n\t\t\t}\n\t\t}\n\n\t\tthis._isOpeningSerialPort = false;\n\n\t\tconst message = `Failed to open the serial port: ${\n\t\t\tgetErrorMessage(\n\t\t\t\tlastError,\n\t\t\t)\n\t\t}`;\n\t\tthis.driverLog.print(message, \"error\");\n\n\t\tthrow new ZWaveError(message, ZWaveErrorCodes.Driver_Failed);\n\t}\n\n\t/** Indicates whether all nodes are ready, i.e. the \"all nodes ready\" event has been emitted */\n\tpublic get allNodesReady(): boolean {\n\t\treturn this._nodesReadyEventEmitted;\n\t}\n\n\tprivate getJsonlDBOptions(): JsonlDBOptions<any> {\n\t\tconst options: JsonlDBOptions<any> = {\n\t\t\tignoreReadErrors: true,\n\t\t\t...throttlePresets[this._options.storage.throttle],\n\t\t};\n\t\tif (this._options.storage.lockDir) {\n\t\t\toptions.lockfile = {\n\t\t\t\tdirectory: this._options.storage.lockDir,\n\t\t\t};\n\t\t}\n\t\treturn options;\n\t}\n\n\tprivate async initNetworkCache(homeId: number): Promise<void> {\n\t\tconst options = this.getJsonlDBOptions();\n\n\t\tconst networkCacheFile = path.join(\n\t\t\tthis.cacheDir,\n\t\t\t`${homeId.toString(16)}.jsonl`,\n\t\t);\n\t\tthis._networkCache = this.bindings.db.createInstance(networkCacheFile, {\n\t\t\t...options,\n\t\t\tserializer: serializeNetworkCacheValue,\n\t\t\treviver: deserializeNetworkCacheValue,\n\t\t});\n\t\tawait this._networkCache.open();\n\n\t\tif (getenv(\"NO_CACHE\") === \"true\") {\n\t\t\t// Since the network cache is append-only, we need to\n\t\t\t// clear it if the cache should be ignored\n\t\t\tthis._networkCache.clear();\n\t\t}\n\t}\n\n\tprivate async initValueDBs(homeId: number): Promise<void> {\n\t\tconst options = this.getJsonlDBOptions();\n\n\t\tconst valueDBFile = path.join(\n\t\t\tthis.cacheDir,\n\t\t\t`${homeId.toString(16)}.values.jsonl`,\n\t\t);\n\t\tthis._valueDB = this.bindings.db.createInstance(valueDBFile, {\n\t\t\t...options,\n\t\t\tenableTimestamps: true,\n\t\t\treviver: (_key, value) => deserializeCacheValue(value),\n\t\t\tserializer: (_key, value) => serializeCacheValue(value),\n\t\t});\n\t\tawait this._valueDB.open();\n\n\t\tconst metadataDBFile = path.join(\n\t\t\tthis.cacheDir,\n\t\t\t`${homeId.toString(16)}.metadata.jsonl`,\n\t\t);\n\t\tthis._metadataDB = this.bindings.db.createInstance(\n\t\t\tmetadataDBFile,\n\t\t\toptions,\n\t\t);\n\t\tawait this._metadataDB.open();\n\n\t\tif (getenv(\"NO_CACHE\") === \"true\") {\n\t\t\t// Since value/metadata DBs are append-only, we need to\n\t\t\t// clear them if the cache should be ignored\n\t\t\tthis._valueDB.clear();\n\t\t\tthis._metadataDB.clear();\n\t\t}\n\t}\n\n\tprivate async performCacheMigration(): Promise<void> {\n\t\tif (\n\t\t\t!this._controller\n\t\t\t|| !this.controller.homeId\n\t\t\t|| !this._networkCache\n\t\t\t|| !this._valueDB\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// In v9, the network cache was switched from a json file to use a Jsonl-DB\n\t\t// Therefore the legacy cache file must be migrated to the new format\n\t\tif (this._networkCache.size === 0) {\n\t\t\t// version the cache format, so migrations in the future are easier\n\t\t\tthis._networkCache.set(\"cacheFormat\", 1);\n\n\t\t\ttry {\n\t\t\t\tawait migrateLegacyNetworkCache(\n\t\t\t\t\tthis.controller.homeId,\n\t\t\t\t\tthis._networkCache,\n\t\t\t\t\tthis._valueDB,\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\tthis._options.storage.driver\n\t\t\t\t\t\t? wrapLegacyFSDriverForCacheMigrationOnly(\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\t\t\tthis._options.storage.driver,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t: this.bindings.fs,\n\t\t\t\t\tthis.cacheDir,\n\t\t\t\t);\n\n\t\t\t\t// Go through the value DB and remove all keys referencing commandClass -1, which used to be a\n\t\t\t\t// hacky way to store non-CC specific values\n\t\t\t\tfor (const key of this._valueDB.keys()) {\n\t\t\t\t\tif (-1 === key.indexOf(`,\"commandClass\":-1,`)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthis._valueDB.delete(key);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tconst message =\n\t\t\t\t\t`Migrating the legacy cache file to jsonl failed: ${\n\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`;\n\t\t\t\tthis.driverLog.print(message, \"error\");\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Initializes the variables for controller and nodes,\n\t * adds event handlers and starts the interview process.\n\t */\n\tprivate async initializeControllerAndNodes(): Promise<void> {\n\t\tif (this._controller) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The controller was already initialized!\",\n\t\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t\t);\n\t\t}\n\n\t\tthis._controller = new ZWaveController(this);\n\t\tthis._controller\n\t\t\t.on(\"node found\", this.onNodeFound.bind(this))\n\t\t\t.on(\"node added\", this.onNodeAdded.bind(this))\n\t\t\t.on(\"node removed\", this.onNodeRemoved.bind(this))\n\t\t\t.on(\n\t\t\t\t\"status changed\",\n\t\t\t\tthis.onControllerStatusChanged.bind(this),\n\t\t\t)\n\t\t\t.on(\"network found\", this.onNetworkFound.bind(this))\n\t\t\t.on(\"network joined\", this.onNetworkJoined.bind(this))\n\t\t\t.on(\"network left\", this.onNetworkLeft.bind(this));\n\n\t\t// Create and start all queues after creating the controller instance\n\t\tthis.initTransactionQueues();\n\t\tthis.initSerialAPIQueue();\n\n\t\tif (!this._options.testingHooks?.skipControllerIdentification) {\n\t\t\t// Determine what the controller can do\n\t\t\tconst { nodeIds } = await this.controller.queryCapabilities();\n\n\t\t\t// Configure the radio\n\t\t\tawait this.controller.queryAndConfigureRF();\n\n\t\t\t// Soft-reset the stick if possible.\n\t\t\t// On 700+ series, we'll also learn about whether the stick supports\n\t\t\t// Z-Wave Long Range in the current region.\n\t\t\tconst maySoftReset = this.maySoftReset();\n\t\t\tif (this._options.features.softReset && !maySoftReset) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t`Soft reset is enabled through config, but this stick does not support it.`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tthis._options.features.softReset = false;\n\t\t\t}\n\n\t\t\tif (maySoftReset) {\n\t\t\t\tawait this.softResetInternal(false);\n\t\t\t}\n\n\t\t\tlet lrNodeIds: readonly number[] | undefined;\n\t\t\tif (this.controller.supportsLongRange) {\n\t\t\t\t// If the controller supports ZWLR, we need to query the node IDs again\n\t\t\t\t// to get the full list of nodes\n\t\t\t\tlrNodeIds = (await this.controller.queryLongRangeCapabilities())\n\t\t\t\t\t.lrNodeIds;\n\t\t\t}\n\n\t\t\t// If the controller supports ZWLR in the current region, switch to\n\t\t\t// 16-bit node IDs. Otherwise, make sure that it is actually using 8-bit node IDs.\n\t\t\tawait this.controller.trySetNodeIDType(\n\t\t\t\tthis.controller.supportsLongRange\n\t\t\t\t\t? NodeIDType.Long\n\t\t\t\t\t: NodeIDType.Short,\n\t\t\t);\n\n\t\t\t// Now that we know the node ID type, we can identify the controller\n\t\t\tawait this.controller.identify();\n\n\t\t\t// Perform additional configuration\n\t\t\tawait this.controller.configure();\n\n\t\t\t// now that we know the home ID, we can open the databases\n\t\t\tawait this.initNetworkCache(this.controller.homeId!);\n\t\t\tawait this.initValueDBs(this.controller.homeId!);\n\t\t\tawait this.performCacheMigration();\n\n\t\t\t// Initialize all nodes and restore the data from cache\n\t\t\tawait this.controller.initNodes(\n\t\t\t\tnodeIds,\n\t\t\t\tlrNodeIds ?? [],\n\t\t\t\tasync () => {\n\t\t\t\t\t// Try to restore the network information from the cache\n\t\t\t\t\tif (getenv(\"NO_CACHE\") !== \"true\") {\n\t\t\t\t\t\t// FIXME: This entire thing is a pretty convoluted way of looking up the device config\n\t\t\t\t\t\tawait this.restoreNetworkStructureFromCache();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// For controllers with proprietary implementations, interview them too\n\t\t\tawait this.controller.interviewProprietary();\n\n\t\t\tthis.controllerLog.print(\"Interview completed\");\n\n\t\t\tif (this.controller.role === ControllerRole.Primary) {\n\t\t\t\t// Auto-enable smart start inclusion\n\t\t\t\tthis.controller.autoProvisionSmartStart();\n\t\t\t}\n\t\t} else {\n\t\t\t// When skipping the controller identification, set the flags to consider the controller a primary\n\t\t\tthis.controller[\"_wasRealPrimary\"] = true;\n\t\t\tthis.controller[\"_isSUC\"] = true;\n\t\t\tthis.controller[\"_isSISPresent\"] = true;\n\t\t\tthis.controller[\"_sucNodeId\"] = 1;\n\t\t}\n\n\t\tif (this.controller.role === ControllerRole.Primary) {\n\t\t\t// Set up the S0 security manager. We can only do that after the controller\n\t\t\t// interview because we need to know the controller node id.\n\t\t\tconst S0Key = this._options.securityKeys?.S0_Legacy;\n\t\t\tif (S0Key) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Network key for S0 configured, enabling S0 security manager...\",\n\t\t\t\t);\n\t\t\t\tthis._securityManager = new SecurityManager({\n\t\t\t\t\tnetworkKey: S0Key,\n\t\t\t\t\townNodeId: this._controller.ownNodeId!,\n\t\t\t\t\tnonceTimeout: this._options.timeouts.nonce,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"No network key for S0 configured, communication with secure (S0) devices won't work!\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// The S2 security manager could be initialized earlier, but we do it here for consistency\n\t\t\tif (\n\t\t\t\tthis._options.securityKeys\n\t\t\t\t// Only set it up if we have security keys for at least one S2 security class\n\t\t\t\t&& Object.keys(this._options.securityKeys).some(\n\t\t\t\t\t(key) =>\n\t\t\t\t\t\tkey.startsWith(\"S2_\")\n\t\t\t\t\t\t&& key in SecurityClass\n\t\t\t\t\t\t&& securityClassIsS2((SecurityClass as any)[key]),\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"At least one network key for S2 configured, enabling S2 security manager...\",\n\t\t\t\t);\n\t\t\t\tthis._securityManager2 = await SecurityManager2.create();\n\t\t\t\t// Set up all keys\n\t\t\t\tfor (\n\t\t\t\t\tconst secClass of [\n\t\t\t\t\t\t\"S2_Unauthenticated\",\n\t\t\t\t\t\t\"S2_Authenticated\",\n\t\t\t\t\t\t\"S2_AccessControl\",\n\t\t\t\t\t\t\"S0_Legacy\",\n\t\t\t\t\t] as const\n\t\t\t\t) {\n\t\t\t\t\tconst key = this._options.securityKeys[secClass];\n\t\t\t\t\tif (key) {\n\t\t\t\t\t\tawait this._securityManager2.setKey(\n\t\t\t\t\t\t\tSecurityClass[secClass],\n\t\t\t\t\t\t\tkey,\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.driverLog.print(\n\t\t\t\t\t\"No network key for S2 configured, communication with secure (S2) devices won't work!\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tthis._options.securityKeysLongRange?.S2_AccessControl\n\t\t\t\t|| this._options.securityKeysLongRange?.S2_Authenticated\n\t\t\t) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"At least one network key for Z-Wave Long Range configured, enabling security manager...\",\n\t\t\t\t);\n\t\t\t\tthis._securityManagerLR = await SecurityManager2.create();\n\t\t\t\tif (this._options.securityKeysLongRange?.S2_AccessControl) {\n\t\t\t\t\tawait this._securityManagerLR.setKey(\n\t\t\t\t\t\tSecurityClass.S2_AccessControl,\n\t\t\t\t\t\tthis._options.securityKeysLongRange.S2_AccessControl,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (this._options.securityKeysLongRange?.S2_Authenticated) {\n\t\t\t\t\tawait this._securityManagerLR.setKey(\n\t\t\t\t\t\tSecurityClass.S2_Authenticated,\n\t\t\t\t\t\tthis._options.securityKeysLongRange.S2_Authenticated,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"No network key for Z-Wave Long Range configured, communication won't work!\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\t// Secondary controller - load security keys from cache.\n\t\t\t// Either LR or S2+S0, not both\n\t\t\t// FIXME: The fallback code duplicates the logic from the primary controller above. Find a nicer solution.\n\t\t\tif (isLongRangeNodeId(this.controller.ownNodeId!)) {\n\t\t\t\tconst securityKeysLongRange = [\n\t\t\t\t\tSecurityClass.S2_AccessControl,\n\t\t\t\t\tSecurityClass.S2_Authenticated,\n\t\t\t\t].map(\n\t\t\t\t\t(sc) => ([\n\t\t\t\t\t\tsc,\n\t\t\t\t\t\tthis.cacheGet<Uint8Array>(\n\t\t\t\t\t\t\tcacheKeys.controller.securityKeysLongRange(sc),\n\t\t\t\t\t\t),\n\t\t\t\t\t] as [SecurityClass, Uint8Array | undefined]),\n\t\t\t\t).filter((v): v is [SecurityClass, Uint8Array] =>\n\t\t\t\t\tv[1] != undefined\n\t\t\t\t);\n\t\t\t\tif (securityKeysLongRange.length) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"At least one network key for Z-Wave Long Range found in cache, enabling security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManagerLR = await SecurityManager2.create();\n\t\t\t\t\tfor (const [sc, key] of securityKeysLongRange) {\n\t\t\t\t\t\tawait this._securityManagerLR.setKey(sc, key);\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tthis._options.securityKeysLongRange?.S2_AccessControl\n\t\t\t\t\t|| this._options.securityKeysLongRange?.S2_Authenticated\n\t\t\t\t) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Fallback to configured network keys for Z-Wave Long Range, enabling security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManagerLR = await SecurityManager2.create();\n\t\t\t\t\tif (this._options.securityKeysLongRange?.S2_AccessControl) {\n\t\t\t\t\t\tawait this._securityManagerLR.setKey(\n\t\t\t\t\t\t\tSecurityClass.S2_AccessControl,\n\t\t\t\t\t\t\tthis._options.securityKeysLongRange\n\t\t\t\t\t\t\t\t.S2_AccessControl,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (this._options.securityKeysLongRange?.S2_Authenticated) {\n\t\t\t\t\t\tawait this._securityManagerLR.setKey(\n\t\t\t\t\t\t\tSecurityClass.S2_Authenticated,\n\t\t\t\t\t\t\tthis._options.securityKeysLongRange\n\t\t\t\t\t\t\t\t.S2_Authenticated,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"No network key for Z-Wave Long Range configured, communication won't work!\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst s0Key = this.cacheGet<Uint8Array>(\n\t\t\t\t\tcacheKeys.controller.securityKeys(SecurityClass.S0_Legacy),\n\t\t\t\t);\n\t\t\t\tif (s0Key) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Network key for S0 found in cache, enabling S0 security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManager = new SecurityManager({\n\t\t\t\t\t\tnetworkKey: s0Key,\n\t\t\t\t\t\townNodeId: this._controller.ownNodeId!,\n\t\t\t\t\t\tnonceTimeout: this._options.timeouts.nonce,\n\t\t\t\t\t});\n\t\t\t\t} else if (this._options.securityKeys?.S0_Legacy) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Fallback to configured S0 network key, enabling S0 security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManager = new SecurityManager({\n\t\t\t\t\t\tnetworkKey: this._options.securityKeys.S0_Legacy,\n\t\t\t\t\t\townNodeId: this._controller.ownNodeId!,\n\t\t\t\t\t\tnonceTimeout: this._options.timeouts.nonce,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"No network key for S0 found in cache, communication with secure (S0) devices won't work!\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst securityKeys = securityClassOrder.map(\n\t\t\t\t\t(sc) => ([\n\t\t\t\t\t\tsc,\n\t\t\t\t\t\tthis.cacheGet<Uint8Array>(\n\t\t\t\t\t\t\tcacheKeys.controller.securityKeys(sc),\n\t\t\t\t\t\t),\n\t\t\t\t\t] as [SecurityClass, Uint8Array | undefined]),\n\t\t\t\t).filter((v): v is [SecurityClass, Uint8Array] =>\n\t\t\t\t\tv[1] != undefined\n\t\t\t\t);\n\t\t\t\tif (securityKeys.length) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"At least one network key for S2 found in cache, enabling S2 security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManager2 = await SecurityManager2.create();\n\t\t\t\t\tfor (const [sc, key] of securityKeys) {\n\t\t\t\t\t\tawait this._securityManager2.setKey(sc, key);\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tthis._options.securityKeys\n\t\t\t\t\t&& Object.keys(this._options.securityKeys).some(\n\t\t\t\t\t\t(key) =>\n\t\t\t\t\t\t\tkey.startsWith(\"S2_\")\n\t\t\t\t\t\t\t&& key in SecurityClass\n\t\t\t\t\t\t\t&& securityClassIsS2((SecurityClass as any)[key]),\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Fallback to configured network keys for S2, enabling S2 security manager...\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._securityManager2 = await SecurityManager2.create();\n\t\t\t\t\t// Set up all keys\n\t\t\t\t\tfor (\n\t\t\t\t\t\tconst secClass of [\n\t\t\t\t\t\t\t\"S2_Unauthenticated\",\n\t\t\t\t\t\t\t\"S2_Authenticated\",\n\t\t\t\t\t\t\t\"S2_AccessControl\",\n\t\t\t\t\t\t\t\"S0_Legacy\",\n\t\t\t\t\t\t] as const\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst key = this._options.securityKeys[secClass];\n\t\t\t\t\t\tif (key) {\n\t\t\t\t\t\t\tawait this._securityManager2.setKey(\n\t\t\t\t\t\t\t\tSecurityClass[secClass],\n\t\t\t\t\t\t\t\tkey,\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 {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"No network key for S2 found in cache, communication with secure (S2) devices won't work!\",\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\n\t\t// in any case we need to emit the driver ready event here\n\t\tthis._controllerInterviewed = true;\n\t\tthis.driverLog.print(\"driver ready\");\n\t\tthis.emit(\"driver ready\");\n\n\t\t// Add event handlers for the nodes\n\t\tfor (const node of this._controller.nodes.values()) {\n\t\t\tthis.addNodeEventHandlers(node);\n\t\t}\n\n\t\tif (this.controller.role === ControllerRole.Primary) {\n\t\t\t// Before interviewing nodes reset our knowledge about their ready state\n\t\t\tthis._nodesReady.clear();\n\t\t\tthis._nodesReadyEventEmitted = false;\n\n\t\t\tif (!this._options.testingHooks?.skipNodeInterview) {\n\t\t\t\t// Now interview all nodes\n\t\t\t\t// First complete the controller interview\n\t\t\t\tconst controllerNode = this._controller.nodes.get(\n\t\t\t\t\tthis._controller.ownNodeId!,\n\t\t\t\t)!;\n\t\t\t\tawait this.interviewNodeInternal(controllerNode);\n\t\t\t\t// The controller node is always alive\n\t\t\t\tcontrollerNode.markAsAlive();\n\n\t\t\t\t// Then do all the nodes in parallel, but prioritize nodes that are more likely to be ready\n\t\t\t\tconst nodeInterviewOrder = [...this._controller.nodes.values()]\n\t\t\t\t\t.filter((n) => n.id !== this._controller!.ownNodeId)\n\t\t\t\t\t.sort((a, b) =>\n\t\t\t\t\t\t// Fully-interviewed devices first (need the least amount of communication now)\n\t\t\t\t\t\t(b.interviewStage - a.interviewStage)\n\t\t\t\t\t\t// Always listening -> FLiRS -> sleeping\n\t\t\t\t\t\t|| (\n\t\t\t\t\t\t\t(b.isListening ? 2 : b.isFrequentListening ? 1 : 0)\n\t\t\t\t\t\t\t- (a.isListening\n\t\t\t\t\t\t\t\t? 2\n\t\t\t\t\t\t\t\t: a.isFrequentListening\n\t\t\t\t\t\t\t\t? 1\n\t\t\t\t\t\t\t\t: 0)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Then by last seen, more recently first\n\t\t\t\t\t\t|| (\n\t\t\t\t\t\t\t(b.lastSeen?.getTime() ?? 0)\n\t\t\t\t\t\t\t- (a.lastSeen?.getTime() ?? 0)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Lastly ascending by node ID\n\t\t\t\t\t\t|| (a.id - b.id)\n\t\t\t\t\t);\n\n\t\t\t\tif (nodeInterviewOrder.length) {\n\t\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t\t`Interviewing nodes and/or determining their status: ${\n\t\t\t\t\t\t\tnodeInterviewOrder.map((n) => n.id).join(\", \")\n\t\t\t\t\t\t}`,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const node of nodeInterviewOrder) {\n\t\t\t\t\t\tif (node.canSleep) {\n\t\t\t\t\t\t\t// A node that can sleep should be assumed to be sleeping after resuming from cache\n\t\t\t\t\t\t\tnode.markAsAsleep();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvoid (async () => {\n\t\t\t\t\t\t\t// Continue the interview if necessary. If that is not necessary, at least\n\t\t\t\t\t\t\t// determine the node's status\n\t\t\t\t\t\t\tif (node.interviewStage < InterviewStage.Complete) {\n\t\t\t\t\t\t\t\tawait this.interviewNodeInternal(node);\n\t\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\t\tnode.isListening || node.isFrequentListening\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// Ping non-sleeping nodes to determine their status\n\t\t\t\t\t\t\t\tawait node.ping();\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\t\t\t}\n\t\t} else {\n\t\t\tif (!this._options.testingHooks?.skipNodeInterview) {\n\t\t\t\t// We're a secondary controller. Just determine if nodes are ready and do the interview at another time.\n\n\t\t\t\t// First complete the controller \"interview\"\n\t\t\t\tconst controllerNode = this._controller.nodes.get(\n\t\t\t\t\tthis._controller.ownNodeId!,\n\t\t\t\t)!;\n\t\t\t\tawait this.interviewNodeInternal(controllerNode);\n\t\t\t\t// The controller node is always alive\n\t\t\t\tcontrollerNode.markAsAlive();\n\n\t\t\t\t// Query the protocol information from the controller\n\t\t\t\tfor (const node of this._controller.nodes.values()) {\n\t\t\t\t\tif (node.isControllerNode) continue;\n\t\t\t\t\tawait node[\"queryProtocolInfo\"]();\n\t\t\t\t}\n\n\t\t\t\t// Then ping (frequently) listening nodes to determine their status\n\t\t\t\tconst nodeInterviewOrder = [...this._controller.nodes.values()]\n\t\t\t\t\t.filter((n) => n.id !== this._controller!.ownNodeId)\n\t\t\t\t\t.filter((n) => n.isListening || n.isFrequentListening)\n\t\t\t\t\t.sort((a, b) =>\n\t\t\t\t\t\t// Always listening -> FLiRS\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\t(b.isListening ? 1 : 0)\n\t\t\t\t\t\t\t- (a.isListening ? 1 : 0)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Then by last seen, more recently first\n\t\t\t\t\t\t|| (\n\t\t\t\t\t\t\t(b.lastSeen?.getTime() ?? 0)\n\t\t\t\t\t\t\t- (a.lastSeen?.getTime() ?? 0)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Lastly ascending by node ID\n\t\t\t\t\t\t|| (a.id - b.id)\n\t\t\t\t\t);\n\n\t\t\t\tif (nodeInterviewOrder.length) {\n\t\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t\t`Determining node status: ${\n\t\t\t\t\t\t\tnodeInterviewOrder.map((n) => n.id).join(\", \")\n\t\t\t\t\t\t}`,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const node of nodeInterviewOrder) {\n\t\t\t\t\t\tvoid node.ping();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If we only have sleeping nodes or a controller-only network, the send\n\t\t// thread is idle before the driver gets marked ready, the idle tasks won't be triggered.\n\t\t// So do it manually.\n\t\tthis.handleQueueIdleChange(this.queueIdle);\n\t}\n\n\tprivate autoRefreshNodeValueTimers = new Map<number, Interval>();\n\tprivate retryNodeInterviewTimeouts = new Map<number, Timer>();\n\t/**\n\t * @internal\n\t * Starts or resumes the interview of a Z-Wave node. It is advised to NOT\n\t * await this method as it can take a very long time (minutes to hours)!\n\t *\n\t * WARNING: Do not call this method from application code. To refresh the information\n\t * for a specific node, use `node.refreshInfo()` instead\n\t */\n\tpublic async interviewNodeInternal(node: ZWaveNode): Promise<void> {\n\t\tif (node.interviewStage === InterviewStage.Complete) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Avoid having multiple restart timeouts active\n\t\tif (this.retryNodeInterviewTimeouts.has(node.id)) {\n\t\t\tthis.retryNodeInterviewTimeouts.get(node.id)?.clear();\n\t\t\tthis.retryNodeInterviewTimeouts.delete(node.id);\n\t\t}\n\n\t\t// Drop all pending messages that come from a previous interview attempt\n\t\tawait this.rejectTransactions(\n\t\t\t(t) =>\n\t\t\t\tt.message.getNodeId() === node.id\n\t\t\t\t&& (t.priority === MessagePriority.NodeQuery\n\t\t\t\t\t|| t.tag === \"interview\"),\n\t\t\t\"The interview was restarted\",\n\t\t\tZWaveErrorCodes.Controller_InterviewRestarted,\n\t\t);\n\n\t\tconst maxInterviewAttempts = this._options.attempts.nodeInterview;\n\n\t\ttry {\n\t\t\tif (!(await node.interviewInternal())) {\n\t\t\t\t// Find out if we may retry the interview\n\t\t\t\tif (node.status === NodeStatus.Dead) {\n\t\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t`Interview attempt (${node.interviewAttempts}/${maxInterviewAttempts}) failed, node is dead.`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tnode.emit(\"interview failed\", node, {\n\t\t\t\t\t\terrorMessage: \"The node is dead\",\n\t\t\t\t\t\tisFinal: true,\n\t\t\t\t\t});\n\t\t\t\t} else if (node.interviewAttempts < maxInterviewAttempts) {\n\t\t\t\t\t// This is most likely because the node is unable to handle our load of requests now. Give it some time\n\t\t\t\t\tconst retryTimeout = Math.min(\n\t\t\t\t\t\t30000,\n\t\t\t\t\t\tnode.interviewAttempts * 5000,\n\t\t\t\t\t);\n\t\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t`Interview attempt ${node.interviewAttempts}/${maxInterviewAttempts} failed, retrying in ${retryTimeout} ms...`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tnode.emit(\"interview failed\", node, {\n\t\t\t\t\t\terrorMessage:\n\t\t\t\t\t\t\t`Attempt ${node.interviewAttempts}/${maxInterviewAttempts} failed`,\n\t\t\t\t\t\tisFinal: false,\n\t\t\t\t\t\tattempt: node.interviewAttempts,\n\t\t\t\t\t\tmaxAttempts: maxInterviewAttempts,\n\t\t\t\t\t});\n\t\t\t\t\t// Schedule the retry and remember the timeout instance\n\t\t\t\t\tthis.retryNodeInterviewTimeouts.set(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\tsetTimer(() => {\n\t\t\t\t\t\t\tthis.retryNodeInterviewTimeouts.delete(node.id);\n\t\t\t\t\t\t\tvoid this.interviewNodeInternal(node);\n\t\t\t\t\t\t}, retryTimeout).unref(),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t`Failed all interview attempts, giving up.`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tnode.emit(\"interview failed\", node, {\n\t\t\t\t\t\terrorMessage: `Maximum interview attempts reached`,\n\t\t\t\t\t\tisFinal: true,\n\t\t\t\t\t\tattempt: maxInterviewAttempts,\n\t\t\t\t\t\tmaxAttempts: maxInterviewAttempts,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\tnode.manufacturerId != undefined\n\t\t\t\t&& node.productType != undefined\n\t\t\t\t&& node.productId != undefined\n\t\t\t\t&& node.firmwareVersion != undefined\n\t\t\t\t&& !node.deviceConfig\n\t\t\t\t&& process.env.NODE_ENV !== \"test\"\n\t\t\t) {\n\t\t\t\t// The interview succeeded, but we don't have a device config for this node.\n\t\t\t\t// Report it, so we can add a config file\n\n\t\t\t\tvoid reportMissingDeviceConfig(this, node as any).catch(noop);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (isZWaveError(e)) {\n\t\t\t\tif (\n\t\t\t\t\te.code === ZWaveErrorCodes.Driver_NotReady\n\t\t\t\t\t|| e.code === ZWaveErrorCodes.Controller_NodeRemoved\n\t\t\t\t) {\n\t\t\t\t\t// This only happens when a node is removed during the interview - we don't log this\n\t\t\t\t\treturn;\n\t\t\t\t} else if (\n\t\t\t\t\te.code === ZWaveErrorCodes.Controller_InterviewRestarted\n\t\t\t\t) {\n\t\t\t\t\t// The interview was restarted by a user - we don't log this\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\tnode.id,\n\t\t\t\t\t`Error during node interview: ${e.message}`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Adds the necessary event handlers for a node instance */\n\tprivate addNodeEventHandlers(node: ZWaveNode): void {\n\t\tnode.on(\"wake up\", this.onNodeWakeUp.bind(this))\n\t\t\t.on(\"sleep\", this.onNodeSleep.bind(this))\n\t\t\t.on(\"alive\", this.onNodeAlive.bind(this))\n\t\t\t.on(\"dead\", this.onNodeDead.bind(this))\n\t\t\t.on(\"interview completed\", this.onNodeInterviewCompleted.bind(this))\n\t\t\t.on(\"ready\", this.onNodeReady.bind(this))\n\t\t\t.on(\n\t\t\t\t\"firmware update finished\",\n\t\t\t\tthis.onNodeFirmwareUpdated.bind(this),\n\t\t\t)\n\t\t\t.on(\"notification\", this.onNodeNotification.bind(this));\n\n\t\t// Add forwarders for all node events\n\t\tfor (const event of zWaveNodeEvents) {\n\t\t\tnode.on(event, (...args: any[]) => {\n\t\t\t\t// @ts-expect-error We made sure that args matches\n\t\t\t\tthis.emit(`node ${event}`, ...args);\n\t\t\t});\n\t\t}\n\t}\n\n\t/** Removes a node's event handlers that were added with addNodeEventHandlers */\n\tprivate removeNodeEventHandlers(node: ZWaveNode): void {\n\t\tnode.removeAllListeners();\n\t}\n\n\t/** Is called when a node wakes up */\n\tprivate onNodeWakeUp(node: ZWaveNode, oldStatus: NodeStatus): void {\n\t\tthis.controllerLog.logNode(\n\t\t\tnode.id,\n\t\t\t`The node is ${\n\t\t\t\toldStatus === NodeStatus.Unknown ? \"\" : \"now \"\n\t\t\t}awake.`,\n\t\t);\n\n\t\t// Make sure to handle the pending messages as quickly as possible\n\t\tif (oldStatus === NodeStatus.Asleep) {\n\t\t\tvoid this.reduceQueues(({ message }) => {\n\t\t\t\t// Ignore messages that are not for this node\n\t\t\t\tif (message.getNodeId() !== node.id) return { type: \"keep\" };\n\t\t\t\t// Resolve pings, so we don't need to send them (we know the node is awake)\n\t\t\t\tif (messageIsPing(message)) {\n\t\t\t\t\treturn { type: \"resolve\", message: undefined };\n\t\t\t\t}\n\t\t\t\t// Re-queue all other transactions for this node, so they get added in front of the others\n\t\t\t\treturn { type: \"requeue\" };\n\t\t\t});\n\t\t}\n\n\t\t// Start the timer for sending the node to sleep again\n\t\tthis.debounceSendNodeToSleep(node);\n\t}\n\n\t/** Is called when a node goes to sleep */\n\tprivate onNodeSleep(node: ZWaveNode, oldStatus: NodeStatus): void {\n\t\tthis.controllerLog.logNode(\n\t\t\tnode.id,\n\t\t\t`The node is ${\n\t\t\t\toldStatus === NodeStatus.Unknown ? \"\" : \"now \"\n\t\t\t}asleep.`,\n\t\t);\n\n\t\t// Move all its pending messages to the WakeupQueue\n\t\t// This clears the current transaction and continues sending the next messages\n\t\tthis.moveMessagesToWakeupQueue(node.id);\n\t}\n\n\t/** Is called when a previously dead node starts communicating again */\n\tprivate onNodeAlive(node: ZWaveNode, oldStatus: NodeStatus): void {\n\t\tthis.controllerLog.logNode(\n\t\t\tnode.id,\n\t\t\t`The node is ${\n\t\t\t\toldStatus === NodeStatus.Unknown ? \"\" : \"now \"\n\t\t\t}alive.`,\n\t\t);\n\t\tif (\n\t\t\toldStatus === NodeStatus.Dead\n\t\t\t&& node.interviewStage !== InterviewStage.Complete\n\t\t\t&& !this._options.testingHooks?.skipNodeInterview\n\t\t) {\n\t\t\tvoid this.interviewNodeInternal(node);\n\t\t}\n\t}\n\n\t/** Is called when a node is marked as dead */\n\tprivate onNodeDead(node: ZWaveNode, oldStatus: NodeStatus): void {\n\t\tthis.controllerLog.logNode(\n\t\t\tnode.id,\n\t\t\t`The node is ${\n\t\t\t\toldStatus === NodeStatus.Unknown ? \"\" : \"now \"\n\t\t\t}dead.`,\n\t\t);\n\n\t\t// This could mean that we need to ignore it in the all nodes ready check,\n\t\t// so perform the check again\n\t\tthis.checkAllNodesReady();\n\t}\n\n\t/** Is called when a node is ready to be used */\n\tprivate onNodeReady(node: ZWaveNode): void {\n\t\tthis._nodesReady.add(node.id);\n\t\tthis.controllerLog.logNode(node.id, \"The node is ready to be used\");\n\n\t\t// Regularly check if values of non-sleeping nodes need to be refreshed per the specs\n\t\t// For sleeping nodes this is done on wakeup\n\t\tif (this.autoRefreshNodeValueTimers.has(node.id)) {\n\t\t\tthis.autoRefreshNodeValueTimers.get(node.id)?.clear();\n\t\t\tthis.autoRefreshNodeValueTimers.delete(node.id);\n\t\t}\n\t\tif (!node.canSleep) {\n\t\t\t// Randomize the interval so we don't get a flood of queries for all listening nodes\n\t\t\tconst intervalMinutes = 50 + Math.random() * 20;\n\t\t\tthis.autoRefreshNodeValueTimers.set(\n\t\t\t\tnode.id,\n\t\t\t\tsetInterval(() => {\n\t\t\t\t\tvoid node.autoRefreshValues().catch(() => {\n\t\t\t\t\t\t// ignore errors\n\t\t\t\t\t});\n\t\t\t\t}, timespan.minutes(intervalMinutes)).unref(),\n\t\t\t);\n\t\t}\n\n\t\tthis.checkAllNodesReady();\n\t}\n\n\t/** Checks if all nodes are ready and emits the \"all nodes ready\" event if they are */\n\tprivate checkAllNodesReady(): void {\n\t\t// Only emit \"all nodes ready\" once\n\t\tif (this._nodesReadyEventEmitted) return;\n\n\t\tfor (const [id, node] of this.controller.nodes) {\n\t\t\t// Ignore dead nodes or the all nodes ready event will never be emitted without physical user interaction\n\t\t\tif (node.status === NodeStatus.Dead) continue;\n\n\t\t\tif (!this._nodesReady.has(id)) return;\n\t\t}\n\t\t// All nodes are ready\n\t\tthis.controllerLog.print(\"All nodes are ready to be used\");\n\t\tthis.emit(\"all nodes ready\");\n\t\tthis._nodesReadyEventEmitted = true;\n\n\t\t// We know we have all data, this is the time to send statistics (when enabled)\n\t\tvoid this.compileAndSendStatistics().catch(() => {\n\t\t\t/* ignore */\n\t\t});\n\t}\n\n\tprivate _statisticsEnabled: boolean = false;\n\t/** Whether reporting usage statistics is currently enabled */\n\tpublic get statisticsEnabled(): boolean {\n\t\treturn this._statisticsEnabled;\n\t}\n\n\tprivate statisticsAppInfo:\n\t\t| Pick<AppInfo, \"applicationName\" | \"applicationVersion\">\n\t\t| undefined;\n\n\tprivate userAgentComponents = new Map<string, string>();\n\n\t/**\n\t * Updates individual components of the user agent. Versions for individual applications can be added or removed.\n\t * @param components An object with application/module/component names and their versions. Set a version to `null` or `undefined` explicitly to remove it from the user agent.\n\t */\n\tpublic updateUserAgent(\n\t\tcomponents: Record<string, string | null | undefined>,\n\t): void {\n\t\tthis.userAgentComponents = mergeUserAgent(\n\t\t\tthis.userAgentComponents,\n\t\t\tcomponents,\n\t\t);\n\t\tthis._userAgent = this.getEffectiveUserAgentString(\n\t\t\tthis.userAgentComponents,\n\t\t);\n\t}\n\n\t/**\n\t * Returns the effective user agent string for the given components.\n\t * The driver name and version is automatically prepended and the statisticsAppInfo data is automatically appended if no components were given.\n\t */\n\tprivate getEffectiveUserAgentString(\n\t\tcomponents: Map<string, string>,\n\t): string {\n\t\tconst effectiveComponents = new Map([\n\t\t\t[libName, libVersion],\n\t\t\t...components,\n\t\t]);\n\t\tif (\n\t\t\teffectiveComponents.size === 1\n\t\t\t&& this.statisticsAppInfo\n\t\t\t&& this.statisticsAppInfo.applicationName !== \"node-zwave-js\"\n\t\t\t// node-zwave-js was renamed to just zwave-js in v15\n\t\t\t&& this.statisticsAppInfo.applicationName !== \"zwave-js\"\n\t\t) {\n\t\t\teffectiveComponents.set(\n\t\t\t\tthis.statisticsAppInfo.applicationName,\n\t\t\t\tthis.statisticsAppInfo.applicationVersion,\n\t\t\t);\n\t\t}\n\t\treturn userAgentComponentsToString(effectiveComponents);\n\t}\n\n\tprivate _userAgent: string = `zwave-js/${libVersion}`;\n\t/** Returns the user agent string used for service requests */\n\tpublic get userAgent(): string {\n\t\treturn this._userAgent;\n\t}\n\n\t/** Returns the user agent string combined with the additional components (if given) */\n\tpublic getUserAgentStringWithComponents(\n\t\tcomponents?: Record<string, string | null | undefined>,\n\t): string {\n\t\tif (!components || Object.keys(components).length === 0) {\n\t\t\treturn this._userAgent;\n\t\t}\n\n\t\tconst merged = mergeUserAgent(\n\t\t\tthis.userAgentComponents,\n\t\t\tcomponents,\n\t\t\tfalse,\n\t\t);\n\t\treturn this.getEffectiveUserAgentString(merged);\n\t}\n\n\t/**\n\t * Enable sending usage statistics. Although this does not include any sensitive information, we expect that you\n\t * inform your users before enabling statistics.\n\t */\n\tpublic enableStatistics(\n\t\tappInfo: Pick<AppInfo, \"applicationName\" | \"applicationVersion\">,\n\t): void {\n\t\tif (this._statisticsEnabled) return;\n\n\t\tif (\n\t\t\t!isObject(appInfo)\n\t\t\t|| typeof appInfo.applicationName !== \"string\"\n\t\t\t|| typeof appInfo.applicationVersion !== \"string\"\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The application statistics must be an object with two string properties \"applicationName\" and \"applicationVersion\"!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t} else if (appInfo.applicationName.length > 100) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The applicationName for statistics must be maximum 100 characters long!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t} else if (appInfo.applicationVersion.length > 100) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The applicationVersion for statistics must be maximum 100 characters long!`,\n\t\t\t\tZWaveErrorCodes.Driver_InvalidOptions,\n\t\t\t);\n\t\t}\n\n\t\tthis._statisticsEnabled = true;\n\t\tthis.statisticsAppInfo = appInfo;\n\n\t\t// If we're already ready, send statistics\n\t\tif (this._nodesReadyEventEmitted) {\n\t\t\tvoid this.compileAndSendStatistics().catch(() => {\n\t\t\t\t/* ignore */\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Disable sending usage statistics\n\t */\n\tpublic disableStatistics(): void {\n\t\tthis._statisticsEnabled = false;\n\t\tthis.statisticsAppInfo = undefined;\n\t\tthis.statisticsTimeout?.clear();\n\t\tthis.statisticsTimeout = undefined;\n\t}\n\n\t/** @internal */\n\t// eslint-disable-next-line @typescript-eslint/require-await\n\tpublic async getUUID(): Promise<string> {\n\t\t// To anonymously identify a network, we create a unique ID and use it to salt the Home ID\n\t\tif (!this._valueDB!.has(\"uuid\")) {\n\t\t\tthis._valueDB!.set(\n\t\t\t\t\"uuid\",\n\t\t\t\tBytes.view(randomBytes(32)).toString(\"hex\"),\n\t\t\t);\n\t\t}\n\t\tconst ret = this._valueDB!.get(\"uuid\") as string;\n\t\treturn ret;\n\t}\n\n\tprivate statisticsTimeout: Timer | undefined;\n\tprivate async compileAndSendStatistics(): Promise<void> {\n\t\t// Don't send anything if statistics are not enabled\n\t\tif (!this.statisticsEnabled || !this.statisticsAppInfo) return;\n\n\t\tthis.statisticsTimeout?.clear();\n\t\tthis.statisticsTimeout = undefined;\n\n\t\tlet success: number | boolean = false;\n\t\ttry {\n\t\t\tconst statistics = await compileStatistics(this, {\n\t\t\t\tdriverVersion: libVersion,\n\t\t\t\t...this.statisticsAppInfo,\n\t\t\t\tnodeVersion: process.versions.node,\n\t\t\t\tos: process.platform,\n\t\t\t\tarch: process.arch,\n\t\t\t});\n\t\t\tsuccess = await sendStatistics(statistics);\n\t\t} catch {\n\t\t\t// Didn't work - try again in a few hours\n\t\t\tsuccess = false;\n\t\t} finally {\n\t\t\tif (typeof success === \"number\") {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t`Sending usage statistics was rate limited - next attempt scheduled in ${success} seconds.`,\n\t\t\t\t\t\"verbose\",\n\t\t\t\t);\n\t\t\t\t// Wait at most 6 hours to try again\n\t\t\t\tconst retryMs = Math.max(\n\t\t\t\t\ttimespan.minutes(1),\n\t\t\t\t\tMath.min(success * 1000, timespan.hours(6)),\n\t\t\t\t);\n\t\t\t\tthis.statisticsTimeout = setTimer(() => {\n\t\t\t\t\tvoid this.compileAndSendStatistics();\n\t\t\t\t}, retryMs).unref();\n\t\t\t} else {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\tsuccess\n\t\t\t\t\t\t? `Usage statistics sent - next transmission scheduled in 23 hours.`\n\t\t\t\t\t\t: `Failed to send usage statistics - next transmission scheduled in 6 hours.`,\n\t\t\t\t\t\"verbose\",\n\t\t\t\t);\n\t\t\t\tthis.statisticsTimeout = setTimer(() => {\n\t\t\t\t\tvoid this.compileAndSendStatistics();\n\t\t\t\t}, timespan.hours(success ? 23 : 6)).unref();\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Is called when a node interview is completed */\n\tprivate onNodeInterviewCompleted(node: ZWaveNode): void {\n\t\tthis.debounceSendNodeToSleep(node);\n\t}\n\n\t/** This is called when a new node was found and is being added to the network */\n\tprivate onNodeFound(node: FoundNode): void {\n\t\t// GH#7692: In some cases, an old node's info may be left in the value DB,\n\t\t// causing incorrect behavior during interview.\n\n\t\t// Delete from value and metadata DBs\n\t\tconst prefix = `{\"nodeId\":${node.id},`;\n\t\tfor (const key of this.valueDB!.keys()) {\n\t\t\tif (key.startsWith(prefix)) {\n\t\t\t\tthis.valueDB!.delete(key);\n\t\t\t}\n\t\t}\n\t\tfor (const key of this.metadataDB!.keys()) {\n\t\t\tif (key.startsWith(prefix)) {\n\t\t\t\tthis.metadataDB!.delete(key);\n\t\t\t}\n\t\t}\n\n\t\tthis.cachePurge(cacheKeys.node(node.id)._baseKey);\n\t}\n\n\t/** This is called when a new node has been added to the network */\n\tprivate onNodeAdded(node: ZWaveNode): void {\n\t\tthis.addNodeEventHandlers(node);\n\n\t\tif (this._options.interview?.disableOnNodeAdded) return;\n\t\tif (this._options.testingHooks?.skipNodeInterview) return;\n\n\t\t// Interview the node\n\t\t// don't await the interview, because it may take a very long time\n\t\t// if a node is asleep\n\t\tvoid this.interviewNodeInternal(node);\n\t}\n\n\t/** This is called when a node was removed from the network */\n\tprivate onNodeRemoved(node: ZWaveNode, reason: RemoveNodeReason): void {\n\t\t// Remove all listeners and timers\n\t\tthis.removeNodeEventHandlers(node);\n\t\tif (this.sendNodeToSleepTimers.has(node.id)) {\n\t\t\tthis.sendNodeToSleepTimers.get(node.id)?.clear();\n\t\t\tthis.sendNodeToSleepTimers.delete(node.id);\n\t\t}\n\t\tif (this.retryNodeInterviewTimeouts.has(node.id)) {\n\t\t\tthis.retryNodeInterviewTimeouts.get(node.id)?.clear();\n\t\t\tthis.retryNodeInterviewTimeouts.delete(node.id);\n\t\t}\n\t\tif (this.autoRefreshNodeValueTimers.has(node.id)) {\n\t\t\tthis.autoRefreshNodeValueTimers.get(node.id)?.clear();\n\t\t\tthis.autoRefreshNodeValueTimers.delete(node.id);\n\t\t}\n\n\t\t// purge node values from the DB\n\t\tnode.valueDB.clear();\n\t\tthis.cachePurge(cacheKeys.node(node.id)._baseKey);\n\n\t\t// Remove the node from all security manager instances\n\t\tthis.securityManager?.deleteAllNoncesForReceiver(node.id);\n\t\tthis.securityManager2?.deleteNonce(node.id);\n\t\tthis.securityManagerLR?.deleteNonce(node.id);\n\n\t\tvoid this.rejectAllTransactionsForNode(\n\t\t\tnode.id,\n\t\t\t\"The node was removed from the network\",\n\t\t\tZWaveErrorCodes.Controller_NodeRemoved,\n\t\t);\n\n\t\tconst replaced = reason === RemoveNodeReason.Replaced\n\t\t\t|| reason === RemoveNodeReason.ProxyReplaced;\n\t\tif (!replaced) {\n\t\t\t// Asynchronously remove the node from all possible associations, ignore potential errors\n\t\t\t// but only if the node is not getting replaced, because the removal will interfere with\n\t\t\t// bootstrapping the new node\n\t\t\tthis.controller\n\t\t\t\t.removeNodeFromAllAssociations(node.id)\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Failed to remove node ${node.id} from all associations: ${err.message}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t}\n\n\t\t// And clean up all remaining resources used by the node\n\t\tnode.destroy();\n\n\t\t// If this was a failed node it could mean that all nodes are now ready\n\t\tthis.checkAllNodesReady();\n\t}\n\n\tprivate onControllerStatusChanged(_status: ControllerStatus): void {\n\t\tthis.triggerQueues();\n\t}\n\n\tprivate async onNetworkFound(\n\t\thomeId: number,\n\t\t_ownNodeId: number,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Joined network with home ID ${\n\t\t\t\t\tnum2hex(homeId)\n\t\t\t\t}, switching to new network cache...`,\n\t\t\t);\n\t\t\tawait this.recreateNetworkCacheAndValueDBs();\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Recreating the network cache and value DBs failed: ${\n\t\t\t\t\tgetErrorMessage(e)\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate onNetworkJoined(): void {\n\t\tthis.driverLog.print(`Finished joining network`);\n\t}\n\n\tprivate async onNetworkLeft(): Promise<void> {\n\t\ttry {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Left the previous network, switching network cache to new home ID ${\n\t\t\t\t\tnum2hex(this.controller.homeId)\n\t\t\t\t}...`,\n\t\t\t);\n\t\t\tawait this.recreateNetworkCacheAndValueDBs();\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Recreating the network cache and value DBs failed: ${\n\t\t\t\t\tgetErrorMessage(e)\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async recreateNetworkCacheAndValueDBs(): Promise<void> {\n\t\tawait this._networkCache?.close();\n\t\tawait this._valueDB?.close();\n\t\tawait this._metadataDB?.close();\n\n\t\t// Reopen with the new home ID\n\t\tawait this.initNetworkCache(this.controller.homeId!);\n\t\tawait this.initValueDBs(this.controller.homeId!);\n\t\tawait this.performCacheMigration();\n\t}\n\n\t/**\n\t * Returns the time in seconds to actually wait after a firmware upgrade, depending on what the device said.\n\t * This number will always be a bit greater than the advertised duration, because devices have been found to take longer to actually reboot.\n\t */\n\tpublic getConservativeWaitTimeAfterFirmwareUpdate(\n\t\tadvertisedWaitTime: number | undefined,\n\t): number {\n\t\t// Wait the specified time plus a bit, so the device is actually ready to use\n\t\tif (!advertisedWaitTime) {\n\t\t\t// Wait at least 5 seconds\n\t\t\treturn 5;\n\t\t} else if (advertisedWaitTime < 20) {\n\t\t\treturn advertisedWaitTime + 5;\n\t\t} else if (advertisedWaitTime < 60) {\n\t\t\treturn advertisedWaitTime + 10;\n\t\t} else {\n\t\t\treturn advertisedWaitTime + 30;\n\t\t}\n\t}\n\n\t/** This is called when the firmware on one of a node's firmware targets was updated */\n\tprivate async onNodeFirmwareUpdated(\n\t\tnode: ZWaveNode,\n\t\tresult: FirmwareUpdateResult,\n\t): Promise<void> {\n\t\tconst { success, reInterview } = result;\n\n\t\t// Nothing to do for non-successful updates\n\t\tif (!success) return;\n\n\t\t// TODO: Add support for delayed activation\n\n\t\t// Reset nonces etc. to prevent false-positive duplicates after the update\n\t\tthis.securityManager?.deleteAllNoncesForReceiver(node.id);\n\t\tthis.securityManager2?.deleteNonce(node.id);\n\t\tthis.securityManagerLR?.deleteNonce(node.id);\n\n\t\t// waitTime should always be defined, but just to be sure\n\t\tconst waitTime = result.waitTime ?? 5;\n\n\t\tif (reInterview) {\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tnode.id,\n\t\t\t\t`Firmware updated, scheduling interview in ${waitTime} seconds...`,\n\t\t\t);\n\t\t\t// We reuse the retryNodeInterviewTimeouts here because they serve a similar purpose\n\t\t\tthis.retryNodeInterviewTimeouts.set(\n\t\t\t\tnode.id,\n\t\t\t\tsetTimer(() => {\n\t\t\t\t\tthis.retryNodeInterviewTimeouts.delete(node.id);\n\t\t\t\t\tvoid node.refreshInfo({\n\t\t\t\t\t\t// After a firmware update, we need to refresh the node info\n\t\t\t\t\t\twaitForWakeup: false,\n\t\t\t\t\t});\n\t\t\t\t}, waitTime * 1000).unref(),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tnode.id,\n\t\t\t\t`Firmware updated. No restart or re-interview required. Refreshing version information in ${waitTime} seconds...`,\n\t\t\t);\n\n\t\t\tawait wait(waitTime * 1000, true);\n\n\t\t\ttry {\n\t\t\t\tconst versionAPI = node.commandClasses.Version;\n\t\t\t\tawait versionAPI.get();\n\t\t\t\tif (\n\t\t\t\t\tversionAPI.supportsCommand(VersionCommand.CapabilitiesGet)\n\t\t\t\t) {\n\t\t\t\t\tawait versionAPI.getCapabilities();\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\tversionAPI.supportsCommand(VersionCommand.ZWaveSoftwareGet)\n\t\t\t\t) {\n\t\t\t\t\tawait versionAPI.getZWaveSoftware();\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// ignore\n\t\t\t}\n\n\t\t\t// No need to keep the node awake longer than necessary\n\t\t\tnode.keepAwake = false;\n\t\t\tthis.debounceSendNodeToSleep(node);\n\t\t}\n\t}\n\n\t/** This is called when a node emits a `\"notification\"` event */\n\tprivate onNodeNotification: ZWaveNotificationCallback = (\n\t\tendpoint,\n\t\tccId,\n\t\tccArgs,\n\t) => {\n\t\tlet prefix: string;\n\t\tlet details: string[];\n\t\tif (ccId === CommandClasses.Notification) {\n\t\t\tconst msg: MessageRecord = {\n\t\t\t\ttype: ccArgs.label,\n\t\t\t\tevent: ccArgs.eventLabel,\n\t\t\t};\n\t\t\tif (ccArgs.parameters) {\n\t\t\t\tif (isUint8Array(ccArgs.parameters)) {\n\t\t\t\t\tmsg.parameters = buffer2hex(ccArgs.parameters);\n\t\t\t\t} else if (Duration.isDuration(ccArgs.parameters)) {\n\t\t\t\t\tmsg.duration = ccArgs.parameters.toString();\n\t\t\t\t} else if (isObject(ccArgs.parameters)) {\n\t\t\t\t\tObject.assign(msg, ccArgs.parameters);\n\t\t\t\t}\n\t\t\t}\n\t\t\tprefix = \"[Notification]\";\n\t\t\tdetails = messageRecordToLines(msg);\n\t\t} else if (ccId === CommandClasses[\"Entry Control\"]) {\n\t\t\tprefix = \"[Notification] Entry Control\";\n\t\t\tdetails = messageRecordToLines({\n\t\t\t\t\"event type\": ccArgs.eventTypeLabel,\n\t\t\t\t\"data type\": ccArgs.dataTypeLabel,\n\t\t\t});\n\t\t} else if (ccId === CommandClasses[\"Multilevel Switch\"]) {\n\t\t\tprefix = \"[Notification] Multilevel Switch\";\n\t\t\tdetails = messageRecordToLines(\n\t\t\t\tstripUndefined({\n\t\t\t\t\t\"event type\": ccArgs.eventTypeLabel,\n\t\t\t\t\tdirection: ccArgs.direction,\n\t\t\t\t}),\n\t\t\t);\n\t\t} /*if (ccId === CommandClasses.Powerlevel)*/ else {\n\t\t\t// Don't bother logging this\n\t\t\treturn;\n\t\t}\n\n\t\tthis.controllerLog.logNode(endpoint.nodeId, {\n\t\t\tendpoint: endpoint.index,\n\t\t\tmessage: [prefix, ...details.map((d) => ` ${d}`)].join(\"\\n\"),\n\t\t});\n\t};\n\n\t/** Checks if there are any pending messages for the given node */\n\tprivate hasPendingMessages(\n\t\tnode: ZWaveNodeBase & NodeSchedulePoll,\n\t): boolean {\n\t\t// First check if there are messages in the queue\n\t\tif (\n\t\t\tthis.hasPendingTransactions(\n\t\t\t\t(t) => t.message.getNodeId() === node.id,\n\t\t\t)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Then check if there are scheduled polls\n\t\treturn node.hasScheduledPolls();\n\t}\n\n\t/** Checks if there are any pending transactions that match the given predicate */\n\tpublic hasPendingTransactions(\n\t\tpredicate: (t: Transaction) => boolean,\n\t): boolean {\n\t\t// Queue is not an array\n\t\t// eslint-disable-next-line unicorn/prefer-array-some\n\t\tif (!!this.queue.find((t) => predicate(t))) return true;\n\t\treturn this.queues.some(\n\t\t\t(q) => q.currentTransaction && predicate(q.currentTransaction),\n\t\t);\n\t}\n\n\t/**\n\t * Retrieves the maximum version of a command class the given endpoint supports.\n\t * Returns 0 when the CC is not supported. Also returns 0 when the node was not found.\n\t * Falls back to querying the root endpoint if an endpoint was not found on the node\n\t *\n\t * @param cc The command class whose version should be retrieved\n\t * @param nodeId The node for which the CC version should be retrieved\n\t * @param endpointIndex The endpoint in question\n\t */\n\tpublic getSupportedCCVersion(\n\t\tcc: CommandClasses,\n\t\tnodeId: number,\n\t\tendpointIndex: number = 0,\n\t): number {\n\t\tif (!this._controller?.nodes.has(nodeId)) {\n\t\t\treturn 0;\n\t\t}\n\t\tconst node = this.controller.nodes.get(nodeId)!;\n\t\tconst endpoint = node.getEndpoint(endpointIndex);\n\t\tif (endpoint) return endpoint.getCCVersion(cc);\n\t\t// We sometimes receive messages from an endpoint, but can't find that endpoint.\n\t\t// In that case fall back to the root endpoint to determine the supported version.\n\t\treturn node.getCCVersion(cc);\n\t}\n\n\t/**\n\t * Retrieves the maximum version of a command class that can be used to communicate with a node.\n\t * Returns the highest implemented version if the node's CC version is unknown.\n\t * Returns `undefined` for CCs that are not implemented in this library yet.\n\t *\n\t * @param cc The command class whose version should be retrieved\n\t * @param nodeId The node for which the CC version should be retrieved\n\t * @param endpointIndex The endpoint for which the CC version should be retrieved\n\t */\n\tpublic getSafeCCVersion(\n\t\tcc: CommandClasses,\n\t\tnodeId: number,\n\t\tendpointIndex: number = 0,\n\t): number | undefined {\n\t\tconst implementedVersion = getImplementedVersion(cc);\n\t\tif (\n\t\t\timplementedVersion === 0\n\t\t\t|| implementedVersion === Number.POSITIVE_INFINITY\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst supportedVersion = this.getSupportedCCVersion(\n\t\t\tcc,\n\t\t\tnodeId,\n\t\t\tendpointIndex,\n\t\t);\n\t\tif (supportedVersion === 0) {\n\t\t\t// Unknown, use the highest implemented version\n\t\t\treturn implementedVersion;\n\t\t}\n\n\t\treturn Math.min(supportedVersion, implementedVersion);\n\t}\n\n\t/**\n\t * Determines whether a CC must be secure for a given node and endpoint.\n\t *\n\t * @param ccId The command class in question\n\t * @param nodeId The node for which the CC security should be determined\n\t * @param endpointIndex The endpoint for which the CC security should be determined\n\t */\n\tisCCSecure(\n\t\tccId: CommandClasses,\n\t\tnodeId: number,\n\t\tendpointIndex: number = 0,\n\t): boolean {\n\t\t// This is obvious\n\t\tif (\n\t\t\tccId === CommandClasses.Security\n\t\t\t|| ccId === CommandClasses[\"Security 2\"]\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst node = this.controller.nodes.get(nodeId);\n\t\t// Node is unknown, don't use secure communication\n\t\tif (!node) return false;\n\n\t\tconst endpoint = node.getEndpoint(endpointIndex);\n\n\t\tconst securityClass = node.getHighestSecurityClass();\n\t\t// Node is not secure, don't use secure communication\n\t\tif (\n\t\t\tsecurityClass === undefined || securityClass === SecurityClass.None\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Special case for Basic CC, which we sometimes hide:\n\t\t// A securely included node MAY support the Basic Command Class at the highest security level but it\n\t\t// MUST NOT support the Basic Command Class at any lower security level or non-securely.\n\t\tconst isBasicCC = ccId === CommandClasses.Basic;\n\n\t\t// Security S2 specs also mandate that all non-securely supported CCs MUST also be supported securely\n\t\t// so we can just shortcut if the node is using S2\n\t\tif (securityClassIsS2(securityClass)) {\n\t\t\t// Use secure communication if the CC is supported. This avoids silly things like S2-encapsulated pings\n\t\t\treturn (\n\t\t\t\t!!this.getSecurityManager2(nodeId)\n\t\t\t\t&& (isBasicCC || (endpoint ?? node).supportsCC(ccId))\n\t\t\t);\n\t\t}\n\n\t\t// Security S0 can be a little more complicated, with secure and non-secure endpoints\n\t\tif (securityClass === SecurityClass.S0_Legacy) {\n\t\t\t// Therefore actually check if the CC is marked as secure\n\t\t\treturn (\n\t\t\t\t!!this.securityManager\n\t\t\t\t&& (isBasicCC || (endpoint ?? node).isCCSecure(ccId))\n\t\t\t);\n\t\t}\n\n\t\t// We shouldn't be here\n\t\treturn false;\n\t}\n\n\t/**\n\t * **!!! INTERNAL !!!**\n\t *\n\t * Not intended to be used by applications.\n\t * Needed for compatibility with CCAPIHost\n\t */\n\tpublic schedulePoll(\n\t\tnodeId: number,\n\t\tvalueId: ValueID,\n\t\toptions: SchedulePollOptions,\n\t): boolean {\n\t\tconst node = this.controller.nodes.getOrThrow(nodeId);\n\t\treturn node.schedulePoll(valueId, options);\n\t}\n\n\tprivate isSoftResetting: boolean = false;\n\n\tprivate maySoftReset(): boolean {\n\t\t// 700+ series controllers have no problems with soft reset and MUST even be soft reset in some cases\n\t\tif (this._controller?.sdkVersionGt(\"7.0\")) return true;\n\n\t\t// Blacklist some sticks that are known to not support soft reset\n\t\tconst { manufacturerId, productType, productId } = this.controller;\n\n\t\t// Z-Wave.me UZB1\n\t\tif (\n\t\t\tmanufacturerId === 0x0115\n\t\t\t&& productType === 0x0000\n\t\t\t&& productId === 0x0000\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Z-Wave.me UZB\n\t\tif (\n\t\t\tmanufacturerId === 0x0115\n\t\t\t&& productType === 0x0400\n\t\t\t&& productId === 0x0001\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Vision Gen5 USB Stick\n\t\tif (\n\t\t\tmanufacturerId === 0x0109\n\t\t\t&& productType === 0x1001\n\t\t\t&& productId === 0x0201\n\t\t\t// firmware version 15.1 (GH#3730)\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// No clear indication, make the result depend on the config option\n\t\treturn !!this._options.features.softReset;\n\t}\n\n\t/**\n\t * Soft-resets the controller if the feature is enabled\n\t */\n\tpublic async trySoftReset(): Promise<void> {\n\t\tif (this.maySoftReset()) {\n\t\t\tawait this.softReset();\n\t\t} else {\n\t\t\tconst message =\n\t\t\t\t`The controller should not or cannot be soft reset, skipping API call.`;\n\t\t\tthis.controllerLog.print(message, \"warn\");\n\t\t}\n\t}\n\n\t/**\n\t * Instruct the controller to soft-reset.\n\t *\n\t * **Warning:** USB modules will reconnect, meaning that they might get a new address.\n\t *\n\t * **Warning:** This call will throw if soft-reset is not enabled.\n\t */\n\tpublic async softReset(): Promise<void> {\n\t\tif (!this.maySoftReset()) {\n\t\t\tconst message =\n\t\t\t\t`The controller does not support soft reset or the soft reset feature has been disabled with a config option or the ZWAVEJS_DISABLE_SOFT_RESET environment variable.`;\n\t\t\tthis.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(\n\t\t\t\tmessage,\n\t\t\t\tZWaveErrorCodes.Driver_FeatureDisabled,\n\t\t\t);\n\t\t}\n\n\t\tif (this._controller?.isAnyOTAFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to soft reset controller: A firmware update is in progress on this network.`;\n\t\t\tthis.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\treturn this.softResetInternal(true);\n\t}\n\n\tprivate async softResetInternal(destroyOnError: boolean): Promise<void> {\n\t\tthis.controllerLog.print(\"Performing soft reset...\");\n\n\t\ttry {\n\t\t\tthis.isSoftResetting = true;\n\t\t\tawait this.sendMessage(new SoftResetRequest(), {\n\t\t\t\tsupportCheck: false,\n\t\t\t\tpauseSendThread: true,\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tthis.controllerLog.print(\n\t\t\t\t`Soft reset failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\t// Don't continue if the controller is unresponsive\n\t\t\tif (isMissingControllerACK(e)) {\n\t\t\t\tthis.isSoftResetting = false;\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\n\t\tif (this._controller) {\n\t\t\t// Soft-reset resets the node ID type back to 8 bit\n\t\t\tthis._controller[\"_nodeIdType\"] = NodeIDType.Short;\n\n\t\t\t// Soft-resetting disables any ongoing inclusion, so we need to reset\n\t\t\t// the state that is tracked in the controller\n\t\t\tthis._controller.setInclusionState(InclusionState.Idle);\n\t\t}\n\n\t\t// Make sure we're able to communicate with the controller again\n\t\tif (!(await this.ensureSerialAPI())) {\n\t\t\tif (destroyOnError) {\n\t\t\t\tawait this.destroy();\n\t\t\t} else {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The Serial API did not respond after soft-reset\",\n\t\t\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthis.isSoftResetting = false;\n\n\t\t// This is a bit hacky, but what the heck...\n\t\tif (!this._enteringBootloader) {\n\t\t\t// Start the watchdog again, unless disabled\n\t\t\tif (this.options.features.watchdog) {\n\t\t\t\tvoid this._controller?.startWatchdog();\n\t\t\t}\n\n\t\t\t// If desired, re-configure the controller to use 16 bit node IDs\n\t\t\tvoid this._controller?.trySetNodeIDType(NodeIDType.Long);\n\n\t\t\t// Resume sending\n\t\t\tthis.unpauseSendQueue();\n\t\t}\n\t}\n\n\t/** Soft-reset the Z-Wave module and restart the driver instance */\n\tpublic async softResetAndRestart(): Promise<void> {\n\t\tthis.controllerLog.print(\"Performing soft reset...\");\n\n\t\ttry {\n\t\t\tthis.isSoftResetting = true;\n\t\t\tawait this.sendMessage(new SoftResetRequest(), {\n\t\t\t\tsupportCheck: false,\n\t\t\t\tpauseSendThread: true,\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tthis.controllerLog.print(\n\t\t\t\t`Soft reset failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\n\t\t// Make sure we're able to communicate with the controller again\n\t\tif (!(await this.ensureSerialAPI())) {\n\t\t\tawait this.destroyWithMessage(\n\t\t\t\t\"The Serial API did not respond after soft-reset\",\n\t\t\t);\n\t\t}\n\n\t\tthis.isSoftResetting = false;\n\n\t\t// Clean up and interview the controller again\n\t\tawait this.destroyController();\n\t\tvoid this.initializeControllerAndNodes();\n\t}\n\n\t/**\n\t * Checks whether recovering an unresponsive controller is enabled\n\t * and whether the driver is in a state where it makes sense.\n\t */\n\tprivate mayRecoverUnresponsiveController(): boolean {\n\t\tif (!this._options.features.unresponsiveControllerRecovery) {\n\t\t\treturn false;\n\t\t}\n\t\t// Only recover after we know the controller has been responsive\n\t\treturn this._controllerInterviewed;\n\t}\n\n\tprivate async ensureSerialAPI(): Promise<boolean> {\n\t\t// Wait 1.5 seconds after reset to ensure that the module is ready for communication again\n\t\t// Z-Wave 700 sticks are relatively fast, so we also wait for the Serial API started command\n\t\t// to bail early\n\t\tthis.controllerLog.print(\"Waiting for the controller to reconnect...\");\n\t\tlet waitResult = await this.waitForMessage<SerialAPIStartedRequest>(\n\t\t\t(msg) => msg.functionType === FunctionType.SerialAPIStarted,\n\t\t\t1500,\n\t\t).catch(() => false as const);\n\n\t\tif (waitResult) {\n\t\t\t// Serial API did start\n\t\t\tthis.controllerLog.print(\"reconnected and restarted\");\n\t\t\tif (this._controller) {\n\t\t\t\tthis._controller[\"_supportsLongRange\"] =\n\t\t\t\t\twaitResult.supportsLongRange;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t// If the controller disconnected the serial port during the soft reset, we need to re-open it\n\t\tif (!this.serial!.isOpen) {\n\t\t\tthis.controllerLog.print(\"Re-opening serial port...\");\n\t\t\ttry {\n\t\t\t\tawait this.openSerialport();\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// Wait the configured amount of time for the Serial API started command to be received\n\t\tthis.controllerLog.print(\"Waiting for the Serial API to start...\");\n\t\twaitResult = await this.waitForMessage<SerialAPIStartedRequest>(\n\t\t\t(msg) => {\n\t\t\t\treturn msg.functionType === FunctionType.SerialAPIStarted;\n\t\t\t},\n\t\t\tthis._options.timeouts.serialAPIStarted,\n\t\t).catch(() => false as const);\n\n\t\tif (waitResult) {\n\t\t\t// Serial API did start, maybe do something with the information?\n\t\t\tthis.controllerLog.print(\"Serial API started\");\n\t\t\tif (this._controller) {\n\t\t\t\tthis._controller[\"_supportsLongRange\"] =\n\t\t\t\t\twaitResult.supportsLongRange;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tthis.controllerLog.print(\n\t\t\t\"Did not receive notification that Serial API has started, checking if it responds...\",\n\t\t);\n\n\t\t// We don't need to use any specific command here. However we're going to use this one in the interview\n\t\t// anyways, so we might aswell use it here too\n\t\tconst pollController = async () => {\n\t\t\ttry {\n\t\t\t\t// And resume sending - this requires us to unpause the send thread\n\t\t\t\tthis.unpauseSendQueue();\n\t\t\t\tawait this.sendMessage(new GetControllerVersionRequest(), {\n\t\t\t\t\tsupportCheck: false,\n\t\t\t\t\tpriority: MessagePriority.ControllerImmediate,\n\t\t\t\t});\n\t\t\t\tthis.pauseSendQueue();\n\t\t\t\tthis.controllerLog.print(\"Serial API responded\");\n\t\t\t\treturn true;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\t\t// Poll the controller with increasing backoff delay\n\t\tif (await pollController()) return true;\n\t\tfor (const backoff of [2, 5, 10, 15]) {\n\t\t\tthis.controllerLog.print(\n\t\t\t\t`Serial API did not respond, trying again in ${backoff} seconds...`,\n\t\t\t);\n\t\t\tawait wait(backoff * 1000);\n\t\t\tif (await pollController()) return true;\n\t\t}\n\n\t\tthis.controllerLog.print(\n\t\t\t\"Serial API did not respond, giving up\",\n\t\t\t\"error\",\n\t\t);\n\t\treturn false;\n\t}\n\n\tprivate _ensureCLIReadyPromise: DeferredPromise<boolean> | undefined;\n\tprivate async ensureCLIReady(): Promise<boolean> {\n\t\t// Ensure this is only called once and all subsequent calls block\n\t\tif (this._ensureCLIReadyPromise) return this._ensureCLIReadyPromise;\n\t\tthis._ensureCLIReadyPromise = createDeferredPromise();\n\n\t\t// Try to detect the available CLI commands and wait long enough for the communication to succeed\n\t\t// Wait 1.5 seconds after reset to ensure that the module is ready for communication again\n\t\t// Z-Wave 700 sticks are relatively fast, so we also wait for the Serial API started command\n\t\t// to bail early\n\t\tthis.controllerLog.print(\"Waiting for the CLI to be ready...\");\n\n\t\t// After booting, the CLI can take a while to respond to commands\n\t\t// Try up to 3 times to detect the available commands\n\t\tawait wait(250);\n\t\tfor (let i = 0;; i++) {\n\t\t\ttry {\n\t\t\t\tawait this.cli.detectCommands();\n\t\t\t\tthis.controllerLog.print(\"CLI started\");\n\t\t\t\tthis._ensureCLIReadyPromise?.resolve(true);\n\t\t\t\tthis._ensureCLIReadyPromise = undefined;\n\t\t\t\treturn true;\n\t\t\t} catch {\n\t\t\t\tif (i === 2) {\n\t\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t\t\"CLI did not respond, giving up\",\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._ensureCLIReadyPromise?.resolve(false);\n\t\t\t\t\tthis._ensureCLIReadyPromise = undefined;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tawait wait(1000);\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 *\n\t * The returned Promise resolves when the hard reset has been performed.\n\t * It does not wait for the initialization process which is started afterwards.\n\t */\n\tpublic async hardReset(): Promise<void> {\n\t\tthis.ensureReady(true);\n\n\t\tif (this.controller.isAnyOTAFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to hard reset controller: A firmware update is in progress on this network.`;\n\t\t\tthis.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\t// Preserve the private key for the authenticated learn mode ECDH key pair\n\t\tconst oldPrivateKey = this.cacheGet<Uint8Array>(\n\t\t\tcacheKeys.controller.privateKey,\n\t\t);\n\n\t\t// Drop all scheduled tasks - they don't make sense after a hard reset\n\t\tawait this.scheduler.removeTasks(\n\t\t\t() => true,\n\t\t\tnew ZWaveError(\n\t\t\t\t\"The controller is being hard-reset\",\n\t\t\t\tZWaveErrorCodes.Driver_TaskRemoved,\n\t\t\t),\n\t\t);\n\n\t\t// Update the controller NIF prior to hard resetting\n\t\tawait this.controller.setControllerNIF();\n\t\tawait this.controller.hardReset();\n\n\t\t// Clean up\n\t\tawait this.destroyController();\n\t\tvoid this.initializeControllerAndNodes();\n\n\t\t// Save the key pair in the new cache again\n\t\tif (oldPrivateKey) {\n\t\t\tthis.once(\"driver ready\", () => {\n\t\t\t\tthis.cacheSet(cacheKeys.controller.privateKey, oldPrivateKey);\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Instructs the Z-Wave API to shut down in order to safely remove the power.\n\t * This will destroy the driver instance if it succeeds.\n\t */\n\tpublic async shutdown(): Promise<boolean> {\n\t\tthis.ensureReady(true);\n\n\t\t// Not a good idea to abort firmware updates this way\n\t\tif (this.controller.isAnyOTAFirmwareUpdateInProgress()) {\n\t\t\tconst message =\n\t\t\t\t`Failed to shut down controller: A firmware update is in progress on this network.`;\n\t\t\tthis.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 result = await this.controller.shutdown();\n\t\ttry {\n\t\t\tif (result) await this.destroy();\n\t\t} finally {\n\t\t\treturn result;\n\t\t}\n\t}\n\n\tprivate _destroyPromise: DeferredPromise<void> | undefined;\n\tprivate get wasDestroyed(): boolean {\n\t\treturn !!this._destroyPromise;\n\t}\n\n\t/**\n\t * Ensures that the driver is ready to communicate (serial port open and not destroyed).\n\t * If desired, also checks that the controller interview has been completed.\n\t */\n\tprivate ensureReady(includingController: boolean = false): void {\n\t\tif (!this._wasStarted || !this._isOpen || this.wasDestroyed) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The driver is not ready or has been destroyed\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\tif (includingController && !this._controllerInterviewed) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The controller is not ready yet\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\tif (this._bootloader) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Cannot do this while in bootloader mode\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Indicates whether the driver is ready, i.e. the \"driver ready\" event was emitted */\n\tpublic get ready(): boolean {\n\t\treturn (\n\t\t\tthis._wasStarted\n\t\t\t&& this._isOpen\n\t\t\t&& !this.wasDestroyed\n\t\t\t&& this._controllerInterviewed\n\t\t);\n\t}\n\n\tprivate async destroyWithMessage(message: string): Promise<void> {\n\t\tthis.driverLog.print(message, \"error\");\n\n\t\tconst error = new ZWaveError(\n\t\t\tmessage,\n\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t);\n\t\tthis.emit(\"error\", error);\n\n\t\tawait this.destroy();\n\t}\n\n\t/**\n\t * Terminates the driver instance and closes the underlying serial connection.\n\t * Must be called under any circumstances.\n\t */\n\tpublic async destroy(): Promise<void> {\n\t\t// Ensure this is only called once and all subsequent calls block\n\t\tif (this._destroyPromise) return this._destroyPromise;\n\t\tthis._destroyPromise = createDeferredPromise();\n\n\t\tthis.driverLog.print(\"destroying driver instance...\");\n\n\t\t// First stop the scheduler, all queues and close the serial port, so nothing happens anymore\n\t\tawait this._scheduler.stop();\n\n\t\tawait this.destroyTransactionQueues(\n\t\t\t\"driver instance destroyed\",\n\t\t\tZWaveErrorCodes.Driver_Destroyed,\n\t\t);\n\n\t\tthis.destroySerialAPIQueue(\n\t\t\t\"driver instance destroyed\",\n\t\t\tZWaveErrorCodes.Driver_Destroyed,\n\t\t);\n\n\t\tif (this.serial != undefined) {\n\t\t\t// Avoid spewing errors if the port was in the middle of receiving something\n\t\t\tif (this.serial.isOpen) await this.serial.close();\n\t\t\tthis.serial = undefined;\n\t\t}\n\n\t\tawait this.destroyController();\n\n\t\tthis.driverLog.print(`driver instance destroyed`);\n\n\t\t// destroy loggers as the very last thing\n\t\tthis._logContainer.destroy();\n\n\t\tthis._destroyPromise.resolve();\n\t}\n\n\t/** Cleanly destroy the controller instance, but not the entire driver */\n\t// FIXME: Too much overlap with destroy()\n\tprivate async destroyController(): Promise<void> {\n\t\t// Avoid re-transmissions etc. communicating with other applications\n\t\t// or the bootloader\n\t\tawait this.scheduler.removeTasks(\n\t\t\t() => true,\n\t\t\tnew ZWaveError(\n\t\t\t\t\"The controller instance is being destroyed\",\n\t\t\t\tZWaveErrorCodes.Driver_TaskRemoved,\n\t\t\t),\n\t\t);\n\n\t\tawait this.destroyTransactionQueues(\n\t\t\t\"The controller instance is being destroyed\",\n\t\t\tZWaveErrorCodes.Driver_TaskRemoved,\n\t\t);\n\n\t\tthis.destroySerialAPIQueue(\n\t\t\t\"The controller instance is being destroyed\",\n\t\t\tZWaveErrorCodes.Driver_TaskRemoved,\n\t\t);\n\n\t\tthis.requestHandlers.clear();\n\n\t\t// Attempt to close the value DBs and network cache\n\t\tawait this.closeDatabases();\n\n\t\t// Remove all timeouts\n\t\tthis.clearAllTimeouts();\n\n\t\t// Destroy all nodes and the controller\n\t\tif (this._controller) {\n\t\t\tthis._controller.destroy();\n\t\t\tthis._controller = undefined;\n\t\t}\n\n\t\tthis._controllerInterviewed = false;\n\t\tthis._nodesReady.clear();\n\t\tthis._nodesReadyEventEmitted = false;\n\t}\n\n\tprivate async closeDatabases(): Promise<void> {\n\t\ttry {\n\t\t\tawait this._valueDB?.close();\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Closing the value DB failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\tawait this._metadataDB?.close();\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Closing the metadata DB failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\tawait this._networkCache?.close();\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Closing the network cache failed: ${getErrorMessage(e)}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate clearAllTimeouts() {\n\t\tfor (\n\t\t\tconst timeout of [\n\t\t\t\tthis._powerlevelTestNodeContext?.timeout,\n\t\t\t]\n\t\t) {\n\t\t\tif (timeout) clearTimeout(timeout);\n\t\t}\n\t\tfor (\n\t\t\tconst timeout of [\n\t\t\t\t...this.retryNodeInterviewTimeouts.values(),\n\t\t\t\t...this.autoRefreshNodeValueTimers.values(),\n\t\t\t\tthis.statisticsTimeout,\n\t\t\t\tthis.pollBackgroundRSSITimer,\n\t\t\t\t...this.sendNodeToSleepTimers.values(),\n\t\t\t\t...this.awaitedCommands.map((c) => c.timeout),\n\t\t\t\t...this.awaitedMessages.map((m) => m.timeout),\n\t\t\t\t...this.awaitedMessageHeaders.map((h) => h.timeout),\n\t\t\t\t...this.awaitedBootloaderChunks.map((b) => b.timeout),\n\t\t\t\t...this.awaitedCLIChunks.map((c) => c.timeout),\n\t\t\t]\n\t\t) {\n\t\t\ttimeout?.clear();\n\t\t}\n\t}\n\n\tprivate async handleSerialData(serial: ZWaveSerialStream): Promise<void> {\n\t\ttry {\n\t\t\tfor await (const frame of serial.readable) {\n\t\t\t\tsetImmediate(() => {\n\t\t\t\t\tif (frame.type === ZWaveSerialFrameType.SerialAPI) {\n\t\t\t\t\t\tvoid this.serialport_onData(frame.data);\n\t\t\t\t\t} else if (frame.type === ZWaveSerialFrameType.Bootloader) {\n\t\t\t\t\t\tvoid this.serialport_onBootloaderData(frame.data);\n\t\t\t\t\t} else if (frame.type === ZWaveSerialFrameType.CLI) {\n\t\t\t\t\t\tvoid this.serialport_onCLIData(frame.data);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Handle discarded data?\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (isAbortError(e)) {\n\t\t\t\treturn;\n\t\t\t} else if (\n\t\t\t\tisZWaveError(e) && e.code === ZWaveErrorCodes.Driver_Failed\n\t\t\t) {\n\t\t\t\t// A disconnection while soft resetting is to be expected.\n\t\t\t\t// The soft reset method will handle reopening\n\t\t\t\tif (this.isSoftResetting || this._isOpeningSerialPort) return;\n\n\t\t\t\tvoid this.destroyWithMessage(e.message);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Is called when the serial port has received a single-byte message or a complete message buffer\n\t */\n\tprivate async serialport_onData(\n\t\tdata:\n\t\t\t| Uint8Array\n\t\t\t| MessageHeaders.ACK\n\t\t\t| MessageHeaders.CAN\n\t\t\t| MessageHeaders.NAK,\n\t): Promise<void> {\n\t\tif (typeof data === \"number\") {\n\t\t\tswitch (data) {\n\t\t\t\tcase MessageHeaders.ACK:\n\t\t\t\tcase MessageHeaders.NAK:\n\t\t\t\tcase MessageHeaders.CAN: {\n\t\t\t\t\t// check if someone is waiting for this\n\t\t\t\t\tfor (const entry of this.awaitedMessageHeaders) {\n\t\t\t\t\t\tif (entry.predicate(data)) {\n\t\t\t\t\t\t\tentry.handler(data);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._cli = undefined;\n\t\tthis._bootloader = undefined;\n\n\t\tlet msg: Message | undefined;\n\t\ttry {\n\t\t\t// Parse the message while remembering potential decoding errors in embedded CCs\n\t\t\t// This way we can log the invalid CC contents\n\t\t\tmsg = Message.parse(\n\t\t\t\tdata,\n\t\t\t\tthis.getMessageParsingContext(),\n\t\t\t);\n\n\t\t\t// Parse embedded CCs\n\t\t\tif (isCommandRequest(msg) && containsSerializedCC(msg)) {\n\t\t\t\tmsg.command = await CommandClass.parse(\n\t\t\t\t\tmsg.serializedCC,\n\t\t\t\t\t{\n\t\t\t\t\t\t...this.getCCParsingContext(),\n\t\t\t\t\t\tsourceNodeId: msg.getNodeId()!,\n\t\t\t\t\t\tframeType: msg.frameType,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\t// Whether successful or not, a message from a node should update last seen\n\t\t\t\tconst node = this.tryGetNode(msg);\n\t\t\t\tif (node) node.lastSeen = new Date();\n\n\t\t\t\t// Ensure there are no errors\n\t\t\t\tassertValidCCs(msg as ContainsCC);\n\t\t\t}\n\t\t\t// And update statistics\n\t\t\tif (!!this._controller) {\n\t\t\t\tif (containsCC(msg)) {\n\t\t\t\t\tthis.tryGetNode(msg)?.incrementStatistics(\"commandsRX\");\n\t\t\t\t} else {\n\t\t\t\t\tthis._controller.incrementStatistics(\"messagesRX\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t// all good, send ACK\n\t\t\tawait this.writeHeader(MessageHeaders.ACK);\n\t\t} catch (e: any) {\n\t\t\ttry {\n\t\t\t\tif (await this.handleSecurityS2DecodeError(e, msg)) {\n\t\t\t\t\t// TODO\n\t\t\t\t} else {\n\t\t\t\t\tconst response = this.handleDecodeError(e, data, msg);\n\t\t\t\t\tif (response) await this.writeHeader(response);\n\t\t\t\t\tif (!!this._controller) {\n\t\t\t\t\t\tif (containsCC(msg)) {\n\t\t\t\t\t\t\tthis.tryGetNode(msg)?.incrementStatistics(\n\t\t\t\t\t\t\t\t\"commandsDroppedRX\",\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t// Figure out if the command was received with supervision encapsulation\n\t\t\t\t\t\t\tconst supervisionSessionId = SupervisionCC\n\t\t\t\t\t\t\t\t.getSessionId(msg.command);\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tsupervisionSessionId !== undefined\n\t\t\t\t\t\t\t\t&& msg.command instanceof InvalidCC\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// If it was, we need to notify the sender that we couldn't decode the command\n\t\t\t\t\t\t\t\tconst node = this.tryGetNode(msg);\n\t\t\t\t\t\t\t\tif (node) {\n\t\t\t\t\t\t\t\t\tconst endpoint = node.getEndpoint(\n\t\t\t\t\t\t\t\t\t\tmsg.command.endpointIndex,\n\t\t\t\t\t\t\t\t\t) ?? node;\n\t\t\t\t\t\t\t\t\tconst encapsulationFlags =\n\t\t\t\t\t\t\t\t\t\tmsg.command.encapsulationFlags;\n\t\t\t\t\t\t\t\t\tawait endpoint\n\t\t\t\t\t\t\t\t\t\t.createAPI(\n\t\t\t\t\t\t\t\t\t\t\tCommandClasses.Supervision,\n\t\t\t\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t.sendReport({\n\t\t\t\t\t\t\t\t\t\t\tsessionId: supervisionSessionId,\n\t\t\t\t\t\t\t\t\t\t\tmoreUpdatesFollow: false,\n\t\t\t\t\t\t\t\t\t\t\tstatus: SupervisionStatus.NoSupport,\n\t\t\t\t\t\t\t\t\t\t\trequestWakeUpOnDemand: this\n\t\t\t\t\t\t\t\t\t\t\t\t.shouldRequestWakeupOnDemand(\n\t\t\t\t\t\t\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\tencapsulationFlags,\n\t\t\t\t\t\t\t\t\t\t\tlowPriority: this\n\t\t\t\t\t\t\t\t\t\t\t\t.shouldUseLowPriorityForSupervisionReport(\n\t\t\t\t\t\t\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\t\t\t\t\t\t\tencapsulationFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\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\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis._controller.incrementStatistics(\n\t\t\t\t\t\t\t\t\"messagesDroppedRX\",\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\t\t\t} catch (ee) {\n\t\t\t\tif (ee instanceof Error) {\n\t\t\t\t\tif (/serial port is not open/.test(ee.message)) {\n\t\t\t\t\t\tthis.emit(\"error\", ee);\n\t\t\t\t\t\tvoid this.destroy();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// Print something, so we know what is wrong\n\t\t\t\t\tthis._driverLog.print(ee.stack ?? ee.message, \"error\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Don't keep handling the message\n\t\t\tmsg = undefined;\n\t\t}\n\n\t\t// If we receive a CC from a node while the controller is not ready yet,\n\t\t// we can't do anything with it, but logging it may assume that it can access the controller.\n\t\t// To prevent this problem, we just ignore CCs until the controller is ready\n\t\tif (!this._controller && containsCC(msg)) return;\n\n\t\t// If the message could be decoded, forward it to the send thread\n\t\tif (msg) {\n\t\t\tlet wasMessageLogged = false;\n\t\t\tif (isCommandRequest(msg) && containsCC(msg)) {\n\t\t\t\t// SecurityCCCommandEncapsulationNonceGet is two commands in one, but\n\t\t\t\t// we're not set up to handle things like this. Reply to the nonce get\n\t\t\t\t// and handle the encapsulation part normally\n\t\t\t\tif (\n\t\t\t\t\tmsg.command\n\t\t\t\t\t\tinstanceof SecurityCCCommandEncapsulationNonceGet\n\t\t\t\t) {\n\t\t\t\t\tconst node = this.tryGetNode(msg);\n\t\t\t\t\tif (node) {\n\t\t\t\t\t\tvoid this.handleSecurityNonceGet(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Transport Service commands must be handled before assembling partial CCs\n\t\t\t\tif (isTransportServiceEncapsulation(msg.command)) {\n\t\t\t\t\t// Log Transport Service commands before doing anything else\n\t\t\t\t\tthis.driverLog.logMessage(msg, {\n\t\t\t\t\t\tsecondaryTags: [\"partial\"],\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t});\n\t\t\t\t\twasMessageLogged = true;\n\n\t\t\t\t\tvoid this.handleTransportServiceCommand(msg.command).catch(\n\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t// Don't care about errors in incoming transport service commands\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Assemble partial CCs on the driver level. Only forward complete messages to the send thread machine\n\t\t\t\tif (!(await this.assemblePartialCCs(msg))) {\n\t\t\t\t\t// Check if a message timer needs to be refreshed.\n\t\t\t\t\tfor (const entry of this.awaitedMessages) {\n\t\t\t\t\t\tif (entry.refreshPredicate?.(msg)) {\n\t\t\t\t\t\t\tentry.timeout?.refresh();\n\t\t\t\t\t\t\t// Since this is a partial message there may be no clear 1:1 match.\n\t\t\t\t\t\t\t// Therefore we loop through all awaited messages\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Make sure we are allowed to handle this command\n\t\t\t\tif (\n\t\t\t\t\tthis.isSecurityLevelTooLow(msg.command)\n\t\t\t\t\t|| this.shouldDiscardCC(msg.command)\n\t\t\t\t) {\n\t\t\t\t\tif (!wasMessageLogged) {\n\t\t\t\t\t\tthis.driverLog.logMessage(msg, {\n\t\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\t\tsecondaryTags: [\"discarded\"],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// When we have a complete CC, save its values\n\t\t\t\ttry {\n\t\t\t\t\tthis.persistCCValues(msg.command);\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// Indicate invalid payloads with a special CC type\n\t\t\t\t\tif (\n\t\t\t\t\t\tisZWaveError(e)\n\t\t\t\t\t\t&& e.code\n\t\t\t\t\t\t\t=== ZWaveErrorCodes.PacketFormat_InvalidPayload\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t`dropping CC with invalid values${\n\t\t\t\t\t\t\t\ttypeof e.context === \"string\"\n\t\t\t\t\t\t\t\t\t? ` (Reason: ${e.context})`\n\t\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\t// TODO: We may need to do the S2 MOS dance here - or we can deal with it when the next valid CC arrives\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Transport Service CC can be eliminated from the encapsulation stack, since it is always the outermost CC\n\t\t\t\tif (isTransportServiceEncapsulation(msg.command)) {\n\t\t\t\t\tmsg.command = msg.command.encapsulated;\n\t\t\t\t\t// Now we do want to log the command again, so we can see what was inside\n\t\t\t\t\twasMessageLogged = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!wasMessageLogged) {\n\t\t\t\ttry {\n\t\t\t\t\tthis.driverLog.logMessage(msg, {\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t});\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// We shouldn't throw just because logging a message fails\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Logging a message failed: ${getErrorMessage(e)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// // Check if this message is unsolicited by passing it to the Serial API command interpreter if possible\n\t\t\t// if (\n\t\t\t// \tthis.serialAPIInterpreter?.status === InterpreterStatus.Running\n\t\t\t// ) {\n\t\t\t// \tthis.serialAPIInterpreter.send({\n\t\t\t// \t\ttype: \"message\",\n\t\t\t// \t\tmessage: msg,\n\t\t\t// \t});\n\t\t\t// } else {\n\t\t\tvoid this.handleUnsolicitedMessage(msg);\n\t\t\t// }\n\t\t}\n\t}\n\n\t/** Handles a decoding error and returns the desired reply to the stick */\n\tprivate handleDecodeError(\n\t\te: Error,\n\t\tdata: Uint8Array,\n\t\tmsg: Message | undefined,\n\t): MessageHeaders | undefined {\n\t\tif (isZWaveError(e)) {\n\t\t\tswitch (e.code) {\n\t\t\t\tcase ZWaveErrorCodes.PacketFormat_Invalid:\n\t\t\t\tcase ZWaveErrorCodes.PacketFormat_Checksum:\n\t\t\t\tcase ZWaveErrorCodes.PacketFormat_Truncated:\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Dropping message because it contains invalid data`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\treturn MessageHeaders.NAK;\n\n\t\t\t\tcase ZWaveErrorCodes.Deserialization_NotImplemented:\n\t\t\t\tcase ZWaveErrorCodes.CC_NotImplemented:\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Dropping message because it could not be deserialized: ${e.message}`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\treturn MessageHeaders.ACK;\n\n\t\t\t\tcase ZWaveErrorCodes.Driver_NotReady:\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Dropping message because the driver is not ready to handle it yet.`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\treturn MessageHeaders.ACK;\n\n\t\t\t\tcase ZWaveErrorCodes.PacketFormat_InvalidPayload:\n\t\t\t\t\tif (msg) {\n\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t`Dropping message with invalid payload`,\n\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tthis.driverLog.logMessage(msg, {\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} catch (e) {\n\t\t\t\t\t\t\t// We shouldn't throw just because logging a message fails\n\t\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\t`Logging a message failed: ${\n\t\t\t\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\t\t\t\te,\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}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t`Dropping message with invalid payload${\n\t\t\t\t\t\t\t\ttypeof e.context === \"string\"\n\t\t\t\t\t\t\t\t\t? ` (Reason: ${e.context})`\n\t\t\t\t\t\t\t\t\t: \"\"\n\t\t\t\t\t\t\t}:\\n${buffer2hex(data)}`,\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\treturn MessageHeaders.ACK;\n\n\t\t\t\tcase ZWaveErrorCodes.Driver_NoSecurity:\n\t\t\t\tcase ZWaveErrorCodes.Security2CC_NotInitialized:\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Dropping message because network keys are not set or the driver is not yet ready to receive secure messages.`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\treturn MessageHeaders.ACK;\n\n\t\t\t\tcase ZWaveErrorCodes.Controller_NodeNotFound:\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Dropping message because ${\n\t\t\t\t\t\t\ttypeof e.context === \"number\"\n\t\t\t\t\t\t\t\t? `node ${e.context}`\n\t\t\t\t\t\t\t\t: \"the node\"\n\t\t\t\t\t\t} does not exist.`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\treturn MessageHeaders.ACK;\n\t\t\t}\n\t\t} else {\n\t\t\tif (/database is not open/.test(e.message)) {\n\t\t\t\t// The JSONL-DB is not open yet\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t`Dropping message because the driver is not ready to handle it yet.`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\treturn MessageHeaders.ACK;\n\t\t\t}\n\t\t}\n\t\t// Pass all other errors through\n\t\tthrow e;\n\t}\n\n\tprivate mustReplyWithSecurityS2MOS(\n\t\tmsg: ContainsCC & CommandRequest,\n\t): boolean {\n\t\t// We're looking for a singlecast S2-encapsulated request\n\t\tif (msg.frameType !== \"singlecast\") return false;\n\t\tconst encapS2 = msg.command.getEncapsulatingCC(\n\t\t\tCommandClasses[\"Security 2\"],\n\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t) as Security2CCMessageEncapsulation | undefined;\n\t\tif (!encapS2) return false;\n\n\t\t// With the MGRP extension present\n\t\tconst node = this.tryGetNode(msg);\n\t\tif (!node) return false;\n\t\tconst groupId = encapS2.getMulticastGroupId();\n\t\tif (groupId == undefined) return false;\n\t\tconst securityManager = this.getSecurityManager2(node.id);\n\t\tif (\n\t\t\t// but where we don't have an MPAN stored\n\t\t\tsecurityManager?.getPeerMPAN(\n\t\t\t\tmsg.command.nodeId as number,\n\t\t\t\tgroupId,\n\t\t\t).type !== MPANState.MPAN\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate async handleSecurityS2DecodeError(\n\t\te: Error,\n\t\tmsg: Message | undefined,\n\t): Promise<boolean> {\n\t\tif (!isZWaveError(e)) return false;\n\t\tif (\n\t\t\t(e.code === ZWaveErrorCodes.Security2CC_NoSPAN\n\t\t\t\t|| e.code === ZWaveErrorCodes.Security2CC_CannotDecode)\n\t\t\t&& containsCC(msg)\n\t\t) {\n\t\t\t// Decoding the command failed because no SPAN has been established with the other node\n\t\t\tconst nodeId = msg.getNodeId()!;\n\t\t\t// If the node isn't known, ignore this error\n\t\t\tconst node = this._controller?.nodes.get(nodeId);\n\t\t\tif (!node) return false;\n\n\t\t\t// Before we can send anything, ACK the command\n\t\t\tawait this.writeHeader(MessageHeaders.ACK);\n\n\t\t\tthis.driverLog.logMessage(msg, { direction: \"inbound\" });\n\t\t\tnode.incrementStatistics(\"commandsDroppedRX\");\n\n\t\t\t// We might receive this before the node has been interviewed. If that case, we need to mark Security S2 as\n\t\t\t// supported or we won't ever be able to communicate with the node\n\t\t\tif (node.interviewStage < InterviewStage.NodeInfo) {\n\t\t\t\tnode.addCC(CommandClasses[\"Security 2\"], {\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// Ensure that we're not flooding the queue with unnecessary NonceReports\n\t\t\tconst isS2NonceReport = (t: Transaction) =>\n\t\t\t\tt.message.getNodeId() === nodeId\n\t\t\t\t&& containsCC(t.message)\n\t\t\t\t&& t.message.command instanceof Security2CCNonceReport;\n\n\t\t\tconst message: string =\n\t\t\t\te.code === ZWaveErrorCodes.Security2CC_CannotDecode\n\t\t\t\t\t? \"Message authentication failed\"\n\t\t\t\t\t: \"No SPAN is established yet\";\n\n\t\t\tif (this.controller.bootstrappingS2NodeId === nodeId) {\n\t\t\t\t// The node is currently being bootstrapped.\n\t\t\t\tconst securityManager = this.getSecurityManager2(nodeId);\n\t\t\t\tif (securityManager?.tempKeys.has(nodeId)) {\n\t\t\t\t\t// The DSK has been verified, so we should be able to decode this command.\n\t\t\t\t\t// If this is the first attempt, we need to request a nonce first\n\t\t\t\t\tif (\n\t\t\t\t\t\tsecurityManager.getSPANState(nodeId).type\n\t\t\t\t\t\t\t=== SPANState.None\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`${message}, cannot decode command. Requesting a nonce...`,\n\t\t\t\t\t\t\tlevel: \"verbose\",\n\t\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Send the node our nonce\n\t\t\t\t\t\tnode.commandClasses[\"Security 2\"]\n\t\t\t\t\t\t\t.sendNonce()\n\t\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\t\t// Ignore errors\n\t\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Us repeatedly not being able to decode the command means we need to abort the bootstrapping process\n\t\t\t\t\t\t// because the PIN is wrong\n\t\t\t\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`${message}, cannot decode command. Aborting the S2 bootstrapping process...`,\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tthis.controller.cancelSecureBootstrapS2(\n\t\t\t\t\t\t\tKEXFailType.BootstrappingCanceled,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Ignoring KEXSet because the DSK has not been verified yet`,\n\t\t\t\t\t\tlevel: \"verbose\",\n\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (!this.hasPendingTransactions(isS2NonceReport)) {\n\t\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`${message}, cannot decode command. Requesting a nonce...`,\n\t\t\t\t\tlevel: \"verbose\",\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t});\n\t\t\t\t// Send the node our nonce, and use the chance to re-sync the MPAN if necessary\n\t\t\t\tconst s2MulticastOutOfSync = isCommandRequest(msg)\n\t\t\t\t\t&& this.mustReplyWithSecurityS2MOS(msg);\n\n\t\t\t\tnode.commandClasses[\"Security 2\"]\n\t\t\t\t\t.withOptions({ s2MulticastOutOfSync })\n\t\t\t\t\t.sendNonce()\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t// Ignore errors\n\t\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\t\tmessage: `${message}, cannot decode command.`,\n\t\t\t\t\tlevel: \"verbose\",\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} else if (\n\t\t\t(e.code === ZWaveErrorCodes.Security2CC_NoMPAN\n\t\t\t\t|| e.code === ZWaveErrorCodes.Security2CC_CannotDecodeMulticast)\n\t\t\t&& containsCC(msg)\n\t\t) {\n\t\t\t// Decoding the command failed because the MPAN used by the other node\n\t\t\t// is not known to us yet\n\t\t\tconst nodeId = msg.getNodeId()!;\n\t\t\t// If the node isn't known, ignore this error\n\t\t\tconst node = this._controller?.nodes.get(nodeId);\n\t\t\tif (!node) return false;\n\n\t\t\t// Before we can send anything, ACK the command\n\t\t\tawait this.writeHeader(MessageHeaders.ACK);\n\n\t\t\tthis.driverLog.logMessage(msg, { direction: \"inbound\" });\n\t\t\tnode.incrementStatistics(\"commandsDroppedRX\");\n\n\t\t\tthis.controllerLog.logNode(nodeId, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Cannot decode S2 multicast command, since MPAN is not known yet. Will attempt re-sync after the next singlecast.`,\n\t\t\t\tlevel: \"verbose\",\n\t\t\t});\n\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Checks if a transaction failed because a node didn't respond in time */\n\tprivate isMissingNodeACK(\n\t\ttransaction: Transaction,\n\t\te: ZWaveError,\n\t): transaction is Transaction & {\n\t\tmessage: SendDataRequest | SendDataBridgeRequest;\n\t} {\n\t\treturn (\n\t\t\t// If the node does not acknowledge our request, it is either asleep or dead\n\t\t\te.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t&& (transaction.message instanceof SendDataRequest\n\t\t\t\t|| transaction.message instanceof SendDataBridgeRequest)\n\t\t);\n\t}\n\n\t/**\n\t * @internal\n\t * Handles the case that a node failed to respond in time.\n\t * Returns `true` if the transaction failure was handled, `false` if it needs to be rejected.\n\t */\n\tpublic handleMissingNodeACK(\n\t\ttransaction: Transaction & {\n\t\t\tmessage: HasNodeId;\n\t\t},\n\t\terror: ZWaveError,\n\t): boolean {\n\t\tconst node = this.tryGetNode(transaction.message);\n\t\tif (!node) return false; // This should never happen, but whatever\n\n\t\tconst messagePart1 = isSendData(transaction.message)\n\t\t\t? `The node did not respond after ${transaction.message.maxSendAttempts} attempts`\n\t\t\t: `The node did not respond`;\n\n\t\tif (!transaction.changeNodeStatusOnTimeout) {\n\t\t\t// The sender of this transaction doesn't want it to change the status of the node\n\t\t\treturn false;\n\t\t} else if (node.canSleep) {\n\t\t\tif (node.status === NodeStatus.Asleep) {\n\t\t\t\t// We already moved the messages to the wakeup queue before. If we end up here, this means a command\n\t\t\t\t// was sent that may be sent to potentially asleep nodes - including pings.\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tnode.id,\n\t\t\t\t`${messagePart1}. It is probably asleep, moving its messages to the wakeup queue.`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\n\t\t\t// There is no longer a reference to the current transaction. If it should be moved to the wakeup queue,\n\t\t\t// it temporarily needs to be added to the queue again.\n\t\t\tconst handled = this.mayMoveToWakeupQueue(transaction);\n\t\t\tif (handled) {\n\t\t\t\tthis.queue.add(transaction);\n\t\t\t}\n\n\t\t\t// Mark the node as asleep. This will move the messages to the wakeup queue\n\t\t\tnode.markAsAsleep();\n\n\t\t\treturn handled;\n\t\t} else {\n\t\t\tconst errorMsg = `${messagePart1}, it is presumed dead`;\n\t\t\tthis.controllerLog.logNode(node.id, errorMsg, \"warn\");\n\n\t\t\tnode.markAsDead();\n\n\t\t\t// There is no longer a reference to the current transaction on the queue, so we need to reject it separately.\n\t\t\ttransaction.setProgress({\n\t\t\t\tstate: TransactionState.Failed,\n\t\t\t\treason: errorMsg,\n\t\t\t});\n\n\t\t\ttransaction.abort(error);\n\t\t\tvoid this.rejectAllTransactionsForNode(node.id, errorMsg);\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Handles the case that the controller didn't acknowledge a command in time\n\t * Returns `true` if the transaction failure was handled, `false` if it needs to be rejected.\n\t */\n\tprivate handleMissingControllerACK(\n\t\ttransaction: Transaction,\n\t\terror: ZWaveError & {\n\t\t\tcode: ZWaveErrorCodes.Controller_Timeout;\n\t\t\tcontext: \"ACK\";\n\t\t},\n\t): boolean {\n\t\tif (!this._controller || !this.mayRecoverUnresponsiveController()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst recoverByReopeningSerialport = async () => {\n\t\t\tif (!this.serial) return;\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"Attempting to recover unresponsive controller by reopening the serial port...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tif (this.serial.isOpen) await this.serial.close();\n\t\t\tawait wait(1000);\n\t\t\tawait this.openSerialport();\n\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"Serial port reopened. Returning to normal operation and hoping for the best...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\n\t\t\t// We don't know if this worked\n\t\t\t// Go back to normal operation and hope for the best.\n\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t};\n\n\t\tif (\n\t\t\t(this._controller.status !== ControllerStatus.Unresponsive\n\t\t\t\t&& !this.maySoftReset())\n\t\t\t|| this._recoveryPhase\n\t\t\t\t=== ControllerRecoveryPhase.ACKTimeoutAfterReset\n\t\t) {\n\t\t\t// Either we can/could not do a soft reset or the controller is still timing out afterwards\n\t\t\tvoid recoverByReopeningSerialport().catch(noop);\n\n\t\t\treturn true;\n\t\t} else if (this._controller.status !== ControllerStatus.Unresponsive) {\n\t\t\t// The controller was responsive before this transaction failed.\n\t\t\t// Mark it as unresponsive and try to soft-reset it.\n\t\t\tthis.controller.setStatus(\n\t\t\t\tControllerStatus.Unresponsive,\n\t\t\t);\n\n\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.ACKTimeout;\n\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"Attempting to recover unresponsive controller by restarting it...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\n\t\t\t// Execute the soft-reset asynchronously\n\t\t\tvoid this.softReset().then(() => {\n\t\t\t\t// The controller responded. It is no longer unresponsive\n\n\t\t\t\t// Re-queue the transaction, so it can get handled next.\n\t\t\t\t// Its message generator may have finished, so reset that too.\n\t\t\t\ttransaction.reset();\n\t\t\t\tthis.getQueueForTransaction(transaction).add(\n\t\t\t\t\ttransaction.clone(),\n\t\t\t\t);\n\n\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t\t}).catch(() => {\n\t\t\t\t// Soft-reset failed. Reject the transaction\n\t\t\t\tthis.rejectTransaction(transaction, error);\n\n\t\t\t\t// and reopen the serial port\n\t\t\t\treturn recoverByReopeningSerialport();\n\t\t\t});\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\t// Not sure what to do here\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Handles the case that the controller didn't send the callback for a SendData in time\n\t * Returns `true` if the transaction failure was handled, `false` if it needs to be rejected.\n\t */\n\tprivate handleMissingSendDataResponseOrCallback(\n\t\ttransaction: Transaction,\n\t\terror: ZWaveError & {\n\t\t\tcode: ZWaveErrorCodes.Controller_Timeout;\n\t\t\tcontext: \"callback\" | \"response\";\n\t\t},\n\t): boolean {\n\t\tif (!this._controller || !this.mayRecoverUnresponsiveController()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (\n\t\t\t// The SendData response can time out on older controllers trying to reach a dead node.\n\t\t\t// In this case, we do not want to reset the controller, but just mark the node as dead.\n\t\t\terror.context === \"response\"\n\t\t\t// Also do this if the callback is timing out even after restarting the controller\n\t\t\t|| this._recoveryPhase\n\t\t\t\t=== ControllerRecoveryPhase.CallbackTimeoutAfterReset\n\t\t) {\n\t\t\tconst node = this.tryGetNode(transaction.message);\n\t\t\tif (!node) return false; // This should never happen, but whatever\n\n\t\t\t// The controller is still timing out transmitting after a soft reset, don't try again.\n\t\t\t// Real-world experience has shown that for older controllers this situation can be caused by unresponsive nodes.\n\n\t\t\t// The following is essentially a copy of handleMissingNodeACK, but with updated error messages\n\t\t\tconst messagePart1 =\n\t\t\t\t\"The node is causing the controller to become unresponsive\";\n\n\t\t\tlet handled: boolean;\n\n\t\t\tif (node.canSleep) {\n\t\t\t\tif (node.status === NodeStatus.Asleep) {\n\t\t\t\t\t// We already moved the messages to the wakeup queue before. If we end up here, this means a command\n\t\t\t\t\t// was sent that may be sent to potentially asleep nodes - including pings.\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\tnode.id,\n\t\t\t\t\t`${messagePart1}. It is probably asleep, moving its messages to the wakeup queue.`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\n\t\t\t\t// There is no longer a reference to the current transaction. If it should be moved to the wakeup queue,\n\t\t\t\t// it temporarily needs to be added to the queue again.\n\t\t\t\thandled = this.mayMoveToWakeupQueue(transaction);\n\t\t\t\tif (handled) {\n\t\t\t\t\tthis.queue.add(transaction);\n\t\t\t\t}\n\n\t\t\t\t// Mark the node as asleep. This will move the messages to the wakeup queue\n\t\t\t\tnode.markAsAsleep();\n\t\t\t} else {\n\t\t\t\tconst errorMsg = `${messagePart1}, it is presumed dead`;\n\t\t\t\tthis.controllerLog.logNode(node.id, errorMsg, \"warn\");\n\n\t\t\t\tnode.markAsDead();\n\n\t\t\t\t// There is no longer a reference to the current transaction on the queue, so we need to reject it separately.\n\t\t\t\ttransaction.setProgress({\n\t\t\t\t\tstate: TransactionState.Failed,\n\t\t\t\t\treason: errorMsg,\n\t\t\t\t});\n\n\t\t\t\ttransaction.abort(error);\n\t\t\t\tvoid this.rejectAllTransactionsForNode(node.id, errorMsg);\n\n\t\t\t\thandled = true;\n\t\t\t}\n\n\t\t\t// If the controller is still timing out, reset it once more\n\t\t\tif (\n\t\t\t\tthis._recoveryPhase\n\t\t\t\t\t=== ControllerRecoveryPhase.CallbackTimeoutAfterReset\n\t\t\t) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Attempting to recover controller again...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tvoid this.softResetInternal(true).catch(() => {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Automatic controller recovery failed. Returning to normal operation and hoping for the best.\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}).finally(() => {\n\t\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn handled;\n\t\t} else if (this._controller.status !== ControllerStatus.Unresponsive) {\n\t\t\t// The controller was responsive before this transaction failed.\n\n\t\t\tif (this.maySoftReset()) {\n\t\t\t\t// Mark it as unresponsive and try to soft-reset it.\n\t\t\t\tthis.controller.setStatus(\n\t\t\t\t\tControllerStatus.Unresponsive,\n\t\t\t\t);\n\n\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.CallbackTimeout;\n\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Controller missed Send Data callback. Attempting to recover...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\n\t\t\t\t// Execute the soft-reset asynchronously\n\t\t\t\tvoid this.softResetInternal(true).then(() => {\n\t\t\t\t\t// The controller responded. It is no longer unresponsive.\n\n\t\t\t\t\t// Re-queue the transaction, so it can get handled next.\n\t\t\t\t\t// Its message generator may have finished, so reset that too.\n\t\t\t\t\ttransaction.reset();\n\t\t\t\t\tthis.getQueueForTransaction(transaction).add(\n\t\t\t\t\t\ttransaction.clone(),\n\t\t\t\t\t);\n\n\t\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\t\tthis._recoveryPhase =\n\t\t\t\t\t\tControllerRecoveryPhase.CallbackTimeoutAfterReset;\n\t\t\t\t}).catch(() => {\n\t\t\t\t\t// Soft-reset failed. Just reject the transaction\n\t\t\t\t\tthis.rejectTransaction(transaction, error);\n\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Automatic controller recovery failed. Returning to normal operation and hoping for the best.\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Controller missed Send Data callback. Cannot recover automatically because the soft reset feature is unsupported or disabled. Returning to normal operation and hoping for the best...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tthis.rejectTransaction(transaction, error);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\t// Not sure what to do here\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Handles the case that the controller locks up and fails to transmit continuously\n\t */\n\tprivate handleJammedController(\n\t\ttransaction: Transaction,\n\t\terror: ZWaveError,\n\t): boolean {\n\t\tif (!this._controller || !this.mayRecoverUnresponsiveController()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (\n\t\t\t// Transmits still fail even after restarting the controller\n\t\t\tthis._recoveryPhase\n\t\t\t\t=== ControllerRecoveryPhase.JammedAfterReset\n\t\t) {\n\t\t\t// Maybe this isn't actually the controller being jammed. Give up on this command.\n\t\t\tthis.driverLog.print(\n\t\t\t\t\"Automatic controller recovery failed. Returning to normal operation and hoping for the best.\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t\tthis._controller.setStatus(ControllerStatus.Ready);\n\n\t\t\treturn false;\n\t\t} else if (this._controller.status === ControllerStatus.Jammed) {\n\t\t\t// The controller failed to transmit continuously. Try to soft-reset it if we can.\n\t\t\tif (this.controller.sdkVersionLt(\"7.0\")) {\n\t\t\t\t// The workaround only makes sense on 700/800 series\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Cannot recover jammed controller automatically. Returning to normal operation and hoping for the best...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\tthis.rejectTransaction(transaction, error);\n\t\t\t} else if (this.maySoftReset()) {\n\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.Jammed;\n\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Attempting to recover jammed controller...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\n\t\t\t\t// Execute the soft-reset asynchronously\n\t\t\t\tvoid this.softReset().then(() => {\n\t\t\t\t\t// The controller responded. It is no longer unresponsive.\n\n\t\t\t\t\t// Re-queue the transaction, so it can get handled next.\n\t\t\t\t\t// Its message generator may have finished, so reset that too.\n\t\t\t\t\ttransaction.reset();\n\t\t\t\t\tthis.getQueueForTransaction(transaction).add(\n\t\t\t\t\t\ttransaction.clone(),\n\t\t\t\t\t);\n\n\t\t\t\t\tthis._recoveryPhase =\n\t\t\t\t\t\tControllerRecoveryPhase.JammedAfterReset;\n\t\t\t\t}).catch(() => {\n\t\t\t\t\t// Soft-reset failed. Just reject the transaction\n\t\t\t\t\tthis.rejectTransaction(transaction, error);\n\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Automatic controller recovery failed. Returning to normal operation and hoping for the best.\",\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tthis._recoveryPhase = ControllerRecoveryPhase.None;\n\t\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\"Cannot recover jammed controller automatically because the soft reset feature is unsupported or disabled. Returning to normal operation and hoping for the best...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tthis._controller?.setStatus(ControllerStatus.Ready);\n\t\t\t\tthis.rejectTransaction(transaction, error);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\t// Not sure what to do here\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate shouldRequestWakeupOnDemand(node: ZWaveNode): boolean {\n\t\treturn (\n\t\t\t!!node.supportsWakeUpOnDemand\n\t\t\t&& node.status === NodeStatus.Asleep\n\t\t\t&& this.hasPendingTransactions(\n\t\t\t\t(t) =>\n\t\t\t\t\tt.requestWakeUpOnDemand\n\t\t\t\t\t&& t.message.getNodeId() === node.id,\n\t\t\t)\n\t\t);\n\t}\n\n\tprivate partialCCSessions = new Map<string, CommandClass[]>();\n\tprivate getPartialCCSession(\n\t\tcommand: CommandClass,\n\t\tcreateIfMissing: false,\n\t): { partialSessionKey: string; session?: CommandClass[] } | undefined;\n\tprivate getPartialCCSession(\n\t\tcommand: CommandClass,\n\t\tcreateIfMissing: true,\n\t): { partialSessionKey: string; session: CommandClass[] } | undefined;\n\tprivate getPartialCCSession(\n\t\tcommand: CommandClass,\n\t\tcreateIfMissing: boolean,\n\t): { partialSessionKey: string; session?: CommandClass[] } | undefined {\n\t\tconst sessionId = command.getPartialCCSessionId();\n\n\t\tif (sessionId) {\n\t\t\t// This CC belongs to a partial session\n\t\t\tconst partialSessionKey = JSON.stringify({\n\t\t\t\tnodeId: command.nodeId,\n\t\t\t\tccId: command.ccId,\n\t\t\t\tccCommand: command.ccCommand!,\n\t\t\t\t...sessionId,\n\t\t\t});\n\t\t\tif (\n\t\t\t\tcreateIfMissing\n\t\t\t\t&& !this.partialCCSessions.has(partialSessionKey)\n\t\t\t) {\n\t\t\t\tthis.partialCCSessions.set(partialSessionKey, []);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tpartialSessionKey,\n\t\t\t\tsession: this.partialCCSessions.get(partialSessionKey),\n\t\t\t};\n\t\t}\n\t}\n\t/**\n\t * Assembles partial CCs of in a message body. Returns `true` when the message is complete and can be handled further.\n\t * If the message expects another partial one, this returns `false`.\n\t */\n\tprivate async assemblePartialCCs(\n\t\tmsg: CommandRequest & ContainsCC,\n\t): Promise<boolean> {\n\t\tlet command: CommandClass | undefined = msg.command;\n\t\t// We search for the every CC that provides us with a session ID\n\t\t// There might be newly-completed CCs that contain a partial CC,\n\t\t// so investigate the entire CC encapsulation stack.\n\t\twhile (true) {\n\t\t\tconst { partialSessionKey, session } =\n\t\t\t\tthis.getPartialCCSession(command, true) ?? {};\n\t\t\tif (session) {\n\t\t\t\t// This CC belongs to a partial session\n\t\t\t\tif (command.expectMoreMessages(session)) {\n\t\t\t\t\t// this is not the final one, store it\n\t\t\t\t\tsession.push(command);\n\t\t\t\t\tif (!isTransportServiceEncapsulation(msg.command)) {\n\t\t\t\t\t\t// and don't handle the command now\n\t\t\t\t\t\tthis.driverLog.logMessage(msg, {\n\t\t\t\t\t\t\tsecondaryTags: [\"partial\"],\n\t\t\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t} else {\n\t\t\t\t\t// this is the final one, merge the previous responses\n\t\t\t\t\tthis.partialCCSessions.delete(partialSessionKey!);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait command.mergePartialCCs(session, {\n\t\t\t\t\t\t\t...this.getCCParsingContext(),\n\t\t\t\t\t\t\tsourceNodeId: msg.command.nodeId as number,\n\t\t\t\t\t\t\tframeType: msg.frameType,\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Ensure there are no errors\n\t\t\t\t\t\tassertValidCCs(msg);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (isZWaveError(e)) {\n\t\t\t\t\t\t\tswitch (e.code) {\n\t\t\t\t\t\t\t\tcase ZWaveErrorCodes\n\t\t\t\t\t\t\t\t\t.Deserialization_NotImplemented:\n\t\t\t\t\t\t\t\tcase ZWaveErrorCodes.CC_NotImplemented:\n\t\t\t\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\t\t\t`Dropping message because it could not be deserialized: ${e.message}`,\n\t\t\t\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t// Don't continue handling this message\n\t\t\t\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t\t\t\tcase ZWaveErrorCodes\n\t\t\t\t\t\t\t\t\t.PacketFormat_InvalidPayload:\n\t\t\t\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\t\t\t`Could not assemble partial CCs because the payload is invalid. Dropping them.`,\n\t\t\t\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t// Don't continue handling this message\n\t\t\t\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t\t\t\tcase ZWaveErrorCodes.Driver_NotReady:\n\t\t\t\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\t\t\t`Could not assemble partial CCs because the driver is not ready yet. Dropping them`,\n\t\t\t\t\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t// Don't continue handling this message\n\t\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\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t\t// Assembling this CC was successful - but it might contain another partial CC\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// No partial CC, just continue\n\t\t\t}\n\n\t\t\t// If this is an encapsulating CC, we need to look one level deeper\n\t\t\tif (isEncapsulatingCommandClass(command)) {\n\t\t\t\tcommand = command.encapsulated;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/** Is called when a Transport Service command is received */\n\tprivate async handleTransportServiceCommand(\n\t\tcommand:\n\t\t\t| TransportServiceCCFirstSegment\n\t\t\t| TransportServiceCCSubsequentSegment,\n\t): Promise<void> {\n\t\tconst nodeSessions = this.ensureNodeSessions(command.nodeId);\n\n\t\t// TODO: Figure out how to know which timeout is the correct one. For now use the larger one\n\t\tconst missingSegmentTimeout =\n\t\t\tTransportServiceTimeouts.requestMissingSegmentR2;\n\n\t\tconst advanceTransportServiceSession = async (\n\t\t\tsession: TransportServiceSession,\n\t\t\tinput: TransportServiceRXMachineInput,\n\t\t): Promise<void> => {\n\t\t\tconst machine = session.machine;\n\n\t\t\t// Figure out what needs to be done for this input\n\t\t\tconst transition = machine.next(input);\n\t\t\tif (transition) {\n\t\t\t\tmachine.transition(transition.newState);\n\n\t\t\t\tif (machine.state.value === \"receive\") {\n\t\t\t\t\t// We received a segment in the normal flow. Restart the timer\n\t\t\t\t\tstartMissingSegmentTimeout(session);\n\t\t\t\t} else if (machine.state.value === \"requestMissing\") {\n\t\t\t\t\t// A segment is missing. Request it and restart the timeout\n\t\t\t\t\tthis.controllerLog.logNode(command.nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Transport Service RX session #${command.sessionId}: Segment with offset ${machine.state.offset} missing - requesting it...`,\n\t\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t});\n\t\t\t\t\tconst cc = new TransportServiceCCSegmentRequest({\n\t\t\t\t\t\tnodeId: command.nodeId,\n\t\t\t\t\t\tsessionId: command.sessionId,\n\t\t\t\t\t\tdatagramOffset: machine.state.offset,\n\t\t\t\t\t});\n\t\t\t\t\tawait this.sendCommand(cc, {\n\t\t\t\t\t\tmaxSendAttempts: 1,\n\t\t\t\t\t\tpriority: MessagePriority.Immediate,\n\t\t\t\t\t});\n\n\t\t\t\t\tstartMissingSegmentTimeout(session);\n\t\t\t\t} else if (machine.state.value === \"failure\") {\n\t\t\t\t\tthis.controllerLog.logNode(command.nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Transport Service RX session #${command.sessionId} failed`,\n\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\t});\n\t\t\t\t\t// TODO: Update statistics\n\t\t\t\t\tnodeSessions.transportService.delete(command.sessionId);\n\t\t\t\t\tif (session.timeout) {\n\t\t\t\t\t\tclearTimeout(session.timeout);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (machine.state.value === \"success\") {\n\t\t\t\t// This state may happen without a transition if we received the last segment before\n\t\t\t\t// but the SegmentComplete message got lost\n\t\t\t\tthis.controllerLog.logNode(command.nodeId, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Transport Service RX session #${command.sessionId} complete`,\n\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\t\t\t\tif (session.timeout) {\n\t\t\t\t\tclearTimeout(session.timeout);\n\t\t\t\t}\n\n\t\t\t\tconst cc = new TransportServiceCCSegmentComplete({\n\t\t\t\t\tnodeId: command.nodeId,\n\t\t\t\t\tsessionId: command.sessionId,\n\t\t\t\t});\n\t\t\t\tawait this.sendCommand(cc, {\n\t\t\t\t\tmaxSendAttempts: 1,\n\t\t\t\t\tpriority: MessagePriority.Immediate,\n\t\t\t\t}).catch(noop);\n\t\t\t}\n\t\t};\n\n\t\tfunction startMissingSegmentTimeout(\n\t\t\tsession: TransportServiceSession,\n\t\t): void {\n\t\t\tif (session.timeout) {\n\t\t\t\tclearTimeout(session.timeout);\n\t\t\t}\n\n\t\t\tsession.timeout = setTimeout(() => {\n\t\t\t\tsession.timeout = undefined;\n\t\t\t\tvoid advanceTransportServiceSession(session, {\n\t\t\t\t\tvalue: \"timeout\",\n\t\t\t\t});\n\t\t\t}, missingSegmentTimeout);\n\t\t}\n\n\t\tif (command instanceof TransportServiceCCFirstSegment) {\n\t\t\t// This is the first message in a sequence. Create or re-initialize the session\n\t\t\t// We don't delete finished sessions when the last message is received in order to be able to\n\t\t\t// handle when the SegmentComplete message gets lost. As soon as the node initializes a new session,\n\t\t\t// we do know that the previous one is finished.\n\t\t\tnodeSessions.transportService.clear();\n\n\t\t\tthis.controllerLog.logNode(command.nodeId, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Beginning Transport Service RX session #${command.sessionId}...`,\n\t\t\t\tlevel: \"debug\",\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\n\t\t\tconst machine = createTransportServiceRXMachine(\n\t\t\t\tcommand.datagramSize,\n\t\t\t\tcommand.partialDatagram.length,\n\t\t\t);\n\n\t\t\tconst session: TransportServiceSession = {\n\t\t\t\tfragmentSize: command.partialDatagram.length,\n\t\t\t\tmachine,\n\t\t\t};\n\t\t\tnodeSessions.transportService.set(command.sessionId, session);\n\n\t\t\t// Time out waiting for subsequent segments\n\t\t\tstartMissingSegmentTimeout(session);\n\t\t} else {\n\t\t\t// This is a subsequent message in a sequence. Continue executing the state machine\n\t\t\tconst transportSession = nodeSessions.transportService.get(\n\t\t\t\tcommand.sessionId,\n\t\t\t);\n\t\t\tif (transportSession) {\n\t\t\t\tawait advanceTransportServiceSession(transportSession, {\n\t\t\t\t\tvalue: \"segment\",\n\t\t\t\t\toffset: command.datagramOffset,\n\t\t\t\t\tlength: command.partialDatagram.length,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// This belongs to a session we don't know... tell the sending node to try again\n\t\t\t\tconst cc = new TransportServiceCCSegmentWait({\n\t\t\t\t\tnodeId: command.nodeId,\n\t\t\t\t\tpendingSegments: 0,\n\t\t\t\t});\n\t\t\t\tawait this.sendCommand(cc, {\n\t\t\t\t\tmaxSendAttempts: 1,\n\t\t\t\t\tpriority: MessagePriority.Immediate,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Is called when a message is received that does not belong to any ongoing transactions\n\t * @param msg The decoded message\n\t */\n\tprivate async handleUnsolicitedMessage(msg: Message): Promise<void> {\n\t\t// FIXME: Rename this - msg might not be unsolicited\n\t\t// This is a message we might have registered handlers for\n\t\ttry {\n\t\t\tif (msg.type === MessageType.Request) {\n\t\t\t\tawait this.handleRequest(msg);\n\t\t\t} else {\n\t\t\t\tawait this.handleResponse(msg);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Driver_NotReady\n\t\t\t) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t`Cannot handle message because the driver is not ready to handle it yet.`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Is called when the Serial API restart unexpectedly.\n\t */\n\tprivate async handleSerialAPIStartedUnexpectedly(\n\t\tmsg: SerialAPIStartedRequest,\n\t): Promise<boolean> {\n\t\t// Normally, the soft reset command includes waiting for this message.\n\t\t// If we end up here, it is unexpected.\n\n\t\tswitch (msg.wakeUpReason) {\n\t\t\t// All wakeup reasons that indicate a reset of the Serial API\n\t\t\t// need to be handled here, so we interpret node IDs correctly.\n\t\t\tcase SerialAPIWakeUpReason.Reset:\n\t\t\tcase SerialAPIWakeUpReason.WatchdogReset:\n\t\t\tcase SerialAPIWakeUpReason.SoftwareReset:\n\t\t\tcase SerialAPIWakeUpReason.EmergencyWatchdogReset:\n\t\t\tcase SerialAPIWakeUpReason.BrownoutCircuit: {\n\t\t\t\t// The Serial API restarted unexpectedly\n\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t`Serial API restarted unexpectedly.`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\n\t\t\t\t// In this situation, we may be executing a Serial API command, which will never complete.\n\t\t\t\t// Abort it, so it can be retried\n\t\t\t\tif (this.abortSerialAPICommand) {\n\t\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t\t`Currently active command will be retried...`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t\tthis.abortSerialAPICommand.reject(\n\t\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\t\"The Serial API restarted unexpectedly\",\n\t\t\t\t\t\t\tZWaveErrorCodes.Controller_Reset,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Restart the watchdog unless disabled\n\t\t\t\tif (this.options.features.watchdog) {\n\t\t\t\t\tawait this._controller?.startWatchdog();\n\t\t\t\t}\n\n\t\t\t\tif (this._controller?.nodeIdType === NodeIDType.Long) {\n\t\t\t\t\t// We previously used 16 bit node IDs, but the controller was reset.\n\t\t\t\t\t// Remember this and try to go back to 16 bit.\n\t\t\t\t\tthis._controller.nodeIdType = NodeIDType.Short;\n\t\t\t\t\tawait this._controller.trySetNodeIDType(NodeIDType.Long);\n\t\t\t\t}\n\n\t\t\t\treturn true; // Don't invoke any more handlers\n\t\t\t}\n\t\t}\n\n\t\treturn false; // Not handled\n\t}\n\n\t/**\n\t * Registers a handler for messages that are not handled by the driver as part of a message exchange.\n\t * The handler function needs to return a boolean indicating if the message has been handled.\n\t * Registered handlers are called in sequence until a handler returns `true`.\n\t *\n\t * @param fnType The function type to register the handler for\n\t * @param handler The request handler callback\n\t * @param oneTime Whether the handler should be removed after its first successful invocation\n\t */\n\tpublic registerRequestHandler<T extends Message>(\n\t\tfnType: FunctionType,\n\t\thandler: RequestHandler<T>,\n\t\toneTime: boolean = false,\n\t): void {\n\t\tconst handlers: RequestHandlerEntry<T>[] = this.requestHandlers.has(\n\t\t\t\tfnType,\n\t\t\t)\n\t\t\t? this.requestHandlers.get(fnType)!\n\t\t\t: [];\n\t\tconst entry: RequestHandlerEntry<T> = { invoke: handler, oneTime };\n\t\thandlers.push(entry);\n\t\tthis.driverLog.print(\n\t\t\t`added${oneTime ? \" one-time\" : \"\"} request handler for ${\n\t\t\t\tFunctionType[fnType]\n\t\t\t} (${num2hex(fnType)})...\n${handlers.length} registered`,\n\t\t);\n\t\tthis.requestHandlers.set(fnType, handlers as RequestHandlerEntry[]);\n\t}\n\n\t/**\n\t * Unregisters a message handler that has been added with `registerRequestHandler`\n\t * @param fnType The function type to unregister the handler for\n\t * @param handler The previously registered request handler callback\n\t */\n\tpublic unregisterRequestHandler(\n\t\tfnType: FunctionType,\n\t\thandler: RequestHandler,\n\t): void {\n\t\tconst handlers = this.requestHandlers.has(fnType)\n\t\t\t? this.requestHandlers.get(fnType)!\n\t\t\t: [];\n\t\tfor (let i = 0, entry = handlers[i]; i < handlers.length; i++) {\n\t\t\t// remove the handler if it was found\n\t\t\tif (entry.invoke === handler) {\n\t\t\t\thandlers.splice(i, 1);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tthis.driverLog.print(\n\t\t\t`removed request handler for ${FunctionType[fnType]} (${fnType})...\n${handlers.length} left`,\n\t\t);\n\t\tthis.requestHandlers.set(fnType, handlers);\n\t}\n\n\t/**\n\t * Checks whether a CC has a lower than expected security level and needs to be discarded\n\t */\n\tprivate isSecurityLevelTooLow(cc: CommandClass): boolean {\n\t\t// With Security S0, some commands may be accepted without encryption, some require it\n\t\t// With Security S2, a node MUST support its command classes only when communication is using its\n\t\t// highest Security Class granted during security bootstrapping.\n\n\t\t// We already discard lower S2 keys when decrypting, so all that's left here to check is if the\n\t\t// CC is encrypted at all.\n\n\t\tconst node = this._controller?.nodes.get(cc.nodeId as number);\n\t\tif (!node) {\n\t\t\t// Node does not exist, don't accept the CC\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tcc.nodeId as number,\n\t\t\t\t`is unknown - discarding received command...`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Transport Service has a special handler\n\t\tif (cc instanceof TransportServiceCC) return false;\n\t\t// CRC16 belongs outside of Security encapsulation\n\t\tif (cc instanceof CRC16CCCommandEncapsulation) {\n\t\t\treturn this.isSecurityLevelTooLow(cc.encapsulated);\n\t\t}\n\n\t\tconst secClass = node.getHighestSecurityClass();\n\t\tif (\n\t\t\tsecClass === SecurityClass.None\n\t\t\t|| secClass === SecurityClass.Temporary\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst expectedSecurityCC = securityClassIsS2(secClass)\n\t\t\t? CommandClasses[\"Security 2\"]\n\t\t\t: secClass === SecurityClass.S0_Legacy\n\t\t\t? CommandClasses.Security\n\t\t\t: undefined;\n\n\t\tconst isCCConsideredSecure = (\n\t\t\tcmd: CommandClass,\n\t\t): MaybeNotKnown<boolean> => {\n\t\t\t// Some CCs are always accepted, regardless of security class\n\t\t\tif (cmd instanceof SecurityCC) {\n\t\t\t\tswitch (cmd.ccCommand) {\n\t\t\t\t\t// Cannot be sent encapsulated:\n\t\t\t\t\tcase SecurityCommand.NonceGet:\n\t\t\t\t\tcase SecurityCommand.NonceReport:\n\t\t\t\t\tcase SecurityCommand.SchemeGet:\n\t\t\t\t\tcase SecurityCommand.SchemeReport:\n\t\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tif (cmd instanceof SecurityCCCommandEncapsulation) {\n\t\t\t\t\t// CommandsSupportedReport is always accepted to be able to learn security classes and interview nodes\n\t\t\t\t\t// CommandsSupportedGet is always accepted, so others can learn our security classes\n\t\t\t\t\tif (\n\t\t\t\t\t\tcmd.encapsulated\n\t\t\t\t\t\t\tinstanceof SecurityCCCommandsSupportedReport\n\t\t\t\t\t\t|| cmd.encapsulated\n\t\t\t\t\t\t\tinstanceof SecurityCCCommandsSupportedGet\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Other S0 commands are only accepted if S0 is the highest security class\n\t\t\t\t\treturn secClass === SecurityClass.S0_Legacy;\n\t\t\t\t}\n\t\t\t} else if (cmd instanceof Security2CC) {\n\t\t\t\tif (cmd instanceof Security2CCMessageEncapsulation) {\n\t\t\t\t\t// CommandsSupportedReport is always accepted to be able to learn security classes and interview nodes\n\t\t\t\t\tif (\n\t\t\t\t\t\tcmd.encapsulated\n\t\t\t\t\t\t\tinstanceof Security2CCCommandsSupportedReport\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// CommandsSupportedGet is always accepted, so others can learn our security classes\n\t\t\t\t\tif (\n\t\t\t\t\t\tcmd.encapsulated\n\t\t\t\t\t\t\tinstanceof Security2CCCommandsSupportedGet\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Multicast commands are always accepted\n\t\t\t\t\tif (cmd.getMulticastGroupId() != undefined) return true;\n\n\t\t\t\t\t// This shouldn't happen, but better be sure\n\t\t\t\t\tif (cmd.securityClass == undefined) return false;\n\n\t\t\t\t\t// All other commands are only accepted if the highest security class is used\n\t\t\t\t\treturn cmd.securityClass === secClass;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn cmd.ccId === expectedSecurityCC;\n\t\t};\n\n\t\tlet requiresSecurity = securityClassIsS2(secClass);\n\t\tconst isSecure = isCCConsideredSecure(cc);\n\n\t\twhile (true) {\n\t\t\tif (isEncapsulatingCommandClass(cc)) {\n\t\t\t\tcc = cc.encapsulated;\n\t\t\t} else if (isMultiEncapsulatingCommandClass(cc)) {\n\t\t\t\trequiresSecurity ||= cc.encapsulated.some((cmd) =>\n\t\t\t\t\tnode.isCCSecure(cmd.ccId)\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\trequiresSecurity ||= node.isCCSecure(cc.ccId)\n\t\t\t\t\t&& cc.ccId !== CommandClasses.Security\n\t\t\t\t\t&& cc.ccId !== CommandClasses[\"Security 2\"];\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (requiresSecurity && !isSecure) {\n\t\t\t// none found, don't accept the CC\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tcc.nodeId as number,\n\t\t\t\t`command was received at a lower security level than expected - discarding it...`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/** Checks whether a CC should be discarded */\n\tprivate shouldDiscardCC(cc: CommandClass): boolean {\n\t\tif (isEncapsulatingCommandClass(cc)) {\n\t\t\treturn this.shouldDiscardCC(cc.encapsulated);\n\t\t}\n\n\t\tconst node = this._controller?.nodes.get(cc.nodeId as number);\n\t\t// We should have checked this before, but better be safe than sorry\n\t\tif (!node) {\n\t\t\t// Node does not exist, don't accept the CC\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tcc.nodeId as number,\n\t\t\t\t`is unknown - discarding received command...`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\tif (\n\t\t\tcc.constructor.name.endsWith(\"Get\")\n\t\t\t&& (cc.frameType === \"multicast\" || cc.frameType === \"broadcast\")\n\t\t) {\n\t\t\tthis.controllerLog.logNode(\n\t\t\t\tcc.nodeId as number,\n\t\t\t\t`received GET-type command via ${cc.frameType} - discarding...`,\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Do not accept Meter CC and/or Multilevel Sensor CC if the node does not support them\n\t\t// https://github.com/zwave-js/zwave-js/issues/5510\n\t\t// TODO: Consider expanding this to all CCs and not only reports\n\t\tif (\n\t\t\tcc.ccId === CommandClasses.Meter\n\t\t\t|| cc.ccId === CommandClasses[\"Multilevel Sensor\"]\n\t\t) {\n\t\t\tconst endpoint = node.getEndpoint(cc.endpointIndex) ?? node;\n\t\t\tif (\n\t\t\t\t!endpoint.supportsCC(cc.ccId) && !endpoint.controlsCC(cc.ccId)\n\t\t\t) {\n\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\tcc.nodeId as number,\n\t\t\t\t\t`${\n\t\t\t\t\t\tcc.endpointIndex > 0\n\t\t\t\t\t\t\t? `Endpoint ${cc.endpointIndex} `\n\t\t\t\t\t\t\t: \"\"\n\t\t\t\t\t}does not support CC ${\n\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\tcc.ccId,\n\t\t\t\t\t\t)\n\t\t\t\t\t} - discarding received command...`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Is called when a Response-type message was received\n\t */\n\tprivate handleResponse(msg: Message): Promise<void> {\n\t\t// Check if we have a dynamic handler waiting for this message\n\t\tfor (const entry of this.awaitedMessages) {\n\t\t\tif (entry.predicate(msg)) {\n\t\t\t\t// We do\n\t\t\t\tentry.handler(msg);\n\t\t\t\treturn Promise.resolve();\n\t\t\t}\n\t\t}\n\n\t\tthis.driverLog.transactionResponse(msg, undefined, \"unexpected\");\n\t\tthis.driverLog.print(\"unexpected response, discarding...\", \"warn\");\n\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * Is called when a Request-type message was received\n\t */\n\tprivate async handleRequest(msg: Message): Promise<void> {\n\t\tlet handlers: RequestHandlerEntry[] | undefined;\n\n\t\tif (hasNodeId(msg) || containsCC(msg)) {\n\t\t\tconst node = this.tryGetNode(msg);\n\t\t\tif (node) {\n\t\t\t\t// We have received an unsolicited message from a dead node, bring it back to life\n\t\t\t\tif (node.status === NodeStatus.Dead) {\n\t\t\t\t\tnode.markAsAlive();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check if we have a dynamic handler waiting for this message\n\t\tfor (const entry of this.awaitedMessages) {\n\t\t\tif (entry.predicate(msg)) {\n\t\t\t\t// We do\n\t\t\t\tentry.handler(msg);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (isCommandRequest(msg) && containsCC(msg)) {\n\t\t\tconst nodeId = msg.getNodeId()!;\n\n\t\t\t// It could also be that this is the node's response for a CC that we sent, but where the ACK is delayed\n\t\t\tconst currentMessage = this.queue.currentTransaction\n\t\t\t\t?.getCurrentMessage();\n\t\t\tif (\n\t\t\t\tcurrentMessage\n\t\t\t\t&& currentMessage.expectsNodeUpdate()\n\t\t\t\t&& currentMessage.isExpectedNodeUpdate(msg)\n\t\t\t) {\n\t\t\t\t// The message we're currently sending is still in progress but expects this message in response.\n\t\t\t\t// Remember the message there.\n\t\t\t\tthis.controllerLog.logNode(msg.getNodeId()!, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`received expected response prematurely, remembering it...`,\n\t\t\t\t\tlevel: \"verbose\",\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\t\t\t\tcurrentMessage.prematureNodeUpdate = msg;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// For further actions, we are only interested in the innermost CC\n\t\t\tthis.unwrapCommands(msg);\n\n\t\t\t// cannot handle ApplicationCommandRequests without a controller\n\t\t\tif (this._controller == undefined) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t` the controller is not ready yet, discarding...`,\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t} else if (!this.controller.nodes.has(nodeId)) {\n\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t` the node is unknown or not initialized yet, discarding...`,\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\tconst node = this.controller.nodes.get(nodeId)!;\n\t\t\tconst nodeSessions = this.nodeSessions.get(nodeId);\n\t\t\t// Check if we need to handle the command ourselves\n\n\t\t\t// Some Security-related commands make sense to be handled in the driver\n\t\t\tif (msg.command instanceof SecurityCCNonceGet) {\n\t\t\t\treturn this.handleSecurityNonceGet(node);\n\t\t\t}\n\t\t\tif (msg.command instanceof SecurityCCNonceReport) {\n\t\t\t\treturn this.handleSecurityNonceReport(node, msg.command);\n\t\t\t}\n\t\t\tif (msg.command instanceof SecurityCCCommandsSupportedGet) {\n\t\t\t\treturn this.handleSecurityCommandsSupportedGet(\n\t\t\t\t\tnode,\n\t\t\t\t\tmsg.command,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (msg.command instanceof Security2CCNonceGet) {\n\t\t\t\treturn this.handleSecurity2NonceGet(node);\n\t\t\t}\n\t\t\t// Nonce Report is handled further down, as we might have dynamic handlers for it\n\t\t\tif (msg.command instanceof Security2CCCommandsSupportedGet) {\n\t\t\t\treturn this.handleSecurity2CommandsSupportedGet(\n\t\t\t\t\tnode,\n\t\t\t\t\tmsg.command,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tmsg.command.ccId === CommandClasses.Supervision\n\t\t\t\t&& msg.command instanceof SupervisionCCReport\n\t\t\t\t&& nodeSessions?.supervision.has(msg.command.sessionId)\n\t\t\t) {\n\t\t\t\t// Supervision commands are handled here\n\t\t\t\tthis.controllerLog.logNode(msg.command.nodeId, {\n\t\t\t\t\tmessage: `Received update for a Supervision session`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\n\t\t\t\t// Call the update handler\n\t\t\t\tnodeSessions.supervision.get(msg.command.sessionId)!({\n\t\t\t\t\tstatus: msg.command.status,\n\t\t\t\t\tremainingDuration: msg.command.duration,\n\t\t\t\t} as SupervisionResult);\n\t\t\t\t// If this was a final report, remove the handler\n\t\t\t\tif (!msg.command.moreUpdatesFollow) {\n\t\t\t\t\tnodeSessions.supervision.delete(msg.command.sessionId);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Figure out if the command was received with supervision encapsulation and we need to respond accordingly\n\t\t\tconst supervisionSessionId = SupervisionCC.getSessionId(\n\t\t\t\tmsg.command,\n\t\t\t);\n\t\t\t// Figure out if this is an S2 multicast followup for a group that is out of sync\n\t\t\tconst s2MulticastOutOfSync = this.mustReplyWithSecurityS2MOS(\n\t\t\t\tmsg,\n\t\t\t);\n\n\t\t\tconst encapsulationFlags = msg.command.encapsulationFlags;\n\n\t\t\tlet reply: (\n\t\t\t\tstatus:\n\t\t\t\t\t| SupervisionStatus.Success\n\t\t\t\t\t| SupervisionStatus.Fail\n\t\t\t\t\t| SupervisionStatus.NoSupport,\n\t\t\t) => Promise<void>;\n\t\t\tif (supervisionSessionId != undefined) {\n\t\t\t\t// The command was supervised, and we must respond with a Supervision Report\n\t\t\t\tconst endpoint = node.getEndpoint(msg.command.endpointIndex)\n\t\t\t\t\t?? node;\n\t\t\t\treply = (status) =>\n\t\t\t\t\tendpoint\n\t\t\t\t\t\t.createAPI(CommandClasses.Supervision, false)\n\t\t\t\t\t\t.withOptions({ s2MulticastOutOfSync })\n\t\t\t\t\t\t.sendReport({\n\t\t\t\t\t\t\tsessionId: supervisionSessionId,\n\t\t\t\t\t\t\tmoreUpdatesFollow: false,\n\t\t\t\t\t\t\tstatus,\n\t\t\t\t\t\t\trequestWakeUpOnDemand: this\n\t\t\t\t\t\t\t\t.shouldRequestWakeupOnDemand(node),\n\t\t\t\t\t\t\tencapsulationFlags,\n\t\t\t\t\t\t\tlowPriority: this\n\t\t\t\t\t\t\t\t.shouldUseLowPriorityForSupervisionReport(\n\t\t\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\t\t\tencapsulationFlags,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Unsupervised, reply is a no-op\n\t\t\t\treply = () => Promise.resolve();\n\t\t\t}\n\n\t\t\tconst trySupervised = async (\n\t\t\t\taction: () => void | Promise<void>,\n\t\t\t): Promise<void> => {\n\t\t\t\ttry {\n\t\t\t\t\tawait action();\n\t\t\t\t\tawait reply(SupervisionStatus.Success);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tlet handled = false;\n\t\t\t\t\tif (isZWaveError(e)) {\n\t\t\t\t\t\tif (e.code === ZWaveErrorCodes.CC_OperationFailed) {\n\t\t\t\t\t\t\t// The sending node tried to do something that didn't work\n\t\t\t\t\t\t\tawait reply(SupervisionStatus.Fail);\n\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t} else if (e.code === ZWaveErrorCodes.CC_NotSupported) {\n\t\t\t\t\t\t\t// The sending node sent a command we could not handle\n\t\t\t\t\t\t\tawait reply(SupervisionStatus.NoSupport);\n\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!handled) {\n\t\t\t\t\t\t// Something unexpected happened.\n\t\t\t\t\t\t// Report failure, then re-throw the error, so it can be handled accordingly\n\t\t\t\t\t\tawait reply(SupervisionStatus.Fail);\n\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// DO NOT force-add support for the Supervision CC here. Some devices only support Supervision when sending,\n\t\t\t// so we need to trust the information we already have.\n\n\t\t\t// In the case where the command was unsupervised and we need to send a MOS, do it as soon as possible\n\t\t\tif (supervisionSessionId == undefined && s2MulticastOutOfSync) {\n\t\t\t\t// If the command was NOT received using Supervision,\n\t\t\t\t// we need to respond with an MOS nonce. Otherwise we'll set the flag\n\t\t\t\t// on the Supervision Report\n\t\t\t\tnode.commandClasses[\"Security 2\"].sendMOS().catch(() => {\n\t\t\t\t\t// Ignore errors\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// check if someone is waiting for this command\n\t\t\tfor (const entry of this.awaitedCommands) {\n\t\t\t\tif (entry.predicate(msg.command)) {\n\t\t\t\t\t// there is!\n\t\t\t\t\tentry.handler(msg.command);\n\n\t\t\t\t\t// and possibly reply to a supervised command\n\t\t\t\t\tawait reply(SupervisionStatus.Success);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle Nonce Reports if there was no dynamic handler waiting for them\n\t\t\tif (msg.command instanceof Security2CCNonceReport) {\n\t\t\t\treturn this.handleSecurity2NonceReport(node, msg.command);\n\t\t\t}\n\n\t\t\t// Some S2 commands contain only extensions. Those are handled by the CC implementation.\n\t\t\tif (\n\t\t\t\tmsg.command instanceof Security2CCMessageEncapsulation\n\t\t\t\t&& msg.command.encapsulated == undefined\n\t\t\t) {\n\t\t\t\t// possibly reply to a supervised command\n\t\t\t\tawait reply(SupervisionStatus.Success);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Inclusion controller commands are handled by the controller class\n\t\t\tif (msg.command instanceof InclusionControllerCCInitiate) {\n\t\t\t\tconst command = msg.command;\n\t\t\t\tif (\n\t\t\t\t\tmsg.command.step === InclusionControllerStep.ProxyInclusion\n\t\t\t\t) {\n\t\t\t\t\tawait trySupervised(() =>\n\t\t\t\t\t\tthis.controller\n\t\t\t\t\t\t\t.handleInclusionControllerCCInitiateProxyInclusion(\n\t\t\t\t\t\t\t\tcommand,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t} else if (\n\t\t\t\t\tmsg.command.step\n\t\t\t\t\t\t=== InclusionControllerStep.ProxyInclusionReplace\n\t\t\t\t) {\n\t\t\t\t\tawait trySupervised(() =>\n\t\t\t\t\t\tthis.controller\n\t\t\t\t\t\t\t.handleInclusionControllerCCInitiateReplace(\n\t\t\t\t\t\t\t\tcommand,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// No one is waiting, dispatch the command to the node itself\n\t\t\tawait trySupervised(() => node.handleCommand(msg.command));\n\n\t\t\treturn;\n\t\t} else if (msg instanceof ApplicationUpdateRequest) {\n\t\t\t// Make sure we're ready to handle this command\n\t\t\tthis.ensureReady(true);\n\t\t\treturn this.controller.handleApplicationUpdateRequest(msg);\n\t\t} else if (msg instanceof SerialAPIStartedRequest) {\n\t\t\tif (await this.handleSerialAPIStartedUnexpectedly(msg)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\tif (\n\t\t\t\tmsg.functionType >= FunctionType.Proprietary_F0\n\t\t\t\t&& msg.functionType <= FunctionType.Proprietary_FE\n\t\t\t\t&& await this._controller\n\t\t\t\t\t?.handleUnsolictedProprietaryCommand(msg)\n\t\t\t) {\n\t\t\t\t// Proprietary command was handled\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// TODO: This deserves a nicer formatting\n\t\t\tthis.driverLog.print(\n\t\t\t\t`handling request ${\n\t\t\t\t\tFunctionType[msg.functionType]\n\t\t\t\t} (${msg.functionType})`,\n\t\t\t);\n\t\t\thandlers = this.requestHandlers.get(msg.functionType);\n\t\t}\n\n\t\tif (handlers != undefined && handlers.length > 0) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t` ${handlers.length} handler${\n\t\t\t\t\thandlers.length !== 1 ? \"s\" : \"\"\n\t\t\t\t} registered!`,\n\t\t\t);\n\t\t\t// loop through all handlers and find the first one that returns true to indicate that it handled the message\n\t\t\tfor (let i = 0; i < handlers.length; i++) {\n\t\t\t\tthis.driverLog.print(` invoking handler #${i}`);\n\t\t\t\t// Invoke the handler and remember its result\n\t\t\t\tconst handler = handlers[i];\n\t\t\t\tlet handlerResult = handler.invoke(msg);\n\t\t\t\tif (handlerResult instanceof Promise) {\n\t\t\t\t\thandlerResult = await handlerResult;\n\t\t\t\t}\n\t\t\t\tif (handlerResult) {\n\t\t\t\t\tthis.driverLog.print(` the message was handled`);\n\t\t\t\t\tif (handler.oneTime) {\n\t\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\t\" one-time handler was successfully called, removing it...\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\thandlers.splice(i, 1);\n\t\t\t\t\t}\n\t\t\t\t\t// don't invoke any more handlers\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.driverLog.print(\" no handlers registered!\", \"warn\");\n\t\t}\n\t}\n\n\tprivate hasLoggedNoNetworkKey = false;\n\n\tprivate async handleSecurityNonceGet(\n\t\tnode: ZWaveNode,\n\t): Promise<void> {\n\t\t// Only reply if secure communication is set up\n\t\tif (!this.securityManager) {\n\t\t\tif (!this.hasLoggedNoNetworkKey) {\n\t\t\t\tthis.hasLoggedNoNetworkKey = true;\n\t\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`cannot reply to NonceGet because no network key was configured!`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// When a node asks us for a nonce, it must support Security CC\n\t\tnode.addCC(CommandClasses.Security, {\n\t\t\tisSupported: true,\n\t\t\tversion: 1,\n\t\t\t// Security CC is always secure\n\t\t\tsecure: true,\n\t\t});\n\n\t\t// Ensure that we're not flooding the queue with unnecessary NonceReports (GH#1059)\n\t\tconst isNonceReport = (t: Transaction) =>\n\t\t\tt.message.getNodeId() === node.id\n\t\t\t&& containsCC(t.message)\n\t\t\t&& t.message.command instanceof SecurityCCNonceReport;\n\n\t\tif (this.hasPendingTransactions(isNonceReport)) {\n\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t\"in the process of replying to a NonceGet, won't send another NonceReport\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// Delete all previous nonces we sent the node, since they should no longer be used\n\t\tthis.securityManager.deleteAllNoncesForReceiver(node.id);\n\n\t\t// Now send the current nonce\n\t\ttry {\n\t\t\tawait node.commandClasses.Security.sendNonce();\n\t\t} catch (e) {\n\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\tmessage: `failed to send nonce: ${getErrorMessage(e)}`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Is called when a nonce report is received that does not belong to any transaction.\n\t * The received nonce reports are stored as \"free\" nonces\n\t */\n\tprivate handleSecurityNonceReport(\n\t\tnode: ZWaveNode,\n\t\tcommand: SecurityCCNonceReport,\n\t): void {\n\t\tconst secMan = this.securityManager;\n\t\tif (!secMan) return;\n\n\t\tsecMan.setNonce(\n\t\t\t{\n\t\t\t\tissuer: node.id,\n\t\t\t\tnonceId: secMan.getNonceId(command.nonce),\n\t\t\t},\n\t\t\t{\n\t\t\t\tnonce: command.nonce,\n\t\t\t\treceiver: this.controller.ownNodeId!,\n\t\t\t},\n\t\t\t{ free: true },\n\t\t);\n\t}\n\n\tprivate async handleSecurityCommandsSupportedGet(\n\t\tnode: ZWaveNode,\n\t\tcommand: SecurityCCCommandsSupportedGet,\n\t): Promise<void> {\n\t\tconst endpoint = node.getEndpoint(command.endpointIndex) ?? node;\n\n\t\tif (this.getHighestSecurityClass(node.id) === SecurityClass.S0_Legacy) {\n\t\t\tconst { supportedCCs } = determineNIF();\n\t\t\tawait endpoint.commandClasses.Security.reportSupportedCommands(\n\t\t\t\tsupportedCCs,\n\t\t\t\t// We don't report controlled CCs\n\t\t\t\t[],\n\t\t\t);\n\t\t} else {\n\t\t\t// S0 is not the highest class. Return an empty list\n\t\t\tawait endpoint.commandClasses.Security.reportSupportedCommands(\n\t\t\t\t[],\n\t\t\t\t[],\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Handles a nonce request for S2 */\n\tprivate async handleSecurity2NonceGet(\n\t\tnode: ZWaveNode,\n\t): Promise<void> {\n\t\t// Only reply if secure communication is set up\n\t\tif (!this.getSecurityManager2(node.id)) {\n\t\t\tif (!this.hasLoggedNoNetworkKey) {\n\t\t\t\tthis.hasLoggedNoNetworkKey = true;\n\t\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`cannot reply to NonceGet (S2) because no network key was configured!`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// When a node asks us for a nonce, it must support Security 2 CC\n\t\tnode.addCC(CommandClasses[\"Security 2\"], {\n\t\t\tisSupported: true,\n\t\t\tversion: 1,\n\t\t\t// Security 2 CC is always secure\n\t\t\tsecure: true,\n\t\t});\n\n\t\t// Ensure that we're not flooding the queue with unnecessary NonceReports (GH#1059)\n\t\tconst isNonceReport = (t: Transaction) =>\n\t\t\tt.message.getNodeId() === node.id\n\t\t\t&& containsCC(t.message)\n\t\t\t&& t.message.command instanceof Security2CCNonceReport;\n\n\t\tif (this.hasPendingTransactions(isNonceReport)) {\n\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t\"in the process of replying to a NonceGet, won't send another NonceReport\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait node.commandClasses[\"Security 2\"].sendNonce();\n\t\t} catch (e) {\n\t\t\tthis.controllerLog.logNode(node.id, {\n\t\t\t\tmessage: `failed to send nonce: ${getErrorMessage(e)}`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Is called when a nonce report is received that does not belong to any transaction.\n\t */\n\tprivate handleSecurity2NonceReport(\n\t\tnode: ZWaveNode,\n\t\t_command: Security2CCNonceReport,\n\t): void {\n\t\t// const secMan = this.securityManager2;\n\t\t// if (!secMan) return;\n\n\t\t// This has the potential of resetting our SPAN state in the middle of a transaction which may expect it to be valid\n\t\t// So we probably shouldn't react here, and instead handle the NonceReport we'll get in response to the next command we send\n\n\t\t// if (command.SOS && command.receiverEI) {\n\t\t// \t// The node couldn't decrypt the last command we sent it. Invalidate\n\t\t// \t// the shared SPAN, since it did the same\n\t\t// \tsecMan.storeRemoteEI(node.id, command.receiverEI);\n\t\t// }\n\n\t\t// Since we landed here, this is not in response to any command we sent\n\t\tthis.controllerLog.logNode(node.id, {\n\t\t\tmessage:\n\t\t\t\t`received S2 nonce without an active transaction, not sure what to do with it`,\n\t\t\tlevel: \"warn\",\n\t\t\tdirection: \"inbound\",\n\t\t});\n\t}\n\n\tprivate async handleSecurity2CommandsSupportedGet(\n\t\tnode: ZWaveNode,\n\t\tcommand: Security2CCCommandsSupportedGet,\n\t): Promise<void> {\n\t\tconst endpoint = node.getEndpoint(command.endpointIndex) ?? node;\n\n\t\tconst highestSecurityClass = this.getHighestSecurityClass(node.id);\n\t\tconst actualSecurityClass = (\n\t\t\tcommand.getEncapsulatingCC(\n\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\tSecurity2Command.MessageEncapsulation,\n\t\t\t) as Security2CCMessageEncapsulation | undefined\n\t\t)?.securityClass;\n\n\t\tif (\n\t\t\thighestSecurityClass !== undefined\n\t\t\t&& highestSecurityClass === actualSecurityClass\n\t\t) {\n\t\t\t// The command was received using the highest security class. Return the list of supported CCs\n\n\t\t\tconst implementedCCs = allCCs.filter((cc) =>\n\t\t\t\tgetImplementedVersion(cc) > 0\n\t\t\t);\n\n\t\t\t// Encapsulation CCs are always supported\n\t\t\tconst implementedEncapsulationCCs = encapsulationCCs.filter(\n\t\t\t\t(cc) =>\n\t\t\t\t\timplementedCCs.includes(cc)\n\t\t\t\t\t// A node MUST advertise support for Multi Channel Command Class only if it implements End Points.\n\t\t\t\t\t// A node able to communicate using the Multi Channel encapsulation but implementing no End Point\n\t\t\t\t\t// MUST NOT advertise support for the Multi Channel Command Class.\n\t\t\t\t\t// --> We do not implement end points\n\t\t\t\t\t&& cc !== CommandClasses[\"Multi Channel\"],\n\t\t\t);\n\n\t\t\tconst supportedCCs = new Set([\n\t\t\t\t// DT:00.11.0004.1\n\t\t\t\t// All Root Devices or nodes MUST support:\n\t\t\t\t// - Association, version 2\n\t\t\t\t// - Association Group Information\n\t\t\t\t// - Device Reset Locally\n\t\t\t\t// - Firmware Update Meta Data, version 5\n\t\t\t\t// - Indicator, version 3\n\t\t\t\t// - Manufacturer Specific\n\t\t\t\t// - Multi Channel Association, version 3\n\t\t\t\t// - Powerlevel\n\t\t\t\t// - Security 2\n\t\t\t\t// - Supervision\n\t\t\t\t// - Transport Service, version 2\n\t\t\t\t// - Version, version 2\n\t\t\t\t// - Z-Wave Plus Info, version 2\n\t\t\t\tCommandClasses.Association,\n\t\t\t\tCommandClasses[\"Association Group Information\"],\n\t\t\t\tCommandClasses[\"Device Reset Locally\"],\n\t\t\t\tCommandClasses[\"Firmware Update Meta Data\"],\n\t\t\t\tCommandClasses.Indicator,\n\t\t\t\tCommandClasses[\"Manufacturer Specific\"],\n\t\t\t\tCommandClasses[\"Multi Channel Association\"],\n\t\t\t\tCommandClasses.Powerlevel,\n\t\t\t\tCommandClasses.Version,\n\t\t\t\tCommandClasses[\"Z-Wave Plus Info\"],\n\n\t\t\t\t// Generic Controller device type has no additional support requirements,\n\t\t\t\t// but we also support the following command classes:\n\t\t\t\tCommandClasses[\"Inclusion Controller\"],\n\n\t\t\t\t// plus encapsulation CCs, which are part of the above requirement\n\t\t\t\t...implementedEncapsulationCCs.filter(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\t// CC:009F.01.0E.11.00F\n\t\t\t\t\t\t// The Security 0 and Security 2 Command Class MUST NOT be advertised in this command\n\t\t\t\t\t\t// The Transport Service Command Class MUST NOT be advertised in this command.\n\t\t\t\t\t\tcc !== CommandClasses.Security\n\t\t\t\t\t\t&& cc !== CommandClasses[\"Security 2\"]\n\t\t\t\t\t\t&& cc !== CommandClasses[\"Transport Service\"],\n\t\t\t\t),\n\t\t\t]);\n\n\t\t\t// Commands that are always in the NIF should not appear in the\n\t\t\t// S2 commands supported report\n\t\t\tconst commandsInNIF = new Set(determineNIF().supportedCCs);\n\t\t\tconst supportedCommandsNotInNIF = [...supportedCCs].filter((cc) =>\n\t\t\t\t!commandsInNIF.has(cc)\n\t\t\t);\n\n\t\t\tawait endpoint.commandClasses[\"Security 2\"].reportSupportedCommands(\n\t\t\t\tsupportedCommandsNotInNIF,\n\t\t\t);\n\t\t} else if (securityClassIsS2(actualSecurityClass)) {\n\t\t\t// The command was received using a lower security class. Return an empty list\n\t\t\tawait endpoint.commandClasses[\"Security 2\"]\n\t\t\t\t.withOptions({\n\t\t\t\t\ts2OverrideSecurityClass: actualSecurityClass,\n\t\t\t\t})\n\t\t\t\t.reportSupportedCommands([]);\n\t\t} else {\n\t\t\t// Do not respond\n\t\t}\n\t}\n\n\t/**\n\t * Returns the next callback ID. Callback IDs are used to correlate requests\n\t * to the controller/nodes with its response\n\t */\n\tpublic readonly getNextCallbackId = createWrappingCounter(0xff);\n\n\tprivate readonly supervisionSessionIDs = new Map<number, () => number>();\n\t/**\n\t * Returns the next session ID for Supervision CC\n\t */\n\tpublic getNextSupervisionSessionId(nodeId: number): number {\n\t\tif (!this.supervisionSessionIDs.has(nodeId)) {\n\t\t\tthis.supervisionSessionIDs.set(\n\t\t\t\tnodeId,\n\t\t\t\tcreateWrappingCounter(MAX_SUPERVISION_SESSION_ID, true),\n\t\t\t);\n\t\t}\n\t\treturn this.supervisionSessionIDs.get(nodeId)!();\n\t}\n\n\t/**\n\t * Returns the next session ID for Transport Service CC\n\t */\n\tpublic readonly getNextTransportServiceSessionId = createWrappingCounter(\n\t\tMAX_TRANSPORT_SERVICE_SESSION_ID,\n\t\ttrue,\n\t);\n\n\tprivate encapsulateCommands(\n\t\tcmd: CommandClass,\n\t\toptions: Omit<SendCommandOptions, keyof SendMessageOptions> = {},\n\t): CommandClass {\n\t\t// The encapsulation order (from outside to inside) is as follows:\n\t\t// 5. Any one of the following combinations:\n\t\t// a. Security (S0 or S2) followed by transport service\n\t\t// b. Transport Service\n\t\t// c. Security (S0 or S2)\n\t\t// d. CRC16\n\t\t// b and d are mutually exclusive, security is not\n\t\t// 4. Multi Channel\n\t\t// 3. Supervision\n\t\t// 2. Multi Command\n\t\t// 1. Encapsulated Command Class (payload), e.g. Basic Set\n\n\t\t// TODO: 2.\n\n\t\t// 3.\n\t\tif (SupervisionCC.requiresEncapsulation(cmd)) {\n\t\t\tcmd = SupervisionCC.encapsulate(\n\t\t\t\tcmd,\n\t\t\t\tthis.getNextSupervisionSessionId(cmd.nodeId as number),\n\t\t\t);\n\t\t}\n\n\t\t// 4.\n\t\tif (MultiChannelCC.requiresEncapsulation(cmd)) {\n\t\t\tconst multiChannelCCVersion = this.getSupportedCCVersion(\n\t\t\t\tCommandClasses[\"Multi Channel\"],\n\t\t\t\tcmd.nodeId as number,\n\t\t\t);\n\n\t\t\tcmd = multiChannelCCVersion === 1\n\t\t\t\t? MultiChannelCC.encapsulateV1(cmd)\n\t\t\t\t: MultiChannelCC.encapsulate(cmd);\n\t\t}\n\n\t\t// 5.\n\t\tif (CRC16CC.requiresEncapsulation(cmd)) {\n\t\t\tcmd = CRC16CC.encapsulate(cmd);\n\t\t} else {\n\t\t\t// The command must be S2-encapsulated, if ...\n\t\t\tlet maybeS2 = false;\n\t\t\tconst node = cmd.getNode(this);\n\t\t\tif (node?.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\t\t// ... the node supports S2 and has a valid security class\n\t\t\t\tconst nodeSecClass = node.getHighestSecurityClass();\n\t\t\t\tconst securityManager = this.getSecurityManager2(node.id);\n\t\t\t\tmaybeS2 = securityClassIsS2(nodeSecClass)\n\t\t\t\t\t|| !!securityManager?.tempKeys.has(node.id);\n\t\t\t} else if (options.s2MulticastGroupId != undefined) {\n\t\t\t\t// ... or we're dealing with S2 multicast\n\t\t\t\tmaybeS2 = true;\n\t\t\t}\n\t\t\tif (maybeS2 && Security2CC.requiresEncapsulation(cmd)) {\n\t\t\t\tcmd = Security2CC.encapsulate(\n\t\t\t\t\tcmd,\n\t\t\t\t\tthis.ownNodeId,\n\t\t\t\t\tthis,\n\t\t\t\t\t{\n\t\t\t\t\t\tsecurityClass: options.s2OverrideSecurityClass,\n\t\t\t\t\t\tmulticastOutOfSync: !!options.s2MulticastOutOfSync,\n\t\t\t\t\t\tmulticastGroupId: options.s2MulticastGroupId,\n\t\t\t\t\t\tverifyDelivery: options.s2VerifyDelivery,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// This check will return false for S2-encapsulated commands\n\t\t\tif (SecurityCC.requiresEncapsulation(cmd)) {\n\t\t\t\tcmd = SecurityCC.encapsulate(\n\t\t\t\t\tthis.ownNodeId,\n\t\t\t\t\tthis.securityManager!,\n\t\t\t\t\tcmd,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn cmd;\n\t}\n\n\tpublic unwrapCommands(msg: Message & ContainsCC): void {\n\t\t// Unwrap encapsulating CCs until we get to the core\n\t\twhile (\n\t\t\tisEncapsulatingCommandClass(msg.command)\n\t\t\t|| isMultiEncapsulatingCommandClass(msg.command)\n\t\t) {\n\t\t\tconst unwrapped = msg.command.encapsulated;\n\t\t\tif (isArray(unwrapped)) {\n\t\t\t\t// Multi Command CC cannot be further unwrapped. Preserve the encapsulation flags though.\n\t\t\t\tfor (const cmd of unwrapped) {\n\t\t\t\t\tcmd.toggleEncapsulationFlag(\n\t\t\t\t\t\tmsg.command.encapsulationFlags,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Copy the encapsulation flags and add the current encapsulation\n\t\t\tunwrapped.encapsulationFlags = msg.command.encapsulationFlags;\n\t\t\tswitch (msg.command.ccId) {\n\t\t\t\tcase CommandClasses.Supervision:\n\t\t\t\t\tunwrapped.toggleEncapsulationFlag(\n\t\t\t\t\t\tEncapsulationFlags.Supervision,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase CommandClasses[\"Security 2\"]:\n\t\t\t\tcase CommandClasses.Security:\n\t\t\t\t\tunwrapped.toggleEncapsulationFlag(\n\t\t\t\t\t\tEncapsulationFlags.Security,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase CommandClasses[\"CRC-16 Encapsulation\"]:\n\t\t\t\t\tunwrapped.toggleEncapsulationFlag(\n\t\t\t\t\t\tEncapsulationFlags.CRC16,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tmsg.command = unwrapped;\n\t\t}\n\t}\n\n\tprivate shouldPersistCCValues(cc: CommandClass): boolean {\n\t\t// Always persist encapsulation CCs, otherwise interviews don't work.\n\t\tif (isEncapsulationCC(cc.ccId)) return true;\n\n\t\t// Do not persist values for a node or endpoint that does not exist\n\t\tconst endpoint = this.tryGetEndpoint(cc);\n\t\tconst node = endpoint?.tryGetNode();\n\t\tif (!node) return false;\n\n\t\t// Do not persist values for a CC that was force-removed via config\n\t\tif (endpoint?.wasCCRemovedViaConfig(cc.ccId)) return false;\n\n\t\t// Do not persist values for a CC that's being mapped to another endpoint.\n\t\t// FIXME: This duplicates logic in Node.ts -> handleCommand\n\t\tconst compatConfig = node?.deviceConfig?.compat;\n\t\tif (\n\t\t\tcc.endpointIndex === 0\n\t\t\t&& cc.constructor.name.endsWith(\"Report\")\n\t\t\t&& node.getEndpointCount() >= 1\n\t\t\t// Only map reports from the root device to an endpoint if we know which one\n\t\t\t&& compatConfig?.mapRootReportsToEndpoint != undefined\n\t\t) {\n\t\t\tconst targetEndpoint = node.getEndpoint(\n\t\t\t\tcompatConfig.mapRootReportsToEndpoint,\n\t\t\t);\n\t\t\tif (targetEndpoint?.supportsCC(cc.ccId)) return false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/** Persists the values contained in a Command Class in the corresponding nodes's value DB */\n\tprivate persistCCValues(cc: CommandClass) {\n\t\tif (this.shouldPersistCCValues(cc)) {\n\t\t\tcc.persistValues(this);\n\t\t}\n\n\t\tif (isEncapsulatingCommandClass(cc)) {\n\t\t\tthis.persistCCValues(cc.encapsulated);\n\t\t} else if (isMultiEncapsulatingCommandClass(cc)) {\n\t\t\tfor (const encapsulated of cc.encapsulated) {\n\t\t\t\tthis.persistCCValues(encapsulated);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Gets called whenever any Serial API command succeeded or a SendData command had a negative callback.\n\t */\n\tprivate handleSerialAPICommandResult(\n\t\tmsg: Message,\n\t\toptions: SendMessageOptions,\n\t\tresult: Message | undefined,\n\t): void {\n\t\t// Update statistics\n\t\tconst node = this.tryGetNode(msg);\n\t\tlet success = true;\n\t\tif (isSendData(msg) || hasNodeId(msg)) {\n\t\t\t// This shouldn't happen, but just in case\n\t\t\tif (!node) return;\n\n\t\t\t// If this is a transmit report, use it to update statistics\n\t\t\tif (isTransmitReport(result)) {\n\t\t\t\tif (!result.isOK()) {\n\t\t\t\t\tsuccess = false;\n\t\t\t\t\tnode.incrementStatistics(\"commandsDroppedTX\");\n\t\t\t\t} else {\n\t\t\t\t\tnode.incrementStatistics(\"commandsTX\");\n\t\t\t\t\tnode.updateRTT(msg);\n\t\t\t\t\t// Update last seen state\n\t\t\t\t\tnode.lastSeen = new Date();\n\t\t\t\t}\n\n\t\t\t\t// Notify listeners about the status report if one was received\n\t\t\t\tif (hasTXReport(result)) {\n\t\t\t\t\toptions.onTXReport?.(result.txReport);\n\t\t\t\t\tnode.updateRouteStatistics(result.txReport);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Track and potentially update the status of the node when communication succeeds\n\t\t\tif (success) {\n\t\t\t\tif (node.canSleep) {\n\t\t\t\t\t// Do not update the node status when we only responded to a nonce/supervision request\n\t\t\t\t\tif (options.priority !== MessagePriority.Immediate) {\n\t\t\t\t\t\t// If the node is not meant to be kept awake, try to send it back to sleep\n\t\t\t\t\t\tif (!node.keepAwake) {\n\t\t\t\t\t\t\tsetImmediate(() =>\n\t\t\t\t\t\t\t\tthis.debounceSendNodeToSleep(node)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// The node must be awake because it answered\n\t\t\t\t\t\tnode.markAsAwake();\n\t\t\t\t\t}\n\t\t\t\t} else if (node.status !== NodeStatus.Alive) {\n\t\t\t\t\t// The node status was unknown or dead - in either case it must be alive because it answered\n\t\t\t\t\tnode.markAsAlive();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis._controller?.incrementStatistics(\"messagesTX\");\n\t\t}\n\t}\n\n\tprivate shouldUseLowPriorityForSupervisionReport(\n\t\ttargetNode: ZWaveNode,\n\t\tencapsulationFlags: EncapsulationFlags,\n\t): boolean {\n\t\t// To avoid S2 collisions, we reduce the priority of Supervision reports\n\t\t// when they are S2-encapsulated, and another S2-encapsulated transaction is in\n\t\t// progress for the same node\n\n\t\t// Use Immediate priority if there is no other transaction for this node in progress\n\t\tconst currentNormalMsg = this.queue.currentTransaction?.message;\n\t\tif (currentNormalMsg?.getNodeId() !== targetNode.id) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!containsCC(currentNormalMsg)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Use Immediate priority if the node isn't using Security S2\n\t\tif (!securityClassIsS2(targetNode.getHighestSecurityClass())) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Use Immediate priority unless both messages are S2-encapsulated\n\t\tconst currentMsgIsSecure = currentNormalMsg.command\n\t\t\tinstanceof Security2CCMessageEncapsulation;\n\t\tconst reportIsSecure = !!(\n\t\t\tencapsulationFlags & EncapsulationFlags.Security\n\t\t);\n\t\tif (!currentMsgIsSecure || !reportIsSecure) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// This has potential for a conflict, use low priority\n\t\tthis.controllerLog.logNode(targetNode.id, {\n\t\t\tmessage:\n\t\t\t\t\"S2 collision detected, reducing priority for Supervision report\",\n\t\t\tlevel: \"debug\",\n\t\t});\n\t\treturn true;\n\t}\n\n\tprivate mayStartTransaction(transaction: Transaction): boolean {\n\t\t// We may not send anything on the normal queue if the send thread is paused\n\t\t// or the controller is unresponsive\n\t\tif (\n\t\t\tthis.queuePaused\n\t\t\t|| this.controller.status === ControllerStatus.Unresponsive\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst message = transaction.message;\n\t\tconst targetNode = message.tryGetNode(this);\n\n\t\t// Messages to the controller may always be sent...\n\t\tif (!targetNode) return true;\n\n\t\t// The transaction queue is sorted automatically. If the first message is for a sleeping node, all messages in the queue are.\n\t\t// There are a few exceptions:\n\t\t// 1. Pings may be used to determine whether a node is really asleep.\n\t\t// 2. Responses to nonce requests must be sent independent of the node status, because some sleeping nodes may try to send us encrypted messages.\n\t\t// If we don't send them, they block the send queue\n\t\t// 3. Nodes that can sleep but do not support wakeup: https://github.com/zwave-js/zwave-js/discussions/1537\n\t\t// We need to try and send messages to them even if they are asleep, because we might never hear from them\n\n\t\t// 2. is handled by putting the message into the immediate queue\n\n\t\t// Pings may always be sent\n\t\tif (messageIsPing(message)) return true;\n\n\t\treturn (\n\t\t\ttargetNode.status !== NodeStatus.Asleep\n\t\t\t|| (!targetNode.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t\t\t&& targetNode.interviewStage >= InterviewStage.NodeInfo)\n\t\t);\n\t}\n\n\tprivate markQueueBusy(queue: TransactionQueue, busy: boolean): void {\n\t\tconst index = this.queues.indexOf(queue);\n\t\tif (busy) {\n\t\t\tthis._queuesBusyFlags |= 1 << index;\n\t\t} else {\n\t\t\tthis._queuesBusyFlags &= ~(1 << index);\n\t\t}\n\t\tthis.queueIdle = this._queuesBusyFlags === 0;\n\t}\n\n\tprivate async drainTransactionQueue(\n\t\tqueue: TransactionQueue,\n\t): Promise<void> {\n\t\tlet setIdleTimer: NodeJS.Immediate | undefined;\n\t\tfor await (const transaction of queue) {\n\t\t\tif (setIdleTimer) {\n\t\t\t\tclearImmediate(setIdleTimer);\n\t\t\t\tsetIdleTimer = undefined;\n\t\t\t}\n\t\t\tthis.markQueueBusy(queue, true);\n\n\t\t\tlet error: ZWaveError | undefined;\n\t\t\ttry {\n\t\t\t\tawait this.executeTransaction(transaction);\n\t\t\t} catch (e) {\n\t\t\t\terror = e as ZWaveError;\n\t\t\t} finally {\n\t\t\t\tqueue.finalizeTransaction();\n\t\t\t}\n\n\t\t\t// Handle errors after clearing the current transaction.\n\t\t\t// Otherwise, it will get considered the active transaction and cause an unnecessary SendDataAbort\n\t\t\tif (error) {\n\t\t\t\tthis.handleFailedTransaction(transaction, error);\n\t\t\t}\n\n\t\t\tsetIdleTimer = setImmediate(() => {\n\t\t\t\tthis.markQueueBusy(queue, false);\n\t\t\t});\n\t\t}\n\t}\n\n\t/** Steps through the message generator of a transaction. Throws an error if the transaction should fail. */\n\tprivate async executeTransaction(transaction: Transaction): Promise<void> {\n\t\tlet prevResult: Message | undefined;\n\t\tlet msg: Message | undefined;\n\n\t\ttransaction.start();\n\t\ttransaction.setProgress({ state: TransactionState.Active });\n\n\t\tconst maxJammedAttempts =\n\t\t\tthis._recoveryPhase === ControllerRecoveryPhase.JammedAfterReset\n\t\t\t\t// After attempting soft-reset, only try sending once\n\t\t\t\t? 1\n\t\t\t\t: this.options.attempts.sendDataJammed;\n\n\t\t// Step through the transaction as long as it gives us a next message\n\t\twhile ((msg = await transaction.generateNextMessage(prevResult))) {\n\t\t\t// Keep track of how often the controller failed to send a command, to prevent ending up in an infinite loop\n\t\t\tlet jammedAttempts = 0;\n\t\t\tlet queueAttempts = 0;\n\t\t\tattemptMessage: for (let attemptNumber = 1;; attemptNumber++) {\n\t\t\t\ttry {\n\t\t\t\t\tprevResult = await this.queueSerialAPICommand(\n\t\t\t\t\t\tmsg,\n\t\t\t\t\t\ttransaction.stack,\n\t\t\t\t\t);\n\t\t\t\t\tif (isTransmitReport(prevResult)) {\n\t\t\t\t\t\t// Figure out if the controller is jammed. If it is, wait a second and try again.\n\t\t\t\t\t\t// https://github.com/zwave-js/zwave-js/issues/6199\n\t\t\t\t\t\t// In some cases, the transmit status can be Fail, even after transmitting for a couple of seconds.\n\t\t\t\t\t\t// Not sure what causes this, but it doesn't mean that the controller is jammed.\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tprevResult.transmitStatus === TransmitStatus.Fail\n\t\t\t\t\t\t\t&& \"txReport\" in prevResult\n\t\t\t\t\t\t\t// Ensure the controller didn't actually transmit\n\t\t\t\t\t\t\t&& prevResult.txReport?.txTicks === 0\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tjammedAttempts++;\n\t\t\t\t\t\t\tif (jammedAttempts < maxJammedAttempts) {\n\t\t\t\t\t\t\t\t// The controller is jammed. Wait a bit, then try again.\n\t\t\t\t\t\t\t\tthis.controller.setStatus(\n\t\t\t\t\t\t\t\t\tControllerStatus.Jammed,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tawait wait(\n\t\t\t\t\t\t\t\t\tthis.options.timeouts.retryJammed,\n\t\t\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tcontinue attemptMessage;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Reject the transaction so we can attempt to recover\n\t\t\t\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\t\t\t`Failed to send the command after ${jammedAttempts} attempts`,\n\t\t\t\t\t\t\t\t\tZWaveErrorCodes.Controller_MessageDropped,\n\t\t\t\t\t\t\t\t\tprevResult,\n\t\t\t\t\t\t\t\t\ttransaction.stack,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tthis.controller.status === ControllerStatus.Jammed\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// A command could be sent, so the controller is no longer jammed\n\t\t\t\t\t\t\tthis.controller.setStatus(ControllerStatus.Ready);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!prevResult.isOK()) {\n\t\t\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\t\t\"The node did not acknowledge the command\",\n\t\t\t\t\t\t\t\tZWaveErrorCodes.Controller_CallbackNOK,\n\t\t\t\t\t\t\t\tprevResult,\n\t\t\t\t\t\t\t\ttransaction.stack,\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\t// We got a result - it will be passed to the next iteration\n\t\t\t\t\tbreak attemptMessage;\n\t\t\t\t} catch (e: any) {\n\t\t\t\t\tlet zwError: ZWaveError;\n\t\t\t\t\tlet waitDurationMs = 0;\n\n\t\t\t\t\tif (!isZWaveError(e)) {\n\t\t\t\t\t\tzwError = createMessageDroppedUnexpectedError(e);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tisSendData(msg) && isMissingControllerCallback(e)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// The controller is unresponsive. Reject the transaction, so we can attempt to recover\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t} else if (isMissingControllerACK(e)) {\n\t\t\t\t\t\t\t// The controller is unresponsive. Reject the transaction, so we can attempt to recover\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t} else if (wasControllerReset(e)) {\n\t\t\t\t\t\t\t// The controller was reset unexpectedly. Reject the transaction, so we can attempt to recover\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\tisAnySendDataResponse(e.context)\n\t\t\t\t\t\t\t&& !e.context.wasSent\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// If a SendData command could not be queued, try again after a short delay\n\t\t\t\t\t\t\tqueueAttempts++;\n\t\t\t\t\t\t\tif (queueAttempts < 3) {\n\t\t\t\t\t\t\t\twaitDurationMs = 500;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\te.code === ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// We gave up on this command, so don't retry it\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tthis.mayRetrySerialAPICommand(\n\t\t\t\t\t\t\t\tmsg,\n\t\t\t\t\t\t\t\t// Ignore the number of attempts while jammed or where queuing failed\n\t\t\t\t\t\t\t\tattemptNumber - jammedAttempts - queueAttempts,\n\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tif (waitDurationMs) {\n\t\t\t\t\t\t\t\tawait wait(waitDurationMs, true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Retry the command\n\t\t\t\t\t\t\tcontinue attemptMessage;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tzwError = e;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Sending the command failed, reject the transaction\n\t\t\t\t\tthrow zwError;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// This transaction completed successfully, try the next one\n\t\ttransaction.setProgress({ state: TransactionState.Completed });\n\t}\n\n\t/**\n\t * Provides access to the result Promise for the currently executed serial API command\n\t */\n\tprivate _currentSerialAPICommandPromise:\n\t\t| DeferredPromise<Message | undefined>\n\t\t| undefined;\n\n\t/** Handles sequencing of queued Serial API commands */\n\tprivate async drainSerialAPIQueue(): Promise<void> {\n\t\tfor await (const item of this.serialAPIQueue) {\n\t\t\tconst { msg, transactionSource, result } = item;\n\t\t\tthis._currentSerialAPICommandPromise = result;\n\n\t\t\t// Attempt the command multiple times if necessary\n\t\t\tattempts: for (let attempt = 1;; attempt++) {\n\t\t\t\ttry {\n\t\t\t\t\tconst ret = await this.executeSerialAPICommand(\n\t\t\t\t\t\tmsg,\n\t\t\t\t\t\ttransactionSource,\n\t\t\t\t\t);\n\t\t\t\t\tresult.resolve(ret);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tisZWaveError(e)\n\t\t\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_MessageDropped\n\t\t\t\t\t\t&& e.context === \"CAN\"\n\t\t\t\t\t\t&& attempt < 3\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Retry up to 3 times if there are serial collisions\n\t\t\t\t\t\tawait wait(100);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// In all other cases, reject the transaction\n\t\t\t\t\tresult.reject(e as Error);\n\t\t\t\t} finally {\n\t\t\t\t\tthis._currentSerialAPICommandPromise = undefined;\n\t\t\t\t}\n\t\t\t\tbreak attempts;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate triggerQueues(): void {\n\t\tfor (const queue of this.queues) {\n\t\t\tqueue.trigger();\n\t\t}\n\t}\n\n\t/** Puts a message on the serial API queue and returns or throws the command result */\n\tprivate queueSerialAPICommand(\n\t\tmsg: Message,\n\t\ttransactionSource?: string,\n\t): Promise<Message | undefined> {\n\t\tconst result = createDeferredPromise<Message | undefined>();\n\t\tthis.serialAPIQueue.add({\n\t\t\tmsg,\n\t\t\ttransactionSource,\n\t\t\tresult,\n\t\t\t[Symbol.dispose]: () => {\n\t\t\t\t// Is called when the queue is aborted\n\t\t\t\tresult.reject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\"The message has been removed from the queue\",\n\t\t\t\t\t\tZWaveErrorCodes.Controller_MessageDropped,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\ttransactionSource,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t},\n\t\t});\n\n\t\treturn result;\n\t}\n\n\tprivate mayRetrySerialAPICommand(\n\t\tmsg: Message,\n\t\tattemptNumber: number,\n\t\terror: ZWaveError,\n\t): boolean {\n\t\t// Only retry Send Data, nothing else\n\t\tif (!isSendData(msg)) return false;\n\n\t\t// Don't try to resend SendData commands when the response times out\n\t\tif (\n\t\t\terror.code === ZWaveErrorCodes.Controller_Timeout\n\t\t\t&& error.context === \"response\"\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Don't try to resend multicast messages if they were already transmitted.\n\t\t// One or more nodes might have already reacted\n\t\tif (\n\t\t\t(msg instanceof SendDataMulticastRequest\n\t\t\t\t|| msg instanceof SendDataMulticastBridgeRequest)\n\t\t\t&& error.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn attemptNumber < msg.maxSendAttempts;\n\t}\n\n\t/**\n\t * Executes a Serial API command and returns or throws the result.\n\t * This method should not be called outside of {@link drainSerialAPIQueue}.\n\t */\n\tprivate async executeSerialAPICommand(\n\t\tmsg: Message,\n\t\ttransactionSource?: string,\n\t): Promise<Message | undefined> {\n\t\t// Give the command a callback ID if it needs one\n\t\tif (msg.needsCallbackId() && !msg.hasCallbackId()) {\n\t\t\tmsg.callbackId = this.getNextCallbackId();\n\t\t}\n\n\t\t// Work around an issue in the 700 series firmware where the ACK after a soft-reset has a random high nibble.\n\t\t// This was broken in 7.19, not fixed so far\n\t\tif (\n\t\t\tmsg.functionType === FunctionType.SoftReset\n\t\t\t&& this.controller.sdkVersionGte(\"7.19.0\")\n\t\t) {\n\t\t\tthis.serial?.ignoreAckHighNibbleOnce();\n\t\t}\n\n\t\tconst machine = createSerialAPICommandMachine(msg);\n\t\tthis.abortSerialAPICommand = createDeferredPromise();\n\t\tconst abortController = new AbortController();\n\n\t\tlet nextInput: SerialAPICommandMachineInput | undefined = {\n\t\t\tvalue: \"start\",\n\t\t};\n\n\t\ttry {\n\t\t\twhile (!machine.done) {\n\t\t\t\tif (nextInput == undefined) {\n\t\t\t\t\t// We should not be in a situation where we have no input for the state machine\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Serial API Command machine is in an invalid state: no input provided\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst transition = machine.next(nextInput);\n\t\t\t\tif (transition == undefined) {\n\t\t\t\t\t// We should not be in a situation where the state machine does not transition\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Serial API Command machine is in an invalid state: no transition taken\",\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// The input was used\n\t\t\t\tnextInput = undefined;\n\n\t\t\t\t// Transition to the new state\n\t\t\t\tmachine.transition(transition.newState);\n\n\t\t\t\t// Now check what needs to be done in the new state\n\t\t\t\tswitch (machine.state.value) {\n\t\t\t\t\tcase \"initial\":\n\t\t\t\t\t\t// This should never happen\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\"Serial API Command machine is in an invalid state: transitioned to initial state\",\n\t\t\t\t\t\t);\n\n\t\t\t\t\tcase \"sending\": {\n\t\t\t\t\t\tthis.driverLog.logMessage(msg, {\n\t\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Mark the message as sent immediately before actually sending\n\t\t\t\t\t\tmsg.markAsSent();\n\t\t\t\t\t\tconst data = await msg.serialize(\n\t\t\t\t\t\t\tthis.getEncodingContext(),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait this.writeSerial(data);\n\t\t\t\t\t\tnextInput = { value: \"message sent\" };\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"waitingForACK\": {\n\t\t\t\t\t\tconst controlFlow = await this.waitForMessageHeader(\n\t\t\t\t\t\t\t() => true,\n\t\t\t\t\t\t\tthis.options.timeouts.ack,\n\t\t\t\t\t\t).catch(() => \"timeout\" as const);\n\n\t\t\t\t\t\tif (controlFlow === \"timeout\") {\n\t\t\t\t\t\t\tnextInput = { value: \"timeout\" };\n\t\t\t\t\t\t} else if (controlFlow === MessageHeaders.ACK) {\n\t\t\t\t\t\t\tnextInput = { value: \"ACK\" };\n\t\t\t\t\t\t} else if (controlFlow === MessageHeaders.CAN) {\n\t\t\t\t\t\t\tnextInput = { value: \"CAN\" };\n\t\t\t\t\t\t} else if (controlFlow === MessageHeaders.NAK) {\n\t\t\t\t\t\t\tnextInput = { value: \"NAK\" };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"waitingForResponse\": {\n\t\t\t\t\t\tconst response = await Promise.race([\n\t\t\t\t\t\t\tthis.abortSerialAPICommand?.catch((e) =>\n\t\t\t\t\t\t\t\te as Error\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tthis.waitForMessage(\n\t\t\t\t\t\t\t\t(resp) => msg.isExpectedResponse(resp),\n\t\t\t\t\t\t\t\tmsg.getResponseTimeout()\n\t\t\t\t\t\t\t\t\t?? this.options.timeouts.response,\n\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\tabortController.signal,\n\t\t\t\t\t\t\t).catch(() => \"timeout\" as const),\n\t\t\t\t\t\t]);\n\n\t\t\t\t\t\tif (response instanceof Error) {\n\t\t\t\t\t\t\t// The command was aborted from the outside\n\t\t\t\t\t\t\t// Remove the pending wait entry\n\t\t\t\t\t\t\tabortController.abort();\n\t\t\t\t\t\t\tthrow response;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (response === \"timeout\") {\n\t\t\t\t\t\t\tif (isSendData(msg)) {\n\t\t\t\t\t\t\t\t// Timed out SendData commands must be aborted\n\t\t\t\t\t\t\t\tvoid this.abortSendData();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tnextInput = { value: \"timeout\" };\n\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\tisSuccessIndicator(response) && !response.isOK()\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tnextInput = { value: \"response NOK\", response };\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextInput = { value: \"response\", response };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"waitingForCallback\": {\n\t\t\t\t\t\tlet sendDataAbortTimeout: Timer | undefined;\n\t\t\t\t\t\tif (isSendData(msg)) {\n\t\t\t\t\t\t\t// We abort timed out SendData commands before the callback times out\n\t\t\t\t\t\t\tsendDataAbortTimeout = setTimer(() => {\n\t\t\t\t\t\t\t\tvoid this.abortSendData();\n\t\t\t\t\t\t\t}, this.options.timeouts.sendDataAbort).unref();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst callback = await Promise.race([\n\t\t\t\t\t\t\tthis.abortSerialAPICommand?.catch((e) =>\n\t\t\t\t\t\t\t\te as Error\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tthis.waitForMessage(\n\t\t\t\t\t\t\t\t(resp) => msg.isExpectedCallback(resp),\n\t\t\t\t\t\t\t\tmsg.getCallbackTimeout()\n\t\t\t\t\t\t\t\t\t?? this.options.timeouts.sendDataCallback,\n\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\tabortController.signal,\n\t\t\t\t\t\t\t).catch(() => \"timeout\" as const),\n\t\t\t\t\t\t]);\n\n\t\t\t\t\t\tsendDataAbortTimeout?.clear();\n\n\t\t\t\t\t\tif (callback instanceof Error) {\n\t\t\t\t\t\t\t// The command was aborted from the outside\n\t\t\t\t\t\t\t// Remove the pending wait entry\n\t\t\t\t\t\t\tabortController.abort();\n\t\t\t\t\t\t\tthrow callback;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (callback === \"timeout\") {\n\t\t\t\t\t\t\tnextInput = { value: \"timeout\" };\n\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\tisSuccessIndicator(callback) && !callback.isOK()\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tnextInput = { value: \"callback NOK\", callback };\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextInput = { value: \"callback\", callback };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"success\": {\n\t\t\t\t\t\treturn machine.state.result;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"failure\": {\n\t\t\t\t\t\tconst { reason, result } = machine.state;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\treason === \"callback NOK\"\n\t\t\t\t\t\t\t&& (isSendData(msg) || isTransmitReport(result))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// For messages that were sent to a node, a NOK callback still contains useful info we need to evaluate\n\t\t\t\t\t\t\t// ... so we treat it as a result\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow serialAPICommandErrorToZWaveError(\n\t\t\t\t\t\t\t\treason,\n\t\t\t\t\t\t\t\tmsg,\n\t\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\t\ttransactionSource,\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\t\t\t}\n\t\t} finally {\n\t\t\tthis.abortSerialAPICommand = undefined;\n\t\t}\n\t}\n\n\tprivate getQueueForTransaction(t: Transaction): TransactionQueue {\n\t\tif (\n\t\t\tt.priority === MessagePriority.Immediate\n\t\t\t|| t.priority === MessagePriority.ControllerImmediate\n\t\t) {\n\t\t\treturn this.immediateQueue;\n\t\t} else {\n\t\t\treturn this.queue;\n\t\t}\n\t}\n\n\t/**\n\t * Sends a message to the Z-Wave stick.\n\t * @param msg The message to send\n\t * @param options (optional) Options regarding the message transmission\n\t */\n\tpublic async sendMessage<TResponse extends Message = Message>(\n\t\tmsg: Message,\n\t\toptions: SendMessageOptions = {},\n\t): Promise<TResponse> {\n\t\tthis.ensureReady();\n\n\t\tlet node: ZWaveNode | undefined;\n\t\tif (hasNodeId(msg) || containsCC(msg)) {\n\t\t\tnode = this.tryGetNode(msg);\n\t\t}\n\n\t\tif (options.priority == undefined) {\n\t\t\toptions.priority = getDefaultPriority(msg);\n\t\t}\n\t\tif (options.priority == undefined) {\n\t\t\tconst className = msg.constructor.name;\n\t\t\tconst msgTypeName = FunctionType[msg.functionType];\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`No default priority has been defined for ${className} (${msgTypeName}), so you have to provide one for your message`,\n\t\t\t\tZWaveErrorCodes.Driver_NoPriority,\n\t\t\t);\n\t\t}\n\n\t\tif (options.supportCheck == undefined) options.supportCheck = true;\n\t\tif (\n\t\t\toptions.supportCheck\n\t\t\t&& this._controller != undefined\n\t\t\t&& !this._controller.isFunctionSupported(msg.functionType)\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Your hardware does not support the ${\n\t\t\t\t\tFunctionType[msg.functionType]\n\t\t\t\t} function`,\n\t\t\t\tZWaveErrorCodes.Driver_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\t// When sending a message to a node that is known to be sleeping,\n\t\t// the priority must be WakeUp, so the message gets deprioritized\n\t\t// in comparison with messages to awake nodes.\n\t\t// However there are a few exceptions...\n\t\tif (\n\t\t\t!!node\n\t\t\t// Pings can be used to check if a node is really asleep, so they should be sent regardless\n\t\t\t&& !messageIsPing(msg)\n\t\t\t// Nodes that can sleep and support the Wake Up CC should have their messages queued for wakeup\n\t\t\t&& node.canSleep\n\t\t\t&& (node.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t\t\t// If we don't know the Wake Up support yet, also change the priority or RequestNodeInfoRequests will get stuck\n\t\t\t\t// in front of the queue\n\t\t\t\t|| node.interviewStage < InterviewStage.NodeInfo)\n\t\t\t// If we move multicasts to the wakeup queue, it is unlikely\n\t\t\t// that there is ever a points where all targets are awake\n\t\t\t&& !(msg instanceof SendDataMulticastRequest)\n\t\t\t&& !(msg instanceof SendDataMulticastBridgeRequest)\n\t\t\t// Nonces and responses to Supervision Get have to be sent immediately\n\t\t\t&& options.priority !== MessagePriority.Immediate\n\t\t) {\n\t\t\tif (options.priority === MessagePriority.NodeQuery) {\n\t\t\t\t// Remember that this transaction was part of an interview\n\t\t\t\toptions.tag = \"interview\";\n\t\t\t}\n\t\t\toptions.priority = MessagePriority.WakeUp;\n\t\t}\n\n\t\t// Create the transaction\n\t\tconst { generator, resultPromise } = createMessageGenerator(\n\t\t\tthis,\n\t\t\tthis.getEncodingContext(),\n\t\t\tmsg,\n\t\t\t(msg, _result) => {\n\t\t\t\tthis.handleSerialAPICommandResult(msg, options, _result);\n\t\t\t},\n\t\t);\n\t\tconst transaction = new Transaction(this, {\n\t\t\tmessage: msg,\n\t\t\tpriority: options.priority,\n\t\t\tparts: generator,\n\t\t\tpromise: resultPromise,\n\t\t\tlistener: options.onProgress,\n\t\t});\n\n\t\t// Configure its options\n\t\tif (options.changeNodeStatusOnMissingACK != undefined) {\n\t\t\ttransaction.changeNodeStatusOnTimeout =\n\t\t\t\toptions.changeNodeStatusOnMissingACK;\n\t\t}\n\t\tif (options.pauseSendThread === true) {\n\t\t\ttransaction.pauseSendThread = true;\n\t\t}\n\t\ttransaction.requestWakeUpOnDemand = !!options.requestWakeUpOnDemand;\n\t\ttransaction.tag = options.tag;\n\n\t\t// And queue it\n\t\tthis.getQueueForTransaction(transaction).add(transaction);\n\t\ttransaction.setProgress({ state: TransactionState.Queued });\n\n\t\t// If the transaction should expire, start the timeout\n\t\tlet expirationTimeout: Timer | undefined;\n\t\tif (options.expire) {\n\t\t\texpirationTimeout = setTimer(() => {\n\t\t\t\tvoid this.reduceQueues((t, _source) => {\n\t\t\t\t\tif (t === transaction) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttype: \"reject\",\n\t\t\t\t\t\t\tmessage: `The message has expired`,\n\t\t\t\t\t\t\tcode: ZWaveErrorCodes.Controller_MessageExpired,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\treturn { type: \"keep\" };\n\t\t\t\t});\n\t\t\t}, options.expire).unref();\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = (await resultPromise) as TResponse;\n\n\t\t\t// If this was a successful non-nonce message to a sleeping node, make sure it goes to sleep again\n\t\t\tlet maybeSendToSleep: boolean;\n\t\t\tif (isSendData(msg)) {\n\t\t\t\t// For SendData messages, make sure the message is not a nonce\n\t\t\t\tmaybeSendToSleep =\n\t\t\t\t\toptions.priority !== MessagePriority.Immediate\n\t\t\t\t\t// And that the result is either a response from the node\n\t\t\t\t\t// or a transmit report indicating success\n\t\t\t\t\t&& result\n\t\t\t\t\t&& (result.functionType\n\t\t\t\t\t\t\t=== FunctionType.BridgeApplicationCommand\n\t\t\t\t\t\t|| result.functionType\n\t\t\t\t\t\t\t=== FunctionType.ApplicationCommand\n\t\t\t\t\t\t|| (isSendDataTransmitReport(result) && result.isOK()));\n\t\t\t} else {\n\t\t\t\t// For other messages to the node, just check for successful completion. If the callback is not OK,\n\t\t\t\t// we might not be able to communicate with the node. Sending another message is not a good idea.\n\t\t\t\tmaybeSendToSleep = hasNodeId(msg)\n\t\t\t\t\t&& result\n\t\t\t\t\t&& isSuccessIndicator(result)\n\t\t\t\t\t&& result.isOK();\n\t\t\t}\n\n\t\t\tif (maybeSendToSleep && node && node.canSleep && !node.keepAwake) {\n\t\t\t\tsetImmediate(() => this.debounceSendNodeToSleep(node));\n\t\t\t}\n\n\t\t\t// Set the transaction progress to completed before resolving the Promise\n\t\t\ttransaction.setProgress({ state: TransactionState.Completed });\n\n\t\t\treturn result;\n\t\t} catch (e) {\n\t\t\tif (isZWaveError(e)) {\n\t\t\t\tif (\n\t\t\t\t\t// If a controller command failed (that is not SendData), pass the response/callback through\n\t\t\t\t\t(e.code === ZWaveErrorCodes.Controller_ResponseNOK\n\t\t\t\t\t\t|| e.code === ZWaveErrorCodes.Controller_CallbackNOK)\n\t\t\t\t\t&& e.context instanceof Message\n\t\t\t\t\t// We need to check the function type here because context can be the transmit reports\n\t\t\t\t\t&& e.context.functionType !== FunctionType.SendData\n\t\t\t\t\t&& e.context.functionType !== FunctionType.SendDataMulticast\n\t\t\t\t\t&& e.context.functionType !== FunctionType.SendDataBridge\n\t\t\t\t\t&& e.context.functionType\n\t\t\t\t\t\t!== FunctionType.SendDataMulticastBridge\n\t\t\t\t) {\n\t\t\t\t\tthis._controller?.incrementStatistics(\"messagesDroppedTX\");\n\t\t\t\t\treturn e.context as TResponse;\n\t\t\t\t} else if (e.code === ZWaveErrorCodes.Controller_NodeTimeout) {\n\t\t\t\t\t// If the node failed to respond in time, remember this for the statistics\n\t\t\t\t\tnode?.incrementStatistics(\"timeoutResponse\");\n\t\t\t\t}\n\t\t\t\t// Enrich errors with the transaction's stack instead of the internal stack\n\t\t\t\tif (!e.transactionSource) {\n\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\te.message,\n\t\t\t\t\t\te.code,\n\t\t\t\t\t\te.context,\n\t\t\t\t\t\ttransaction.stack,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\t// The transaction was handled, so it can no longer expire\n\t\t\texpirationTimeout?.clear();\n\t\t}\n\t}\n\n\t/** Wraps a CC in the correct SendData message to use for sending */\n\tpublic createSendDataMessage(\n\t\tcommand: CommandClass,\n\t\toptions: Omit<SendCommandOptions, keyof SendMessageOptions> = {},\n\t): SendDataMessage & ContainsCC {\n\t\t// Automatically encapsulate commands before sending\n\t\tif (options.autoEncapsulate !== false) {\n\t\t\tcommand = this.encapsulateCommands(command, options);\n\t\t}\n\n\t\tlet msg: SendDataMessage;\n\t\tif (command.isSinglecast() || command.isBroadcast()) {\n\t\t\tif (\n\t\t\t\tthis.controller.isFunctionSupported(FunctionType.SendDataBridge)\n\t\t\t) {\n\t\t\t\t// Prioritize Bridge commands when they are supported\n\t\t\t\tmsg = new SendDataBridgeRequest({\n\t\t\t\t\tsourceNodeId: this.ownNodeId,\n\t\t\t\t\tcommand,\n\t\t\t\t\tmaxSendAttempts: this._options.attempts.sendData,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tmsg = new SendDataRequest({\n\t\t\t\t\tcommand,\n\t\t\t\t\tmaxSendAttempts: this._options.attempts.sendData,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (command.isMulticast()) {\n\t\t\tif (\n\t\t\t\tthis.controller.isFunctionSupported(\n\t\t\t\t\tFunctionType.SendDataMulticastBridge,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\t// Prioritize Bridge commands when they are supported\n\t\t\t\tmsg = new SendDataMulticastBridgeRequest({\n\t\t\t\t\tsourceNodeId: this.ownNodeId,\n\t\t\t\t\tcommand,\n\t\t\t\t\tmaxSendAttempts: this._options.attempts.sendData,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tmsg = new SendDataMulticastRequest({\n\t\t\t\t\tcommand,\n\t\t\t\t\tmaxSendAttempts: this._options.attempts.sendData,\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`A CC must either be singlecast or multicast`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\t\t// Specify the number of send attempts for the request\n\t\tif (options.maxSendAttempts != undefined) {\n\t\t\tmsg.maxSendAttempts = options.maxSendAttempts;\n\t\t}\n\n\t\t// Specify transmit options for the request\n\t\tif (options.transmitOptions != undefined) {\n\t\t\tmsg.transmitOptions = options.transmitOptions;\n\t\t}\n\n\t\tif (!!options.reportTimeoutMs) {\n\t\t\tmsg.nodeUpdateTimeout = options.reportTimeoutMs;\n\t\t}\n\n\t\treturn msg as SendDataMessage & ContainsCC;\n\t}\n\n\t/**\n\t * Sends a command to a Z-Wave node. If the node returns a command in response, that command will be the return value.\n\t * If the command expects no response **or** the response times out, nothing will be returned\n\t * @param command The command to send. It will be encapsulated in a SendData[Multicast]Request.\n\t * @param options (optional) Options regarding the message transmission\n\t */\n\tprivate async sendCommandInternal<\n\t\tTResponse extends CCId = CCId,\n\t>(\n\t\tcommand: CommandClass,\n\t\toptions: Omit<\n\t\t\tSendCommandOptions,\n\t\t\t\"requestStatusUpdates\" | \"onUpdate\"\n\t\t> = {},\n\t): Promise<TResponse | undefined> {\n\t\tconst msg = this.createSendDataMessage(command, options);\n\t\ttry {\n\t\t\tconst resp = await this.sendMessage(msg, options);\n\n\t\t\t// And unwrap the response if there was any\n\t\t\tif (containsCC(resp)) {\n\t\t\t\tthis.unwrapCommands(resp);\n\t\t\t\treturn resp.command as unknown as TResponse;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\t// A timeout always has to be expected. In this case return nothing.\n\t\t\tif (\n\t\t\t\tisZWaveError(e)\n\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t) {\n\t\t\t\tif (command.isSinglecast()) {\n\t\t\t\t\tthis.controllerLog.logNode(\n\t\t\t\t\t\tcommand.nodeId,\n\t\t\t\t\t\te.message,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// We don't want to swallow any other errors\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Sends a supervised command to a Z-Wave node. When status updates are requested, the passed callback will be executed for every non-final update.\n\t * @param command The command to send\n\t * @param options (optional) Options regarding the message transmission\n\t */\n\tprivate async sendSupervisedCommand(\n\t\tcommand: SinglecastCC<CommandClass>,\n\t\toptions: SendCommandOptions & { useSupervision?: \"auto\" } = {\n\t\t\trequestStatusUpdates: false,\n\t\t},\n\t): Promise<SupervisionResult | undefined> {\n\t\t// Create the encapsulating CC so we have a session ID\n\t\tconst sessionId = this.getNextSupervisionSessionId(command.nodeId);\n\t\tcommand = SupervisionCC.encapsulate(\n\t\t\tcommand,\n\t\t\tsessionId,\n\t\t\toptions.requestStatusUpdates,\n\t\t);\n\n\t\tconst resp = await this.sendCommandInternal<SupervisionCCReport>(\n\t\t\tcommand,\n\t\t\toptions,\n\t\t);\n\t\tif (!resp) return;\n\n\t\t// If future updates are expected, listen for them\n\t\tif (options.requestStatusUpdates && resp.moreUpdatesFollow) {\n\t\t\tthis.ensureNodeSessions(command.nodeId).supervision.set(\n\t\t\t\t(command as SupervisionCCGet).sessionId,\n\t\t\t\toptions.onUpdate,\n\t\t\t);\n\t\t}\n\t\t// In any case, return the status\n\t\treturn resp.toSupervisionResult();\n\t}\n\n\t/**\n\t * Sends a command to a Z-Wave node. The return value depends on several factors:\n\t * * If the node returns a command in response, that command will be the return value.\n\t * * If the command is a SET-type command and Supervision CC can and should be used, a {@link SupervisionResult} will be returned.\n\t * * If the command expects no response **or** the response times out, nothing will be returned.\n\t *\n\t * @param command The command to send. It will be encapsulated in a SendData[Multicast]Request.\n\t * @param options (optional) Options regarding the message transmission\n\t */\n\tpublic async sendCommand<\n\t\tTResponse extends CCId | undefined = undefined,\n\t>(\n\t\tcommand: CommandClass,\n\t\toptions?: SendCommandOptions,\n\t): Promise<SendCommandReturnType<TResponse>> {\n\t\tif (options?.encapsulationFlags != undefined) {\n\t\t\tcommand.encapsulationFlags = options.encapsulationFlags;\n\t\t}\n\n\t\t// Use security encapsulation for CCs that are only supported securely, and multicast commands\n\t\tif (\n\t\t\tthis.isCCSecure(\n\t\t\t\tcommand.ccId,\n\t\t\t\tcommand.nodeId as number,\n\t\t\t\tcommand.endpointIndex,\n\t\t\t)\n\t\t\t|| options?.s2MulticastGroupId != undefined\n\t\t) {\n\t\t\tcommand.toggleEncapsulationFlag(EncapsulationFlags.Security, true);\n\t\t}\n\n\t\t// Only use supervision if...\n\t\tif (\n\t\t\t// ... not disabled\n\t\t\toptions?.useSupervision !== false\n\t\t\t// ... and it is legal for the command\n\t\t\t&& SupervisionCC.mayUseSupervision(this, command)\n\t\t) {\n\t\t\tconst result = await this.sendSupervisedCommand(command, options);\n\t\t\tif (result?.status !== SupervisionStatus.NoSupport) {\n\t\t\t\t// @ts-expect-error TS doesn't know we've narrowed the return type to match\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\t// The node should support supervision but it doesn't for this command. Remember this\n\t\t\tSupervisionCC.setCCSupportedWithSupervision(\n\t\t\t\tthis,\n\t\t\t\tcommand.getEndpoint(this)!,\n\t\t\t\tcommand.ccId,\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\t// And retry the command without supervision\n\t\t}\n\n\t\t// Fall back to non-supervised commands\n\t\tconst result = await this.sendCommandInternal(command, options);\n\n\t\t// When sending S2 multicast commands to supporting nodes, the singlecast followups\n\t\t// may use supervision. In this case, the multicast message generator returns a\n\t\t// synthetic SupervisionCCReport.\n\t\t// sendCommand is supposed to return a SupervisionResult though.\n\t\tif (\n\t\t\toptions?.s2MulticastGroupId != undefined\n\t\t\t&& result instanceof SupervisionCCReport\n\t\t) {\n\t\t\t// @ts-expect-error TS doesn't know we've narrowed the return type to match\n\t\t\treturn result.toSupervisionResult();\n\t\t}\n\n\t\t// @ts-expect-error TS doesn't know we've narrowed the return type to match\n\t\treturn result;\n\t}\n\n\t/** @internal */\n\tpublic async sendZWaveProtocolCC(\n\t\tcommand: ZWaveProtocolCC,\n\t\toptions: Pick<\n\t\t\tSendCommandOptions,\n\t\t\t\"changeNodeStatusOnMissingACK\" | \"maxSendAttempts\"\n\t\t> = {},\n\t): Promise<void> {\n\t\tawait this.sendCommandInternal(command, {\n\t\t\tpriority: MessagePriority.Controller,\n\t\t\t// No shenanigans, just send the raw command\n\t\t\tautoEncapsulate: false,\n\t\t\tuseSupervision: false,\n\t\t\tchangeNodeStatusOnMissingACK: options.changeNodeStatusOnMissingACK\n\t\t\t\t?? false,\n\t\t\tmaxSendAttempts: options.maxSendAttempts || 1,\n\t\t\ttransmitOptions: TransmitOptions.AutoRoute | TransmitOptions.ACK,\n\t\t});\n\t}\n\n\tprivate async abortSendData(): Promise<void> {\n\t\ttry {\n\t\t\tconst abort = new SendDataAbort();\n\t\t\tawait this.writeSerial(\n\t\t\t\tawait abort.serialize(this.getEncodingContext()),\n\t\t\t);\n\t\t\tthis.driverLog.logMessage(abort, {\n\t\t\t\tdirection: \"outbound\",\n\t\t\t});\n\n\t\t\t// We're bypassing the serial API machine, so we need to wait for the ACK ourselves\n\t\t\t// This could also cause a NAK or CAN, but we don't really care\n\t\t\tawait this.waitForMessageHeader(() => true, 500).catch(noop);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\t/**\n\t * Sends a low-level message like ACK, NAK or CAN immediately\n\t * @param header The low-level message to send\n\t */\n\tprivate writeHeader(header: MessageHeaders): Promise<void> {\n\t\t// ACK, CAN, NAK\n\t\treturn this.writeSerial(Uint8Array.from([header]));\n\t}\n\n\t/** Sends a raw datagram to the serialport (if that is open) */\n\tprivate async writeSerial(data: Uint8Array): Promise<void> {\n\t\treturn this.serial?.writeAsync(data);\n\t}\n\n\t/**\n\t * Waits until a matching message header is received or a timeout has elapsed. Returns the received message.\n\t *\n\t * @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected\n\t * @param predicate A predicate function to test all incoming message headers.\n\t */\n\tpublic waitForMessageHeader(\n\t\tpredicate: (header: MessageHeaders) => boolean,\n\t\ttimeout: number,\n\t): Promise<MessageHeaders> {\n\t\treturn new Promise<MessageHeaders>((resolve, reject) => {\n\t\t\tconst promise = createDeferredPromise<MessageHeaders>();\n\t\t\tconst entry: AwaitedMessageHeader = {\n\t\t\t\tpredicate,\n\t\t\t\thandler: (msg) => promise.resolve(msg),\n\t\t\t\ttimeout: undefined,\n\t\t\t};\n\t\t\tthis.awaitedMessageHeaders.push(entry);\n\t\t\tconst removeEntry = () => {\n\t\t\t\tentry.timeout?.clear();\n\t\t\t\tconst index = this.awaitedMessageHeaders.indexOf(entry);\n\t\t\t\tif (index !== -1) this.awaitedMessageHeaders.splice(index, 1);\n\t\t\t};\n\t\t\t// When the timeout elapses, remove the wait entry and reject the returned Promise\n\t\t\tentry.timeout = setTimer(() => {\n\t\t\t\tremoveEntry();\n\t\t\t\treject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`Received no matching serial frame within the provided timeout!`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_Timeout,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}, timeout);\n\t\t\t// When the promise is resolved, remove the wait entry and resolve the returned Promise\n\t\t\tvoid promise.then((cc) => {\n\t\t\t\tremoveEntry();\n\t\t\t\tresolve(cc);\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Waits until an unsolicited serial message is received or a timeout has elapsed. Returns the received message.\n\t *\n\t * **Note:** To wait for a certain CommandClass, better use {@link waitForCommand}.\n\t * @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected\n\t * @param predicate A predicate function to test all incoming messages.\n\t * @param refreshPredicate A predicate function to test partial messages. If this returns `true` for a message, the timer will be restarted.\n\t */\n\tpublic waitForMessage<T extends Message>(\n\t\tpredicate: (msg: Message) => boolean,\n\t\ttimeout: number,\n\t\trefreshPredicate?: (msg: Message) => boolean,\n\t\tabortSignal?: AbortSignal,\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst promise = createDeferredPromise<Message>();\n\t\t\tconst entry: AwaitedMessageEntry = {\n\t\t\t\tpredicate,\n\t\t\t\trefreshPredicate,\n\t\t\t\thandler: (msg) => promise.resolve(msg),\n\t\t\t\ttimeout: undefined,\n\t\t\t};\n\t\t\tthis.awaitedMessages.push(entry);\n\t\t\tconst removeEntry = () => {\n\t\t\t\tentry.timeout?.clear();\n\t\t\t\tconst index = this.awaitedMessages.indexOf(entry);\n\t\t\t\tif (index !== -1) this.awaitedMessages.splice(index, 1);\n\t\t\t};\n\t\t\t// When the timeout elapses, remove the wait entry and reject the returned Promise\n\t\t\tentry.timeout = setTimer(() => {\n\t\t\t\tremoveEntry();\n\t\t\t\treject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`Received no matching message within the provided timeout!`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_Timeout,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}, timeout);\n\t\t\t// When the promise is resolved, remove the wait entry and resolve the returned Promise\n\t\t\tvoid promise.then((cc) => {\n\t\t\t\tremoveEntry();\n\t\t\t\tresolve(cc as T);\n\t\t\t});\n\t\t\t// When the abort signal is used, silently remove the wait entry\n\t\t\tabortSignal?.addEventListener(\"abort\", () => {\n\t\t\t\tremoveEntry();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Waits until a CommandClass is received or a timeout has elapsed. Returns the received command.\n\t * @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected\n\t * @param predicate A predicate function to test all incoming command classes\n\t */\n\tpublic waitForCommand<T extends CCId>(\n\t\tpredicate: (cc: CCId) => boolean,\n\t\ttimeout: number,\n\t\tabortSignal?: AbortSignal,\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst promise = createDeferredPromise<CCId>();\n\t\t\tconst entry: AwaitedCommandEntry = {\n\t\t\t\tpredicate,\n\t\t\t\thandler: (cc) => promise.resolve(cc),\n\t\t\t\ttimeout: undefined,\n\t\t\t};\n\t\t\tthis.awaitedCommands.push(entry);\n\t\t\tconst removeEntry = () => {\n\t\t\t\tentry.timeout?.clear();\n\t\t\t\tconst index = this.awaitedCommands.indexOf(entry);\n\t\t\t\tif (index !== -1) this.awaitedCommands.splice(index, 1);\n\t\t\t};\n\t\t\t// When the timeout elapses, remove the wait entry and reject the returned Promise\n\t\t\tentry.timeout = setTimer(() => {\n\t\t\t\tremoveEntry();\n\t\t\t\treject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`Received no matching command within the provided timeout!`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_NodeTimeout,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}, timeout);\n\t\t\t// When the promise is resolved, remove the wait entry and resolve the returned Promise\n\t\t\tvoid promise.then((cc) => {\n\t\t\t\tremoveEntry();\n\t\t\t\tresolve(cc as T);\n\t\t\t});\n\t\t\t// When the abort signal is used, silently remove the wait entry\n\t\t\tabortSignal?.addEventListener(\"abort\", () => {\n\t\t\t\tremoveEntry();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Calls the given handler function every time a CommandClass is received that matches the given predicate.\n\t * @param predicate A predicate function to test all incoming command classes\n\t */\n\tpublic registerCommandHandler<T extends CCId>(\n\t\tpredicate: (cc: CCId) => boolean,\n\t\thandler: (cc: T) => void,\n\t): {\n\t\tunregister: () => void;\n\t} {\n\t\tconst entry: AwaitedCommandEntry = {\n\t\t\tpredicate,\n\t\t\thandler: (cc) => handler(cc as T),\n\t\t\ttimeout: undefined,\n\t\t};\n\t\tthis.awaitedCommands.push(entry);\n\t\tconst removeEntry = () => {\n\t\t\tentry.timeout?.clear();\n\t\t\tconst index = this.awaitedCommands.indexOf(entry);\n\t\t\tif (index !== -1) this.awaitedCommands.splice(index, 1);\n\t\t};\n\n\t\treturn {\n\t\t\tunregister: removeEntry,\n\t\t};\n\t}\n\n\tprivate handleFailedTransaction(\n\t\ttransaction: Transaction,\n\t\terror: ZWaveError,\n\t): void {\n\t\t// If a node failed to respond in time, it might be sleeping\n\t\tif (this.isMissingNodeACK(transaction, error)) {\n\t\t\tif (this.handleMissingNodeACK(transaction as any, error)) return;\n\t\t} else if (isMissingControllerACK(error)) {\n\t\t\tif (this.handleMissingControllerACK(transaction, error)) return;\n\t\t} else if (\n\t\t\t// 700/800 series controllers can be jammed due to a bug,\n\t\t\t// which a soft-reset is supposed to work around\n\t\t\tisSendData(transaction.message)\n\t\t\t&& this.controller.status === ControllerStatus.Jammed\n\t\t) {\n\t\t\tif (this.handleJammedController(transaction, error)) return;\n\t\t} else if (\n\t\t\tisSendData(transaction.message)\n\t\t\t&& (isMissingControllerResponse(error)\n\t\t\t\t|| isMissingControllerCallback(error))\n\t\t) {\n\t\t\tif (\n\t\t\t\tthis.handleMissingSendDataResponseOrCallback(transaction, error)\n\t\t\t) return;\n\t\t} else if (wasControllerReset(error)) {\n\t\t\t// The controller was reset in the middle of a transaction.\n\t\t\t// Re-queue the transaction, so it can get handled again\n\t\t\t// Its message generator may have finished, so reset that too.\n\t\t\ttransaction.reset();\n\t\t\tthis.getQueueForTransaction(transaction).add(\n\t\t\t\ttransaction.clone(),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.rejectTransaction(transaction, error);\n\t}\n\n\tprivate rejectTransaction(\n\t\ttransaction: Transaction,\n\t\terror: ZWaveError,\n\t): void {\n\t\ttransaction.setProgress({\n\t\t\tstate: TransactionState.Failed,\n\t\t\treason: error.message,\n\t\t});\n\n\t\ttransaction.abort(error);\n\t}\n\n\tprivate resolveTransaction(\n\t\ttransaction: Transaction,\n\t\tresult?: Message,\n\t): void {\n\t\ttransaction.abort(result);\n\t}\n\n\t/** Checks if a message is allowed to go into the wakeup queue */\n\tprivate mayMoveToWakeupQueue(transaction: Transaction): boolean {\n\t\tconst msg = transaction.message;\n\t\tswitch (true) {\n\t\t\t// Pings, nonces and Supervision Reports will block the send queue until wakeup, so they must be dropped\n\t\t\tcase messageIsPing(msg):\n\t\t\tcase transaction.priority === MessagePriority.Immediate:\n\t\t\t// We also don't want to immediately send the node to sleep when it wakes up\n\t\t\tcase containsCC(msg)\n\t\t\t\t&& msg.command instanceof WakeUpCCNoMoreInformation:\n\t\t\t// compat queries because they will be recreated when the node wakes up\n\t\t\tcase transaction.tag === \"compat\":\n\t\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/** Moves all messages for a given node into the wakeup queue */\n\tprivate moveMessagesToWakeupQueue(nodeId: number): void {\n\t\tconst reject: TransactionReducerResult = {\n\t\t\ttype: \"reject\",\n\t\t\tmessage: `The node is asleep`,\n\t\t\tcode: ZWaveErrorCodes.Controller_MessageDropped,\n\t\t};\n\t\tconst requeue: TransactionReducerResult = {\n\t\t\ttype: \"requeue\",\n\t\t\tpriority: MessagePriority.WakeUp,\n\t\t\t// Reset the transaction so it doesn't simply resolve to `undefined` when we attempt to continue it\n\t\t\treset: true,\n\t\t};\n\t\tconst requeueAndTagAsInterview: TransactionReducerResult = {\n\t\t\t...requeue,\n\t\t\ttag: \"interview\",\n\t\t};\n\n\t\tvoid this.reduceQueues((transaction, _source) => {\n\t\t\tconst msg = transaction.message;\n\t\t\tif (msg.getNodeId() !== nodeId) return { type: \"keep\" };\n\t\t\t// Drop all messages that are not allowed in the wakeup queue\n\t\t\t// For all other messages, change the priority to wakeup\n\t\t\treturn this.mayMoveToWakeupQueue(transaction)\n\t\t\t\t? transaction.priority === MessagePriority.NodeQuery\n\t\t\t\t\t? requeueAndTagAsInterview\n\t\t\t\t\t: requeue\n\t\t\t\t: reject;\n\t\t});\n\t}\n\n\t/**\n\t * @internal\n\t * Rejects all pending transactions that match a predicate and removes them from the send queue\n\t */\n\tpublic rejectTransactions(\n\t\tpredicate: (t: Transaction) => boolean,\n\t\terrorMsg: string = `The message has been removed from the queue`,\n\t\terrorCode: ZWaveErrorCodes = ZWaveErrorCodes.Controller_MessageDropped,\n\t): Promise<void> {\n\t\treturn this.reduceQueues((transaction, _source) => {\n\t\t\tif (predicate(transaction)) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"reject\",\n\t\t\t\t\tmessage: errorMsg,\n\t\t\t\t\tcode: errorCode,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\treturn { type: \"keep\" };\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * @internal\n\t * Rejects all pending transactions for a node and removes them from the send queue\n\t */\n\tpublic rejectAllTransactionsForNode(\n\t\tnodeId: number,\n\t\terrorMsg: string = `The node is dead`,\n\t\terrorCode: ZWaveErrorCodes = ZWaveErrorCodes.Controller_MessageDropped,\n\t): Promise<void> {\n\t\treturn this.rejectTransactions(\n\t\t\t(t) => t.message.getNodeId() === nodeId,\n\t\t\terrorMsg,\n\t\t\terrorCode,\n\t\t);\n\t}\n\n\t/**\n\t * Pauses the send queue, avoiding commands to be sent to the controller\n\t */\n\tprivate pauseSendQueue(): void {\n\t\tthis.queuePaused = true;\n\t}\n\n\t/**\n\t * Unpauses the send queue, allowing commands to be sent to the controller again\n\t */\n\tprivate unpauseSendQueue(): void {\n\t\tthis.queuePaused = false;\n\t\tthis.triggerQueues();\n\t}\n\n\tprivate reduceQueues(reducer: TransactionReducer): Promise<void> {\n\t\t// This function MUST not be async, because this can introduce a\n\t\t// race condition caused by the microtick delay\n\t\treturn Promise\n\t\t\t.all(this.queues.map((queue) => this.reduceQueue(queue, reducer)))\n\t\t\t.then(noop);\n\t}\n\n\tprivate reduceQueue(\n\t\tqueue: TransactionQueue,\n\t\treducer: TransactionReducer,\n\t): Promise<void> {\n\t\tconst dropQueued: Transaction[] = [];\n\t\tlet stopActive: Transaction | undefined;\n\t\tconst requeue: Transaction[] = [];\n\n\t\tconst reduceTransaction: (\n\t\t\t...args: Parameters<TransactionReducer>\n\t\t) => void = (transaction, source) => {\n\t\t\tconst reducerResult = reducer(transaction, source);\n\t\t\tswitch (reducerResult.type) {\n\t\t\t\tcase \"drop\":\n\t\t\t\t\tif (source === \"queue\") {\n\t\t\t\t\t\tdropQueued.push(transaction);\n\n\t\t\t\t\t\t// This will silently drop the transaction, so awaiting it will never resolve.\n\t\t\t\t\t\t// At least notify the listeners about it.\n\t\t\t\t\t\ttransaction.setProgress({\n\t\t\t\t\t\t\tstate: TransactionState.Failed,\n\t\t\t\t\t\t\treason: \"The message was dropped\",\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstopActive = transaction;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"requeue\":\n\t\t\t\t\tif (reducerResult.priority != undefined) {\n\t\t\t\t\t\ttransaction.priority = reducerResult.priority;\n\t\t\t\t\t}\n\t\t\t\t\tif (reducerResult.tag != undefined) {\n\t\t\t\t\t\ttransaction.tag = reducerResult.tag;\n\t\t\t\t\t}\n\t\t\t\t\tif (reducerResult.reset) {\n\t\t\t\t\t\ttransaction.reset();\n\t\t\t\t\t}\n\t\t\t\t\tif (source === \"active\") stopActive = transaction;\n\t\t\t\t\trequeue.push(transaction);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"resolve\":\n\t\t\t\t\tthis.resolveTransaction(transaction, reducerResult.message);\n\t\t\t\t\tif (source === \"queue\") {\n\t\t\t\t\t\tdropQueued.push(transaction);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstopActive = transaction;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"reject\":\n\t\t\t\t\tthis.rejectTransaction(\n\t\t\t\t\t\ttransaction,\n\t\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\treducerResult.message,\n\t\t\t\t\t\t\treducerResult.code,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\ttransaction.stack,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tif (source === \"queue\") {\n\t\t\t\t\t\tdropQueued.push(transaction);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstopActive = transaction;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t};\n\n\t\tfor (const transaction of queue.transactions) {\n\t\t\treduceTransaction(transaction, \"queue\");\n\t\t}\n\n\t\tif (queue.currentTransaction) {\n\t\t\treduceTransaction(queue.currentTransaction, \"active\");\n\t\t}\n\n\t\t// Now we know what to do with the transactions\n\t\tqueue.remove(...dropQueued, ...requeue);\n\t\tconst requeued = requeue.map((t) => t.clone());\n\t\tqueue.add(...requeued);\n\n\t\t// Notify listeners about re-queued transactions\n\t\tfor (const t of requeued) {\n\t\t\tt.setProgress({ state: TransactionState.Queued });\n\t\t}\n\n\t\t// Abort ongoing SendData messages that should be dropped\n\t\tif (isSendData(stopActive?.message)) {\n\t\t\treturn this.abortSendData();\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\n\t/** @internal */\n\tpublic resolvePendingPings(nodeId: number): void {\n\t\t// When a previously sleeping node sends a NIF after a ping was sent to it, but not acknowledged yet,\n\t\t// the node is awake, but the ping would fail. Resolve pending pings, so communication can continue.\n\t\tfor (const { currentTransaction } of this.queues) {\n\t\t\tif (!currentTransaction) continue;\n\t\t\tconst msg = currentTransaction.parts.current;\n\t\t\tif (!!msg && messageIsPing(msg) && msg.getNodeId() === nodeId) {\n\t\t\t\t// The pending transaction is a ping. Short-circuit its message generator by throwing something that's not an error\n\t\t\t\tcurrentTransaction.abort(undefined);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Helper function to read and convert potentially existing values from the network cache\n\t */\n\tpublic cacheGet<T>(\n\t\tcacheKey: string,\n\t\toptions?: {\n\t\t\treviver?: (value: any) => T;\n\t\t},\n\t): T | undefined {\n\t\tlet ret = this.networkCache.get(cacheKey);\n\t\tif (ret !== undefined && typeof options?.reviver === \"function\") {\n\t\t\ttry {\n\t\t\t\tret = options.reviver(ret);\n\t\t\t} catch {\n\t\t\t\t// ignore, invalid entry\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/**\n\t * @internal\n\t * Helper function to convert values and write them to the network cache\n\t */\n\tpublic cacheSet<T>(\n\t\tcacheKey: string,\n\t\tvalue: T | undefined,\n\t\toptions?: {\n\t\t\tserializer?: (value: T) => any;\n\t\t},\n\t): void {\n\t\tif (value !== undefined && typeof options?.serializer === \"function\") {\n\t\t\tvalue = options.serializer(value);\n\t\t}\n\n\t\tif (value === undefined) {\n\t\t\tthis.networkCache.delete(cacheKey);\n\t\t} else {\n\t\t\tthis.networkCache.set(cacheKey, value);\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Helper function to find multiple existing values from the network cache\n\t */\n\tpublic cacheList<T>(\n\t\tprefix: string,\n\t\toptions?: {\n\t\t\treviver?: (value: any) => T;\n\t\t},\n\t): Record<string, T> {\n\t\tconst ret: Record<string, T> = {};\n\t\tfor (const entry of this.networkCache.entries()) {\n\t\t\tconst key = entry[0];\n\t\t\tif (!key.startsWith(prefix)) continue;\n\t\t\tlet value = entry[1];\n\t\t\tif (value === undefined) continue;\n\t\t\tif (typeof options?.reviver === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\tvalue = options.reviver(value);\n\t\t\t\t} catch {\n\t\t\t\t\t// invalid entry\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tret[key] = value;\n\t\t}\n\t\treturn ret;\n\t}\n\n\tprivate cachePurge(prefix: string): void {\n\t\tfor (const key of this.networkCache.keys()) {\n\t\t\tif (key.startsWith(prefix)) {\n\t\t\t\tthis.networkCache.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Restores a previously stored Z-Wave network state from cache to speed up the startup process\n\t */\n\tpublic async restoreNetworkStructureFromCache(): Promise<void> {\n\t\tif (\n\t\t\t!this._controller\n\t\t\t|| !this.controller.homeId\n\t\t\t|| !this._networkCache\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._networkCache.size <= 1) {\n\t\t\t// If the size is 0 or 1, the cache is empty, so we cannot restore it\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Cache file for homeId ${\n\t\t\t\t\tnum2hex(\n\t\t\t\t\t\tthis.controller.homeId,\n\t\t\t\t\t)\n\t\t\t\t} found, attempting to restore the network from cache...`,\n\t\t\t);\n\t\t\tawait this.controller.deserialize();\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Restoring the network from cache was successful!`,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tconst message = `Restoring the network from cache failed: ${\n\t\t\t\tgetErrorMessage(\n\t\t\t\t\te,\n\t\t\t\t\ttrue,\n\t\t\t\t)\n\t\t\t}`;\n\t\t\tthis.emit(\n\t\t\t\t\"error\",\n\t\t\t\tnew ZWaveError(message, ZWaveErrorCodes.Driver_InvalidCache),\n\t\t\t);\n\t\t\tthis.driverLog.print(message, \"error\");\n\t\t}\n\t}\n\n\tprivate sendNodeToSleepTimers = new Map<number, Timer>();\n\t/**\n\t * @internal\n\t * Marks a node for a later sleep command. Every call refreshes the period until the node actually goes to sleep\n\t */\n\tpublic debounceSendNodeToSleep(\n\t\tnode: ZWaveNodeBase & NodeSchedulePoll & NodeValues & NodeWakeup,\n\t): void {\n\t\t// TODO: This should be a single command to the send thread\n\t\t// Delete old timers if any exist\n\t\tif (this.sendNodeToSleepTimers.has(node.id)) {\n\t\t\tthis.sendNodeToSleepTimers.get(node.id)?.clear();\n\t\t}\n\n\t\t// Sends a node to sleep if it has no more messages.\n\t\tconst sendNodeToSleep = (\n\t\t\tnode: ZWaveNodeBase & NodeSchedulePoll & NodeWakeup,\n\t\t): void => {\n\t\t\tthis.sendNodeToSleepTimers.delete(node.id);\n\t\t\tif (!this.hasPendingMessages(node)) {\n\t\t\t\tvoid node.sendNoMoreInformation().catch(() => {\n\t\t\t\t\t/* ignore errors */\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\n\t\t// If a sleeping node has no messages pending (and supports the Wake Up CC), we may send it back to sleep\n\t\tif (\n\t\t\tnode.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t\t&& !this.hasPendingMessages(node)\n\t\t) {\n\t\t\tconst wakeUpInterval = node.getValue<number>(\n\t\t\t\tWakeUpCCValues.wakeUpInterval.id,\n\t\t\t);\n\t\t\t// GH#2179: when a device only wakes up manually, don't send it back to sleep\n\t\t\t// Best case, the user wanted to interact with it.\n\t\t\t// Worst case, the device won't ACK this and cause a delay\n\t\t\tif (wakeUpInterval !== 0) {\n\t\t\t\tthis.sendNodeToSleepTimers.set(\n\t\t\t\t\tnode.id,\n\t\t\t\t\tsetTimer(\n\t\t\t\t\t\t() => sendNodeToSleep(node),\n\t\t\t\t\t\tthis.options.timeouts.sendToSleep,\n\t\t\t\t\t).unref(),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Computes the maximum net CC payload size for the given CC or SendDataRequest */\n\tpublic computeNetCCPayloadSize(\n\t\tcommandOrMsg:\n\t\t\t| CommandClass\n\t\t\t| (SendDataRequest | SendDataBridgeRequest) & ContainsCC,\n\t\tignoreEncapsulation: boolean = false,\n\t): number {\n\t\t// Recreate the correct encapsulation structure\n\t\tlet msg: (SendDataRequest | SendDataBridgeRequest) & ContainsCC;\n\t\tif (isSendDataSinglecast(commandOrMsg)) {\n\t\t\tmsg = commandOrMsg;\n\t\t} else {\n\t\t\tconst SendDataConstructor = this.getSendDataSinglecastConstructor();\n\t\t\tmsg = new SendDataConstructor({\n\t\t\t\tsourceNodeId: this.ownNodeId,\n\t\t\t\tcommand: commandOrMsg,\n\t\t\t}) as (SendDataRequest | SendDataBridgeRequest) & ContainsCC;\n\t\t}\n\t\tif (!ignoreEncapsulation) {\n\t\t\tmsg.command = this.encapsulateCommands(\n\t\t\t\tmsg.command,\n\t\t\t) as SinglecastCC<CommandClass>;\n\t\t}\n\t\treturn msg.command.getMaxPayloadLength(this.getMaxPayloadLength(msg));\n\t}\n\n\t/** Computes the maximum payload size that can be transmitted with the given message */\n\tpublic getMaxPayloadLength(msg: SendDataMessage): number {\n\t\tconst nodeId = msg.getNodeId();\n\n\t\t// For ZWLR, the result is simply the maximum payload size\n\t\tif (\n\t\t\tnodeId != undefined\n\t\t\t&& isLongRangeNodeId(nodeId)\n\t\t\t&& this._controller?.maxPayloadSizeLR\n\t\t) {\n\t\t\treturn this._controller.maxPayloadSizeLR;\n\t\t}\n\n\t\t// For ZW Classic, it depends on the frame type and transmit options\n\t\tconst maxExplorerPayloadSinglecast = this._controller?.maxPayloadSize\n\t\t\t?? 46;\n\t\tif (isSendDataSinglecast(msg)) {\n\t\t\t// From INS13954-7, chapter 4.3.3.1.5\n\t\t\tif (msg.transmitOptions & TransmitOptions.Explore) {\n\t\t\t\treturn maxExplorerPayloadSinglecast;\n\t\t\t}\n\t\t\tif (msg.transmitOptions & TransmitOptions.AutoRoute) {\n\t\t\t\treturn maxExplorerPayloadSinglecast + 2;\n\t\t\t}\n\t\t\treturn maxExplorerPayloadSinglecast + 8;\n\t\t} else {\n\t\t\t// Multicast needs space for the nodes bitmask\n\t\t\tconst maxExplorerPayloadMulticast = maxExplorerPayloadSinglecast\n\t\t\t\t- NUM_NODEMASK_BYTES;\n\n\t\t\t// From INS13954-13, chapter 4.3.3.6\n\t\t\tif (msg.transmitOptions & TransmitOptions.ACK) {\n\t\t\t\tif (msg.transmitOptions & TransmitOptions.Explore) {\n\t\t\t\t\treturn maxExplorerPayloadMulticast;\n\t\t\t\t}\n\t\t\t\tif (msg.transmitOptions & TransmitOptions.AutoRoute) {\n\t\t\t\t\treturn maxExplorerPayloadMulticast + 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn maxExplorerPayloadMulticast + 8;\n\t\t}\n\t}\n\n\tpublic async exceedsMaxPayloadLength(\n\t\tmsg: SendDataMessage,\n\t): Promise<boolean> {\n\t\tconst serializedCC = await msg.serializeCC(\n\t\t\tthis.getEncodingContext(),\n\t\t);\n\t\treturn serializedCC.length > this.getMaxPayloadLength(msg);\n\t}\n\n\t/** Determines time in milliseconds to wait for a report from a node */\n\tpublic getReportTimeout(msg: Message): number {\n\t\tconst node = this.tryGetNode(msg);\n\n\t\treturn (\n\t\t\t// If there's a message-specific timeout, use that\n\t\t\tmsg.nodeUpdateTimeout\n\t\t\t\t// If the node has a compat flag to override the timeout, use that\n\t\t\t\t?? node?.deviceConfig?.compat?.reportTimeout\n\t\t\t\t// otherwise use the driver option\n\t\t\t\t?? this._options.timeouts.report\n\t\t);\n\t}\n\n\t/** Returns the preferred constructor to use for singlecast SendData commands */\n\tpublic getSendDataSinglecastConstructor():\n\t\t| typeof SendDataRequest\n\t\t| typeof SendDataBridgeRequest\n\t{\n\t\treturn this._controller?.isFunctionSupported(\n\t\t\t\tFunctionType.SendDataBridge,\n\t\t\t)\n\t\t\t? SendDataBridgeRequest\n\t\t\t: SendDataRequest;\n\t}\n\n\t/** Returns the preferred constructor to use for multicast SendData commands */\n\tpublic getSendDataMulticastConstructor():\n\t\t| typeof SendDataMulticastRequest\n\t\t| typeof SendDataMulticastBridgeRequest\n\t{\n\t\treturn this._controller?.isFunctionSupported(\n\t\t\t\tFunctionType.SendDataMulticastBridge,\n\t\t\t)\n\t\t\t? SendDataMulticastBridgeRequest\n\t\t\t: SendDataMulticastRequest;\n\t}\n\n\t/**\n\t * Checks whether there is a compatible update for the currently installed config package.\n\t * Returns the new version if there is an update, `undefined` otherwise.\n\t */\n\tpublic async checkForConfigUpdates(\n\t\tsilent: boolean = false,\n\t): Promise<string | undefined> {\n\t\tthis.ensureReady();\n\n\t\ttry {\n\t\t\tif (!silent) {\n\t\t\t\tthis.driverLog.print(\"Checking for configuration updates...\");\n\t\t\t}\n\t\t\tconst ret = await checkForConfigUpdates(this.configVersion);\n\t\t\tif (ret) {\n\t\t\t\tif (!silent) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t`Configuration update available: ${ret}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (!silent) {\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"No configuration update available...\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(getErrorMessage(e), \"error\");\n\t\t}\n\t}\n\n\tprivate _installConfigUpdatePromise: Promise<boolean> | undefined;\n\n\t/**\n\t * Installs an update for the embedded configuration DB if there is a compatible one.\n\t * Returns `true` when an update was installed, `false` otherwise.\n\t *\n\t * **Note:** Bugfixes and changes to device configuration generally require a restart or re-interview to take effect.\n\t */\n\tpublic async installConfigUpdate(): Promise<boolean> {\n\t\tthis.ensureReady();\n\n\t\tif (this._installConfigUpdatePromise) {\n\t\t\treturn this._installConfigUpdatePromise;\n\t\t}\n\t\tthis._installConfigUpdatePromise = this.installConfigUpdateInternal();\n\n\t\ttry {\n\t\t\treturn await this._installConfigUpdatePromise;\n\t\t} finally {\n\t\t\tthis._installConfigUpdatePromise = undefined;\n\t\t}\n\t}\n\n\tprivate async installConfigUpdateInternal(): Promise<boolean> {\n\t\tconst newVersion = await this.checkForConfigUpdates(true);\n\t\tif (!newVersion) return false;\n\n\t\tconst extConfigDir = this.configManager.externalConfigDir;\n\t\tif (!this.configManager.useExternalConfig || !extConfigDir) {\n\t\t\tthis.driverLog.print(\n\t\t\t\t`Cannot update configuration DB update - external config directory is not set`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.driverLog.print(\n\t\t\t`Installing version ${newVersion} of configuration DB...`,\n\t\t);\n\t\ttry {\n\t\t\tawait installConfigUpdate(\n\t\t\t\tthis.bindings.fs,\n\t\t\t\tnewVersion,\n\t\t\t\t{ configDir: extConfigDir },\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.driverLog.print(getErrorMessage(e), \"error\");\n\t\t\treturn false;\n\t\t}\n\t\tthis.driverLog.print(\n\t\t\t`Configuration DB updated to version ${newVersion}, activating...`,\n\t\t);\n\n\t\t// Reload the config files\n\t\tawait this.configManager.loadAll();\n\n\t\t// Now try to apply them to all known devices\n\t\tif (this._controller) {\n\t\t\tfor (const node of this._controller.nodes.values()) {\n\t\t\t\tif (node.ready) await node[\"loadDeviceConfig\"]();\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t// region OTW Firmware Updates\n\n\tprivate _otwFirmwareUpdateInProgress: boolean = false;\n\n\t/**\n\t * Returns whether a firmware update is in progress for the Z-Wave module.\n\t */\n\tpublic isOTWFirmwareUpdateInProgress(): boolean {\n\t\treturn this._otwFirmwareUpdateInProgress;\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<OTWFirmwareUpdateResult> {\n\t\t// Don't interrupt ongoing OTA firmware updates\n\t\tif (this._controller?.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.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.OTW_Update_Busy);\n\t\t}\n\n\t\t// Don't allow updating firmware when the controller is currently updating its own firmware\n\t\tif (this.isOTWFirmwareUpdateInProgress()) {\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.controllerLog.print(message, \"error\");\n\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.OTW_Update_Busy);\n\t\t}\n\n\t\t// When in bootloader mode, we can use the 700 series update method\n\t\tif (this.mode === DriverMode.Bootloader) {\n\t\t\treturn this.firmwareUpdateOTW700(data);\n\t\t} else if (this.mode === DriverMode.SerialAPI) {\n\t\t\tif (this.controller.sdkVersionGte(\"7.0\")) {\n\t\t\t\t// This is at least a 700 series controller, so we can use the 700 series update method\n\t\t\t\treturn this.firmwareUpdateOTW700(data);\n\t\t\t} else if (\n\t\t\t\tthis.controller.sdkVersionGte(\"6.50.0\")\n\t\t\t\t&& this.controller.supportedFunctionTypes\n\t\t\t\t\t?.includes(FunctionType.FirmwareUpdateNVM)\n\t\t\t) {\n\t\t\t\t// This is a 500 series controller, use the 500 series update method\n\t\t\t\tconst wasUpdated = await this.firmwareUpdateOTW500(data);\n\t\t\t\tif (wasUpdated.success) {\n\t\t\t\t\t// After updating the firmware on 500 series sticks, we MUST soft-reset them\n\t\t\t\t\tthis.driverLog.print(\n\t\t\t\t\t\t\"Activating new firmware and restarting driver...\",\n\t\t\t\t\t);\n\t\t\t\t\tawait this.softResetAndRestart();\n\t\t\t\t}\n\t\t\t\treturn wasUpdated;\n\t\t\t}\n\t\t} else if (this.mode === DriverMode.CLI) {\n\t\t\t// If the CLI has an option to enter bootloader, we can use the 700 series update method,\n\t\t\t// since it tries to execute that.\n\t\t\treturn this.firmwareUpdateOTW700(data);\n\t\t}\n\n\t\tthrow new ZWaveError(\n\t\t\t`Firmware updates are not supported on this Z-Wave module`,\n\t\t\tZWaveErrorCodes.Controller_NotSupported,\n\t\t);\n\t}\n\n\tprivate async firmwareUpdateOTW500(\n\t\tdata: Uint8Array,\n\t): Promise<OTWFirmwareUpdateResult> {\n\t\tthis._otwFirmwareUpdateInProgress = true;\n\t\tlet turnedRadioOff = false;\n\t\ttry {\n\t\t\tthis.controllerLog.print(\"Beginning firmware update\");\n\n\t\t\tconst canUpdate = await this.controller.firmwareUpdateNVMInit();\n\t\t\tif (!canUpdate) {\n\t\t\t\tthis.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.controller.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.controller.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: OTWFirmwareUpdateProgress = {\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.controller\n\t\t\t\t.firmwareUpdateNVMIsValidCRC16();\n\t\t\tif (!isValidCRC) {\n\t\t\t\tthis.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.controller.firmwareUpdateNVMSetNewImage();\n\n\t\t\tthis.controllerLog.print(\"Firmware update succeeded\");\n\n\t\t\tconst result: OTWFirmwareUpdateResult = {\n\t\t\t\tsuccess: true,\n\t\t\t\tstatus: OTWFirmwareUpdateStatus.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._otwFirmwareUpdateInProgress = false;\n\t\t\tif (turnedRadioOff) await this.controller.toggleRF(true);\n\t\t}\n\t}\n\n\tprivate async firmwareUpdateOTW700(\n\t\tdata: Uint8Array,\n\t): Promise<OTWFirmwareUpdateResult> {\n\t\tthis._otwFirmwareUpdateInProgress = true;\n\n\t\ttry {\n\t\t\tawait this.enterBootloader();\n\n\t\t\t// Start the update process\n\t\t\tthis.controllerLog.print(\"Beginning firmware upload\");\n\t\t\tawait this.bootloader.beginUpload();\n\n\t\t\t// Wait for the bootloader to accept fragments\n\t\t\ttry {\n\t\t\t\tawait this.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.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.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.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.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.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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: OTWFirmwareUpdateProgress = {\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\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.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.waitForBootloaderChunk<\n\t\t\t\t\tBootloaderChunk & { type: BootloaderChunkType.Message }\n\t\t\t\t>(\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.includes(\"error 0x\"),\n\t\t\t\t\t1000,\n\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.waitForBootloaderChunk(\n\t\t\t\t\t(c) => c.type === BootloaderChunkType.Menu,\n\t\t\t\t\t1000,\n\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.controllerLog.print(message, \"error\");\n\n\t\t\t\tconst result: OTWFirmwareUpdateResult = {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.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.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.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.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: OTWFirmwareUpdateResult = {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.controllerLog.print(\"Firmware update succeeded\");\n\n\t\t\tconst result: OTWFirmwareUpdateResult = {\n\t\t\t\tsuccess: true,\n\t\t\t\tstatus: OTWFirmwareUpdateStatus.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.leaveBootloader();\n\t\t\tthis._otwFirmwareUpdateInProgress = false;\n\t\t}\n\t}\n\n\t// region Bootloader\n\n\tprivate _enteringBootloader: boolean = false;\n\tprivate _enterBootloaderPromise: DeferredPromise<void> | undefined;\n\n\tpublic async enterBootloader(): Promise<void> {\n\t\tif (this._bootloader) return;\n\n\t\tthis.controllerLog.print(\"Entering bootloader...\");\n\t\tthis._enteringBootloader = true;\n\t\ttry {\n\t\t\tif (this.mode === DriverMode.SerialAPI) {\n\t\t\t\tawait this.enterBootloaderFromSerialAPI();\n\t\t\t} else if (this.mode === DriverMode.CLI) {\n\t\t\t\tawait this.enterBootloaderFromCLI();\n\t\t\t}\n\n\t\t\t// Wait if the menu shows up\n\t\t\tthis._enterBootloaderPromise = createDeferredPromise();\n\t\t\tconst success = await Promise.race([\n\t\t\t\tthis._enterBootloaderPromise.then(() => true),\n\t\t\t\twait(5000, true).then(() => false),\n\t\t\t]);\n\t\t\tif (success) {\n\t\t\t\tthis.controllerLog.print(\"Entered bootloader\");\n\t\t\t} else {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"Failed to enter bootloader\",\n\t\t\t\t\tZWaveErrorCodes.Controller_Timeout,\n\t\t\t\t);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._enteringBootloader = false;\n\t\t}\n\t}\n\n\tprivate async enterBootloaderFromSerialAPI(): Promise<void> {\n\t\t// Get the encoding context before destroying the controller\n\t\tconst ctx = this.getEncodingContext();\n\n\t\tawait this.destroyController();\n\n\t\t// It would be nicer to not hardcode the command here, but since we're switching stream parsers\n\t\t// mid-command - thus ignoring the ACK, we can't really use the existing communication machinery\n\t\tconst msg = new EnterBootloaderRequest();\n\t\tconst promise = this.writeSerial(await msg.serialize(ctx));\n\t\tthis.serial!.mode = ZWaveSerialMode.Bootloader;\n\t\tawait promise;\n\t}\n\n\tprivate async enterBootloaderFromCLI(): Promise<void> {\n\t\t// The CLI first responds with a success message, so we have to reset the\n\t\tawait this.cli.executeCommand(\"bootloader\");\n\t\t// serial mode after the command is complete\n\t\tthis.serial!.mode = undefined;\n\t}\n\n\tprivate leaveBootloaderInternal(): Promise<void> {\n\t\tconst promise = this.bootloader.runApplication();\n\t\t// Reset the known serial mode.\n\t\t// We might end up in serial API, CLI or bootloader mode afterwards.\n\t\tthis.serial!.mode = undefined;\n\t\tthis._bootloader = undefined;\n\t\treturn promise;\n\t}\n\n\t/**\n\t * Leaves the bootloader by running the application.\n\t */\n\tpublic async leaveBootloader(): Promise<void> {\n\t\tthis.controllerLog.print(\"Leaving bootloader...\");\n\t\tawait this.leaveBootloaderInternal();\n\n\t\t// We may end up in a Serial API or CLI application. CLI is detected\n\t\t// automatically, Serial API should send a SerialAPIStartedRequest.\n\t\t// Give both a second to respond.\n\t\tconst isSerialAPI = await this.waitForMessage<SerialAPIStartedRequest>(\n\t\t\t(msg) => msg.functionType === FunctionType.SerialAPIStarted,\n\t\t\t1000,\n\t\t)\n\t\t\t.then(() => true as const)\n\t\t\t.catch(() => false as const);\n\n\t\tif (isSerialAPI) {\n\t\t\t// Re-initialize the controller\n\t\t\tawait this.initializeControllerAndNodes();\n\t\t\treturn;\n\t\t} else if (this.mode === DriverMode.CLI) {\n\t\t\tawait this.ensureCLIReady();\n\t\t\treturn;\n\t\t}\n\n\t\t// FIXME: Do we need the pause thing still?\n\t\tthis.unpauseSendQueue();\n\n\t\tawait this.ensureSerialAPI();\n\t}\n\n\tprivate serialport_onBootloaderData(data: BootloaderChunk): void {\n\t\tswitch (data.type) {\n\t\t\tcase BootloaderChunkType.Message: {\n\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t`[BOOTLOADER] ${data.message}`,\n\t\t\t\t\t\"verbose\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase BootloaderChunkType.FlowControl: {\n\t\t\t\tif (data.command === XModemMessageHeaders.C) {\n\t\t\t\t\tthis.controllerLog.print(\n\t\t\t\t\t\t`[BOOTLOADER] awaiting input...`,\n\t\t\t\t\t\t\"verbose\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Check if there is a handler waiting for this chunk\n\t\tfor (const entry of this.awaitedBootloaderChunks) {\n\t\t\tif (entry.predicate(data)) {\n\t\t\t\t// there is!\n\t\t\t\tentry.handler(data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!this._bootloader && data.type === BootloaderChunkType.Menu) {\n\t\t\t// We just entered the bootloader\n\t\t\tthis._controller?.destroy();\n\t\t\tthis._controller = undefined;\n\t\t\tthis._cli = undefined;\n\n\t\t\tthis.controllerLog.print(\n\t\t\t\t`[BOOTLOADER] version ${data.version}`,\n\t\t\t\t\"verbose\",\n\t\t\t);\n\n\t\t\tthis._bootloader = new Bootloader(\n\t\t\t\tthis.writeSerial.bind(this),\n\t\t\t\tdata.version,\n\t\t\t\tdata.options,\n\t\t\t);\n\t\t\tif (this._enterBootloaderPromise) {\n\t\t\t\tthis._enterBootloaderPromise.resolve();\n\t\t\t\tthis._enterBootloaderPromise = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Waits until a specific chunk is received from the bootloader or a timeout has elapsed. Returns the received chunk.\n\t * @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected\n\t * @param predicate A predicate function to test all incoming chunks\n\t */\n\tpublic waitForBootloaderChunk<T extends BootloaderChunk>(\n\t\tpredicate: (chunk: BootloaderChunk) => boolean,\n\t\ttimeout: number,\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst promise = createDeferredPromise<BootloaderChunk>();\n\t\t\tconst entry: AwaitedBootloaderChunkEntry = {\n\t\t\t\tpredicate,\n\t\t\t\thandler: (chunk) => promise.resolve(chunk),\n\t\t\t\ttimeout: undefined,\n\t\t\t};\n\t\t\tthis.awaitedBootloaderChunks.push(entry);\n\t\t\tconst removeEntry = () => {\n\t\t\t\tentry.timeout?.clear();\n\t\t\t\tconst index = this.awaitedBootloaderChunks.indexOf(entry);\n\t\t\t\tif (index !== -1) this.awaitedBootloaderChunks.splice(index, 1);\n\t\t\t};\n\t\t\t// When the timeout elapses, remove the wait entry and reject the returned Promise\n\t\t\tentry.timeout = setTimer(() => {\n\t\t\t\tremoveEntry();\n\t\t\t\treject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`Received no matching chunk within the provided timeout!`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_Timeout,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}, timeout);\n\t\t\t// When the promise is resolved, remove the wait entry and resolve the returned Promise\n\t\t\tvoid promise.then((chunk) => {\n\t\t\t\tremoveEntry();\n\t\t\t\tresolve(chunk as T);\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Waits until a specific chunk is received from the end device CLI or a timeout has elapsed. Returns the received chunk.\n\t * @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected\n\t * @param predicate A predicate function to test all incoming chunks\n\t */\n\tpublic waitForCLIChunk<T extends CLIChunk>(\n\t\tpredicate: (chunk: CLIChunk) => boolean,\n\t\ttimeout: number,\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst promise = createDeferredPromise<CLIChunk>();\n\t\t\tconst entry: AwaitedCLIChunkEntry = {\n\t\t\t\tpredicate,\n\t\t\t\thandler: (chunk) => promise.resolve(chunk),\n\t\t\t\ttimeout: undefined,\n\t\t\t};\n\t\t\tthis.awaitedCLIChunks.push(entry);\n\t\t\tconst removeEntry = () => {\n\t\t\t\tentry.timeout?.clear();\n\t\t\t\tconst index = this.awaitedCLIChunks.indexOf(entry);\n\t\t\t\tif (index !== -1) this.awaitedCLIChunks.splice(index, 1);\n\t\t\t};\n\t\t\t// When the timeout elapses, remove the wait entry and reject the returned Promise\n\t\t\tentry.timeout = setTimer(() => {\n\t\t\t\tremoveEntry();\n\t\t\t\treject(\n\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t`Received no matching chunk within the provided timeout!`,\n\t\t\t\t\t\tZWaveErrorCodes.Controller_Timeout,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}, timeout);\n\t\t\t// When the promise is resolved, remove the wait entry and resolve the returned Promise\n\t\t\tvoid promise.then((chunk) => {\n\t\t\t\tremoveEntry();\n\t\t\t\tresolve(chunk as T);\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async serialport_onCLIData(data: CLIChunk): Promise<void> {\n\t\t// Check if there is a handler waiting for this chunk\n\t\tfor (const entry of this.awaitedCLIChunks) {\n\t\t\tif (entry.predicate(data)) {\n\t\t\t\t// there is!\n\t\t\t\tentry.handler(data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!this._cli && data.type === CLIChunkType.Prompt) {\n\t\t\t// We just detected the CLI\n\t\t\tthis._controller?.destroy();\n\t\t\tthis._controller = undefined;\n\t\t\tthis._bootloader = undefined;\n\n\t\t\tthis._cli = new EndDeviceCLI(\n\t\t\t\tthis.writeSerial.bind(this),\n\t\t\t\t(timeout) =>\n\t\t\t\t\tthis.waitForCLIChunk(\n\t\t\t\t\t\t(c) => c.type === CLIChunkType.Message,\n\t\t\t\t\t\ttimeout ?? 1000,\n\t\t\t\t\t)\n\t\t\t\t\t\t.then((c) =>\n\t\t\t\t\t\t\t(c as CLIChunk & { type: CLIChunkType.Message })\n\t\t\t\t\t\t\t\t.message\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.catch(() => undefined),\n\t\t\t);\n\n\t\t\tif (!(await this.ensureCLIReady())) {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"Failed to detect available CLI commands\",\n\t\t\t\t\tZWaveErrorCodes.Driver_Failed,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate pollBackgroundRSSITimer: Timer | undefined;\n\tprivate lastBackgroundRSSITimestamp = 0;\n\n\tprivate handleQueueIdleChange(idle: boolean): void {\n\t\tif (!this.ready) return;\n\t\tif (\n\t\t\tthis.controller.isFunctionSupported(FunctionType.GetBackgroundRSSI)\n\t\t) {\n\t\t\t// When the send thread stays idle for 5 seconds, poll the background RSSI, but at most every 30s\n\t\t\tif (idle) {\n\t\t\t\tconst timeout = Math.max(\n\t\t\t\t\t// Wait at least 5s\n\t\t\t\t\t5000,\n\t\t\t\t\t// and up to 30s if we recently queried the RSSI\n\t\t\t\t\t30_000 - (Date.now() - this.lastBackgroundRSSITimestamp),\n\t\t\t\t);\n\t\t\t\tthis.pollBackgroundRSSITimer = setTimer(async () => {\n\t\t\t\t\t// Due to the timeout, the driver might have been destroyed in the meantime\n\t\t\t\t\tif (!this.ready) return;\n\n\t\t\t\t\tthis.lastBackgroundRSSITimestamp = Date.now();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.controller.getBackgroundRSSI();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// ignore errors\n\t\t\t\t\t}\n\t\t\t\t}, timeout).unref();\n\t\t\t} else {\n\t\t\t\tthis.pollBackgroundRSSITimer?.clear();\n\t\t\t\tthis.pollBackgroundRSSITimer = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _powerlevelTestNodeContext: {\n\t\ttestNodeId: number;\n\t\ttimeout: NodeJS.Timeout | undefined;\n\t\tacknowledgedFrames: number;\n\t} | undefined;\n\n\t/**\n\t * @internal\n\t * Begins a powerlevel test for the given node using NOP power frames\n\t */\n\tpublic async sendNOPPowerFrames(\n\t\ttestNodeId: number,\n\t\tpowerlevel: Powerlevel,\n\t\tframeCount: number,\n\t): Promise<number> {\n\t\tif (this._powerlevelTestNodeContext) {\n\t\t\t// Cancel the previous test\n\t\t\tclearTimeout(this._powerlevelTestNodeContext.timeout);\n\t\t\tthis._powerlevelTestNodeContext = undefined;\n\t\t}\n\n\t\tif (frameCount < 1) return 0;\n\n\t\tconst ret = createDeferredPromise<number>();\n\n\t\tconst context = {\n\t\t\ttestNodeId,\n\t\t\tacknowledgedFrames: 0,\n\t\t\t// This is set below after defining sendFrame\n\t\t\ttimeout: undefined as NodeJS.Timeout | undefined,\n\t\t};\n\t\tthis._powerlevelTestNodeContext = context;\n\n\t\t// We're expected to send these pretty quickly (260 frames in 25s for the CTT test)\n\t\tconst interval = 50;\n\n\t\tconst sendFrame = async () => {\n\t\t\tconst result = await this.sendTestFrame(testNodeId, powerlevel);\n\t\t\tif (result === TransmitStatus.OK) {\n\t\t\t\tcontext.acknowledgedFrames++;\n\t\t\t}\n\t\t\tframeCount--;\n\n\t\t\tif (frameCount > 0) {\n\t\t\t\tcontext.timeout = setTimeout(sendFrame, interval);\n\t\t\t} else {\n\t\t\t\tcontext.timeout = undefined;\n\t\t\t\tret.resolve(context.acknowledgedFrames);\n\t\t\t}\n\t\t};\n\n\t\tcontext.timeout = setTimeout(sendFrame, interval);\n\n\t\treturn ret;\n\t}\n\n\t/** Sends a NOP Power frame to the given node and returns the transmit status if the frame was sent */\n\tpublic async sendTestFrame(\n\t\tnodeId: number,\n\t\tpowerlevel: Powerlevel,\n\t): Promise<TransmitStatus | undefined> {\n\t\tconst result = await this.sendMessage<\n\t\t\tMessage & SuccessIndicator\n\t\t>(\n\t\t\tnew SendTestFrameRequest({\n\t\t\t\ttestNodeId: nodeId,\n\t\t\t\tpowerlevel,\n\t\t\t}),\n\t\t);\n\n\t\tif (result instanceof SendTestFrameTransmitReport) {\n\t\t\treturn result.transmitStatus;\n\t\t}\n\t}\n\n\t/**\n\t * @internal\n\t * Returns the status of a potentially ongoing NOP power test\n\t */\n\tpublic getNOPPowerTestStatus(): {\n\t\ttestNodeId: number;\n\t\tinProgress: boolean;\n\t\tacknowledgedFrames: number;\n\t} | undefined {\n\t\tif (this._powerlevelTestNodeContext) {\n\t\t\treturn {\n\t\t\t\tinProgress: !!this._powerlevelTestNodeContext.timeout,\n\t\t\t\ttestNodeId: this._powerlevelTestNodeContext.testNodeId,\n\t\t\t\tacknowledgedFrames:\n\t\t\t\t\tthis._powerlevelTestNodeContext.acknowledgedFrames,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Resets the S2 singlecast encryption state (SPAN) for the given node, which forces\n\t * a re-synchronization on the next communication attempt.\n\t */\n\tpublic resetSPAN(nodeId: number): void {\n\t\tthis.getSecurityManager2(nodeId)?.deleteNonce(nodeId);\n\t}\n\n\t/**\n\t * Resets the S2 singlecast encryption state (SPAN) for all nodes, which forces\n\t * a re-synchronization on the next communication attempt.\n\t */\n\tpublic resetAllSPANs(): void {\n\t\tfor (const nodeId of this.controller.nodes.keys()) {\n\t\t\tthis.resetSPAN(nodeId);\n\t\t}\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["import_Types", "path", "e", "msg", "result", "node"]
7
7
  }