zwave-js 14.3.2 → 14.3.3

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/node/Node.ts"],
4
- "sourcesContent": ["import {\n\tAssociationGroupInfoProfile,\n\ttype CCAPI,\n\tCentralSceneKeys,\n\tClockCommand,\n\tCommandClass,\n\tDeviceResetLocallyCCNotification,\n\tDeviceResetLocallyCommand,\n\tDoorLockMode,\n\tEntryControlDataTypes,\n\ttype FirmwareUpdateCapabilities,\n\tInclusionControllerCCInitiate,\n\tInclusionControllerStep,\n\tIndicatorCCDescriptionGet,\n\tIndicatorCCGet,\n\tIndicatorCCSet,\n\tIndicatorCCSupportedGet,\n\tMultiChannelAssociationCCGet,\n\tMultiChannelAssociationCCRemove,\n\tMultiChannelAssociationCCSet,\n\tMultiChannelAssociationCCSupportedGroupingsGet,\n\tMultiCommandCCCommandEncapsulation,\n\tMultilevelSwitchCommand,\n\ttype PollValueImplementation,\n\tPowerlevel,\n\tPowerlevelTestStatus,\n\tScheduleEntryLockCommand,\n\tSecurity2Command,\n\ttype SetValueAPIOptions,\n\tTimeCCDateGet,\n\tTimeCCTimeGet,\n\tTimeCCTimeOffsetGet,\n\tTimeCommand,\n\tTimeParametersCommand,\n\tUserCodeCCValues,\n\ttype ValueIDProperties,\n\tZWavePlusNodeType,\n\tZWavePlusRoleType,\n\tentryControlEventTypeLabels,\n\tgetEffectiveCCVersion,\n\tgetImplementedVersion,\n\tutils as ccUtils,\n} from \"@zwave-js/cc\";\nimport {\n\tAssociationCCGet,\n\tAssociationCCRemove,\n\tAssociationCCSet,\n\tAssociationCCSpecificGroupGet,\n\tAssociationCCSupportedGroupingsGet,\n\tAssociationCCValues,\n} from \"@zwave-js/cc/AssociationCC\";\nimport {\n\tAssociationGroupInfoCCCommandListGet,\n\tAssociationGroupInfoCCInfoGet,\n\tAssociationGroupInfoCCNameGet,\n} from \"@zwave-js/cc/AssociationGroupInfoCC\";\nimport {\n\tBasicCC,\n\tBasicCCReport,\n\tBasicCCSet,\n\tBasicCCValues,\n} from \"@zwave-js/cc/BasicCC\";\nimport {\n\ttype BinarySwitchCC,\n\tBinarySwitchCCSet,\n\tBinarySwitchCCValues,\n} from \"@zwave-js/cc/BinarySwitchCC\";\nimport {\n\tCentralSceneCCNotification,\n\tCentralSceneCCValues,\n} from \"@zwave-js/cc/CentralSceneCC\";\nimport { ClockCCReport } from \"@zwave-js/cc/ClockCC\";\nimport { DoorLockCCValues } from \"@zwave-js/cc/DoorLockCC\";\nimport { EntryControlCCNotification } from \"@zwave-js/cc/EntryControlCC\";\nimport {\n\tFirmwareUpdateMetaDataCCGet,\n\tFirmwareUpdateMetaDataCCMetaDataGet,\n\tFirmwareUpdateMetaDataCCValues,\n} from \"@zwave-js/cc/FirmwareUpdateMetaDataCC\";\nimport { HailCC } from \"@zwave-js/cc/HailCC\";\nimport { LockCCValues } from \"@zwave-js/cc/LockCC\";\nimport {\n\tManufacturerSpecificCCGet,\n\tManufacturerSpecificCCValues,\n} from \"@zwave-js/cc/ManufacturerSpecificCC\";\nimport {\n\tMultilevelSwitchCC,\n\tMultilevelSwitchCCSet,\n\tMultilevelSwitchCCStartLevelChange,\n\tMultilevelSwitchCCStopLevelChange,\n\tMultilevelSwitchCCValues,\n} from \"@zwave-js/cc/MultilevelSwitchCC\";\nimport { NodeNamingAndLocationCCValues } from \"@zwave-js/cc/NodeNamingCC\";\nimport {\n\tNotificationCCReport,\n\tNotificationCCValues,\n\tgetNotificationEnumBehavior,\n\tgetNotificationStateValueWithEnum,\n\tgetNotificationValueMetadata,\n} from \"@zwave-js/cc/NotificationCC\";\nimport {\n\tPowerlevelCCGet,\n\tPowerlevelCCSet,\n\tPowerlevelCCTestNodeGet,\n\tPowerlevelCCTestNodeReport,\n\tPowerlevelCCTestNodeSet,\n} from \"@zwave-js/cc/PowerlevelCC\";\nimport { SceneActivationCCSet } from \"@zwave-js/cc/SceneActivationCC\";\nimport {\n\tSecurity2CCCommandsSupportedGet,\n\tSecurity2CCMessageEncapsulation,\n\tSecurity2CCNonceGet,\n\tSecurity2CCNonceReport,\n} from \"@zwave-js/cc/Security2CC\";\nimport {\n\tSecurityCCCommandsSupportedGet,\n\tSecurityCCNonceGet,\n\tSecurityCCNonceReport,\n} from \"@zwave-js/cc/SecurityCC\";\nimport {\n\ttype ThermostatModeCC,\n\tThermostatModeCCSet,\n\tThermostatModeCCValues,\n} from \"@zwave-js/cc/ThermostatModeCC\";\nimport {\n\tVersionCCCapabilitiesGet,\n\tVersionCCCommandClassGet,\n\tVersionCCGet,\n\tVersionCCValues,\n} from \"@zwave-js/cc/VersionCC\";\nimport {\n\tWakeUpCCValues,\n\tWakeUpCCWakeUpNotification,\n} from \"@zwave-js/cc/WakeUpCC\";\nimport { ZWavePlusCCGet, ZWavePlusCCValues } from \"@zwave-js/cc/ZWavePlusCC\";\nimport {\n\ttype SetValueResult,\n\tSetValueStatus,\n\tsupervisionResultToSetValueResult,\n} from \"@zwave-js/cc/safe\";\nimport { type DeviceConfig, embeddedDevicesDir } from \"@zwave-js/config\";\nimport {\n\tBasicDeviceClass,\n\tCommandClasses,\n\tDuration,\n\tEncapsulationFlags,\n\ttype MaybeNotKnown,\n\tMessagePriority,\n\tNOT_KNOWN,\n\tNodeType,\n\ttype NodeUpdatePayload,\n\ttype Notification,\n\ttype NotificationState,\n\tProtocolVersion,\n\tProtocols,\n\ttype QuerySecurityClasses,\n\ttype RSSI,\n\tRssiError,\n\tSecurityClass,\n\ttype SendCommandOptions,\n\ttype SetValueOptions,\n\ttype SinglecastCC,\n\tSupervisionStatus,\n\ttype TXReport,\n\ttype TranslatedValueID,\n\tTransmitOptions,\n\ttype ValueDB,\n\ttype ValueID,\n\ttype ValueMetadata,\n\ttype ValueMetadataNumeric,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tZWaveLibraryTypes,\n\tactuatorCCs,\n\tallCCs,\n\tapplicationCCs,\n\tdskToString,\n\tencapsulationCCs,\n\tgetCCName,\n\tgetDSTInfo,\n\tgetNotification,\n\tgetNotificationValue,\n\tisRssiError,\n\tisSupervisionResult,\n\tisTransmissionError,\n\tisUnsupervisedOrSucceeded,\n\tisZWaveError,\n\tnonApplicationCCs,\n\tnormalizeValueID,\n\tsecurityClassIsLongRange,\n\tsecurityClassIsS2,\n\tsecurityClassOrder,\n\tsensorCCs,\n\tserializeCacheValue,\n\tsupervisedCommandFailed,\n\tsupervisedCommandSucceeded,\n\ttopologicalSort,\n\tvalueIdToString,\n} from \"@zwave-js/core\";\nimport { FunctionType, type Message } from \"@zwave-js/serial\";\nimport {\n\ttype ApplicationUpdateRequest,\n\tApplicationUpdateRequestNodeInfoReceived,\n\tApplicationUpdateRequestNodeInfoRequestFailed,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetNodeProtocolInfoRequest,\n\ttype GetNodeProtocolInfoResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tRequestNodeInfoRequest,\n\tRequestNodeInfoResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport { containsCC } from \"@zwave-js/serial/serialapi\";\nimport {\n\tBytes,\n\tMixin,\n\ttype TypedEventEmitter,\n\tcloneDeep,\n\tdiscreteLinearSearch,\n\tformatId,\n\tgetEnumMemberName,\n\tgetErrorMessage,\n\tisUint8Array,\n\tnoop,\n\tpick,\n\tstringify,\n} from \"@zwave-js/shared\";\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 { EventEmitter } from \"node:events\";\nimport path from \"node:path\";\nimport semverParse from \"semver/functions/parse.js\";\nimport { RemoveNodeReason } from \"../controller/Inclusion.js\";\nimport { determineNIF } from \"../controller/NodeInformationFrame.js\";\nimport { type Driver, libVersion } from \"../driver/Driver.js\";\nimport { cacheKeys } from \"../driver/NetworkCache.js\";\nimport type { StatisticsEventCallbacksWithSelf } from \"../driver/Statistics.js\";\nimport type { Transaction } from \"../driver/Transaction.js\";\nimport { DeviceClass } from \"./DeviceClass.js\";\nimport { type NodeDump, type ValueDump } from \"./Dump.js\";\nimport { type Endpoint } from \"./Endpoint.js\";\nimport {\n\tformatLifelineHealthCheckSummary,\n\tformatRouteHealthCheckSummary,\n\thealthCheckTestFrameCount,\n} from \"./HealthCheck.js\";\nimport {\n\ttype NodeStatistics,\n\tNodeStatisticsHost,\n\ttype RouteStatistics,\n\trouteStatisticsEquals,\n} from \"./NodeStatistics.js\";\nimport {\n\ttype DateAndTime,\n\ttype LifelineHealthCheckResult,\n\ttype LifelineHealthCheckSummary,\n\tLinkReliabilityCheckMode,\n\ttype LinkReliabilityCheckOptions,\n\ttype LinkReliabilityCheckResult,\n\ttype RefreshInfoOptions,\n\ttype RouteHealthCheckResult,\n\ttype RouteHealthCheckSummary,\n\ttype ZWaveNodeEventCallbacks,\n} from \"./_Types.js\";\nimport { InterviewStage, NodeStatus } from \"./_Types.js\";\nimport { ZWaveNodeMixins } from \"./mixins/index.js\";\nimport * as nodeUtils from \"./utils.js\";\n\nconst MAX_ASSOCIATIONS = 1;\n\ntype AllNodeEvents =\n\t& ZWaveNodeEventCallbacks\n\t& StatisticsEventCallbacksWithSelf<ZWaveNode, NodeStatistics>;\n\nexport interface ZWaveNode\n\textends TypedEventEmitter<AllNodeEvents>, NodeStatisticsHost\n{}\n\n/**\n * A ZWaveNode represents a node in a Z-Wave network. It is also an instance\n * of its root endpoint (index 0)\n */\n@Mixin([EventEmitter, NodeStatisticsHost])\nexport class ZWaveNode extends ZWaveNodeMixins implements QuerySecurityClasses {\n\tpublic constructor(\n\t\tid: number,\n\t\tdriver: Driver,\n\t\tdeviceClass?: DeviceClass,\n\t\tsupportedCCs: CommandClasses[] = [],\n\t\tcontrolledCCs: CommandClasses[] = [],\n\t\tvalueDB?: ValueDB,\n\t) {\n\t\tsuper(\n\t\t\tid,\n\t\t\tdriver,\n\t\t\t// Define this node's intrinsic endpoint as the root device (0)\n\t\t\t0,\n\t\t\tdeviceClass,\n\t\t\tsupportedCCs,\n\t\t\tvalueDB,\n\t\t);\n\n\t\t// Add optional controlled CCs - endpoints don't have this\n\t\tfor (const cc of controlledCCs) this.addCC(cc, { isControlled: true });\n\t}\n\n\t/**\n\t * Cleans up all resources used by this node\n\t */\n\tpublic destroy(): void {\n\t\t// Stop all state machines\n\t\tthis.statusMachine.stop();\n\t\tthis.readyMachine.stop();\n\n\t\t// Remove all timeouts\n\t\tfor (\n\t\t\tconst timeout of [\n\t\t\t\tthis.centralSceneKeyHeldDownContext?.timeout,\n\t\t\t\t...this.notificationIdleTimeouts.values(),\n\t\t\t]\n\t\t) {\n\t\t\tif (timeout) clearTimeout(timeout);\n\t\t}\n\n\t\t// Remove all event handlers\n\t\tthis.removeAllListeners();\n\n\t\t// Clear all scheduled polls that would interfere with the interview\n\t\tthis.cancelAllScheduledPolls();\n\t}\n\n\t/**\n\t * The device specific key (DSK) of this node in binary format.\n\t * This is only set if included with Security S2.\n\t */\n\tpublic get dsk(): Uint8Array | undefined {\n\t\treturn this.driver.cacheGet(cacheKeys.node(this.id).dsk);\n\t}\n\t/** @internal */\n\tpublic set dsk(value: Uint8Array | undefined) {\n\t\tconst cacheKey = cacheKeys.node(this.id).dsk;\n\t\tthis.driver.cacheSet(cacheKey, value);\n\t}\n\n\tpublic get manufacturerId(): MaybeNotKnown<number> {\n\t\treturn this.getValue(ManufacturerSpecificCCValues.manufacturerId.id);\n\t}\n\n\tpublic get productId(): MaybeNotKnown<number> {\n\t\treturn this.getValue(ManufacturerSpecificCCValues.productId.id);\n\t}\n\n\tpublic get productType(): MaybeNotKnown<number> {\n\t\treturn this.getValue(ManufacturerSpecificCCValues.productType.id);\n\t}\n\n\tpublic get firmwareVersion(): MaybeNotKnown<string> {\n\t\t// On supporting nodes, use the applicationVersion, which MUST be\n\t\t// same as the first (main) firmware, plus the patch version.\n\t\tconst firmware0Version = this.getValue<string[]>(\n\t\t\tVersionCCValues.firmwareVersions.id,\n\t\t)?.[0];\n\t\tconst applicationVersion = this.getValue<string>(\n\t\t\tVersionCCValues.applicationVersion.id,\n\t\t);\n\n\t\tlet ret = firmware0Version;\n\t\tif (applicationVersion) {\n\t\t\t// If the application version is set, we cannot blindly trust that it is the firmware version.\n\t\t\t// Some nodes incorrectly set this field to the Z-Wave Application Framework API Version\n\t\t\tif (!ret || applicationVersion.startsWith(`${ret}.`)) {\n\t\t\t\tret = applicationVersion;\n\t\t\t}\n\t\t}\n\n\t\t// Special case for the official 700 series firmwares which are aligned with the SDK version\n\t\t// We want to work with the full x.y.z firmware version here.\n\t\tif (ret && this.isControllerNode) {\n\t\t\tconst sdkVersion = this.sdkVersion;\n\t\t\tif (sdkVersion && sdkVersion.startsWith(`${ret}.`)) {\n\t\t\t\treturn sdkVersion;\n\t\t\t}\n\t\t}\n\t\t// For all others, just return the simple x.y firmware version\n\t\treturn ret;\n\t}\n\n\tpublic get hardwareVersion(): MaybeNotKnown<number> {\n\t\treturn this.getValue(VersionCCValues.hardwareVersion.id);\n\t}\n\n\tpublic get sdkVersion(): MaybeNotKnown<string> {\n\t\treturn this.getValue(VersionCCValues.sdkVersion.id);\n\t}\n\n\tpublic get zwavePlusVersion(): MaybeNotKnown<number> {\n\t\treturn this.getValue(ZWavePlusCCValues.zwavePlusVersion.id);\n\t}\n\n\tpublic get zwavePlusNodeType(): MaybeNotKnown<ZWavePlusNodeType> {\n\t\treturn this.getValue(ZWavePlusCCValues.nodeType.id);\n\t}\n\n\tpublic get zwavePlusRoleType(): MaybeNotKnown<ZWavePlusRoleType> {\n\t\treturn this.getValue(ZWavePlusCCValues.roleType.id);\n\t}\n\n\tpublic get supportsWakeUpOnDemand(): MaybeNotKnown<boolean> {\n\t\treturn this.getValue(WakeUpCCValues.wakeUpOnDemandSupported.id);\n\t}\n\n\t/**\n\t * The user-defined name of this node. Uses the value reported by `Node Naming and Location CC` if it exists.\n\t *\n\t * **Note:** Setting this value only updates the name locally. To permanently change the name of the node, use\n\t * the `commandClasses` API.\n\t */\n\tpublic get name(): MaybeNotKnown<string> {\n\t\treturn this.getValue(NodeNamingAndLocationCCValues.name.id);\n\t}\n\tpublic set name(value: string | undefined) {\n\t\tif (value != undefined) {\n\t\t\tthis._valueDB.setValue(\n\t\t\t\tNodeNamingAndLocationCCValues.name.id,\n\t\t\t\tvalue,\n\t\t\t);\n\t\t} else {\n\t\t\tthis._valueDB.removeValue(NodeNamingAndLocationCCValues.name.id);\n\t\t}\n\t}\n\n\t/**\n\t * The user-defined location of this node. Uses the value reported by `Node Naming and Location CC` if it exists.\n\t *\n\t * **Note:** Setting this value only updates the location locally. To permanently change the location of the node, use\n\t * the `commandClasses` API.\n\t */\n\tpublic get location(): MaybeNotKnown<string> {\n\t\treturn this.getValue(NodeNamingAndLocationCCValues.location.id);\n\t}\n\tpublic set location(value: string | undefined) {\n\t\tif (value != undefined) {\n\t\t\tthis._valueDB.setValue(\n\t\t\t\tNodeNamingAndLocationCCValues.location.id,\n\t\t\t\tvalue,\n\t\t\t);\n\t\t} else {\n\t\t\tthis._valueDB.removeValue(\n\t\t\t\tNodeNamingAndLocationCCValues.location.id,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Whether a SUC return route was configured for this node */\n\tpublic get hasSUCReturnRoute(): boolean {\n\t\treturn !!this.driver.cacheGet(\n\t\t\tcacheKeys.node(this.id).hasSUCReturnRoute,\n\t\t);\n\t}\n\tpublic set hasSUCReturnRoute(value: boolean) {\n\t\tthis.driver.cacheSet(cacheKeys.node(this.id).hasSUCReturnRoute, value);\n\t}\n\n\tprivate _deviceConfig: DeviceConfig | undefined;\n\t/**\n\t * Contains additional information about this node, loaded from a config file\n\t */\n\tpublic get deviceConfig(): DeviceConfig | undefined {\n\t\treturn this._deviceConfig;\n\t}\n\n\tpublic get label(): string | undefined {\n\t\treturn this._deviceConfig?.label;\n\t}\n\n\tpublic get deviceDatabaseUrl(): MaybeNotKnown<string> {\n\t\tif (\n\t\t\tthis.manufacturerId != undefined\n\t\t\t&& this.productType != undefined\n\t\t\t&& this.productId != undefined\n\t\t) {\n\t\t\tconst manufacturerId = formatId(this.manufacturerId);\n\t\t\tconst productType = formatId(this.productType);\n\t\t\tconst productId = formatId(this.productId);\n\t\t\tconst firmwareVersion = this.firmwareVersion || \"0.0\";\n\t\t\treturn `https://devices.zwave-js.io/?jumpTo=${manufacturerId}:${productType}:${productId}:${firmwareVersion}`;\n\t\t}\n\t}\n\n\t/** The last time a message was received from this node */\n\tpublic get lastSeen(): MaybeNotKnown<Date> {\n\t\treturn this.driver.cacheGet(cacheKeys.node(this.id).lastSeen);\n\t}\n\t/** @internal */\n\tpublic set lastSeen(value: MaybeNotKnown<Date>) {\n\t\tthis.driver.cacheSet(cacheKeys.node(this.id).lastSeen, value);\n\t\t// Also update statistics\n\t\tthis.updateStatistics((cur) => ({\n\t\t\t...cur,\n\t\t\tlastSeen: value,\n\t\t}));\n\t}\n\n\t/**\n\t * The default volume level to be used for activating a Sound Switch.\n\t * Can be overridden by command-specific options.\n\t */\n\tpublic get defaultVolume(): number | undefined {\n\t\treturn this.driver.cacheGet(cacheKeys.node(this.id).defaultVolume);\n\t}\n\n\tpublic set defaultVolume(value: number | undefined) {\n\t\tif (value != undefined && (value < 0 || value > 100)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The default volume must be a number between 0 and 100!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\t\tthis.driver.cacheSet(cacheKeys.node(this.id).defaultVolume, value);\n\t}\n\n\t/**\n\t * The default transition duration to be used for transitions like dimming lights or activating scenes.\n\t * Can be overridden by command-specific options.\n\t */\n\tpublic get defaultTransitionDuration(): string | undefined {\n\t\treturn this.driver.cacheGet(\n\t\t\tcacheKeys.node(this.id).defaultTransitionDuration,\n\t\t);\n\t}\n\n\tpublic set defaultTransitionDuration(value: string | Duration | undefined) {\n\t\t// Normalize to strings\n\t\tif (typeof value === \"string\") value = Duration.from(value);\n\t\tif (value instanceof Duration) value = value.toString();\n\n\t\tthis.driver.cacheSet(\n\t\t\tcacheKeys.node(this.id).defaultTransitionDuration,\n\t\t\tvalue,\n\t\t);\n\t}\n\n\t/**\n\t * @internal\n\t * The hash of the device config that was applied during the last interview.\n\t */\n\tpublic get cachedDeviceConfigHash(): Uint8Array | undefined {\n\t\treturn this.driver.cacheGet(cacheKeys.node(this.id).deviceConfigHash);\n\t}\n\n\tprivate set cachedDeviceConfigHash(value: Uint8Array | undefined) {\n\t\tthis.driver.cacheSet(cacheKeys.node(this.id).deviceConfigHash, value);\n\t}\n\n\tprivate _currentDeviceConfigHash: Uint8Array | undefined;\n\t/**\n\t * @internal\n\t * The hash of the currently used device config\n\t */\n\tpublic get currentDeviceConfigHash(): Uint8Array | undefined {\n\t\treturn this._currentDeviceConfigHash;\n\t}\n\n\t/** Returns a list of all value names that are defined on all endpoints of this node */\n\tpublic getDefinedValueIDs(): TranslatedValueID[] {\n\t\treturn nodeUtils.getDefinedValueIDs(this.driver, this);\n\t}\n\n\t/**\n\t * Updates a value for a given property of a given CommandClass on the node.\n\t * This will communicate with the node!\n\t */\n\tpublic async setValue(\n\t\tvalueId: ValueID,\n\t\tvalue: unknown,\n\t\toptions?: SetValueAPIOptions,\n\t): Promise<SetValueResult> {\n\t\t// Ensure we're dealing with a valid value ID, with no extra properties\n\t\tvalueId = normalizeValueID(valueId);\n\n\t\tconst loglevel = this.driver.getLogConfig().level;\n\n\t\t// Try to retrieve the corresponding CC API\n\t\ttry {\n\t\t\t// Access the CC API by name\n\t\t\tconst endpointInstance = this.getEndpoint(valueId.endpoint || 0);\n\t\t\tif (!endpointInstance) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: SetValueStatus.EndpointNotFound,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Endpoint ${valueId.endpoint} does not exist on Node ${this.id}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tlet api = (endpointInstance.commandClasses as any)[\n\t\t\t\tvalueId.commandClass\n\t\t\t] as CCAPI;\n\t\t\t// Check if the setValue method is implemented\n\t\t\tif (!api.setValue) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: SetValueStatus.NotImplemented,\n\t\t\t\t\tmessage: `The ${\n\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\tvalueId.commandClass,\n\t\t\t\t\t\t)\n\t\t\t\t\t} CC does not support setting values`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (loglevel === \"silly\") {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: valueId.endpoint,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`[setValue] calling SET_VALUE API ${api.constructor.name}:\n property: ${valueId.property}\n property key: ${valueId.propertyKey}\n optimistic: ${api.isSetValueOptimistic(valueId)}`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Merge the provided value change options with the defaults\n\t\t\toptions ??= {};\n\t\t\toptions.transitionDuration ??= this.defaultTransitionDuration;\n\t\t\toptions.volume ??= this.defaultVolume;\n\n\t\t\tconst valueIdProps: ValueIDProperties = {\n\t\t\t\tproperty: valueId.property,\n\t\t\t\tpropertyKey: valueId.propertyKey,\n\t\t\t};\n\n\t\t\tconst hooks = api.setValueHooks?.(valueIdProps, value, options);\n\n\t\t\tif (hooks?.supervisionDelayedUpdates) {\n\t\t\t\tapi = api.withOptions({\n\t\t\t\t\trequestStatusUpdates: true,\n\t\t\t\t\tonUpdate: async (update) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (update.status === SupervisionStatus.Success) {\n\t\t\t\t\t\t\t\tawait hooks.supervisionOnSuccess();\n\t\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\t\tupdate.status === SupervisionStatus.Fail\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tawait hooks.supervisionOnFailure();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// TODO: Log error?\n\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// If the caller wants progress updates, they shall have them\n\t\t\tif (typeof options.onProgress === \"function\") {\n\t\t\t\tapi = api.withOptions({\n\t\t\t\t\tonProgress: options.onProgress,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// And call it\n\t\t\tconst result = await api.setValue!.call(\n\t\t\t\tapi,\n\t\t\t\tvalueIdProps,\n\t\t\t\tvalue,\n\t\t\t\toptions,\n\t\t\t);\n\n\t\t\tif (loglevel === \"silly\") {\n\t\t\t\tlet message =\n\t\t\t\t\t`[setValue] result of SET_VALUE API call for ${api.constructor.name}:`;\n\t\t\t\tif (result) {\n\t\t\t\t\tif (isSupervisionResult(result)) {\n\t\t\t\t\t\tmessage += ` (SupervisionResult)\n status: ${getEnumMemberName(SupervisionStatus, result.status)}`;\n\t\t\t\t\t\tif (result.remainingDuration) {\n\t\t\t\t\t\t\tmessage += `\n duration: ${result.remainingDuration.toString()}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmessage += \" (other) \"\n\t\t\t\t\t\t\t+ JSON.stringify(result, null, 2);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tmessage += \" undefined\";\n\t\t\t\t}\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: valueId.endpoint,\n\t\t\t\t\tmessage,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Remember the new value for the value we just set, if...\n\t\t\t// ... the call did not throw (assume that the call was successful)\n\t\t\t// ... the call was supervised and successful\n\t\t\tif (\n\t\t\t\tapi.isSetValueOptimistic(valueId)\n\t\t\t\t&& isUnsupervisedOrSucceeded(result)\n\t\t\t) {\n\t\t\t\tconst emitEvent = !!result\n\t\t\t\t\t|| !!this.driver.options.emitValueUpdateAfterSetValue;\n\n\t\t\t\tif (loglevel === \"silly\") {\n\t\t\t\t\tconst message = emitEvent\n\t\t\t\t\t\t? \"updating value with event\"\n\t\t\t\t\t\t: \"updating value without event\";\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: valueId.endpoint,\n\t\t\t\t\t\tmessage: `[setValue] ${message}`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst options: SetValueOptions = {};\n\t\t\t\t// We need to emit an event if applications opted in, or if this was a supervised call\n\t\t\t\t// because in this case there won't be a verification query which would result in an update\n\t\t\t\tif (emitEvent) {\n\t\t\t\t\toptions.source = \"driver\";\n\t\t\t\t} else {\n\t\t\t\t\toptions.noEvent = true;\n\t\t\t\t}\n\t\t\t\t// Only update the timestamp of the value for successful supervised commands. Otherwise we don't know\n\t\t\t\t// if the command was actually executed. If it wasn't, we'd have a wrong timestamp.\n\t\t\t\toptions.updateTimestamp = supervisedCommandSucceeded(result);\n\n\t\t\t\tthis._valueDB.setValue(valueId, value, options);\n\t\t\t} else if (loglevel === \"silly\") {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: valueId.endpoint,\n\t\t\t\t\tmessage: `[setValue] not updating value`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Depending on the settings of the SET_VALUE implementation, we may have to\n\t\t\t// optimistically update a different value and/or verify the changes\n\t\t\tif (hooks) {\n\t\t\t\tconst supervisedAndSuccessful = isSupervisionResult(result)\n\t\t\t\t\t&& result.status === SupervisionStatus.Success;\n\n\t\t\t\tconst shouldUpdateOptimistically =\n\t\t\t\t\tapi.isSetValueOptimistic(valueId)\n\t\t\t\t\t// For successful supervised commands, we know that an optimistic update is ok\n\t\t\t\t\t&& (supervisedAndSuccessful\n\t\t\t\t\t\t// For unsupervised commands that did not fail, we let the applciation decide whether\n\t\t\t\t\t\t// to update related value optimistically\n\t\t\t\t\t\t|| (!this.driver.options.disableOptimisticValueUpdate\n\t\t\t\t\t\t\t&& result == undefined));\n\n\t\t\t\t// The actual API implementation handles additional optimistic updates\n\t\t\t\tif (shouldUpdateOptimistically) {\n\t\t\t\t\thooks.optimisticallyUpdateRelatedValues?.(\n\t\t\t\t\t\tsupervisedAndSuccessful,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Verify the current value after a delay, unless...\n\t\t\t\t// ...the command was supervised and successful\n\t\t\t\t// ...and the CC API decides not to verify anyways\n\t\t\t\tif (\n\t\t\t\t\t!supervisedCommandSucceeded(result)\n\t\t\t\t\t|| hooks.forceVerifyChanges?.()\n\t\t\t\t) {\n\t\t\t\t\t// Let the CC API implementation handle the verification.\n\t\t\t\t\t// It may still decide not to do it.\n\t\t\t\t\tawait hooks.verifyChanges?.();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn supervisionResultToSetValueResult(result);\n\t\t} catch (e) {\n\t\t\t// Define which errors during setValue are expected and won't throw an error\n\t\t\tif (isZWaveError(e)) {\n\t\t\t\tlet result: SetValueResult | undefined;\n\t\t\t\tswitch (e.code) {\n\t\t\t\t\t// This CC or API is not implemented\n\t\t\t\t\tcase ZWaveErrorCodes.CC_NotImplemented:\n\t\t\t\t\tcase ZWaveErrorCodes.CC_NoAPI:\n\t\t\t\t\t\tresult = {\n\t\t\t\t\t\t\tstatus: SetValueStatus.NotImplemented,\n\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t// A user tried to set an invalid value\n\t\t\t\t\tcase ZWaveErrorCodes.Argument_Invalid:\n\t\t\t\t\t\tresult = {\n\t\t\t\t\t\t\tstatus: SetValueStatus.InvalidValue,\n\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (loglevel === \"silly\") {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: valueId.endpoint,\n\t\t\t\t\t\tmessage: `[setValue] raised ZWaveError (${\n\t\t\t\t\t\t\t!!result ? \"handled\" : \"not handled\"\n\t\t\t\t\t\t}, code ${\n\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\tZWaveErrorCodes,\n\t\t\t\t\t\t\t\te.code,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}): ${e.message}`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (result) return result;\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Requests a value for a given property of a given CommandClass by polling the node.\n\t * **Warning:** Some value IDs share a command, so make sure not to blindly call this for every property\n\t */\n\tpublic pollValue<T = unknown>(\n\t\tvalueId: ValueID,\n\t\tsendCommandOptions: SendCommandOptions = {},\n\t): Promise<MaybeNotKnown<T>> {\n\t\t// Ensure we're dealing with a valid value ID, with no extra properties\n\t\tvalueId = normalizeValueID(valueId);\n\n\t\t// Try to retrieve the corresponding CC API\n\t\tconst endpointInstance = this.getEndpoint(valueId.endpoint || 0);\n\t\tif (!endpointInstance) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Endpoint ${valueId.endpoint} does not exist on Node ${this.id}`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tconst api = (\n\t\t\t(endpointInstance.commandClasses as any)[\n\t\t\t\tvalueId.commandClass\n\t\t\t] as CCAPI\n\t\t).withOptions({\n\t\t\t// We do not want to delay more important communication by polling, so give it\n\t\t\t// the lowest priority and don't retry unless overwritten by the options\n\t\t\tmaxSendAttempts: 1,\n\t\t\tpriority: MessagePriority.Poll,\n\t\t\t...sendCommandOptions,\n\t\t});\n\n\t\t// Check if the pollValue method is implemented\n\t\tif (!api.pollValue) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The pollValue API is not implemented for CC ${\n\t\t\t\t\tgetCCName(\n\t\t\t\t\t\tvalueId.commandClass,\n\t\t\t\t\t)\n\t\t\t\t}!`,\n\t\t\t\tZWaveErrorCodes.CC_NoAPI,\n\t\t\t);\n\t\t}\n\n\t\t// And call it\n\t\treturn (api.pollValue as PollValueImplementation<T>).call(api, {\n\t\t\tproperty: valueId.property,\n\t\t\tpropertyKey: valueId.propertyKey,\n\t\t});\n\t}\n\n\tprivate _interviewAttempts: number = 0;\n\t/** How many attempts to interview this node have already been made */\n\tpublic get interviewAttempts(): number {\n\t\treturn this._interviewAttempts;\n\t}\n\n\tprivate _hasEmittedNoS2NetworkKeyError: boolean = false;\n\tprivate _hasEmittedNoS0NetworkKeyError: boolean = false;\n\n\t/**\n\t * Starts or resumes a deferred initial interview of this node.\n\t *\n\t * **WARNING:** This is only allowed when the initial interview was deferred using the\n\t * `interview.disableOnNodeAdded` option. Otherwise, this method will throw an error.\n\t *\n\t * **NOTE:** It is advised to NOT await this method as it can take a very long time (minutes to hours)!\n\t */\n\tpublic async interview(): Promise<void> {\n\t\t// The initial interview of the controller node is always done\n\t\t// and cannot be deferred.\n\t\tif (this.isControllerNode) return;\n\n\t\tif (!this.driver.options.interview?.disableOnNodeAdded) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Calling ZWaveNode.interview() is not allowed because automatic node interviews are enabled. Wait for the driver to interview the node or use ZWaveNode.refreshInfo() to re-interview a node.`,\n\t\t\t\tZWaveErrorCodes.Driver_FeatureDisabled,\n\t\t\t);\n\t\t}\n\n\t\treturn this.driver.interviewNodeInternal(this);\n\t}\n\n\tprivate _refreshInfoPending: boolean = false;\n\n\t/**\n\t * Resets all information about this node and forces a fresh interview.\n\t * **Note:** This does nothing for the controller node.\n\t *\n\t * **WARNING:** Take care NOT to call this method when the node is already being interviewed.\n\t * Otherwise the node information may become inconsistent.\n\t */\n\tpublic async refreshInfo(options: RefreshInfoOptions = {}): Promise<void> {\n\t\t// It does not make sense to re-interview the controller. All important information is queried\n\t\t// directly via the serial API\n\t\tif (this.isControllerNode) return;\n\n\t\t// The driver does deduplicate re-interview requests, but only at the end of this method.\n\t\t// Without blocking here, many re-interview tasks for sleeping nodes may be queued, leading to parallel interviews\n\t\tif (this._refreshInfoPending) return;\n\t\tthis._refreshInfoPending = true;\n\n\t\tconst { resetSecurityClasses = false, waitForWakeup = true } = options;\n\t\t// Unless desired, don't forget the information about sleeping nodes immediately, so they continue to function\n\t\tlet didWakeUp = false;\n\t\tif (\n\t\t\twaitForWakeup\n\t\t\t&& this.canSleep\n\t\t\t&& this.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"Re-interview scheduled, waiting for node to wake up...\",\n\t\t\t);\n\t\t\tdidWakeUp = await this.waitForWakeup()\n\t\t\t\t.then(() => true)\n\t\t\t\t.catch(() => false);\n\t\t}\n\n\t\t// preserve the node name and location, since they might not be stored on the node\n\t\tconst name = this.name;\n\t\tconst location = this.location;\n\n\t\t// Preserve user codes if they aren't queried during the interview\n\t\tconst preservedValues: (ValueID & { value: unknown })[] = [];\n\t\tconst preservedMetadata: (ValueID & { metadata: ValueMetadata })[] = [];\n\t\tif (\n\t\t\tthis.supportsCC(CommandClasses[\"User Code\"])\n\t\t\t&& !this.driver.options.interview.queryAllUserCodes\n\t\t) {\n\t\t\tconst mustBackup = (v: ValueID) =>\n\t\t\t\tUserCodeCCValues.userCode.is(v)\n\t\t\t\t|| UserCodeCCValues.userIdStatus.is(v)\n\t\t\t\t|| UserCodeCCValues.userCodeChecksum.is(v);\n\n\t\t\tconst values = this.valueDB\n\t\t\t\t.getValues(CommandClasses[\"User Code\"])\n\t\t\t\t.filter(mustBackup);\n\t\t\tpreservedValues.push(...values);\n\n\t\t\tconst meta = this.valueDB\n\t\t\t\t.getAllMetadata(CommandClasses[\"User Code\"])\n\t\t\t\t.filter(mustBackup);\n\t\t\tpreservedMetadata.push(...meta);\n\t\t}\n\n\t\t// Force a new detection of security classes if desired\n\t\tif (resetSecurityClasses) this.securityClasses.clear();\n\n\t\tthis._interviewAttempts = 0;\n\t\tthis.interviewStage = InterviewStage.None;\n\t\tthis.ready = false;\n\t\tthis.deviceClass = undefined;\n\t\tthis.isListening = undefined;\n\t\tthis.isFrequentListening = undefined;\n\t\tthis.isRouting = undefined;\n\t\tthis.supportedDataRates = undefined;\n\t\tthis.protocolVersion = undefined;\n\t\tthis.nodeType = undefined;\n\t\tthis.supportsSecurity = undefined;\n\t\tthis.supportsBeaming = undefined;\n\t\tthis._deviceConfig = undefined;\n\t\tthis._currentDeviceConfigHash = undefined;\n\t\tthis.cachedDeviceConfigHash = undefined;\n\t\tthis._hasEmittedNoS0NetworkKeyError = false;\n\t\tthis._hasEmittedNoS2NetworkKeyError = false;\n\t\tfor (const ep of this.getAllEndpoints()) {\n\t\t\tep[\"reset\"]();\n\t\t}\n\t\tthis._valueDB.clear({ noEvent: true });\n\t\tthis._endpointInstances.clear();\n\t\tsuper.reset();\n\n\t\t// Restart all state machines\n\t\tthis.readyMachine.restart();\n\t\tthis.statusMachine.restart();\n\n\t\t// Remove queued polls that would interfere with the interview\n\t\tthis.cancelAllScheduledPolls();\n\n\t\t// Restore the previously saved name/location\n\t\tif (name != undefined) this.name = name;\n\t\tif (location != undefined) this.location = location;\n\n\t\t// And preserved values/metadata\n\t\tfor (const { value, ...valueId } of preservedValues) {\n\t\t\tthis.valueDB.setValue(valueId, value, { noEvent: true });\n\t\t}\n\t\tfor (const { metadata, ...valueId } of preservedMetadata) {\n\t\t\tthis.valueDB.setMetadata(valueId, metadata, { noEvent: true });\n\t\t}\n\n\t\t// Don't keep the node awake after the interview\n\t\tthis.keepAwake = false;\n\n\t\t// If we did wait for the wakeup, mark the node as awake again so it does not\n\t\t// get considered asleep after querying protocol info.\n\t\tif (didWakeUp) this.markAsAwake();\n\n\t\tvoid this.driver.interviewNodeInternal(this);\n\t\tthis._refreshInfoPending = false;\n\t}\n\n\t/**\n\t * @internal\n\t * Interviews this node. Returns true when it succeeded, false otherwise\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 interviewInternal(): Promise<boolean> {\n\t\tif (this.interviewStage === InterviewStage.Complete) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`skipping interview because it is already completed`,\n\t\t\t);\n\t\t\treturn true;\n\t\t} else {\n\t\t\tthis.driver.controllerLog.interviewStart(this);\n\t\t}\n\n\t\t// Remember that we tried to interview this node\n\t\tthis._interviewAttempts++;\n\n\t\t// Wrapper around interview methods to return false in case of a communication error\n\t\t// This way the single methods don't all need to have the same error handler\n\t\tconst tryInterviewStage = async (\n\t\t\tmethod: () => Promise<void>,\n\t\t): Promise<boolean> => {\n\t\t\ttry {\n\t\t\t\tawait method();\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tif (isTransmissionError(e)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t};\n\n\t\t// The interview is done in several stages. At each point, the interview process might be aborted\n\t\t// due to a stage failing. The reached stage is saved, so we can continue it later without\n\t\t// repeating stages unnecessarily\n\n\t\tif (this.interviewStage === InterviewStage.None) {\n\t\t\t// do a full interview starting with the protocol info\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`new node, doing a full interview...`,\n\t\t\t);\n\t\t\tthis.emit(\"interview started\", this);\n\t\t\tawait this.queryProtocolInfo();\n\t\t}\n\n\t\tif (!this.isControllerNode) {\n\t\t\tif (\n\t\t\t\t(this.isListening || this.isFrequentListening)\n\t\t\t\t&& this.status !== NodeStatus.Alive\n\t\t\t) {\n\t\t\t\t// Ping non-sleeping nodes to determine their status\n\t\t\t\tif (!await this.ping()) {\n\t\t\t\t\t// Not alive, abort the interview\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.interviewStage === InterviewStage.ProtocolInfo) {\n\t\t\t\tif (\n\t\t\t\t\t!(await tryInterviewStage(() => this.interviewNodeInfo()))\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// At this point the basic interview of new nodes is done. Start here when re-interviewing known nodes\n\t\t\t// to get updated information about command classes\n\t\t\tif (this.interviewStage === InterviewStage.NodeInfo) {\n\t\t\t\t// Only advance the interview if it was completed, otherwise abort\n\t\t\t\tif (await this.interviewCCs()) {\n\t\t\t\t\tthis.setInterviewStage(InterviewStage.CommandClasses);\n\t\t\t\t} else {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\t(this.isControllerNode\n\t\t\t\t&& this.interviewStage === InterviewStage.ProtocolInfo)\n\t\t\t|| (!this.isControllerNode\n\t\t\t\t&& this.interviewStage === InterviewStage.CommandClasses)\n\t\t) {\n\t\t\t// Load a config file for this node if it exists and overwrite the previously reported information\n\t\t\tawait this.overwriteConfig();\n\t\t}\n\n\t\t// Remember the state of the device config that is used for this node\n\t\tthis.cachedDeviceConfigHash = await this._deviceConfig?.getHash();\n\n\t\tthis.setInterviewStage(InterviewStage.Complete);\n\t\tthis.readyMachine.send(\"INTERVIEW_DONE\");\n\n\t\t// Tell listeners that the interview is completed\n\t\t// The driver will then send this node to sleep\n\t\tthis.emit(\"interview completed\", this);\n\t\treturn true;\n\t}\n\n\t/** Updates this node's interview stage and saves to cache when appropriate */\n\tprivate setInterviewStage(completedStage: InterviewStage): void {\n\t\tthis.interviewStage = completedStage;\n\t\tthis.emit(\n\t\t\t\"interview stage completed\",\n\t\t\tthis,\n\t\t\tgetEnumMemberName(InterviewStage, completedStage),\n\t\t);\n\t\tthis.driver.controllerLog.interviewStage(this);\n\t}\n\n\t/** Step #1 of the node interview */\n\tprotected async queryProtocolInfo(): Promise<void> {\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: \"querying protocol info...\",\n\t\t\tdirection: \"outbound\",\n\t\t});\n\t\t// The GetNodeProtocolInfoRequest needs to know the node ID to distinguish\n\t\t// between ZWLR and ZW classic. We store it on the driver's context, so it\n\t\t// can be retrieved when needed.\n\t\tthis.driver.requestStorage.set(FunctionType.GetNodeProtocolInfo, {\n\t\t\tnodeId: this.id,\n\t\t});\n\t\tconst resp = await this.driver.sendMessage<GetNodeProtocolInfoResponse>(\n\t\t\tnew GetNodeProtocolInfoRequest({\n\t\t\t\trequestedNodeId: this.id,\n\t\t\t}),\n\t\t);\n\t\tthis.isListening = resp.isListening;\n\t\tthis.isFrequentListening = resp.isFrequentListening;\n\t\tthis.isRouting = resp.isRouting;\n\t\tthis.supportedDataRates = resp.supportedDataRates;\n\t\tthis.protocolVersion = resp.protocolVersion;\n\t\tthis.nodeType = resp.nodeType;\n\t\tthis.supportsSecurity = resp.supportsSecurity;\n\t\tthis.supportsBeaming = resp.supportsBeaming;\n\n\t\tthis.deviceClass = new DeviceClass(\n\t\t\tresp.basicDeviceClass,\n\t\t\tresp.genericDeviceClass,\n\t\t\tresp.specificDeviceClass,\n\t\t);\n\n\t\tconst logMessage = `received response for protocol info:\nbasic device class: ${\n\t\t\tgetEnumMemberName(BasicDeviceClass, this.deviceClass.basic)\n\t\t}\ngeneric device class: ${this.deviceClass.generic.label}\nspecific device class: ${this.deviceClass.specific.label}\nnode type: ${getEnumMemberName(NodeType, this.nodeType)}\nis always listening: ${this.isListening}\nis frequent listening: ${this.isFrequentListening}\ncan route messages: ${this.isRouting}\nsupports security: ${this.supportsSecurity}\nsupports beaming: ${this.supportsBeaming}\nmaximum data rate: ${this.maxDataRate} kbps\nprotocol version: ${this.protocolVersion}`;\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: logMessage,\n\t\t\tdirection: \"inbound\",\n\t\t});\n\n\t\t// Assume that sleeping nodes start asleep (unless we know it is awake)\n\t\tif (this.canSleep) {\n\t\t\tif (this.status === NodeStatus.Alive) {\n\t\t\t\t// If it was just included and is currently communicating with us,\n\t\t\t\t// then we didn't know yet that it can sleep. So we need to switch from alive/dead to awake/asleep\n\t\t\t\tthis.markAsAwake();\n\t\t\t} else if (this.status !== NodeStatus.Awake) {\n\t\t\t\tthis.markAsAsleep();\n\t\t\t}\n\t\t}\n\n\t\tthis.setInterviewStage(InterviewStage.ProtocolInfo);\n\t}\n\n\t/** Node interview: pings the node to see if it responds */\n\tpublic async ping(): Promise<boolean> {\n\t\tif (this.isControllerNode) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"is the controller node, cannot ping\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: \"pinging the node...\",\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\tawait this.commandClasses[\"No Operation\"].send();\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: \"ping successful\",\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`ping failed: ${getErrorMessage(e)}`,\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Step #5 of the node interview\n\t * Request node info\n\t */\n\tprotected async interviewNodeInfo(): Promise<void> {\n\t\tif (this.isControllerNode) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"is the controller node, cannot query node info\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// If we incorrectly assumed a sleeping node to be awake, this step will fail.\n\t\t// In order to fail the interview, we retry here\n\t\tfor (let attempts = 1; attempts <= 2; attempts++) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: \"querying node info...\",\n\t\t\t\tdirection: \"outbound\",\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tconst nodeInfo = await this.requestNodeInfo();\n\t\t\t\tconst logLines: string[] = [\n\t\t\t\t\t\"node info received\",\n\t\t\t\t\t\"supported CCs:\",\n\t\t\t\t];\n\t\t\t\tfor (const cc of nodeInfo.supportedCCs) {\n\t\t\t\t\tlogLines.push(`\u00B7 ${getCCName(cc)}`);\n\t\t\t\t}\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: logLines.join(\"\\n\"),\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\t\t\t\tthis.updateNodeInfo(nodeInfo);\n\t\t\t\tbreak;\n\t\t\t} catch (e) {\n\t\t\t\tif (isZWaveError(e)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tattempts === 1\n\t\t\t\t\t\t&& this.canSleep\n\t\t\t\t\t\t&& this.status !== NodeStatus.Asleep\n\t\t\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t\t`Querying the node info failed, the node is probably asleep. Retrying after wakeup...`,\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// We assumed the node to be awake, but it is not.\n\t\t\t\t\t\tthis.markAsAsleep();\n\t\t\t\t\t\t// Retry the query when the node wakes up\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (\n\t\t\t\t\t\te.code === ZWaveErrorCodes.Controller_ResponseNOK\n\t\t\t\t\t\t|| e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t\t`Querying the node info failed`,\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.setInterviewStage(InterviewStage.NodeInfo);\n\t}\n\n\tpublic async requestNodeInfo(): Promise<NodeUpdatePayload> {\n\t\tconst resp = await this.driver.sendMessage<\n\t\t\tRequestNodeInfoResponse | ApplicationUpdateRequest\n\t\t>(new RequestNodeInfoRequest({ nodeId: this.id }));\n\t\tif (resp instanceof RequestNodeInfoResponse && !resp.wasSent) {\n\t\t\t// TODO: handle this in SendThreadMachine\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Querying the node info failed`,\n\t\t\t\tZWaveErrorCodes.Controller_ResponseNOK,\n\t\t\t);\n\t\t} else if (\n\t\t\tresp instanceof ApplicationUpdateRequestNodeInfoRequestFailed\n\t\t) {\n\t\t\t// TODO: handle this in SendThreadMachine\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Querying the node info failed`,\n\t\t\t\tZWaveErrorCodes.Controller_CallbackNOK,\n\t\t\t);\n\t\t} else if (resp instanceof ApplicationUpdateRequestNodeInfoReceived) {\n\t\t\tconst logLines: string[] = [\"node info received\", \"supported CCs:\"];\n\t\t\tfor (const cc of resp.nodeInformation.supportedCCs) {\n\t\t\t\tlogLines.push(`\u00B7 ${getCCName(cc)}`);\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: logLines.join(\"\\n\"),\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t\treturn resp.nodeInformation;\n\t\t}\n\t\tthrow new ZWaveError(\n\t\t\t`Received unexpected response to RequestNodeInfoRequest`,\n\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t);\n\t}\n\n\t/**\n\t * Loads the device configuration for this node from a config file\n\t */\n\tprotected async loadDeviceConfig(): Promise<void> {\n\t\t// But the configuration definitions might change\n\t\tif (\n\t\t\tthis.manufacturerId != undefined\n\t\t\t&& this.productType != undefined\n\t\t\t&& this.productId != undefined\n\t\t) {\n\t\t\t// Try to load the config file\n\t\t\tthis._deviceConfig = await this.driver.configManager.lookupDevice(\n\t\t\t\tthis.manufacturerId,\n\t\t\t\tthis.productType,\n\t\t\t\tthis.productId,\n\t\t\t\tthis.firmwareVersion,\n\t\t\t);\n\t\t\tif (this._deviceConfig) {\n\t\t\t\t// Also remember the current hash of the device config\n\t\t\t\tif (this.cachedDeviceConfigHash?.length === 16) {\n\t\t\t\t\t// legacy hash using MD5\n\t\t\t\t\tthis._currentDeviceConfigHash = await this._deviceConfig\n\t\t\t\t\t\t.getHash(\"md5\");\n\t\t\t\t} else {\n\t\t\t\t\tthis._currentDeviceConfigHash = await this._deviceConfig\n\t\t\t\t\t\t.getHash();\n\t\t\t\t}\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t`${\n\t\t\t\t\t\tthis._deviceConfig.isEmbedded\n\t\t\t\t\t\t\t? \"Embedded\"\n\t\t\t\t\t\t\t: \"User-provided\"\n\t\t\t\t\t} device config loaded`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t\"No device config found\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Step #? of the node interview */\n\tprotected async interviewCCs(): Promise<boolean> {\n\t\tif (this.isControllerNode) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"is the controller node, cannot interview CCs\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\tconst securityManager2 = this.driver.getSecurityManager2(this.id);\n\n\t\t/**\n\t\t * @param force When this is `true`, the interview will be attempted even when the CC is not supported by the endpoint.\n\t\t */\n\t\tconst interviewEndpoint = async (\n\t\t\tendpoint: Endpoint,\n\t\t\tcc: CommandClasses,\n\t\t\tforce: boolean = false,\n\t\t): Promise<\"continue\" | false | void> => {\n\t\t\tlet instance: CommandClass;\n\t\t\ttry {\n\t\t\t\tif (force) {\n\t\t\t\t\tinstance = CommandClass.createInstanceUnchecked(\n\t\t\t\t\t\tendpoint,\n\t\t\t\t\t\tcc,\n\t\t\t\t\t)!;\n\t\t\t\t} else {\n\t\t\t\t\tinstance = endpoint.createCCInstance(cc)!;\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (\n\t\t\t\t\tisZWaveError(e)\n\t\t\t\t\t&& e.code === ZWaveErrorCodes.CC_NotSupported\n\t\t\t\t) {\n\t\t\t\t\t// The CC is no longer supported. This can happen if the node tells us\n\t\t\t\t\t// something different in the Version interview than it did in its NIF\n\t\t\t\t\treturn \"continue\";\n\t\t\t\t}\n\t\t\t\t// we want to pass all other errors through\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tendpoint.isCCSecure(cc)\n\t\t\t\t&& !this.driver.securityManager\n\t\t\t\t&& !securityManager2\n\t\t\t) {\n\t\t\t\t// The CC is only supported securely, but the network key is not set up\n\t\t\t\t// Skip the CC\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t`Skipping interview for secure CC ${\n\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\tcc,\n\t\t\t\t\t\t)\n\t\t\t\t\t} because no network key is configured!`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn \"continue\";\n\t\t\t}\n\n\t\t\t// Skip this step if the CC was already interviewed\n\t\t\tif (instance.isInterviewComplete(this.driver)) return \"continue\";\n\n\t\t\ttry {\n\t\t\t\tawait instance.interview(this.driver);\n\t\t\t} catch (e) {\n\t\t\t\tif (isTransmissionError(e)) {\n\t\t\t\t\t// We had a CAN or timeout during the interview\n\t\t\t\t\t// or the node is presumed dead. Abort the process\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t// we want to pass all other errors through\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t};\n\n\t\t// Always interview Security first because it changes the interview order\n\t\tif (this.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\t// Security S2 is always supported *securely*\n\t\t\tthis.addCC(CommandClasses[\"Security 2\"], { secure: true });\n\n\t\t\t// Query supported CCs unless we know for sure that the node wasn't assigned a S2 security class\n\t\t\tconst securityClass = this.getHighestSecurityClass();\n\t\t\tif (\n\t\t\t\tsecurityClass == undefined\n\t\t\t\t|| securityClassIsS2(securityClass)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t\"Root device interview: Security S2\",\n\t\t\t\t\t\"silly\",\n\t\t\t\t);\n\n\t\t\t\tif (!securityManager2) {\n\t\t\t\t\tif (!this._hasEmittedNoS2NetworkKeyError) {\n\t\t\t\t\t\t// Cannot interview a secure device securely without a network key\n\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t`supports Security S2, but no S2 network keys were configured. The interview might not include all functionality.`;\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t\terrorMessage,\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.driver.emit(\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\t\t`Node ${\n\t\t\t\t\t\t\t\t\tthis.id.toString().padStart(\n\t\t\t\t\t\t\t\t\t\t3,\n\t\t\t\t\t\t\t\t\t\t\"0\",\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t} ${errorMessage}`,\n\t\t\t\t\t\t\t\tZWaveErrorCodes\n\t\t\t\t\t\t\t\t\t.Controller_NodeInsecureCommunication,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis._hasEmittedNoS2NetworkKeyError = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t);\n\t\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// If there is any doubt about granted S2 security classes, we now know they are not granted\n\t\t\tfor (\n\t\t\t\tconst secClass of [\n\t\t\t\t\tSecurityClass.S2_AccessControl,\n\t\t\t\t\tSecurityClass.S2_Authenticated,\n\t\t\t\t\tSecurityClass.S2_Unauthenticated,\n\t\t\t\t] as const\n\t\t\t) {\n\t\t\t\tif (this.hasSecurityClass(secClass) === NOT_KNOWN) {\n\t\t\t\t\tthis.securityClasses.set(secClass, false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.supportsCC(CommandClasses.Security)) {\n\t\t\t// Security S0 is always supported *securely*\n\t\t\tthis.addCC(CommandClasses.Security, { secure: true });\n\n\t\t\t// Query supported CCs unless we know for sure that the node wasn't assigned the S0 security class\n\t\t\tif (this.hasSecurityClass(SecurityClass.S0_Legacy) !== false) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t\"Root device interview: Security S0\",\n\t\t\t\t\t\"silly\",\n\t\t\t\t);\n\n\t\t\t\tif (!this.driver.securityManager) {\n\t\t\t\t\tif (!this._hasEmittedNoS0NetworkKeyError) {\n\t\t\t\t\t\t// Cannot interview a secure device securely without a network key\n\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t`supports Security S0, but the S0 network key was not configured. The interview might not include all functionality.`;\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t\terrorMessage,\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.driver.emit(\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\t\t`Node ${\n\t\t\t\t\t\t\t\t\tthis.id.toString().padStart(\n\t\t\t\t\t\t\t\t\t\t3,\n\t\t\t\t\t\t\t\t\t\t\"0\",\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t} ${errorMessage}`,\n\t\t\t\t\t\t\t\tZWaveErrorCodes\n\t\t\t\t\t\t\t\t\t.Controller_NodeInsecureCommunication,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis._hasEmittedNoS0NetworkKeyError = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tCommandClasses.Security,\n\t\t\t\t\t);\n\t\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (this.hasSecurityClass(SecurityClass.S0_Legacy) === NOT_KNOWN) {\n\t\t\t\t// Remember that this node hasn't been granted the S0 security class\n\t\t\t\tthis.securityClasses.set(SecurityClass.S0_Legacy, false);\n\t\t\t}\n\t\t}\n\n\t\t// Manufacturer Specific and Version CC need to be handled before the other CCs because they are needed to\n\t\t// identify the device and apply device configurations\n\t\tif (this.supportsCC(CommandClasses[\"Manufacturer Specific\"])) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"Root device interview: Manufacturer Specific\",\n\t\t\t\t\"silly\",\n\t\t\t);\n\n\t\t\tconst action = await interviewEndpoint(\n\t\t\t\tthis,\n\t\t\t\tCommandClasses[\"Manufacturer Specific\"],\n\t\t\t);\n\t\t\tif (typeof action === \"boolean\") return action;\n\t\t}\n\n\t\tif (this.supportsCC(CommandClasses.Version)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"Root device interview: Version\",\n\t\t\t\t\"silly\",\n\t\t\t);\n\n\t\t\tconst action = await interviewEndpoint(\n\t\t\t\tthis,\n\t\t\t\tCommandClasses.Version,\n\t\t\t);\n\t\t\tif (typeof action === \"boolean\") return action;\n\n\t\t\t// After the version CC interview of the root endpoint, we have enough info to load the correct device config file\n\t\t\tawait this.loadDeviceConfig();\n\n\t\t\t// At this point we may need to make some changes to the CCs the device reports\n\t\t\tthis.applyCommandClassesCompatFlag();\n\t\t} else {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"Version CC is not supported. Using the highest implemented version for each CC\",\n\t\t\t\t\"debug\",\n\t\t\t);\n\n\t\t\tfor (const [ccId, info] of this.getCCs()) {\n\t\t\t\tif (\n\t\t\t\t\tinfo.isSupported\n\t\t\t\t\t// The support status of Basic CC is not known yet at this point\n\t\t\t\t\t|| ccId === CommandClasses.Basic\n\t\t\t\t) {\n\t\t\t\t\tthis.addCC(ccId, { version: getImplementedVersion(ccId) });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// The Wakeup interview should be done as early as possible\n\t\tif (this.supportsCC(CommandClasses[\"Wake Up\"])) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"Root device interview: Wake Up\",\n\t\t\t\t\"silly\",\n\t\t\t);\n\n\t\t\tconst action = await interviewEndpoint(\n\t\t\t\tthis,\n\t\t\t\tCommandClasses[\"Wake Up\"],\n\t\t\t);\n\t\t\tif (typeof action === \"boolean\") return action;\n\t\t}\n\n\t\tthis.modifySupportedCCBeforeInterview(this);\n\n\t\t// We determine the correct interview order of the remaining CCs by topologically sorting two dependency graph\n\t\t// In order to avoid emitting unnecessary value events for the root endpoint,\n\t\t// we defer the application CC interview until after the other endpoints have been interviewed\n\t\t// The following CCs are interviewed \"manually\" outside of the automatic interview sequence,\n\t\t// because there are special rules around them.\n\t\tconst specialCCs = [\n\t\t\tCommandClasses.Security,\n\t\t\tCommandClasses[\"Security 2\"],\n\t\t\tCommandClasses[\"Manufacturer Specific\"],\n\t\t\tCommandClasses.Version,\n\t\t\tCommandClasses[\"Wake Up\"],\n\t\t\t// Basic CC is interviewed last\n\t\t\tCommandClasses.Basic,\n\t\t];\n\t\tconst rootInterviewGraphBeforeEndpoints = this.buildCCInterviewGraph([\n\t\t\t...specialCCs,\n\t\t\t...applicationCCs,\n\t\t]);\n\t\tlet rootInterviewOrderBeforeEndpoints: CommandClasses[];\n\n\t\tconst rootInterviewGraphAfterEndpoints = this.buildCCInterviewGraph([\n\t\t\t...specialCCs,\n\t\t\t...nonApplicationCCs,\n\t\t]);\n\t\tlet rootInterviewOrderAfterEndpoints: CommandClasses[];\n\n\t\ttry {\n\t\t\trootInterviewOrderBeforeEndpoints = topologicalSort(\n\t\t\t\trootInterviewGraphBeforeEndpoints,\n\t\t\t);\n\t\t\trootInterviewOrderAfterEndpoints = topologicalSort(\n\t\t\t\trootInterviewGraphAfterEndpoints,\n\t\t\t);\n\t\t} catch {\n\t\t\t// This interview cannot be done\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The CC interview cannot be completed because there are circular dependencies between CCs!\",\n\t\t\t\tZWaveErrorCodes.CC_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Root device interviews before endpoints: ${\n\t\t\t\trootInterviewOrderBeforeEndpoints\n\t\t\t\t\t.map((cc) => `\\n\u00B7 ${getCCName(cc)}`)\n\t\t\t\t\t.join(\"\")\n\t\t\t}`,\n\t\t\t\"silly\",\n\t\t);\n\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Root device interviews after endpoints: ${\n\t\t\t\trootInterviewOrderAfterEndpoints\n\t\t\t\t\t.map((cc) => `\\n\u00B7 ${getCCName(cc)}`)\n\t\t\t\t\t.join(\"\")\n\t\t\t}`,\n\t\t\t\"silly\",\n\t\t);\n\n\t\t// Now that we know the correct order, do the interview in sequence\n\t\tfor (const cc of rootInterviewOrderBeforeEndpoints) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`Root device interview: ${getCCName(cc)}`,\n\t\t\t\t\"silly\",\n\t\t\t);\n\n\t\t\tconst action = await interviewEndpoint(this, cc);\n\t\t\tif (action === \"continue\") continue;\n\t\t\telse if (typeof action === \"boolean\") return action;\n\t\t}\n\n\t\t// Before querying the endpoints, we may need to make some more changes to the CCs the device reports\n\t\t// This time, the non-root endpoints are relevant\n\t\tthis.applyCommandClassesCompatFlag();\n\n\t\t// Now query ALL endpoints\n\t\tfor (const endpointIndex of this.getEndpointIndizes()) {\n\t\t\tconst endpoint = this.getEndpoint(endpointIndex);\n\t\t\tif (!endpoint) continue;\n\n\t\t\t// The root endpoint has been interviewed, so we know if the device supports security and which security classes it has\n\t\t\tconst securityClass = this.getHighestSecurityClass();\n\n\t\t\t// From the specs, Multi Channel Capability Report Command:\n\t\t\t// Non-secure End Point capabilities MUST also be supported securely and MUST also be advertised in\n\t\t\t// the S0/S2 Commands Supported Report Commands unless they are encapsulated outside Security or\n\t\t\t// Security themselves.\n\t\t\t// Nodes supporting S2 MUST support addressing every End Point with S2 encapsulation and MAY\n\t\t\t// explicitly list S2 in the non-secure End Point capabilities.\n\n\t\t\t// This means we need to explicitly add S2 to the list of supported CCs of the endpoint, if the node is using S2.\n\t\t\t// Otherwise the communication will incorrectly use no encryption.\n\t\t\tconst endpointMissingS2 = securityClassIsS2(securityClass)\n\t\t\t\t&& this.supportsCC(CommandClasses[\"Security 2\"])\n\t\t\t\t&& !endpoint.supportsCC(CommandClasses[\"Security 2\"]);\n\t\t\tif (endpointMissingS2) {\n\t\t\t\tendpoint.addCC(\n\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\tthis.implementedCommandClasses.get(\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t)!,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Always interview Security first because it changes the interview order\n\n\t\t\tif (endpoint.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\t\t// Security S2 is always supported *securely*\n\t\t\t\tendpoint.addCC(CommandClasses[\"Security 2\"], { secure: true });\n\n\t\t\t\t// If S2 is the highest security class, interview it for the endpoint\n\t\t\t\tif (\n\t\t\t\t\tsecurityClassIsS2(securityClass)\n\t\t\t\t\t&& !!securityManager2\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Endpoint ${endpoint.index} interview: Security S2`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\n\t\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\t\tendpoint,\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t);\n\t\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (endpoint.supportsCC(CommandClasses.Security)) {\n\t\t\t\t// Security S0 is always supported *securely*\n\t\t\t\tendpoint.addCC(CommandClasses.Security, { secure: true });\n\n\t\t\t\t// If S0 is the highest security class, interview it for the endpoint\n\t\t\t\tif (\n\t\t\t\t\tsecurityClass === SecurityClass.S0_Legacy\n\t\t\t\t\t&& !!this.driver.securityManager\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Endpoint ${endpoint.index} interview: Security S0`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\n\t\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\t\tendpoint,\n\t\t\t\t\t\tCommandClasses.Security,\n\t\t\t\t\t);\n\t\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// It has been found that legacy nodes do not always advertise the S0 Command Class in their Multi\n\t\t\t// Channel Capability Report and still accept all their Command Class using S0 encapsulation.\n\t\t\t// A controlling node SHOULD try to control End Points with S0 encapsulation even if S0 is not\n\t\t\t// listed in the Multi Channel Capability Report.\n\n\t\t\tconst endpointMissingS0 = securityClass === SecurityClass.S0_Legacy\n\t\t\t\t&& this.supportsCC(CommandClasses.Security)\n\t\t\t\t&& !endpoint.supportsCC(CommandClasses.Security);\n\n\t\t\tif (endpointMissingS0) {\n\t\t\t\t// Define which CCs we can use to test this - and if supported, how\n\t\t\t\tconst possibleTests: {\n\t\t\t\t\tccId: CommandClasses;\n\t\t\t\t\t// The test must return a truthy value if the check was successful\n\t\t\t\t\ttest: () => Promise<unknown>;\n\t\t\t\t}[] = [\n\t\t\t\t\t{\n\t\t\t\t\t\tccId: CommandClasses[\"Z-Wave Plus Info\"],\n\t\t\t\t\t\ttest: () =>\n\t\t\t\t\t\t\tendpoint.commandClasses[\"Z-Wave Plus Info\"].get(),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tccId: CommandClasses[\"Binary Switch\"],\n\t\t\t\t\t\ttest: () =>\n\t\t\t\t\t\t\tendpoint.commandClasses[\"Binary Switch\"].get(),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tccId: CommandClasses[\"Binary Sensor\"],\n\t\t\t\t\t\ttest: () =>\n\t\t\t\t\t\t\tendpoint.commandClasses[\"Binary Sensor\"].get(),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tccId: CommandClasses[\"Multilevel Switch\"],\n\t\t\t\t\t\ttest: () =>\n\t\t\t\t\t\t\tendpoint.commandClasses[\"Multilevel Switch\"].get(),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tccId: CommandClasses[\"Multilevel Sensor\"],\n\t\t\t\t\t\ttest: () =>\n\t\t\t\t\t\t\tendpoint.commandClasses[\"Multilevel Sensor\"].get(),\n\t\t\t\t\t},\n\t\t\t\t\t// TODO: add other tests if necessary\n\t\t\t\t];\n\n\t\t\t\tconst foundTest = possibleTests.find((t) =>\n\t\t\t\t\tendpoint.supportsCC(t.ccId)\n\t\t\t\t);\n\t\t\t\tif (foundTest) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`is included using Security S0, but endpoint ${endpoint.index} does not list the CC. Testing if it accepts secure commands anyways.`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\n\t\t\t\t\tconst { ccId, test } = foundTest;\n\n\t\t\t\t\t// Temporarily mark the CC as secure so we can use it to test\n\t\t\t\t\tendpoint.addCC(ccId, { secure: true });\n\n\t\t\t\t\t// Perform the test and treat errors as negative results\n\t\t\t\t\tconst success = !!(await test().catch(() => false));\n\n\t\t\t\t\tif (success) {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`Endpoint ${endpoint.index} accepts/expects secure commands`,\n\t\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Mark all endpoint CCs as secure\n\t\t\t\t\t\tfor (const [ccId] of endpoint.getCCs()) {\n\t\t\t\t\t\t\tendpoint.addCC(ccId, { secure: true });\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`Endpoint ${endpoint.index} is actually not using S0`,\n\t\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Mark the CC as not secure again\n\t\t\t\t\t\tendpoint.addCC(ccId, { secure: false });\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`is included using Security S0, but endpoint ${endpoint.index} does not list the CC. Found no way to test if accepts secure commands anyways.`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// This intentionally checks for Version CC support on the root device.\n\t\t\t// Endpoints SHOULD not support this CC, but we still need to query their\n\t\t\t// CCs that the root device may or may not support\n\t\t\tif (this.supportsCC(CommandClasses.Version)) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tmessage: `Endpoint ${endpoint.index} interview: ${\n\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\tCommandClasses.Version,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\n\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\tendpoint,\n\t\t\t\t\tCommandClasses.Version,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Version CC is not supported. Using the highest implemented version for each CC\",\n\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t});\n\n\t\t\t\tfor (const [ccId, info] of endpoint.getCCs()) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tinfo.isSupported\n\t\t\t\t\t\t// The support status of Basic CC is not known yet at this point\n\t\t\t\t\t\t|| ccId === CommandClasses.Basic\n\t\t\t\t\t) {\n\t\t\t\t\t\tendpoint.addCC(ccId, {\n\t\t\t\t\t\t\tversion: getImplementedVersion(ccId),\n\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// The Security S0/S2 CC adds new CCs to the endpoint, so we need to once more remove those\n\t\t\t// that aren't actually properly supported by the device.\n\t\t\tthis.applyCommandClassesCompatFlag(endpoint.index);\n\n\t\t\t// We need to add/remove some CCs based on other criteria\n\t\t\tthis.modifySupportedCCBeforeInterview(endpoint);\n\n\t\t\tconst endpointInterviewGraph = endpoint.buildCCInterviewGraph([\n\t\t\t\tCommandClasses.Security,\n\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\tCommandClasses.Version,\n\t\t\t\tCommandClasses.Basic,\n\t\t\t]);\n\t\t\tlet endpointInterviewOrder: CommandClasses[];\n\t\t\ttry {\n\t\t\t\tendpointInterviewOrder = topologicalSort(\n\t\t\t\t\tendpointInterviewGraph,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// This interview cannot be done\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The CC interview cannot be completed because there are circular dependencies between CCs!\",\n\t\t\t\t\tZWaveErrorCodes.CC_Invalid,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: endpoint.index,\n\t\t\t\tmessage: `Endpoint ${endpoint.index} interview order: ${\n\t\t\t\t\tendpointInterviewOrder\n\t\t\t\t\t\t.map((cc) => `\\n\u00B7 ${getCCName(cc)}`)\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}`,\n\t\t\t\tlevel: \"silly\",\n\t\t\t});\n\n\t\t\t// Now that we know the correct order, do the interview in sequence\n\t\t\tfor (const cc of endpointInterviewOrder) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tmessage: `Endpoint ${endpoint.index} interview: ${\n\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\tcc,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\n\t\t\t\tconst action = await interviewEndpoint(endpoint, cc);\n\t\t\t\tif (action === \"continue\") continue;\n\t\t\t\telse if (typeof action === \"boolean\") return action;\n\t\t\t}\n\t\t}\n\n\t\t// Continue with the application CCs for the root endpoint\n\t\tfor (const cc of rootInterviewOrderAfterEndpoints) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`Root device interview: ${getCCName(cc)}`,\n\t\t\t\t\"silly\",\n\t\t\t);\n\n\t\t\tconst action = await interviewEndpoint(this, cc);\n\t\t\tif (action === \"continue\") continue;\n\t\t\telse if (typeof action === \"boolean\") return action;\n\t\t}\n\n\t\t// At the very end, figure out if Basic CC is supposed to be supported\n\t\t// First on the root device\n\t\tconst compat = this.deviceConfig?.compat;\n\t\tif (\n\t\t\t!this.wasCCRemovedViaConfig(CommandClasses.Basic)\n\t\t\t&& this.getCCVersion(CommandClasses.Basic) > 0\n\t\t) {\n\t\t\tif (this.maySupportBasicCC()) {\n\t\t\t\t// The device probably supports Basic CC and is allowed to.\n\t\t\t\t// Interview the Basic CC to figure out if it actually supports it\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t`Root device interview: ${getCCName(CommandClasses.Basic)}`,\n\t\t\t\t\t\"silly\",\n\t\t\t\t);\n\n\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\tthis,\n\t\t\t\t\tCommandClasses.Basic,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t} else {\n\t\t\t\t// Consider the device to control Basic CC, but only if we want to expose the currentValue\n\t\t\t\tif (\n\t\t\t\t\tcompat?.mapBasicReport === false\n\t\t\t\t\t|| compat?.mapBasicSet === \"report\"\n\t\t\t\t) {\n\t\t\t\t\t// TODO: Figure out if we need to consider mapBasicSet === \"auto\" in the case where it falls back to Basic CC currentValue\n\t\t\t\t\tthis.addCC(CommandClasses.Basic, { isControlled: true });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Then on all endpoints\n\t\tfor (const endpointIndex of this.getEndpointIndizes()) {\n\t\t\tconst endpoint = this.getEndpoint(endpointIndex);\n\t\t\tif (!endpoint) continue;\n\t\t\tif (endpoint.wasCCRemovedViaConfig(CommandClasses.Basic)) continue;\n\t\t\tif (endpoint.getCCVersion(CommandClasses.Basic) === 0) continue;\n\n\t\t\tif (endpoint.maySupportBasicCC()) {\n\t\t\t\t// The endpoint probably supports Basic CC and is allowed to.\n\t\t\t\t// Interview the Basic CC to figure out if it actually supports it\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tmessage: `Endpoint ${endpoint.index} interview: Basic CC`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\n\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\tendpoint,\n\t\t\t\t\tCommandClasses.Basic,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t} else {\n\t\t\t\t// Consider the device to control Basic CC, but only if we want to expose the currentValue\n\t\t\t\tif (\n\t\t\t\t\tcompat?.mapBasicReport === false\n\t\t\t\t\t|| compat?.mapBasicSet === \"report\"\n\t\t\t\t) {\n\t\t\t\t\t// TODO: Figure out if we need to consider mapBasicSet === \"auto\" in the case where it falls back to Basic CC currentValue\n\t\t\t\t\tendpoint.addCC(CommandClasses.Basic, {\n\t\t\t\t\t\tisControlled: true,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * @internal\n\t * Handles the receipt of a NIF / NodeUpdatePayload\n\t */\n\tpublic updateNodeInfo(nodeInfo: NodeUpdatePayload): void {\n\t\tif (this.interviewStage < InterviewStage.NodeInfo) {\n\t\t\tfor (const cc of nodeInfo.supportedCCs) {\n\t\t\t\tif (cc === CommandClasses.Basic) {\n\t\t\t\t\t// Basic CC MUST not be in the NIF and we have special rules to determine support\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.addCC(cc, { isSupported: true });\n\t\t\t}\n\t\t}\n\n\t\t// As the NIF is sent on wakeup, treat this as a sign that the node is awake\n\t\tthis.markAsAwake();\n\n\t\t// SDS14223 Unless unsolicited <XYZ> Report Commands are received,\n\t\t// a controlling node MUST probe the current values when the\n\t\t// supporting node issues a Wake Up Notification Command for sleeping nodes.\n\n\t\t// This is not the handler for wakeup notifications, but some legacy devices send this\n\t\t// message whenever there's an update and want to be polled.\n\t\t// We do this unless we know for certain that the device sends unsolicited reports for its actuator or sensor CCs\n\t\tif (\n\t\t\tthis.interviewStage === InterviewStage.Complete\n\t\t\t&& !this.supportsCC(CommandClasses[\"Z-Wave Plus Info\"])\n\t\t\t&& (!this.valueDB.getValue(AssociationCCValues.hasLifeline.id)\n\t\t\t\t|| !ccUtils.doesAnyLifelineSendActuatorOrSensorReports(\n\t\t\t\t\tthis.driver,\n\t\t\t\t\tthis,\n\t\t\t\t))\n\t\t) {\n\t\t\tconst delay = this.deviceConfig?.compat?.manualValueRefreshDelayMs\n\t\t\t\t|| 0;\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Node does not send unsolicited updates; refreshing actuator and sensor values${\n\t\t\t\t\t\tdelay > 0 ? ` in ${delay} ms` : \"\"\n\t\t\t\t\t}...`,\n\t\t\t});\n\t\t\tsetTimeout(() => this.refreshValues(), delay);\n\t\t}\n\t}\n\n\t/**\n\t * Rediscovers all capabilities of a single CC on this node and all endpoints.\n\t * This can be considered a more targeted variant of `refreshInfo`.\n\t *\n\t * WARNING: It is not recommended to await this method!\n\t */\n\tpublic async interviewCC(cc: CommandClasses): Promise<void> {\n\t\tconst endpoints = this.getAllEndpoints();\n\t\t// Interview the node itself last\n\t\tendpoints.push(endpoints.shift()!);\n\t\tfor (const endpoint of endpoints) {\n\t\t\tconst instance = endpoint.createCCInstanceUnsafe(cc);\n\t\t\tif (instance) {\n\t\t\t\ttry {\n\t\t\t\t\tawait instance.interview(this.driver);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t`failed to interview CC ${\n\t\t\t\t\t\t\tgetCCName(cc)\n\t\t\t\t\t\t}, endpoint ${endpoint.index}: ${getErrorMessage(e)}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Refreshes all non-static values of a single CC from this node (all endpoints).\n\t * WARNING: It is not recommended to await this method!\n\t */\n\tpublic async refreshCCValues(cc: CommandClasses): Promise<void> {\n\t\tfor (const endpoint of this.getAllEndpoints()) {\n\t\t\tconst instance = endpoint.createCCInstanceUnsafe(cc);\n\t\t\tif (instance) {\n\t\t\t\ttry {\n\t\t\t\t\tawait instance.refreshValues(this.driver);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t`failed to refresh values for ${\n\t\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\t\tcc,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}, endpoint ${endpoint.index}: ${getErrorMessage(e)}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Refreshes all non-static values from this node's actuator and sensor CCs.\n\t * WARNING: It is not recommended to await this method!\n\t */\n\tpublic async refreshValues(): Promise<void> {\n\t\tfor (const endpoint of this.getAllEndpoints()) {\n\t\t\tfor (const cc of endpoint.getSupportedCCInstances()) {\n\t\t\t\t// Only query actuator and sensor CCs\n\t\t\t\tif (\n\t\t\t\t\t!actuatorCCs.includes(cc.ccId)\n\t\t\t\t\t&& !sensorCCs.includes(cc.ccId)\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait cc.refreshValues(this.driver);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t`failed to refresh values for ${\n\t\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\t\tcc.ccId,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}, endpoint ${endpoint.index}: ${getErrorMessage(e)}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Refreshes the values of all CCs that should be reporting regularly, but haven't been updated recently\n\t * @internal\n\t */\n\tpublic async autoRefreshValues(): Promise<void> {\n\t\t// Do not attempt to communicate with dead nodes automatically\n\t\tif (this.status === NodeStatus.Dead) return;\n\n\t\tfor (const endpoint of this.getAllEndpoints()) {\n\t\t\tfor (\n\t\t\t\tconst cc of endpoint\n\t\t\t\t\t.getSupportedCCInstances() as readonly SinglecastCC<\n\t\t\t\t\t\tCommandClass\n\t\t\t\t\t>[]\n\t\t\t) {\n\t\t\t\tif (!cc.shouldRefreshValues(this.driver)) continue;\n\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `${\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} CC values may be stale, refreshing...`,\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t});\n\n\t\t\t\ttry {\n\t\t\t\t\tawait cc.refreshValues(this.driver);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tmessage: `failed to refresh values for ${\n\t\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\t\tcc.ccId,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t} CC: ${getErrorMessage(e)}`,\n\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Uses the `commandClasses` compat flag defined in the node's config file to\n\t * override the reported command classes.\n\t * @param endpointIndex If given, limits the application of the compat flag to the given endpoint.\n\t */\n\tprivate applyCommandClassesCompatFlag(endpointIndex?: number): void {\n\t\tif (this.deviceConfig) {\n\t\t\t// Add CCs the device config file tells us to\n\t\t\tconst addCCs = this.deviceConfig.compat?.addCCs;\n\t\t\tif (addCCs) {\n\t\t\t\tfor (const [cc, { endpoints }] of addCCs) {\n\t\t\t\t\tif (endpointIndex === undefined) {\n\t\t\t\t\t\tfor (const [ep, info] of endpoints) {\n\t\t\t\t\t\t\tthis.getEndpoint(ep)?.addCC(cc, info);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (endpoints.has(endpointIndex)) {\n\t\t\t\t\t\tthis.getEndpoint(endpointIndex)?.addCC(\n\t\t\t\t\t\t\tcc,\n\t\t\t\t\t\t\tendpoints.get(endpointIndex)!,\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\t// And remove those that it marks as unsupported\n\t\t\tconst removeCCs = this.deviceConfig.compat?.removeCCs;\n\t\t\tif (removeCCs) {\n\t\t\t\tfor (const [cc, endpoints] of removeCCs) {\n\t\t\t\t\tif (endpoints === \"*\") {\n\t\t\t\t\t\tif (endpointIndex === undefined) {\n\t\t\t\t\t\t\tfor (const ep of this.getAllEndpoints()) {\n\t\t\t\t\t\t\t\tep.removeCC(cc);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.getEndpoint(endpointIndex)?.removeCC(cc);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (endpointIndex === undefined) {\n\t\t\t\t\t\t\tfor (const ep of endpoints) {\n\t\t\t\t\t\t\t\tthis.getEndpoint(ep)?.removeCC(cc);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (endpoints.includes(endpointIndex)) {\n\t\t\t\t\t\t\tthis.getEndpoint(endpointIndex)?.removeCC(cc);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Updates the supported CCs of the given endpoint depending on compat flags\n\t * and certification requirements\n\t */\n\tprivate modifySupportedCCBeforeInterview(endpoint: Endpoint): void {\n\t\t// Window Covering CC:\n\t\t// CL:006A.01.51.01.2: A controlling node MUST NOT interview and provide controlling functionalities for the\n\t\t// Multilevel Switch Command Class for a node (or endpoint) supporting the Window Covering CC, as it is a fully\n\t\t// redundant and less precise application functionality.\n\t\tif (\n\t\t\tendpoint.supportsCC(CommandClasses[\"Multilevel Switch\"])\n\t\t\t&& endpoint.supportsCC(CommandClasses[\"Window Covering\"])\n\t\t) {\n\t\t\tendpoint.removeCC(CommandClasses[\"Multilevel Switch\"]);\n\t\t}\n\t}\n\n\t/** Overwrites the reported configuration with information from a config file */\n\tprotected async overwriteConfig(): Promise<void> {\n\t\tif (this.isControllerNode) {\n\t\t\t// The device config was not loaded prior to this step because the Version CC is not interviewed.\n\t\t\t// Therefore do it here.\n\t\t\tawait this.loadDeviceConfig();\n\t\t}\n\n\t\tif (this.deviceConfig) {\n\t\t\t// Add CCs the device config file tells us to\n\t\t\tconst addCCs = this.deviceConfig.compat?.addCCs;\n\t\t\tif (addCCs) {\n\t\t\t\tfor (const [cc, { endpoints }] of addCCs) {\n\t\t\t\t\tfor (const [ep, info] of endpoints) {\n\t\t\t\t\t\tthis.getEndpoint(ep)?.addCC(cc, info);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// And remove those that it marks as unsupported\n\t\t\tconst removeCCs = this.deviceConfig.compat?.removeCCs;\n\t\t\tif (removeCCs) {\n\t\t\t\tfor (const [cc, endpoints] of removeCCs) {\n\t\t\t\t\tif (endpoints === \"*\") {\n\t\t\t\t\t\tfor (const ep of this.getAllEndpoints()) {\n\t\t\t\t\t\t\tep.removeCC(cc);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor (const ep of endpoints) {\n\t\t\t\t\t\t\tthis.getEndpoint(ep)?.removeCC(cc);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.setInterviewStage(InterviewStage.OverwriteConfig);\n\t}\n\n\t/**\n\t * @internal\n\t * Handles a CommandClass that was received from this node\n\t */\n\tpublic async handleCommand(command: CommandClass): Promise<void> {\n\t\t// If this is a report for the root endpoint and the node supports the CC on another endpoint,\n\t\t// we need to map it to endpoint 1. Either it does not support multi channel associations or\n\t\t// it is misbehaving. In any case, we would hide this report if we didn't map it\n\t\tif (\n\t\t\tcommand.endpointIndex === 0\n\t\t\t&& command.constructor.name.endsWith(\"Report\")\n\t\t\t&& this.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&& this._deviceConfig?.compat?.mapRootReportsToEndpoint != undefined\n\t\t) {\n\t\t\tconst endpoint = this.getEndpoint(\n\t\t\t\tthis._deviceConfig?.compat?.mapRootReportsToEndpoint,\n\t\t\t);\n\t\t\tif (endpoint && endpoint.supportsCC(command.ccId)) {\n\t\t\t\t// Force the CC to store its values again under the supporting endpoint\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t`Mapping unsolicited report from root device to endpoint #${endpoint.index}`,\n\t\t\t\t);\n\t\t\t\tcommand.endpointIndex = endpoint.index;\n\t\t\t\tcommand.persistValues(this.driver);\n\t\t\t}\n\t\t}\n\n\t\t// If we're being queried by another node, treat this as a sign that the other node is awake\n\t\tif (\n\t\t\tcommand.constructor.name.endsWith(\"Get\")\n\t\t\t// Nonces can be sent while asleep though\n\t\t\t&& !(command instanceof SecurityCCNonceGet)\n\t\t\t&& !(command instanceof Security2CCNonceGet)\n\t\t) {\n\t\t\tthis.markAsAwake();\n\t\t}\n\n\t\t// If the received CC was force-removed via config file, ignore it completely\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex);\n\t\tif (endpoint?.wasCCRemovedViaConfig(command.ccId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t{\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Ignoring ${command.constructor.name} because CC support was removed via config file`,\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (command instanceof BasicCC) {\n\t\t\treturn this.handleBasicCommand(command);\n\t\t} else if (command instanceof MultilevelSwitchCC) {\n\t\t\treturn this.handleMultilevelSwitchCommand(command);\n\t\t} else if (command instanceof BinarySwitchCCSet) {\n\t\t\treturn this.handleBinarySwitchCommand(command);\n\t\t} else if (command instanceof ThermostatModeCCSet) {\n\t\t\treturn this.handleThermostatModeCommand(command);\n\t\t} else if (command instanceof CentralSceneCCNotification) {\n\t\t\treturn this.handleCentralSceneNotification(command);\n\t\t} else if (command instanceof WakeUpCCWakeUpNotification) {\n\t\t\treturn this.handleWakeUpNotification();\n\t\t} else if (command instanceof NotificationCCReport) {\n\t\t\treturn this.handleNotificationReport(command);\n\t\t} else if (command instanceof ClockCCReport) {\n\t\t\treturn this.handleClockReport(command);\n\t\t} else if (command instanceof SecurityCCNonceGet) {\n\t\t\treturn this.handleSecurityNonceGet();\n\t\t} else if (command instanceof SecurityCCNonceReport) {\n\t\t\treturn this.handleSecurityNonceReport(command);\n\t\t} else if (command instanceof SecurityCCCommandsSupportedGet) {\n\t\t\treturn this.handleSecurityCommandsSupportedGet(command);\n\t\t} else if (command instanceof Security2CCNonceGet) {\n\t\t\treturn this.handleSecurity2NonceGet();\n\t\t} else if (command instanceof Security2CCNonceReport) {\n\t\t\treturn this.handleSecurity2NonceReport(command);\n\t\t} else if (command instanceof Security2CCCommandsSupportedGet) {\n\t\t\treturn this.handleSecurity2CommandsSupportedGet(command);\n\t\t} else if (command instanceof HailCC) {\n\t\t\treturn this.handleHail(command);\n\t\t} else if (command instanceof FirmwareUpdateMetaDataCCGet) {\n\t\t\treturn this.handleUnexpectedFirmwareUpdateGet(command);\n\t\t} else if (command instanceof FirmwareUpdateMetaDataCCMetaDataGet) {\n\t\t\treturn this.handleFirmwareUpdateMetaDataGet(command);\n\t\t} else if (command instanceof EntryControlCCNotification) {\n\t\t\treturn this.handleEntryControlNotification(command);\n\t\t} else if (command instanceof TimeCCTimeGet) {\n\t\t\treturn this.handleTimeGet(command);\n\t\t} else if (command instanceof TimeCCDateGet) {\n\t\t\treturn this.handleDateGet(command);\n\t\t} else if (command instanceof TimeCCTimeOffsetGet) {\n\t\t\treturn this.handleTimeOffsetGet(command);\n\t\t} else if (command instanceof ZWavePlusCCGet) {\n\t\t\treturn this.handleZWavePlusGet(command);\n\t\t} else if (command instanceof VersionCCGet) {\n\t\t\treturn this.handleVersionGet(command);\n\t\t} else if (command instanceof VersionCCCommandClassGet) {\n\t\t\treturn this.handleVersionCommandClassGet(command);\n\t\t} else if (command instanceof VersionCCCapabilitiesGet) {\n\t\t\treturn this.handleVersionCapabilitiesGet(command);\n\t\t} else if (command instanceof ManufacturerSpecificCCGet) {\n\t\t\treturn this.handleManufacturerSpecificGet(command);\n\t\t} else if (command instanceof AssociationGroupInfoCCNameGet) {\n\t\t\treturn this.handleAGINameGet(command);\n\t\t} else if (command instanceof AssociationGroupInfoCCInfoGet) {\n\t\t\treturn this.handleAGIInfoGet(command);\n\t\t} else if (command instanceof AssociationGroupInfoCCCommandListGet) {\n\t\t\treturn this.handleAGICommandListGet(command);\n\t\t} else if (command instanceof AssociationCCSupportedGroupingsGet) {\n\t\t\treturn this.handleAssociationSupportedGroupingsGet(command);\n\t\t} else if (command instanceof AssociationCCGet) {\n\t\t\treturn this.handleAssociationGet(command);\n\t\t} else if (command instanceof AssociationCCSet) {\n\t\t\treturn this.handleAssociationSet(command);\n\t\t} else if (command instanceof AssociationCCRemove) {\n\t\t\treturn this.handleAssociationRemove(command);\n\t\t} else if (command instanceof AssociationCCSpecificGroupGet) {\n\t\t\treturn this.handleAssociationSpecificGroupGet(command);\n\t\t} else if (\n\t\t\tcommand instanceof MultiChannelAssociationCCSupportedGroupingsGet\n\t\t) {\n\t\t\treturn this.handleMultiChannelAssociationSupportedGroupingsGet(\n\t\t\t\tcommand,\n\t\t\t);\n\t\t} else if (command instanceof MultiChannelAssociationCCGet) {\n\t\t\treturn this.handleMultiChannelAssociationGet(command);\n\t\t} else if (command instanceof MultiChannelAssociationCCSet) {\n\t\t\treturn this.handleMultiChannelAssociationSet(command);\n\t\t} else if (command instanceof MultiChannelAssociationCCRemove) {\n\t\t\treturn this.handleMultiChannelAssociationRemove(command);\n\t\t} else if (command instanceof IndicatorCCSupportedGet) {\n\t\t\treturn this.handleIndicatorSupportedGet(command);\n\t\t} else if (command instanceof IndicatorCCSet) {\n\t\t\treturn this.handleIndicatorSet(command);\n\t\t} else if (command instanceof IndicatorCCGet) {\n\t\t\treturn this.handleIndicatorGet(command);\n\t\t} else if (command instanceof IndicatorCCDescriptionGet) {\n\t\t\treturn this.handleIndicatorDescriptionGet(command);\n\t\t} else if (command instanceof PowerlevelCCSet) {\n\t\t\treturn this.handlePowerlevelSet(command);\n\t\t} else if (command instanceof PowerlevelCCGet) {\n\t\t\treturn this.handlePowerlevelGet(command);\n\t\t} else if (command instanceof PowerlevelCCTestNodeSet) {\n\t\t\treturn this.handlePowerlevelTestNodeSet(command);\n\t\t} else if (command instanceof PowerlevelCCTestNodeGet) {\n\t\t\treturn this.handlePowerlevelTestNodeGet(command);\n\t\t} else if (command instanceof PowerlevelCCTestNodeReport) {\n\t\t\treturn this.handlePowerlevelTestNodeReport(command);\n\t\t} else if (command instanceof DeviceResetLocallyCCNotification) {\n\t\t\treturn this.handleDeviceResetLocallyNotification(command);\n\t\t} else if (command instanceof InclusionControllerCCInitiate) {\n\t\t\t// Inclusion controller commands are handled by the controller class\n\t\t\tif (\n\t\t\t\tcommand.step === InclusionControllerStep.ProxyInclusionReplace\n\t\t\t) {\n\t\t\t\treturn this.driver.controller\n\t\t\t\t\t.handleInclusionControllerCCInitiateReplace(\n\t\t\t\t\t\tcommand,\n\t\t\t\t\t);\n\t\t\t}\n\t\t} else if (command instanceof MultiCommandCCCommandEncapsulation) {\n\t\t\t// Handle each encapsulated command individually\n\t\t\tfor (const cmd of command.encapsulated) {\n\t\t\t\tawait this.handleCommand(cmd);\n\t\t\t}\n\t\t\treturn;\n\t\t} else if (\n\t\t\tcommand instanceof Security2CCMessageEncapsulation\n\t\t\t&& command.encapsulated == undefined\n\t\t) {\n\t\t\t// Some S2 commands contain only extensions. Those are handled by the CC implementation.\n\t\t\treturn;\n\t\t}\n\n\t\t// Ignore all commands that don't need to be handled\n\t\tswitch (true) {\n\t\t\t// Reports are either a response to a Get command or\n\t\t\t// automatically store their values in the Value DB.\n\t\t\t// No need to manually handle them - other than what we've already done\n\t\t\tcase command.constructor.name.endsWith(\"Report\"):\n\n\t\t\t// When this command is received, its values get emitted as an event.\n\t\t\t// Nothing else to do here\n\t\t\tcase command instanceof SceneActivationCCSet:\n\t\t\t\treturn;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: `TODO: no handler for application command`,\n\t\t\tdirection: \"inbound\",\n\t\t});\n\n\t\tif (command.encapsulationFlags & EncapsulationFlags.Supervision) {\n\t\t\t// Report no support for supervised commands we cannot handle\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"No handler for application command\",\n\t\t\t\tZWaveErrorCodes.CC_NotSupported,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate hasLoggedNoNetworkKey = false;\n\n\t/**\n\t * @internal\n\t * Handles a nonce request\n\t */\n\tpublic async handleSecurityNonceGet(): Promise<void> {\n\t\t// Only reply if secure communication is set up\n\t\tif (!this.driver.securityManager) {\n\t\t\tif (!this.hasLoggedNoNetworkKey) {\n\t\t\t\tthis.hasLoggedNoNetworkKey = true;\n\t\t\t\tthis.driver.controllerLog.logNode(this.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\tthis.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() === this.id\n\t\t\t&& containsCC(t.message)\n\t\t\t&& t.message.command instanceof SecurityCCNonceReport;\n\n\t\tif (this.driver.hasPendingTransactions(isNonceReport)) {\n\t\t\tthis.driver.controllerLog.logNode(this.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.driver.securityManager.deleteAllNoncesForReceiver(this.id);\n\n\t\t// Now send the current nonce\n\t\ttry {\n\t\t\tawait this.commandClasses.Security.sendNonce();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(this.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(command: SecurityCCNonceReport): void {\n\t\tconst secMan = this.driver.securityManager;\n\t\tif (!secMan) return;\n\n\t\tsecMan.setNonce(\n\t\t\t{\n\t\t\t\tissuer: this.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.driver.controller.ownNodeId!,\n\t\t\t},\n\t\t\t{ free: true },\n\t\t);\n\t}\n\n\t/**\n\t * @internal\n\t * Handles a nonce request for S2\n\t */\n\tpublic async handleSecurity2NonceGet(): Promise<void> {\n\t\t// Only reply if secure communication is set up\n\t\tif (!this.driver.getSecurityManager2(this.id)) {\n\t\t\tif (!this.hasLoggedNoNetworkKey) {\n\t\t\t\tthis.hasLoggedNoNetworkKey = true;\n\t\t\t\tthis.driver.controllerLog.logNode(this.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\tthis.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() === this.id\n\t\t\t&& containsCC(t.message)\n\t\t\t&& t.message.command instanceof Security2CCNonceReport;\n\n\t\tif (this.driver.hasPendingTransactions(isNonceReport)) {\n\t\t\tthis.driver.controllerLog.logNode(this.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 this.commandClasses[\"Security 2\"].sendNonce();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(this.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(_command: Security2CCNonceReport): void {\n\t\t// const secMan = this.driver.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(this.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.driver.controllerLog.logNode(this.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 busyPollingAfterHail: boolean = false;\n\tprivate async handleHail(_command: HailCC): Promise<void> {\n\t\t// treat this as a sign that the node is awake\n\t\tthis.markAsAwake();\n\n\t\tif (this.busyPollingAfterHail) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Hail received from node, but still busy with previous one...`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tthis.busyPollingAfterHail = true;\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage:\n\t\t\t\t`Hail received from node, refreshing actuator and sensor values...`,\n\t\t});\n\t\ttry {\n\t\t\tawait this.refreshValues();\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t\tthis.busyPollingAfterHail = false;\n\t}\n\n\t/** Stores information about a currently held down key */\n\tprivate centralSceneKeyHeldDownContext:\n\t\t| {\n\t\t\ttimeout: NodeJS.Timeout;\n\t\t\tsceneNumber: number;\n\t\t}\n\t\t| undefined;\n\tprivate lastCentralSceneNotificationSequenceNumber: number | undefined;\n\tprivate centralSceneForcedKeyUp = false;\n\n\t/** Handles the receipt of a Central Scene notifification */\n\tprivate handleCentralSceneNotification(\n\t\tcommand: CentralSceneCCNotification,\n\t): void {\n\t\t// Did we already receive this command?\n\t\tif (\n\t\t\tcommand.sequenceNumber\n\t\t\t\t=== this.lastCentralSceneNotificationSequenceNumber\n\t\t) {\n\t\t\treturn;\n\t\t} else {\n\t\t\tthis.lastCentralSceneNotificationSequenceNumber =\n\t\t\t\tcommand.sequenceNumber;\n\t\t}\n\t\t/*\n\t\tIf the Slow Refresh field is false:\n\t\t- A new Key Held Down notification MUST be sent every 200ms until the key is released.\n\t\t- The Sequence Number field MUST be updated at each notification transmission.\n\t\t- If not receiving a new Key Held Down notification within 400ms, a controlling node SHOULD use an adaptive timeout approach as described in 4.17.1:\n\t\tA controller SHOULD apply an adaptive approach based on the reception of the Key Released Notification.\n\t\tInitially, the controller SHOULD time out if not receiving any Key Held Down Notification refresh after\n\t\t400ms and consider this to be a Key Up Notification. If, however, the controller subsequently receives a\n\t\tKey Released Notification, the controller SHOULD consider the sending node to be operating with the Slow\n\t\tRefresh capability enabled.\n\n\t\tIf the Slow Refresh field is true:\n\t\t- A new Key Held Down notification MUST be sent every 55 seconds until the key is released.\n\t\t- The Sequence Number field MUST be updated at each notification refresh.\n\t\t- If not receiving a new Key Held Down notification within 60 seconds after the most recent Key Held Down\n\t\tnotification, a receiving node MUST respond as if it received a Key Release notification.\n\t\t*/\n\n\t\tconst setSceneValue = (\n\t\t\tsceneNumber: number,\n\t\t\tkey: CentralSceneKeys,\n\t\t): void => {\n\t\t\tconst valueId = CentralSceneCCValues.scene(sceneNumber).id;\n\t\t\tthis.valueDB.setValue(valueId, key, { stateful: false });\n\t\t};\n\n\t\tconst forceKeyUp = (): void => {\n\t\t\tthis.centralSceneForcedKeyUp = true;\n\t\t\t// force key up event\n\t\t\tsetSceneValue(\n\t\t\t\tthis.centralSceneKeyHeldDownContext!.sceneNumber,\n\t\t\t\tCentralSceneKeys.KeyReleased,\n\t\t\t);\n\t\t\t// clear old timer\n\t\t\tclearTimeout(this.centralSceneKeyHeldDownContext!.timeout);\n\t\t\t// clear the key down context\n\t\t\tthis.centralSceneKeyHeldDownContext = undefined;\n\t\t};\n\n\t\tif (\n\t\t\tthis.centralSceneKeyHeldDownContext\n\t\t\t&& this.centralSceneKeyHeldDownContext.sceneNumber\n\t\t\t\t!== command.sceneNumber\n\t\t) {\n\t\t\t// The user pressed another button, force release\n\t\t\tforceKeyUp();\n\t\t}\n\n\t\tconst slowRefreshValueId = CentralSceneCCValues.slowRefresh.endpoint(\n\t\t\tthis.index,\n\t\t);\n\t\tif (command.keyAttribute === CentralSceneKeys.KeyHeldDown) {\n\t\t\t// Set or refresh timer to force a release of the key\n\t\t\tthis.centralSceneForcedKeyUp = false;\n\t\t\tif (this.centralSceneKeyHeldDownContext) {\n\t\t\t\tclearTimeout(this.centralSceneKeyHeldDownContext.timeout);\n\t\t\t}\n\t\t\t// If the node does not advertise support for the slow refresh capability, we might still be dealing with a\n\t\t\t// slow refresh node. We use the stored value for fallback behavior\n\t\t\tconst slowRefresh = command.slowRefresh\n\t\t\t\t?? this.valueDB.getValue<boolean>(slowRefreshValueId);\n\t\t\tthis.centralSceneKeyHeldDownContext = {\n\t\t\t\tsceneNumber: command.sceneNumber,\n\t\t\t\t// Unref'ing long running timers allows the process to exit mid-timeout\n\t\t\t\ttimeout: setTimeout(\n\t\t\t\t\tforceKeyUp,\n\t\t\t\t\tslowRefresh ? 60000 : 400,\n\t\t\t\t).unref(),\n\t\t\t};\n\t\t} else if (command.keyAttribute === CentralSceneKeys.KeyReleased) {\n\t\t\t// Stop the release timer\n\t\t\tif (this.centralSceneKeyHeldDownContext) {\n\t\t\t\tclearTimeout(this.centralSceneKeyHeldDownContext.timeout);\n\t\t\t\tthis.centralSceneKeyHeldDownContext = undefined;\n\t\t\t} else if (this.centralSceneForcedKeyUp) {\n\t\t\t\t// If we timed out and the controller subsequently receives a Key Released Notification,\n\t\t\t\t// we SHOULD consider the sending node to be operating with the Slow Refresh capability enabled.\n\t\t\t\tthis.valueDB.setValue(slowRefreshValueId, true);\n\t\t\t\t// Do not raise the duplicate event\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tsetSceneValue(command.sceneNumber, command.keyAttribute);\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: `received CentralScene notification ${stringify(command)}`,\n\t\t\tdirection: \"inbound\",\n\t\t});\n\t}\n\n\t/** The timestamp of the last received wakeup notification */\n\tprivate lastWakeUp: number | undefined;\n\n\t/** Handles the receipt of a Wake Up notification */\n\tprivate handleWakeUpNotification(): void {\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: `received wakeup notification`,\n\t\t\tdirection: \"inbound\",\n\t\t});\n\n\t\t// It can happen that the node has not told us that it supports the Wake Up CC\n\t\t// https://sentry.io/share/issue/6a681729d7db46d591f1dcadabe8d02e/\n\t\t// To avoid a crash, mark it as supported\n\t\tif (this.getCCVersion(CommandClasses[\"Wake Up\"]) === 0) {\n\t\t\tthis.addCC(CommandClasses[\"Wake Up\"], {\n\t\t\t\tisSupported: true,\n\t\t\t\tversion: 1,\n\t\t\t});\n\t\t}\n\n\t\tthis.markAsAwake();\n\n\t\t// From the specs:\n\t\t// A controlling node SHOULD read the Wake Up Interval of a supporting node when the delays between\n\t\t// Wake Up periods are larger than what was last set at the supporting node.\n\t\tconst now = Date.now();\n\t\tif (this.lastWakeUp) {\n\t\t\t// we've already measured the wake up interval, so we can check whether a refresh is necessary\n\t\t\tconst wakeUpInterval =\n\t\t\t\tthis.getValue<number>(WakeUpCCValues.wakeUpInterval.id) ?? 1;\n\t\t\t// The wakeup interval is specified in seconds. Also add 5 minutes tolerance to avoid\n\t\t\t// unnecessary queries since there might be some delay. A wakeup interval of 0 means manual wakeup,\n\t\t\t// so the interval shouldn't be verified\n\t\t\tif (\n\t\t\t\twakeUpInterval > 0\n\t\t\t\t&& (now - this.lastWakeUp) / 1000 > wakeUpInterval + 5 * 60\n\t\t\t) {\n\t\t\t\tthis.commandClasses[\"Wake Up\"].getInterval().catch(() => {\n\t\t\t\t\t// Don't throw if there's an error\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.lastWakeUp = now;\n\n\t\t// Some legacy devices expect us to query them on wake up in order to function correctly\n\t\tif (this._deviceConfig?.compat?.queryOnWakeup) {\n\t\t\tvoid this.compatDoWakeupQueries();\n\t\t} else if (!this._deviceConfig?.compat?.disableAutoRefresh) {\n\t\t\t// For other devices we may have to refresh their values from time to time\n\t\t\tvoid this.autoRefreshValues().catch(() => {\n\t\t\t\t// ignore\n\t\t\t});\n\t\t}\n\n\t\t// In case there are no messages in the queue, the node may go back to sleep very soon\n\t\tthis.driver.debounceSendNodeToSleep(this);\n\t}\n\n\tprivate async compatDoWakeupQueries(): Promise<void> {\n\t\tif (!this._deviceConfig?.compat?.queryOnWakeup) return;\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: `expects some queries after wake up, so it shall receive`,\n\t\t\tdirection: \"none\",\n\t\t});\n\n\t\tfor (\n\t\t\tconst [ccName, apiMethod, ...args] of this._deviceConfig.compat\n\t\t\t\t.queryOnWakeup\n\t\t) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: `compat query \"${ccName}\"::${apiMethod}(${\n\t\t\t\t\targs\n\t\t\t\t\t\t.map((arg) => JSON.stringify(arg))\n\t\t\t\t\t\t.join(\", \")\n\t\t\t\t})`,\n\t\t\t\tdirection: \"none\",\n\t\t\t});\n\n\t\t\t// Try to access the API - if it doesn't work, skip this option\n\t\t\tlet API: CCAPI;\n\t\t\ttry {\n\t\t\t\tAPI = (\n\t\t\t\t\t(this.commandClasses as any)[ccName] as CCAPI\n\t\t\t\t).withOptions({\n\t\t\t\t\t// Tag the resulting transactions as compat queries\n\t\t\t\t\ttag: \"compat\",\n\t\t\t\t\t// Do not retry them or they may cause congestion if the node is asleep again\n\t\t\t\t\tmaxSendAttempts: 1,\n\t\t\t\t\t// This is for a sleeping node - there's no point in keeping the transactions when the node is asleep\n\t\t\t\t\texpire: 10000,\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `could not access API, skipping query`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!API.isSupported()) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `API not supported, skipping query`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t} else if (!(API as any)[apiMethod]) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`method ${apiMethod} not found on API, skipping query`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Retrieve the method\n\t\t\t// eslint-disable-next-line\n\t\t\tconst method = (API as any)[apiMethod].bind(API) as Function;\n\t\t\t// And replace \"smart\" arguments with their corresponding value\n\t\t\tconst methodArgs = args.map<unknown>((arg) => {\n\t\t\t\tif (isObject(arg)) {\n\t\t\t\t\tconst valueId = {\n\t\t\t\t\t\tcommandClass: API.ccId,\n\t\t\t\t\t\t...arg,\n\t\t\t\t\t};\n\t\t\t\t\treturn this.getValue(valueId);\n\t\t\t\t}\n\t\t\t\treturn arg;\n\t\t\t});\n\n\t\t\t// Do the API call and ignore/log any errors\n\t\t\ttry {\n\t\t\t\tawait method(...methodArgs);\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `API call successful`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t});\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `error during API call: ${getErrorMessage(e)}`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tif (\n\t\t\t\t\tisZWaveError(e)\n\t\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_MessageExpired\n\t\t\t\t) {\n\t\t\t\t\t// A compat query expired - no point in trying the others too\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Handles the receipt of a BasicCC Set or Report */\n\tprivate handleBasicCommand(command: BasicCC): void {\n\t\t// Retrieve the endpoint the command is coming from\n\t\tconst sourceEndpoint = this.getEndpoint(command.endpointIndex ?? 0)\n\t\t\t?? this;\n\n\t\t// Depending on the generic device class, we may need to map the basic command to other CCs\n\t\tlet mappedTargetCC: CommandClass | undefined;\n\t\tswitch (sourceEndpoint.deviceClass?.generic.key) {\n\t\t\tcase 0x20: // Binary Sensor\n\t\t\t\tmappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(\n\t\t\t\t\tCommandClasses[\"Binary Sensor\"],\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 0x10: // Binary Switch\n\t\t\t\tmappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(\n\t\t\t\t\tCommandClasses[\"Binary Switch\"],\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 0x11: // Multilevel Switch\n\t\t\t\tmappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(\n\t\t\t\t\tCommandClasses[\"Multilevel Switch\"],\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 0x12: // Remote Switch\n\t\t\t\tswitch (sourceEndpoint.deviceClass.specific.key) {\n\t\t\t\t\tcase 0x01: // Binary Remote Switch\n\t\t\t\t\t\tmappedTargetCC = sourceEndpoint\n\t\t\t\t\t\t\t.createCCInstanceUnsafe(\n\t\t\t\t\t\t\t\tCommandClasses[\"Binary Switch\"],\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 0x02: // Multilevel Remote Switch\n\t\t\t\t\t\tmappedTargetCC = sourceEndpoint\n\t\t\t\t\t\t\t.createCCInstanceUnsafe(\n\t\t\t\t\t\t\t\tCommandClasses[\"Multilevel Switch\"],\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\n\t\tif (command instanceof BasicCCReport) {\n\t\t\t// By default, map Basic CC Reports to a more appropriate CC, unless stated otherwise in a config file\n\t\t\tconst basicReportMapping = this.deviceConfig?.compat?.mapBasicReport\n\t\t\t\t?? \"auto\";\n\n\t\t\tif (basicReportMapping === \"Binary Sensor\") {\n\t\t\t\t// Treat the command as a BinarySensorCC Report, regardless of the device class\n\t\t\t\tmappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(\n\t\t\t\t\tCommandClasses[\"Binary Sensor\"],\n\t\t\t\t);\n\t\t\t\tif (typeof command.currentValue === \"number\") {\n\t\t\t\t\tif (mappedTargetCC) {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\"treating BasicCC::Report as a BinarySensorCC::Report\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tmappedTargetCC.setMappedBasicValue(\n\t\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\t\tcommand.currentValue,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\"cannot treat BasicCC::Report as a BinarySensorCC::Report, because the Binary Sensor CC is not supported\",\n\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"cannot map BasicCC::Report to a different CC, because the current value is unknown\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\tbasicReportMapping === \"auto\" || basicReportMapping === false\n\t\t\t) {\n\t\t\t\t// Try to set the mapped value on the target CC\n\t\t\t\tconst didSetMappedValue =\n\t\t\t\t\ttypeof command.currentValue === \"number\"\n\t\t\t\t\t// ... unless forbidden\n\t\t\t\t\t&& basicReportMapping === \"auto\"\n\t\t\t\t\t&& mappedTargetCC?.setMappedBasicValue(\n\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\tcommand.currentValue,\n\t\t\t\t\t);\n\n\t\t\t\t// Otherwise fall back to setting it ourselves\n\t\t\t\tif (!didSetMappedValue) {\n\t\t\t\t\t// Store the value in the value DB now\n\t\t\t\t\tcommand.persistValues(this.driver);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (command instanceof BasicCCSet) {\n\t\t\t// By default, map Basic CC Set to Basic CC Report, unless stated otherwise in a config file\n\t\t\tconst basicSetMapping = this.deviceConfig?.compat?.mapBasicSet\n\t\t\t\t?? \"report\";\n\n\t\t\tif (basicSetMapping === \"event\") {\n\t\t\t\t// Treat BasicCCSet as value events if desired\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\tmessage: \"treating BasicCC::Set as a value event\",\n\t\t\t\t});\n\t\t\t\tthis._valueDB.setValue(\n\t\t\t\t\tBasicCCValues.compatEvent.endpoint(\n\t\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t\t),\n\t\t\t\t\tcommand.targetValue,\n\t\t\t\t\t{\n\t\t\t\t\t\tstateful: false,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} else if (basicSetMapping === \"Binary Sensor\") {\n\t\t\t\t// Treat the Set command as a BinarySensorCC Report, regardless of the device class\n\t\t\t\tmappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(\n\t\t\t\t\tCommandClasses[\"Binary Sensor\"],\n\t\t\t\t);\n\t\t\t\tif (mappedTargetCC) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"treating BasicCC::Set as a BinarySensorCC::Report\",\n\t\t\t\t\t});\n\t\t\t\t\tmappedTargetCC.setMappedBasicValue(\n\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\tcommand.targetValue,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"cannot treat BasicCC::Set as a BinarySensorCC::Report, because the Binary Sensor CC is not supported\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\t!this.deviceConfig?.compat?.mapBasicSet\n\t\t\t\t&& !!(command.encapsulationFlags\n\t\t\t\t\t& EncapsulationFlags.Supervision)\n\t\t\t) {\n\t\t\t\t// A controller MUST not support Basic CC per the specifications. While we can interpret its contents,\n\t\t\t\t// we MUST respond to supervised Basic CC Set with \"no support\".\n\t\t\t\t// All known devices that use BasicCCSet for reporting send it unsupervised, so this should be safe to do.\n\t\t\t\tif (\n\t\t\t\t\tcommand.encapsulationFlags & EncapsulationFlags.Supervision\n\t\t\t\t) {\n\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\"Basic CC is not supported\",\n\t\t\t\t\t\tZWaveErrorCodes.CC_NotSupported,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\tbasicSetMapping === \"auto\" || basicSetMapping === \"report\"\n\t\t\t) {\n\t\t\t\t// Some devices send their current state using BasicCCSet to their associations\n\t\t\t\t// instead of using reports. We still interpret them like reports\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\tmessage: \"treating BasicCC::Set as a report\",\n\t\t\t\t});\n\n\t\t\t\t// In \"auto\" mode, try to set the mapped value on the target CC first\n\t\t\t\tconst didSetMappedValue = basicSetMapping === \"auto\"\n\t\t\t\t\t&& !!mappedTargetCC?.setMappedBasicValue(\n\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\tcommand.targetValue,\n\t\t\t\t\t);\n\n\t\t\t\t// Otherwise handle the command ourselves\n\t\t\t\tif (!didSetMappedValue) {\n\t\t\t\t\t// Basic Set commands cannot store their value automatically, so store the values manually\n\t\t\t\t\tthis._valueDB.setValue(\n\t\t\t\t\t\tBasicCCValues.currentValue.endpoint(\n\t\t\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tcommand.targetValue,\n\t\t\t\t\t);\n\t\t\t\t\t// Since the node sent us a Basic Set, we are sure that it is at least controlled\n\t\t\t\t\t// Add it to the support list, so the information lands in the network cache\n\t\t\t\t\tif (!sourceEndpoint.controlsCC(CommandClasses.Basic)) {\n\t\t\t\t\t\tsourceEndpoint.addCC(CommandClasses.Basic, {\n\t\t\t\t\t\t\tisControlled: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Handles the receipt of a MultilevelCC Set or Report */\n\tprivate handleMultilevelSwitchCommand(command: MultilevelSwitchCC): void {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex ?? 0) ?? this;\n\n\t\tif (command instanceof MultilevelSwitchCCSet) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\tmessage: \"treating MultiLevelSwitchCCSet::Set as a value event\",\n\t\t\t});\n\t\t\tthis._valueDB.setValue(\n\t\t\t\tMultilevelSwitchCCValues.compatEvent.endpoint(\n\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t),\n\t\t\t\tcommand.targetValue,\n\t\t\t\t{\n\t\t\t\t\tstateful: false,\n\t\t\t\t},\n\t\t\t);\n\t\t} else if (command instanceof MultilevelSwitchCCStartLevelChange) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\tmessage:\n\t\t\t\t\t\"treating MultilevelSwitchCC::StartLevelChange as a notification\",\n\t\t\t});\n\t\t\tthis.emit(\n\t\t\t\t\"notification\",\n\t\t\t\tendpoint,\n\t\t\t\tCommandClasses[\"Multilevel Switch\"],\n\t\t\t\t{\n\t\t\t\t\teventType: MultilevelSwitchCommand.StartLevelChange,\n\t\t\t\t\teventTypeLabel: \"Start level change\",\n\t\t\t\t\tdirection: command.direction,\n\t\t\t\t},\n\t\t\t);\n\t\t} else if (command instanceof MultilevelSwitchCCStopLevelChange) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\tmessage:\n\t\t\t\t\t\"treating MultilevelSwitchCC::StopLevelChange as a notification\",\n\t\t\t});\n\t\t\tthis.emit(\n\t\t\t\t\"notification\",\n\t\t\t\tendpoint,\n\t\t\t\tCommandClasses[\"Multilevel Switch\"],\n\t\t\t\t{\n\t\t\t\t\teventType: MultilevelSwitchCommand.StopLevelChange,\n\t\t\t\t\teventTypeLabel: \"Stop level change\",\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate handleBinarySwitchCommand(command: BinarySwitchCC): void {\n\t\t// Treat BinarySwitchCCSet as a report if desired\n\t\tif (\n\t\t\tcommand instanceof BinarySwitchCCSet\n\t\t\t&& this._deviceConfig?.compat?.treatSetAsReport?.has(\n\t\t\t\tcommand.constructor.name,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\tmessage: \"treating BinarySwitchCC::Set as a report\",\n\t\t\t});\n\t\t\tthis._valueDB.setValue(\n\t\t\t\tBinarySwitchCCValues.currentValue.endpoint(\n\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t),\n\t\t\t\tcommand.targetValue,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate handleThermostatModeCommand(command: ThermostatModeCC): void {\n\t\t// Treat ThermostatModeCCSet as a report if desired\n\t\tif (\n\t\t\tcommand instanceof ThermostatModeCCSet\n\t\t\t&& this._deviceConfig?.compat?.treatSetAsReport?.has(\n\t\t\t\tcommand.constructor.name,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\tmessage: \"treating ThermostatModeCC::Set as a report\",\n\t\t\t});\n\t\t\tthis._valueDB.setValue(\n\t\t\t\tThermostatModeCCValues.thermostatMode.endpoint(\n\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t),\n\t\t\t\tcommand.mode,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async handleZWavePlusGet(command: ZWavePlusCCGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tawait endpoint\n\t\t\t.createAPI(CommandClasses[\"Z-Wave Plus Info\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t})\n\t\t\t.sendReport({\n\t\t\t\tzwavePlusVersion: 2,\n\t\t\t\troleType: ZWavePlusRoleType.CentralStaticController,\n\t\t\t\tnodeType: ZWavePlusNodeType.Node,\n\t\t\t\tinstallerIcon: this.driver.options.vendor?.installerIcon\n\t\t\t\t\t?? 0x0500, // Generic Gateway\n\t\t\t\tuserIcon: this.driver.options.vendor?.userIcon ?? 0x0500, // Generic Gateway\n\t\t\t});\n\t}\n\n\tprivate async handleVersionGet(command: VersionCCGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Version, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tconst firmwareVersion1 = semverParse(libVersion, { loose: true })!;\n\n\t\tawait api.sendReport({\n\t\t\tlibraryType: ZWaveLibraryTypes[\"Static Controller\"],\n\t\t\tprotocolVersion: this.driver.controller.protocolVersion!,\n\t\t\tfirmwareVersions: [\n\t\t\t\t// Firmware 0 is the Z-Wave chip firmware\n\t\t\t\tthis.driver.controller.firmwareVersion!,\n\t\t\t\t// Firmware 1 is Z-Wave JS itself\n\t\t\t\t`${firmwareVersion1.major}.${firmwareVersion1.minor}.${firmwareVersion1.patch}`,\n\t\t\t],\n\t\t\thardwareVersion: this.driver.options.vendor?.hardwareVersion,\n\t\t});\n\t}\n\n\tprivate async handleVersionCommandClassGet(\n\t\tcommand: VersionCCCommandClassGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Version, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.reportCCVersion(command.requestedCC);\n\t}\n\n\tprivate async handleVersionCapabilitiesGet(\n\t\tcommand: VersionCCCapabilitiesGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Version, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.reportCapabilities();\n\t}\n\n\tprivate async handleManufacturerSpecificGet(\n\t\tcommand: ManufacturerSpecificCCGet,\n\t): Promise<void> {\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = this\n\t\t\t.createAPI(CommandClasses[\"Manufacturer Specific\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.sendReport({\n\t\t\tmanufacturerId: this.driver.options.vendor?.manufacturerId\n\t\t\t\t?? 0xffff, // Reserved manufacturer ID, definitely invalid!\n\t\t\tproductType: this.driver.options.vendor?.productType ?? 0xffff,\n\t\t\tproductId: this.driver.options.vendor?.productId ?? 0xffff,\n\t\t});\n\t}\n\n\tprivate async handleAGINameGet(\n\t\tcommand: AssociationGroupInfoCCNameGet,\n\t): Promise<void> {\n\t\tif (command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group\n\t\t\treturn;\n\t\t}\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses[\"Association Group Information\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.reportGroupName(1, \"Lifeline\");\n\t}\n\n\tprivate async handleAGIInfoGet(\n\t\tcommand: AssociationGroupInfoCCInfoGet,\n\t): Promise<void> {\n\t\tif (!command.listMode && command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group\n\t\t\treturn;\n\t\t}\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses[\"Association Group Information\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.reportGroupInfo({\n\t\t\tisListMode: command.listMode ?? false,\n\t\t\thasDynamicInfo: false,\n\t\t\tgroups: [\n\t\t\t\t{\n\t\t\t\t\tgroupId: 1,\n\t\t\t\t\teventCode: 0, // ignored anyways\n\t\t\t\t\tprofile: AssociationGroupInfoProfile[\"General: Lifeline\"],\n\t\t\t\t\tmode: 0, // ignored anyways\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\tprivate async handleAGICommandListGet(\n\t\tcommand: AssociationGroupInfoCCCommandListGet,\n\t): Promise<void> {\n\t\tif (command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group\n\t\t\treturn;\n\t\t}\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses[\"Association Group Information\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.reportCommands(\n\t\t\tcommand.groupId,\n\t\t\tnew Map([\n\t\t\t\t[\n\t\t\t\t\tCommandClasses[\"Device Reset Locally\"],\n\t\t\t\t\t[DeviceResetLocallyCommand.Notification],\n\t\t\t\t],\n\t\t\t]),\n\t\t);\n\t}\n\n\tprivate async handleAssociationSupportedGroupingsGet(\n\t\tcommand: AssociationCCSupportedGroupingsGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Association, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\t// We only \"support\" the lifeline group\n\t\tawait api.reportGroupCount(1);\n\t}\n\n\tprivate async handleAssociationGet(\n\t\tcommand: AssociationCCGet,\n\t): Promise<void> {\n\t\t// We only \"support\" the lifeline group\n\t\tconst groupId = 1;\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Association, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tconst nodeIds =\n\t\t\tthis.driver.controller.associations.filter((a) =>\n\t\t\t\ta.endpoint == undefined\n\t\t\t)\n\t\t\t\t.map((a) => a.nodeId) ?? [];\n\n\t\tawait api.sendReport({\n\t\t\tgroupId,\n\t\t\tmaxNodes: MAX_ASSOCIATIONS,\n\t\t\tnodeIds,\n\t\t\treportsToFollow: 0,\n\t\t});\n\t}\n\n\tprivate handleAssociationSet(command: AssociationCCSet): void {\n\t\tif (command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group.\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Association group ${command.groupId} is not supported.`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\n\t\t// Ignore associations that already exist\n\t\tconst newAssociations = command.nodeIds.filter((newNodeId) =>\n\t\t\t!this.driver.controller.associations.some(\n\t\t\t\t({ nodeId, endpoint }) =>\n\t\t\t\t\tendpoint === undefined && nodeId === newNodeId,\n\t\t\t)\n\t\t).map((nodeId) => ({ nodeId }));\n\n\t\tconst associations = [...this.driver.controller.associations];\n\t\tassociations.push(...newAssociations);\n\n\t\t// Report error if the association group is already full\n\t\tif (associations.length > MAX_ASSOCIATIONS) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Association group ${command.groupId} is full`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\t\tthis.driver.controller.associations = associations;\n\t}\n\n\tprivate handleAssociationRemove(command: AssociationCCRemove): void {\n\t\t// Allow accessing the lifeline group or all groups (which is the same)\n\t\tif (!!command.groupId && command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group\n\t\t\treturn;\n\t\t}\n\n\t\tif (!command.nodeIds?.length) {\n\t\t\t// clear\n\t\t\tthis.driver.controller.associations = [];\n\t\t} else {\n\t\t\tthis.driver.controller.associations = this.driver.controller\n\t\t\t\t.associations.filter(\n\t\t\t\t\t({ nodeId, endpoint }) =>\n\t\t\t\t\t\tendpoint === undefined\n\t\t\t\t\t\t&& !command.nodeIds!.includes(nodeId),\n\t\t\t\t);\n\t\t}\n\t}\n\n\tprivate async handleAssociationSpecificGroupGet(\n\t\tcommand: AssociationCCSpecificGroupGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Association, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\t// We don't support this feature.\n\t\t// It is RECOMMENDED that the value 0 is returned by non-supporting devices.\n\t\tawait api.reportSpecificGroup(0);\n\t}\n\n\tprivate async handleMultiChannelAssociationSupportedGroupingsGet(\n\t\tcommand: MultiChannelAssociationCCSupportedGroupingsGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses[\"Multi Channel Association\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\t// We only \"support\" the lifeline group\n\t\tawait api.reportGroupCount(1);\n\t}\n\n\tprivate async handleMultiChannelAssociationGet(\n\t\tcommand: MultiChannelAssociationCCGet,\n\t): Promise<void> {\n\t\t// We only \"support\" the lifeline group\n\t\tconst groupId = 1;\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses[\"Multi Channel Association\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tconst nodeIds =\n\t\t\tthis.driver.controller.associations.filter((a) =>\n\t\t\t\ta.endpoint == undefined\n\t\t\t)\n\t\t\t\t.map((a) => a.nodeId) ?? [];\n\t\tconst endpoints =\n\t\t\tthis.driver.controller.associations.filter((a) =>\n\t\t\t\ta.endpoint != undefined\n\t\t\t)\n\t\t\t\t.map(({ nodeId, endpoint }) => ({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tendpoint: endpoint!,\n\t\t\t\t}))\n\t\t\t\t?? [];\n\n\t\tawait api.sendReport({\n\t\t\tgroupId,\n\t\t\tmaxNodes: MAX_ASSOCIATIONS,\n\t\t\tnodeIds,\n\t\t\tendpoints,\n\t\t\treportsToFollow: 0,\n\t\t});\n\t}\n\n\tprivate handleMultiChannelAssociationSet(\n\t\tcommand: MultiChannelAssociationCCSet,\n\t): void {\n\t\tif (command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group.\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Multi Channel Association group ${command.groupId} is not supported.`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\n\t\t// Ignore associations that already exists\n\t\tconst newNodeIdAssociations = command.nodeIds.filter((newNodeId) =>\n\t\t\t!this.driver.controller.associations.some(\n\t\t\t\t({ nodeId, endpoint }) =>\n\t\t\t\t\tendpoint === undefined && nodeId === newNodeId,\n\t\t\t)\n\t\t).map((nodeId) => ({ nodeId }));\n\t\tconst newEndpointAssociations = command.endpoints.flatMap(\n\t\t\t({ nodeId, endpoint }) => {\n\t\t\t\tif (typeof endpoint === \"number\") {\n\t\t\t\t\treturn { nodeId, endpoint };\n\t\t\t\t} else {\n\t\t\t\t\treturn endpoint.map((e) => ({ nodeId, endpoint: e }));\n\t\t\t\t}\n\t\t\t},\n\t\t).filter(({ nodeId: newNodeId, endpoint: newEndpoint }) =>\n\t\t\t!this.driver.controller.associations.some(({ nodeId, endpoint }) =>\n\t\t\t\tnodeId === newNodeId && endpoint === newEndpoint\n\t\t\t)\n\t\t);\n\n\t\tconst associations = [...this.driver.controller.associations];\n\t\tassociations.push(...newNodeIdAssociations, ...newEndpointAssociations);\n\n\t\t// Report error if the association group is already full\n\t\tif (associations.length > MAX_ASSOCIATIONS) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Multi Channel Association group ${command.groupId} is full`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controller.associations = associations.slice(\n\t\t\t0,\n\t\t\tMAX_ASSOCIATIONS,\n\t\t);\n\t}\n\n\tprivate handleMultiChannelAssociationRemove(\n\t\tcommand: MultiChannelAssociationCCRemove,\n\t): void {\n\t\t// Allow accessing the lifeline group or all groups (which is the same)\n\t\tif (!!command.groupId && command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group\n\t\t\treturn;\n\t\t}\n\n\t\tif (!command.nodeIds?.length && !command.endpoints?.length) {\n\t\t\t// Clear all associations\n\t\t\tthis.driver.controller.associations = [];\n\t\t} else {\n\t\t\tlet associations = [...this.driver.controller.associations];\n\t\t\tif (command.nodeIds?.length) {\n\t\t\t\tassociations = associations.filter(\n\t\t\t\t\t({ nodeId, endpoint }) =>\n\t\t\t\t\t\tendpoint === undefined\n\t\t\t\t\t\t&& !command.nodeIds!.includes(nodeId),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (command.endpoints?.length) {\n\t\t\t\tassociations = associations.filter(\n\t\t\t\t\t({ nodeId, endpoint }) =>\n\t\t\t\t\t\t!command.endpoints!.some((dest) =>\n\t\t\t\t\t\t\tdest.nodeId === nodeId && dest.endpoint === endpoint\n\t\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.driver.controller.associations = associations;\n\t\t}\n\t}\n\n\tprivate handleIndicatorSupportedGet(\n\t\tcommand: IndicatorCCSupportedGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Indicator, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tswitch (command.indicatorId) {\n\t\t\tcase 0:\n\t\t\t// 0 must be answered with the first supported indicator ID.\n\t\t\t// We only support identify (0x50)\n\t\t\tcase 0x50:\n\t\t\t\t// Identify\n\t\t\t\treturn api.reportSupported(0x50, [0x03, 0x04, 0x05], 0);\n\t\t\tdefault:\n\t\t\t\t// A supporting node receiving a non-zero Indicator ID that is\n\t\t\t\t// not supported MUST set all fields to 0x00 in the returned response.\n\t\t\t\treturn api.reportSupported(0, [], 0);\n\t\t}\n\t}\n\n\tprivate handleIndicatorSet(command: IndicatorCCSet): void {\n\t\t// We only support \"identify\"\n\t\tif (command.values?.length !== 3) return;\n\t\tconst [v1, v2, v3] = command.values;\n\t\tif (v1.indicatorId !== 0x50 || v1.propertyId !== 0x03) return;\n\t\tif (v2.indicatorId !== 0x50 || v2.propertyId !== 0x04) return;\n\t\tif (v3.indicatorId !== 0x50 || v3.propertyId !== 0x05) return;\n\n\t\t// This isn't really sane, but since we only support a single indicator, it's fine\n\t\tconst store = this.driver.controller.indicatorValues;\n\t\tstore.set(0x50, [v1, v2, v3]);\n\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: \"Received identify command\",\n\t\t\tdirection: \"inbound\",\n\t\t});\n\n\t\tthis.driver.controller.emit(\"identify\", this);\n\t}\n\n\tprivate async handleIndicatorGet(command: IndicatorCCGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Indicator, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\t// We only support \"identify\"\n\t\tif (command.indicatorId === 0x50) {\n\t\t\tconst values = this.driver.controller.indicatorValues.get(0x50) ?? [\n\t\t\t\t{ indicatorId: 0x50, propertyId: 0x03, value: 0 },\n\t\t\t\t{ indicatorId: 0x50, propertyId: 0x04, value: 0 },\n\t\t\t\t{ indicatorId: 0x50, propertyId: 0x05, value: 0 },\n\t\t\t];\n\t\t\tawait api.sendReport({ values });\n\t\t} else if (typeof command.indicatorId === \"number\") {\n\t\t\t// V2+ report\n\t\t\tawait api.sendReport({\n\t\t\t\tvalues: [\n\t\t\t\t\t{\n\t\t\t\t\t\tindicatorId: command.indicatorId,\n\t\t\t\t\t\tpropertyId: 0,\n\t\t\t\t\t\tvalue: 0,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t} else {\n\t\t\t// V1+ report\n\t\t\tawait api.sendReport({ value: 0 });\n\t\t}\n\t}\n\n\tprivate async handleIndicatorDescriptionGet(\n\t\tcommand: IndicatorCCDescriptionGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Indicator, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\t// We only support \"identify\" (0x50) and requests for indicators outside the 0x80...0x9f range\n\t\t// MUST return an Indicator Description Report with the Description Length set to 0.\n\t\t// So we can just always do that.\n\t\tawait api.reportDescription(command.indicatorId, \"\");\n\t}\n\n\tprivate handlePowerlevelSet(command: PowerlevelCCSet): void {\n\t\t// Check if the powerlevel is valid\n\t\tif (!(command.powerlevel in Powerlevel)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Invalid powerlevel ${command.powerlevel}.`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\n\t\t// CC:0073.01.01.11.001: A supporting node MAY decide not to change its actual Tx configuration.\n\t\t// In any case, the value received in this Command MUST be returned in a Powerlevel Report Command\n\t\t// in response to a Powerlevel Get Command as if the power setting was accepted for the indicated duration.\n\t\tthis.driver.controller.powerlevel = {\n\t\t\tpowerlevel: command.powerlevel,\n\t\t\tuntil: command.timeout\n\t\t\t\t? new Date(Date.now() + command.timeout * 1000)\n\t\t\t\t: new Date(),\n\t\t};\n\t}\n\n\tprivate async handlePowerlevelGet(command: PowerlevelCCGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Powerlevel, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tconst { powerlevel, until } = this.driver.controller.powerlevel;\n\n\t\tif (\n\t\t\t// Setting elapsed\n\t\t\tuntil.getTime() < Date.now()\n\t\t\t// or it is already set to normal power\n\t\t\t|| powerlevel === Powerlevel[\"Normal Power\"]\n\t\t) {\n\t\t\tawait api.reportPowerlevel({\n\t\t\t\tpowerlevel: Powerlevel[\"Normal Power\"],\n\t\t\t});\n\t\t} else {\n\t\t\tconst timeoutSeconds = Math.max(\n\t\t\t\t0,\n\t\t\t\tMath.min(\n\t\t\t\t\tMath.round((until.getTime() - Date.now()) / 1000),\n\t\t\t\t\t255,\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tawait api.reportPowerlevel({\n\t\t\t\tpowerlevel,\n\t\t\t\ttimeout: timeoutSeconds,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async handlePowerlevelTestNodeSet(\n\t\tcommand: PowerlevelCCTestNodeSet,\n\t): Promise<void> {\n\t\t// Check if the powerlevel is valid\n\t\tif (!(command.powerlevel in Powerlevel)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Invalid powerlevel ${command.powerlevel}.`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t} else if (command.testFrameCount < 1) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"testFrameCount must be at least 1\",\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Powerlevel, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\ttry {\n\t\t\tconst acknowledgedFrames = await this.driver.sendNOPPowerFrames(\n\t\t\t\tcommand.testNodeId,\n\t\t\t\tcommand.powerlevel,\n\t\t\t\tcommand.testFrameCount,\n\t\t\t);\n\t\t\t// Test results are in, send report\n\t\t\tvoid api.sendNodeTestReport({\n\t\t\t\tstatus: acknowledgedFrames > 0\n\t\t\t\t\t? PowerlevelTestStatus.Success\n\t\t\t\t\t: PowerlevelTestStatus.Failed,\n\t\t\t\ttestNodeId: command.testNodeId,\n\t\t\t\tacknowledgedFrames,\n\t\t\t}).catch(noop);\n\t\t} catch {\n\t\t\t// Test failed for some reason (e.g. invalid node)\n\t\t\tvoid api.sendNodeTestReport({\n\t\t\t\tstatus: PowerlevelTestStatus.Failed,\n\t\t\t\ttestNodeId: command.testNodeId,\n\t\t\t\tacknowledgedFrames: 0,\n\t\t\t}).catch(noop);\n\t\t}\n\t}\n\n\tprivate async handlePowerlevelTestNodeGet(\n\t\tcommand: PowerlevelCCTestNodeGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Powerlevel, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tconst status = this.driver.getNOPPowerTestStatus();\n\n\t\tif (status) {\n\t\t\tawait api.sendNodeTestReport({\n\t\t\t\tstatus: status.inProgress\n\t\t\t\t\t? PowerlevelTestStatus[\"In Progress\"]\n\t\t\t\t\t: status.acknowledgedFrames > 0\n\t\t\t\t\t? PowerlevelTestStatus.Success\n\t\t\t\t\t: PowerlevelTestStatus.Failed,\n\t\t\t\t...status,\n\t\t\t});\n\t\t} else {\n\t\t\t// No test was done\n\t\t\tawait api.sendNodeTestReport({\n\t\t\t\tstatus: PowerlevelTestStatus.Success,\n\t\t\t\ttestNodeId: 0,\n\t\t\t\tacknowledgedFrames: 0,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate handlePowerlevelTestNodeReport(\n\t\tcommand: PowerlevelCCTestNodeReport,\n\t): void {\n\t\t// Notify listeners\n\t\tthis.emit(\n\t\t\t\"notification\",\n\t\t\tthis,\n\t\t\tCommandClasses.Powerlevel,\n\t\t\tpick(command, [\"testNodeId\", \"status\", \"acknowledgedFrames\"]),\n\t\t);\n\t}\n\n\tprivate async handleSecurityCommandsSupportedGet(\n\t\tcommand: SecurityCCCommandsSupportedGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\tif (this.getHighestSecurityClass() === 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\tprivate async handleSecurity2CommandsSupportedGet(\n\t\tcommand: Security2CCCommandsSupportedGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\tconst highestSecurityClass = this.getHighestSecurityClass();\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\tprivate handleDeviceResetLocallyNotification(\n\t\tcmd: DeviceResetLocallyCCNotification,\n\t): void {\n\t\tif (cmd.endpointIndex !== 0) {\n\t\t\t// The notification MUST be issued by the root device, otherwise it is likely a corrupted message\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Received reset locally notification from non-root endpoint - ignoring it...`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// Handling this command can take a few seconds and require communication with the node.\n\t\t// If it was received with Supervision, we need to acknowledge it immediately. Therefore\n\t\t// defer the handling half a second.\n\n\t\tsetTimeout(async () => {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: `The node was reset locally, removing it`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait this.driver.controller.removeFailedNodeInternal(\n\t\t\t\t\tthis.id,\n\t\t\t\t\tRemoveNodeReason.Reset,\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `removing the node failed: ${\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\tlevel: \"error\",\n\t\t\t\t});\n\t\t\t}\n\t\t}, 500);\n\t}\n\n\t/**\n\t * Allows automatically resetting notification values to idle if the node does not do it itself\n\t */\n\tprivate notificationIdleTimeouts = new Map<string, NodeJS.Timeout>();\n\t/** Schedules a notification value to be reset */\n\tprivate scheduleNotificationIdleReset(\n\t\tvalueId: ValueID,\n\t\thandler: () => void,\n\t): void {\n\t\tthis.clearNotificationIdleReset(valueId);\n\t\tconst key = valueIdToString(valueId);\n\t\tthis.notificationIdleTimeouts.set(\n\t\t\tkey,\n\t\t\t// Unref'ing long running timeouts allows to quit the application before the timeout elapses\n\t\t\tsetTimeout(handler, 5 * 60 * 1000 /* 5 minutes */).unref(),\n\t\t);\n\t}\n\n\t/** Removes a scheduled notification reset */\n\tprivate clearNotificationIdleReset(valueId: ValueID): void {\n\t\tconst key = valueIdToString(valueId);\n\t\tif (this.notificationIdleTimeouts.has(key)) {\n\t\t\tclearTimeout(this.notificationIdleTimeouts.get(key));\n\t\t\tthis.notificationIdleTimeouts.delete(key);\n\t\t}\n\t}\n\n\t// Fallback for V2 notifications that don't allow us to predefine the metadata during the interview.\n\t// Instead of defining useless values for each possible notification event, we build the metadata on demand\n\tprivate extendNotificationValueMetadata(\n\t\tvalueId: ValueID,\n\t\tnotification: Notification,\n\t\tvalueConfig: NotificationState,\n\t) {\n\t\tconst ccVersion = this.driver.getSupportedCCVersion(\n\t\t\tCommandClasses.Notification,\n\t\t\tthis.id,\n\t\t\tthis.index,\n\t\t);\n\t\tif (ccVersion === 2 || !this.valueDB.hasMetadata(valueId)) {\n\t\t\tconst metadata = getNotificationValueMetadata(\n\t\t\t\tthis.valueDB.getMetadata(valueId) as\n\t\t\t\t\t| ValueMetadataNumeric\n\t\t\t\t\t| undefined,\n\t\t\t\tnotification,\n\t\t\t\tvalueConfig,\n\t\t\t);\n\t\t\tthis.valueDB.setMetadata(valueId, metadata);\n\t\t}\n\t}\n\n\t/**\n\t * Manually resets a single notification value to idle.\n\t */\n\tpublic manuallyIdleNotificationValue(valueId: ValueID): void;\n\n\tpublic manuallyIdleNotificationValue(\n\t\tnotificationType: number,\n\t\tprevValue: number,\n\t\tendpointIndex?: number,\n\t): void;\n\n\tpublic manuallyIdleNotificationValue(\n\t\tnotificationTypeOrValueID: number | ValueID,\n\t\tprevValue?: number,\n\t\tendpointIndex: number = 0,\n\t): void {\n\t\tlet notificationType: number | undefined;\n\t\tif (typeof notificationTypeOrValueID === \"number\") {\n\t\t\tnotificationType = notificationTypeOrValueID;\n\t\t} else {\n\t\t\tnotificationType = this.valueDB.getMetadata(\n\t\t\t\tnotificationTypeOrValueID,\n\t\t\t)?.ccSpecific?.notificationType as number | undefined;\n\t\t\tif (notificationType === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprevValue = this.valueDB.getValue(notificationTypeOrValueID);\n\t\t\tendpointIndex = notificationTypeOrValueID.endpoint ?? 0;\n\t\t}\n\n\t\tif (\n\t\t\t!this.getEndpoint(endpointIndex)?.supportsCC(\n\t\t\t\tCommandClasses.Notification,\n\t\t\t)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst notification = getNotification(notificationType);\n\t\tif (!notification) return;\n\n\t\treturn this.manuallyIdleNotificationValueInternal(\n\t\t\tnotification,\n\t\t\tprevValue!,\n\t\t\tendpointIndex,\n\t\t);\n\t}\n\n\t/** Manually resets a single notification value to idle */\n\tprivate manuallyIdleNotificationValueInternal(\n\t\tnotification: Notification,\n\t\tprevValue: number,\n\t\tendpointIndex: number,\n\t): void {\n\t\tconst valueConfig = getNotificationValue(notification, prevValue);\n\t\t// Only known variables may be reset to idle\n\t\tif (!valueConfig || valueConfig.type !== \"state\") return;\n\t\t// Some properties may not be reset to idle\n\t\tif (!valueConfig.idle) return;\n\n\t\tconst notificationName = notification.name;\n\t\tconst variableName = valueConfig.variableName;\n\t\tconst valueId = NotificationCCValues.notificationVariable(\n\t\t\tnotificationName,\n\t\t\tvariableName,\n\t\t).endpoint(endpointIndex);\n\n\t\t// Make sure the value is actually set to the previous value\n\t\tif (this.valueDB.getValue(valueId) !== prevValue) return;\n\n\t\t// Since the node has reset the notification itself, we don't need the idle reset\n\t\tthis.clearNotificationIdleReset(valueId);\n\t\tthis.extendNotificationValueMetadata(\n\t\t\tvalueId,\n\t\t\tnotification,\n\t\t\tvalueConfig,\n\t\t);\n\t\tthis.valueDB.setValue(valueId, 0 /* idle */);\n\t}\n\n\t/**\n\t * Handles the receipt of a Notification Report\n\t */\n\tprivate handleNotificationReport(command: NotificationCCReport): void {\n\t\tif (command.notificationType == undefined) {\n\t\t\tif (command.alarmType == undefined) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `received unsupported notification ${\n\t\t\t\t\t\tstringify(\n\t\t\t\t\t\t\tcommand,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst ccVersion = getEffectiveCCVersion(this.driver, command);\n\n\t\t// Look up the received notification in the config\n\t\tconst notification = getNotification(command.notificationType);\n\n\t\tif (notification) {\n\t\t\t// This is a notification (status or event) with a known type\n\t\t\tconst notificationName = notification.name;\n\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`[handleNotificationReport] notificationName: ${notificationName}`,\n\t\t\t\tlevel: \"silly\",\n\t\t\t});\n\n\t\t\t/** Returns a single notification state to idle */\n\t\t\tconst setStateIdle = (prevValue: number): void => {\n\t\t\t\tthis.manuallyIdleNotificationValueInternal(\n\t\t\t\t\tnotification,\n\t\t\t\t\tprevValue,\n\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t);\n\t\t\t};\n\n\t\t\tconst setUnknownStateIdle = (prevValue?: number) => {\n\t\t\t\t// Find the value for the unknown notification variable bucket\n\t\t\t\tconst unknownNotificationVariableValueId = NotificationCCValues\n\t\t\t\t\t.unknownNotificationVariable(\n\t\t\t\t\t\tcommand.notificationType!,\n\t\t\t\t\t\tnotificationName,\n\t\t\t\t\t).endpoint(command.endpointIndex);\n\t\t\t\tconst currentValue = this.valueDB.getValue(\n\t\t\t\t\tunknownNotificationVariableValueId,\n\t\t\t\t);\n\t\t\t\t// ... and if it exists\n\t\t\t\tif (currentValue == undefined) return;\n\t\t\t\t// ... reset it to idle\n\t\t\t\tif (prevValue == undefined || currentValue === prevValue) {\n\t\t\t\t\tthis.valueDB.setValue(\n\t\t\t\t\t\tunknownNotificationVariableValueId,\n\t\t\t\t\t\t0, /* idle */\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst value = command.notificationEvent!;\n\t\t\tif (value === 0) {\n\t\t\t\t// Generic idle notification, this contains a value to be reset\n\t\t\t\tif (\n\t\t\t\t\tisUint8Array(command.eventParameters)\n\t\t\t\t\t&& command.eventParameters.length\n\t\t\t\t) {\n\t\t\t\t\t// The target value is the first byte of the event parameters\n\t\t\t\t\tsetStateIdle(command.eventParameters[0]);\n\t\t\t\t\tsetUnknownStateIdle(command.eventParameters[0]);\n\t\t\t\t} else {\n\t\t\t\t\t// Reset all values to idle\n\t\t\t\t\tconst nonIdleValues = this.valueDB\n\t\t\t\t\t\t.getValues(CommandClasses.Notification)\n\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t(v) =>\n\t\t\t\t\t\t\t\t(v.endpoint || 0) === command.endpointIndex\n\t\t\t\t\t\t\t\t&& v.property === notificationName\n\t\t\t\t\t\t\t\t&& typeof v.value === \"number\"\n\t\t\t\t\t\t\t\t&& v.value !== 0,\n\t\t\t\t\t\t);\n\t\t\t\t\tfor (const v of nonIdleValues) {\n\t\t\t\t\t\tsetStateIdle(v.value as number);\n\t\t\t\t\t}\n\t\t\t\t\tsetUnknownStateIdle();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Find out which property we need to update\n\t\t\tconst valueConfig = getNotificationValue(notification, value);\n\n\t\t\tif (valueConfig) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `[handleNotificationReport] valueConfig:\n label: ${valueConfig.label}\n ${\n\t\t\t\t\t\tvalueConfig.type === \"event\"\n\t\t\t\t\t\t\t? \"type: event\"\n\t\t\t\t\t\t\t: `type: state\n variableName: ${valueConfig.variableName}`\n\t\t\t\t\t}`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`[handleNotificationReport] valueConfig: undefined`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Perform some heuristics on the known notification\n\t\t\tthis.handleKnownNotification(command);\n\n\t\t\tlet allowIdleReset: boolean;\n\t\t\tif (!valueConfig) {\n\t\t\t\t// We don't know what this notification refers to, so we don't force a reset\n\t\t\t\tallowIdleReset = false;\n\t\t\t} else if (valueConfig.type === \"state\") {\n\t\t\t\tallowIdleReset = valueConfig.idle;\n\t\t\t} else {\n\t\t\t\t// This is an event\n\t\t\t\tconst endpoint = this.getEndpoint(command.endpointIndex)\n\t\t\t\t\t?? this;\n\t\t\t\tthis.emit(\n\t\t\t\t\t\"notification\",\n\t\t\t\t\tendpoint,\n\t\t\t\t\tCommandClasses.Notification,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: command.notificationType,\n\t\t\t\t\t\tevent: value,\n\t\t\t\t\t\tlabel: notification.name,\n\t\t\t\t\t\teventLabel: valueConfig.label,\n\t\t\t\t\t\tparameters: command.eventParameters,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\t// We may need to reset some linked states to idle\n\t\t\t\tif (valueConfig.idleVariables?.length) {\n\t\t\t\t\tfor (const variable of valueConfig.idleVariables) {\n\t\t\t\t\t\tsetStateIdle(variable);\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// Now that we've gathered all we need to know, update the value in our DB\n\t\t\tlet valueId: ValueID;\n\t\t\tif (valueConfig) {\n\t\t\t\tvalueId = NotificationCCValues.notificationVariable(\n\t\t\t\t\tnotificationName,\n\t\t\t\t\tvalueConfig.variableName,\n\t\t\t\t).endpoint(command.endpointIndex);\n\n\t\t\t\tthis.extendNotificationValueMetadata(\n\t\t\t\t\tvalueId,\n\t\t\t\t\tnotification,\n\t\t\t\t\tvalueConfig,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// Collect unknown values in an \"unknown\" bucket\n\t\t\t\tconst unknownValue = NotificationCCValues\n\t\t\t\t\t.unknownNotificationVariable(\n\t\t\t\t\t\tcommand.notificationType,\n\t\t\t\t\t\tnotificationName,\n\t\t\t\t\t);\n\t\t\t\tvalueId = unknownValue.endpoint(command.endpointIndex);\n\n\t\t\t\tif (ccVersion >= 2) {\n\t\t\t\t\tif (!this.valueDB.hasMetadata(valueId)) {\n\t\t\t\t\t\tthis.valueDB.setMetadata(valueId, unknownValue.meta);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (typeof command.eventParameters === \"number\") {\n\t\t\t\t// This notification contains an enum value. Depending on how the enum behaves,\n\t\t\t\t// we may need to set \"fake\" values for these to distinguish them\n\t\t\t\t// from states without enum values\n\t\t\t\tconst enumBehavior = valueConfig\n\t\t\t\t\t? getNotificationEnumBehavior(\n\t\t\t\t\t\tnotification,\n\t\t\t\t\t\tvalueConfig,\n\t\t\t\t\t)\n\t\t\t\t\t: \"extend\";\n\n\t\t\t\tconst valueWithEnum = enumBehavior === \"replace\"\n\t\t\t\t\t? command.eventParameters\n\t\t\t\t\t: getNotificationStateValueWithEnum(\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tcommand.eventParameters,\n\t\t\t\t\t);\n\t\t\t\tthis.valueDB.setValue(valueId, valueWithEnum);\n\t\t\t} else {\n\t\t\t\tthis.valueDB.setValue(valueId, value);\n\t\t\t}\n\n\t\t\t// Nodes before V8 (and some misbehaving V8 ones) don't necessarily reset the notification to idle.\n\t\t\t// The specifications advise to auto-reset the variables, but it has been found that this interferes\n\t\t\t// with some motion sensors that don't refresh their active notification. Therefore, we set a fallback\n\t\t\t// timer if the `forceNotificationIdleReset` compat flag is set.\n\t\t\tif (\n\t\t\t\tallowIdleReset\n\t\t\t\t&& !!this._deviceConfig?.compat?.forceNotificationIdleReset\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `[handleNotificationReport] scheduling idle reset`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t\tthis.scheduleNotificationIdleReset(\n\t\t\t\t\tvalueId,\n\t\t\t\t\t() => setStateIdle(value),\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\t// This is an unknown notification\n\t\t\tconst unknownValue = NotificationCCValues.unknownNotificationType(\n\t\t\t\tcommand.notificationType,\n\t\t\t);\n\t\t\tconst valueId = unknownValue.endpoint(command.endpointIndex);\n\n\t\t\t// Make sure the metdata exists\n\t\t\tif (ccVersion >= 2) {\n\t\t\t\tif (!this.valueDB.hasMetadata(valueId)) {\n\t\t\t\t\tthis.valueDB.setMetadata(valueId, unknownValue.meta);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// And set its value\n\t\t\tthis.valueDB.setValue(valueId, command.notificationEvent);\n\t\t\t// We don't know what this notification refers to, so we don't force a reset\n\t\t}\n\t}\n\n\tprivate handleKnownNotification(command: NotificationCCReport): void {\n\t\tconst lockEvents = [0x01, 0x03, 0x05, 0x09];\n\t\tconst unlockEvents = [0x02, 0x04, 0x06];\n\t\tconst doorStatusEvents = [\n\t\t\t// Actual status\n\t\t\t0x16,\n\t\t\t0x17,\n\t\t\t// Synthetic status with enum\n\t\t\t0x1600,\n\t\t\t0x1601,\n\t\t];\n\t\tif (\n\t\t\t// Access Control, manual/keypad/rf/auto (un)lock operation\n\t\t\tcommand.notificationType === 0x06\n\t\t\t&& (lockEvents.includes(command.notificationEvent as number)\n\t\t\t\t|| unlockEvents.includes(command.notificationEvent as number))\n\t\t\t&& (this.supportsCC(CommandClasses[\"Door Lock\"])\n\t\t\t\t|| this.supportsCC(CommandClasses.Lock))\n\t\t) {\n\t\t\t// The Door Lock Command Class is constrained to the S2 Access Control key,\n\t\t\t// while the Notification Command Class, in the same device, could use a\n\t\t\t// different key. This way the device can notify devices which don't belong\n\t\t\t// to the S2 Access Control key group of changes in its state.\n\n\t\t\tconst isLocked = lockEvents.includes(\n\t\t\t\tcommand.notificationEvent as number,\n\t\t\t);\n\n\t\t\t// Update the current lock status\n\t\t\tif (this.supportsCC(CommandClasses[\"Door Lock\"])) {\n\t\t\t\tthis.valueDB.setValue(\n\t\t\t\t\tDoorLockCCValues.currentMode.endpoint(\n\t\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t\t),\n\t\t\t\t\tisLocked ? DoorLockMode.Secured : DoorLockMode.Unsecured,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.supportsCC(CommandClasses.Lock)) {\n\t\t\t\tthis.valueDB.setValue(\n\t\t\t\t\tLockCCValues.locked.endpoint(command.endpointIndex),\n\t\t\t\t\tisLocked,\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (\n\t\t\tcommand.notificationType === 0x06\n\t\t\t&& doorStatusEvents.includes(command.notificationEvent as number)\n\t\t) {\n\t\t\t// https://github.com/zwave-js/node-zwave-js/pull/5394 added support for\n\t\t\t// notification enums. Unfortunately, there's no way to discover which nodes\n\t\t\t// actually support them, which makes working with the Door state variable\n\t\t\t// very cumbersome. Also, this is currently the only notification where the enum values\n\t\t\t// extend the state value.\n\n\t\t\t// To work around this, we hard-code a notification value for the door status\n\t\t\t// which only includes the \"legacy\" states for open/closed.\n\t\t\tthis.valueDB.setValue(\n\t\t\t\tNotificationCCValues.doorStateSimple.endpoint(\n\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t),\n\t\t\t\tcommand.notificationEvent === 0x17 ? 0x17 : 0x16,\n\t\t\t);\n\n\t\t\t// In addition to that, we also hard-code a notification value for only the tilt status.\n\t\t\t// This will only be created after receiving a notification for the tilted state.\n\t\t\t// Only after it exists, it will be updated. Otherwise, we'd get phantom\n\t\t\t// values, since some devices send the enum value, even when they don't support tilt.\n\t\t\tconst tiltValue = NotificationCCValues.doorTiltState;\n\t\t\tconst tiltValueId = tiltValue.endpoint(command.endpointIndex);\n\t\t\tlet tiltValueWasCreated = this.valueDB.hasMetadata(tiltValueId);\n\t\t\tif (command.eventParameters === 0x01 && !tiltValueWasCreated) {\n\t\t\t\tthis.valueDB.setMetadata(tiltValueId, tiltValue.meta);\n\t\t\t\ttiltValueWasCreated = true;\n\t\t\t}\n\t\t\tif (tiltValueWasCreated) {\n\t\t\t\tthis.valueDB.setValue(\n\t\t\t\t\ttiltValueId,\n\t\t\t\t\tcommand.eventParameters === 0x01 ? 0x01 : 0x00,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate busySettingClock: boolean = false;\n\tprivate async handleClockReport(command: ClockCCReport): Promise<void> {\n\t\tif (this.busySettingClock) return;\n\n\t\t// A Z-Wave Plus node SHOULD issue a Clock Report Command via the Lifeline Association Group if they\n\t\t// suspect to have inaccurate time and/or weekdays (e.g. after battery removal).\n\t\t// A controlling node SHOULD compare the received time and weekday with its current time and set the\n\t\t// time again at the supporting node if a deviation is observed (e.g. different weekday or more than a\n\t\t// minute difference)\n\n\t\t// A sending node knowing the current time with seconds precision SHOULD round its\n\t\t// current time to the nearest minute when sending this command.\n\t\tlet now = new Date();\n\t\tconst seconds = now.getSeconds();\n\t\tif (seconds >= 30) {\n\t\t\tnow = new Date(now.getTime() + (60 - seconds) * 1000);\n\t\t}\n\n\t\t// Get desired time in local time\n\t\tconst hours = now.getHours();\n\t\tconst minutes = now.getMinutes();\n\t\t// Sunday is 0 in JS, but 7 in Z-Wave\n\t\tlet weekday = now.getDay();\n\t\tif (weekday === 0) weekday = 7;\n\n\t\tif (\n\t\t\tcommand.weekday !== weekday\n\t\t\t|| command.hour !== hours\n\t\t\t|| command.minute !== minutes\n\t\t) {\n\t\t\tconst endpoint = this.driver.tryGetEndpoint(command);\n\t\t\tif (!endpoint /*|| !endpoint.commandClasses.Clock.isSupported()*/) {\n\t\t\t\t// Make sure the endpoint supports the CC (GH#1704)\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`detected a deviation of the node's clock, updating it...`,\n\t\t\t);\n\t\t\tthis.busySettingClock = true;\n\t\t\ttry {\n\t\t\t\tawait endpoint.commandClasses.Clock.set(\n\t\t\t\t\thours,\n\t\t\t\t\tminutes,\n\t\t\t\t\tweekday,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t\tthis.busySettingClock = false;\n\t\t}\n\t}\n\n\tprivate async handleTimeGet(command: TimeCCTimeGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\tconst now = new Date();\n\t\tconst hours = now.getHours();\n\t\tconst minutes = now.getMinutes();\n\t\tconst seconds = now.getSeconds();\n\n\t\ttry {\n\t\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t\t// Using the commandClasses property would throw in that case\n\t\t\tconst api = endpoint\n\t\t\t\t.createAPI(CommandClasses.Time, false)\n\t\t\t\t.withOptions({\n\t\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t\t});\n\t\t\tawait api.reportTime(hours, minutes, seconds);\n\t\t} catch (e: any) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: e.message,\n\t\t\t\tlevel: \"error\",\n\t\t\t});\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tprivate async handleDateGet(command: TimeCCDateGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\tconst now = new Date();\n\t\tconst year = now.getFullYear();\n\t\tconst month = now.getMonth() + 1;\n\t\tconst day = now.getDate();\n\n\t\ttry {\n\t\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t\t// Using the commandClasses property would throw in that case\n\t\t\tconst api = endpoint\n\t\t\t\t.createAPI(CommandClasses.Time, false)\n\t\t\t\t.withOptions({\n\t\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t\t});\n\t\t\tawait api.reportDate(year, month, day);\n\t\t} catch (e: any) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: e.message,\n\t\t\t\tlevel: \"error\",\n\t\t\t});\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tprivate async handleTimeOffsetGet(\n\t\tcommand: TimeCCTimeOffsetGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\tconst timezone = getDSTInfo(new Date());\n\n\t\ttry {\n\t\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t\t// Using the commandClasses property would throw in that case\n\t\t\tconst api = endpoint\n\t\t\t\t.createAPI(CommandClasses.Time, false)\n\t\t\t\t.withOptions({\n\t\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t\t});\n\t\t\tawait api.reportTimezone(timezone);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves the firmware update capabilities of a node to decide which options to offer a user prior to the update.\n\t * This method uses cached information from the most recent interview.\n\t */\n\tpublic getFirmwareUpdateCapabilitiesCached(): FirmwareUpdateCapabilities {\n\t\tconst firmwareUpgradable = this.getValue<boolean>(\n\t\t\tFirmwareUpdateMetaDataCCValues.firmwareUpgradable.id,\n\t\t);\n\t\tconst supportsActivation = this.getValue<boolean>(\n\t\t\tFirmwareUpdateMetaDataCCValues.supportsActivation.id,\n\t\t);\n\t\tconst continuesToFunction = this.getValue<boolean>(\n\t\t\tFirmwareUpdateMetaDataCCValues.continuesToFunction.id,\n\t\t);\n\t\tconst additionalFirmwareIDs = this.getValue<number[]>(\n\t\t\tFirmwareUpdateMetaDataCCValues.additionalFirmwareIDs.id,\n\t\t);\n\t\tconst supportsResuming = this.getValue<boolean>(\n\t\t\tFirmwareUpdateMetaDataCCValues.supportsResuming.id,\n\t\t);\n\t\tconst supportsNonSecureTransfer = this.getValue<boolean>(\n\t\t\tFirmwareUpdateMetaDataCCValues.supportsNonSecureTransfer.id,\n\t\t);\n\n\t\t// Ensure all information was queried\n\t\tif (\n\t\t\t!firmwareUpgradable\n\t\t\t|| !isArray(additionalFirmwareIDs)\n\t\t) {\n\t\t\treturn { firmwareUpgradable: false };\n\t\t}\n\n\t\treturn {\n\t\t\tfirmwareUpgradable: true,\n\t\t\t// TODO: Targets are not the list of IDs - maybe expose the IDs as well?\n\t\t\tfirmwareTargets: new Array(1 + additionalFirmwareIDs.length).fill(0)\n\t\t\t\t.map((_, i) => i),\n\t\t\tcontinuesToFunction,\n\t\t\tsupportsActivation,\n\t\t\tsupportsResuming,\n\t\t\tsupportsNonSecureTransfer,\n\t\t};\n\t}\n\n\t/**\n\t * Retrieves the firmware update capabilities of a node to decide which options to offer a user prior to the update.\n\t * This communicates with the node to retrieve fresh information.\n\t */\n\tpublic async getFirmwareUpdateCapabilities(): Promise<\n\t\tFirmwareUpdateCapabilities\n\t> {\n\t\tconst api = this.commandClasses[\"Firmware Update Meta Data\"];\n\t\tconst meta = await api.getMetaData();\n\t\tif (!meta) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Failed to request firmware update capabilities: The node did not respond in time!`,\n\t\t\t\tZWaveErrorCodes.Controller_NodeTimeout,\n\t\t\t);\n\t\t} else if (!meta.firmwareUpgradable) {\n\t\t\treturn {\n\t\t\t\tfirmwareUpgradable: false,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tfirmwareUpgradable: true,\n\t\t\t// TODO: Targets are not the list of IDs - maybe expose the IDs as well?\n\t\t\tfirmwareTargets: new Array(1 + meta.additionalFirmwareIDs.length)\n\t\t\t\t.fill(0).map((_, i) => i),\n\t\t\tcontinuesToFunction: meta.continuesToFunction,\n\t\t\tsupportsActivation: meta.supportsActivation,\n\t\t\tsupportsResuming: meta.supportsResuming,\n\t\t\tsupportsNonSecureTransfer: meta.supportsNonSecureTransfer,\n\t\t};\n\t}\n\n\tprivate recentEntryControlNotificationSequenceNumbers: number[] = [];\n\tprivate handleEntryControlNotification(\n\t\tcommand: EntryControlCCNotification,\n\t): void {\n\t\tif (\n\t\t\t!this._deviceConfig?.compat?.disableStrictEntryControlDataValidation\n\t\t) {\n\t\t\tif (\n\t\t\t\tthis.recentEntryControlNotificationSequenceNumbers.includes(\n\t\t\t\t\tcommand.sequenceNumber,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t`Received duplicate Entry Control Notification (sequence number ${command.sequenceNumber}), ignoring...`,\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\t// Keep track of the last 5 sequence numbers\n\t\t\tthis.recentEntryControlNotificationSequenceNumbers.unshift(\n\t\t\t\tcommand.sequenceNumber,\n\t\t\t);\n\t\t\tif (this.recentEntryControlNotificationSequenceNumbers.length > 5) {\n\t\t\t\tthis.recentEntryControlNotificationSequenceNumbers.pop();\n\t\t\t}\n\t\t}\n\n\t\t// Notify listeners\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\t\tthis.emit(\"notification\", endpoint, CommandClasses[\"Entry Control\"], {\n\t\t\t...pick(command, [\"eventType\", \"dataType\", \"eventData\"]),\n\t\t\teventTypeLabel: entryControlEventTypeLabels[command.eventType],\n\t\t\tdataTypeLabel: getEnumMemberName(\n\t\t\t\tEntryControlDataTypes,\n\t\t\t\tcommand.dataType,\n\t\t\t),\n\t\t});\n\t}\n\n\t/**\n\t * @internal\n\t * Deserializes the information of this node from a cache.\n\t */\n\tpublic async deserialize(): Promise<void> {\n\t\tif (!this.driver.networkCache) return;\n\n\t\t// Restore the device config\n\t\tawait this.loadDeviceConfig();\n\n\t\t// Mark already-interviewed nodes as potentially ready\n\t\tif (this.interviewStage === InterviewStage.Complete) {\n\t\t\tthis.readyMachine.send(\"RESTART_FROM_CACHE\");\n\t\t}\n\t}\n\n\t/**\n\t * Instructs the node to send powerlevel test frames to the other node using the given powerlevel. Returns how many frames were acknowledged during the test.\n\t *\n\t * **Note:** Depending on the number of test frames, this may take a while\n\t */\n\tpublic async testPowerlevel(\n\t\ttestNodeId: number,\n\t\tpowerlevel: Powerlevel,\n\t\thealthCheckTestFrameCount: number,\n\t\tonProgress?: (acknowledged: number, total: number) => void,\n\t): Promise<number> {\n\t\tconst api = this.commandClasses.Powerlevel;\n\n\t\t// Keep sleeping nodes awake\n\t\tconst wasKeptAwake = this.keepAwake;\n\t\tif (this.canSleep) this.keepAwake = true;\n\t\tconst result = <T>(value: T) => {\n\t\t\t// And undo the change when we're done\n\t\t\tthis.keepAwake = wasKeptAwake;\n\t\t\treturn value;\n\t\t};\n\n\t\t// Start the process\n\t\tawait api.startNodeTest(\n\t\t\ttestNodeId,\n\t\t\tpowerlevel,\n\t\t\thealthCheckTestFrameCount,\n\t\t);\n\n\t\t// Each frame will take a few ms to be sent, let's assume 5 per second\n\t\t// to estimate how long the test will take\n\t\tconst expectedDurationMs = Math.round(\n\t\t\t(healthCheckTestFrameCount / 5) * 1000,\n\t\t);\n\n\t\t// Poll the status of the test regularly, but not too frequently. Especially for quick tests, polling too often\n\t\t// increases the likelyhood of us querying the node at the same time it sends an unsolicited update.\n\t\t// If using Security S2, this can cause a desync.\n\t\tconst pollFrequencyMs = expectedDurationMs >= 60000 ? 20000 : 5000;\n\n\t\t// Track how often we failed to get a response from the node, so we can abort if the connection is too bad\n\t\tlet continuousErrors = 0;\n\t\t// Also track how many times in a row there was no progress, which also indicates a bad connection\n\t\tlet previousProgress = 0;\n\t\twhile (true) {\n\t\t\t// The node might send an unsolicited update when it finishes the test\n\t\t\tconst report = await this.driver\n\t\t\t\t.waitForCommand<PowerlevelCCTestNodeReport>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc.nodeId === this.id\n\t\t\t\t\t\t&& cc instanceof PowerlevelCCTestNodeReport,\n\t\t\t\t\tpollFrequencyMs,\n\t\t\t\t)\n\t\t\t\t.catch(() => undefined);\n\n\t\t\tconst status = report\n\t\t\t\t? pick(report, [\"status\", \"acknowledgedFrames\"])\n\t\t\t\t// If it didn't come in the wait time, poll for an update\n\t\t\t\t: await api.getNodeTestStatus().catch(() => undefined);\n\n\t\t\t// Safeguard against infinite loop:\n\t\t\t// If we didn't get a result, or there was no progress, try again next iteration\n\t\t\tif (\n\t\t\t\t!status\n\t\t\t\t|| (status.status === PowerlevelTestStatus[\"In Progress\"]\n\t\t\t\t\t&& status.acknowledgedFrames === previousProgress)\n\t\t\t) {\n\t\t\t\tif (continuousErrors > 5) return result(0);\n\t\t\t\tcontinuousErrors++;\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\tpreviousProgress = status.acknowledgedFrames;\n\t\t\t\tcontinuousErrors = 0;\n\t\t\t}\n\n\t\t\tif (status.status === PowerlevelTestStatus.Failed) {\n\t\t\t\treturn result(0);\n\t\t\t} else if (status.status === PowerlevelTestStatus.Success) {\n\t\t\t\treturn result(status.acknowledgedFrames);\n\t\t\t} else if (onProgress) {\n\t\t\t\t// Notify the caller of the test progress\n\t\t\t\tonProgress(\n\t\t\t\t\tstatus.acknowledgedFrames,\n\t\t\t\t\thealthCheckTestFrameCount,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _healthCheckInProgress: boolean = false;\n\t/**\n\t * Returns whether a health check is currently in progress for this node\n\t */\n\tpublic isHealthCheckInProgress(): boolean {\n\t\treturn this._healthCheckInProgress;\n\t}\n\n\tprivate _healthCheckAborted: boolean = false;\n\tprivate _abortHealthCheckPromise: DeferredPromise<void> | undefined;\n\n\t/**\n\t * Aborts an ongoing health check if one is currently in progress.\n\t *\n\t * **Note:** The health check may take a few seconds to actually be aborted.\n\t * When it is, the promise returned by {@link checkLifelineHealth} or\n\t * {@link checkRouteHealth} will be resolved with the results obtained so far.\n\t */\n\tpublic abortHealthCheck(): void {\n\t\tif (!this._healthCheckInProgress) return;\n\t\tthis._healthCheckAborted = true;\n\t\tthis._abortHealthCheckPromise?.resolve();\n\t}\n\n\t/**\n\t * Checks the health of connection between the controller and this node and returns the results.\n\t */\n\tpublic async checkLifelineHealth(\n\t\trounds: number = 5,\n\t\tonProgress?: (\n\t\t\tround: number,\n\t\t\ttotalRounds: number,\n\t\t\tlastRating: number,\n\t\t\tlastResult: LifelineHealthCheckResult,\n\t\t) => void,\n\t): Promise<LifelineHealthCheckSummary> {\n\t\tif (this._healthCheckInProgress) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"A health check is already in progress for this node!\",\n\t\t\t\tZWaveErrorCodes.HealthCheck_Busy,\n\t\t\t);\n\t\t}\n\n\t\tif (rounds > 10 || rounds < 1) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The number of health check rounds must be between 1 and 10!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tthis._healthCheckInProgress = true;\n\t\t\tthis._healthCheckAborted = false;\n\t\t\tthis._abortHealthCheckPromise = createDeferredPromise();\n\n\t\t\treturn await this.checkLifelineHealthInternal(rounds, onProgress);\n\t\t} finally {\n\t\t\tthis._healthCheckInProgress = false;\n\t\t\tthis._healthCheckAborted = false;\n\t\t\tthis._abortHealthCheckPromise = undefined;\n\t\t}\n\t}\n\n\tprivate async checkLifelineHealthInternal(\n\t\trounds: number,\n\t\tonProgress?: (\n\t\t\tround: number,\n\t\t\ttotalRounds: number,\n\t\t\tlastRating: number,\n\t\t\tlastResult: LifelineHealthCheckResult,\n\t\t) => void,\n\t): Promise<LifelineHealthCheckSummary> {\n\t\t// No. of pings per round\n\t\tconst start = Date.now();\n\n\t\t/** Computes a health rating from a health check result */\n\t\tconst computeRating = (result: LifelineHealthCheckResult) => {\n\t\t\tconst failedPings = Math.max(\n\t\t\t\tresult.failedPingsController ?? 0,\n\t\t\t\tresult.failedPingsNode,\n\t\t\t);\n\t\t\tconst numNeighbors = result.numNeighbors;\n\t\t\tconst minPowerlevel = result.minPowerlevel ?? Powerlevel[\"-6 dBm\"];\n\t\t\tconst snrMargin = result.snrMargin ?? 17;\n\t\t\tconst latency = result.latency;\n\n\t\t\tif (failedPings === 10) return 0;\n\t\t\tif (failedPings > 2) return 1;\n\t\t\tif (failedPings === 2 || latency > 1000) return 2;\n\t\t\tif (failedPings === 1 || latency > 500) return 3;\n\t\t\tif (latency > 250) return 4;\n\t\t\tif (latency > 100) return 5;\n\t\t\tif (minPowerlevel < Powerlevel[\"-6 dBm\"] || snrMargin < 17) {\n\t\t\t\t// Lower powerlevel reductions (= higher power) have lower numeric values\n\t\t\t\tif (numNeighbors == undefined) return 7; // ZWLR has no neighbors\n\t\t\t\treturn numNeighbors > 2 ? 7 : 6;\n\t\t\t}\n\t\t\tif (numNeighbors != undefined && numNeighbors <= 2) return 8; // ZWLR has no neighbors\n\t\t\tif (latency > 50) return 9;\n\t\t\treturn 10;\n\t\t};\n\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Starting lifeline health check (${rounds} round${\n\t\t\t\trounds !== 1 ? \"s\" : \"\"\n\t\t\t})...`,\n\t\t);\n\n\t\tconst results: LifelineHealthCheckResult[] = [];\n\t\tconst aborted = () => {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`Lifeline health check aborted`,\n\t\t\t);\n\t\t\tif (results.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\trating: 0,\n\t\t\t\t\tresults: [],\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\treturn {\n\t\t\t\t\trating: Math.min(...results.map((r) => r.rating)),\n\t\t\t\t\tresults,\n\t\t\t\t};\n\t\t\t}\n\t\t};\n\n\t\tif (this.canSleep && this.status !== NodeStatus.Awake) {\n\t\t\t// Wait for node to wake up to avoid incorrectly long delays in the first health check round\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`waiting for node to wake up...`,\n\t\t\t);\n\t\t\tawait Promise.race([\n\t\t\t\tthis.waitForWakeup(),\n\t\t\t\tthis._abortHealthCheckPromise,\n\t\t\t]);\n\t\t\tif (this._healthCheckAborted) return aborted();\n\t\t}\n\n\t\tfor (let round = 1; round <= rounds; round++) {\n\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t// Determine the number of repeating neighbors for Z-Wave Classic\n\t\t\tlet numNeighbors: number | undefined;\n\t\t\tif (this.protocol === Protocols.ZWave) {\n\t\t\t\tnumNeighbors = (await this.driver.controller.getNodeNeighbors(\n\t\t\t\t\tthis.id,\n\t\t\t\t\ttrue,\n\t\t\t\t)).length;\n\t\t\t}\n\n\t\t\t// Ping the node 10x, measuring the RSSI\n\t\t\tlet txReport: TXReport | undefined;\n\t\t\tlet routeChanges: number | undefined;\n\t\t\tlet rssi: RSSI | undefined;\n\t\t\tlet channel: number | undefined;\n\t\t\tlet snrMargin: number | undefined;\n\t\t\tlet failedPingsNode = 0;\n\t\t\tlet latency = 0;\n\t\t\tconst pingAPI = this.commandClasses[\"No Operation\"].withOptions({\n\t\t\t\t// Don't change the node status when the ACK is missing. We're likely testing the limits here.\n\t\t\t\tchangeNodeStatusOnMissingACK: false,\n\t\t\t\t// Avoid using explorer frames, because they can create a ton of delay\n\t\t\t\ttransmitOptions: TransmitOptions.ACK\n\t\t\t\t\t| TransmitOptions.AutoRoute,\n\t\t\t\t// And remember the transmit report, so we can evaluate it\n\t\t\t\tonTXReport: (report) => {\n\t\t\t\t\ttxReport = report;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tfor (let i = 1; i <= healthCheckTestFrameCount; i++) {\n\t\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t\tconst start = Date.now();\n\t\t\t\t// Reset TX report before each ping\n\t\t\t\ttxReport = undefined as any;\n\t\t\t\tconst pingResult = await pingAPI.send().then(\n\t\t\t\t\t() => true,\n\t\t\t\t\t() => false,\n\t\t\t\t);\n\t\t\t\tconst rtt = Date.now() - start;\n\t\t\t\tlatency = Math.max(\n\t\t\t\t\tlatency,\n\t\t\t\t\ttxReport ? txReport.txTicks * 10 : rtt,\n\t\t\t\t);\n\t\t\t\tif (!pingResult) {\n\t\t\t\t\tfailedPingsNode++;\n\t\t\t\t} else if (txReport) {\n\t\t\t\t\trouteChanges ??= 0;\n\t\t\t\t\tif (txReport.routingAttempts > 1) {\n\t\t\t\t\t\trouteChanges++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (\n\t\t\t\t\t\ttxReport.ackRSSI != undefined\n\t\t\t\t\t\t&& !isRssiError(txReport.ackRSSI)\n\t\t\t\t\t) {\n\t\t\t\t\t\t// If possible, determine the SNR margin from the report\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttxReport.measuredNoiseFloor != undefined\n\t\t\t\t\t\t\t&& !isRssiError(txReport.measuredNoiseFloor)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst currentSNRMargin = txReport.ackRSSI\n\t\t\t\t\t\t\t\t- txReport.measuredNoiseFloor;\n\t\t\t\t\t\t\t// And remember it if it's the lowest we've seen so far\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tsnrMargin == undefined\n\t\t\t\t\t\t\t\t|| currentSNRMargin < snrMargin\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tsnrMargin = currentSNRMargin;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Also remember the worst RSSI and the channel it was received on\n\t\t\t\t\t\tif (rssi == undefined || txReport.ackRSSI < rssi) {\n\t\t\t\t\t\t\trssi = txReport.ackRSSI;\n\t\t\t\t\t\t\tchannel = txReport.ackChannelNo;\n\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// If possible, compute the SNR margin from the test results,\n\t\t\t// unless it could already be determined from the transmit reports\n\t\t\tif (\n\t\t\t\tsnrMargin == undefined\n\t\t\t\t&& rssi != undefined\n\t\t\t\t&& rssi < RssiError.NoSignalDetected\n\t\t\t\t&& channel != undefined\n\t\t\t) {\n\t\t\t\tconst backgroundRSSI = await this.driver.controller\n\t\t\t\t\t.getBackgroundRSSI();\n\t\t\t\tif (`rssiChannel${channel}` in backgroundRSSI) {\n\t\t\t\t\tconst bgRSSI = (backgroundRSSI as any)[\n\t\t\t\t\t\t`rssiChannel${channel}`\n\t\t\t\t\t];\n\t\t\t\t\tif (isRssiError(bgRSSI)) {\n\t\t\t\t\t\tif (bgRSSI === RssiError.ReceiverSaturated) {\n\t\t\t\t\t\t\t// RSSI is too high to measure, so there can't be any margin left\n\t\t\t\t\t\t\tsnrMargin = 0;\n\t\t\t\t\t\t} else if (bgRSSI === RssiError.NoSignalDetected) {\n\t\t\t\t\t\t\t// It is very quiet, assume -128 dBm\n\t\t\t\t\t\t\tsnrMargin = rssi + 128;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsnrMargin = undefined;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsnrMargin = rssi - bgRSSI;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst ret: LifelineHealthCheckResult = {\n\t\t\t\tlatency,\n\t\t\t\tfailedPingsNode,\n\t\t\t\tnumNeighbors,\n\t\t\t\trouteChanges,\n\t\t\t\tsnrMargin,\n\t\t\t\trating: 0,\n\t\t\t};\n\n\t\t\t// Now instruct the node to ping the controller, figuring out the minimum powerlevel\n\t\t\tif (this.supportsCC(CommandClasses.Powerlevel)) {\n\t\t\t\t// Do a binary search and find the highest reduction in powerlevel for which there are no errors\n\t\t\t\tlet failedPingsController = 0;\n\n\t\t\t\tconst executor = async (powerlevel: Powerlevel) => {\n\t\t\t\t\t// Abort the search if the health check was aborted\n\t\t\t\t\tif (this._healthCheckAborted) return undefined;\n\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t`Sending ${healthCheckTestFrameCount} pings to controller at ${\n\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\tPowerlevel,\n\t\t\t\t\t\t\t\tpowerlevel,\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\tconst result = await this.testPowerlevel(\n\t\t\t\t\t\tthis.driver.controller.ownNodeId!,\n\t\t\t\t\t\tpowerlevel,\n\t\t\t\t\t\thealthCheckTestFrameCount,\n\t\t\t\t\t);\n\t\t\t\t\tfailedPingsController = healthCheckTestFrameCount - result;\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t`At ${\n\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\tPowerlevel,\n\t\t\t\t\t\t\t\tpowerlevel,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}, ${result}/${healthCheckTestFrameCount} pings were acknowledged...`,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Wait a second for things to settle down\n\t\t\t\t\tawait wait(1000);\n\n\t\t\t\t\treturn failedPingsController === 0;\n\t\t\t\t};\n\t\t\t\ttry {\n\t\t\t\t\tconst powerlevel = await discreteLinearSearch(\n\t\t\t\t\t\tPowerlevel[\"Normal Power\"], // minimum reduction\n\t\t\t\t\t\tPowerlevel[\"-9 dBm\"], // maximum reduction\n\t\t\t\t\t\texecutor,\n\t\t\t\t\t);\n\t\t\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t\t\tif (powerlevel == undefined) {\n\t\t\t\t\t\t// There were still failures at normal power, report it\n\t\t\t\t\t\tret.minPowerlevel = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tret.failedPingsController = failedPingsController;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tret.minPowerlevel = powerlevel;\n\t\t\t\t\t}\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_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\t// The node is dead, treat this as a failure\n\t\t\t\t\t\tret.minPowerlevel = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tret.failedPingsController = healthCheckTestFrameCount;\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\t\t\t}\n\n\t\t\tret.rating = computeRating(ret);\n\t\t\tresults.push(ret);\n\t\t\tonProgress?.(round, rounds, ret.rating, { ...ret });\n\t\t}\n\n\t\tconst duration = Date.now() - start;\n\n\t\tconst rating = Math.min(...results.map((r) => r.rating));\n\t\tconst summary = { results, rating };\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Lifeline health check complete in ${duration} ms\n${formatLifelineHealthCheckSummary(summary)}`,\n\t\t);\n\n\t\treturn summary;\n\t}\n\n\t/**\n\t * Checks the health of connection between this node and the target node and returns the results.\n\t */\n\tpublic async checkRouteHealth(\n\t\ttargetNodeId: number,\n\t\trounds: number = 5,\n\t\tonProgress?: (\n\t\t\tround: number,\n\t\t\ttotalRounds: number,\n\t\t\tlastRating: number,\n\t\t\tlastResult: RouteHealthCheckResult,\n\t\t) => void,\n\t): Promise<RouteHealthCheckSummary> {\n\t\tif (this._healthCheckInProgress) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"A health check is already in progress for this node!\",\n\t\t\t\tZWaveErrorCodes.HealthCheck_Busy,\n\t\t\t);\n\t\t}\n\n\t\tif (rounds > 10 || rounds < 1) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The number of health check rounds must be between 1 and 10!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tthis._healthCheckInProgress = true;\n\t\t\tthis._healthCheckAborted = false;\n\t\t\tthis._abortHealthCheckPromise = createDeferredPromise();\n\n\t\t\treturn await this.checkRouteHealthInternal(\n\t\t\t\ttargetNodeId,\n\t\t\t\trounds,\n\t\t\t\tonProgress,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis._healthCheckInProgress = false;\n\t\t\tthis._healthCheckAborted = false;\n\t\t\tthis._abortHealthCheckPromise = undefined;\n\t\t}\n\t}\n\n\tprivate async checkRouteHealthInternal(\n\t\ttargetNodeId: number,\n\t\trounds: number,\n\t\tonProgress?: (\n\t\t\tround: number,\n\t\t\ttotalRounds: number,\n\t\t\tlastRating: number,\n\t\t\tlastResult: RouteHealthCheckResult,\n\t\t) => void,\n\t): Promise<RouteHealthCheckSummary> {\n\t\tconst otherNode = this.driver.controller.nodes.getOrThrow(targetNodeId);\n\n\t\tif (this.protocol === Protocols.ZWaveLongRange) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot perform route health check for Long Range node ${this.id}.`,\n\t\t\t\tZWaveErrorCodes.Controller_NotSupportedForLongRange,\n\t\t\t);\n\t\t} else if (otherNode.protocol === Protocols.ZWaveLongRange) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot perform route health check for Long Range node ${otherNode.id}.`,\n\t\t\t\tZWaveErrorCodes.Controller_NotSupportedForLongRange,\n\t\t\t);\n\t\t}\n\n\t\tif (otherNode.canSleep) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Nodes which can sleep are not a valid target for a route health check!\",\n\t\t\t\tZWaveErrorCodes.CC_NotSupported,\n\t\t\t);\n\t\t} else if (\n\t\t\tthis.canSleep\n\t\t\t&& !this.supportsCC(CommandClasses.Powerlevel)\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"For a route health check, nodes which can sleep must support Powerlevel CC!\",\n\t\t\t\tZWaveErrorCodes.CC_NotSupported,\n\t\t\t);\n\t\t} else if (\n\t\t\t!this.supportsCC(CommandClasses.Powerlevel)\n\t\t\t&& !otherNode.supportsCC(CommandClasses.Powerlevel)\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"For a route health check, at least one of the nodes must support Powerlevel CC!\",\n\t\t\t\tZWaveErrorCodes.CC_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\t// No. of pings per round\n\t\tconst healthCheckTestFrameCount = 10;\n\t\tconst start = Date.now();\n\n\t\t/** Computes a health rating from a health check result */\n\t\tconst computeRating = (result: RouteHealthCheckResult) => {\n\t\t\tconst failedPings = Math.max(\n\t\t\t\tresult.failedPingsToSource ?? 0,\n\t\t\t\tresult.failedPingsToTarget ?? 0,\n\t\t\t);\n\t\t\tconst numNeighbors = result.numNeighbors;\n\t\t\tconst minPowerlevel = Math.max(\n\t\t\t\tresult.minPowerlevelSource ?? Powerlevel[\"-6 dBm\"],\n\t\t\t\tresult.minPowerlevelTarget ?? Powerlevel[\"-6 dBm\"],\n\t\t\t);\n\n\t\t\tif (failedPings === 10) return 0;\n\t\t\tif (failedPings > 2) return 1;\n\t\t\tif (failedPings === 2) return 2;\n\t\t\tif (failedPings === 1) return 3;\n\t\t\tif (minPowerlevel < Powerlevel[\"-6 dBm\"]) {\n\t\t\t\t// Lower powerlevel reductions (= higher power) have lower numeric values\n\t\t\t\treturn numNeighbors > 2 ? 7 : 6;\n\t\t\t}\n\t\t\tif (numNeighbors <= 2) return 8;\n\t\t\treturn 10;\n\t\t};\n\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Starting route health check to node ${targetNodeId} (${rounds} round${\n\t\t\t\trounds !== 1 ? \"s\" : \"\"\n\t\t\t})...`,\n\t\t);\n\n\t\tconst results: RouteHealthCheckResult[] = [];\n\t\tconst aborted = () => {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`Route health check to node ${targetNodeId} aborted`,\n\t\t\t);\n\t\t\tif (results.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\trating: 0,\n\t\t\t\t\tresults: [],\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\treturn {\n\t\t\t\t\trating: Math.min(...results.map((r) => r.rating)),\n\t\t\t\t\tresults,\n\t\t\t\t};\n\t\t\t}\n\t\t};\n\n\t\tif (this.canSleep && this.status !== NodeStatus.Awake) {\n\t\t\t// Wait for node to wake up to avoid incorrectly long delays in the first health check round\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`waiting for node to wake up...`,\n\t\t\t);\n\t\t\tawait Promise.race([\n\t\t\t\tthis.waitForWakeup(),\n\t\t\t\tthis._abortHealthCheckPromise,\n\t\t\t]);\n\t\t\tif (this._healthCheckAborted) return aborted();\n\t\t}\n\n\t\tfor (let round = 1; round <= rounds; round++) {\n\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t// Determine the minimum number of repeating neighbors between the\n\t\t\t// source and target node\n\t\t\tconst numNeighbors = Math.min(\n\t\t\t\t(\n\t\t\t\t\tawait this.driver.controller.getNodeNeighbors(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t)\n\t\t\t\t).length,\n\t\t\t\t(\n\t\t\t\t\tawait this.driver.controller.getNodeNeighbors(\n\t\t\t\t\t\ttargetNodeId,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t)\n\t\t\t\t).length,\n\t\t\t);\n\n\t\t\tlet failedPings = 0;\n\t\t\tlet failedPingsToSource: number | undefined;\n\t\t\tlet minPowerlevelSource: Powerlevel | undefined;\n\t\t\tlet failedPingsToTarget: number | undefined;\n\t\t\tlet minPowerlevelTarget: Powerlevel | undefined;\n\t\t\tconst executor =\n\t\t\t\t(node: ZWaveNode, otherNode: ZWaveNode) =>\n\t\t\t\tasync (powerlevel: Powerlevel) => {\n\t\t\t\t\t// Abort the search if the health check was aborted\n\t\t\t\t\tif (this._healthCheckAborted) return undefined;\n\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t`Sending ${healthCheckTestFrameCount} pings to node ${otherNode.id} at ${\n\t\t\t\t\t\t\tgetEnumMemberName(Powerlevel, powerlevel)\n\t\t\t\t\t\t}...`,\n\t\t\t\t\t);\n\t\t\t\t\tconst result = await node.testPowerlevel(\n\t\t\t\t\t\totherNode.id,\n\t\t\t\t\t\tpowerlevel,\n\t\t\t\t\t\thealthCheckTestFrameCount,\n\t\t\t\t\t);\n\t\t\t\t\tfailedPings = healthCheckTestFrameCount - result;\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t`At ${\n\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\tPowerlevel,\n\t\t\t\t\t\t\t\tpowerlevel,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}, ${result}/${healthCheckTestFrameCount} pings were acknowledged by node ${otherNode.id}...`,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Wait a second for things to settle down\n\t\t\t\t\tawait wait(1000);\n\n\t\t\t\t\treturn failedPings === 0;\n\t\t\t\t};\n\n\t\t\t// Now instruct this node to ping the other one, figuring out the minimum powerlevel\n\t\t\tif (this.supportsCC(CommandClasses.Powerlevel)) {\n\t\t\t\ttry {\n\t\t\t\t\t// We have to start with the maximum powerlevel and work our way down\n\t\t\t\t\t// Otherwise some nodes get stuck trying to complete the check at a bad powerlevel\n\t\t\t\t\t// causing the following measurements to fail.\n\t\t\t\t\tconst powerlevel = await discreteLinearSearch(\n\t\t\t\t\t\tPowerlevel[\"Normal Power\"], // minimum reduction\n\t\t\t\t\t\tPowerlevel[\"-9 dBm\"], // maximum reduction\n\t\t\t\t\t\texecutor(this, otherNode),\n\t\t\t\t\t);\n\t\t\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t\t\tif (powerlevel == undefined) {\n\t\t\t\t\t\t// There were still failures at normal power, report it\n\t\t\t\t\t\tminPowerlevelSource = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tfailedPingsToTarget = failedPings;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tminPowerlevelSource = powerlevel;\n\t\t\t\t\t\tfailedPingsToTarget = 0;\n\t\t\t\t\t}\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_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\t// The node is dead, treat this as a failure\n\t\t\t\t\t\tminPowerlevelSource = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tfailedPingsToTarget = healthCheckTestFrameCount;\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\t\t\t}\n\n\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t// And do the same with the other node - unless the current node is a sleeping node, then this doesn't make sense\n\t\t\tif (\n\t\t\t\t!this.canSleep\n\t\t\t\t&& otherNode.supportsCC(CommandClasses.Powerlevel)\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\tconst powerlevel = await discreteLinearSearch(\n\t\t\t\t\t\tPowerlevel[\"Normal Power\"], // minimum reduction\n\t\t\t\t\t\tPowerlevel[\"-9 dBm\"], // maximum reduction\n\t\t\t\t\t\texecutor(otherNode, this),\n\t\t\t\t\t);\n\t\t\t\t\tif (powerlevel == undefined) {\n\t\t\t\t\t\t// There were still failures at normal power, report it\n\t\t\t\t\t\tminPowerlevelTarget = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tfailedPingsToSource = failedPings;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tminPowerlevelTarget = powerlevel;\n\t\t\t\t\t\tfailedPingsToSource = 0;\n\t\t\t\t\t}\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_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\t// The node is dead, treat this as a failure\n\t\t\t\t\t\tminPowerlevelTarget = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tfailedPingsToSource = healthCheckTestFrameCount;\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\t\t\t}\n\n\t\t\tconst ret: RouteHealthCheckResult = {\n\t\t\t\tnumNeighbors,\n\t\t\t\tfailedPingsToSource,\n\t\t\t\tfailedPingsToTarget,\n\t\t\t\tminPowerlevelSource,\n\t\t\t\tminPowerlevelTarget,\n\t\t\t\trating: 0,\n\t\t\t};\n\t\t\tret.rating = computeRating(ret);\n\t\t\tresults.push(ret);\n\t\t\tonProgress?.(round, rounds, ret.rating, { ...ret });\n\t\t}\n\n\t\tconst duration = Date.now() - start;\n\n\t\tconst rating = Math.min(...results.map((r) => r.rating));\n\t\tconst summary = { results, rating };\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Route health check to node ${otherNode.id} complete in ${duration} ms\n${formatRouteHealthCheckSummary(this.id, otherNode.id, summary)}`,\n\t\t);\n\n\t\treturn summary;\n\t}\n\n\tprivate _linkReliabilityCheckInProgress: boolean = false;\n\t/**\n\t * Returns whether a link reliability check is currently in progress for this node\n\t */\n\tpublic isLinkReliabilityCheckInProgress(): boolean {\n\t\treturn this._linkReliabilityCheckInProgress;\n\t}\n\n\tprivate _linkReliabilityCheckAborted: boolean = false;\n\tprivate _abortLinkReliabilityCheckPromise:\n\t\t| DeferredPromise<void>\n\t\t| undefined;\n\n\t/**\n\t * Aborts an ongoing link reliability check if one is currently in progress.\n\t *\n\t * **Note:** The link reliability check may take a few seconds to actually be aborted.\n\t * When it is, the promise returned by {@link checkLinkReliability} will be resolved with the results obtained so far.\n\t */\n\tpublic abortLinkReliabilityCheck(): void {\n\t\tif (!this._linkReliabilityCheckInProgress) return;\n\t\tthis._linkReliabilityCheckAborted = true;\n\t\tthis._abortLinkReliabilityCheckPromise?.resolve();\n\t}\n\n\t/**\n\t * Tests the reliability of the link between the controller and this node and returns the results.\n\t */\n\tpublic async checkLinkReliability(\n\t\toptions: LinkReliabilityCheckOptions,\n\t): Promise<LinkReliabilityCheckResult> {\n\t\tif (this._linkReliabilityCheckInProgress) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"A link reliability check is already in progress for this node!\",\n\t\t\t\tZWaveErrorCodes.LinkReliabilityCheck_Busy,\n\t\t\t);\n\t\t}\n\n\t\tif (typeof options.rounds === \"number\" && options.rounds < 1) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The number of rounds must be at least 1!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tthis._linkReliabilityCheckInProgress = true;\n\t\t\tthis._linkReliabilityCheckAborted = false;\n\t\t\tthis._abortLinkReliabilityCheckPromise = createDeferredPromise();\n\n\t\t\tswitch (options.mode) {\n\t\t\t\tcase LinkReliabilityCheckMode.BasicSetOnOff:\n\t\t\t\t\treturn await this.checkLinkReliabilityBasicSetOnOff(\n\t\t\t\t\t\toptions,\n\t\t\t\t\t);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._linkReliabilityCheckInProgress = false;\n\t\t\tthis._linkReliabilityCheckAborted = false;\n\t\t\tthis._abortLinkReliabilityCheckPromise = undefined;\n\t\t}\n\t}\n\n\tprivate async checkLinkReliabilityBasicSetOnOff(\n\t\toptions: LinkReliabilityCheckOptions,\n\t): Promise<LinkReliabilityCheckResult> {\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Starting link reliability check (Basic Set On/Off) with ${options.rounds} round${\n\t\t\t\toptions.rounds !== 1 ? \"s\" : \"\"\n\t\t\t}...`,\n\t\t);\n\n\t\tconst useSupervision = this.supportsCC(CommandClasses.Supervision);\n\t\tconst result: LinkReliabilityCheckResult = {\n\t\t\trounds: 0,\n\t\t\tcommandsSent: 0,\n\t\t\tcommandErrors: 0,\n\t\t\tmissingResponses: useSupervision ? 0 : undefined,\n\t\t\tlatency: {\n\t\t\t\tmin: Number.POSITIVE_INFINITY,\n\t\t\t\tmax: 0,\n\t\t\t\taverage: 0,\n\t\t\t},\n\t\t\trtt: {\n\t\t\t\tmin: Number.POSITIVE_INFINITY,\n\t\t\t\tmax: 0,\n\t\t\t\taverage: 0,\n\t\t\t},\n\t\t\tackRSSI: {\n\t\t\t\tmin: 0,\n\t\t\t\tmax: Number.NEGATIVE_INFINITY,\n\t\t\t\taverage: Number.NEGATIVE_INFINITY,\n\t\t\t},\n\t\t\tresponseRSSI: useSupervision\n\t\t\t\t? {\n\t\t\t\t\tmin: 0,\n\t\t\t\t\tmax: Number.NEGATIVE_INFINITY,\n\t\t\t\t\taverage: Number.NEGATIVE_INFINITY,\n\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t};\n\n\t\tconst aborted = () => {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`Link reliability check aborted`,\n\t\t\t);\n\t\t\treturn result;\n\t\t};\n\n\t\tlet lastProgressReport = 0;\n\t\tconst reportProgress = () => {\n\t\t\tif (Date.now() - lastProgressReport >= 250) {\n\t\t\t\toptions.onProgress?.(cloneDeep(result));\n\t\t\t\tlastProgressReport = Date.now();\n\t\t\t}\n\t\t};\n\n\t\tif (this.canSleep && this.status !== NodeStatus.Awake) {\n\t\t\t// Wait for node to wake up to avoid incorrectly long delays in the first health check round\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`waiting for node to wake up...`,\n\t\t\t);\n\t\t\tawait Promise.race([\n\t\t\t\tthis.waitForWakeup(),\n\t\t\t\tthis._abortLinkReliabilityCheckPromise,\n\t\t\t]);\n\t\t\tif (this._linkReliabilityCheckAborted) return aborted();\n\t\t}\n\n\t\t// TODO: report progress with throttle\n\n\t\tlet txReport: TXReport | undefined;\n\n\t\tconst basicSetAPI = this.commandClasses.Basic.withOptions({\n\t\t\t// Don't change the node status when the ACK is missing. We're likely testing the limits here.\n\t\t\tchangeNodeStatusOnMissingACK: false,\n\t\t\t// Avoid using explorer frames, because they can create a ton of delay\n\t\t\ttransmitOptions: TransmitOptions.ACK\n\t\t\t\t| TransmitOptions.AutoRoute,\n\t\t\t// Do not wait for SOS NonceReports, as it slows down the test\n\t\t\ts2VerifyDelivery: false,\n\t\t\t// And remember the transmit report, so we can evaluate it\n\t\t\tonTXReport: (report) => {\n\t\t\t\ttxReport = report;\n\t\t\t},\n\t\t});\n\n\t\tlet lastStart: number;\n\t\tfor (\n\t\t\tlet round = 1;\n\t\t\tround <= (options.rounds ?? Number.POSITIVE_INFINITY);\n\t\t\tround++\n\t\t) {\n\t\t\tif (this._linkReliabilityCheckAborted) return aborted();\n\n\t\t\tresult.rounds = round;\n\n\t\t\tlastStart = Date.now();\n\t\t\t// Reset TX report before each command\n\t\t\ttxReport = undefined as any;\n\n\t\t\ttry {\n\t\t\t\tawait basicSetAPI.set(\n\t\t\t\t\tround % 2 === 1 ? 0xff : 0x00,\n\t\t\t\t);\n\t\t\t\t// The command was sent successfully (and possibly got a response)\n\t\t\t\tresult.commandsSent++;\n\n\t\t\t\t// Measure the RTT or latency, whatever is available\n\t\t\t\tconst rtt = Date.now() - lastStart;\n\t\t\t\tresult.rtt.min = Math.min(result.rtt.min, rtt);\n\t\t\t\tresult.rtt.max = Math.max(result.rtt.max, rtt);\n\t\t\t\t// incrementally update the average rtt\n\t\t\t\tresult.rtt.average += (rtt - result.rtt.average) / round;\n\n\t\t\t\tif (txReport) {\n\t\t\t\t\tconst latency = txReport.txTicks * 10;\n\t\t\t\t\tif (result.latency) {\n\t\t\t\t\t\tresult.latency.min = Math.min(\n\t\t\t\t\t\t\tresult.latency.min,\n\t\t\t\t\t\t\tlatency,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tresult.latency.max = Math.max(\n\t\t\t\t\t\t\tresult.latency.max,\n\t\t\t\t\t\t\tlatency,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// incrementally update the average RTT\n\t\t\t\t\t\tresult.latency.average +=\n\t\t\t\t\t\t\t(latency - result.latency.average)\n\t\t\t\t\t\t\t/ round;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult.latency = {\n\t\t\t\t\t\t\tmin: latency,\n\t\t\t\t\t\t\tmax: latency,\n\t\t\t\t\t\t\taverage: latency,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (isZWaveError(e)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\te.code === ZWaveErrorCodes.Controller_ResponseNOK\n\t\t\t\t\t\t|| e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\t// The command could not be sent or was not acknowledged\n\t\t\t\t\t\tresult.commandErrors++;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\te.code === ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t\t\t) {\n\t\t\t\t\t\t// The command was sent using Supervision and a response was\n\t\t\t\t\t\t// expected but none came\n\t\t\t\t\t\tresult.missingResponses ??= 0;\n\t\t\t\t\t\tresult.missingResponses++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\ttxReport?.ackRSSI != undefined\n\t\t\t\t&& !isRssiError(txReport.ackRSSI)\n\t\t\t) {\n\t\t\t\tresult.ackRSSI.min = Math.min(\n\t\t\t\t\tresult.ackRSSI.min,\n\t\t\t\t\ttxReport.ackRSSI,\n\t\t\t\t);\n\t\t\t\tresult.ackRSSI.max = Math.max(\n\t\t\t\t\tresult.ackRSSI.max,\n\t\t\t\t\ttxReport.ackRSSI,\n\t\t\t\t);\n\t\t\t\t// incrementally update the average RSSI\n\t\t\t\tif (Number.isFinite(result.ackRSSI.average)) {\n\t\t\t\t\tresult.ackRSSI.average +=\n\t\t\t\t\t\t(txReport.ackRSSI - result.ackRSSI.average)\n\t\t\t\t\t\t/ round;\n\t\t\t\t} else {\n\t\t\t\t\tresult.ackRSSI.average = txReport.ackRSSI;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// TODO: Capture incoming RSSI and average it\n\n\t\t\treportProgress();\n\n\t\t\t// Throttle the next command\n\t\t\tconst waitDurationMs = Math.max(\n\t\t\t\t0,\n\t\t\t\toptions.interval - (Date.now() - lastStart),\n\t\t\t);\n\t\t\tawait Promise.race([\n\t\t\t\twait(waitDurationMs, true),\n\t\t\t\tthis._abortLinkReliabilityCheckPromise,\n\t\t\t]);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Updates the average RTT of this node\n\t * @internal\n\t */\n\tpublic updateRTT(sentMessage: Message): void {\n\t\tif (sentMessage.rtt) {\n\t\t\tconst rttMs = sentMessage.rtt / 1e6;\n\t\t\tthis.updateStatistics((current) => ({\n\t\t\t\t...current,\n\t\t\t\trtt: current.rtt != undefined\n\t\t\t\t\t? roundTo(current.rtt * 0.75 + rttMs * 0.25, 1)\n\t\t\t\t\t: roundTo(rttMs, 1),\n\t\t\t}));\n\t\t}\n\t}\n\n\t/**\n\t * Updates route/transmission statistics for this node\n\t * @internal\n\t */\n\tpublic updateRouteStatistics(txReport: TXReport): void {\n\t\tthis.updateStatistics((current) => {\n\t\t\tconst ret = { ...current };\n\t\t\t// Update ACK RSSI\n\t\t\tif (txReport.ackRSSI != undefined) {\n\t\t\t\tret.rssi =\n\t\t\t\t\tret.rssi == undefined || isRssiError(txReport.ackRSSI)\n\t\t\t\t\t\t? txReport.ackRSSI\n\t\t\t\t\t\t: Math.round(ret.rssi * 0.75 + txReport.ackRSSI * 0.25);\n\t\t\t}\n\n\t\t\t// Update the LWR's statistics\n\t\t\tconst newStats: RouteStatistics = {\n\t\t\t\tprotocolDataRate: txReport.routeSpeed,\n\t\t\t\trepeaters: (txReport.repeaterNodeIds ?? []) as number[],\n\t\t\t\trssi: txReport.ackRSSI\n\t\t\t\t\t?? ret.lwr?.rssi\n\t\t\t\t\t?? RssiError.NotAvailable,\n\t\t\t};\n\t\t\tif (txReport.ackRepeaterRSSI != undefined) {\n\t\t\t\tnewStats.repeaterRSSI = txReport.ackRepeaterRSSI as number[];\n\t\t\t}\n\t\t\tif (\n\t\t\t\ttxReport.failedRouteLastFunctionalNodeId\n\t\t\t\t&& txReport.failedRouteFirstNonFunctionalNodeId\n\t\t\t) {\n\t\t\t\tnewStats.routeFailedBetween = [\n\t\t\t\t\ttxReport.failedRouteLastFunctionalNodeId,\n\t\t\t\t\ttxReport.failedRouteFirstNonFunctionalNodeId,\n\t\t\t\t];\n\t\t\t}\n\n\t\t\tif (ret.lwr && !routeStatisticsEquals(ret.lwr, newStats)) {\n\t\t\t\t// The old LWR becomes the NLWR\n\t\t\t\tret.nlwr = ret.lwr;\n\t\t\t}\n\t\t\tret.lwr = newStats;\n\t\t\treturn ret;\n\t\t});\n\t}\n\n\t/**\n\t * Sets the current date, time and timezone (or a subset of those) on the node using one or more of the respective CCs.\n\t * Returns whether the operation was successful.\n\t */\n\tpublic async setDateAndTime(now: Date = new Date()): Promise<boolean> {\n\t\t// There are multiple ways to communicate the current time to a node:\n\t\t// 1. Time Parameters CC\n\t\t// 2. Clock CC\n\t\t// 3. Time CC, but only in response to requests from the node\n\t\tconst timeParametersAPI = this.commandClasses[\"Time Parameters\"];\n\t\tconst timeAPI = this.commandClasses.Time;\n\t\tconst clockAPI = this.commandClasses.Clock;\n\t\tconst scheduleEntryLockAPI = this.commandClasses[\"Schedule Entry Lock\"];\n\n\t\tif (\n\t\t\ttimeParametersAPI.isSupported()\n\t\t\t&& timeParametersAPI.supportsCommand(TimeParametersCommand.Set)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeParametersAPI.set(now);\n\t\t\t\tif (supervisedCommandFailed(result)) return false;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (\n\t\t\tclockAPI.isSupported()\n\t\t\t&& clockAPI.supportsCommand(ClockCommand.Set)\n\t\t) {\n\t\t\ttry {\n\t\t\t\t// Get desired time in local time\n\t\t\t\tconst hours = now.getHours();\n\t\t\t\tconst minutes = now.getMinutes();\n\t\t\t\t// Sunday is 0 in JS, but 7 in Z-Wave\n\t\t\t\tlet weekday = now.getDay();\n\t\t\t\tif (weekday === 0) weekday = 7;\n\n\t\t\t\tconst result = await clockAPI.set(hours, minutes, weekday);\n\t\t\t\tif (supervisedCommandFailed(result)) return false;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (\n\t\t\ttimeAPI.isSupported()\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.DateReport)\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.TimeReport)\n\t\t) {\n\t\t\t// According to https://github.com/zwave-js/node-zwave-js/issues/6032#issuecomment-1641945555\n\t\t\t// some devices update their date and time when they receive an unsolicited Time CC report.\n\t\t\t// Even if this isn't intended, we should at least try.\n\n\t\t\tconst api = timeAPI.withOptions({\n\t\t\t\tuseSupervision: false,\n\t\t\t});\n\t\t\ttry {\n\t\t\t\t// First date\n\t\t\t\tconst year = now.getFullYear();\n\t\t\t\tconst month = now.getMonth() + 1;\n\t\t\t\tconst day = now.getDate();\n\t\t\t\tawait api.reportDate(year, month, day);\n\n\t\t\t\tconst verification = await api.getDate();\n\t\t\t\tif (\n\t\t\t\t\t!verification\n\t\t\t\t\t|| verification.year !== year\n\t\t\t\t\t|| verification.month !== month\n\t\t\t\t\t|| verification.day !== day\n\t\t\t\t) {\n\t\t\t\t\t// Didn't work\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Then time\n\t\t\t\tconst hour = now.getHours();\n\t\t\t\tconst minute = now.getMinutes();\n\t\t\t\tconst second = now.getSeconds();\n\t\t\t\tawait api.reportTime(hour, minute, second);\n\n\t\t\t\tconst verification = await api.getTime();\n\t\t\t\tif (!verification) return false;\n\t\t\t\t// To leave a bit of tolerance for communication delays, we compare the seconds since midnight\n\t\t\t\tconst secondsPerDay = 24 * 60 * 60;\n\t\t\t\tconst expected = hour * 60 * 60 + minute * 60 + second;\n\t\t\t\tconst expectedMin = expected - 30;\n\t\t\t\tconst expectedMax = expected + 30;\n\t\t\t\tconst actual = verification.hour * 60 * 60\n\t\t\t\t\t+ verification.minute * 60\n\t\t\t\t\t+ verification.second;\n\t\t\t\t// The time may have wrapped around midnight since we set the date\n\t\t\t\tif (actual >= expectedMin && actual <= expectedMax) {\n\t\t\t\t\t// ok\n\t\t\t\t} else if (\n\t\t\t\t\tactual + secondsPerDay >= expectedMin\n\t\t\t\t\t&& actual + secondsPerDay <= expectedMax\n\t\t\t\t) {\n\t\t\t\t\t// ok\n\t\t\t\t} else {\n\t\t\t\t\t// Didn't work\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// We might also have to change the timezone. That is done with the Time CC.\n\t\t// Or in really strange cases using the Schedule Entry Lock CC\n\t\tconst timezone = getDSTInfo(now);\n\t\tif (\n\t\t\ttimeAPI.isSupported()\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.TimeOffsetSet)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeAPI.setTimezone(timezone);\n\t\t\t\tif (supervisedCommandFailed(result)) return false;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (\n\t\t\tscheduleEntryLockAPI.isSupported()\n\t\t\t&& scheduleEntryLockAPI.supportsCommand(\n\t\t\t\tScheduleEntryLockCommand.TimeOffsetSet,\n\t\t\t)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await scheduleEntryLockAPI.setTimezone(timezone);\n\t\t\t\tif (supervisedCommandFailed(result)) return false;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns the current date, time and timezone (or a subset of those) on the node using one or more of the respective CCs.\n\t */\n\tpublic async getDateAndTime(): Promise<DateAndTime> {\n\t\tconst timeParametersAPI = this.commandClasses[\"Time Parameters\"];\n\t\tconst timeAPI = this.commandClasses.Time;\n\t\tconst clockAPI = this.commandClasses.Clock;\n\t\tconst scheduleEntryLockAPI = this.commandClasses[\"Schedule Entry Lock\"];\n\n\t\tconst response: DateAndTime = {};\n\n\t\tif (\n\t\t\ttimeParametersAPI.isSupported()\n\t\t\t&& timeParametersAPI.supportsCommand(TimeParametersCommand.Get)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeParametersAPI.get();\n\t\t\t\tif (result) {\n\t\t\t\t\t// Time Parameters is all UTC per the spec\n\t\t\t\t\tObject.assign(response, {\n\t\t\t\t\t\thour: result.getUTCHours(),\n\t\t\t\t\t\tminute: result.getUTCMinutes(),\n\t\t\t\t\t\tsecond: result.getUTCSeconds(),\n\t\t\t\t\t\tstandardOffset: 0,\n\t\t\t\t\t\tdstOffset: 0,\n\t\t\t\t\t\tweekday: result.getUTCDay(),\n\t\t\t\t\t\tday: result.getUTCDate(),\n\t\t\t\t\t\tmonth: result.getUTCMonth() + 1,\n\t\t\t\t\t\tyear: result.getUTCFullYear(),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// That's everything\n\t\t\t\treturn response;\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (\n\t\t\tclockAPI.isSupported()\n\t\t\t&& clockAPI.supportsCommand(ClockCommand.Get)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await clockAPI.get();\n\t\t\t\tif (result) {\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thour: result.hour,\n\t\t\t\t\t\t\tminute: result.minute,\n\t\t\t\t\t\t\tweekday: result.weekday,\n\t\t\t\t\t\t} satisfies DateAndTime,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (\n\t\t\ttimeAPI.isSupported()\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.TimeGet)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeAPI.getTime();\n\t\t\t\tif (result) {\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thour: result.hour,\n\t\t\t\t\t\t\tminute: result.minute,\n\t\t\t\t\t\t\tsecond: result.second,\n\t\t\t\t\t\t} satisfies DateAndTime,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (\n\t\t\ttimeAPI.isSupported()\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.DateGet)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeAPI.getDate();\n\t\t\t\tif (result) {\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tday: result.day,\n\t\t\t\t\t\t\tmonth: result.month,\n\t\t\t\t\t\t\tyear: result.year,\n\t\t\t\t\t\t} satisfies DateAndTime,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (\n\t\t\ttimeAPI.isSupported()\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.TimeOffsetGet)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeAPI.getTimezone();\n\t\t\t\tif (result) {\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstandardOffset: result.standardOffset,\n\t\t\t\t\t\t\tdstOffset: result.dstOffset,\n\t\t\t\t\t\t} satisfies DateAndTime,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (\n\t\t\tscheduleEntryLockAPI.isSupported()\n\t\t\t&& scheduleEntryLockAPI.supportsCommand(\n\t\t\t\tScheduleEntryLockCommand.TimeOffsetGet,\n\t\t\t)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await scheduleEntryLockAPI.getTimezone();\n\t\t\t\tif (result) {\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstandardOffset: result.standardOffset,\n\t\t\t\t\t\t\tdstOffset: result.dstOffset,\n\t\t\t\t\t\t} satisfies DateAndTime,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn response;\n\t}\n\n\tpublic async sendResetLocallyNotification(): Promise<void> {\n\t\t// We don't care if the CC is supported by the receiving node\n\t\tconst api = this.createAPI(\n\t\t\tCommandClasses[\"Device Reset Locally\"],\n\t\t\tfalse,\n\t\t);\n\n\t\tawait api.sendNotification();\n\t}\n\n\t/**\n\t * Returns whether the device config for this node has changed since the last interview.\n\t * If it has, the node likely needs to be re-interviewed for the changes to be picked up.\n\t */\n\tpublic hasDeviceConfigChanged(): MaybeNotKnown<boolean> {\n\t\t// We can't know if the node is not fully interviewed\n\t\tif (this.interviewStage !== InterviewStage.Complete) return NOT_KNOWN;\n\n\t\t// The controller cannot be re-interviewed\n\t\tif (this.isControllerNode) return false;\n\n\t\t// If the hash was never stored, we can only (very likely) know if the config has not changed\n\t\tif (this.cachedDeviceConfigHash == undefined) {\n\t\t\treturn this.deviceConfig == undefined ? false : NOT_KNOWN;\n\t\t}\n\n\t\t// If it was, a change in hash means the config has changed\n\t\tif (this._currentDeviceConfigHash) {\n\t\t\treturn !Bytes.view(this._currentDeviceConfigHash).equals(\n\t\t\t\tthis.cachedDeviceConfigHash,\n\t\t\t);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/** Returns a dump of this node's information for debugging purposes */\n\tpublic createDump(): NodeDump {\n\t\tconst { index, ...endpointDump } = this.createEndpointDump();\n\t\tconst ret: NodeDump = {\n\t\t\tid: this.id,\n\t\t\tmanufacturer: this.deviceConfig?.manufacturer,\n\t\t\tlabel: this.label,\n\t\t\tdescription: this.deviceConfig?.description,\n\t\t\tfingerprint: {\n\t\t\t\tmanufacturerId: this.manufacturerId != undefined\n\t\t\t\t\t? formatId(this.manufacturerId)\n\t\t\t\t\t: \"unknown\",\n\t\t\t\tproductType: this.productType != undefined\n\t\t\t\t\t? formatId(this.productType)\n\t\t\t\t\t: \"unknown\",\n\t\t\t\tproductId: this.productId != undefined\n\t\t\t\t\t? formatId(this.productId)\n\t\t\t\t\t: \"unknown\",\n\t\t\t\tfirmwareVersion: this.firmwareVersion ?? \"unknown\",\n\t\t\t},\n\t\t\tinterviewStage: getEnumMemberName(\n\t\t\t\tInterviewStage,\n\t\t\t\tthis.interviewStage,\n\t\t\t),\n\t\t\tready: this.ready,\n\n\t\t\tdsk: this.dsk ? dskToString(this.dsk) : undefined,\n\t\t\tsecurityClasses: {},\n\n\t\t\tisListening: this.isListening ?? \"unknown\",\n\t\t\tisFrequentListening: this.isFrequentListening ?? \"unknown\",\n\t\t\tisRouting: this.isRouting ?? \"unknown\",\n\t\t\tsupportsBeaming: this.supportsBeaming ?? \"unknown\",\n\t\t\tsupportsSecurity: this.supportsSecurity ?? \"unknown\",\n\t\t\tprotocol: getEnumMemberName(Protocols, this.protocol),\n\t\t\tsupportedProtocols: this.driver.controller.getProvisioningEntry(\n\t\t\t\tthis.id,\n\t\t\t)?.supportedProtocols?.map((p) => getEnumMemberName(Protocols, p)),\n\t\t\tprotocolVersion: this.protocolVersion != undefined\n\t\t\t\t? getEnumMemberName(ProtocolVersion, this.protocolVersion)\n\t\t\t\t: \"unknown\",\n\t\t\tsdkVersion: this.sdkVersion ?? \"unknown\",\n\t\t\tsupportedDataRates: this.supportedDataRates\n\t\t\t\t? [...this.supportedDataRates]\n\t\t\t\t: \"unknown\",\n\n\t\t\t...endpointDump,\n\t\t};\n\n\t\tif (this.hardwareVersion != undefined) {\n\t\t\tret.fingerprint.hardwareVersion = this.hardwareVersion;\n\t\t}\n\n\t\tfor (const secClass of securityClassOrder) {\n\t\t\tif (\n\t\t\t\tthis.protocol === Protocols.ZWaveLongRange\n\t\t\t\t&& !securityClassIsLongRange(secClass)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tret.securityClasses[getEnumMemberName(SecurityClass, secClass)] =\n\t\t\t\tthis.hasSecurityClass(secClass) ?? \"unknown\";\n\t\t}\n\n\t\tconst allValueIds = nodeUtils.getDefinedValueIDsInternal(\n\t\t\tthis.driver,\n\t\t\tthis,\n\t\t\ttrue,\n\t\t);\n\n\t\tconst collectValues = (\n\t\t\tendpointIndex: number,\n\t\t\tgetCollection: (ccId: CommandClasses) => ValueDump[] | undefined,\n\t\t) => {\n\t\t\tfor (const valueId of allValueIds) {\n\t\t\t\tif ((valueId.endpoint ?? 0) !== endpointIndex) continue;\n\n\t\t\t\tconst value = this._valueDB.getValue(valueId);\n\t\t\t\tconst metadata = this._valueDB.getMetadata(valueId);\n\t\t\t\tconst timestamp = this._valueDB.getTimestamp(valueId);\n\t\t\t\tconst timestampAsDate = timestamp\n\t\t\t\t\t? new Date(timestamp).toISOString()\n\t\t\t\t\t: undefined;\n\n\t\t\t\tconst ccInstance = CommandClass.createInstanceUnchecked(\n\t\t\t\t\tthis,\n\t\t\t\t\tvalueId.commandClass,\n\t\t\t\t);\n\t\t\t\tconst isInternalValue = ccInstance?.isInternalValue(valueId);\n\n\t\t\t\tconst valueDump: ValueDump = {\n\t\t\t\t\t...pick(valueId, [\n\t\t\t\t\t\t\"property\",\n\t\t\t\t\t\t\"propertyKey\",\n\t\t\t\t\t]),\n\t\t\t\t\tmetadata,\n\t\t\t\t\tvalue: metadata?.secret\n\t\t\t\t\t\t? \"(redacted)\"\n\t\t\t\t\t\t: serializeCacheValue(value),\n\t\t\t\t\ttimestamp: timestampAsDate,\n\t\t\t\t};\n\t\t\t\tif (isInternalValue) valueDump.internal = true;\n\n\t\t\t\tfor (const [prop, value] of Object.entries(valueDump)) {\n\t\t\t\t\t// @ts-expect-error\n\t\t\t\t\tif (value === undefined) delete valueDump[prop];\n\t\t\t\t}\n\n\t\t\t\tgetCollection(valueId.commandClass)?.push(valueDump);\n\t\t\t}\n\t\t};\n\t\tcollectValues(0, (ccId) => ret.commandClasses[getCCName(ccId)]?.values);\n\n\t\tfor (const endpoint of this.getAllEndpoints()) {\n\t\t\tif (endpoint.index === 0) continue;\n\t\t\tret.endpoints ??= {};\n\t\t\tconst endpointDump = endpoint.createEndpointDump();\n\t\t\tcollectValues(\n\t\t\t\tendpoint.index,\n\t\t\t\t(ccId) => endpointDump.commandClasses[getCCName(ccId)]?.values,\n\t\t\t);\n\t\t\tret.endpoints[endpoint.index] = endpointDump;\n\t\t}\n\n\t\tif (this.deviceConfig) {\n\t\t\tconst relativePath = path.relative(\n\t\t\t\tembeddedDevicesDir,\n\t\t\t\tthis.deviceConfig.filename,\n\t\t\t);\n\t\t\tif (relativePath.startsWith(\"..\")) {\n\t\t\t\t// The path is outside our embedded config dir, take the full path\n\t\t\t\tret.configFileName = this.deviceConfig.filename;\n\t\t\t} else {\n\t\t\t\tret.configFileName = relativePath;\n\t\t\t}\n\n\t\t\tif (this.deviceConfig.compat) {\n\t\t\t\t// TODO: Check if everything comes through this way.\n\t\t\t\tret.compatFlags = this.deviceConfig.compat;\n\t\t\t}\n\t\t}\n\t\tfor (const [prop, value] of Object.entries(ret)) {\n\t\t\t// @ts-expect-error\n\t\t\tif (value === undefined) delete ret[prop];\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\tprotected _emit<TEvent extends keyof AllNodeEvents>(\n\t\tevent: TEvent,\n\t\t...args: Parameters<AllNodeEvents[TEvent]>\n\t): boolean {\n\t\treturn this.emit(event, ...args);\n\t}\n\n\tprotected _on<TEvent extends keyof AllNodeEvents>(\n\t\tevent: TEvent,\n\t\tcallback: AllNodeEvents[TEvent],\n\t): this {\n\t\treturn this.on(event, callback);\n\t}\n\n\tprotected _once<TEvent extends keyof AllNodeEvents>(\n\t\tevent: TEvent,\n\t\tcallback: AllNodeEvents[TEvent],\n\t): this {\n\t\treturn this.once(event, callback);\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tAssociationGroupInfoProfile,\n\ttype CCAPI,\n\tCentralSceneKeys,\n\tClockCommand,\n\tCommandClass,\n\tDeviceResetLocallyCCNotification,\n\tDeviceResetLocallyCommand,\n\tDoorLockMode,\n\tEntryControlDataTypes,\n\ttype FirmwareUpdateCapabilities,\n\tInclusionControllerCCInitiate,\n\tInclusionControllerStep,\n\tIndicatorCCDescriptionGet,\n\tIndicatorCCGet,\n\tIndicatorCCSet,\n\tIndicatorCCSupportedGet,\n\tMultiChannelAssociationCCGet,\n\tMultiChannelAssociationCCRemove,\n\tMultiChannelAssociationCCSet,\n\tMultiChannelAssociationCCSupportedGroupingsGet,\n\tMultiCommandCCCommandEncapsulation,\n\tMultilevelSwitchCommand,\n\ttype PollValueImplementation,\n\tPowerlevel,\n\tPowerlevelTestStatus,\n\tScheduleEntryLockCommand,\n\tSecurity2Command,\n\ttype SetValueAPIOptions,\n\tTimeCCDateGet,\n\tTimeCCTimeGet,\n\tTimeCCTimeOffsetGet,\n\tTimeCommand,\n\tTimeParametersCommand,\n\tUserCodeCCValues,\n\ttype ValueIDProperties,\n\tZWavePlusNodeType,\n\tZWavePlusRoleType,\n\tentryControlEventTypeLabels,\n\tgetEffectiveCCVersion,\n\tgetImplementedVersion,\n\tutils as ccUtils,\n} from \"@zwave-js/cc\";\nimport {\n\tAssociationCCGet,\n\tAssociationCCRemove,\n\tAssociationCCSet,\n\tAssociationCCSpecificGroupGet,\n\tAssociationCCSupportedGroupingsGet,\n\tAssociationCCValues,\n} from \"@zwave-js/cc/AssociationCC\";\nimport {\n\tAssociationGroupInfoCCCommandListGet,\n\tAssociationGroupInfoCCInfoGet,\n\tAssociationGroupInfoCCNameGet,\n} from \"@zwave-js/cc/AssociationGroupInfoCC\";\nimport {\n\tBasicCC,\n\tBasicCCReport,\n\tBasicCCSet,\n\tBasicCCValues,\n} from \"@zwave-js/cc/BasicCC\";\nimport {\n\ttype BinarySwitchCC,\n\tBinarySwitchCCSet,\n\tBinarySwitchCCValues,\n} from \"@zwave-js/cc/BinarySwitchCC\";\nimport {\n\tCentralSceneCCNotification,\n\tCentralSceneCCValues,\n} from \"@zwave-js/cc/CentralSceneCC\";\nimport { ClockCCReport } from \"@zwave-js/cc/ClockCC\";\nimport { DoorLockCCValues } from \"@zwave-js/cc/DoorLockCC\";\nimport { EntryControlCCNotification } from \"@zwave-js/cc/EntryControlCC\";\nimport {\n\tFirmwareUpdateMetaDataCCGet,\n\tFirmwareUpdateMetaDataCCMetaDataGet,\n\tFirmwareUpdateMetaDataCCValues,\n} from \"@zwave-js/cc/FirmwareUpdateMetaDataCC\";\nimport { HailCC } from \"@zwave-js/cc/HailCC\";\nimport { LockCCValues } from \"@zwave-js/cc/LockCC\";\nimport {\n\tManufacturerSpecificCCGet,\n\tManufacturerSpecificCCValues,\n} from \"@zwave-js/cc/ManufacturerSpecificCC\";\nimport {\n\tMultilevelSwitchCC,\n\tMultilevelSwitchCCSet,\n\tMultilevelSwitchCCStartLevelChange,\n\tMultilevelSwitchCCStopLevelChange,\n\tMultilevelSwitchCCValues,\n} from \"@zwave-js/cc/MultilevelSwitchCC\";\nimport { NodeNamingAndLocationCCValues } from \"@zwave-js/cc/NodeNamingCC\";\nimport {\n\tNotificationCCReport,\n\tNotificationCCValues,\n\tgetNotificationEnumBehavior,\n\tgetNotificationStateValueWithEnum,\n\tgetNotificationValueMetadata,\n} from \"@zwave-js/cc/NotificationCC\";\nimport {\n\tPowerlevelCCGet,\n\tPowerlevelCCSet,\n\tPowerlevelCCTestNodeGet,\n\tPowerlevelCCTestNodeReport,\n\tPowerlevelCCTestNodeSet,\n} from \"@zwave-js/cc/PowerlevelCC\";\nimport { SceneActivationCCSet } from \"@zwave-js/cc/SceneActivationCC\";\nimport {\n\tSecurity2CCCommandsSupportedGet,\n\tSecurity2CCMessageEncapsulation,\n\tSecurity2CCNonceGet,\n\tSecurity2CCNonceReport,\n} from \"@zwave-js/cc/Security2CC\";\nimport {\n\tSecurityCCCommandsSupportedGet,\n\tSecurityCCNonceGet,\n\tSecurityCCNonceReport,\n} from \"@zwave-js/cc/SecurityCC\";\nimport {\n\ttype ThermostatModeCC,\n\tThermostatModeCCSet,\n\tThermostatModeCCValues,\n} from \"@zwave-js/cc/ThermostatModeCC\";\nimport {\n\tVersionCCCapabilitiesGet,\n\tVersionCCCommandClassGet,\n\tVersionCCGet,\n\tVersionCCValues,\n} from \"@zwave-js/cc/VersionCC\";\nimport {\n\tWakeUpCCValues,\n\tWakeUpCCWakeUpNotification,\n} from \"@zwave-js/cc/WakeUpCC\";\nimport { ZWavePlusCCGet, ZWavePlusCCValues } from \"@zwave-js/cc/ZWavePlusCC\";\nimport {\n\ttype SetValueResult,\n\tSetValueStatus,\n\tsupervisionResultToSetValueResult,\n} from \"@zwave-js/cc/safe\";\nimport { type DeviceConfig, embeddedDevicesDir } from \"@zwave-js/config\";\nimport {\n\tBasicDeviceClass,\n\tCommandClasses,\n\tDuration,\n\tEncapsulationFlags,\n\ttype MaybeNotKnown,\n\tMessagePriority,\n\tNOT_KNOWN,\n\tNodeType,\n\ttype NodeUpdatePayload,\n\ttype Notification,\n\ttype NotificationState,\n\tProtocolVersion,\n\tProtocols,\n\ttype QuerySecurityClasses,\n\ttype RSSI,\n\tRssiError,\n\tSecurityClass,\n\ttype SendCommandOptions,\n\ttype SetValueOptions,\n\ttype SinglecastCC,\n\tSupervisionStatus,\n\ttype TXReport,\n\ttype TranslatedValueID,\n\tTransmitOptions,\n\ttype ValueDB,\n\ttype ValueID,\n\ttype ValueMetadata,\n\ttype ValueMetadataNumeric,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tZWaveLibraryTypes,\n\tactuatorCCs,\n\tallCCs,\n\tapplicationCCs,\n\tdskToString,\n\tencapsulationCCs,\n\tgetCCName,\n\tgetDSTInfo,\n\tgetNotification,\n\tgetNotificationValue,\n\tisRssiError,\n\tisSupervisionResult,\n\tisTransmissionError,\n\tisUnsupervisedOrSucceeded,\n\tisZWaveError,\n\tnonApplicationCCs,\n\tnormalizeValueID,\n\tsecurityClassIsLongRange,\n\tsecurityClassIsS2,\n\tsecurityClassOrder,\n\tsensorCCs,\n\tserializeCacheValue,\n\tsupervisedCommandFailed,\n\tsupervisedCommandSucceeded,\n\ttopologicalSort,\n\tvalueIdToString,\n} from \"@zwave-js/core\";\nimport { FunctionType, type Message } from \"@zwave-js/serial\";\nimport {\n\ttype ApplicationUpdateRequest,\n\tApplicationUpdateRequestNodeInfoReceived,\n\tApplicationUpdateRequestNodeInfoRequestFailed,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tGetNodeProtocolInfoRequest,\n\ttype GetNodeProtocolInfoResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport {\n\tRequestNodeInfoRequest,\n\tRequestNodeInfoResponse,\n} from \"@zwave-js/serial/serialapi\";\nimport { containsCC } from \"@zwave-js/serial/serialapi\";\nimport {\n\tBytes,\n\tMixin,\n\ttype TypedEventEmitter,\n\tcloneDeep,\n\tdiscreteLinearSearch,\n\tformatId,\n\tgetEnumMemberName,\n\tgetErrorMessage,\n\tisUint8Array,\n\tnoop,\n\tpick,\n\tstringify,\n} from \"@zwave-js/shared\";\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 { EventEmitter } from \"node:events\";\nimport path from \"node:path\";\nimport semverParse from \"semver/functions/parse.js\";\nimport { RemoveNodeReason } from \"../controller/Inclusion.js\";\nimport { determineNIF } from \"../controller/NodeInformationFrame.js\";\nimport { type Driver, libVersion } from \"../driver/Driver.js\";\nimport { cacheKeys } from \"../driver/NetworkCache.js\";\nimport type { StatisticsEventCallbacksWithSelf } from \"../driver/Statistics.js\";\nimport type { Transaction } from \"../driver/Transaction.js\";\nimport { DeviceClass } from \"./DeviceClass.js\";\nimport { type NodeDump, type ValueDump } from \"./Dump.js\";\nimport { type Endpoint } from \"./Endpoint.js\";\nimport {\n\tformatLifelineHealthCheckSummary,\n\tformatRouteHealthCheckSummary,\n\thealthCheckTestFrameCount,\n} from \"./HealthCheck.js\";\nimport {\n\ttype NodeStatistics,\n\tNodeStatisticsHost,\n\ttype RouteStatistics,\n\trouteStatisticsEquals,\n} from \"./NodeStatistics.js\";\nimport {\n\ttype DateAndTime,\n\ttype LifelineHealthCheckResult,\n\ttype LifelineHealthCheckSummary,\n\tLinkReliabilityCheckMode,\n\ttype LinkReliabilityCheckOptions,\n\ttype LinkReliabilityCheckResult,\n\ttype RefreshInfoOptions,\n\ttype RouteHealthCheckResult,\n\ttype RouteHealthCheckSummary,\n\ttype ZWaveNodeEventCallbacks,\n} from \"./_Types.js\";\nimport { InterviewStage, NodeStatus } from \"./_Types.js\";\nimport { ZWaveNodeMixins } from \"./mixins/index.js\";\nimport * as nodeUtils from \"./utils.js\";\n\nconst MAX_ASSOCIATIONS = 1;\n\ntype AllNodeEvents =\n\t& ZWaveNodeEventCallbacks\n\t& StatisticsEventCallbacksWithSelf<ZWaveNode, NodeStatistics>;\n\nexport interface ZWaveNode\n\textends TypedEventEmitter<AllNodeEvents>, NodeStatisticsHost\n{}\n\n/**\n * A ZWaveNode represents a node in a Z-Wave network. It is also an instance\n * of its root endpoint (index 0)\n */\n@Mixin([EventEmitter, NodeStatisticsHost])\nexport class ZWaveNode extends ZWaveNodeMixins implements QuerySecurityClasses {\n\tpublic constructor(\n\t\tid: number,\n\t\tdriver: Driver,\n\t\tdeviceClass?: DeviceClass,\n\t\tsupportedCCs: CommandClasses[] = [],\n\t\tcontrolledCCs: CommandClasses[] = [],\n\t\tvalueDB?: ValueDB,\n\t) {\n\t\tsuper(\n\t\t\tid,\n\t\t\tdriver,\n\t\t\t// Define this node's intrinsic endpoint as the root device (0)\n\t\t\t0,\n\t\t\tdeviceClass,\n\t\t\tsupportedCCs,\n\t\t\tvalueDB,\n\t\t);\n\n\t\t// Add optional controlled CCs - endpoints don't have this\n\t\tfor (const cc of controlledCCs) this.addCC(cc, { isControlled: true });\n\t}\n\n\t/**\n\t * Cleans up all resources used by this node\n\t */\n\tpublic destroy(): void {\n\t\t// Stop all state machines\n\t\tthis.statusMachine.stop();\n\t\tthis.readyMachine.stop();\n\n\t\t// Remove all timeouts\n\t\tfor (\n\t\t\tconst timeout of [\n\t\t\t\tthis.centralSceneKeyHeldDownContext?.timeout,\n\t\t\t\t...this.notificationIdleTimeouts.values(),\n\t\t\t]\n\t\t) {\n\t\t\tif (timeout) clearTimeout(timeout);\n\t\t}\n\n\t\t// Remove all event handlers\n\t\tthis.removeAllListeners();\n\n\t\t// Clear all scheduled polls that would interfere with the interview\n\t\tthis.cancelAllScheduledPolls();\n\t}\n\n\t/**\n\t * The device specific key (DSK) of this node in binary format.\n\t * This is only set if included with Security S2.\n\t */\n\tpublic get dsk(): Uint8Array | undefined {\n\t\treturn this.driver.cacheGet(cacheKeys.node(this.id).dsk);\n\t}\n\t/** @internal */\n\tpublic set dsk(value: Uint8Array | undefined) {\n\t\tconst cacheKey = cacheKeys.node(this.id).dsk;\n\t\tthis.driver.cacheSet(cacheKey, value);\n\t}\n\n\tpublic get manufacturerId(): MaybeNotKnown<number> {\n\t\treturn this.getValue(ManufacturerSpecificCCValues.manufacturerId.id);\n\t}\n\n\tpublic get productId(): MaybeNotKnown<number> {\n\t\treturn this.getValue(ManufacturerSpecificCCValues.productId.id);\n\t}\n\n\tpublic get productType(): MaybeNotKnown<number> {\n\t\treturn this.getValue(ManufacturerSpecificCCValues.productType.id);\n\t}\n\n\tpublic get firmwareVersion(): MaybeNotKnown<string> {\n\t\t// On supporting nodes, use the applicationVersion, which MUST be\n\t\t// same as the first (main) firmware, plus the patch version.\n\t\tconst firmware0Version = this.getValue<string[]>(\n\t\t\tVersionCCValues.firmwareVersions.id,\n\t\t)?.[0];\n\t\tconst applicationVersion = this.getValue<string>(\n\t\t\tVersionCCValues.applicationVersion.id,\n\t\t);\n\n\t\tlet ret = firmware0Version;\n\t\tif (applicationVersion) {\n\t\t\t// If the application version is set, we cannot blindly trust that it is the firmware version.\n\t\t\t// Some nodes incorrectly set this field to the Z-Wave Application Framework API Version\n\t\t\tif (!ret || applicationVersion.startsWith(`${ret}.`)) {\n\t\t\t\tret = applicationVersion;\n\t\t\t}\n\t\t}\n\n\t\t// Special case for the official 700 series firmwares which are aligned with the SDK version\n\t\t// We want to work with the full x.y.z firmware version here.\n\t\tif (ret && this.isControllerNode) {\n\t\t\tconst sdkVersion = this.sdkVersion;\n\t\t\tif (sdkVersion && sdkVersion.startsWith(`${ret}.`)) {\n\t\t\t\treturn sdkVersion;\n\t\t\t}\n\t\t}\n\t\t// For all others, just return the simple x.y firmware version\n\t\treturn ret;\n\t}\n\n\tpublic get hardwareVersion(): MaybeNotKnown<number> {\n\t\treturn this.getValue(VersionCCValues.hardwareVersion.id);\n\t}\n\n\tpublic get sdkVersion(): MaybeNotKnown<string> {\n\t\treturn this.getValue(VersionCCValues.sdkVersion.id);\n\t}\n\n\tpublic get zwavePlusVersion(): MaybeNotKnown<number> {\n\t\treturn this.getValue(ZWavePlusCCValues.zwavePlusVersion.id);\n\t}\n\n\tpublic get zwavePlusNodeType(): MaybeNotKnown<ZWavePlusNodeType> {\n\t\treturn this.getValue(ZWavePlusCCValues.nodeType.id);\n\t}\n\n\tpublic get zwavePlusRoleType(): MaybeNotKnown<ZWavePlusRoleType> {\n\t\treturn this.getValue(ZWavePlusCCValues.roleType.id);\n\t}\n\n\tpublic get supportsWakeUpOnDemand(): MaybeNotKnown<boolean> {\n\t\treturn this.getValue(WakeUpCCValues.wakeUpOnDemandSupported.id);\n\t}\n\n\t/**\n\t * The user-defined name of this node. Uses the value reported by `Node Naming and Location CC` if it exists.\n\t *\n\t * **Note:** Setting this value only updates the name locally. To permanently change the name of the node, use\n\t * the `commandClasses` API.\n\t */\n\tpublic get name(): MaybeNotKnown<string> {\n\t\treturn this.getValue(NodeNamingAndLocationCCValues.name.id);\n\t}\n\tpublic set name(value: string | undefined) {\n\t\tif (value != undefined) {\n\t\t\tthis._valueDB.setValue(\n\t\t\t\tNodeNamingAndLocationCCValues.name.id,\n\t\t\t\tvalue,\n\t\t\t);\n\t\t} else {\n\t\t\tthis._valueDB.removeValue(NodeNamingAndLocationCCValues.name.id);\n\t\t}\n\t}\n\n\t/**\n\t * The user-defined location of this node. Uses the value reported by `Node Naming and Location CC` if it exists.\n\t *\n\t * **Note:** Setting this value only updates the location locally. To permanently change the location of the node, use\n\t * the `commandClasses` API.\n\t */\n\tpublic get location(): MaybeNotKnown<string> {\n\t\treturn this.getValue(NodeNamingAndLocationCCValues.location.id);\n\t}\n\tpublic set location(value: string | undefined) {\n\t\tif (value != undefined) {\n\t\t\tthis._valueDB.setValue(\n\t\t\t\tNodeNamingAndLocationCCValues.location.id,\n\t\t\t\tvalue,\n\t\t\t);\n\t\t} else {\n\t\t\tthis._valueDB.removeValue(\n\t\t\t\tNodeNamingAndLocationCCValues.location.id,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Whether a SUC return route was configured for this node */\n\tpublic get hasSUCReturnRoute(): boolean {\n\t\treturn !!this.driver.cacheGet(\n\t\t\tcacheKeys.node(this.id).hasSUCReturnRoute,\n\t\t);\n\t}\n\tpublic set hasSUCReturnRoute(value: boolean) {\n\t\tthis.driver.cacheSet(cacheKeys.node(this.id).hasSUCReturnRoute, value);\n\t}\n\n\tprivate _deviceConfig: DeviceConfig | undefined;\n\t/**\n\t * Contains additional information about this node, loaded from a config file\n\t */\n\tpublic get deviceConfig(): DeviceConfig | undefined {\n\t\treturn this._deviceConfig;\n\t}\n\n\tpublic get label(): string | undefined {\n\t\treturn this._deviceConfig?.label;\n\t}\n\n\tpublic get deviceDatabaseUrl(): MaybeNotKnown<string> {\n\t\tif (\n\t\t\tthis.manufacturerId != undefined\n\t\t\t&& this.productType != undefined\n\t\t\t&& this.productId != undefined\n\t\t) {\n\t\t\tconst manufacturerId = formatId(this.manufacturerId);\n\t\t\tconst productType = formatId(this.productType);\n\t\t\tconst productId = formatId(this.productId);\n\t\t\tconst firmwareVersion = this.firmwareVersion || \"0.0\";\n\t\t\treturn `https://devices.zwave-js.io/?jumpTo=${manufacturerId}:${productType}:${productId}:${firmwareVersion}`;\n\t\t}\n\t}\n\n\t/** The last time a message was received from this node */\n\tpublic get lastSeen(): MaybeNotKnown<Date> {\n\t\treturn this.driver.cacheGet(cacheKeys.node(this.id).lastSeen);\n\t}\n\t/** @internal */\n\tpublic set lastSeen(value: MaybeNotKnown<Date>) {\n\t\tthis.driver.cacheSet(cacheKeys.node(this.id).lastSeen, value);\n\t\t// Also update statistics\n\t\tthis.updateStatistics((cur) => ({\n\t\t\t...cur,\n\t\t\tlastSeen: value,\n\t\t}));\n\t}\n\n\t/**\n\t * The default volume level to be used for activating a Sound Switch.\n\t * Can be overridden by command-specific options.\n\t */\n\tpublic get defaultVolume(): number | undefined {\n\t\treturn this.driver.cacheGet(cacheKeys.node(this.id).defaultVolume);\n\t}\n\n\tpublic set defaultVolume(value: number | undefined) {\n\t\tif (value != undefined && (value < 0 || value > 100)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The default volume must be a number between 0 and 100!`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\t\tthis.driver.cacheSet(cacheKeys.node(this.id).defaultVolume, value);\n\t}\n\n\t/**\n\t * The default transition duration to be used for transitions like dimming lights or activating scenes.\n\t * Can be overridden by command-specific options.\n\t */\n\tpublic get defaultTransitionDuration(): string | undefined {\n\t\treturn this.driver.cacheGet(\n\t\t\tcacheKeys.node(this.id).defaultTransitionDuration,\n\t\t);\n\t}\n\n\tpublic set defaultTransitionDuration(value: string | Duration | undefined) {\n\t\t// Normalize to strings\n\t\tif (typeof value === \"string\") value = Duration.from(value);\n\t\tif (Duration.isDuration(value)) value = value.toString();\n\n\t\tthis.driver.cacheSet(\n\t\t\tcacheKeys.node(this.id).defaultTransitionDuration,\n\t\t\tvalue,\n\t\t);\n\t}\n\n\t/**\n\t * @internal\n\t * The hash of the device config that was applied during the last interview.\n\t */\n\tpublic get cachedDeviceConfigHash(): Uint8Array | undefined {\n\t\treturn this.driver.cacheGet(cacheKeys.node(this.id).deviceConfigHash);\n\t}\n\n\tprivate set cachedDeviceConfigHash(value: Uint8Array | undefined) {\n\t\tthis.driver.cacheSet(cacheKeys.node(this.id).deviceConfigHash, value);\n\t}\n\n\tprivate _currentDeviceConfigHash: Uint8Array | undefined;\n\t/**\n\t * @internal\n\t * The hash of the currently used device config\n\t */\n\tpublic get currentDeviceConfigHash(): Uint8Array | undefined {\n\t\treturn this._currentDeviceConfigHash;\n\t}\n\n\t/** Returns a list of all value names that are defined on all endpoints of this node */\n\tpublic getDefinedValueIDs(): TranslatedValueID[] {\n\t\treturn nodeUtils.getDefinedValueIDs(this.driver, this);\n\t}\n\n\t/**\n\t * Updates a value for a given property of a given CommandClass on the node.\n\t * This will communicate with the node!\n\t */\n\tpublic async setValue(\n\t\tvalueId: ValueID,\n\t\tvalue: unknown,\n\t\toptions?: SetValueAPIOptions,\n\t): Promise<SetValueResult> {\n\t\t// Ensure we're dealing with a valid value ID, with no extra properties\n\t\tvalueId = normalizeValueID(valueId);\n\n\t\tconst loglevel = this.driver.getLogConfig().level;\n\n\t\t// Try to retrieve the corresponding CC API\n\t\ttry {\n\t\t\t// Access the CC API by name\n\t\t\tconst endpointInstance = this.getEndpoint(valueId.endpoint || 0);\n\t\t\tif (!endpointInstance) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: SetValueStatus.EndpointNotFound,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Endpoint ${valueId.endpoint} does not exist on Node ${this.id}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tlet api = (endpointInstance.commandClasses as any)[\n\t\t\t\tvalueId.commandClass\n\t\t\t] as CCAPI;\n\t\t\t// Check if the setValue method is implemented\n\t\t\tif (!api.setValue) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: SetValueStatus.NotImplemented,\n\t\t\t\t\tmessage: `The ${\n\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\tvalueId.commandClass,\n\t\t\t\t\t\t)\n\t\t\t\t\t} CC does not support setting values`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (loglevel === \"silly\") {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: valueId.endpoint,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`[setValue] calling SET_VALUE API ${api.constructor.name}:\n property: ${valueId.property}\n property key: ${valueId.propertyKey}\n optimistic: ${api.isSetValueOptimistic(valueId)}`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Merge the provided value change options with the defaults\n\t\t\toptions ??= {};\n\t\t\toptions.transitionDuration ??= this.defaultTransitionDuration;\n\t\t\toptions.volume ??= this.defaultVolume;\n\n\t\t\tconst valueIdProps: ValueIDProperties = {\n\t\t\t\tproperty: valueId.property,\n\t\t\t\tpropertyKey: valueId.propertyKey,\n\t\t\t};\n\n\t\t\tconst hooks = api.setValueHooks?.(valueIdProps, value, options);\n\n\t\t\tif (hooks?.supervisionDelayedUpdates) {\n\t\t\t\tapi = api.withOptions({\n\t\t\t\t\trequestStatusUpdates: true,\n\t\t\t\t\tonUpdate: async (update) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (update.status === SupervisionStatus.Success) {\n\t\t\t\t\t\t\t\tawait hooks.supervisionOnSuccess();\n\t\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\t\tupdate.status === SupervisionStatus.Fail\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tawait hooks.supervisionOnFailure();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// TODO: Log error?\n\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// If the caller wants progress updates, they shall have them\n\t\t\tif (typeof options.onProgress === \"function\") {\n\t\t\t\tapi = api.withOptions({\n\t\t\t\t\tonProgress: options.onProgress,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// And call it\n\t\t\tconst result = await api.setValue!.call(\n\t\t\t\tapi,\n\t\t\t\tvalueIdProps,\n\t\t\t\tvalue,\n\t\t\t\toptions,\n\t\t\t);\n\n\t\t\tif (loglevel === \"silly\") {\n\t\t\t\tlet message =\n\t\t\t\t\t`[setValue] result of SET_VALUE API call for ${api.constructor.name}:`;\n\t\t\t\tif (result) {\n\t\t\t\t\tif (isSupervisionResult(result)) {\n\t\t\t\t\t\tmessage += ` (SupervisionResult)\n status: ${getEnumMemberName(SupervisionStatus, result.status)}`;\n\t\t\t\t\t\tif (result.remainingDuration) {\n\t\t\t\t\t\t\tmessage += `\n duration: ${result.remainingDuration.toString()}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmessage += \" (other) \"\n\t\t\t\t\t\t\t+ JSON.stringify(result, null, 2);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tmessage += \" undefined\";\n\t\t\t\t}\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: valueId.endpoint,\n\t\t\t\t\tmessage,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Remember the new value for the value we just set, if...\n\t\t\t// ... the call did not throw (assume that the call was successful)\n\t\t\t// ... the call was supervised and successful\n\t\t\tif (\n\t\t\t\tapi.isSetValueOptimistic(valueId)\n\t\t\t\t&& isUnsupervisedOrSucceeded(result)\n\t\t\t) {\n\t\t\t\tconst emitEvent = !!result\n\t\t\t\t\t|| !!this.driver.options.emitValueUpdateAfterSetValue;\n\n\t\t\t\tif (loglevel === \"silly\") {\n\t\t\t\t\tconst message = emitEvent\n\t\t\t\t\t\t? \"updating value with event\"\n\t\t\t\t\t\t: \"updating value without event\";\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: valueId.endpoint,\n\t\t\t\t\t\tmessage: `[setValue] ${message}`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst options: SetValueOptions = {};\n\t\t\t\t// We need to emit an event if applications opted in, or if this was a supervised call\n\t\t\t\t// because in this case there won't be a verification query which would result in an update\n\t\t\t\tif (emitEvent) {\n\t\t\t\t\toptions.source = \"driver\";\n\t\t\t\t} else {\n\t\t\t\t\toptions.noEvent = true;\n\t\t\t\t}\n\t\t\t\t// Only update the timestamp of the value for successful supervised commands. Otherwise we don't know\n\t\t\t\t// if the command was actually executed. If it wasn't, we'd have a wrong timestamp.\n\t\t\t\toptions.updateTimestamp = supervisedCommandSucceeded(result);\n\n\t\t\t\tthis._valueDB.setValue(valueId, value, options);\n\t\t\t} else if (loglevel === \"silly\") {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: valueId.endpoint,\n\t\t\t\t\tmessage: `[setValue] not updating value`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Depending on the settings of the SET_VALUE implementation, we may have to\n\t\t\t// optimistically update a different value and/or verify the changes\n\t\t\tif (hooks) {\n\t\t\t\tconst supervisedAndSuccessful = isSupervisionResult(result)\n\t\t\t\t\t&& result.status === SupervisionStatus.Success;\n\n\t\t\t\tconst shouldUpdateOptimistically =\n\t\t\t\t\tapi.isSetValueOptimistic(valueId)\n\t\t\t\t\t// For successful supervised commands, we know that an optimistic update is ok\n\t\t\t\t\t&& (supervisedAndSuccessful\n\t\t\t\t\t\t// For unsupervised commands that did not fail, we let the applciation decide whether\n\t\t\t\t\t\t// to update related value optimistically\n\t\t\t\t\t\t|| (!this.driver.options.disableOptimisticValueUpdate\n\t\t\t\t\t\t\t&& result == undefined));\n\n\t\t\t\t// The actual API implementation handles additional optimistic updates\n\t\t\t\tif (shouldUpdateOptimistically) {\n\t\t\t\t\thooks.optimisticallyUpdateRelatedValues?.(\n\t\t\t\t\t\tsupervisedAndSuccessful,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Verify the current value after a delay, unless...\n\t\t\t\t// ...the command was supervised and successful\n\t\t\t\t// ...and the CC API decides not to verify anyways\n\t\t\t\tif (\n\t\t\t\t\t!supervisedCommandSucceeded(result)\n\t\t\t\t\t|| hooks.forceVerifyChanges?.()\n\t\t\t\t) {\n\t\t\t\t\t// Let the CC API implementation handle the verification.\n\t\t\t\t\t// It may still decide not to do it.\n\t\t\t\t\tawait hooks.verifyChanges?.();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn supervisionResultToSetValueResult(result);\n\t\t} catch (e) {\n\t\t\t// Define which errors during setValue are expected and won't throw an error\n\t\t\tif (isZWaveError(e)) {\n\t\t\t\tlet result: SetValueResult | undefined;\n\t\t\t\tswitch (e.code) {\n\t\t\t\t\t// This CC or API is not implemented\n\t\t\t\t\tcase ZWaveErrorCodes.CC_NotImplemented:\n\t\t\t\t\tcase ZWaveErrorCodes.CC_NoAPI:\n\t\t\t\t\t\tresult = {\n\t\t\t\t\t\t\tstatus: SetValueStatus.NotImplemented,\n\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t// A user tried to set an invalid value\n\t\t\t\t\tcase ZWaveErrorCodes.Argument_Invalid:\n\t\t\t\t\t\tresult = {\n\t\t\t\t\t\t\tstatus: SetValueStatus.InvalidValue,\n\t\t\t\t\t\t\tmessage: e.message,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (loglevel === \"silly\") {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: valueId.endpoint,\n\t\t\t\t\t\tmessage: `[setValue] raised ZWaveError (${\n\t\t\t\t\t\t\t!!result ? \"handled\" : \"not handled\"\n\t\t\t\t\t\t}, code ${\n\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\tZWaveErrorCodes,\n\t\t\t\t\t\t\t\te.code,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}): ${e.message}`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (result) return result;\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Requests a value for a given property of a given CommandClass by polling the node.\n\t * **Warning:** Some value IDs share a command, so make sure not to blindly call this for every property\n\t */\n\tpublic pollValue<T = unknown>(\n\t\tvalueId: ValueID,\n\t\tsendCommandOptions: SendCommandOptions = {},\n\t): Promise<MaybeNotKnown<T>> {\n\t\t// Ensure we're dealing with a valid value ID, with no extra properties\n\t\tvalueId = normalizeValueID(valueId);\n\n\t\t// Try to retrieve the corresponding CC API\n\t\tconst endpointInstance = this.getEndpoint(valueId.endpoint || 0);\n\t\tif (!endpointInstance) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Endpoint ${valueId.endpoint} does not exist on Node ${this.id}`,\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tconst api = (\n\t\t\t(endpointInstance.commandClasses as any)[\n\t\t\t\tvalueId.commandClass\n\t\t\t] as CCAPI\n\t\t).withOptions({\n\t\t\t// We do not want to delay more important communication by polling, so give it\n\t\t\t// the lowest priority and don't retry unless overwritten by the options\n\t\t\tmaxSendAttempts: 1,\n\t\t\tpriority: MessagePriority.Poll,\n\t\t\t...sendCommandOptions,\n\t\t});\n\n\t\t// Check if the pollValue method is implemented\n\t\tif (!api.pollValue) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`The pollValue API is not implemented for CC ${\n\t\t\t\t\tgetCCName(\n\t\t\t\t\t\tvalueId.commandClass,\n\t\t\t\t\t)\n\t\t\t\t}!`,\n\t\t\t\tZWaveErrorCodes.CC_NoAPI,\n\t\t\t);\n\t\t}\n\n\t\t// And call it\n\t\treturn (api.pollValue as PollValueImplementation<T>).call(api, {\n\t\t\tproperty: valueId.property,\n\t\t\tpropertyKey: valueId.propertyKey,\n\t\t});\n\t}\n\n\tprivate _interviewAttempts: number = 0;\n\t/** How many attempts to interview this node have already been made */\n\tpublic get interviewAttempts(): number {\n\t\treturn this._interviewAttempts;\n\t}\n\n\tprivate _hasEmittedNoS2NetworkKeyError: boolean = false;\n\tprivate _hasEmittedNoS0NetworkKeyError: boolean = false;\n\n\t/**\n\t * Starts or resumes a deferred initial interview of this node.\n\t *\n\t * **WARNING:** This is only allowed when the initial interview was deferred using the\n\t * `interview.disableOnNodeAdded` option. Otherwise, this method will throw an error.\n\t *\n\t * **NOTE:** It is advised to NOT await this method as it can take a very long time (minutes to hours)!\n\t */\n\tpublic async interview(): Promise<void> {\n\t\t// The initial interview of the controller node is always done\n\t\t// and cannot be deferred.\n\t\tif (this.isControllerNode) return;\n\n\t\tif (!this.driver.options.interview?.disableOnNodeAdded) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Calling ZWaveNode.interview() is not allowed because automatic node interviews are enabled. Wait for the driver to interview the node or use ZWaveNode.refreshInfo() to re-interview a node.`,\n\t\t\t\tZWaveErrorCodes.Driver_FeatureDisabled,\n\t\t\t);\n\t\t}\n\n\t\treturn this.driver.interviewNodeInternal(this);\n\t}\n\n\tprivate _refreshInfoPending: boolean = false;\n\n\t/**\n\t * Resets all information about this node and forces a fresh interview.\n\t * **Note:** This does nothing for the controller node.\n\t *\n\t * **WARNING:** Take care NOT to call this method when the node is already being interviewed.\n\t * Otherwise the node information may become inconsistent.\n\t */\n\tpublic async refreshInfo(options: RefreshInfoOptions = {}): Promise<void> {\n\t\t// It does not make sense to re-interview the controller. All important information is queried\n\t\t// directly via the serial API\n\t\tif (this.isControllerNode) return;\n\n\t\t// The driver does deduplicate re-interview requests, but only at the end of this method.\n\t\t// Without blocking here, many re-interview tasks for sleeping nodes may be queued, leading to parallel interviews\n\t\tif (this._refreshInfoPending) return;\n\t\tthis._refreshInfoPending = true;\n\n\t\tconst { resetSecurityClasses = false, waitForWakeup = true } = options;\n\t\t// Unless desired, don't forget the information about sleeping nodes immediately, so they continue to function\n\t\tlet didWakeUp = false;\n\t\tif (\n\t\t\twaitForWakeup\n\t\t\t&& this.canSleep\n\t\t\t&& this.supportsCC(CommandClasses[\"Wake Up\"])\n\t\t) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"Re-interview scheduled, waiting for node to wake up...\",\n\t\t\t);\n\t\t\tdidWakeUp = await this.waitForWakeup()\n\t\t\t\t.then(() => true)\n\t\t\t\t.catch(() => false);\n\t\t}\n\n\t\t// preserve the node name and location, since they might not be stored on the node\n\t\tconst name = this.name;\n\t\tconst location = this.location;\n\n\t\t// Preserve user codes if they aren't queried during the interview\n\t\tconst preservedValues: (ValueID & { value: unknown })[] = [];\n\t\tconst preservedMetadata: (ValueID & { metadata: ValueMetadata })[] = [];\n\t\tif (\n\t\t\tthis.supportsCC(CommandClasses[\"User Code\"])\n\t\t\t&& !this.driver.options.interview.queryAllUserCodes\n\t\t) {\n\t\t\tconst mustBackup = (v: ValueID) =>\n\t\t\t\tUserCodeCCValues.userCode.is(v)\n\t\t\t\t|| UserCodeCCValues.userIdStatus.is(v)\n\t\t\t\t|| UserCodeCCValues.userCodeChecksum.is(v);\n\n\t\t\tconst values = this.valueDB\n\t\t\t\t.getValues(CommandClasses[\"User Code\"])\n\t\t\t\t.filter(mustBackup);\n\t\t\tpreservedValues.push(...values);\n\n\t\t\tconst meta = this.valueDB\n\t\t\t\t.getAllMetadata(CommandClasses[\"User Code\"])\n\t\t\t\t.filter(mustBackup);\n\t\t\tpreservedMetadata.push(...meta);\n\t\t}\n\n\t\t// Force a new detection of security classes if desired\n\t\tif (resetSecurityClasses) this.securityClasses.clear();\n\n\t\tthis._interviewAttempts = 0;\n\t\tthis.interviewStage = InterviewStage.None;\n\t\tthis.ready = false;\n\t\tthis.deviceClass = undefined;\n\t\tthis.isListening = undefined;\n\t\tthis.isFrequentListening = undefined;\n\t\tthis.isRouting = undefined;\n\t\tthis.supportedDataRates = undefined;\n\t\tthis.protocolVersion = undefined;\n\t\tthis.nodeType = undefined;\n\t\tthis.supportsSecurity = undefined;\n\t\tthis.supportsBeaming = undefined;\n\t\tthis._deviceConfig = undefined;\n\t\tthis._currentDeviceConfigHash = undefined;\n\t\tthis.cachedDeviceConfigHash = undefined;\n\t\tthis._hasEmittedNoS0NetworkKeyError = false;\n\t\tthis._hasEmittedNoS2NetworkKeyError = false;\n\t\tfor (const ep of this.getAllEndpoints()) {\n\t\t\tep[\"reset\"]();\n\t\t}\n\t\tthis._valueDB.clear({ noEvent: true });\n\t\tthis._endpointInstances.clear();\n\t\tsuper.reset();\n\n\t\t// Restart all state machines\n\t\tthis.readyMachine.restart();\n\t\tthis.statusMachine.restart();\n\n\t\t// Remove queued polls that would interfere with the interview\n\t\tthis.cancelAllScheduledPolls();\n\n\t\t// Restore the previously saved name/location\n\t\tif (name != undefined) this.name = name;\n\t\tif (location != undefined) this.location = location;\n\n\t\t// And preserved values/metadata\n\t\tfor (const { value, ...valueId } of preservedValues) {\n\t\t\tthis.valueDB.setValue(valueId, value, { noEvent: true });\n\t\t}\n\t\tfor (const { metadata, ...valueId } of preservedMetadata) {\n\t\t\tthis.valueDB.setMetadata(valueId, metadata, { noEvent: true });\n\t\t}\n\n\t\t// Don't keep the node awake after the interview\n\t\tthis.keepAwake = false;\n\n\t\t// If we did wait for the wakeup, mark the node as awake again so it does not\n\t\t// get considered asleep after querying protocol info.\n\t\tif (didWakeUp) this.markAsAwake();\n\n\t\tvoid this.driver.interviewNodeInternal(this);\n\t\tthis._refreshInfoPending = false;\n\t}\n\n\t/**\n\t * @internal\n\t * Interviews this node. Returns true when it succeeded, false otherwise\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 interviewInternal(): Promise<boolean> {\n\t\tif (this.interviewStage === InterviewStage.Complete) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`skipping interview because it is already completed`,\n\t\t\t);\n\t\t\treturn true;\n\t\t} else {\n\t\t\tthis.driver.controllerLog.interviewStart(this);\n\t\t}\n\n\t\t// Remember that we tried to interview this node\n\t\tthis._interviewAttempts++;\n\n\t\t// Wrapper around interview methods to return false in case of a communication error\n\t\t// This way the single methods don't all need to have the same error handler\n\t\tconst tryInterviewStage = async (\n\t\t\tmethod: () => Promise<void>,\n\t\t): Promise<boolean> => {\n\t\t\ttry {\n\t\t\t\tawait method();\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tif (isTransmissionError(e)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t};\n\n\t\t// The interview is done in several stages. At each point, the interview process might be aborted\n\t\t// due to a stage failing. The reached stage is saved, so we can continue it later without\n\t\t// repeating stages unnecessarily\n\n\t\tif (this.interviewStage === InterviewStage.None) {\n\t\t\t// do a full interview starting with the protocol info\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`new node, doing a full interview...`,\n\t\t\t);\n\t\t\tthis.emit(\"interview started\", this);\n\t\t\tawait this.queryProtocolInfo();\n\t\t}\n\n\t\tif (!this.isControllerNode) {\n\t\t\tif (\n\t\t\t\t(this.isListening || this.isFrequentListening)\n\t\t\t\t&& this.status !== NodeStatus.Alive\n\t\t\t) {\n\t\t\t\t// Ping non-sleeping nodes to determine their status\n\t\t\t\tif (!await this.ping()) {\n\t\t\t\t\t// Not alive, abort the interview\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.interviewStage === InterviewStage.ProtocolInfo) {\n\t\t\t\tif (\n\t\t\t\t\t!(await tryInterviewStage(() => this.interviewNodeInfo()))\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// At this point the basic interview of new nodes is done. Start here when re-interviewing known nodes\n\t\t\t// to get updated information about command classes\n\t\t\tif (this.interviewStage === InterviewStage.NodeInfo) {\n\t\t\t\t// Only advance the interview if it was completed, otherwise abort\n\t\t\t\tif (await this.interviewCCs()) {\n\t\t\t\t\tthis.setInterviewStage(InterviewStage.CommandClasses);\n\t\t\t\t} else {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\t(this.isControllerNode\n\t\t\t\t&& this.interviewStage === InterviewStage.ProtocolInfo)\n\t\t\t|| (!this.isControllerNode\n\t\t\t\t&& this.interviewStage === InterviewStage.CommandClasses)\n\t\t) {\n\t\t\t// Load a config file for this node if it exists and overwrite the previously reported information\n\t\t\tawait this.overwriteConfig();\n\t\t}\n\n\t\t// Remember the state of the device config that is used for this node\n\t\tthis.cachedDeviceConfigHash = await this._deviceConfig?.getHash();\n\n\t\tthis.setInterviewStage(InterviewStage.Complete);\n\t\tthis.readyMachine.send(\"INTERVIEW_DONE\");\n\n\t\t// Tell listeners that the interview is completed\n\t\t// The driver will then send this node to sleep\n\t\tthis.emit(\"interview completed\", this);\n\t\treturn true;\n\t}\n\n\t/** Updates this node's interview stage and saves to cache when appropriate */\n\tprivate setInterviewStage(completedStage: InterviewStage): void {\n\t\tthis.interviewStage = completedStage;\n\t\tthis.emit(\n\t\t\t\"interview stage completed\",\n\t\t\tthis,\n\t\t\tgetEnumMemberName(InterviewStage, completedStage),\n\t\t);\n\t\tthis.driver.controllerLog.interviewStage(this);\n\t}\n\n\t/** Step #1 of the node interview */\n\tprotected async queryProtocolInfo(): Promise<void> {\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: \"querying protocol info...\",\n\t\t\tdirection: \"outbound\",\n\t\t});\n\t\t// The GetNodeProtocolInfoRequest needs to know the node ID to distinguish\n\t\t// between ZWLR and ZW classic. We store it on the driver's context, so it\n\t\t// can be retrieved when needed.\n\t\tthis.driver.requestStorage.set(FunctionType.GetNodeProtocolInfo, {\n\t\t\tnodeId: this.id,\n\t\t});\n\t\tconst resp = await this.driver.sendMessage<GetNodeProtocolInfoResponse>(\n\t\t\tnew GetNodeProtocolInfoRequest({\n\t\t\t\trequestedNodeId: this.id,\n\t\t\t}),\n\t\t);\n\t\tthis.isListening = resp.isListening;\n\t\tthis.isFrequentListening = resp.isFrequentListening;\n\t\tthis.isRouting = resp.isRouting;\n\t\tthis.supportedDataRates = resp.supportedDataRates;\n\t\tthis.protocolVersion = resp.protocolVersion;\n\t\tthis.nodeType = resp.nodeType;\n\t\tthis.supportsSecurity = resp.supportsSecurity;\n\t\tthis.supportsBeaming = resp.supportsBeaming;\n\n\t\tthis.deviceClass = new DeviceClass(\n\t\t\tresp.basicDeviceClass,\n\t\t\tresp.genericDeviceClass,\n\t\t\tresp.specificDeviceClass,\n\t\t);\n\n\t\tconst logMessage = `received response for protocol info:\nbasic device class: ${\n\t\t\tgetEnumMemberName(BasicDeviceClass, this.deviceClass.basic)\n\t\t}\ngeneric device class: ${this.deviceClass.generic.label}\nspecific device class: ${this.deviceClass.specific.label}\nnode type: ${getEnumMemberName(NodeType, this.nodeType)}\nis always listening: ${this.isListening}\nis frequent listening: ${this.isFrequentListening}\ncan route messages: ${this.isRouting}\nsupports security: ${this.supportsSecurity}\nsupports beaming: ${this.supportsBeaming}\nmaximum data rate: ${this.maxDataRate} kbps\nprotocol version: ${this.protocolVersion}`;\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: logMessage,\n\t\t\tdirection: \"inbound\",\n\t\t});\n\n\t\t// Assume that sleeping nodes start asleep (unless we know it is awake)\n\t\tif (this.canSleep) {\n\t\t\tif (this.status === NodeStatus.Alive) {\n\t\t\t\t// If it was just included and is currently communicating with us,\n\t\t\t\t// then we didn't know yet that it can sleep. So we need to switch from alive/dead to awake/asleep\n\t\t\t\tthis.markAsAwake();\n\t\t\t} else if (this.status !== NodeStatus.Awake) {\n\t\t\t\tthis.markAsAsleep();\n\t\t\t}\n\t\t}\n\n\t\tthis.setInterviewStage(InterviewStage.ProtocolInfo);\n\t}\n\n\t/** Node interview: pings the node to see if it responds */\n\tpublic async ping(): Promise<boolean> {\n\t\tif (this.isControllerNode) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"is the controller node, cannot ping\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: \"pinging the node...\",\n\t\t\tdirection: \"outbound\",\n\t\t});\n\n\t\ttry {\n\t\t\tawait this.commandClasses[\"No Operation\"].send();\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: \"ping successful\",\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t\treturn true;\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`ping failed: ${getErrorMessage(e)}`,\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Step #5 of the node interview\n\t * Request node info\n\t */\n\tprotected async interviewNodeInfo(): Promise<void> {\n\t\tif (this.isControllerNode) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"is the controller node, cannot query node info\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// If we incorrectly assumed a sleeping node to be awake, this step will fail.\n\t\t// In order to fail the interview, we retry here\n\t\tfor (let attempts = 1; attempts <= 2; attempts++) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: \"querying node info...\",\n\t\t\t\tdirection: \"outbound\",\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tconst nodeInfo = await this.requestNodeInfo();\n\t\t\t\tconst logLines: string[] = [\n\t\t\t\t\t\"node info received\",\n\t\t\t\t\t\"supported CCs:\",\n\t\t\t\t];\n\t\t\t\tfor (const cc of nodeInfo.supportedCCs) {\n\t\t\t\t\tlogLines.push(`\u00B7 ${getCCName(cc)}`);\n\t\t\t\t}\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: logLines.join(\"\\n\"),\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\t\t\t\tthis.updateNodeInfo(nodeInfo);\n\t\t\t\tbreak;\n\t\t\t} catch (e) {\n\t\t\t\tif (isZWaveError(e)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tattempts === 1\n\t\t\t\t\t\t&& this.canSleep\n\t\t\t\t\t\t&& this.status !== NodeStatus.Asleep\n\t\t\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t\t`Querying the node info failed, the node is probably asleep. Retrying after wakeup...`,\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// We assumed the node to be awake, but it is not.\n\t\t\t\t\t\tthis.markAsAsleep();\n\t\t\t\t\t\t// Retry the query when the node wakes up\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (\n\t\t\t\t\t\te.code === ZWaveErrorCodes.Controller_ResponseNOK\n\t\t\t\t\t\t|| e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t\t`Querying the node info failed`,\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.setInterviewStage(InterviewStage.NodeInfo);\n\t}\n\n\tpublic async requestNodeInfo(): Promise<NodeUpdatePayload> {\n\t\tconst resp = await this.driver.sendMessage<\n\t\t\tRequestNodeInfoResponse | ApplicationUpdateRequest\n\t\t>(new RequestNodeInfoRequest({ nodeId: this.id }));\n\t\tif (resp instanceof RequestNodeInfoResponse && !resp.wasSent) {\n\t\t\t// TODO: handle this in SendThreadMachine\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Querying the node info failed`,\n\t\t\t\tZWaveErrorCodes.Controller_ResponseNOK,\n\t\t\t);\n\t\t} else if (\n\t\t\tresp instanceof ApplicationUpdateRequestNodeInfoRequestFailed\n\t\t) {\n\t\t\t// TODO: handle this in SendThreadMachine\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Querying the node info failed`,\n\t\t\t\tZWaveErrorCodes.Controller_CallbackNOK,\n\t\t\t);\n\t\t} else if (resp instanceof ApplicationUpdateRequestNodeInfoReceived) {\n\t\t\tconst logLines: string[] = [\"node info received\", \"supported CCs:\"];\n\t\t\tfor (const cc of resp.nodeInformation.supportedCCs) {\n\t\t\t\tlogLines.push(`\u00B7 ${getCCName(cc)}`);\n\t\t\t}\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: logLines.join(\"\\n\"),\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t\treturn resp.nodeInformation;\n\t\t}\n\t\tthrow new ZWaveError(\n\t\t\t`Received unexpected response to RequestNodeInfoRequest`,\n\t\t\tZWaveErrorCodes.Controller_CommandError,\n\t\t);\n\t}\n\n\t/**\n\t * Loads the device configuration for this node from a config file\n\t */\n\tprotected async loadDeviceConfig(): Promise<void> {\n\t\t// But the configuration definitions might change\n\t\tif (\n\t\t\tthis.manufacturerId != undefined\n\t\t\t&& this.productType != undefined\n\t\t\t&& this.productId != undefined\n\t\t) {\n\t\t\t// Try to load the config file\n\t\t\tthis._deviceConfig = await this.driver.configManager.lookupDevice(\n\t\t\t\tthis.manufacturerId,\n\t\t\t\tthis.productType,\n\t\t\t\tthis.productId,\n\t\t\t\tthis.firmwareVersion,\n\t\t\t);\n\t\t\tif (this._deviceConfig) {\n\t\t\t\t// Also remember the current hash of the device config\n\t\t\t\tif (this.cachedDeviceConfigHash?.length === 16) {\n\t\t\t\t\t// legacy hash using MD5\n\t\t\t\t\tthis._currentDeviceConfigHash = await this._deviceConfig\n\t\t\t\t\t\t.getHash(\"md5\");\n\t\t\t\t} else {\n\t\t\t\t\tthis._currentDeviceConfigHash = await this._deviceConfig\n\t\t\t\t\t\t.getHash();\n\t\t\t\t}\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t`${\n\t\t\t\t\t\tthis._deviceConfig.isEmbedded\n\t\t\t\t\t\t\t? \"Embedded\"\n\t\t\t\t\t\t\t: \"User-provided\"\n\t\t\t\t\t} device config loaded`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t\"No device config found\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Step #? of the node interview */\n\tprotected async interviewCCs(): Promise<boolean> {\n\t\tif (this.isControllerNode) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"is the controller node, cannot interview CCs\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\tconst securityManager2 = this.driver.getSecurityManager2(this.id);\n\n\t\t/**\n\t\t * @param force When this is `true`, the interview will be attempted even when the CC is not supported by the endpoint.\n\t\t */\n\t\tconst interviewEndpoint = async (\n\t\t\tendpoint: Endpoint,\n\t\t\tcc: CommandClasses,\n\t\t\tforce: boolean = false,\n\t\t): Promise<\"continue\" | false | void> => {\n\t\t\tlet instance: CommandClass;\n\t\t\ttry {\n\t\t\t\tif (force) {\n\t\t\t\t\tinstance = CommandClass.createInstanceUnchecked(\n\t\t\t\t\t\tendpoint,\n\t\t\t\t\t\tcc,\n\t\t\t\t\t)!;\n\t\t\t\t} else {\n\t\t\t\t\tinstance = endpoint.createCCInstance(cc)!;\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (\n\t\t\t\t\tisZWaveError(e)\n\t\t\t\t\t&& e.code === ZWaveErrorCodes.CC_NotSupported\n\t\t\t\t) {\n\t\t\t\t\t// The CC is no longer supported. This can happen if the node tells us\n\t\t\t\t\t// something different in the Version interview than it did in its NIF\n\t\t\t\t\treturn \"continue\";\n\t\t\t\t}\n\t\t\t\t// we want to pass all other errors through\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tendpoint.isCCSecure(cc)\n\t\t\t\t&& !this.driver.securityManager\n\t\t\t\t&& !securityManager2\n\t\t\t) {\n\t\t\t\t// The CC is only supported securely, but the network key is not set up\n\t\t\t\t// Skip the CC\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t`Skipping interview for secure CC ${\n\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\tcc,\n\t\t\t\t\t\t)\n\t\t\t\t\t} because no network key is configured!`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn \"continue\";\n\t\t\t}\n\n\t\t\t// Skip this step if the CC was already interviewed\n\t\t\tif (instance.isInterviewComplete(this.driver)) return \"continue\";\n\n\t\t\ttry {\n\t\t\t\tawait instance.interview(this.driver);\n\t\t\t} catch (e) {\n\t\t\t\tif (isTransmissionError(e)) {\n\t\t\t\t\t// We had a CAN or timeout during the interview\n\t\t\t\t\t// or the node is presumed dead. Abort the process\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t// we want to pass all other errors through\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t};\n\n\t\t// Always interview Security first because it changes the interview order\n\t\tif (this.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\t// Security S2 is always supported *securely*\n\t\t\tthis.addCC(CommandClasses[\"Security 2\"], { secure: true });\n\n\t\t\t// Query supported CCs unless we know for sure that the node wasn't assigned a S2 security class\n\t\t\tconst securityClass = this.getHighestSecurityClass();\n\t\t\tif (\n\t\t\t\tsecurityClass == undefined\n\t\t\t\t|| securityClassIsS2(securityClass)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t\"Root device interview: Security S2\",\n\t\t\t\t\t\"silly\",\n\t\t\t\t);\n\n\t\t\t\tif (!securityManager2) {\n\t\t\t\t\tif (!this._hasEmittedNoS2NetworkKeyError) {\n\t\t\t\t\t\t// Cannot interview a secure device securely without a network key\n\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t`supports Security S2, but no S2 network keys were configured. The interview might not include all functionality.`;\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t\terrorMessage,\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.driver.emit(\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\t\t`Node ${\n\t\t\t\t\t\t\t\t\tthis.id.toString().padStart(\n\t\t\t\t\t\t\t\t\t\t3,\n\t\t\t\t\t\t\t\t\t\t\"0\",\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t} ${errorMessage}`,\n\t\t\t\t\t\t\t\tZWaveErrorCodes\n\t\t\t\t\t\t\t\t\t.Controller_NodeInsecureCommunication,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis._hasEmittedNoS2NetworkKeyError = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t);\n\t\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// If there is any doubt about granted S2 security classes, we now know they are not granted\n\t\t\tfor (\n\t\t\t\tconst secClass of [\n\t\t\t\t\tSecurityClass.S2_AccessControl,\n\t\t\t\t\tSecurityClass.S2_Authenticated,\n\t\t\t\t\tSecurityClass.S2_Unauthenticated,\n\t\t\t\t] as const\n\t\t\t) {\n\t\t\t\tif (this.hasSecurityClass(secClass) === NOT_KNOWN) {\n\t\t\t\t\tthis.securityClasses.set(secClass, false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.supportsCC(CommandClasses.Security)) {\n\t\t\t// Security S0 is always supported *securely*\n\t\t\tthis.addCC(CommandClasses.Security, { secure: true });\n\n\t\t\t// Query supported CCs unless we know for sure that the node wasn't assigned the S0 security class\n\t\t\tif (this.hasSecurityClass(SecurityClass.S0_Legacy) !== false) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t\"Root device interview: Security S0\",\n\t\t\t\t\t\"silly\",\n\t\t\t\t);\n\n\t\t\t\tif (!this.driver.securityManager) {\n\t\t\t\t\tif (!this._hasEmittedNoS0NetworkKeyError) {\n\t\t\t\t\t\t// Cannot interview a secure device securely without a network key\n\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t`supports Security S0, but the S0 network key was not configured. The interview might not include all functionality.`;\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t\terrorMessage,\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.driver.emit(\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\tnew ZWaveError(\n\t\t\t\t\t\t\t\t`Node ${\n\t\t\t\t\t\t\t\t\tthis.id.toString().padStart(\n\t\t\t\t\t\t\t\t\t\t3,\n\t\t\t\t\t\t\t\t\t\t\"0\",\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t} ${errorMessage}`,\n\t\t\t\t\t\t\t\tZWaveErrorCodes\n\t\t\t\t\t\t\t\t\t.Controller_NodeInsecureCommunication,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis._hasEmittedNoS0NetworkKeyError = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tCommandClasses.Security,\n\t\t\t\t\t);\n\t\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (this.hasSecurityClass(SecurityClass.S0_Legacy) === NOT_KNOWN) {\n\t\t\t\t// Remember that this node hasn't been granted the S0 security class\n\t\t\t\tthis.securityClasses.set(SecurityClass.S0_Legacy, false);\n\t\t\t}\n\t\t}\n\n\t\t// Manufacturer Specific and Version CC need to be handled before the other CCs because they are needed to\n\t\t// identify the device and apply device configurations\n\t\tif (this.supportsCC(CommandClasses[\"Manufacturer Specific\"])) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"Root device interview: Manufacturer Specific\",\n\t\t\t\t\"silly\",\n\t\t\t);\n\n\t\t\tconst action = await interviewEndpoint(\n\t\t\t\tthis,\n\t\t\t\tCommandClasses[\"Manufacturer Specific\"],\n\t\t\t);\n\t\t\tif (typeof action === \"boolean\") return action;\n\t\t}\n\n\t\tif (this.supportsCC(CommandClasses.Version)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"Root device interview: Version\",\n\t\t\t\t\"silly\",\n\t\t\t);\n\n\t\t\tconst action = await interviewEndpoint(\n\t\t\t\tthis,\n\t\t\t\tCommandClasses.Version,\n\t\t\t);\n\t\t\tif (typeof action === \"boolean\") return action;\n\n\t\t\t// After the version CC interview of the root endpoint, we have enough info to load the correct device config file\n\t\t\tawait this.loadDeviceConfig();\n\n\t\t\t// At this point we may need to make some changes to the CCs the device reports\n\t\t\tthis.applyCommandClassesCompatFlag();\n\t\t} else {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"Version CC is not supported. Using the highest implemented version for each CC\",\n\t\t\t\t\"debug\",\n\t\t\t);\n\n\t\t\tfor (const [ccId, info] of this.getCCs()) {\n\t\t\t\tif (\n\t\t\t\t\tinfo.isSupported\n\t\t\t\t\t// The support status of Basic CC is not known yet at this point\n\t\t\t\t\t|| ccId === CommandClasses.Basic\n\t\t\t\t) {\n\t\t\t\t\tthis.addCC(ccId, { version: getImplementedVersion(ccId) });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// The Wakeup interview should be done as early as possible\n\t\tif (this.supportsCC(CommandClasses[\"Wake Up\"])) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t\"Root device interview: Wake Up\",\n\t\t\t\t\"silly\",\n\t\t\t);\n\n\t\t\tconst action = await interviewEndpoint(\n\t\t\t\tthis,\n\t\t\t\tCommandClasses[\"Wake Up\"],\n\t\t\t);\n\t\t\tif (typeof action === \"boolean\") return action;\n\t\t}\n\n\t\tthis.modifySupportedCCBeforeInterview(this);\n\n\t\t// We determine the correct interview order of the remaining CCs by topologically sorting two dependency graph\n\t\t// In order to avoid emitting unnecessary value events for the root endpoint,\n\t\t// we defer the application CC interview until after the other endpoints have been interviewed\n\t\t// The following CCs are interviewed \"manually\" outside of the automatic interview sequence,\n\t\t// because there are special rules around them.\n\t\tconst specialCCs = [\n\t\t\tCommandClasses.Security,\n\t\t\tCommandClasses[\"Security 2\"],\n\t\t\tCommandClasses[\"Manufacturer Specific\"],\n\t\t\tCommandClasses.Version,\n\t\t\tCommandClasses[\"Wake Up\"],\n\t\t\t// Basic CC is interviewed last\n\t\t\tCommandClasses.Basic,\n\t\t];\n\t\tconst rootInterviewGraphBeforeEndpoints = this.buildCCInterviewGraph([\n\t\t\t...specialCCs,\n\t\t\t...applicationCCs,\n\t\t]);\n\t\tlet rootInterviewOrderBeforeEndpoints: CommandClasses[];\n\n\t\tconst rootInterviewGraphAfterEndpoints = this.buildCCInterviewGraph([\n\t\t\t...specialCCs,\n\t\t\t...nonApplicationCCs,\n\t\t]);\n\t\tlet rootInterviewOrderAfterEndpoints: CommandClasses[];\n\n\t\ttry {\n\t\t\trootInterviewOrderBeforeEndpoints = topologicalSort(\n\t\t\t\trootInterviewGraphBeforeEndpoints,\n\t\t\t);\n\t\t\trootInterviewOrderAfterEndpoints = topologicalSort(\n\t\t\t\trootInterviewGraphAfterEndpoints,\n\t\t\t);\n\t\t} catch {\n\t\t\t// This interview cannot be done\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The CC interview cannot be completed because there are circular dependencies between CCs!\",\n\t\t\t\tZWaveErrorCodes.CC_Invalid,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Root device interviews before endpoints: ${\n\t\t\t\trootInterviewOrderBeforeEndpoints\n\t\t\t\t\t.map((cc) => `\\n\u00B7 ${getCCName(cc)}`)\n\t\t\t\t\t.join(\"\")\n\t\t\t}`,\n\t\t\t\"silly\",\n\t\t);\n\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Root device interviews after endpoints: ${\n\t\t\t\trootInterviewOrderAfterEndpoints\n\t\t\t\t\t.map((cc) => `\\n\u00B7 ${getCCName(cc)}`)\n\t\t\t\t\t.join(\"\")\n\t\t\t}`,\n\t\t\t\"silly\",\n\t\t);\n\n\t\t// Now that we know the correct order, do the interview in sequence\n\t\tfor (const cc of rootInterviewOrderBeforeEndpoints) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`Root device interview: ${getCCName(cc)}`,\n\t\t\t\t\"silly\",\n\t\t\t);\n\n\t\t\tconst action = await interviewEndpoint(this, cc);\n\t\t\tif (action === \"continue\") continue;\n\t\t\telse if (typeof action === \"boolean\") return action;\n\t\t}\n\n\t\t// Before querying the endpoints, we may need to make some more changes to the CCs the device reports\n\t\t// This time, the non-root endpoints are relevant\n\t\tthis.applyCommandClassesCompatFlag();\n\n\t\t// Now query ALL endpoints\n\t\tfor (const endpointIndex of this.getEndpointIndizes()) {\n\t\t\tconst endpoint = this.getEndpoint(endpointIndex);\n\t\t\tif (!endpoint) continue;\n\n\t\t\t// The root endpoint has been interviewed, so we know if the device supports security and which security classes it has\n\t\t\tconst securityClass = this.getHighestSecurityClass();\n\n\t\t\t// From the specs, Multi Channel Capability Report Command:\n\t\t\t// Non-secure End Point capabilities MUST also be supported securely and MUST also be advertised in\n\t\t\t// the S0/S2 Commands Supported Report Commands unless they are encapsulated outside Security or\n\t\t\t// Security themselves.\n\t\t\t// Nodes supporting S2 MUST support addressing every End Point with S2 encapsulation and MAY\n\t\t\t// explicitly list S2 in the non-secure End Point capabilities.\n\n\t\t\t// This means we need to explicitly add S2 to the list of supported CCs of the endpoint, if the node is using S2.\n\t\t\t// Otherwise the communication will incorrectly use no encryption.\n\t\t\tconst endpointMissingS2 = securityClassIsS2(securityClass)\n\t\t\t\t&& this.supportsCC(CommandClasses[\"Security 2\"])\n\t\t\t\t&& !endpoint.supportsCC(CommandClasses[\"Security 2\"]);\n\t\t\tif (endpointMissingS2) {\n\t\t\t\tendpoint.addCC(\n\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\tthis.implementedCommandClasses.get(\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t)!,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Always interview Security first because it changes the interview order\n\n\t\t\tif (endpoint.supportsCC(CommandClasses[\"Security 2\"])) {\n\t\t\t\t// Security S2 is always supported *securely*\n\t\t\t\tendpoint.addCC(CommandClasses[\"Security 2\"], { secure: true });\n\n\t\t\t\t// If S2 is the highest security class, interview it for the endpoint\n\t\t\t\tif (\n\t\t\t\t\tsecurityClassIsS2(securityClass)\n\t\t\t\t\t&& !!securityManager2\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Endpoint ${endpoint.index} interview: Security S2`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\n\t\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\t\tendpoint,\n\t\t\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\t\t);\n\t\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (endpoint.supportsCC(CommandClasses.Security)) {\n\t\t\t\t// Security S0 is always supported *securely*\n\t\t\t\tendpoint.addCC(CommandClasses.Security, { secure: true });\n\n\t\t\t\t// If S0 is the highest security class, interview it for the endpoint\n\t\t\t\tif (\n\t\t\t\t\tsecurityClass === SecurityClass.S0_Legacy\n\t\t\t\t\t&& !!this.driver.securityManager\n\t\t\t\t) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Endpoint ${endpoint.index} interview: Security S0`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\n\t\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\t\tendpoint,\n\t\t\t\t\t\tCommandClasses.Security,\n\t\t\t\t\t);\n\t\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// It has been found that legacy nodes do not always advertise the S0 Command Class in their Multi\n\t\t\t// Channel Capability Report and still accept all their Command Class using S0 encapsulation.\n\t\t\t// A controlling node SHOULD try to control End Points with S0 encapsulation even if S0 is not\n\t\t\t// listed in the Multi Channel Capability Report.\n\n\t\t\tconst endpointMissingS0 = securityClass === SecurityClass.S0_Legacy\n\t\t\t\t&& this.supportsCC(CommandClasses.Security)\n\t\t\t\t&& !endpoint.supportsCC(CommandClasses.Security);\n\n\t\t\tif (endpointMissingS0) {\n\t\t\t\t// Define which CCs we can use to test this - and if supported, how\n\t\t\t\tconst possibleTests: {\n\t\t\t\t\tccId: CommandClasses;\n\t\t\t\t\t// The test must return a truthy value if the check was successful\n\t\t\t\t\ttest: () => Promise<unknown>;\n\t\t\t\t}[] = [\n\t\t\t\t\t{\n\t\t\t\t\t\tccId: CommandClasses[\"Z-Wave Plus Info\"],\n\t\t\t\t\t\ttest: () =>\n\t\t\t\t\t\t\tendpoint.commandClasses[\"Z-Wave Plus Info\"].get(),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tccId: CommandClasses[\"Binary Switch\"],\n\t\t\t\t\t\ttest: () =>\n\t\t\t\t\t\t\tendpoint.commandClasses[\"Binary Switch\"].get(),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tccId: CommandClasses[\"Binary Sensor\"],\n\t\t\t\t\t\ttest: () =>\n\t\t\t\t\t\t\tendpoint.commandClasses[\"Binary Sensor\"].get(),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tccId: CommandClasses[\"Multilevel Switch\"],\n\t\t\t\t\t\ttest: () =>\n\t\t\t\t\t\t\tendpoint.commandClasses[\"Multilevel Switch\"].get(),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tccId: CommandClasses[\"Multilevel Sensor\"],\n\t\t\t\t\t\ttest: () =>\n\t\t\t\t\t\t\tendpoint.commandClasses[\"Multilevel Sensor\"].get(),\n\t\t\t\t\t},\n\t\t\t\t\t// TODO: add other tests if necessary\n\t\t\t\t];\n\n\t\t\t\tconst foundTest = possibleTests.find((t) =>\n\t\t\t\t\tendpoint.supportsCC(t.ccId)\n\t\t\t\t);\n\t\t\t\tif (foundTest) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`is included using Security S0, but endpoint ${endpoint.index} does not list the CC. Testing if it accepts secure commands anyways.`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\n\t\t\t\t\tconst { ccId, test } = foundTest;\n\n\t\t\t\t\t// Temporarily mark the CC as secure so we can use it to test\n\t\t\t\t\tendpoint.addCC(ccId, { secure: true });\n\n\t\t\t\t\t// Perform the test and treat errors as negative results\n\t\t\t\t\tconst success = !!(await test().catch(() => false));\n\n\t\t\t\t\tif (success) {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`Endpoint ${endpoint.index} accepts/expects secure commands`,\n\t\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Mark all endpoint CCs as secure\n\t\t\t\t\t\tfor (const [ccId] of endpoint.getCCs()) {\n\t\t\t\t\t\t\tendpoint.addCC(ccId, { secure: true });\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`Endpoint ${endpoint.index} is actually not using S0`,\n\t\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\t// Mark the CC as not secure again\n\t\t\t\t\t\tendpoint.addCC(ccId, { secure: false });\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`is included using Security S0, but endpoint ${endpoint.index} does not list the CC. Found no way to test if accepts secure commands anyways.`,\n\t\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// This intentionally checks for Version CC support on the root device.\n\t\t\t// Endpoints SHOULD not support this CC, but we still need to query their\n\t\t\t// CCs that the root device may or may not support\n\t\t\tif (this.supportsCC(CommandClasses.Version)) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tmessage: `Endpoint ${endpoint.index} interview: ${\n\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\tCommandClasses.Version,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\n\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\tendpoint,\n\t\t\t\t\tCommandClasses.Version,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Version CC is not supported. Using the highest implemented version for each CC\",\n\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t});\n\n\t\t\t\tfor (const [ccId, info] of endpoint.getCCs()) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tinfo.isSupported\n\t\t\t\t\t\t// The support status of Basic CC is not known yet at this point\n\t\t\t\t\t\t|| ccId === CommandClasses.Basic\n\t\t\t\t\t) {\n\t\t\t\t\t\tendpoint.addCC(ccId, {\n\t\t\t\t\t\t\tversion: getImplementedVersion(ccId),\n\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// The Security S0/S2 CC adds new CCs to the endpoint, so we need to once more remove those\n\t\t\t// that aren't actually properly supported by the device.\n\t\t\tthis.applyCommandClassesCompatFlag(endpoint.index);\n\n\t\t\t// We need to add/remove some CCs based on other criteria\n\t\t\tthis.modifySupportedCCBeforeInterview(endpoint);\n\n\t\t\tconst endpointInterviewGraph = endpoint.buildCCInterviewGraph([\n\t\t\t\tCommandClasses.Security,\n\t\t\t\tCommandClasses[\"Security 2\"],\n\t\t\t\tCommandClasses.Version,\n\t\t\t\tCommandClasses.Basic,\n\t\t\t]);\n\t\t\tlet endpointInterviewOrder: CommandClasses[];\n\t\t\ttry {\n\t\t\t\tendpointInterviewOrder = topologicalSort(\n\t\t\t\t\tendpointInterviewGraph,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// This interview cannot be done\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"The CC interview cannot be completed because there are circular dependencies between CCs!\",\n\t\t\t\t\tZWaveErrorCodes.CC_Invalid,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: endpoint.index,\n\t\t\t\tmessage: `Endpoint ${endpoint.index} interview order: ${\n\t\t\t\t\tendpointInterviewOrder\n\t\t\t\t\t\t.map((cc) => `\\n\u00B7 ${getCCName(cc)}`)\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}`,\n\t\t\t\tlevel: \"silly\",\n\t\t\t});\n\n\t\t\t// Now that we know the correct order, do the interview in sequence\n\t\t\tfor (const cc of endpointInterviewOrder) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tmessage: `Endpoint ${endpoint.index} interview: ${\n\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\tcc,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\n\t\t\t\tconst action = await interviewEndpoint(endpoint, cc);\n\t\t\t\tif (action === \"continue\") continue;\n\t\t\t\telse if (typeof action === \"boolean\") return action;\n\t\t\t}\n\t\t}\n\n\t\t// Continue with the application CCs for the root endpoint\n\t\tfor (const cc of rootInterviewOrderAfterEndpoints) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`Root device interview: ${getCCName(cc)}`,\n\t\t\t\t\"silly\",\n\t\t\t);\n\n\t\t\tconst action = await interviewEndpoint(this, cc);\n\t\t\tif (action === \"continue\") continue;\n\t\t\telse if (typeof action === \"boolean\") return action;\n\t\t}\n\n\t\t// At the very end, figure out if Basic CC is supposed to be supported\n\t\t// First on the root device\n\t\tconst compat = this.deviceConfig?.compat;\n\t\tif (\n\t\t\t!this.wasCCRemovedViaConfig(CommandClasses.Basic)\n\t\t\t&& this.getCCVersion(CommandClasses.Basic) > 0\n\t\t) {\n\t\t\tif (this.maySupportBasicCC()) {\n\t\t\t\t// The device probably supports Basic CC and is allowed to.\n\t\t\t\t// Interview the Basic CC to figure out if it actually supports it\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t`Root device interview: ${getCCName(CommandClasses.Basic)}`,\n\t\t\t\t\t\"silly\",\n\t\t\t\t);\n\n\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\tthis,\n\t\t\t\t\tCommandClasses.Basic,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t} else {\n\t\t\t\t// Consider the device to control Basic CC, but only if we want to expose the currentValue\n\t\t\t\tif (\n\t\t\t\t\tcompat?.mapBasicReport === false\n\t\t\t\t\t|| compat?.mapBasicSet === \"report\"\n\t\t\t\t) {\n\t\t\t\t\t// TODO: Figure out if we need to consider mapBasicSet === \"auto\" in the case where it falls back to Basic CC currentValue\n\t\t\t\t\tthis.addCC(CommandClasses.Basic, { isControlled: true });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Then on all endpoints\n\t\tfor (const endpointIndex of this.getEndpointIndizes()) {\n\t\t\tconst endpoint = this.getEndpoint(endpointIndex);\n\t\t\tif (!endpoint) continue;\n\t\t\tif (endpoint.wasCCRemovedViaConfig(CommandClasses.Basic)) continue;\n\t\t\tif (endpoint.getCCVersion(CommandClasses.Basic) === 0) continue;\n\n\t\t\tif (endpoint.maySupportBasicCC()) {\n\t\t\t\t// The endpoint probably supports Basic CC and is allowed to.\n\t\t\t\t// Interview the Basic CC to figure out if it actually supports it\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tmessage: `Endpoint ${endpoint.index} interview: Basic CC`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\n\t\t\t\tconst action = await interviewEndpoint(\n\t\t\t\t\tendpoint,\n\t\t\t\t\tCommandClasses.Basic,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t} else {\n\t\t\t\t// Consider the device to control Basic CC, but only if we want to expose the currentValue\n\t\t\t\tif (\n\t\t\t\t\tcompat?.mapBasicReport === false\n\t\t\t\t\t|| compat?.mapBasicSet === \"report\"\n\t\t\t\t) {\n\t\t\t\t\t// TODO: Figure out if we need to consider mapBasicSet === \"auto\" in the case where it falls back to Basic CC currentValue\n\t\t\t\t\tendpoint.addCC(CommandClasses.Basic, {\n\t\t\t\t\t\tisControlled: true,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * @internal\n\t * Handles the receipt of a NIF / NodeUpdatePayload\n\t */\n\tpublic updateNodeInfo(nodeInfo: NodeUpdatePayload): void {\n\t\tif (this.interviewStage < InterviewStage.NodeInfo) {\n\t\t\tfor (const cc of nodeInfo.supportedCCs) {\n\t\t\t\tif (cc === CommandClasses.Basic) {\n\t\t\t\t\t// Basic CC MUST not be in the NIF and we have special rules to determine support\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.addCC(cc, { isSupported: true });\n\t\t\t}\n\t\t}\n\n\t\t// As the NIF is sent on wakeup, treat this as a sign that the node is awake\n\t\tthis.markAsAwake();\n\n\t\t// SDS14223 Unless unsolicited <XYZ> Report Commands are received,\n\t\t// a controlling node MUST probe the current values when the\n\t\t// supporting node issues a Wake Up Notification Command for sleeping nodes.\n\n\t\t// This is not the handler for wakeup notifications, but some legacy devices send this\n\t\t// message whenever there's an update and want to be polled.\n\t\t// We do this unless we know for certain that the device sends unsolicited reports for its actuator or sensor CCs\n\t\tif (\n\t\t\tthis.interviewStage === InterviewStage.Complete\n\t\t\t&& !this.supportsCC(CommandClasses[\"Z-Wave Plus Info\"])\n\t\t\t&& (!this.valueDB.getValue(AssociationCCValues.hasLifeline.id)\n\t\t\t\t|| !ccUtils.doesAnyLifelineSendActuatorOrSensorReports(\n\t\t\t\t\tthis.driver,\n\t\t\t\t\tthis,\n\t\t\t\t))\n\t\t) {\n\t\t\tconst delay = this.deviceConfig?.compat?.manualValueRefreshDelayMs\n\t\t\t\t|| 0;\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Node does not send unsolicited updates; refreshing actuator and sensor values${\n\t\t\t\t\t\tdelay > 0 ? ` in ${delay} ms` : \"\"\n\t\t\t\t\t}...`,\n\t\t\t});\n\t\t\tsetTimeout(() => this.refreshValues(), delay);\n\t\t}\n\t}\n\n\t/**\n\t * Rediscovers all capabilities of a single CC on this node and all endpoints.\n\t * This can be considered a more targeted variant of `refreshInfo`.\n\t *\n\t * WARNING: It is not recommended to await this method!\n\t */\n\tpublic async interviewCC(cc: CommandClasses): Promise<void> {\n\t\tconst endpoints = this.getAllEndpoints();\n\t\t// Interview the node itself last\n\t\tendpoints.push(endpoints.shift()!);\n\t\tfor (const endpoint of endpoints) {\n\t\t\tconst instance = endpoint.createCCInstanceUnsafe(cc);\n\t\t\tif (instance) {\n\t\t\t\ttry {\n\t\t\t\t\tawait instance.interview(this.driver);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t`failed to interview CC ${\n\t\t\t\t\t\t\tgetCCName(cc)\n\t\t\t\t\t\t}, endpoint ${endpoint.index}: ${getErrorMessage(e)}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Refreshes all non-static values of a single CC from this node (all endpoints).\n\t * WARNING: It is not recommended to await this method!\n\t */\n\tpublic async refreshCCValues(cc: CommandClasses): Promise<void> {\n\t\tfor (const endpoint of this.getAllEndpoints()) {\n\t\t\tconst instance = endpoint.createCCInstanceUnsafe(cc);\n\t\t\tif (instance) {\n\t\t\t\ttry {\n\t\t\t\t\tawait instance.refreshValues(this.driver);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t`failed to refresh values for ${\n\t\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\t\tcc,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}, endpoint ${endpoint.index}: ${getErrorMessage(e)}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Refreshes all non-static values from this node's actuator and sensor CCs.\n\t * WARNING: It is not recommended to await this method!\n\t */\n\tpublic async refreshValues(): Promise<void> {\n\t\tfor (const endpoint of this.getAllEndpoints()) {\n\t\t\tfor (const cc of endpoint.getSupportedCCInstances()) {\n\t\t\t\t// Only query actuator and sensor CCs\n\t\t\t\tif (\n\t\t\t\t\t!actuatorCCs.includes(cc.ccId)\n\t\t\t\t\t&& !sensorCCs.includes(cc.ccId)\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait cc.refreshValues(this.driver);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t`failed to refresh values for ${\n\t\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\t\tcc.ccId,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}, endpoint ${endpoint.index}: ${getErrorMessage(e)}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Refreshes the values of all CCs that should be reporting regularly, but haven't been updated recently\n\t * @internal\n\t */\n\tpublic async autoRefreshValues(): Promise<void> {\n\t\t// Do not attempt to communicate with dead nodes automatically\n\t\tif (this.status === NodeStatus.Dead) return;\n\n\t\tfor (const endpoint of this.getAllEndpoints()) {\n\t\t\tfor (\n\t\t\t\tconst cc of endpoint\n\t\t\t\t\t.getSupportedCCInstances() as readonly SinglecastCC<\n\t\t\t\t\t\tCommandClass\n\t\t\t\t\t>[]\n\t\t\t) {\n\t\t\t\tif (!cc.shouldRefreshValues(this.driver)) continue;\n\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `${\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} CC values may be stale, refreshing...`,\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t});\n\n\t\t\t\ttry {\n\t\t\t\t\tawait cc.refreshValues(this.driver);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tmessage: `failed to refresh values for ${\n\t\t\t\t\t\t\tgetCCName(\n\t\t\t\t\t\t\t\tcc.ccId,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t} CC: ${getErrorMessage(e)}`,\n\t\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Uses the `commandClasses` compat flag defined in the node's config file to\n\t * override the reported command classes.\n\t * @param endpointIndex If given, limits the application of the compat flag to the given endpoint.\n\t */\n\tprivate applyCommandClassesCompatFlag(endpointIndex?: number): void {\n\t\tif (this.deviceConfig) {\n\t\t\t// Add CCs the device config file tells us to\n\t\t\tconst addCCs = this.deviceConfig.compat?.addCCs;\n\t\t\tif (addCCs) {\n\t\t\t\tfor (const [cc, { endpoints }] of addCCs) {\n\t\t\t\t\tif (endpointIndex === undefined) {\n\t\t\t\t\t\tfor (const [ep, info] of endpoints) {\n\t\t\t\t\t\t\tthis.getEndpoint(ep)?.addCC(cc, info);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (endpoints.has(endpointIndex)) {\n\t\t\t\t\t\tthis.getEndpoint(endpointIndex)?.addCC(\n\t\t\t\t\t\t\tcc,\n\t\t\t\t\t\t\tendpoints.get(endpointIndex)!,\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\t// And remove those that it marks as unsupported\n\t\t\tconst removeCCs = this.deviceConfig.compat?.removeCCs;\n\t\t\tif (removeCCs) {\n\t\t\t\tfor (const [cc, endpoints] of removeCCs) {\n\t\t\t\t\tif (endpoints === \"*\") {\n\t\t\t\t\t\tif (endpointIndex === undefined) {\n\t\t\t\t\t\t\tfor (const ep of this.getAllEndpoints()) {\n\t\t\t\t\t\t\t\tep.removeCC(cc);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.getEndpoint(endpointIndex)?.removeCC(cc);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (endpointIndex === undefined) {\n\t\t\t\t\t\t\tfor (const ep of endpoints) {\n\t\t\t\t\t\t\t\tthis.getEndpoint(ep)?.removeCC(cc);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (endpoints.includes(endpointIndex)) {\n\t\t\t\t\t\t\tthis.getEndpoint(endpointIndex)?.removeCC(cc);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Updates the supported CCs of the given endpoint depending on compat flags\n\t * and certification requirements\n\t */\n\tprivate modifySupportedCCBeforeInterview(endpoint: Endpoint): void {\n\t\t// Window Covering CC:\n\t\t// CL:006A.01.51.01.2: A controlling node MUST NOT interview and provide controlling functionalities for the\n\t\t// Multilevel Switch Command Class for a node (or endpoint) supporting the Window Covering CC, as it is a fully\n\t\t// redundant and less precise application functionality.\n\t\tif (\n\t\t\tendpoint.supportsCC(CommandClasses[\"Multilevel Switch\"])\n\t\t\t&& endpoint.supportsCC(CommandClasses[\"Window Covering\"])\n\t\t) {\n\t\t\tendpoint.removeCC(CommandClasses[\"Multilevel Switch\"]);\n\t\t}\n\t}\n\n\t/** Overwrites the reported configuration with information from a config file */\n\tprotected async overwriteConfig(): Promise<void> {\n\t\tif (this.isControllerNode) {\n\t\t\t// The device config was not loaded prior to this step because the Version CC is not interviewed.\n\t\t\t// Therefore do it here.\n\t\t\tawait this.loadDeviceConfig();\n\t\t}\n\n\t\tif (this.deviceConfig) {\n\t\t\t// Add CCs the device config file tells us to\n\t\t\tconst addCCs = this.deviceConfig.compat?.addCCs;\n\t\t\tif (addCCs) {\n\t\t\t\tfor (const [cc, { endpoints }] of addCCs) {\n\t\t\t\t\tfor (const [ep, info] of endpoints) {\n\t\t\t\t\t\tthis.getEndpoint(ep)?.addCC(cc, info);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// And remove those that it marks as unsupported\n\t\t\tconst removeCCs = this.deviceConfig.compat?.removeCCs;\n\t\t\tif (removeCCs) {\n\t\t\t\tfor (const [cc, endpoints] of removeCCs) {\n\t\t\t\t\tif (endpoints === \"*\") {\n\t\t\t\t\t\tfor (const ep of this.getAllEndpoints()) {\n\t\t\t\t\t\t\tep.removeCC(cc);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor (const ep of endpoints) {\n\t\t\t\t\t\t\tthis.getEndpoint(ep)?.removeCC(cc);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.setInterviewStage(InterviewStage.OverwriteConfig);\n\t}\n\n\t/**\n\t * @internal\n\t * Handles a CommandClass that was received from this node\n\t */\n\tpublic async handleCommand(command: CommandClass): Promise<void> {\n\t\t// If this is a report for the root endpoint and the node supports the CC on another endpoint,\n\t\t// we need to map it to endpoint 1. Either it does not support multi channel associations or\n\t\t// it is misbehaving. In any case, we would hide this report if we didn't map it\n\t\tif (\n\t\t\tcommand.endpointIndex === 0\n\t\t\t&& command.constructor.name.endsWith(\"Report\")\n\t\t\t&& this.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&& this._deviceConfig?.compat?.mapRootReportsToEndpoint != undefined\n\t\t) {\n\t\t\tconst endpoint = this.getEndpoint(\n\t\t\t\tthis._deviceConfig?.compat?.mapRootReportsToEndpoint,\n\t\t\t);\n\t\t\tif (endpoint && endpoint.supportsCC(command.ccId)) {\n\t\t\t\t// Force the CC to store its values again under the supporting endpoint\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t`Mapping unsolicited report from root device to endpoint #${endpoint.index}`,\n\t\t\t\t);\n\t\t\t\tcommand.endpointIndex = endpoint.index;\n\t\t\t\tcommand.persistValues(this.driver);\n\t\t\t}\n\t\t}\n\n\t\t// If we're being queried by another node, treat this as a sign that the other node is awake\n\t\tif (\n\t\t\tcommand.constructor.name.endsWith(\"Get\")\n\t\t\t// Nonces can be sent while asleep though\n\t\t\t&& !(command instanceof SecurityCCNonceGet)\n\t\t\t&& !(command instanceof Security2CCNonceGet)\n\t\t) {\n\t\t\tthis.markAsAwake();\n\t\t}\n\n\t\t// If the received CC was force-removed via config file, ignore it completely\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex);\n\t\tif (endpoint?.wasCCRemovedViaConfig(command.ccId)) {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t{\n\t\t\t\t\tendpoint: endpoint.index,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`Ignoring ${command.constructor.name} because CC support was removed via config file`,\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (command instanceof BasicCC) {\n\t\t\treturn this.handleBasicCommand(command);\n\t\t} else if (command instanceof MultilevelSwitchCC) {\n\t\t\treturn this.handleMultilevelSwitchCommand(command);\n\t\t} else if (command instanceof BinarySwitchCCSet) {\n\t\t\treturn this.handleBinarySwitchCommand(command);\n\t\t} else if (command instanceof ThermostatModeCCSet) {\n\t\t\treturn this.handleThermostatModeCommand(command);\n\t\t} else if (command instanceof CentralSceneCCNotification) {\n\t\t\treturn this.handleCentralSceneNotification(command);\n\t\t} else if (command instanceof WakeUpCCWakeUpNotification) {\n\t\t\treturn this.handleWakeUpNotification();\n\t\t} else if (command instanceof NotificationCCReport) {\n\t\t\treturn this.handleNotificationReport(command);\n\t\t} else if (command instanceof ClockCCReport) {\n\t\t\treturn this.handleClockReport(command);\n\t\t} else if (command instanceof SecurityCCNonceGet) {\n\t\t\treturn this.handleSecurityNonceGet();\n\t\t} else if (command instanceof SecurityCCNonceReport) {\n\t\t\treturn this.handleSecurityNonceReport(command);\n\t\t} else if (command instanceof SecurityCCCommandsSupportedGet) {\n\t\t\treturn this.handleSecurityCommandsSupportedGet(command);\n\t\t} else if (command instanceof Security2CCNonceGet) {\n\t\t\treturn this.handleSecurity2NonceGet();\n\t\t} else if (command instanceof Security2CCNonceReport) {\n\t\t\treturn this.handleSecurity2NonceReport(command);\n\t\t} else if (command instanceof Security2CCCommandsSupportedGet) {\n\t\t\treturn this.handleSecurity2CommandsSupportedGet(command);\n\t\t} else if (command instanceof HailCC) {\n\t\t\treturn this.handleHail(command);\n\t\t} else if (command instanceof FirmwareUpdateMetaDataCCGet) {\n\t\t\treturn this.handleUnexpectedFirmwareUpdateGet(command);\n\t\t} else if (command instanceof FirmwareUpdateMetaDataCCMetaDataGet) {\n\t\t\treturn this.handleFirmwareUpdateMetaDataGet(command);\n\t\t} else if (command instanceof EntryControlCCNotification) {\n\t\t\treturn this.handleEntryControlNotification(command);\n\t\t} else if (command instanceof TimeCCTimeGet) {\n\t\t\treturn this.handleTimeGet(command);\n\t\t} else if (command instanceof TimeCCDateGet) {\n\t\t\treturn this.handleDateGet(command);\n\t\t} else if (command instanceof TimeCCTimeOffsetGet) {\n\t\t\treturn this.handleTimeOffsetGet(command);\n\t\t} else if (command instanceof ZWavePlusCCGet) {\n\t\t\treturn this.handleZWavePlusGet(command);\n\t\t} else if (command instanceof VersionCCGet) {\n\t\t\treturn this.handleVersionGet(command);\n\t\t} else if (command instanceof VersionCCCommandClassGet) {\n\t\t\treturn this.handleVersionCommandClassGet(command);\n\t\t} else if (command instanceof VersionCCCapabilitiesGet) {\n\t\t\treturn this.handleVersionCapabilitiesGet(command);\n\t\t} else if (command instanceof ManufacturerSpecificCCGet) {\n\t\t\treturn this.handleManufacturerSpecificGet(command);\n\t\t} else if (command instanceof AssociationGroupInfoCCNameGet) {\n\t\t\treturn this.handleAGINameGet(command);\n\t\t} else if (command instanceof AssociationGroupInfoCCInfoGet) {\n\t\t\treturn this.handleAGIInfoGet(command);\n\t\t} else if (command instanceof AssociationGroupInfoCCCommandListGet) {\n\t\t\treturn this.handleAGICommandListGet(command);\n\t\t} else if (command instanceof AssociationCCSupportedGroupingsGet) {\n\t\t\treturn this.handleAssociationSupportedGroupingsGet(command);\n\t\t} else if (command instanceof AssociationCCGet) {\n\t\t\treturn this.handleAssociationGet(command);\n\t\t} else if (command instanceof AssociationCCSet) {\n\t\t\treturn this.handleAssociationSet(command);\n\t\t} else if (command instanceof AssociationCCRemove) {\n\t\t\treturn this.handleAssociationRemove(command);\n\t\t} else if (command instanceof AssociationCCSpecificGroupGet) {\n\t\t\treturn this.handleAssociationSpecificGroupGet(command);\n\t\t} else if (\n\t\t\tcommand instanceof MultiChannelAssociationCCSupportedGroupingsGet\n\t\t) {\n\t\t\treturn this.handleMultiChannelAssociationSupportedGroupingsGet(\n\t\t\t\tcommand,\n\t\t\t);\n\t\t} else if (command instanceof MultiChannelAssociationCCGet) {\n\t\t\treturn this.handleMultiChannelAssociationGet(command);\n\t\t} else if (command instanceof MultiChannelAssociationCCSet) {\n\t\t\treturn this.handleMultiChannelAssociationSet(command);\n\t\t} else if (command instanceof MultiChannelAssociationCCRemove) {\n\t\t\treturn this.handleMultiChannelAssociationRemove(command);\n\t\t} else if (command instanceof IndicatorCCSupportedGet) {\n\t\t\treturn this.handleIndicatorSupportedGet(command);\n\t\t} else if (command instanceof IndicatorCCSet) {\n\t\t\treturn this.handleIndicatorSet(command);\n\t\t} else if (command instanceof IndicatorCCGet) {\n\t\t\treturn this.handleIndicatorGet(command);\n\t\t} else if (command instanceof IndicatorCCDescriptionGet) {\n\t\t\treturn this.handleIndicatorDescriptionGet(command);\n\t\t} else if (command instanceof PowerlevelCCSet) {\n\t\t\treturn this.handlePowerlevelSet(command);\n\t\t} else if (command instanceof PowerlevelCCGet) {\n\t\t\treturn this.handlePowerlevelGet(command);\n\t\t} else if (command instanceof PowerlevelCCTestNodeSet) {\n\t\t\treturn this.handlePowerlevelTestNodeSet(command);\n\t\t} else if (command instanceof PowerlevelCCTestNodeGet) {\n\t\t\treturn this.handlePowerlevelTestNodeGet(command);\n\t\t} else if (command instanceof PowerlevelCCTestNodeReport) {\n\t\t\treturn this.handlePowerlevelTestNodeReport(command);\n\t\t} else if (command instanceof DeviceResetLocallyCCNotification) {\n\t\t\treturn this.handleDeviceResetLocallyNotification(command);\n\t\t} else if (command instanceof InclusionControllerCCInitiate) {\n\t\t\t// Inclusion controller commands are handled by the controller class\n\t\t\tif (\n\t\t\t\tcommand.step === InclusionControllerStep.ProxyInclusionReplace\n\t\t\t) {\n\t\t\t\treturn this.driver.controller\n\t\t\t\t\t.handleInclusionControllerCCInitiateReplace(\n\t\t\t\t\t\tcommand,\n\t\t\t\t\t);\n\t\t\t}\n\t\t} else if (command instanceof MultiCommandCCCommandEncapsulation) {\n\t\t\t// Handle each encapsulated command individually\n\t\t\tfor (const cmd of command.encapsulated) {\n\t\t\t\tawait this.handleCommand(cmd);\n\t\t\t}\n\t\t\treturn;\n\t\t} else if (\n\t\t\tcommand instanceof Security2CCMessageEncapsulation\n\t\t\t&& command.encapsulated == undefined\n\t\t) {\n\t\t\t// Some S2 commands contain only extensions. Those are handled by the CC implementation.\n\t\t\treturn;\n\t\t}\n\n\t\t// Ignore all commands that don't need to be handled\n\t\tswitch (true) {\n\t\t\t// Reports are either a response to a Get command or\n\t\t\t// automatically store their values in the Value DB.\n\t\t\t// No need to manually handle them - other than what we've already done\n\t\t\tcase command.constructor.name.endsWith(\"Report\"):\n\n\t\t\t// When this command is received, its values get emitted as an event.\n\t\t\t// Nothing else to do here\n\t\t\tcase command instanceof SceneActivationCCSet:\n\t\t\t\treturn;\n\t\t}\n\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: `TODO: no handler for application command`,\n\t\t\tdirection: \"inbound\",\n\t\t});\n\n\t\tif (command.encapsulationFlags & EncapsulationFlags.Supervision) {\n\t\t\t// Report no support for supervised commands we cannot handle\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"No handler for application command\",\n\t\t\t\tZWaveErrorCodes.CC_NotSupported,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate hasLoggedNoNetworkKey = false;\n\n\t/**\n\t * @internal\n\t * Handles a nonce request\n\t */\n\tpublic async handleSecurityNonceGet(): Promise<void> {\n\t\t// Only reply if secure communication is set up\n\t\tif (!this.driver.securityManager) {\n\t\t\tif (!this.hasLoggedNoNetworkKey) {\n\t\t\t\tthis.hasLoggedNoNetworkKey = true;\n\t\t\t\tthis.driver.controllerLog.logNode(this.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\tthis.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() === this.id\n\t\t\t&& containsCC(t.message)\n\t\t\t&& t.message.command instanceof SecurityCCNonceReport;\n\n\t\tif (this.driver.hasPendingTransactions(isNonceReport)) {\n\t\t\tthis.driver.controllerLog.logNode(this.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.driver.securityManager.deleteAllNoncesForReceiver(this.id);\n\n\t\t// Now send the current nonce\n\t\ttry {\n\t\t\tawait this.commandClasses.Security.sendNonce();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(this.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(command: SecurityCCNonceReport): void {\n\t\tconst secMan = this.driver.securityManager;\n\t\tif (!secMan) return;\n\n\t\tsecMan.setNonce(\n\t\t\t{\n\t\t\t\tissuer: this.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.driver.controller.ownNodeId!,\n\t\t\t},\n\t\t\t{ free: true },\n\t\t);\n\t}\n\n\t/**\n\t * @internal\n\t * Handles a nonce request for S2\n\t */\n\tpublic async handleSecurity2NonceGet(): Promise<void> {\n\t\t// Only reply if secure communication is set up\n\t\tif (!this.driver.getSecurityManager2(this.id)) {\n\t\t\tif (!this.hasLoggedNoNetworkKey) {\n\t\t\t\tthis.hasLoggedNoNetworkKey = true;\n\t\t\t\tthis.driver.controllerLog.logNode(this.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\tthis.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() === this.id\n\t\t\t&& containsCC(t.message)\n\t\t\t&& t.message.command instanceof Security2CCNonceReport;\n\n\t\tif (this.driver.hasPendingTransactions(isNonceReport)) {\n\t\t\tthis.driver.controllerLog.logNode(this.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 this.commandClasses[\"Security 2\"].sendNonce();\n\t\t} catch (e) {\n\t\t\tthis.driver.controllerLog.logNode(this.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(_command: Security2CCNonceReport): void {\n\t\t// const secMan = this.driver.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(this.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.driver.controllerLog.logNode(this.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 busyPollingAfterHail: boolean = false;\n\tprivate async handleHail(_command: HailCC): Promise<void> {\n\t\t// treat this as a sign that the node is awake\n\t\tthis.markAsAwake();\n\n\t\tif (this.busyPollingAfterHail) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Hail received from node, but still busy with previous one...`,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tthis.busyPollingAfterHail = true;\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage:\n\t\t\t\t`Hail received from node, refreshing actuator and sensor values...`,\n\t\t});\n\t\ttry {\n\t\t\tawait this.refreshValues();\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t\tthis.busyPollingAfterHail = false;\n\t}\n\n\t/** Stores information about a currently held down key */\n\tprivate centralSceneKeyHeldDownContext:\n\t\t| {\n\t\t\ttimeout: NodeJS.Timeout;\n\t\t\tsceneNumber: number;\n\t\t}\n\t\t| undefined;\n\tprivate lastCentralSceneNotificationSequenceNumber: number | undefined;\n\tprivate centralSceneForcedKeyUp = false;\n\n\t/** Handles the receipt of a Central Scene notifification */\n\tprivate handleCentralSceneNotification(\n\t\tcommand: CentralSceneCCNotification,\n\t): void {\n\t\t// Did we already receive this command?\n\t\tif (\n\t\t\tcommand.sequenceNumber\n\t\t\t\t=== this.lastCentralSceneNotificationSequenceNumber\n\t\t) {\n\t\t\treturn;\n\t\t} else {\n\t\t\tthis.lastCentralSceneNotificationSequenceNumber =\n\t\t\t\tcommand.sequenceNumber;\n\t\t}\n\t\t/*\n\t\tIf the Slow Refresh field is false:\n\t\t- A new Key Held Down notification MUST be sent every 200ms until the key is released.\n\t\t- The Sequence Number field MUST be updated at each notification transmission.\n\t\t- If not receiving a new Key Held Down notification within 400ms, a controlling node SHOULD use an adaptive timeout approach as described in 4.17.1:\n\t\tA controller SHOULD apply an adaptive approach based on the reception of the Key Released Notification.\n\t\tInitially, the controller SHOULD time out if not receiving any Key Held Down Notification refresh after\n\t\t400ms and consider this to be a Key Up Notification. If, however, the controller subsequently receives a\n\t\tKey Released Notification, the controller SHOULD consider the sending node to be operating with the Slow\n\t\tRefresh capability enabled.\n\n\t\tIf the Slow Refresh field is true:\n\t\t- A new Key Held Down notification MUST be sent every 55 seconds until the key is released.\n\t\t- The Sequence Number field MUST be updated at each notification refresh.\n\t\t- If not receiving a new Key Held Down notification within 60 seconds after the most recent Key Held Down\n\t\tnotification, a receiving node MUST respond as if it received a Key Release notification.\n\t\t*/\n\n\t\tconst setSceneValue = (\n\t\t\tsceneNumber: number,\n\t\t\tkey: CentralSceneKeys,\n\t\t): void => {\n\t\t\tconst valueId = CentralSceneCCValues.scene(sceneNumber).id;\n\t\t\tthis.valueDB.setValue(valueId, key, { stateful: false });\n\t\t};\n\n\t\tconst forceKeyUp = (): void => {\n\t\t\tthis.centralSceneForcedKeyUp = true;\n\t\t\t// force key up event\n\t\t\tsetSceneValue(\n\t\t\t\tthis.centralSceneKeyHeldDownContext!.sceneNumber,\n\t\t\t\tCentralSceneKeys.KeyReleased,\n\t\t\t);\n\t\t\t// clear old timer\n\t\t\tclearTimeout(this.centralSceneKeyHeldDownContext!.timeout);\n\t\t\t// clear the key down context\n\t\t\tthis.centralSceneKeyHeldDownContext = undefined;\n\t\t};\n\n\t\tif (\n\t\t\tthis.centralSceneKeyHeldDownContext\n\t\t\t&& this.centralSceneKeyHeldDownContext.sceneNumber\n\t\t\t\t!== command.sceneNumber\n\t\t) {\n\t\t\t// The user pressed another button, force release\n\t\t\tforceKeyUp();\n\t\t}\n\n\t\tconst slowRefreshValueId = CentralSceneCCValues.slowRefresh.endpoint(\n\t\t\tthis.index,\n\t\t);\n\t\tif (command.keyAttribute === CentralSceneKeys.KeyHeldDown) {\n\t\t\t// Set or refresh timer to force a release of the key\n\t\t\tthis.centralSceneForcedKeyUp = false;\n\t\t\tif (this.centralSceneKeyHeldDownContext) {\n\t\t\t\tclearTimeout(this.centralSceneKeyHeldDownContext.timeout);\n\t\t\t}\n\t\t\t// If the node does not advertise support for the slow refresh capability, we might still be dealing with a\n\t\t\t// slow refresh node. We use the stored value for fallback behavior\n\t\t\tconst slowRefresh = command.slowRefresh\n\t\t\t\t?? this.valueDB.getValue<boolean>(slowRefreshValueId);\n\t\t\tthis.centralSceneKeyHeldDownContext = {\n\t\t\t\tsceneNumber: command.sceneNumber,\n\t\t\t\t// Unref'ing long running timers allows the process to exit mid-timeout\n\t\t\t\ttimeout: setTimeout(\n\t\t\t\t\tforceKeyUp,\n\t\t\t\t\tslowRefresh ? 60000 : 400,\n\t\t\t\t).unref(),\n\t\t\t};\n\t\t} else if (command.keyAttribute === CentralSceneKeys.KeyReleased) {\n\t\t\t// Stop the release timer\n\t\t\tif (this.centralSceneKeyHeldDownContext) {\n\t\t\t\tclearTimeout(this.centralSceneKeyHeldDownContext.timeout);\n\t\t\t\tthis.centralSceneKeyHeldDownContext = undefined;\n\t\t\t} else if (this.centralSceneForcedKeyUp) {\n\t\t\t\t// If we timed out and the controller subsequently receives a Key Released Notification,\n\t\t\t\t// we SHOULD consider the sending node to be operating with the Slow Refresh capability enabled.\n\t\t\t\tthis.valueDB.setValue(slowRefreshValueId, true);\n\t\t\t\t// Do not raise the duplicate event\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tsetSceneValue(command.sceneNumber, command.keyAttribute);\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: `received CentralScene notification ${stringify(command)}`,\n\t\t\tdirection: \"inbound\",\n\t\t});\n\t}\n\n\t/** The timestamp of the last received wakeup notification */\n\tprivate lastWakeUp: number | undefined;\n\n\t/** Handles the receipt of a Wake Up notification */\n\tprivate handleWakeUpNotification(): void {\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: `received wakeup notification`,\n\t\t\tdirection: \"inbound\",\n\t\t});\n\n\t\t// It can happen that the node has not told us that it supports the Wake Up CC\n\t\t// https://sentry.io/share/issue/6a681729d7db46d591f1dcadabe8d02e/\n\t\t// To avoid a crash, mark it as supported\n\t\tif (this.getCCVersion(CommandClasses[\"Wake Up\"]) === 0) {\n\t\t\tthis.addCC(CommandClasses[\"Wake Up\"], {\n\t\t\t\tisSupported: true,\n\t\t\t\tversion: 1,\n\t\t\t});\n\t\t}\n\n\t\tthis.markAsAwake();\n\n\t\t// From the specs:\n\t\t// A controlling node SHOULD read the Wake Up Interval of a supporting node when the delays between\n\t\t// Wake Up periods are larger than what was last set at the supporting node.\n\t\tconst now = Date.now();\n\t\tif (this.lastWakeUp) {\n\t\t\t// we've already measured the wake up interval, so we can check whether a refresh is necessary\n\t\t\tconst wakeUpInterval =\n\t\t\t\tthis.getValue<number>(WakeUpCCValues.wakeUpInterval.id) ?? 1;\n\t\t\t// The wakeup interval is specified in seconds. Also add 5 minutes tolerance to avoid\n\t\t\t// unnecessary queries since there might be some delay. A wakeup interval of 0 means manual wakeup,\n\t\t\t// so the interval shouldn't be verified\n\t\t\tif (\n\t\t\t\twakeUpInterval > 0\n\t\t\t\t&& (now - this.lastWakeUp) / 1000 > wakeUpInterval + 5 * 60\n\t\t\t) {\n\t\t\t\tthis.commandClasses[\"Wake Up\"].getInterval().catch(() => {\n\t\t\t\t\t// Don't throw if there's an error\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.lastWakeUp = now;\n\n\t\t// Some legacy devices expect us to query them on wake up in order to function correctly\n\t\tif (this._deviceConfig?.compat?.queryOnWakeup) {\n\t\t\tvoid this.compatDoWakeupQueries();\n\t\t} else if (!this._deviceConfig?.compat?.disableAutoRefresh) {\n\t\t\t// For other devices we may have to refresh their values from time to time\n\t\t\tvoid this.autoRefreshValues().catch(() => {\n\t\t\t\t// ignore\n\t\t\t});\n\t\t}\n\n\t\t// In case there are no messages in the queue, the node may go back to sleep very soon\n\t\tthis.driver.debounceSendNodeToSleep(this);\n\t}\n\n\tprivate async compatDoWakeupQueries(): Promise<void> {\n\t\tif (!this._deviceConfig?.compat?.queryOnWakeup) return;\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: `expects some queries after wake up, so it shall receive`,\n\t\t\tdirection: \"none\",\n\t\t});\n\n\t\tfor (\n\t\t\tconst [ccName, apiMethod, ...args] of this._deviceConfig.compat\n\t\t\t\t.queryOnWakeup\n\t\t) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: `compat query \"${ccName}\"::${apiMethod}(${\n\t\t\t\t\targs\n\t\t\t\t\t\t.map((arg) => JSON.stringify(arg))\n\t\t\t\t\t\t.join(\", \")\n\t\t\t\t})`,\n\t\t\t\tdirection: \"none\",\n\t\t\t});\n\n\t\t\t// Try to access the API - if it doesn't work, skip this option\n\t\t\tlet API: CCAPI;\n\t\t\ttry {\n\t\t\t\tAPI = (\n\t\t\t\t\t(this.commandClasses as any)[ccName] as CCAPI\n\t\t\t\t).withOptions({\n\t\t\t\t\t// Tag the resulting transactions as compat queries\n\t\t\t\t\ttag: \"compat\",\n\t\t\t\t\t// Do not retry them or they may cause congestion if the node is asleep again\n\t\t\t\t\tmaxSendAttempts: 1,\n\t\t\t\t\t// This is for a sleeping node - there's no point in keeping the transactions when the node is asleep\n\t\t\t\t\texpire: 10000,\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `could not access API, skipping query`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!API.isSupported()) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `API not supported, skipping query`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t} else if (!(API as any)[apiMethod]) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`method ${apiMethod} not found on API, skipping query`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Retrieve the method\n\t\t\t// eslint-disable-next-line\n\t\t\tconst method = (API as any)[apiMethod].bind(API) as Function;\n\t\t\t// And replace \"smart\" arguments with their corresponding value\n\t\t\tconst methodArgs = args.map<unknown>((arg) => {\n\t\t\t\tif (isObject(arg)) {\n\t\t\t\t\tconst valueId = {\n\t\t\t\t\t\tcommandClass: API.ccId,\n\t\t\t\t\t\t...arg,\n\t\t\t\t\t};\n\t\t\t\t\treturn this.getValue(valueId);\n\t\t\t\t}\n\t\t\t\treturn arg;\n\t\t\t});\n\n\t\t\t// Do the API call and ignore/log any errors\n\t\t\ttry {\n\t\t\t\tawait method(...methodArgs);\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `API call successful`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t});\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `error during API call: ${getErrorMessage(e)}`,\n\t\t\t\t\tdirection: \"none\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t\tif (\n\t\t\t\t\tisZWaveError(e)\n\t\t\t\t\t&& e.code === ZWaveErrorCodes.Controller_MessageExpired\n\t\t\t\t) {\n\t\t\t\t\t// A compat query expired - no point in trying the others too\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Handles the receipt of a BasicCC Set or Report */\n\tprivate handleBasicCommand(command: BasicCC): void {\n\t\t// Retrieve the endpoint the command is coming from\n\t\tconst sourceEndpoint = this.getEndpoint(command.endpointIndex ?? 0)\n\t\t\t?? this;\n\n\t\t// Depending on the generic device class, we may need to map the basic command to other CCs\n\t\tlet mappedTargetCC: CommandClass | undefined;\n\t\tswitch (sourceEndpoint.deviceClass?.generic.key) {\n\t\t\tcase 0x20: // Binary Sensor\n\t\t\t\tmappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(\n\t\t\t\t\tCommandClasses[\"Binary Sensor\"],\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 0x10: // Binary Switch\n\t\t\t\tmappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(\n\t\t\t\t\tCommandClasses[\"Binary Switch\"],\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 0x11: // Multilevel Switch\n\t\t\t\tmappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(\n\t\t\t\t\tCommandClasses[\"Multilevel Switch\"],\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 0x12: // Remote Switch\n\t\t\t\tswitch (sourceEndpoint.deviceClass.specific.key) {\n\t\t\t\t\tcase 0x01: // Binary Remote Switch\n\t\t\t\t\t\tmappedTargetCC = sourceEndpoint\n\t\t\t\t\t\t\t.createCCInstanceUnsafe(\n\t\t\t\t\t\t\t\tCommandClasses[\"Binary Switch\"],\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 0x02: // Multilevel Remote Switch\n\t\t\t\t\t\tmappedTargetCC = sourceEndpoint\n\t\t\t\t\t\t\t.createCCInstanceUnsafe(\n\t\t\t\t\t\t\t\tCommandClasses[\"Multilevel Switch\"],\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\n\t\tif (command instanceof BasicCCReport) {\n\t\t\t// By default, map Basic CC Reports to a more appropriate CC, unless stated otherwise in a config file\n\t\t\tconst basicReportMapping = this.deviceConfig?.compat?.mapBasicReport\n\t\t\t\t?? \"auto\";\n\n\t\t\tif (basicReportMapping === \"Binary Sensor\") {\n\t\t\t\t// Treat the command as a BinarySensorCC Report, regardless of the device class\n\t\t\t\tmappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(\n\t\t\t\t\tCommandClasses[\"Binary Sensor\"],\n\t\t\t\t);\n\t\t\t\tif (typeof command.currentValue === \"number\") {\n\t\t\t\t\tif (mappedTargetCC) {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\"treating BasicCC::Report as a BinarySensorCC::Report\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tmappedTargetCC.setMappedBasicValue(\n\t\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\t\tcommand.currentValue,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\"cannot treat BasicCC::Report as a BinarySensorCC::Report, because the Binary Sensor CC is not supported\",\n\t\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"cannot map BasicCC::Report to a different CC, because the current value is unknown\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\tbasicReportMapping === \"auto\" || basicReportMapping === false\n\t\t\t) {\n\t\t\t\t// Try to set the mapped value on the target CC\n\t\t\t\tconst didSetMappedValue =\n\t\t\t\t\ttypeof command.currentValue === \"number\"\n\t\t\t\t\t// ... unless forbidden\n\t\t\t\t\t&& basicReportMapping === \"auto\"\n\t\t\t\t\t&& mappedTargetCC?.setMappedBasicValue(\n\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\tcommand.currentValue,\n\t\t\t\t\t);\n\n\t\t\t\t// Otherwise fall back to setting it ourselves\n\t\t\t\tif (!didSetMappedValue) {\n\t\t\t\t\t// Store the value in the value DB now\n\t\t\t\t\tcommand.persistValues(this.driver);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (command instanceof BasicCCSet) {\n\t\t\t// By default, map Basic CC Set to Basic CC Report, unless stated otherwise in a config file\n\t\t\tconst basicSetMapping = this.deviceConfig?.compat?.mapBasicSet\n\t\t\t\t?? \"report\";\n\n\t\t\tif (basicSetMapping === \"event\") {\n\t\t\t\t// Treat BasicCCSet as value events if desired\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\tmessage: \"treating BasicCC::Set as a value event\",\n\t\t\t\t});\n\t\t\t\tthis._valueDB.setValue(\n\t\t\t\t\tBasicCCValues.compatEvent.endpoint(\n\t\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t\t),\n\t\t\t\t\tcommand.targetValue,\n\t\t\t\t\t{\n\t\t\t\t\t\tstateful: false,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} else if (basicSetMapping === \"Binary Sensor\") {\n\t\t\t\t// Treat the Set command as a BinarySensorCC Report, regardless of the device class\n\t\t\t\tmappedTargetCC = sourceEndpoint.createCCInstanceUnsafe(\n\t\t\t\t\tCommandClasses[\"Binary Sensor\"],\n\t\t\t\t);\n\t\t\t\tif (mappedTargetCC) {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"treating BasicCC::Set as a BinarySensorCC::Report\",\n\t\t\t\t\t});\n\t\t\t\t\tmappedTargetCC.setMappedBasicValue(\n\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\tcommand.targetValue,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"cannot treat BasicCC::Set as a BinarySensorCC::Report, because the Binary Sensor CC is not supported\",\n\t\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\t!this.deviceConfig?.compat?.mapBasicSet\n\t\t\t\t&& !!(command.encapsulationFlags\n\t\t\t\t\t& EncapsulationFlags.Supervision)\n\t\t\t) {\n\t\t\t\t// A controller MUST not support Basic CC per the specifications. While we can interpret its contents,\n\t\t\t\t// we MUST respond to supervised Basic CC Set with \"no support\".\n\t\t\t\t// All known devices that use BasicCCSet for reporting send it unsupervised, so this should be safe to do.\n\t\t\t\tif (\n\t\t\t\t\tcommand.encapsulationFlags & EncapsulationFlags.Supervision\n\t\t\t\t) {\n\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\"Basic CC is not supported\",\n\t\t\t\t\t\tZWaveErrorCodes.CC_NotSupported,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\tbasicSetMapping === \"auto\" || basicSetMapping === \"report\"\n\t\t\t) {\n\t\t\t\t// Some devices send their current state using BasicCCSet to their associations\n\t\t\t\t// instead of using reports. We still interpret them like reports\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\t\tmessage: \"treating BasicCC::Set as a report\",\n\t\t\t\t});\n\n\t\t\t\t// In \"auto\" mode, try to set the mapped value on the target CC first\n\t\t\t\tconst didSetMappedValue = basicSetMapping === \"auto\"\n\t\t\t\t\t&& !!mappedTargetCC?.setMappedBasicValue(\n\t\t\t\t\t\tthis.driver,\n\t\t\t\t\t\tcommand.targetValue,\n\t\t\t\t\t);\n\n\t\t\t\t// Otherwise handle the command ourselves\n\t\t\t\tif (!didSetMappedValue) {\n\t\t\t\t\t// Basic Set commands cannot store their value automatically, so store the values manually\n\t\t\t\t\tthis._valueDB.setValue(\n\t\t\t\t\t\tBasicCCValues.currentValue.endpoint(\n\t\t\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tcommand.targetValue,\n\t\t\t\t\t);\n\t\t\t\t\t// Since the node sent us a Basic Set, we are sure that it is at least controlled\n\t\t\t\t\t// Add it to the support list, so the information lands in the network cache\n\t\t\t\t\tif (!sourceEndpoint.controlsCC(CommandClasses.Basic)) {\n\t\t\t\t\t\tsourceEndpoint.addCC(CommandClasses.Basic, {\n\t\t\t\t\t\t\tisControlled: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Handles the receipt of a MultilevelCC Set or Report */\n\tprivate handleMultilevelSwitchCommand(command: MultilevelSwitchCC): void {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex ?? 0) ?? this;\n\n\t\tif (command instanceof MultilevelSwitchCCSet) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\tmessage: \"treating MultiLevelSwitchCCSet::Set as a value event\",\n\t\t\t});\n\t\t\tthis._valueDB.setValue(\n\t\t\t\tMultilevelSwitchCCValues.compatEvent.endpoint(\n\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t),\n\t\t\t\tcommand.targetValue,\n\t\t\t\t{\n\t\t\t\t\tstateful: false,\n\t\t\t\t},\n\t\t\t);\n\t\t} else if (command instanceof MultilevelSwitchCCStartLevelChange) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\tmessage:\n\t\t\t\t\t\"treating MultilevelSwitchCC::StartLevelChange as a notification\",\n\t\t\t});\n\t\t\tthis.emit(\n\t\t\t\t\"notification\",\n\t\t\t\tendpoint,\n\t\t\t\tCommandClasses[\"Multilevel Switch\"],\n\t\t\t\t{\n\t\t\t\t\teventType: MultilevelSwitchCommand.StartLevelChange,\n\t\t\t\t\teventTypeLabel: \"Start level change\",\n\t\t\t\t\tdirection: command.direction,\n\t\t\t\t},\n\t\t\t);\n\t\t} else if (command instanceof MultilevelSwitchCCStopLevelChange) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\tmessage:\n\t\t\t\t\t\"treating MultilevelSwitchCC::StopLevelChange as a notification\",\n\t\t\t});\n\t\t\tthis.emit(\n\t\t\t\t\"notification\",\n\t\t\t\tendpoint,\n\t\t\t\tCommandClasses[\"Multilevel Switch\"],\n\t\t\t\t{\n\t\t\t\t\teventType: MultilevelSwitchCommand.StopLevelChange,\n\t\t\t\t\teventTypeLabel: \"Stop level change\",\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate handleBinarySwitchCommand(command: BinarySwitchCC): void {\n\t\t// Treat BinarySwitchCCSet as a report if desired\n\t\tif (\n\t\t\tcommand instanceof BinarySwitchCCSet\n\t\t\t&& this._deviceConfig?.compat?.treatSetAsReport?.has(\n\t\t\t\tcommand.constructor.name,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\tmessage: \"treating BinarySwitchCC::Set as a report\",\n\t\t\t});\n\t\t\tthis._valueDB.setValue(\n\t\t\t\tBinarySwitchCCValues.currentValue.endpoint(\n\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t),\n\t\t\t\tcommand.targetValue,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate handleThermostatModeCommand(command: ThermostatModeCC): void {\n\t\t// Treat ThermostatModeCCSet as a report if desired\n\t\tif (\n\t\t\tcommand instanceof ThermostatModeCCSet\n\t\t\t&& this._deviceConfig?.compat?.treatSetAsReport?.has(\n\t\t\t\tcommand.constructor.name,\n\t\t\t)\n\t\t) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tendpoint: command.endpointIndex,\n\t\t\t\tmessage: \"treating ThermostatModeCC::Set as a report\",\n\t\t\t});\n\t\t\tthis._valueDB.setValue(\n\t\t\t\tThermostatModeCCValues.thermostatMode.endpoint(\n\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t),\n\t\t\t\tcommand.mode,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async handleZWavePlusGet(command: ZWavePlusCCGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tawait endpoint\n\t\t\t.createAPI(CommandClasses[\"Z-Wave Plus Info\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t})\n\t\t\t.sendReport({\n\t\t\t\tzwavePlusVersion: 2,\n\t\t\t\troleType: ZWavePlusRoleType.CentralStaticController,\n\t\t\t\tnodeType: ZWavePlusNodeType.Node,\n\t\t\t\tinstallerIcon: this.driver.options.vendor?.installerIcon\n\t\t\t\t\t?? 0x0500, // Generic Gateway\n\t\t\t\tuserIcon: this.driver.options.vendor?.userIcon ?? 0x0500, // Generic Gateway\n\t\t\t});\n\t}\n\n\tprivate async handleVersionGet(command: VersionCCGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Version, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tconst firmwareVersion1 = semverParse(libVersion, { loose: true })!;\n\n\t\tawait api.sendReport({\n\t\t\tlibraryType: ZWaveLibraryTypes[\"Static Controller\"],\n\t\t\tprotocolVersion: this.driver.controller.protocolVersion!,\n\t\t\tfirmwareVersions: [\n\t\t\t\t// Firmware 0 is the Z-Wave chip firmware\n\t\t\t\tthis.driver.controller.firmwareVersion!,\n\t\t\t\t// Firmware 1 is Z-Wave JS itself\n\t\t\t\t`${firmwareVersion1.major}.${firmwareVersion1.minor}.${firmwareVersion1.patch}`,\n\t\t\t],\n\t\t\thardwareVersion: this.driver.options.vendor?.hardwareVersion,\n\t\t});\n\t}\n\n\tprivate async handleVersionCommandClassGet(\n\t\tcommand: VersionCCCommandClassGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Version, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.reportCCVersion(command.requestedCC);\n\t}\n\n\tprivate async handleVersionCapabilitiesGet(\n\t\tcommand: VersionCCCapabilitiesGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Version, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.reportCapabilities();\n\t}\n\n\tprivate async handleManufacturerSpecificGet(\n\t\tcommand: ManufacturerSpecificCCGet,\n\t): Promise<void> {\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = this\n\t\t\t.createAPI(CommandClasses[\"Manufacturer Specific\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.sendReport({\n\t\t\tmanufacturerId: this.driver.options.vendor?.manufacturerId\n\t\t\t\t?? 0xffff, // Reserved manufacturer ID, definitely invalid!\n\t\t\tproductType: this.driver.options.vendor?.productType ?? 0xffff,\n\t\t\tproductId: this.driver.options.vendor?.productId ?? 0xffff,\n\t\t});\n\t}\n\n\tprivate async handleAGINameGet(\n\t\tcommand: AssociationGroupInfoCCNameGet,\n\t): Promise<void> {\n\t\tif (command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group\n\t\t\treturn;\n\t\t}\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses[\"Association Group Information\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.reportGroupName(1, \"Lifeline\");\n\t}\n\n\tprivate async handleAGIInfoGet(\n\t\tcommand: AssociationGroupInfoCCInfoGet,\n\t): Promise<void> {\n\t\tif (!command.listMode && command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group\n\t\t\treturn;\n\t\t}\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses[\"Association Group Information\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.reportGroupInfo({\n\t\t\tisListMode: command.listMode ?? false,\n\t\t\thasDynamicInfo: false,\n\t\t\tgroups: [\n\t\t\t\t{\n\t\t\t\t\tgroupId: 1,\n\t\t\t\t\teventCode: 0, // ignored anyways\n\t\t\t\t\tprofile: AssociationGroupInfoProfile[\"General: Lifeline\"],\n\t\t\t\t\tmode: 0, // ignored anyways\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\tprivate async handleAGICommandListGet(\n\t\tcommand: AssociationGroupInfoCCCommandListGet,\n\t): Promise<void> {\n\t\tif (command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group\n\t\t\treturn;\n\t\t}\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses[\"Association Group Information\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tawait api.reportCommands(\n\t\t\tcommand.groupId,\n\t\t\tnew Map([\n\t\t\t\t[\n\t\t\t\t\tCommandClasses[\"Device Reset Locally\"],\n\t\t\t\t\t[DeviceResetLocallyCommand.Notification],\n\t\t\t\t],\n\t\t\t]),\n\t\t);\n\t}\n\n\tprivate async handleAssociationSupportedGroupingsGet(\n\t\tcommand: AssociationCCSupportedGroupingsGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Association, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\t// We only \"support\" the lifeline group\n\t\tawait api.reportGroupCount(1);\n\t}\n\n\tprivate async handleAssociationGet(\n\t\tcommand: AssociationCCGet,\n\t): Promise<void> {\n\t\t// We only \"support\" the lifeline group\n\t\tconst groupId = 1;\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Association, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tconst nodeIds =\n\t\t\tthis.driver.controller.associations.filter((a) =>\n\t\t\t\ta.endpoint == undefined\n\t\t\t)\n\t\t\t\t.map((a) => a.nodeId) ?? [];\n\n\t\tawait api.sendReport({\n\t\t\tgroupId,\n\t\t\tmaxNodes: MAX_ASSOCIATIONS,\n\t\t\tnodeIds,\n\t\t\treportsToFollow: 0,\n\t\t});\n\t}\n\n\tprivate handleAssociationSet(command: AssociationCCSet): void {\n\t\tif (command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group.\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Association group ${command.groupId} is not supported.`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\n\t\t// Ignore associations that already exist\n\t\tconst newAssociations = command.nodeIds.filter((newNodeId) =>\n\t\t\t!this.driver.controller.associations.some(\n\t\t\t\t({ nodeId, endpoint }) =>\n\t\t\t\t\tendpoint === undefined && nodeId === newNodeId,\n\t\t\t)\n\t\t).map((nodeId) => ({ nodeId }));\n\n\t\tconst associations = [...this.driver.controller.associations];\n\t\tassociations.push(...newAssociations);\n\n\t\t// Report error if the association group is already full\n\t\tif (associations.length > MAX_ASSOCIATIONS) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Association group ${command.groupId} is full`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\t\tthis.driver.controller.associations = associations;\n\t}\n\n\tprivate handleAssociationRemove(command: AssociationCCRemove): void {\n\t\t// Allow accessing the lifeline group or all groups (which is the same)\n\t\tif (!!command.groupId && command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group\n\t\t\treturn;\n\t\t}\n\n\t\tif (!command.nodeIds?.length) {\n\t\t\t// clear\n\t\t\tthis.driver.controller.associations = [];\n\t\t} else {\n\t\t\tthis.driver.controller.associations = this.driver.controller\n\t\t\t\t.associations.filter(\n\t\t\t\t\t({ nodeId, endpoint }) =>\n\t\t\t\t\t\tendpoint === undefined\n\t\t\t\t\t\t&& !command.nodeIds!.includes(nodeId),\n\t\t\t\t);\n\t\t}\n\t}\n\n\tprivate async handleAssociationSpecificGroupGet(\n\t\tcommand: AssociationCCSpecificGroupGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Association, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\t// We don't support this feature.\n\t\t// It is RECOMMENDED that the value 0 is returned by non-supporting devices.\n\t\tawait api.reportSpecificGroup(0);\n\t}\n\n\tprivate async handleMultiChannelAssociationSupportedGroupingsGet(\n\t\tcommand: MultiChannelAssociationCCSupportedGroupingsGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses[\"Multi Channel Association\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\t// We only \"support\" the lifeline group\n\t\tawait api.reportGroupCount(1);\n\t}\n\n\tprivate async handleMultiChannelAssociationGet(\n\t\tcommand: MultiChannelAssociationCCGet,\n\t): Promise<void> {\n\t\t// We only \"support\" the lifeline group\n\t\tconst groupId = 1;\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses[\"Multi Channel Association\"], false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tconst nodeIds =\n\t\t\tthis.driver.controller.associations.filter((a) =>\n\t\t\t\ta.endpoint == undefined\n\t\t\t)\n\t\t\t\t.map((a) => a.nodeId) ?? [];\n\t\tconst endpoints =\n\t\t\tthis.driver.controller.associations.filter((a) =>\n\t\t\t\ta.endpoint != undefined\n\t\t\t)\n\t\t\t\t.map(({ nodeId, endpoint }) => ({\n\t\t\t\t\tnodeId,\n\t\t\t\t\tendpoint: endpoint!,\n\t\t\t\t}))\n\t\t\t\t?? [];\n\n\t\tawait api.sendReport({\n\t\t\tgroupId,\n\t\t\tmaxNodes: MAX_ASSOCIATIONS,\n\t\t\tnodeIds,\n\t\t\tendpoints,\n\t\t\treportsToFollow: 0,\n\t\t});\n\t}\n\n\tprivate handleMultiChannelAssociationSet(\n\t\tcommand: MultiChannelAssociationCCSet,\n\t): void {\n\t\tif (command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group.\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Multi Channel Association group ${command.groupId} is not supported.`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\n\t\t// Ignore associations that already exists\n\t\tconst newNodeIdAssociations = command.nodeIds.filter((newNodeId) =>\n\t\t\t!this.driver.controller.associations.some(\n\t\t\t\t({ nodeId, endpoint }) =>\n\t\t\t\t\tendpoint === undefined && nodeId === newNodeId,\n\t\t\t)\n\t\t).map((nodeId) => ({ nodeId }));\n\t\tconst newEndpointAssociations = command.endpoints.flatMap(\n\t\t\t({ nodeId, endpoint }) => {\n\t\t\t\tif (typeof endpoint === \"number\") {\n\t\t\t\t\treturn { nodeId, endpoint };\n\t\t\t\t} else {\n\t\t\t\t\treturn endpoint.map((e) => ({ nodeId, endpoint: e }));\n\t\t\t\t}\n\t\t\t},\n\t\t).filter(({ nodeId: newNodeId, endpoint: newEndpoint }) =>\n\t\t\t!this.driver.controller.associations.some(({ nodeId, endpoint }) =>\n\t\t\t\tnodeId === newNodeId && endpoint === newEndpoint\n\t\t\t)\n\t\t);\n\n\t\tconst associations = [...this.driver.controller.associations];\n\t\tassociations.push(...newNodeIdAssociations, ...newEndpointAssociations);\n\n\t\t// Report error if the association group is already full\n\t\tif (associations.length > MAX_ASSOCIATIONS) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Multi Channel Association group ${command.groupId} is full`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\n\t\tthis.driver.controller.associations = associations.slice(\n\t\t\t0,\n\t\t\tMAX_ASSOCIATIONS,\n\t\t);\n\t}\n\n\tprivate handleMultiChannelAssociationRemove(\n\t\tcommand: MultiChannelAssociationCCRemove,\n\t): void {\n\t\t// Allow accessing the lifeline group or all groups (which is the same)\n\t\tif (!!command.groupId && command.groupId !== 1) {\n\t\t\t// We only \"support\" the lifeline group\n\t\t\treturn;\n\t\t}\n\n\t\tif (!command.nodeIds?.length && !command.endpoints?.length) {\n\t\t\t// Clear all associations\n\t\t\tthis.driver.controller.associations = [];\n\t\t} else {\n\t\t\tlet associations = [...this.driver.controller.associations];\n\t\t\tif (command.nodeIds?.length) {\n\t\t\t\tassociations = associations.filter(\n\t\t\t\t\t({ nodeId, endpoint }) =>\n\t\t\t\t\t\tendpoint === undefined\n\t\t\t\t\t\t&& !command.nodeIds!.includes(nodeId),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (command.endpoints?.length) {\n\t\t\t\tassociations = associations.filter(\n\t\t\t\t\t({ nodeId, endpoint }) =>\n\t\t\t\t\t\t!command.endpoints!.some((dest) =>\n\t\t\t\t\t\t\tdest.nodeId === nodeId && dest.endpoint === endpoint\n\t\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.driver.controller.associations = associations;\n\t\t}\n\t}\n\n\tprivate handleIndicatorSupportedGet(\n\t\tcommand: IndicatorCCSupportedGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Indicator, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tswitch (command.indicatorId) {\n\t\t\tcase 0:\n\t\t\t// 0 must be answered with the first supported indicator ID.\n\t\t\t// We only support identify (0x50)\n\t\t\tcase 0x50:\n\t\t\t\t// Identify\n\t\t\t\treturn api.reportSupported(0x50, [0x03, 0x04, 0x05], 0);\n\t\t\tdefault:\n\t\t\t\t// A supporting node receiving a non-zero Indicator ID that is\n\t\t\t\t// not supported MUST set all fields to 0x00 in the returned response.\n\t\t\t\treturn api.reportSupported(0, [], 0);\n\t\t}\n\t}\n\n\tprivate handleIndicatorSet(command: IndicatorCCSet): void {\n\t\t// We only support \"identify\"\n\t\tif (command.values?.length !== 3) return;\n\t\tconst [v1, v2, v3] = command.values;\n\t\tif (v1.indicatorId !== 0x50 || v1.propertyId !== 0x03) return;\n\t\tif (v2.indicatorId !== 0x50 || v2.propertyId !== 0x04) return;\n\t\tif (v3.indicatorId !== 0x50 || v3.propertyId !== 0x05) return;\n\n\t\t// This isn't really sane, but since we only support a single indicator, it's fine\n\t\tconst store = this.driver.controller.indicatorValues;\n\t\tstore.set(0x50, [v1, v2, v3]);\n\n\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\tmessage: \"Received identify command\",\n\t\t\tdirection: \"inbound\",\n\t\t});\n\n\t\tthis.driver.controller.emit(\"identify\", this);\n\t}\n\n\tprivate async handleIndicatorGet(command: IndicatorCCGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Indicator, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\t// We only support \"identify\"\n\t\tif (command.indicatorId === 0x50) {\n\t\t\tconst values = this.driver.controller.indicatorValues.get(0x50) ?? [\n\t\t\t\t{ indicatorId: 0x50, propertyId: 0x03, value: 0 },\n\t\t\t\t{ indicatorId: 0x50, propertyId: 0x04, value: 0 },\n\t\t\t\t{ indicatorId: 0x50, propertyId: 0x05, value: 0 },\n\t\t\t];\n\t\t\tawait api.sendReport({ values });\n\t\t} else if (typeof command.indicatorId === \"number\") {\n\t\t\t// V2+ report\n\t\t\tawait api.sendReport({\n\t\t\t\tvalues: [\n\t\t\t\t\t{\n\t\t\t\t\t\tindicatorId: command.indicatorId,\n\t\t\t\t\t\tpropertyId: 0,\n\t\t\t\t\t\tvalue: 0,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t} else {\n\t\t\t// V1+ report\n\t\t\tawait api.sendReport({ value: 0 });\n\t\t}\n\t}\n\n\tprivate async handleIndicatorDescriptionGet(\n\t\tcommand: IndicatorCCDescriptionGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Indicator, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\t// We only support \"identify\" (0x50) and requests for indicators outside the 0x80...0x9f range\n\t\t// MUST return an Indicator Description Report with the Description Length set to 0.\n\t\t// So we can just always do that.\n\t\tawait api.reportDescription(command.indicatorId, \"\");\n\t}\n\n\tprivate handlePowerlevelSet(command: PowerlevelCCSet): void {\n\t\t// Check if the powerlevel is valid\n\t\tif (!(command.powerlevel in Powerlevel)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Invalid powerlevel ${command.powerlevel}.`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\n\t\t// CC:0073.01.01.11.001: A supporting node MAY decide not to change its actual Tx configuration.\n\t\t// In any case, the value received in this Command MUST be returned in a Powerlevel Report Command\n\t\t// in response to a Powerlevel Get Command as if the power setting was accepted for the indicated duration.\n\t\tthis.driver.controller.powerlevel = {\n\t\t\tpowerlevel: command.powerlevel,\n\t\t\tuntil: command.timeout\n\t\t\t\t? new Date(Date.now() + command.timeout * 1000)\n\t\t\t\t: new Date(),\n\t\t};\n\t}\n\n\tprivate async handlePowerlevelGet(command: PowerlevelCCGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Powerlevel, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tconst { powerlevel, until } = this.driver.controller.powerlevel;\n\n\t\tif (\n\t\t\t// Setting elapsed\n\t\t\tuntil.getTime() < Date.now()\n\t\t\t// or it is already set to normal power\n\t\t\t|| powerlevel === Powerlevel[\"Normal Power\"]\n\t\t) {\n\t\t\tawait api.reportPowerlevel({\n\t\t\t\tpowerlevel: Powerlevel[\"Normal Power\"],\n\t\t\t});\n\t\t} else {\n\t\t\tconst timeoutSeconds = Math.max(\n\t\t\t\t0,\n\t\t\t\tMath.min(\n\t\t\t\t\tMath.round((until.getTime() - Date.now()) / 1000),\n\t\t\t\t\t255,\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tawait api.reportPowerlevel({\n\t\t\t\tpowerlevel,\n\t\t\t\ttimeout: timeoutSeconds,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async handlePowerlevelTestNodeSet(\n\t\tcommand: PowerlevelCCTestNodeSet,\n\t): Promise<void> {\n\t\t// Check if the powerlevel is valid\n\t\tif (!(command.powerlevel in Powerlevel)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Invalid powerlevel ${command.powerlevel}.`,\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t} else if (command.testFrameCount < 1) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"testFrameCount must be at least 1\",\n\t\t\t\tZWaveErrorCodes.CC_OperationFailed,\n\t\t\t);\n\t\t}\n\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Powerlevel, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\ttry {\n\t\t\tconst acknowledgedFrames = await this.driver.sendNOPPowerFrames(\n\t\t\t\tcommand.testNodeId,\n\t\t\t\tcommand.powerlevel,\n\t\t\t\tcommand.testFrameCount,\n\t\t\t);\n\t\t\t// Test results are in, send report\n\t\t\tvoid api.sendNodeTestReport({\n\t\t\t\tstatus: acknowledgedFrames > 0\n\t\t\t\t\t? PowerlevelTestStatus.Success\n\t\t\t\t\t: PowerlevelTestStatus.Failed,\n\t\t\t\ttestNodeId: command.testNodeId,\n\t\t\t\tacknowledgedFrames,\n\t\t\t}).catch(noop);\n\t\t} catch {\n\t\t\t// Test failed for some reason (e.g. invalid node)\n\t\t\tvoid api.sendNodeTestReport({\n\t\t\t\tstatus: PowerlevelTestStatus.Failed,\n\t\t\t\ttestNodeId: command.testNodeId,\n\t\t\t\tacknowledgedFrames: 0,\n\t\t\t}).catch(noop);\n\t\t}\n\t}\n\n\tprivate async handlePowerlevelTestNodeGet(\n\t\tcommand: PowerlevelCCTestNodeGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t// Using the commandClasses property would throw in that case\n\t\tconst api = endpoint\n\t\t\t.createAPI(CommandClasses.Powerlevel, false)\n\t\t\t.withOptions({\n\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t});\n\n\t\tconst status = this.driver.getNOPPowerTestStatus();\n\n\t\tif (status) {\n\t\t\tawait api.sendNodeTestReport({\n\t\t\t\tstatus: status.inProgress\n\t\t\t\t\t? PowerlevelTestStatus[\"In Progress\"]\n\t\t\t\t\t: status.acknowledgedFrames > 0\n\t\t\t\t\t? PowerlevelTestStatus.Success\n\t\t\t\t\t: PowerlevelTestStatus.Failed,\n\t\t\t\t...status,\n\t\t\t});\n\t\t} else {\n\t\t\t// No test was done\n\t\t\tawait api.sendNodeTestReport({\n\t\t\t\tstatus: PowerlevelTestStatus.Success,\n\t\t\t\ttestNodeId: 0,\n\t\t\t\tacknowledgedFrames: 0,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate handlePowerlevelTestNodeReport(\n\t\tcommand: PowerlevelCCTestNodeReport,\n\t): void {\n\t\t// Notify listeners\n\t\tthis.emit(\n\t\t\t\"notification\",\n\t\t\tthis,\n\t\t\tCommandClasses.Powerlevel,\n\t\t\tpick(command, [\"testNodeId\", \"status\", \"acknowledgedFrames\"]),\n\t\t);\n\t}\n\n\tprivate async handleSecurityCommandsSupportedGet(\n\t\tcommand: SecurityCCCommandsSupportedGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\tif (this.getHighestSecurityClass() === 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\tprivate async handleSecurity2CommandsSupportedGet(\n\t\tcommand: Security2CCCommandsSupportedGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\tconst highestSecurityClass = this.getHighestSecurityClass();\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\tprivate handleDeviceResetLocallyNotification(\n\t\tcmd: DeviceResetLocallyCCNotification,\n\t): void {\n\t\tif (cmd.endpointIndex !== 0) {\n\t\t\t// The notification MUST be issued by the root device, otherwise it is likely a corrupted message\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Received reset locally notification from non-root endpoint - ignoring it...`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// Handling this command can take a few seconds and require communication with the node.\n\t\t// If it was received with Supervision, we need to acknowledge it immediately. Therefore\n\t\t// defer the handling half a second.\n\n\t\tsetTimeout(async () => {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: `The node was reset locally, removing it`,\n\t\t\t\tdirection: \"inbound\",\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait this.driver.controller.removeFailedNodeInternal(\n\t\t\t\t\tthis.id,\n\t\t\t\t\tRemoveNodeReason.Reset,\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `removing the node failed: ${\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\tlevel: \"error\",\n\t\t\t\t});\n\t\t\t}\n\t\t}, 500);\n\t}\n\n\t/**\n\t * Allows automatically resetting notification values to idle if the node does not do it itself\n\t */\n\tprivate notificationIdleTimeouts = new Map<string, NodeJS.Timeout>();\n\t/** Schedules a notification value to be reset */\n\tprivate scheduleNotificationIdleReset(\n\t\tvalueId: ValueID,\n\t\thandler: () => void,\n\t): void {\n\t\tthis.clearNotificationIdleReset(valueId);\n\t\tconst key = valueIdToString(valueId);\n\t\tthis.notificationIdleTimeouts.set(\n\t\t\tkey,\n\t\t\t// Unref'ing long running timeouts allows to quit the application before the timeout elapses\n\t\t\tsetTimeout(handler, 5 * 60 * 1000 /* 5 minutes */).unref(),\n\t\t);\n\t}\n\n\t/** Removes a scheduled notification reset */\n\tprivate clearNotificationIdleReset(valueId: ValueID): void {\n\t\tconst key = valueIdToString(valueId);\n\t\tif (this.notificationIdleTimeouts.has(key)) {\n\t\t\tclearTimeout(this.notificationIdleTimeouts.get(key));\n\t\t\tthis.notificationIdleTimeouts.delete(key);\n\t\t}\n\t}\n\n\t// Fallback for V2 notifications that don't allow us to predefine the metadata during the interview.\n\t// Instead of defining useless values for each possible notification event, we build the metadata on demand\n\tprivate extendNotificationValueMetadata(\n\t\tvalueId: ValueID,\n\t\tnotification: Notification,\n\t\tvalueConfig: NotificationState,\n\t) {\n\t\tconst ccVersion = this.driver.getSupportedCCVersion(\n\t\t\tCommandClasses.Notification,\n\t\t\tthis.id,\n\t\t\tthis.index,\n\t\t);\n\t\tif (ccVersion === 2 || !this.valueDB.hasMetadata(valueId)) {\n\t\t\tconst metadata = getNotificationValueMetadata(\n\t\t\t\tthis.valueDB.getMetadata(valueId) as\n\t\t\t\t\t| ValueMetadataNumeric\n\t\t\t\t\t| undefined,\n\t\t\t\tnotification,\n\t\t\t\tvalueConfig,\n\t\t\t);\n\t\t\tthis.valueDB.setMetadata(valueId, metadata);\n\t\t}\n\t}\n\n\t/**\n\t * Manually resets a single notification value to idle.\n\t */\n\tpublic manuallyIdleNotificationValue(valueId: ValueID): void;\n\n\tpublic manuallyIdleNotificationValue(\n\t\tnotificationType: number,\n\t\tprevValue: number,\n\t\tendpointIndex?: number,\n\t): void;\n\n\tpublic manuallyIdleNotificationValue(\n\t\tnotificationTypeOrValueID: number | ValueID,\n\t\tprevValue?: number,\n\t\tendpointIndex: number = 0,\n\t): void {\n\t\tlet notificationType: number | undefined;\n\t\tif (typeof notificationTypeOrValueID === \"number\") {\n\t\t\tnotificationType = notificationTypeOrValueID;\n\t\t} else {\n\t\t\tnotificationType = this.valueDB.getMetadata(\n\t\t\t\tnotificationTypeOrValueID,\n\t\t\t)?.ccSpecific?.notificationType as number | undefined;\n\t\t\tif (notificationType === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprevValue = this.valueDB.getValue(notificationTypeOrValueID);\n\t\t\tendpointIndex = notificationTypeOrValueID.endpoint ?? 0;\n\t\t}\n\n\t\tif (\n\t\t\t!this.getEndpoint(endpointIndex)?.supportsCC(\n\t\t\t\tCommandClasses.Notification,\n\t\t\t)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst notification = getNotification(notificationType);\n\t\tif (!notification) return;\n\n\t\treturn this.manuallyIdleNotificationValueInternal(\n\t\t\tnotification,\n\t\t\tprevValue!,\n\t\t\tendpointIndex,\n\t\t);\n\t}\n\n\t/** Manually resets a single notification value to idle */\n\tprivate manuallyIdleNotificationValueInternal(\n\t\tnotification: Notification,\n\t\tprevValue: number,\n\t\tendpointIndex: number,\n\t): void {\n\t\tconst valueConfig = getNotificationValue(notification, prevValue);\n\t\t// Only known variables may be reset to idle\n\t\tif (!valueConfig || valueConfig.type !== \"state\") return;\n\t\t// Some properties may not be reset to idle\n\t\tif (!valueConfig.idle) return;\n\n\t\tconst notificationName = notification.name;\n\t\tconst variableName = valueConfig.variableName;\n\t\tconst valueId = NotificationCCValues.notificationVariable(\n\t\t\tnotificationName,\n\t\t\tvariableName,\n\t\t).endpoint(endpointIndex);\n\n\t\t// Make sure the value is actually set to the previous value\n\t\tif (this.valueDB.getValue(valueId) !== prevValue) return;\n\n\t\t// Since the node has reset the notification itself, we don't need the idle reset\n\t\tthis.clearNotificationIdleReset(valueId);\n\t\tthis.extendNotificationValueMetadata(\n\t\t\tvalueId,\n\t\t\tnotification,\n\t\t\tvalueConfig,\n\t\t);\n\t\tthis.valueDB.setValue(valueId, 0 /* idle */);\n\t}\n\n\t/**\n\t * Handles the receipt of a Notification Report\n\t */\n\tprivate handleNotificationReport(command: NotificationCCReport): void {\n\t\tif (command.notificationType == undefined) {\n\t\t\tif (command.alarmType == undefined) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `received unsupported notification ${\n\t\t\t\t\t\tstringify(\n\t\t\t\t\t\t\tcommand,\n\t\t\t\t\t\t)\n\t\t\t\t\t}`,\n\t\t\t\t\tdirection: \"inbound\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst ccVersion = getEffectiveCCVersion(this.driver, command);\n\n\t\t// Look up the received notification in the config\n\t\tconst notification = getNotification(command.notificationType);\n\n\t\tif (notification) {\n\t\t\t// This is a notification (status or event) with a known type\n\t\t\tconst notificationName = notification.name;\n\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage:\n\t\t\t\t\t`[handleNotificationReport] notificationName: ${notificationName}`,\n\t\t\t\tlevel: \"silly\",\n\t\t\t});\n\n\t\t\t/** Returns a single notification state to idle */\n\t\t\tconst setStateIdle = (prevValue: number): void => {\n\t\t\t\tthis.manuallyIdleNotificationValueInternal(\n\t\t\t\t\tnotification,\n\t\t\t\t\tprevValue,\n\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t);\n\t\t\t};\n\n\t\t\tconst setUnknownStateIdle = (prevValue?: number) => {\n\t\t\t\t// Find the value for the unknown notification variable bucket\n\t\t\t\tconst unknownNotificationVariableValueId = NotificationCCValues\n\t\t\t\t\t.unknownNotificationVariable(\n\t\t\t\t\t\tcommand.notificationType!,\n\t\t\t\t\t\tnotificationName,\n\t\t\t\t\t).endpoint(command.endpointIndex);\n\t\t\t\tconst currentValue = this.valueDB.getValue(\n\t\t\t\t\tunknownNotificationVariableValueId,\n\t\t\t\t);\n\t\t\t\t// ... and if it exists\n\t\t\t\tif (currentValue == undefined) return;\n\t\t\t\t// ... reset it to idle\n\t\t\t\tif (prevValue == undefined || currentValue === prevValue) {\n\t\t\t\t\tthis.valueDB.setValue(\n\t\t\t\t\t\tunknownNotificationVariableValueId,\n\t\t\t\t\t\t0, /* idle */\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst value = command.notificationEvent!;\n\t\t\tif (value === 0) {\n\t\t\t\t// Generic idle notification, this contains a value to be reset\n\t\t\t\tif (\n\t\t\t\t\tisUint8Array(command.eventParameters)\n\t\t\t\t\t&& command.eventParameters.length\n\t\t\t\t) {\n\t\t\t\t\t// The target value is the first byte of the event parameters\n\t\t\t\t\tsetStateIdle(command.eventParameters[0]);\n\t\t\t\t\tsetUnknownStateIdle(command.eventParameters[0]);\n\t\t\t\t} else {\n\t\t\t\t\t// Reset all values to idle\n\t\t\t\t\tconst nonIdleValues = this.valueDB\n\t\t\t\t\t\t.getValues(CommandClasses.Notification)\n\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t(v) =>\n\t\t\t\t\t\t\t\t(v.endpoint || 0) === command.endpointIndex\n\t\t\t\t\t\t\t\t&& v.property === notificationName\n\t\t\t\t\t\t\t\t&& typeof v.value === \"number\"\n\t\t\t\t\t\t\t\t&& v.value !== 0,\n\t\t\t\t\t\t);\n\t\t\t\t\tfor (const v of nonIdleValues) {\n\t\t\t\t\t\tsetStateIdle(v.value as number);\n\t\t\t\t\t}\n\t\t\t\t\tsetUnknownStateIdle();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Find out which property we need to update\n\t\t\tconst valueConfig = getNotificationValue(notification, value);\n\n\t\t\tif (valueConfig) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `[handleNotificationReport] valueConfig:\n label: ${valueConfig.label}\n ${\n\t\t\t\t\t\tvalueConfig.type === \"event\"\n\t\t\t\t\t\t\t? \"type: event\"\n\t\t\t\t\t\t\t: `type: state\n variableName: ${valueConfig.variableName}`\n\t\t\t\t\t}`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`[handleNotificationReport] valueConfig: undefined`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Perform some heuristics on the known notification\n\t\t\tthis.handleKnownNotification(command);\n\n\t\t\tlet allowIdleReset: boolean;\n\t\t\tif (!valueConfig) {\n\t\t\t\t// We don't know what this notification refers to, so we don't force a reset\n\t\t\t\tallowIdleReset = false;\n\t\t\t} else if (valueConfig.type === \"state\") {\n\t\t\t\tallowIdleReset = valueConfig.idle;\n\t\t\t} else {\n\t\t\t\t// This is an event\n\t\t\t\tconst endpoint = this.getEndpoint(command.endpointIndex)\n\t\t\t\t\t?? this;\n\t\t\t\tthis.emit(\n\t\t\t\t\t\"notification\",\n\t\t\t\t\tendpoint,\n\t\t\t\t\tCommandClasses.Notification,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: command.notificationType,\n\t\t\t\t\t\tevent: value,\n\t\t\t\t\t\tlabel: notification.name,\n\t\t\t\t\t\teventLabel: valueConfig.label,\n\t\t\t\t\t\tparameters: command.eventParameters,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\t// We may need to reset some linked states to idle\n\t\t\t\tif (valueConfig.idleVariables?.length) {\n\t\t\t\t\tfor (const variable of valueConfig.idleVariables) {\n\t\t\t\t\t\tsetStateIdle(variable);\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// Now that we've gathered all we need to know, update the value in our DB\n\t\t\tlet valueId: ValueID;\n\t\t\tif (valueConfig) {\n\t\t\t\tvalueId = NotificationCCValues.notificationVariable(\n\t\t\t\t\tnotificationName,\n\t\t\t\t\tvalueConfig.variableName,\n\t\t\t\t).endpoint(command.endpointIndex);\n\n\t\t\t\tthis.extendNotificationValueMetadata(\n\t\t\t\t\tvalueId,\n\t\t\t\t\tnotification,\n\t\t\t\t\tvalueConfig,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// Collect unknown values in an \"unknown\" bucket\n\t\t\t\tconst unknownValue = NotificationCCValues\n\t\t\t\t\t.unknownNotificationVariable(\n\t\t\t\t\t\tcommand.notificationType,\n\t\t\t\t\t\tnotificationName,\n\t\t\t\t\t);\n\t\t\t\tvalueId = unknownValue.endpoint(command.endpointIndex);\n\n\t\t\t\tif (ccVersion >= 2) {\n\t\t\t\t\tif (!this.valueDB.hasMetadata(valueId)) {\n\t\t\t\t\t\tthis.valueDB.setMetadata(valueId, unknownValue.meta);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (typeof command.eventParameters === \"number\") {\n\t\t\t\t// This notification contains an enum value. Depending on how the enum behaves,\n\t\t\t\t// we may need to set \"fake\" values for these to distinguish them\n\t\t\t\t// from states without enum values\n\t\t\t\tconst enumBehavior = valueConfig\n\t\t\t\t\t? getNotificationEnumBehavior(\n\t\t\t\t\t\tnotification,\n\t\t\t\t\t\tvalueConfig,\n\t\t\t\t\t)\n\t\t\t\t\t: \"extend\";\n\n\t\t\t\tconst valueWithEnum = enumBehavior === \"replace\"\n\t\t\t\t\t? command.eventParameters\n\t\t\t\t\t: getNotificationStateValueWithEnum(\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tcommand.eventParameters,\n\t\t\t\t\t);\n\t\t\t\tthis.valueDB.setValue(valueId, valueWithEnum);\n\t\t\t} else {\n\t\t\t\tthis.valueDB.setValue(valueId, value);\n\t\t\t}\n\n\t\t\t// Nodes before V8 (and some misbehaving V8 ones) don't necessarily reset the notification to idle.\n\t\t\t// The specifications advise to auto-reset the variables, but it has been found that this interferes\n\t\t\t// with some motion sensors that don't refresh their active notification. Therefore, we set a fallback\n\t\t\t// timer if the `forceNotificationIdleReset` compat flag is set.\n\t\t\tif (\n\t\t\t\tallowIdleReset\n\t\t\t\t&& !!this._deviceConfig?.compat?.forceNotificationIdleReset\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\t\tmessage: `[handleNotificationReport] scheduling idle reset`,\n\t\t\t\t\tlevel: \"silly\",\n\t\t\t\t});\n\t\t\t\tthis.scheduleNotificationIdleReset(\n\t\t\t\t\tvalueId,\n\t\t\t\t\t() => setStateIdle(value),\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\t// This is an unknown notification\n\t\t\tconst unknownValue = NotificationCCValues.unknownNotificationType(\n\t\t\t\tcommand.notificationType,\n\t\t\t);\n\t\t\tconst valueId = unknownValue.endpoint(command.endpointIndex);\n\n\t\t\t// Make sure the metdata exists\n\t\t\tif (ccVersion >= 2) {\n\t\t\t\tif (!this.valueDB.hasMetadata(valueId)) {\n\t\t\t\t\tthis.valueDB.setMetadata(valueId, unknownValue.meta);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// And set its value\n\t\t\tthis.valueDB.setValue(valueId, command.notificationEvent);\n\t\t\t// We don't know what this notification refers to, so we don't force a reset\n\t\t}\n\t}\n\n\tprivate handleKnownNotification(command: NotificationCCReport): void {\n\t\tconst lockEvents = [0x01, 0x03, 0x05, 0x09];\n\t\tconst unlockEvents = [0x02, 0x04, 0x06];\n\t\tconst doorStatusEvents = [\n\t\t\t// Actual status\n\t\t\t0x16,\n\t\t\t0x17,\n\t\t\t// Synthetic status with enum\n\t\t\t0x1600,\n\t\t\t0x1601,\n\t\t];\n\t\tif (\n\t\t\t// Access Control, manual/keypad/rf/auto (un)lock operation\n\t\t\tcommand.notificationType === 0x06\n\t\t\t&& (lockEvents.includes(command.notificationEvent as number)\n\t\t\t\t|| unlockEvents.includes(command.notificationEvent as number))\n\t\t\t&& (this.supportsCC(CommandClasses[\"Door Lock\"])\n\t\t\t\t|| this.supportsCC(CommandClasses.Lock))\n\t\t) {\n\t\t\t// The Door Lock Command Class is constrained to the S2 Access Control key,\n\t\t\t// while the Notification Command Class, in the same device, could use a\n\t\t\t// different key. This way the device can notify devices which don't belong\n\t\t\t// to the S2 Access Control key group of changes in its state.\n\n\t\t\tconst isLocked = lockEvents.includes(\n\t\t\t\tcommand.notificationEvent as number,\n\t\t\t);\n\n\t\t\t// Update the current lock status\n\t\t\tif (this.supportsCC(CommandClasses[\"Door Lock\"])) {\n\t\t\t\tthis.valueDB.setValue(\n\t\t\t\t\tDoorLockCCValues.currentMode.endpoint(\n\t\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t\t),\n\t\t\t\t\tisLocked ? DoorLockMode.Secured : DoorLockMode.Unsecured,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.supportsCC(CommandClasses.Lock)) {\n\t\t\t\tthis.valueDB.setValue(\n\t\t\t\t\tLockCCValues.locked.endpoint(command.endpointIndex),\n\t\t\t\t\tisLocked,\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (\n\t\t\tcommand.notificationType === 0x06\n\t\t\t&& doorStatusEvents.includes(command.notificationEvent as number)\n\t\t) {\n\t\t\t// https://github.com/zwave-js/node-zwave-js/pull/5394 added support for\n\t\t\t// notification enums. Unfortunately, there's no way to discover which nodes\n\t\t\t// actually support them, which makes working with the Door state variable\n\t\t\t// very cumbersome. Also, this is currently the only notification where the enum values\n\t\t\t// extend the state value.\n\n\t\t\t// To work around this, we hard-code a notification value for the door status\n\t\t\t// which only includes the \"legacy\" states for open/closed.\n\t\t\tthis.valueDB.setValue(\n\t\t\t\tNotificationCCValues.doorStateSimple.endpoint(\n\t\t\t\t\tcommand.endpointIndex,\n\t\t\t\t),\n\t\t\t\tcommand.notificationEvent === 0x17 ? 0x17 : 0x16,\n\t\t\t);\n\n\t\t\t// In addition to that, we also hard-code a notification value for only the tilt status.\n\t\t\t// This will only be created after receiving a notification for the tilted state.\n\t\t\t// Only after it exists, it will be updated. Otherwise, we'd get phantom\n\t\t\t// values, since some devices send the enum value, even when they don't support tilt.\n\t\t\tconst tiltValue = NotificationCCValues.doorTiltState;\n\t\t\tconst tiltValueId = tiltValue.endpoint(command.endpointIndex);\n\t\t\tlet tiltValueWasCreated = this.valueDB.hasMetadata(tiltValueId);\n\t\t\tif (command.eventParameters === 0x01 && !tiltValueWasCreated) {\n\t\t\t\tthis.valueDB.setMetadata(tiltValueId, tiltValue.meta);\n\t\t\t\ttiltValueWasCreated = true;\n\t\t\t}\n\t\t\tif (tiltValueWasCreated) {\n\t\t\t\tthis.valueDB.setValue(\n\t\t\t\t\ttiltValueId,\n\t\t\t\t\tcommand.eventParameters === 0x01 ? 0x01 : 0x00,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate busySettingClock: boolean = false;\n\tprivate async handleClockReport(command: ClockCCReport): Promise<void> {\n\t\tif (this.busySettingClock) return;\n\n\t\t// A Z-Wave Plus node SHOULD issue a Clock Report Command via the Lifeline Association Group if they\n\t\t// suspect to have inaccurate time and/or weekdays (e.g. after battery removal).\n\t\t// A controlling node SHOULD compare the received time and weekday with its current time and set the\n\t\t// time again at the supporting node if a deviation is observed (e.g. different weekday or more than a\n\t\t// minute difference)\n\n\t\t// A sending node knowing the current time with seconds precision SHOULD round its\n\t\t// current time to the nearest minute when sending this command.\n\t\tlet now = new Date();\n\t\tconst seconds = now.getSeconds();\n\t\tif (seconds >= 30) {\n\t\t\tnow = new Date(now.getTime() + (60 - seconds) * 1000);\n\t\t}\n\n\t\t// Get desired time in local time\n\t\tconst hours = now.getHours();\n\t\tconst minutes = now.getMinutes();\n\t\t// Sunday is 0 in JS, but 7 in Z-Wave\n\t\tlet weekday = now.getDay();\n\t\tif (weekday === 0) weekday = 7;\n\n\t\tif (\n\t\t\tcommand.weekday !== weekday\n\t\t\t|| command.hour !== hours\n\t\t\t|| command.minute !== minutes\n\t\t) {\n\t\t\tconst endpoint = this.driver.tryGetEndpoint(command);\n\t\t\tif (!endpoint /*|| !endpoint.commandClasses.Clock.isSupported()*/) {\n\t\t\t\t// Make sure the endpoint supports the CC (GH#1704)\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`detected a deviation of the node's clock, updating it...`,\n\t\t\t);\n\t\t\tthis.busySettingClock = true;\n\t\t\ttry {\n\t\t\t\tawait endpoint.commandClasses.Clock.set(\n\t\t\t\t\thours,\n\t\t\t\t\tminutes,\n\t\t\t\t\tweekday,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t\tthis.busySettingClock = false;\n\t\t}\n\t}\n\n\tprivate async handleTimeGet(command: TimeCCTimeGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\tconst now = new Date();\n\t\tconst hours = now.getHours();\n\t\tconst minutes = now.getMinutes();\n\t\tconst seconds = now.getSeconds();\n\n\t\ttry {\n\t\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t\t// Using the commandClasses property would throw in that case\n\t\t\tconst api = endpoint\n\t\t\t\t.createAPI(CommandClasses.Time, false)\n\t\t\t\t.withOptions({\n\t\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t\t});\n\t\t\tawait api.reportTime(hours, minutes, seconds);\n\t\t} catch (e: any) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: e.message,\n\t\t\t\tlevel: \"error\",\n\t\t\t});\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tprivate async handleDateGet(command: TimeCCDateGet): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\tconst now = new Date();\n\t\tconst year = now.getFullYear();\n\t\tconst month = now.getMonth() + 1;\n\t\tconst day = now.getDate();\n\n\t\ttry {\n\t\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t\t// Using the commandClasses property would throw in that case\n\t\t\tconst api = endpoint\n\t\t\t\t.createAPI(CommandClasses.Time, false)\n\t\t\t\t.withOptions({\n\t\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t\t});\n\t\t\tawait api.reportDate(year, month, day);\n\t\t} catch (e: any) {\n\t\t\tthis.driver.controllerLog.logNode(this.id, {\n\t\t\t\tmessage: e.message,\n\t\t\t\tlevel: \"error\",\n\t\t\t});\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tprivate async handleTimeOffsetGet(\n\t\tcommand: TimeCCTimeOffsetGet,\n\t): Promise<void> {\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\n\t\tconst timezone = getDSTInfo(new Date());\n\n\t\ttry {\n\t\t\t// We are being queried, so the device may actually not support the CC, just control it.\n\t\t\t// Using the commandClasses property would throw in that case\n\t\t\tconst api = endpoint\n\t\t\t\t.createAPI(CommandClasses.Time, false)\n\t\t\t\t.withOptions({\n\t\t\t\t\t// Answer with the same encapsulation as asked, but omit\n\t\t\t\t\t// Supervision as it shouldn't be used for Get-Report flows\n\t\t\t\t\tencapsulationFlags: command.encapsulationFlags\n\t\t\t\t\t\t& ~EncapsulationFlags.Supervision,\n\t\t\t\t});\n\t\t\tawait api.reportTimezone(timezone);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves the firmware update capabilities of a node to decide which options to offer a user prior to the update.\n\t * This method uses cached information from the most recent interview.\n\t */\n\tpublic getFirmwareUpdateCapabilitiesCached(): FirmwareUpdateCapabilities {\n\t\tconst firmwareUpgradable = this.getValue<boolean>(\n\t\t\tFirmwareUpdateMetaDataCCValues.firmwareUpgradable.id,\n\t\t);\n\t\tconst supportsActivation = this.getValue<boolean>(\n\t\t\tFirmwareUpdateMetaDataCCValues.supportsActivation.id,\n\t\t);\n\t\tconst continuesToFunction = this.getValue<boolean>(\n\t\t\tFirmwareUpdateMetaDataCCValues.continuesToFunction.id,\n\t\t);\n\t\tconst additionalFirmwareIDs = this.getValue<number[]>(\n\t\t\tFirmwareUpdateMetaDataCCValues.additionalFirmwareIDs.id,\n\t\t);\n\t\tconst supportsResuming = this.getValue<boolean>(\n\t\t\tFirmwareUpdateMetaDataCCValues.supportsResuming.id,\n\t\t);\n\t\tconst supportsNonSecureTransfer = this.getValue<boolean>(\n\t\t\tFirmwareUpdateMetaDataCCValues.supportsNonSecureTransfer.id,\n\t\t);\n\n\t\t// Ensure all information was queried\n\t\tif (\n\t\t\t!firmwareUpgradable\n\t\t\t|| !isArray(additionalFirmwareIDs)\n\t\t) {\n\t\t\treturn { firmwareUpgradable: false };\n\t\t}\n\n\t\treturn {\n\t\t\tfirmwareUpgradable: true,\n\t\t\t// TODO: Targets are not the list of IDs - maybe expose the IDs as well?\n\t\t\tfirmwareTargets: new Array(1 + additionalFirmwareIDs.length).fill(0)\n\t\t\t\t.map((_, i) => i),\n\t\t\tcontinuesToFunction,\n\t\t\tsupportsActivation,\n\t\t\tsupportsResuming,\n\t\t\tsupportsNonSecureTransfer,\n\t\t};\n\t}\n\n\t/**\n\t * Retrieves the firmware update capabilities of a node to decide which options to offer a user prior to the update.\n\t * This communicates with the node to retrieve fresh information.\n\t */\n\tpublic async getFirmwareUpdateCapabilities(): Promise<\n\t\tFirmwareUpdateCapabilities\n\t> {\n\t\tconst api = this.commandClasses[\"Firmware Update Meta Data\"];\n\t\tconst meta = await api.getMetaData();\n\t\tif (!meta) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Failed to request firmware update capabilities: The node did not respond in time!`,\n\t\t\t\tZWaveErrorCodes.Controller_NodeTimeout,\n\t\t\t);\n\t\t} else if (!meta.firmwareUpgradable) {\n\t\t\treturn {\n\t\t\t\tfirmwareUpgradable: false,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tfirmwareUpgradable: true,\n\t\t\t// TODO: Targets are not the list of IDs - maybe expose the IDs as well?\n\t\t\tfirmwareTargets: new Array(1 + meta.additionalFirmwareIDs.length)\n\t\t\t\t.fill(0).map((_, i) => i),\n\t\t\tcontinuesToFunction: meta.continuesToFunction,\n\t\t\tsupportsActivation: meta.supportsActivation,\n\t\t\tsupportsResuming: meta.supportsResuming,\n\t\t\tsupportsNonSecureTransfer: meta.supportsNonSecureTransfer,\n\t\t};\n\t}\n\n\tprivate recentEntryControlNotificationSequenceNumbers: number[] = [];\n\tprivate handleEntryControlNotification(\n\t\tcommand: EntryControlCCNotification,\n\t): void {\n\t\tif (\n\t\t\t!this._deviceConfig?.compat?.disableStrictEntryControlDataValidation\n\t\t) {\n\t\t\tif (\n\t\t\t\tthis.recentEntryControlNotificationSequenceNumbers.includes(\n\t\t\t\t\tcommand.sequenceNumber,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\tthis.id,\n\t\t\t\t\t`Received duplicate Entry Control Notification (sequence number ${command.sequenceNumber}), ignoring...`,\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\t// Keep track of the last 5 sequence numbers\n\t\t\tthis.recentEntryControlNotificationSequenceNumbers.unshift(\n\t\t\t\tcommand.sequenceNumber,\n\t\t\t);\n\t\t\tif (this.recentEntryControlNotificationSequenceNumbers.length > 5) {\n\t\t\t\tthis.recentEntryControlNotificationSequenceNumbers.pop();\n\t\t\t}\n\t\t}\n\n\t\t// Notify listeners\n\t\tconst endpoint = this.getEndpoint(command.endpointIndex) ?? this;\n\t\tthis.emit(\"notification\", endpoint, CommandClasses[\"Entry Control\"], {\n\t\t\t...pick(command, [\"eventType\", \"dataType\", \"eventData\"]),\n\t\t\teventTypeLabel: entryControlEventTypeLabels[command.eventType],\n\t\t\tdataTypeLabel: getEnumMemberName(\n\t\t\t\tEntryControlDataTypes,\n\t\t\t\tcommand.dataType,\n\t\t\t),\n\t\t});\n\t}\n\n\t/**\n\t * @internal\n\t * Deserializes the information of this node from a cache.\n\t */\n\tpublic async deserialize(): Promise<void> {\n\t\tif (!this.driver.networkCache) return;\n\n\t\t// Restore the device config\n\t\tawait this.loadDeviceConfig();\n\n\t\t// Mark already-interviewed nodes as potentially ready\n\t\tif (this.interviewStage === InterviewStage.Complete) {\n\t\t\tthis.readyMachine.send(\"RESTART_FROM_CACHE\");\n\t\t}\n\t}\n\n\t/**\n\t * Instructs the node to send powerlevel test frames to the other node using the given powerlevel. Returns how many frames were acknowledged during the test.\n\t *\n\t * **Note:** Depending on the number of test frames, this may take a while\n\t */\n\tpublic async testPowerlevel(\n\t\ttestNodeId: number,\n\t\tpowerlevel: Powerlevel,\n\t\thealthCheckTestFrameCount: number,\n\t\tonProgress?: (acknowledged: number, total: number) => void,\n\t): Promise<number> {\n\t\tconst api = this.commandClasses.Powerlevel;\n\n\t\t// Keep sleeping nodes awake\n\t\tconst wasKeptAwake = this.keepAwake;\n\t\tif (this.canSleep) this.keepAwake = true;\n\t\tconst result = <T>(value: T) => {\n\t\t\t// And undo the change when we're done\n\t\t\tthis.keepAwake = wasKeptAwake;\n\t\t\treturn value;\n\t\t};\n\n\t\t// Start the process\n\t\tawait api.startNodeTest(\n\t\t\ttestNodeId,\n\t\t\tpowerlevel,\n\t\t\thealthCheckTestFrameCount,\n\t\t);\n\n\t\t// Each frame will take a few ms to be sent, let's assume 5 per second\n\t\t// to estimate how long the test will take\n\t\tconst expectedDurationMs = Math.round(\n\t\t\t(healthCheckTestFrameCount / 5) * 1000,\n\t\t);\n\n\t\t// Poll the status of the test regularly, but not too frequently. Especially for quick tests, polling too often\n\t\t// increases the likelyhood of us querying the node at the same time it sends an unsolicited update.\n\t\t// If using Security S2, this can cause a desync.\n\t\tconst pollFrequencyMs = expectedDurationMs >= 60000 ? 20000 : 5000;\n\n\t\t// Track how often we failed to get a response from the node, so we can abort if the connection is too bad\n\t\tlet continuousErrors = 0;\n\t\t// Also track how many times in a row there was no progress, which also indicates a bad connection\n\t\tlet previousProgress = 0;\n\t\twhile (true) {\n\t\t\t// The node might send an unsolicited update when it finishes the test\n\t\t\tconst report = await this.driver\n\t\t\t\t.waitForCommand<PowerlevelCCTestNodeReport>(\n\t\t\t\t\t(cc) =>\n\t\t\t\t\t\tcc.nodeId === this.id\n\t\t\t\t\t\t&& cc instanceof PowerlevelCCTestNodeReport,\n\t\t\t\t\tpollFrequencyMs,\n\t\t\t\t)\n\t\t\t\t.catch(() => undefined);\n\n\t\t\tconst status = report\n\t\t\t\t? pick(report, [\"status\", \"acknowledgedFrames\"])\n\t\t\t\t// If it didn't come in the wait time, poll for an update\n\t\t\t\t: await api.getNodeTestStatus().catch(() => undefined);\n\n\t\t\t// Safeguard against infinite loop:\n\t\t\t// If we didn't get a result, or there was no progress, try again next iteration\n\t\t\tif (\n\t\t\t\t!status\n\t\t\t\t|| (status.status === PowerlevelTestStatus[\"In Progress\"]\n\t\t\t\t\t&& status.acknowledgedFrames === previousProgress)\n\t\t\t) {\n\t\t\t\tif (continuousErrors > 5) return result(0);\n\t\t\t\tcontinuousErrors++;\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\tpreviousProgress = status.acknowledgedFrames;\n\t\t\t\tcontinuousErrors = 0;\n\t\t\t}\n\n\t\t\tif (status.status === PowerlevelTestStatus.Failed) {\n\t\t\t\treturn result(0);\n\t\t\t} else if (status.status === PowerlevelTestStatus.Success) {\n\t\t\t\treturn result(status.acknowledgedFrames);\n\t\t\t} else if (onProgress) {\n\t\t\t\t// Notify the caller of the test progress\n\t\t\t\tonProgress(\n\t\t\t\t\tstatus.acknowledgedFrames,\n\t\t\t\t\thealthCheckTestFrameCount,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _healthCheckInProgress: boolean = false;\n\t/**\n\t * Returns whether a health check is currently in progress for this node\n\t */\n\tpublic isHealthCheckInProgress(): boolean {\n\t\treturn this._healthCheckInProgress;\n\t}\n\n\tprivate _healthCheckAborted: boolean = false;\n\tprivate _abortHealthCheckPromise: DeferredPromise<void> | undefined;\n\n\t/**\n\t * Aborts an ongoing health check if one is currently in progress.\n\t *\n\t * **Note:** The health check may take a few seconds to actually be aborted.\n\t * When it is, the promise returned by {@link checkLifelineHealth} or\n\t * {@link checkRouteHealth} will be resolved with the results obtained so far.\n\t */\n\tpublic abortHealthCheck(): void {\n\t\tif (!this._healthCheckInProgress) return;\n\t\tthis._healthCheckAborted = true;\n\t\tthis._abortHealthCheckPromise?.resolve();\n\t}\n\n\t/**\n\t * Checks the health of connection between the controller and this node and returns the results.\n\t */\n\tpublic async checkLifelineHealth(\n\t\trounds: number = 5,\n\t\tonProgress?: (\n\t\t\tround: number,\n\t\t\ttotalRounds: number,\n\t\t\tlastRating: number,\n\t\t\tlastResult: LifelineHealthCheckResult,\n\t\t) => void,\n\t): Promise<LifelineHealthCheckSummary> {\n\t\tif (this._healthCheckInProgress) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"A health check is already in progress for this node!\",\n\t\t\t\tZWaveErrorCodes.HealthCheck_Busy,\n\t\t\t);\n\t\t}\n\n\t\tif (rounds > 10 || rounds < 1) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The number of health check rounds must be between 1 and 10!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tthis._healthCheckInProgress = true;\n\t\t\tthis._healthCheckAborted = false;\n\t\t\tthis._abortHealthCheckPromise = createDeferredPromise();\n\n\t\t\treturn await this.checkLifelineHealthInternal(rounds, onProgress);\n\t\t} finally {\n\t\t\tthis._healthCheckInProgress = false;\n\t\t\tthis._healthCheckAborted = false;\n\t\t\tthis._abortHealthCheckPromise = undefined;\n\t\t}\n\t}\n\n\tprivate async checkLifelineHealthInternal(\n\t\trounds: number,\n\t\tonProgress?: (\n\t\t\tround: number,\n\t\t\ttotalRounds: number,\n\t\t\tlastRating: number,\n\t\t\tlastResult: LifelineHealthCheckResult,\n\t\t) => void,\n\t): Promise<LifelineHealthCheckSummary> {\n\t\t// No. of pings per round\n\t\tconst start = Date.now();\n\n\t\t/** Computes a health rating from a health check result */\n\t\tconst computeRating = (result: LifelineHealthCheckResult) => {\n\t\t\tconst failedPings = Math.max(\n\t\t\t\tresult.failedPingsController ?? 0,\n\t\t\t\tresult.failedPingsNode,\n\t\t\t);\n\t\t\tconst numNeighbors = result.numNeighbors;\n\t\t\tconst minPowerlevel = result.minPowerlevel ?? Powerlevel[\"-6 dBm\"];\n\t\t\tconst snrMargin = result.snrMargin ?? 17;\n\t\t\tconst latency = result.latency;\n\n\t\t\tif (failedPings === 10) return 0;\n\t\t\tif (failedPings > 2) return 1;\n\t\t\tif (failedPings === 2 || latency > 1000) return 2;\n\t\t\tif (failedPings === 1 || latency > 500) return 3;\n\t\t\tif (latency > 250) return 4;\n\t\t\tif (latency > 100) return 5;\n\t\t\tif (minPowerlevel < Powerlevel[\"-6 dBm\"] || snrMargin < 17) {\n\t\t\t\t// Lower powerlevel reductions (= higher power) have lower numeric values\n\t\t\t\tif (numNeighbors == undefined) return 7; // ZWLR has no neighbors\n\t\t\t\treturn numNeighbors > 2 ? 7 : 6;\n\t\t\t}\n\t\t\tif (numNeighbors != undefined && numNeighbors <= 2) return 8; // ZWLR has no neighbors\n\t\t\tif (latency > 50) return 9;\n\t\t\treturn 10;\n\t\t};\n\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Starting lifeline health check (${rounds} round${\n\t\t\t\trounds !== 1 ? \"s\" : \"\"\n\t\t\t})...`,\n\t\t);\n\n\t\tconst results: LifelineHealthCheckResult[] = [];\n\t\tconst aborted = () => {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`Lifeline health check aborted`,\n\t\t\t);\n\t\t\tif (results.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\trating: 0,\n\t\t\t\t\tresults: [],\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\treturn {\n\t\t\t\t\trating: Math.min(...results.map((r) => r.rating)),\n\t\t\t\t\tresults,\n\t\t\t\t};\n\t\t\t}\n\t\t};\n\n\t\tif (this.canSleep && this.status !== NodeStatus.Awake) {\n\t\t\t// Wait for node to wake up to avoid incorrectly long delays in the first health check round\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`waiting for node to wake up...`,\n\t\t\t);\n\t\t\tawait Promise.race([\n\t\t\t\tthis.waitForWakeup(),\n\t\t\t\tthis._abortHealthCheckPromise,\n\t\t\t]);\n\t\t\tif (this._healthCheckAborted) return aborted();\n\t\t}\n\n\t\tfor (let round = 1; round <= rounds; round++) {\n\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t// Determine the number of repeating neighbors for Z-Wave Classic\n\t\t\tlet numNeighbors: number | undefined;\n\t\t\tif (this.protocol === Protocols.ZWave) {\n\t\t\t\tnumNeighbors = (await this.driver.controller.getNodeNeighbors(\n\t\t\t\t\tthis.id,\n\t\t\t\t\ttrue,\n\t\t\t\t)).length;\n\t\t\t}\n\n\t\t\t// Ping the node 10x, measuring the RSSI\n\t\t\tlet txReport: TXReport | undefined;\n\t\t\tlet routeChanges: number | undefined;\n\t\t\tlet rssi: RSSI | undefined;\n\t\t\tlet channel: number | undefined;\n\t\t\tlet snrMargin: number | undefined;\n\t\t\tlet failedPingsNode = 0;\n\t\t\tlet latency = 0;\n\t\t\tconst pingAPI = this.commandClasses[\"No Operation\"].withOptions({\n\t\t\t\t// Don't change the node status when the ACK is missing. We're likely testing the limits here.\n\t\t\t\tchangeNodeStatusOnMissingACK: false,\n\t\t\t\t// Avoid using explorer frames, because they can create a ton of delay\n\t\t\t\ttransmitOptions: TransmitOptions.ACK\n\t\t\t\t\t| TransmitOptions.AutoRoute,\n\t\t\t\t// And remember the transmit report, so we can evaluate it\n\t\t\t\tonTXReport: (report) => {\n\t\t\t\t\ttxReport = report;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tfor (let i = 1; i <= healthCheckTestFrameCount; i++) {\n\t\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t\tconst start = Date.now();\n\t\t\t\t// Reset TX report before each ping\n\t\t\t\ttxReport = undefined as any;\n\t\t\t\tconst pingResult = await pingAPI.send().then(\n\t\t\t\t\t() => true,\n\t\t\t\t\t() => false,\n\t\t\t\t);\n\t\t\t\tconst rtt = Date.now() - start;\n\t\t\t\tlatency = Math.max(\n\t\t\t\t\tlatency,\n\t\t\t\t\ttxReport ? txReport.txTicks * 10 : rtt,\n\t\t\t\t);\n\t\t\t\tif (!pingResult) {\n\t\t\t\t\tfailedPingsNode++;\n\t\t\t\t} else if (txReport) {\n\t\t\t\t\trouteChanges ??= 0;\n\t\t\t\t\tif (txReport.routingAttempts > 1) {\n\t\t\t\t\t\trouteChanges++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (\n\t\t\t\t\t\ttxReport.ackRSSI != undefined\n\t\t\t\t\t\t&& !isRssiError(txReport.ackRSSI)\n\t\t\t\t\t) {\n\t\t\t\t\t\t// If possible, determine the SNR margin from the report\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttxReport.measuredNoiseFloor != undefined\n\t\t\t\t\t\t\t&& !isRssiError(txReport.measuredNoiseFloor)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst currentSNRMargin = txReport.ackRSSI\n\t\t\t\t\t\t\t\t- txReport.measuredNoiseFloor;\n\t\t\t\t\t\t\t// And remember it if it's the lowest we've seen so far\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tsnrMargin == undefined\n\t\t\t\t\t\t\t\t|| currentSNRMargin < snrMargin\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tsnrMargin = currentSNRMargin;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Also remember the worst RSSI and the channel it was received on\n\t\t\t\t\t\tif (rssi == undefined || txReport.ackRSSI < rssi) {\n\t\t\t\t\t\t\trssi = txReport.ackRSSI;\n\t\t\t\t\t\t\tchannel = txReport.ackChannelNo;\n\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// If possible, compute the SNR margin from the test results,\n\t\t\t// unless it could already be determined from the transmit reports\n\t\t\tif (\n\t\t\t\tsnrMargin == undefined\n\t\t\t\t&& rssi != undefined\n\t\t\t\t&& rssi < RssiError.NoSignalDetected\n\t\t\t\t&& channel != undefined\n\t\t\t) {\n\t\t\t\tconst backgroundRSSI = await this.driver.controller\n\t\t\t\t\t.getBackgroundRSSI();\n\t\t\t\tif (`rssiChannel${channel}` in backgroundRSSI) {\n\t\t\t\t\tconst bgRSSI = (backgroundRSSI as any)[\n\t\t\t\t\t\t`rssiChannel${channel}`\n\t\t\t\t\t];\n\t\t\t\t\tif (isRssiError(bgRSSI)) {\n\t\t\t\t\t\tif (bgRSSI === RssiError.ReceiverSaturated) {\n\t\t\t\t\t\t\t// RSSI is too high to measure, so there can't be any margin left\n\t\t\t\t\t\t\tsnrMargin = 0;\n\t\t\t\t\t\t} else if (bgRSSI === RssiError.NoSignalDetected) {\n\t\t\t\t\t\t\t// It is very quiet, assume -128 dBm\n\t\t\t\t\t\t\tsnrMargin = rssi + 128;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsnrMargin = undefined;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsnrMargin = rssi - bgRSSI;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst ret: LifelineHealthCheckResult = {\n\t\t\t\tlatency,\n\t\t\t\tfailedPingsNode,\n\t\t\t\tnumNeighbors,\n\t\t\t\trouteChanges,\n\t\t\t\tsnrMargin,\n\t\t\t\trating: 0,\n\t\t\t};\n\n\t\t\t// Now instruct the node to ping the controller, figuring out the minimum powerlevel\n\t\t\tif (this.supportsCC(CommandClasses.Powerlevel)) {\n\t\t\t\t// Do a binary search and find the highest reduction in powerlevel for which there are no errors\n\t\t\t\tlet failedPingsController = 0;\n\n\t\t\t\tconst executor = async (powerlevel: Powerlevel) => {\n\t\t\t\t\t// Abort the search if the health check was aborted\n\t\t\t\t\tif (this._healthCheckAborted) return undefined;\n\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t`Sending ${healthCheckTestFrameCount} pings to controller at ${\n\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\tPowerlevel,\n\t\t\t\t\t\t\t\tpowerlevel,\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\tconst result = await this.testPowerlevel(\n\t\t\t\t\t\tthis.driver.controller.ownNodeId!,\n\t\t\t\t\t\tpowerlevel,\n\t\t\t\t\t\thealthCheckTestFrameCount,\n\t\t\t\t\t);\n\t\t\t\t\tfailedPingsController = healthCheckTestFrameCount - result;\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\t`At ${\n\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\tPowerlevel,\n\t\t\t\t\t\t\t\tpowerlevel,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}, ${result}/${healthCheckTestFrameCount} pings were acknowledged...`,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Wait a second for things to settle down\n\t\t\t\t\tawait wait(1000);\n\n\t\t\t\t\treturn failedPingsController === 0;\n\t\t\t\t};\n\t\t\t\ttry {\n\t\t\t\t\tconst powerlevel = await discreteLinearSearch(\n\t\t\t\t\t\tPowerlevel[\"Normal Power\"], // minimum reduction\n\t\t\t\t\t\tPowerlevel[\"-9 dBm\"], // maximum reduction\n\t\t\t\t\t\texecutor,\n\t\t\t\t\t);\n\t\t\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t\t\tif (powerlevel == undefined) {\n\t\t\t\t\t\t// There were still failures at normal power, report it\n\t\t\t\t\t\tret.minPowerlevel = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tret.failedPingsController = failedPingsController;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tret.minPowerlevel = powerlevel;\n\t\t\t\t\t}\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_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\t// The node is dead, treat this as a failure\n\t\t\t\t\t\tret.minPowerlevel = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tret.failedPingsController = healthCheckTestFrameCount;\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\t\t\t}\n\n\t\t\tret.rating = computeRating(ret);\n\t\t\tresults.push(ret);\n\t\t\tonProgress?.(round, rounds, ret.rating, { ...ret });\n\t\t}\n\n\t\tconst duration = Date.now() - start;\n\n\t\tconst rating = Math.min(...results.map((r) => r.rating));\n\t\tconst summary = { results, rating };\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Lifeline health check complete in ${duration} ms\n${formatLifelineHealthCheckSummary(summary)}`,\n\t\t);\n\n\t\treturn summary;\n\t}\n\n\t/**\n\t * Checks the health of connection between this node and the target node and returns the results.\n\t */\n\tpublic async checkRouteHealth(\n\t\ttargetNodeId: number,\n\t\trounds: number = 5,\n\t\tonProgress?: (\n\t\t\tround: number,\n\t\t\ttotalRounds: number,\n\t\t\tlastRating: number,\n\t\t\tlastResult: RouteHealthCheckResult,\n\t\t) => void,\n\t): Promise<RouteHealthCheckSummary> {\n\t\tif (this._healthCheckInProgress) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"A health check is already in progress for this node!\",\n\t\t\t\tZWaveErrorCodes.HealthCheck_Busy,\n\t\t\t);\n\t\t}\n\n\t\tif (rounds > 10 || rounds < 1) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The number of health check rounds must be between 1 and 10!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tthis._healthCheckInProgress = true;\n\t\t\tthis._healthCheckAborted = false;\n\t\t\tthis._abortHealthCheckPromise = createDeferredPromise();\n\n\t\t\treturn await this.checkRouteHealthInternal(\n\t\t\t\ttargetNodeId,\n\t\t\t\trounds,\n\t\t\t\tonProgress,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis._healthCheckInProgress = false;\n\t\t\tthis._healthCheckAborted = false;\n\t\t\tthis._abortHealthCheckPromise = undefined;\n\t\t}\n\t}\n\n\tprivate async checkRouteHealthInternal(\n\t\ttargetNodeId: number,\n\t\trounds: number,\n\t\tonProgress?: (\n\t\t\tround: number,\n\t\t\ttotalRounds: number,\n\t\t\tlastRating: number,\n\t\t\tlastResult: RouteHealthCheckResult,\n\t\t) => void,\n\t): Promise<RouteHealthCheckSummary> {\n\t\tconst otherNode = this.driver.controller.nodes.getOrThrow(targetNodeId);\n\n\t\tif (this.protocol === Protocols.ZWaveLongRange) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot perform route health check for Long Range node ${this.id}.`,\n\t\t\t\tZWaveErrorCodes.Controller_NotSupportedForLongRange,\n\t\t\t);\n\t\t} else if (otherNode.protocol === Protocols.ZWaveLongRange) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Cannot perform route health check for Long Range node ${otherNode.id}.`,\n\t\t\t\tZWaveErrorCodes.Controller_NotSupportedForLongRange,\n\t\t\t);\n\t\t}\n\n\t\tif (otherNode.canSleep) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Nodes which can sleep are not a valid target for a route health check!\",\n\t\t\t\tZWaveErrorCodes.CC_NotSupported,\n\t\t\t);\n\t\t} else if (\n\t\t\tthis.canSleep\n\t\t\t&& !this.supportsCC(CommandClasses.Powerlevel)\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"For a route health check, nodes which can sleep must support Powerlevel CC!\",\n\t\t\t\tZWaveErrorCodes.CC_NotSupported,\n\t\t\t);\n\t\t} else if (\n\t\t\t!this.supportsCC(CommandClasses.Powerlevel)\n\t\t\t&& !otherNode.supportsCC(CommandClasses.Powerlevel)\n\t\t) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"For a route health check, at least one of the nodes must support Powerlevel CC!\",\n\t\t\t\tZWaveErrorCodes.CC_NotSupported,\n\t\t\t);\n\t\t}\n\n\t\t// No. of pings per round\n\t\tconst healthCheckTestFrameCount = 10;\n\t\tconst start = Date.now();\n\n\t\t/** Computes a health rating from a health check result */\n\t\tconst computeRating = (result: RouteHealthCheckResult) => {\n\t\t\tconst failedPings = Math.max(\n\t\t\t\tresult.failedPingsToSource ?? 0,\n\t\t\t\tresult.failedPingsToTarget ?? 0,\n\t\t\t);\n\t\t\tconst numNeighbors = result.numNeighbors;\n\t\t\tconst minPowerlevel = Math.max(\n\t\t\t\tresult.minPowerlevelSource ?? Powerlevel[\"-6 dBm\"],\n\t\t\t\tresult.minPowerlevelTarget ?? Powerlevel[\"-6 dBm\"],\n\t\t\t);\n\n\t\t\tif (failedPings === 10) return 0;\n\t\t\tif (failedPings > 2) return 1;\n\t\t\tif (failedPings === 2) return 2;\n\t\t\tif (failedPings === 1) return 3;\n\t\t\tif (minPowerlevel < Powerlevel[\"-6 dBm\"]) {\n\t\t\t\t// Lower powerlevel reductions (= higher power) have lower numeric values\n\t\t\t\treturn numNeighbors > 2 ? 7 : 6;\n\t\t\t}\n\t\t\tif (numNeighbors <= 2) return 8;\n\t\t\treturn 10;\n\t\t};\n\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Starting route health check to node ${targetNodeId} (${rounds} round${\n\t\t\t\trounds !== 1 ? \"s\" : \"\"\n\t\t\t})...`,\n\t\t);\n\n\t\tconst results: RouteHealthCheckResult[] = [];\n\t\tconst aborted = () => {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`Route health check to node ${targetNodeId} aborted`,\n\t\t\t);\n\t\t\tif (results.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\trating: 0,\n\t\t\t\t\tresults: [],\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\treturn {\n\t\t\t\t\trating: Math.min(...results.map((r) => r.rating)),\n\t\t\t\t\tresults,\n\t\t\t\t};\n\t\t\t}\n\t\t};\n\n\t\tif (this.canSleep && this.status !== NodeStatus.Awake) {\n\t\t\t// Wait for node to wake up to avoid incorrectly long delays in the first health check round\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`waiting for node to wake up...`,\n\t\t\t);\n\t\t\tawait Promise.race([\n\t\t\t\tthis.waitForWakeup(),\n\t\t\t\tthis._abortHealthCheckPromise,\n\t\t\t]);\n\t\t\tif (this._healthCheckAborted) return aborted();\n\t\t}\n\n\t\tfor (let round = 1; round <= rounds; round++) {\n\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t// Determine the minimum number of repeating neighbors between the\n\t\t\t// source and target node\n\t\t\tconst numNeighbors = Math.min(\n\t\t\t\t(\n\t\t\t\t\tawait this.driver.controller.getNodeNeighbors(\n\t\t\t\t\t\tthis.id,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t)\n\t\t\t\t).length,\n\t\t\t\t(\n\t\t\t\t\tawait this.driver.controller.getNodeNeighbors(\n\t\t\t\t\t\ttargetNodeId,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t)\n\t\t\t\t).length,\n\t\t\t);\n\n\t\t\tlet failedPings = 0;\n\t\t\tlet failedPingsToSource: number | undefined;\n\t\t\tlet minPowerlevelSource: Powerlevel | undefined;\n\t\t\tlet failedPingsToTarget: number | undefined;\n\t\t\tlet minPowerlevelTarget: Powerlevel | undefined;\n\t\t\tconst executor =\n\t\t\t\t(node: ZWaveNode, otherNode: ZWaveNode) =>\n\t\t\t\tasync (powerlevel: Powerlevel) => {\n\t\t\t\t\t// Abort the search if the health check was aborted\n\t\t\t\t\tif (this._healthCheckAborted) return undefined;\n\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t`Sending ${healthCheckTestFrameCount} pings to node ${otherNode.id} at ${\n\t\t\t\t\t\t\tgetEnumMemberName(Powerlevel, powerlevel)\n\t\t\t\t\t\t}...`,\n\t\t\t\t\t);\n\t\t\t\t\tconst result = await node.testPowerlevel(\n\t\t\t\t\t\totherNode.id,\n\t\t\t\t\t\tpowerlevel,\n\t\t\t\t\t\thealthCheckTestFrameCount,\n\t\t\t\t\t);\n\t\t\t\t\tfailedPings = healthCheckTestFrameCount - result;\n\t\t\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\t\t\tnode.id,\n\t\t\t\t\t\t`At ${\n\t\t\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\t\t\tPowerlevel,\n\t\t\t\t\t\t\t\tpowerlevel,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}, ${result}/${healthCheckTestFrameCount} pings were acknowledged by node ${otherNode.id}...`,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Wait a second for things to settle down\n\t\t\t\t\tawait wait(1000);\n\n\t\t\t\t\treturn failedPings === 0;\n\t\t\t\t};\n\n\t\t\t// Now instruct this node to ping the other one, figuring out the minimum powerlevel\n\t\t\tif (this.supportsCC(CommandClasses.Powerlevel)) {\n\t\t\t\ttry {\n\t\t\t\t\t// We have to start with the maximum powerlevel and work our way down\n\t\t\t\t\t// Otherwise some nodes get stuck trying to complete the check at a bad powerlevel\n\t\t\t\t\t// causing the following measurements to fail.\n\t\t\t\t\tconst powerlevel = await discreteLinearSearch(\n\t\t\t\t\t\tPowerlevel[\"Normal Power\"], // minimum reduction\n\t\t\t\t\t\tPowerlevel[\"-9 dBm\"], // maximum reduction\n\t\t\t\t\t\texecutor(this, otherNode),\n\t\t\t\t\t);\n\t\t\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t\t\tif (powerlevel == undefined) {\n\t\t\t\t\t\t// There were still failures at normal power, report it\n\t\t\t\t\t\tminPowerlevelSource = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tfailedPingsToTarget = failedPings;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tminPowerlevelSource = powerlevel;\n\t\t\t\t\t\tfailedPingsToTarget = 0;\n\t\t\t\t\t}\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_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\t// The node is dead, treat this as a failure\n\t\t\t\t\t\tminPowerlevelSource = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tfailedPingsToTarget = healthCheckTestFrameCount;\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\t\t\t}\n\n\t\t\tif (this._healthCheckAborted) return aborted();\n\n\t\t\t// And do the same with the other node - unless the current node is a sleeping node, then this doesn't make sense\n\t\t\tif (\n\t\t\t\t!this.canSleep\n\t\t\t\t&& otherNode.supportsCC(CommandClasses.Powerlevel)\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\tconst powerlevel = await discreteLinearSearch(\n\t\t\t\t\t\tPowerlevel[\"Normal Power\"], // minimum reduction\n\t\t\t\t\t\tPowerlevel[\"-9 dBm\"], // maximum reduction\n\t\t\t\t\t\texecutor(otherNode, this),\n\t\t\t\t\t);\n\t\t\t\t\tif (powerlevel == undefined) {\n\t\t\t\t\t\t// There were still failures at normal power, report it\n\t\t\t\t\t\tminPowerlevelTarget = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tfailedPingsToSource = failedPings;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tminPowerlevelTarget = powerlevel;\n\t\t\t\t\t\tfailedPingsToSource = 0;\n\t\t\t\t\t}\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_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\t// The node is dead, treat this as a failure\n\t\t\t\t\t\tminPowerlevelTarget = Powerlevel[\"Normal Power\"];\n\t\t\t\t\t\tfailedPingsToSource = healthCheckTestFrameCount;\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\t\t\t}\n\n\t\t\tconst ret: RouteHealthCheckResult = {\n\t\t\t\tnumNeighbors,\n\t\t\t\tfailedPingsToSource,\n\t\t\t\tfailedPingsToTarget,\n\t\t\t\tminPowerlevelSource,\n\t\t\t\tminPowerlevelTarget,\n\t\t\t\trating: 0,\n\t\t\t};\n\t\t\tret.rating = computeRating(ret);\n\t\t\tresults.push(ret);\n\t\t\tonProgress?.(round, rounds, ret.rating, { ...ret });\n\t\t}\n\n\t\tconst duration = Date.now() - start;\n\n\t\tconst rating = Math.min(...results.map((r) => r.rating));\n\t\tconst summary = { results, rating };\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Route health check to node ${otherNode.id} complete in ${duration} ms\n${formatRouteHealthCheckSummary(this.id, otherNode.id, summary)}`,\n\t\t);\n\n\t\treturn summary;\n\t}\n\n\tprivate _linkReliabilityCheckInProgress: boolean = false;\n\t/**\n\t * Returns whether a link reliability check is currently in progress for this node\n\t */\n\tpublic isLinkReliabilityCheckInProgress(): boolean {\n\t\treturn this._linkReliabilityCheckInProgress;\n\t}\n\n\tprivate _linkReliabilityCheckAborted: boolean = false;\n\tprivate _abortLinkReliabilityCheckPromise:\n\t\t| DeferredPromise<void>\n\t\t| undefined;\n\n\t/**\n\t * Aborts an ongoing link reliability check if one is currently in progress.\n\t *\n\t * **Note:** The link reliability check may take a few seconds to actually be aborted.\n\t * When it is, the promise returned by {@link checkLinkReliability} will be resolved with the results obtained so far.\n\t */\n\tpublic abortLinkReliabilityCheck(): void {\n\t\tif (!this._linkReliabilityCheckInProgress) return;\n\t\tthis._linkReliabilityCheckAborted = true;\n\t\tthis._abortLinkReliabilityCheckPromise?.resolve();\n\t}\n\n\t/**\n\t * Tests the reliability of the link between the controller and this node and returns the results.\n\t */\n\tpublic async checkLinkReliability(\n\t\toptions: LinkReliabilityCheckOptions,\n\t): Promise<LinkReliabilityCheckResult> {\n\t\tif (this._linkReliabilityCheckInProgress) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"A link reliability check is already in progress for this node!\",\n\t\t\t\tZWaveErrorCodes.LinkReliabilityCheck_Busy,\n\t\t\t);\n\t\t}\n\n\t\tif (typeof options.rounds === \"number\" && options.rounds < 1) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The number of rounds must be at least 1!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tthis._linkReliabilityCheckInProgress = true;\n\t\t\tthis._linkReliabilityCheckAborted = false;\n\t\t\tthis._abortLinkReliabilityCheckPromise = createDeferredPromise();\n\n\t\t\tswitch (options.mode) {\n\t\t\t\tcase LinkReliabilityCheckMode.BasicSetOnOff:\n\t\t\t\t\treturn await this.checkLinkReliabilityBasicSetOnOff(\n\t\t\t\t\t\toptions,\n\t\t\t\t\t);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._linkReliabilityCheckInProgress = false;\n\t\t\tthis._linkReliabilityCheckAborted = false;\n\t\t\tthis._abortLinkReliabilityCheckPromise = undefined;\n\t\t}\n\t}\n\n\tprivate async checkLinkReliabilityBasicSetOnOff(\n\t\toptions: LinkReliabilityCheckOptions,\n\t): Promise<LinkReliabilityCheckResult> {\n\t\tthis.driver.controllerLog.logNode(\n\t\t\tthis.id,\n\t\t\t`Starting link reliability check (Basic Set On/Off) with ${options.rounds} round${\n\t\t\t\toptions.rounds !== 1 ? \"s\" : \"\"\n\t\t\t}...`,\n\t\t);\n\n\t\tconst useSupervision = this.supportsCC(CommandClasses.Supervision);\n\t\tconst result: LinkReliabilityCheckResult = {\n\t\t\trounds: 0,\n\t\t\tcommandsSent: 0,\n\t\t\tcommandErrors: 0,\n\t\t\tmissingResponses: useSupervision ? 0 : undefined,\n\t\t\tlatency: {\n\t\t\t\tmin: Number.POSITIVE_INFINITY,\n\t\t\t\tmax: 0,\n\t\t\t\taverage: 0,\n\t\t\t},\n\t\t\trtt: {\n\t\t\t\tmin: Number.POSITIVE_INFINITY,\n\t\t\t\tmax: 0,\n\t\t\t\taverage: 0,\n\t\t\t},\n\t\t\tackRSSI: {\n\t\t\t\tmin: 0,\n\t\t\t\tmax: Number.NEGATIVE_INFINITY,\n\t\t\t\taverage: Number.NEGATIVE_INFINITY,\n\t\t\t},\n\t\t\tresponseRSSI: useSupervision\n\t\t\t\t? {\n\t\t\t\t\tmin: 0,\n\t\t\t\t\tmax: Number.NEGATIVE_INFINITY,\n\t\t\t\t\taverage: Number.NEGATIVE_INFINITY,\n\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t};\n\n\t\tconst aborted = () => {\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`Link reliability check aborted`,\n\t\t\t);\n\t\t\treturn result;\n\t\t};\n\n\t\tlet lastProgressReport = 0;\n\t\tconst reportProgress = () => {\n\t\t\tif (Date.now() - lastProgressReport >= 250) {\n\t\t\t\toptions.onProgress?.(cloneDeep(result));\n\t\t\t\tlastProgressReport = Date.now();\n\t\t\t}\n\t\t};\n\n\t\tif (this.canSleep && this.status !== NodeStatus.Awake) {\n\t\t\t// Wait for node to wake up to avoid incorrectly long delays in the first health check round\n\t\t\tthis.driver.controllerLog.logNode(\n\t\t\t\tthis.id,\n\t\t\t\t`waiting for node to wake up...`,\n\t\t\t);\n\t\t\tawait Promise.race([\n\t\t\t\tthis.waitForWakeup(),\n\t\t\t\tthis._abortLinkReliabilityCheckPromise,\n\t\t\t]);\n\t\t\tif (this._linkReliabilityCheckAborted) return aborted();\n\t\t}\n\n\t\t// TODO: report progress with throttle\n\n\t\tlet txReport: TXReport | undefined;\n\n\t\tconst basicSetAPI = this.commandClasses.Basic.withOptions({\n\t\t\t// Don't change the node status when the ACK is missing. We're likely testing the limits here.\n\t\t\tchangeNodeStatusOnMissingACK: false,\n\t\t\t// Avoid using explorer frames, because they can create a ton of delay\n\t\t\ttransmitOptions: TransmitOptions.ACK\n\t\t\t\t| TransmitOptions.AutoRoute,\n\t\t\t// Do not wait for SOS NonceReports, as it slows down the test\n\t\t\ts2VerifyDelivery: false,\n\t\t\t// And remember the transmit report, so we can evaluate it\n\t\t\tonTXReport: (report) => {\n\t\t\t\ttxReport = report;\n\t\t\t},\n\t\t});\n\n\t\tlet lastStart: number;\n\t\tfor (\n\t\t\tlet round = 1;\n\t\t\tround <= (options.rounds ?? Number.POSITIVE_INFINITY);\n\t\t\tround++\n\t\t) {\n\t\t\tif (this._linkReliabilityCheckAborted) return aborted();\n\n\t\t\tresult.rounds = round;\n\n\t\t\tlastStart = Date.now();\n\t\t\t// Reset TX report before each command\n\t\t\ttxReport = undefined as any;\n\n\t\t\ttry {\n\t\t\t\tawait basicSetAPI.set(\n\t\t\t\t\tround % 2 === 1 ? 0xff : 0x00,\n\t\t\t\t);\n\t\t\t\t// The command was sent successfully (and possibly got a response)\n\t\t\t\tresult.commandsSent++;\n\n\t\t\t\t// Measure the RTT or latency, whatever is available\n\t\t\t\tconst rtt = Date.now() - lastStart;\n\t\t\t\tresult.rtt.min = Math.min(result.rtt.min, rtt);\n\t\t\t\tresult.rtt.max = Math.max(result.rtt.max, rtt);\n\t\t\t\t// incrementally update the average rtt\n\t\t\t\tresult.rtt.average += (rtt - result.rtt.average) / round;\n\n\t\t\t\tif (txReport) {\n\t\t\t\t\tconst latency = txReport.txTicks * 10;\n\t\t\t\t\tif (result.latency) {\n\t\t\t\t\t\tresult.latency.min = Math.min(\n\t\t\t\t\t\t\tresult.latency.min,\n\t\t\t\t\t\t\tlatency,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tresult.latency.max = Math.max(\n\t\t\t\t\t\t\tresult.latency.max,\n\t\t\t\t\t\t\tlatency,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// incrementally update the average RTT\n\t\t\t\t\t\tresult.latency.average +=\n\t\t\t\t\t\t\t(latency - result.latency.average)\n\t\t\t\t\t\t\t/ round;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult.latency = {\n\t\t\t\t\t\t\tmin: latency,\n\t\t\t\t\t\t\tmax: latency,\n\t\t\t\t\t\t\taverage: latency,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (isZWaveError(e)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\te.code === ZWaveErrorCodes.Controller_ResponseNOK\n\t\t\t\t\t\t|| e.code === ZWaveErrorCodes.Controller_CallbackNOK\n\t\t\t\t\t) {\n\t\t\t\t\t\t// The command could not be sent or was not acknowledged\n\t\t\t\t\t\tresult.commandErrors++;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\te.code === ZWaveErrorCodes.Controller_NodeTimeout\n\t\t\t\t\t) {\n\t\t\t\t\t\t// The command was sent using Supervision and a response was\n\t\t\t\t\t\t// expected but none came\n\t\t\t\t\t\tresult.missingResponses ??= 0;\n\t\t\t\t\t\tresult.missingResponses++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\ttxReport?.ackRSSI != undefined\n\t\t\t\t&& !isRssiError(txReport.ackRSSI)\n\t\t\t) {\n\t\t\t\tresult.ackRSSI.min = Math.min(\n\t\t\t\t\tresult.ackRSSI.min,\n\t\t\t\t\ttxReport.ackRSSI,\n\t\t\t\t);\n\t\t\t\tresult.ackRSSI.max = Math.max(\n\t\t\t\t\tresult.ackRSSI.max,\n\t\t\t\t\ttxReport.ackRSSI,\n\t\t\t\t);\n\t\t\t\t// incrementally update the average RSSI\n\t\t\t\tif (Number.isFinite(result.ackRSSI.average)) {\n\t\t\t\t\tresult.ackRSSI.average +=\n\t\t\t\t\t\t(txReport.ackRSSI - result.ackRSSI.average)\n\t\t\t\t\t\t/ round;\n\t\t\t\t} else {\n\t\t\t\t\tresult.ackRSSI.average = txReport.ackRSSI;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// TODO: Capture incoming RSSI and average it\n\n\t\t\treportProgress();\n\n\t\t\t// Throttle the next command\n\t\t\tconst waitDurationMs = Math.max(\n\t\t\t\t0,\n\t\t\t\toptions.interval - (Date.now() - lastStart),\n\t\t\t);\n\t\t\tawait Promise.race([\n\t\t\t\twait(waitDurationMs, true),\n\t\t\t\tthis._abortLinkReliabilityCheckPromise,\n\t\t\t]);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Updates the average RTT of this node\n\t * @internal\n\t */\n\tpublic updateRTT(sentMessage: Message): void {\n\t\tif (sentMessage.rtt) {\n\t\t\tconst rttMs = sentMessage.rtt / 1e6;\n\t\t\tthis.updateStatistics((current) => ({\n\t\t\t\t...current,\n\t\t\t\trtt: current.rtt != undefined\n\t\t\t\t\t? roundTo(current.rtt * 0.75 + rttMs * 0.25, 1)\n\t\t\t\t\t: roundTo(rttMs, 1),\n\t\t\t}));\n\t\t}\n\t}\n\n\t/**\n\t * Updates route/transmission statistics for this node\n\t * @internal\n\t */\n\tpublic updateRouteStatistics(txReport: TXReport): void {\n\t\tthis.updateStatistics((current) => {\n\t\t\tconst ret = { ...current };\n\t\t\t// Update ACK RSSI\n\t\t\tif (txReport.ackRSSI != undefined) {\n\t\t\t\tret.rssi =\n\t\t\t\t\tret.rssi == undefined || isRssiError(txReport.ackRSSI)\n\t\t\t\t\t\t? txReport.ackRSSI\n\t\t\t\t\t\t: Math.round(ret.rssi * 0.75 + txReport.ackRSSI * 0.25);\n\t\t\t}\n\n\t\t\t// Update the LWR's statistics\n\t\t\tconst newStats: RouteStatistics = {\n\t\t\t\tprotocolDataRate: txReport.routeSpeed,\n\t\t\t\trepeaters: (txReport.repeaterNodeIds ?? []) as number[],\n\t\t\t\trssi: txReport.ackRSSI\n\t\t\t\t\t?? ret.lwr?.rssi\n\t\t\t\t\t?? RssiError.NotAvailable,\n\t\t\t};\n\t\t\tif (txReport.ackRepeaterRSSI != undefined) {\n\t\t\t\tnewStats.repeaterRSSI = txReport.ackRepeaterRSSI as number[];\n\t\t\t}\n\t\t\tif (\n\t\t\t\ttxReport.failedRouteLastFunctionalNodeId\n\t\t\t\t&& txReport.failedRouteFirstNonFunctionalNodeId\n\t\t\t) {\n\t\t\t\tnewStats.routeFailedBetween = [\n\t\t\t\t\ttxReport.failedRouteLastFunctionalNodeId,\n\t\t\t\t\ttxReport.failedRouteFirstNonFunctionalNodeId,\n\t\t\t\t];\n\t\t\t}\n\n\t\t\tif (ret.lwr && !routeStatisticsEquals(ret.lwr, newStats)) {\n\t\t\t\t// The old LWR becomes the NLWR\n\t\t\t\tret.nlwr = ret.lwr;\n\t\t\t}\n\t\t\tret.lwr = newStats;\n\t\t\treturn ret;\n\t\t});\n\t}\n\n\t/**\n\t * Sets the current date, time and timezone (or a subset of those) on the node using one or more of the respective CCs.\n\t * Returns whether the operation was successful.\n\t */\n\tpublic async setDateAndTime(now: Date = new Date()): Promise<boolean> {\n\t\t// There are multiple ways to communicate the current time to a node:\n\t\t// 1. Time Parameters CC\n\t\t// 2. Clock CC\n\t\t// 3. Time CC, but only in response to requests from the node\n\t\tconst timeParametersAPI = this.commandClasses[\"Time Parameters\"];\n\t\tconst timeAPI = this.commandClasses.Time;\n\t\tconst clockAPI = this.commandClasses.Clock;\n\t\tconst scheduleEntryLockAPI = this.commandClasses[\"Schedule Entry Lock\"];\n\n\t\tif (\n\t\t\ttimeParametersAPI.isSupported()\n\t\t\t&& timeParametersAPI.supportsCommand(TimeParametersCommand.Set)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeParametersAPI.set(now);\n\t\t\t\tif (supervisedCommandFailed(result)) return false;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (\n\t\t\tclockAPI.isSupported()\n\t\t\t&& clockAPI.supportsCommand(ClockCommand.Set)\n\t\t) {\n\t\t\ttry {\n\t\t\t\t// Get desired time in local time\n\t\t\t\tconst hours = now.getHours();\n\t\t\t\tconst minutes = now.getMinutes();\n\t\t\t\t// Sunday is 0 in JS, but 7 in Z-Wave\n\t\t\t\tlet weekday = now.getDay();\n\t\t\t\tif (weekday === 0) weekday = 7;\n\n\t\t\t\tconst result = await clockAPI.set(hours, minutes, weekday);\n\t\t\t\tif (supervisedCommandFailed(result)) return false;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (\n\t\t\ttimeAPI.isSupported()\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.DateReport)\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.TimeReport)\n\t\t) {\n\t\t\t// According to https://github.com/zwave-js/node-zwave-js/issues/6032#issuecomment-1641945555\n\t\t\t// some devices update their date and time when they receive an unsolicited Time CC report.\n\t\t\t// Even if this isn't intended, we should at least try.\n\n\t\t\tconst api = timeAPI.withOptions({\n\t\t\t\tuseSupervision: false,\n\t\t\t});\n\t\t\ttry {\n\t\t\t\t// First date\n\t\t\t\tconst year = now.getFullYear();\n\t\t\t\tconst month = now.getMonth() + 1;\n\t\t\t\tconst day = now.getDate();\n\t\t\t\tawait api.reportDate(year, month, day);\n\n\t\t\t\tconst verification = await api.getDate();\n\t\t\t\tif (\n\t\t\t\t\t!verification\n\t\t\t\t\t|| verification.year !== year\n\t\t\t\t\t|| verification.month !== month\n\t\t\t\t\t|| verification.day !== day\n\t\t\t\t) {\n\t\t\t\t\t// Didn't work\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Then time\n\t\t\t\tconst hour = now.getHours();\n\t\t\t\tconst minute = now.getMinutes();\n\t\t\t\tconst second = now.getSeconds();\n\t\t\t\tawait api.reportTime(hour, minute, second);\n\n\t\t\t\tconst verification = await api.getTime();\n\t\t\t\tif (!verification) return false;\n\t\t\t\t// To leave a bit of tolerance for communication delays, we compare the seconds since midnight\n\t\t\t\tconst secondsPerDay = 24 * 60 * 60;\n\t\t\t\tconst expected = hour * 60 * 60 + minute * 60 + second;\n\t\t\t\tconst expectedMin = expected - 30;\n\t\t\t\tconst expectedMax = expected + 30;\n\t\t\t\tconst actual = verification.hour * 60 * 60\n\t\t\t\t\t+ verification.minute * 60\n\t\t\t\t\t+ verification.second;\n\t\t\t\t// The time may have wrapped around midnight since we set the date\n\t\t\t\tif (actual >= expectedMin && actual <= expectedMax) {\n\t\t\t\t\t// ok\n\t\t\t\t} else if (\n\t\t\t\t\tactual + secondsPerDay >= expectedMin\n\t\t\t\t\t&& actual + secondsPerDay <= expectedMax\n\t\t\t\t) {\n\t\t\t\t\t// ok\n\t\t\t\t} else {\n\t\t\t\t\t// Didn't work\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// We might also have to change the timezone. That is done with the Time CC.\n\t\t// Or in really strange cases using the Schedule Entry Lock CC\n\t\tconst timezone = getDSTInfo(now);\n\t\tif (\n\t\t\ttimeAPI.isSupported()\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.TimeOffsetSet)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeAPI.setTimezone(timezone);\n\t\t\t\tif (supervisedCommandFailed(result)) return false;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (\n\t\t\tscheduleEntryLockAPI.isSupported()\n\t\t\t&& scheduleEntryLockAPI.supportsCommand(\n\t\t\t\tScheduleEntryLockCommand.TimeOffsetSet,\n\t\t\t)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await scheduleEntryLockAPI.setTimezone(timezone);\n\t\t\t\tif (supervisedCommandFailed(result)) return false;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns the current date, time and timezone (or a subset of those) on the node using one or more of the respective CCs.\n\t */\n\tpublic async getDateAndTime(): Promise<DateAndTime> {\n\t\tconst timeParametersAPI = this.commandClasses[\"Time Parameters\"];\n\t\tconst timeAPI = this.commandClasses.Time;\n\t\tconst clockAPI = this.commandClasses.Clock;\n\t\tconst scheduleEntryLockAPI = this.commandClasses[\"Schedule Entry Lock\"];\n\n\t\tconst response: DateAndTime = {};\n\n\t\tif (\n\t\t\ttimeParametersAPI.isSupported()\n\t\t\t&& timeParametersAPI.supportsCommand(TimeParametersCommand.Get)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeParametersAPI.get();\n\t\t\t\tif (result) {\n\t\t\t\t\t// Time Parameters is all UTC per the spec\n\t\t\t\t\tObject.assign(response, {\n\t\t\t\t\t\thour: result.getUTCHours(),\n\t\t\t\t\t\tminute: result.getUTCMinutes(),\n\t\t\t\t\t\tsecond: result.getUTCSeconds(),\n\t\t\t\t\t\tstandardOffset: 0,\n\t\t\t\t\t\tdstOffset: 0,\n\t\t\t\t\t\tweekday: result.getUTCDay(),\n\t\t\t\t\t\tday: result.getUTCDate(),\n\t\t\t\t\t\tmonth: result.getUTCMonth() + 1,\n\t\t\t\t\t\tyear: result.getUTCFullYear(),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// That's everything\n\t\t\t\treturn response;\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (\n\t\t\tclockAPI.isSupported()\n\t\t\t&& clockAPI.supportsCommand(ClockCommand.Get)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await clockAPI.get();\n\t\t\t\tif (result) {\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thour: result.hour,\n\t\t\t\t\t\t\tminute: result.minute,\n\t\t\t\t\t\t\tweekday: result.weekday,\n\t\t\t\t\t\t} satisfies DateAndTime,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (\n\t\t\ttimeAPI.isSupported()\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.TimeGet)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeAPI.getTime();\n\t\t\t\tif (result) {\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thour: result.hour,\n\t\t\t\t\t\t\tminute: result.minute,\n\t\t\t\t\t\t\tsecond: result.second,\n\t\t\t\t\t\t} satisfies DateAndTime,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (\n\t\t\ttimeAPI.isSupported()\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.DateGet)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeAPI.getDate();\n\t\t\t\tif (result) {\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tday: result.day,\n\t\t\t\t\t\t\tmonth: result.month,\n\t\t\t\t\t\t\tyear: result.year,\n\t\t\t\t\t\t} satisfies DateAndTime,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (\n\t\t\ttimeAPI.isSupported()\n\t\t\t&& timeAPI.supportsCommand(TimeCommand.TimeOffsetGet)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await timeAPI.getTimezone();\n\t\t\t\tif (result) {\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstandardOffset: result.standardOffset,\n\t\t\t\t\t\t\tdstOffset: result.dstOffset,\n\t\t\t\t\t\t} satisfies DateAndTime,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (\n\t\t\tscheduleEntryLockAPI.isSupported()\n\t\t\t&& scheduleEntryLockAPI.supportsCommand(\n\t\t\t\tScheduleEntryLockCommand.TimeOffsetGet,\n\t\t\t)\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst result = await scheduleEntryLockAPI.getTimezone();\n\t\t\t\tif (result) {\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstandardOffset: result.standardOffset,\n\t\t\t\t\t\t\tdstOffset: result.dstOffset,\n\t\t\t\t\t\t} satisfies DateAndTime,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn response;\n\t}\n\n\tpublic async sendResetLocallyNotification(): Promise<void> {\n\t\t// We don't care if the CC is supported by the receiving node\n\t\tconst api = this.createAPI(\n\t\t\tCommandClasses[\"Device Reset Locally\"],\n\t\t\tfalse,\n\t\t);\n\n\t\tawait api.sendNotification();\n\t}\n\n\t/**\n\t * Returns whether the device config for this node has changed since the last interview.\n\t * If it has, the node likely needs to be re-interviewed for the changes to be picked up.\n\t */\n\tpublic hasDeviceConfigChanged(): MaybeNotKnown<boolean> {\n\t\t// We can't know if the node is not fully interviewed\n\t\tif (this.interviewStage !== InterviewStage.Complete) return NOT_KNOWN;\n\n\t\t// The controller cannot be re-interviewed\n\t\tif (this.isControllerNode) return false;\n\n\t\t// If the hash was never stored, we can only (very likely) know if the config has not changed\n\t\tif (this.cachedDeviceConfigHash == undefined) {\n\t\t\treturn this.deviceConfig == undefined ? false : NOT_KNOWN;\n\t\t}\n\n\t\t// If it was, a change in hash means the config has changed\n\t\tif (this._currentDeviceConfigHash) {\n\t\t\treturn !Bytes.view(this._currentDeviceConfigHash).equals(\n\t\t\t\tthis.cachedDeviceConfigHash,\n\t\t\t);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/** Returns a dump of this node's information for debugging purposes */\n\tpublic createDump(): NodeDump {\n\t\tconst { index, ...endpointDump } = this.createEndpointDump();\n\t\tconst ret: NodeDump = {\n\t\t\tid: this.id,\n\t\t\tmanufacturer: this.deviceConfig?.manufacturer,\n\t\t\tlabel: this.label,\n\t\t\tdescription: this.deviceConfig?.description,\n\t\t\tfingerprint: {\n\t\t\t\tmanufacturerId: this.manufacturerId != undefined\n\t\t\t\t\t? formatId(this.manufacturerId)\n\t\t\t\t\t: \"unknown\",\n\t\t\t\tproductType: this.productType != undefined\n\t\t\t\t\t? formatId(this.productType)\n\t\t\t\t\t: \"unknown\",\n\t\t\t\tproductId: this.productId != undefined\n\t\t\t\t\t? formatId(this.productId)\n\t\t\t\t\t: \"unknown\",\n\t\t\t\tfirmwareVersion: this.firmwareVersion ?? \"unknown\",\n\t\t\t},\n\t\t\tinterviewStage: getEnumMemberName(\n\t\t\t\tInterviewStage,\n\t\t\t\tthis.interviewStage,\n\t\t\t),\n\t\t\tready: this.ready,\n\n\t\t\tdsk: this.dsk ? dskToString(this.dsk) : undefined,\n\t\t\tsecurityClasses: {},\n\n\t\t\tisListening: this.isListening ?? \"unknown\",\n\t\t\tisFrequentListening: this.isFrequentListening ?? \"unknown\",\n\t\t\tisRouting: this.isRouting ?? \"unknown\",\n\t\t\tsupportsBeaming: this.supportsBeaming ?? \"unknown\",\n\t\t\tsupportsSecurity: this.supportsSecurity ?? \"unknown\",\n\t\t\tprotocol: getEnumMemberName(Protocols, this.protocol),\n\t\t\tsupportedProtocols: this.driver.controller.getProvisioningEntry(\n\t\t\t\tthis.id,\n\t\t\t)?.supportedProtocols?.map((p) => getEnumMemberName(Protocols, p)),\n\t\t\tprotocolVersion: this.protocolVersion != undefined\n\t\t\t\t? getEnumMemberName(ProtocolVersion, this.protocolVersion)\n\t\t\t\t: \"unknown\",\n\t\t\tsdkVersion: this.sdkVersion ?? \"unknown\",\n\t\t\tsupportedDataRates: this.supportedDataRates\n\t\t\t\t? [...this.supportedDataRates]\n\t\t\t\t: \"unknown\",\n\n\t\t\t...endpointDump,\n\t\t};\n\n\t\tif (this.hardwareVersion != undefined) {\n\t\t\tret.fingerprint.hardwareVersion = this.hardwareVersion;\n\t\t}\n\n\t\tfor (const secClass of securityClassOrder) {\n\t\t\tif (\n\t\t\t\tthis.protocol === Protocols.ZWaveLongRange\n\t\t\t\t&& !securityClassIsLongRange(secClass)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tret.securityClasses[getEnumMemberName(SecurityClass, secClass)] =\n\t\t\t\tthis.hasSecurityClass(secClass) ?? \"unknown\";\n\t\t}\n\n\t\tconst allValueIds = nodeUtils.getDefinedValueIDsInternal(\n\t\t\tthis.driver,\n\t\t\tthis,\n\t\t\ttrue,\n\t\t);\n\n\t\tconst collectValues = (\n\t\t\tendpointIndex: number,\n\t\t\tgetCollection: (ccId: CommandClasses) => ValueDump[] | undefined,\n\t\t) => {\n\t\t\tfor (const valueId of allValueIds) {\n\t\t\t\tif ((valueId.endpoint ?? 0) !== endpointIndex) continue;\n\n\t\t\t\tconst value = this._valueDB.getValue(valueId);\n\t\t\t\tconst metadata = this._valueDB.getMetadata(valueId);\n\t\t\t\tconst timestamp = this._valueDB.getTimestamp(valueId);\n\t\t\t\tconst timestampAsDate = timestamp\n\t\t\t\t\t? new Date(timestamp).toISOString()\n\t\t\t\t\t: undefined;\n\n\t\t\t\tconst ccInstance = CommandClass.createInstanceUnchecked(\n\t\t\t\t\tthis,\n\t\t\t\t\tvalueId.commandClass,\n\t\t\t\t);\n\t\t\t\tconst isInternalValue = ccInstance?.isInternalValue(valueId);\n\n\t\t\t\tconst valueDump: ValueDump = {\n\t\t\t\t\t...pick(valueId, [\n\t\t\t\t\t\t\"property\",\n\t\t\t\t\t\t\"propertyKey\",\n\t\t\t\t\t]),\n\t\t\t\t\tmetadata,\n\t\t\t\t\tvalue: metadata?.secret\n\t\t\t\t\t\t? \"(redacted)\"\n\t\t\t\t\t\t: serializeCacheValue(value),\n\t\t\t\t\ttimestamp: timestampAsDate,\n\t\t\t\t};\n\t\t\t\tif (isInternalValue) valueDump.internal = true;\n\n\t\t\t\tfor (const [prop, value] of Object.entries(valueDump)) {\n\t\t\t\t\t// @ts-expect-error\n\t\t\t\t\tif (value === undefined) delete valueDump[prop];\n\t\t\t\t}\n\n\t\t\t\tgetCollection(valueId.commandClass)?.push(valueDump);\n\t\t\t}\n\t\t};\n\t\tcollectValues(0, (ccId) => ret.commandClasses[getCCName(ccId)]?.values);\n\n\t\tfor (const endpoint of this.getAllEndpoints()) {\n\t\t\tif (endpoint.index === 0) continue;\n\t\t\tret.endpoints ??= {};\n\t\t\tconst endpointDump = endpoint.createEndpointDump();\n\t\t\tcollectValues(\n\t\t\t\tendpoint.index,\n\t\t\t\t(ccId) => endpointDump.commandClasses[getCCName(ccId)]?.values,\n\t\t\t);\n\t\t\tret.endpoints[endpoint.index] = endpointDump;\n\t\t}\n\n\t\tif (this.deviceConfig) {\n\t\t\tconst relativePath = path.relative(\n\t\t\t\tembeddedDevicesDir,\n\t\t\t\tthis.deviceConfig.filename,\n\t\t\t);\n\t\t\tif (relativePath.startsWith(\"..\")) {\n\t\t\t\t// The path is outside our embedded config dir, take the full path\n\t\t\t\tret.configFileName = this.deviceConfig.filename;\n\t\t\t} else {\n\t\t\t\tret.configFileName = relativePath;\n\t\t\t}\n\n\t\t\tif (this.deviceConfig.compat) {\n\t\t\t\t// TODO: Check if everything comes through this way.\n\t\t\t\tret.compatFlags = this.deviceConfig.compat;\n\t\t\t}\n\t\t}\n\t\tfor (const [prop, value] of Object.entries(ret)) {\n\t\t\t// @ts-expect-error\n\t\t\tif (value === undefined) delete ret[prop];\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\tprotected _emit<TEvent extends keyof AllNodeEvents>(\n\t\tevent: TEvent,\n\t\t...args: Parameters<AllNodeEvents[TEvent]>\n\t): boolean {\n\t\treturn this.emit(event, ...args);\n\t}\n\n\tprotected _on<TEvent extends keyof AllNodeEvents>(\n\t\tevent: TEvent,\n\t\tcallback: AllNodeEvents[TEvent],\n\t): this {\n\t\treturn this.on(event, callback);\n\t}\n\n\tprotected _once<TEvent extends keyof AllNodeEvents>(\n\t\tevent: TEvent,\n\t\tcallback: AllNodeEvents[TEvent],\n\t): this {\n\t\treturn this.once(event, callback);\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gBA0CO;AACP,2BAOO;AACP,oCAIO;AACP,qBAKO;AACP,4BAIO;AACP,4BAGO;AACP,qBAA8B;AAC9B,wBAAiC;AACjC,4BAA2C;AAC3C,sCAIO;AACP,oBAAuB;AACvB,oBAA6B;AAC7B,oCAGO;AACP,gCAMO;AACP,0BAA8C;AAC9C,4BAMO;AACP,0BAMO;AACP,+BAAqC;AACrC,yBAKO;AACP,wBAIO;AACP,8BAIO;AACP,uBAKO;AACP,sBAGO;AACP,yBAAkD;AAClD,kBAIO;AACP,oBAAsD;AACtD,kBAyDO;AACP,oBAA2C;AAC3C,uBAIO;AACP,IAAAA,oBAGO;AACP,IAAAA,oBAGO;AACP,IAAAA,oBAA2B;AAC3B,oBAaO;AACP,mBAAqB;AACrB,8BAGO;AACP,kBAAwB;AACxB,wBAAkC;AAClC,yBAA6B;AAC7B,uBAAiB;AACjB,mBAAwB;AACxB,uBAAiC;AACjC,kCAA6B;AAC7B,oBAAwC;AACxC,0BAA0B;AAG1B,yBAA4B;AAG5B,yBAIO;AACP,4BAKO;AACP,mBAWO;AACP,IAAAC,gBAA2C;AAC3C,oBAAgC;AAChC,gBAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE3B,MAAM,mBAAmB;IAeZ,aAAS,MAAA;8BADrB,qBAAM,CAAC,iCAAc,wCAAkB,CAAC,CAAC;;;;oBACX;iCAAA,YAAe;;;;;;AAA9C,mBAAA,MAAA,mBAAA,EAAA,OAAA,WAAA,GAAA,kBAAA,EAAA,MAAA,SAAA,MAAA,WAAA,MAAA,UAAA,UAAA,GAAA,MAAA,uBAAA;;;AAAa,wBAAA,YAAA,uBAAA;;IACZ,YACC,IACA,QACA,aACA,eAAiC,CAAA,GACjC,gBAAkC,CAAA,GAClC,SAAiB;AAEjB;QACC;QACA;;QAEA;QACA;QACA;QACA;MAAO;AAIR,iBAAW,MAAM;AAAe,aAAK,MAAM,IAAI,EAAE,cAAc,KAAI,CAAE;IACtE;;;;IAKO,UAAO;AAEb,WAAK,cAAc,KAAI;AACvB,WAAK,aAAa,KAAI;AAGtB,iBACO,WAAW;QAChB,KAAK,gCAAgC;QACrC,GAAG,KAAK,yBAAyB,OAAM;SAEvC;AACD,YAAI;AAAS,uBAAa,OAAO;MAClC;AAGA,WAAK,mBAAkB;AAGvB,WAAK,wBAAuB;IAC7B;;;;;IAMA,IAAW,MAAG;AACb,aAAO,KAAK,OAAO,SAAS,8BAAU,KAAK,KAAK,EAAE,EAAE,GAAG;IACxD;;IAEA,IAAW,IAAI,OAA6B;AAC3C,YAAM,WAAW,8BAAU,KAAK,KAAK,EAAE,EAAE;AACzC,WAAK,OAAO,SAAS,UAAU,KAAK;IACrC;IAEA,IAAW,iBAAc;AACxB,aAAO,KAAK,SAAS,2DAA6B,eAAe,EAAE;IACpE;IAEA,IAAW,YAAS;AACnB,aAAO,KAAK,SAAS,2DAA6B,UAAU,EAAE;IAC/D;IAEA,IAAW,cAAW;AACrB,aAAO,KAAK,SAAS,2DAA6B,YAAY,EAAE;IACjE;IAEA,IAAW,kBAAe;AAGzB,YAAM,mBAAmB,KAAK,SAC7B,iCAAgB,iBAAiB,EAAE,IAChC,CAAC;AACL,YAAM,qBAAqB,KAAK,SAC/B,iCAAgB,mBAAmB,EAAE;AAGtC,UAAI,MAAM;AACV,UAAI,oBAAoB;AAGvB,YAAI,CAAC,OAAO,mBAAmB,WAAW,GAAG,GAAG,GAAG,GAAG;AACrD,gBAAM;QACP;MACD;AAIA,UAAI,OAAO,KAAK,kBAAkB;AACjC,cAAM,aAAa,KAAK;AACxB,YAAI,cAAc,WAAW,WAAW,GAAG,GAAG,GAAG,GAAG;AACnD,iBAAO;QACR;MACD;AAEA,aAAO;IACR;IAEA,IAAW,kBAAe;AACzB,aAAO,KAAK,SAAS,iCAAgB,gBAAgB,EAAE;IACxD;IAEA,IAAW,aAAU;AACpB,aAAO,KAAK,SAAS,iCAAgB,WAAW,EAAE;IACnD;IAEA,IAAW,mBAAgB;AAC1B,aAAO,KAAK,SAAS,qCAAkB,iBAAiB,EAAE;IAC3D;IAEA,IAAW,oBAAiB;AAC3B,aAAO,KAAK,SAAS,qCAAkB,SAAS,EAAE;IACnD;IAEA,IAAW,oBAAiB;AAC3B,aAAO,KAAK,SAAS,qCAAkB,SAAS,EAAE;IACnD;IAEA,IAAW,yBAAsB;AAChC,aAAO,KAAK,SAAS,+BAAe,wBAAwB,EAAE;IAC/D;;;;;;;IAQA,IAAW,OAAI;AACd,aAAO,KAAK,SAAS,kDAA8B,KAAK,EAAE;IAC3D;IACA,IAAW,KAAK,OAAyB;AACxC,UAAI,SAAS,QAAW;AACvB,aAAK,SAAS,SACb,kDAA8B,KAAK,IACnC,KAAK;MAEP,OAAO;AACN,aAAK,SAAS,YAAY,kDAA8B,KAAK,EAAE;MAChE;IACD;;;;;;;IAQA,IAAW,WAAQ;AAClB,aAAO,KAAK,SAAS,kDAA8B,SAAS,EAAE;IAC/D;IACA,IAAW,SAAS,OAAyB;AAC5C,UAAI,SAAS,QAAW;AACvB,aAAK,SAAS,SACb,kDAA8B,SAAS,IACvC,KAAK;MAEP,OAAO;AACN,aAAK,SAAS,YACb,kDAA8B,SAAS,EAAE;MAE3C;IACD;;IAGA,IAAW,oBAAiB;AAC3B,aAAO,CAAC,CAAC,KAAK,OAAO,SACpB,8BAAU,KAAK,KAAK,EAAE,EAAE,iBAAiB;IAE3C;IACA,IAAW,kBAAkB,OAAc;AAC1C,WAAK,OAAO,SAAS,8BAAU,KAAK,KAAK,EAAE,EAAE,mBAAmB,KAAK;IACtE;IAEQ;;;;IAIR,IAAW,eAAY;AACtB,aAAO,KAAK;IACb;IAEA,IAAW,QAAK;AACf,aAAO,KAAK,eAAe;IAC5B;IAEA,IAAW,oBAAiB;AAC3B,UACC,KAAK,kBAAkB,UACpB,KAAK,eAAe,UACpB,KAAK,aAAa,QACpB;AACD,cAAM,qBAAiB,wBAAS,KAAK,cAAc;AACnD,cAAM,kBAAc,wBAAS,KAAK,WAAW;AAC7C,cAAM,gBAAY,wBAAS,KAAK,SAAS;AACzC,cAAM,kBAAkB,KAAK,mBAAmB;AAChD,eAAO,uCAAuC,cAAc,IAAI,WAAW,IAAI,SAAS,IAAI,eAAe;MAC5G;IACD;;IAGA,IAAW,WAAQ;AAClB,aAAO,KAAK,OAAO,SAAS,8BAAU,KAAK,KAAK,EAAE,EAAE,QAAQ;IAC7D;;IAEA,IAAW,SAAS,OAA0B;AAC7C,WAAK,OAAO,SAAS,8BAAU,KAAK,KAAK,EAAE,EAAE,UAAU,KAAK;AAE5D,WAAK,iBAAiB,CAAC,SAAS;QAC/B,GAAG;QACH,UAAU;QACT;IACH;;;;;IAMA,IAAW,gBAAa;AACvB,aAAO,KAAK,OAAO,SAAS,8BAAU,KAAK,KAAK,EAAE,EAAE,aAAa;IAClE;IAEA,IAAW,cAAc,OAAyB;AACjD,UAAI,SAAS,WAAc,QAAQ,KAAK,QAAQ,MAAM;AACrD,cAAM,IAAI,uBACT,0DACA,4BAAgB,gBAAgB;MAElC;AACA,WAAK,OAAO,SAAS,8BAAU,KAAK,KAAK,EAAE,EAAE,eAAe,KAAK;IAClE;;;;;IAMA,IAAW,4BAAyB;AACnC,aAAO,KAAK,OAAO,SAClB,8BAAU,KAAK,KAAK,EAAE,EAAE,yBAAyB;IAEnD;IAEA,IAAW,0BAA0B,OAAoC;AAExE,UAAI,OAAO,UAAU;AAAU,gBAAQ,qBAAS,KAAK,KAAK;AAC1D,UAAI,qBAAS,WAAW,KAAK;AAAG,gBAAQ,MAAM,SAAQ;AAEtD,WAAK,OAAO,SACX,8BAAU,KAAK,KAAK,EAAE,EAAE,2BACxB,KAAK;IAEP;;;;;IAMA,IAAW,yBAAsB;AAChC,aAAO,KAAK,OAAO,SAAS,8BAAU,KAAK,KAAK,EAAE,EAAE,gBAAgB;IACrE;IAEA,IAAY,uBAAuB,OAA6B;AAC/D,WAAK,OAAO,SAAS,8BAAU,KAAK,KAAK,EAAE,EAAE,kBAAkB,KAAK;IACrE;IAEQ;;;;;IAKR,IAAW,0BAAuB;AACjC,aAAO,KAAK;IACb;;IAGO,qBAAkB;AACxB,aAAO,UAAU,mBAAmB,KAAK,QAAQ,IAAI;IACtD;;;;;IAMO,MAAM,SACZ,SACA,OACA,SAA4B;AAG5B,oBAAU,8BAAiB,OAAO;AAElC,YAAM,WAAW,KAAK,OAAO,aAAY,EAAG;AAG5C,UAAI;AAEH,cAAM,mBAAmB,KAAK,YAAY,QAAQ,YAAY,CAAC;AAC/D,YAAI,CAAC,kBAAkB;AACtB,iBAAO;YACN,QAAQ,2BAAe;YACvB,SACC,YAAY,QAAQ,QAAQ,2BAA2B,KAAK,EAAE;;QAEjE;AACA,YAAI,MAAO,iBAAiB,eAC3B,QAAQ,YAAY;AAGrB,YAAI,CAAC,IAAI,UAAU;AAClB,iBAAO;YACN,QAAQ,2BAAe;YACvB,SAAS,WACR,uBACC,QAAQ,YAAY,CAEtB;;QAEF;AAEA,YAAI,aAAa,SAAS;AACzB,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,UAAU,QAAQ;YAClB,SACC,oCAAoC,IAAI,YAAY,IAAI;kBAC5C,QAAQ,QAAQ;kBAChB,QAAQ,WAAW;kBACnB,IAAI,qBAAqB,OAAO,CAAC;YAC9C,OAAO;WACP;QACF;AAGA,oBAAY,CAAA;AACZ,gBAAQ,uBAAuB,KAAK;AACpC,gBAAQ,WAAW,KAAK;AAExB,cAAM,eAAkC;UACvC,UAAU,QAAQ;UAClB,aAAa,QAAQ;;AAGtB,cAAM,QAAQ,IAAI,gBAAgB,cAAc,OAAO,OAAO;AAE9D,YAAI,OAAO,2BAA2B;AACrC,gBAAM,IAAI,YAAY;YACrB,sBAAsB;YACtB,UAAU,OAAO,WAAU;AAC1B,kBAAI;AACH,oBAAI,OAAO,WAAW,8BAAkB,SAAS;AAChD,wBAAM,MAAM,qBAAoB;gBACjC,WACC,OAAO,WAAW,8BAAkB,MACnC;AACD,wBAAM,MAAM,qBAAoB;gBACjC;cACD,QAAQ;cAER;YACD;WACA;QACF;AAGA,YAAI,OAAO,QAAQ,eAAe,YAAY;AAC7C,gBAAM,IAAI,YAAY;YACrB,YAAY,QAAQ;WACpB;QACF;AAGA,cAAM,SAAS,MAAM,IAAI,SAAU,KAClC,KACA,cACA,OACA,OAAO;AAGR,YAAI,aAAa,SAAS;AACzB,cAAI,UACH,+CAA+C,IAAI,YAAY,IAAI;AACpE,cAAI,QAAQ;AACX,oBAAI,iCAAoB,MAAM,GAAG;AAChC,yBAAW;kBACH,iCAAkB,+BAAmB,OAAO,MAAM,CAAC;AAC3D,kBAAI,OAAO,mBAAmB;AAC7B,2BAAW;cACJ,OAAO,kBAAkB,SAAQ,CAAE;cAC3C;YACD,OAAO;AACN,yBAAW,cACR,KAAK,UAAU,QAAQ,MAAM,CAAC;YAClC;UACD,OAAO;AACN,uBAAW;UACZ;AACA,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,UAAU,QAAQ;YAClB;YACA,OAAO;WACP;QACF;AAKA,YACC,IAAI,qBAAqB,OAAO,SAC7B,uCAA0B,MAAM,GAClC;AACD,gBAAM,YAAY,CAAC,CAAC,UAChB,CAAC,CAAC,KAAK,OAAO,QAAQ;AAE1B,cAAI,aAAa,SAAS;AACzB,kBAAM,UAAU,YACb,8BACA;AACH,iBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;cAC1C,UAAU,QAAQ;cAClB,SAAS,cAAc,OAAO;cAC9B,OAAO;aACP;UACF;AAEA,gBAAMC,WAA2B,CAAA;AAGjC,cAAI,WAAW;AACd,YAAAA,SAAQ,SAAS;UAClB,OAAO;AACN,YAAAA,SAAQ,UAAU;UACnB;AAGA,UAAAA,SAAQ,sBAAkB,wCAA2B,MAAM;AAE3D,eAAK,SAAS,SAAS,SAAS,OAAOA,QAAO;QAC/C,WAAW,aAAa,SAAS;AAChC,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,UAAU,QAAQ;YAClB,SAAS;YACT,OAAO;WACP;QACF;AAIA,YAAI,OAAO;AACV,gBAAM,8BAA0B,iCAAoB,MAAM,KACtD,OAAO,WAAW,8BAAkB;AAExC,gBAAM,6BACL,IAAI,qBAAqB,OAAO,MAE5B,2BAGC,CAAC,KAAK,OAAO,QAAQ,gCACrB,UAAU;AAGhB,cAAI,4BAA4B;AAC/B,kBAAM,oCACL,uBAAuB;UAEzB;AAKA,cACC,KAAC,wCAA2B,MAAM,KAC/B,MAAM,qBAAoB,GAC5B;AAGD,kBAAM,MAAM,gBAAe;UAC5B;QACD;AAEA,mBAAO,+CAAkC,MAAM;MAChD,SAAS,GAAG;AAEX,gBAAI,0BAAa,CAAC,GAAG;AACpB,cAAI;AACJ,kBAAQ,EAAE,MAAM;;YAEf,KAAK,4BAAgB;YACrB,KAAK,4BAAgB;AACpB,uBAAS;gBACR,QAAQ,2BAAe;gBACvB,SAAS,EAAE;;AAEZ;;YAED,KAAK,4BAAgB;AACpB,uBAAS;gBACR,QAAQ,2BAAe;gBACvB,SAAS,EAAE;;AAEZ;UACF;AAEA,cAAI,aAAa,SAAS;AACzB,iBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;cAC1C,UAAU,QAAQ;cAClB,SAAS,iCACR,CAAC,CAAC,SAAS,YAAY,aACxB,cACC,iCACC,6BACA,EAAE,IAAI,CAER,MAAM,EAAE,OAAO;cACf,OAAO;aACP;UACF;AAEA,cAAI;AAAQ,mBAAO;QACpB;AACA,cAAM;MACP;IACD;;;;;IAMO,UACN,SACA,qBAAyC,CAAA,GAAE;AAG3C,oBAAU,8BAAiB,OAAO;AAGlC,YAAM,mBAAmB,KAAK,YAAY,QAAQ,YAAY,CAAC;AAC/D,UAAI,CAAC,kBAAkB;AACtB,cAAM,IAAI,uBACT,YAAY,QAAQ,QAAQ,2BAA2B,KAAK,EAAE,IAC9D,4BAAgB,gBAAgB;MAElC;AAEA,YAAM,MACJ,iBAAiB,eACjB,QAAQ,YAAY,EAEpB,YAAY;;;QAGb,iBAAiB;QACjB,UAAU,4BAAgB;QAC1B,GAAG;OACH;AAGD,UAAI,CAAC,IAAI,WAAW;AACnB,cAAM,IAAI,uBACT,mDACC,uBACC,QAAQ,YAAY,CAEtB,KACA,4BAAgB,QAAQ;MAE1B;AAGA,aAAQ,IAAI,UAAyC,KAAK,KAAK;QAC9D,UAAU,QAAQ;QAClB,aAAa,QAAQ;OACrB;IACF;IAEQ,qBAA6B;;IAErC,IAAW,oBAAiB;AAC3B,aAAO,KAAK;IACb;IAEQ,iCAA0C;IAC1C,iCAA0C;;;;;;;;;IAU3C,MAAM,YAAS;AAGrB,UAAI,KAAK;AAAkB;AAE3B,UAAI,CAAC,KAAK,OAAO,QAAQ,WAAW,oBAAoB;AACvD,cAAM,IAAI,uBACT,gMACA,4BAAgB,sBAAsB;MAExC;AAEA,aAAO,KAAK,OAAO,sBAAsB,IAAI;IAC9C;IAEQ,sBAA+B;;;;;;;;IAShC,MAAM,YAAY,UAA8B,CAAA,GAAE;AAGxD,UAAI,KAAK;AAAkB;AAI3B,UAAI,KAAK;AAAqB;AAC9B,WAAK,sBAAsB;AAE3B,YAAM,EAAE,uBAAuB,OAAO,gBAAgB,KAAI,IAAK;AAE/D,UAAI,YAAY;AAChB,UACC,iBACG,KAAK,YACL,KAAK,WAAW,2BAAe,SAAS,CAAC,GAC3C;AACD,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,wDAAwD;AAEzD,oBAAY,MAAM,KAAK,cAAa,EAClC,KAAK,MAAM,IAAI,EACf,MAAM,MAAM,KAAK;MACpB;AAGA,YAAM,OAAO,KAAK;AAClB,YAAM,WAAW,KAAK;AAGtB,YAAM,kBAAoD,CAAA;AAC1D,YAAM,oBAA+D,CAAA;AACrE,UACC,KAAK,WAAW,2BAAe,WAAW,CAAC,KACxC,CAAC,KAAK,OAAO,QAAQ,UAAU,mBACjC;AACD,cAAM,aAAa,CAAC,MACnB,2BAAiB,SAAS,GAAG,CAAC,KAC3B,2BAAiB,aAAa,GAAG,CAAC,KAClC,2BAAiB,iBAAiB,GAAG,CAAC;AAE1C,cAAM,SAAS,KAAK,QAClB,UAAU,2BAAe,WAAW,CAAC,EACrC,OAAO,UAAU;AACnB,wBAAgB,KAAK,GAAG,MAAM;AAE9B,cAAM,OAAO,KAAK,QAChB,eAAe,2BAAe,WAAW,CAAC,EAC1C,OAAO,UAAU;AACnB,0BAAkB,KAAK,GAAG,IAAI;MAC/B;AAGA,UAAI;AAAsB,aAAK,gBAAgB,MAAK;AAEpD,WAAK,qBAAqB;AAC1B,WAAK,iBAAiB,6BAAe;AACrC,WAAK,QAAQ;AACb,WAAK,cAAc;AACnB,WAAK,cAAc;AACnB,WAAK,sBAAsB;AAC3B,WAAK,YAAY;AACjB,WAAK,qBAAqB;AAC1B,WAAK,kBAAkB;AACvB,WAAK,WAAW;AAChB,WAAK,mBAAmB;AACxB,WAAK,kBAAkB;AACvB,WAAK,gBAAgB;AACrB,WAAK,2BAA2B;AAChC,WAAK,yBAAyB;AAC9B,WAAK,iCAAiC;AACtC,WAAK,iCAAiC;AACtC,iBAAW,MAAM,KAAK,gBAAe,GAAI;AACxC,WAAG,OAAO,EAAC;MACZ;AACA,WAAK,SAAS,MAAM,EAAE,SAAS,KAAI,CAAE;AACrC,WAAK,mBAAmB,MAAK;AAC7B,YAAM,MAAK;AAGX,WAAK,aAAa,QAAO;AACzB,WAAK,cAAc,QAAO;AAG1B,WAAK,wBAAuB;AAG5B,UAAI,QAAQ;AAAW,aAAK,OAAO;AACnC,UAAI,YAAY;AAAW,aAAK,WAAW;AAG3C,iBAAW,EAAE,OAAO,GAAG,QAAO,KAAM,iBAAiB;AACpD,aAAK,QAAQ,SAAS,SAAS,OAAO,EAAE,SAAS,KAAI,CAAE;MACxD;AACA,iBAAW,EAAE,UAAU,GAAG,QAAO,KAAM,mBAAmB;AACzD,aAAK,QAAQ,YAAY,SAAS,UAAU,EAAE,SAAS,KAAI,CAAE;MAC9D;AAGA,WAAK,YAAY;AAIjB,UAAI;AAAW,aAAK,YAAW;AAE/B,WAAK,KAAK,OAAO,sBAAsB,IAAI;AAC3C,WAAK,sBAAsB;IAC5B;;;;;;;;IASO,MAAM,oBAAiB;AAC7B,UAAI,KAAK,mBAAmB,6BAAe,UAAU;AACpD,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,oDAAoD;AAErD,eAAO;MACR,OAAO;AACN,aAAK,OAAO,cAAc,eAAe,IAAI;MAC9C;AAGA,WAAK;AAIL,YAAM,oBAAoB,OACzB,WACqB;AACrB,YAAI;AACH,gBAAM,OAAM;AACZ,iBAAO;QACR,SAAS,GAAG;AACX,kBAAI,iCAAoB,CAAC,GAAG;AAC3B,mBAAO;UACR;AACA,gBAAM;QACP;MACD;AAMA,UAAI,KAAK,mBAAmB,6BAAe,MAAM;AAEhD,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,qCAAqC;AAEtC,aAAK,KAAK,qBAAqB,IAAI;AACnC,cAAM,KAAK,kBAAiB;MAC7B;AAEA,UAAI,CAAC,KAAK,kBAAkB;AAC3B,aACE,KAAK,eAAe,KAAK,wBACvB,KAAK,WAAW,yBAAW,OAC7B;AAED,cAAI,CAAC,MAAM,KAAK,KAAI,GAAI;AAEvB,mBAAO;UACR;QACD;AAEA,YAAI,KAAK,mBAAmB,6BAAe,cAAc;AACxD,cACC,CAAE,MAAM,kBAAkB,MAAM,KAAK,kBAAiB,CAAE,GACvD;AACD,mBAAO;UACR;QACD;AAIA,YAAI,KAAK,mBAAmB,6BAAe,UAAU;AAEpD,cAAI,MAAM,KAAK,aAAY,GAAI;AAC9B,iBAAK,kBAAkB,6BAAe,cAAc;UACrD,OAAO;AACN,mBAAO;UACR;QACD;MACD;AAEA,UACE,KAAK,oBACF,KAAK,mBAAmB,6BAAe,gBACvC,CAAC,KAAK,oBACN,KAAK,mBAAmB,6BAAe,gBAC1C;AAED,cAAM,KAAK,gBAAe;MAC3B;AAGA,WAAK,yBAAyB,MAAM,KAAK,eAAe,QAAO;AAE/D,WAAK,kBAAkB,6BAAe,QAAQ;AAC9C,WAAK,aAAa,KAAK,gBAAgB;AAIvC,WAAK,KAAK,uBAAuB,IAAI;AACrC,aAAO;IACR;;IAGQ,kBAAkB,gBAA8B;AACvD,WAAK,iBAAiB;AACtB,WAAK,KACJ,6BACA,UACA,iCAAkB,8BAAgB,cAAc,CAAC;AAElD,WAAK,OAAO,cAAc,eAAe,IAAI;IAC9C;;IAGU,MAAM,oBAAiB;AAChC,WAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;QAC1C,SAAS;QACT,WAAW;OACX;AAID,WAAK,OAAO,eAAe,IAAI,2BAAa,qBAAqB;QAChE,QAAQ,KAAK;OACb;AACD,YAAM,OAAO,MAAM,KAAK,OAAO,YAC9B,IAAI,6CAA2B;QAC9B,iBAAiB,KAAK;OACtB,CAAC;AAEH,WAAK,cAAc,KAAK;AACxB,WAAK,sBAAsB,KAAK;AAChC,WAAK,YAAY,KAAK;AACtB,WAAK,qBAAqB,KAAK;AAC/B,WAAK,kBAAkB,KAAK;AAC5B,WAAK,WAAW,KAAK;AACrB,WAAK,mBAAmB,KAAK;AAC7B,WAAK,kBAAkB,KAAK;AAE5B,WAAK,cAAc,IAAI,+BACtB,KAAK,kBACL,KAAK,oBACL,KAAK,mBAAmB;AAGzB,YAAM,aAAa;6BAElB,iCAAkB,8BAAkB,KAAK,YAAY,KAAK,CAC3D;yBACuB,KAAK,YAAY,QAAQ,KAAK;yBAC9B,KAAK,YAAY,SAAS,KAAK;6BAC/B,iCAAkB,sBAAU,KAAK,QAAQ,CAAC;yBAC1C,KAAK,WAAW;yBAChB,KAAK,mBAAmB;yBACxB,KAAK,SAAS;yBACd,KAAK,gBAAgB;yBACrB,KAAK,eAAe;yBACpB,KAAK,WAAW;yBAChB,KAAK,eAAe;AAC3C,WAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;QAC1C,SAAS;QACT,WAAW;OACX;AAGD,UAAI,KAAK,UAAU;AAClB,YAAI,KAAK,WAAW,yBAAW,OAAO;AAGrC,eAAK,YAAW;QACjB,WAAW,KAAK,WAAW,yBAAW,OAAO;AAC5C,eAAK,aAAY;QAClB;MACD;AAEA,WAAK,kBAAkB,6BAAe,YAAY;IACnD;;IAGO,MAAM,OAAI;AAChB,UAAI,KAAK,kBAAkB;AAC1B,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,uCACA,MAAM;AAEP,eAAO;MACR;AAEA,WAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;QAC1C,SAAS;QACT,WAAW;OACX;AAED,UAAI;AACH,cAAM,KAAK,eAAe,cAAc,EAAE,KAAI;AAC9C,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SAAS;UACT,WAAW;SACX;AACD,eAAO;MACR,SAAS,GAAG;AACX,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,oBAAgB,+BAAgB,CAAC,CAAC,EAAE;AAErC,eAAO;MACR;IACD;;;;;IAMU,MAAM,oBAAiB;AAChC,UAAI,KAAK,kBAAkB;AAC1B,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,kDACA,MAAM;AAEP;MACD;AAIA,eAAS,WAAW,GAAG,YAAY,GAAG,YAAY;AACjD,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SAAS;UACT,WAAW;SACX;AACD,YAAI;AACH,gBAAM,WAAW,MAAM,KAAK,gBAAe;AAC3C,gBAAM,WAAqB;YAC1B;YACA;;AAED,qBAAW,MAAM,SAAS,cAAc;AACvC,qBAAS,KAAK,YAAK,uBAAU,EAAE,CAAC,EAAE;UACnC;AACA,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SAAS,SAAS,KAAK,IAAI;YAC3B,WAAW;WACX;AACD,eAAK,eAAe,QAAQ;AAC5B;QACD,SAAS,GAAG;AACX,kBAAI,0BAAa,CAAC,GAAG;AACpB,gBACC,aAAa,KACV,KAAK,YACL,KAAK,WAAW,yBAAW,UAC3B,EAAE,SAAS,4BAAgB,wBAC7B;AACD,mBAAK,OAAO,cAAc,QACzB,KAAK,IACL,wFACA,OAAO;AAGR,mBAAK,aAAY;AAEjB;YACD;AAEA,gBACC,EAAE,SAAS,4BAAgB,0BACxB,EAAE,SAAS,4BAAgB,wBAC7B;AACD,mBAAK,OAAO,cAAc,QACzB,KAAK,IACL,iCACA,OAAO;YAET;AACA,kBAAM;UACP;QACD;MACD;AAEA,WAAK,kBAAkB,6BAAe,QAAQ;IAC/C;IAEO,MAAM,kBAAe;AAC3B,YAAM,OAAO,MAAM,KAAK,OAAO,YAE7B,IAAI,yCAAuB,EAAE,QAAQ,KAAK,GAAE,CAAE,CAAC;AACjD,UAAI,gBAAgB,6CAA2B,CAAC,KAAK,SAAS;AAE7D,cAAM,IAAI,uBACT,iCACA,4BAAgB,sBAAsB;MAExC,WACC,gBAAgB,gEACf;AAED,cAAM,IAAI,uBACT,iCACA,4BAAgB,sBAAsB;MAExC,WAAW,gBAAgB,2DAA0C;AACpE,cAAM,WAAqB,CAAC,sBAAsB,gBAAgB;AAClE,mBAAW,MAAM,KAAK,gBAAgB,cAAc;AACnD,mBAAS,KAAK,YAAK,uBAAU,EAAE,CAAC,EAAE;QACnC;AACA,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SAAS,SAAS,KAAK,IAAI;UAC3B,WAAW;SACX;AACD,eAAO,KAAK;MACb;AACA,YAAM,IAAI,uBACT,0DACA,4BAAgB,uBAAuB;IAEzC;;;;IAKU,MAAM,mBAAgB;AAE/B,UACC,KAAK,kBAAkB,UACpB,KAAK,eAAe,UACpB,KAAK,aAAa,QACpB;AAED,aAAK,gBAAgB,MAAM,KAAK,OAAO,cAAc,aACpD,KAAK,gBACL,KAAK,aACL,KAAK,WACL,KAAK,eAAe;AAErB,YAAI,KAAK,eAAe;AAEvB,cAAI,KAAK,wBAAwB,WAAW,IAAI;AAE/C,iBAAK,2BAA2B,MAAM,KAAK,cACzC,QAAQ,KAAK;UAChB,OAAO;AACN,iBAAK,2BAA2B,MAAM,KAAK,cACzC,QAAO;UACV;AACA,eAAK,OAAO,cAAc,QACzB,KAAK,IACL,GACC,KAAK,cAAc,aAChB,aACA,eACJ,uBAAuB;QAEzB,OAAO;AACN,eAAK,OAAO,cAAc,QACzB,KAAK,IACL,0BACA,MAAM;QAER;MACD;IACD;;IAGU,MAAM,eAAY;AAC3B,UAAI,KAAK,kBAAkB;AAC1B,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,gDACA,MAAM;AAEP,eAAO;MACR;AAEA,YAAM,mBAAmB,KAAK,OAAO,oBAAoB,KAAK,EAAE;AAKhE,YAAM,oBAAoB,OACzB,UACA,IACA,QAAiB,UACsB;AACvC,YAAI;AACJ,YAAI;AACH,cAAI,OAAO;AACV,uBAAW,uBAAa,wBACvB,UACA,EAAE;UAEJ,OAAO;AACN,uBAAW,SAAS,iBAAiB,EAAE;UACxC;QACD,SAAS,GAAG;AACX,kBACC,0BAAa,CAAC,KACX,EAAE,SAAS,4BAAgB,iBAC7B;AAGD,mBAAO;UACR;AAEA,gBAAM;QACP;AACA,YACC,SAAS,WAAW,EAAE,KACnB,CAAC,KAAK,OAAO,mBACb,CAAC,kBACH;AAGD,eAAK,OAAO,cAAc,QACzB,KAAK,IACL,wCACC,uBACC,EAAE,CAEJ,0CACA,OAAO;AAER,iBAAO;QACR;AAGA,YAAI,SAAS,oBAAoB,KAAK,MAAM;AAAG,iBAAO;AAEtD,YAAI;AACH,gBAAM,SAAS,UAAU,KAAK,MAAM;QACrC,SAAS,GAAG;AACX,kBAAI,iCAAoB,CAAC,GAAG;AAG3B,mBAAO;UACR;AAEA,gBAAM;QACP;MACD;AAGA,UAAI,KAAK,WAAW,2BAAe,YAAY,CAAC,GAAG;AAElD,aAAK,MAAM,2BAAe,YAAY,GAAG,EAAE,QAAQ,KAAI,CAAE;AAGzD,cAAM,gBAAgB,KAAK,wBAAuB;AAClD,YACC,iBAAiB,cACd,+BAAkB,aAAa,GACjC;AACD,eAAK,OAAO,cAAc,QACzB,KAAK,IACL,sCACA,OAAO;AAGR,cAAI,CAAC,kBAAkB;AACtB,gBAAI,CAAC,KAAK,gCAAgC;AAEzC,oBAAM,eACL;AACD,mBAAK,OAAO,cAAc,QACzB,KAAK,IACL,cACA,OAAO;AAER,mBAAK,OAAO,KACX,SACA,IAAI,uBACH,QACC,KAAK,GAAG,SAAQ,EAAG,SAClB,GACA,GAAG,CAEL,IAAI,YAAY,IAChB,4BACE,oCAAoC,CACtC;AAEF,mBAAK,iCAAiC;YACvC;UACD,OAAO;AACN,kBAAM,SAAS,MAAM,kBACpB,MACA,2BAAe,YAAY,CAAC;AAE7B,gBAAI,OAAO,WAAW;AAAW,qBAAO;UACzC;QACD;MACD,OAAO;AAEN,mBACO,YAAY;UACjB,0BAAc;UACd,0BAAc;UACd,0BAAc;WAEd;AACD,cAAI,KAAK,iBAAiB,QAAQ,MAAM,uBAAW;AAClD,iBAAK,gBAAgB,IAAI,UAAU,KAAK;UACzC;QACD;MACD;AAEA,UAAI,KAAK,WAAW,2BAAe,QAAQ,GAAG;AAE7C,aAAK,MAAM,2BAAe,UAAU,EAAE,QAAQ,KAAI,CAAE;AAGpD,YAAI,KAAK,iBAAiB,0BAAc,SAAS,MAAM,OAAO;AAC7D,eAAK,OAAO,cAAc,QACzB,KAAK,IACL,sCACA,OAAO;AAGR,cAAI,CAAC,KAAK,OAAO,iBAAiB;AACjC,gBAAI,CAAC,KAAK,gCAAgC;AAEzC,oBAAM,eACL;AACD,mBAAK,OAAO,cAAc,QACzB,KAAK,IACL,cACA,OAAO;AAER,mBAAK,OAAO,KACX,SACA,IAAI,uBACH,QACC,KAAK,GAAG,SAAQ,EAAG,SAClB,GACA,GAAG,CAEL,IAAI,YAAY,IAChB,4BACE,oCAAoC,CACtC;AAEF,mBAAK,iCAAiC;YACvC;UACD,OAAO;AACN,kBAAM,SAAS,MAAM,kBACpB,MACA,2BAAe,QAAQ;AAExB,gBAAI,OAAO,WAAW;AAAW,qBAAO;UACzC;QACD;MACD,OAAO;AACN,YAAI,KAAK,iBAAiB,0BAAc,SAAS,MAAM,uBAAW;AAEjE,eAAK,gBAAgB,IAAI,0BAAc,WAAW,KAAK;QACxD;MACD;AAIA,UAAI,KAAK,WAAW,2BAAe,uBAAuB,CAAC,GAAG;AAC7D,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,gDACA,OAAO;AAGR,cAAM,SAAS,MAAM,kBACpB,MACA,2BAAe,uBAAuB,CAAC;AAExC,YAAI,OAAO,WAAW;AAAW,iBAAO;MACzC;AAEA,UAAI,KAAK,WAAW,2BAAe,OAAO,GAAG;AAC5C,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,kCACA,OAAO;AAGR,cAAM,SAAS,MAAM,kBACpB,MACA,2BAAe,OAAO;AAEvB,YAAI,OAAO,WAAW;AAAW,iBAAO;AAGxC,cAAM,KAAK,iBAAgB;AAG3B,aAAK,8BAA6B;MACnC,OAAO;AACN,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,kFACA,OAAO;AAGR,mBAAW,CAAC,MAAM,IAAI,KAAK,KAAK,OAAM,GAAI;AACzC,cACC,KAAK,eAEF,SAAS,2BAAe,OAC1B;AACD,iBAAK,MAAM,MAAM,EAAE,aAAS,iCAAsB,IAAI,EAAC,CAAE;UAC1D;QACD;MACD;AAGA,UAAI,KAAK,WAAW,2BAAe,SAAS,CAAC,GAAG;AAC/C,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,kCACA,OAAO;AAGR,cAAM,SAAS,MAAM,kBACpB,MACA,2BAAe,SAAS,CAAC;AAE1B,YAAI,OAAO,WAAW;AAAW,iBAAO;MACzC;AAEA,WAAK,iCAAiC,IAAI;AAO1C,YAAM,aAAa;QAClB,2BAAe;QACf,2BAAe,YAAY;QAC3B,2BAAe,uBAAuB;QACtC,2BAAe;QACf,2BAAe,SAAS;;QAExB,2BAAe;;AAEhB,YAAM,oCAAoC,KAAK,sBAAsB;QACpE,GAAG;QACH,GAAG;OACH;AACD,UAAI;AAEJ,YAAM,mCAAmC,KAAK,sBAAsB;QACnE,GAAG;QACH,GAAG;OACH;AACD,UAAI;AAEJ,UAAI;AACH,gDAAoC,6BACnC,iCAAiC;AAElC,+CAAmC,6BAClC,gCAAgC;MAElC,QAAQ;AAEP,cAAM,IAAI,uBACT,6FACA,4BAAgB,UAAU;MAE5B;AAEA,WAAK,OAAO,cAAc,QACzB,KAAK,IACL,4CACC,kCACE,IAAI,CAAC,OAAO;WAAO,uBAAU,EAAE,CAAC,EAAE,EAClC,KAAK,EAAE,CACV,IACA,OAAO;AAGR,WAAK,OAAO,cAAc,QACzB,KAAK,IACL,2CACC,iCACE,IAAI,CAAC,OAAO;WAAO,uBAAU,EAAE,CAAC,EAAE,EAClC,KAAK,EAAE,CACV,IACA,OAAO;AAIR,iBAAW,MAAM,mCAAmC;AACnD,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,8BAA0B,uBAAU,EAAE,CAAC,IACvC,OAAO;AAGR,cAAM,SAAS,MAAM,kBAAkB,MAAM,EAAE;AAC/C,YAAI,WAAW;AAAY;iBAClB,OAAO,WAAW;AAAW,iBAAO;MAC9C;AAIA,WAAK,8BAA6B;AAGlC,iBAAW,iBAAiB,KAAK,mBAAkB,GAAI;AACtD,cAAM,WAAW,KAAK,YAAY,aAAa;AAC/C,YAAI,CAAC;AAAU;AAGf,cAAM,gBAAgB,KAAK,wBAAuB;AAWlD,cAAM,wBAAoB,+BAAkB,aAAa,KACrD,KAAK,WAAW,2BAAe,YAAY,CAAC,KAC5C,CAAC,SAAS,WAAW,2BAAe,YAAY,CAAC;AACrD,YAAI,mBAAmB;AACtB,mBAAS,MACR,2BAAe,YAAY,GAC3B,KAAK,0BAA0B,IAC9B,2BAAe,YAAY,CAAC,CAC3B;QAEJ;AAIA,YAAI,SAAS,WAAW,2BAAe,YAAY,CAAC,GAAG;AAEtD,mBAAS,MAAM,2BAAe,YAAY,GAAG,EAAE,QAAQ,KAAI,CAAE;AAG7D,kBACC,+BAAkB,aAAa,KAC5B,CAAC,CAAC,kBACJ;AACD,iBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;cAC1C,UAAU,SAAS;cACnB,SACC,YAAY,SAAS,KAAK;cAC3B,OAAO;aACP;AAED,kBAAM,SAAS,MAAM,kBACpB,UACA,2BAAe,YAAY,CAAC;AAE7B,gBAAI,OAAO,WAAW;AAAW,qBAAO;UACzC;QACD;AAEA,YAAI,SAAS,WAAW,2BAAe,QAAQ,GAAG;AAEjD,mBAAS,MAAM,2BAAe,UAAU,EAAE,QAAQ,KAAI,CAAE;AAGxD,cACC,kBAAkB,0BAAc,aAC7B,CAAC,CAAC,KAAK,OAAO,iBAChB;AACD,iBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;cAC1C,UAAU,SAAS;cACnB,SACC,YAAY,SAAS,KAAK;cAC3B,OAAO;aACP;AAED,kBAAM,SAAS,MAAM,kBACpB,UACA,2BAAe,QAAQ;AAExB,gBAAI,OAAO,WAAW;AAAW,qBAAO;UACzC;QACD;AAOA,cAAM,oBAAoB,kBAAkB,0BAAc,aACtD,KAAK,WAAW,2BAAe,QAAQ,KACvC,CAAC,SAAS,WAAW,2BAAe,QAAQ;AAEhD,YAAI,mBAAmB;AAEtB,gBAAM,gBAIA;YACL;cACC,MAAM,2BAAe,kBAAkB;cACvC,MAAM,MACL,SAAS,eAAe,kBAAkB,EAAE,IAAG;;YAEjD;cACC,MAAM,2BAAe,eAAe;cACpC,MAAM,MACL,SAAS,eAAe,eAAe,EAAE,IAAG;;YAE9C;cACC,MAAM,2BAAe,eAAe;cACpC,MAAM,MACL,SAAS,eAAe,eAAe,EAAE,IAAG;;YAE9C;cACC,MAAM,2BAAe,mBAAmB;cACxC,MAAM,MACL,SAAS,eAAe,mBAAmB,EAAE,IAAG;;YAElD;cACC,MAAM,2BAAe,mBAAmB;cACxC,MAAM,MACL,SAAS,eAAe,mBAAmB,EAAE,IAAG;;;;AAKnD,gBAAM,YAAY,cAAc,KAAK,CAAC,MACrC,SAAS,WAAW,EAAE,IAAI,CAAC;AAE5B,cAAI,WAAW;AACd,iBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;cAC1C,UAAU,SAAS;cACnB,SACC,+CAA+C,SAAS,KAAK;cAC9D,OAAO;aACP;AAED,kBAAM,EAAE,MAAM,KAAI,IAAK;AAGvB,qBAAS,MAAM,MAAM,EAAE,QAAQ,KAAI,CAAE;AAGrC,kBAAM,UAAU,CAAC,CAAE,MAAM,KAAI,EAAG,MAAM,MAAM,KAAK;AAEjD,gBAAI,SAAS;AACZ,mBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;gBAC1C,UAAU,SAAS;gBACnB,SACC,YAAY,SAAS,KAAK;gBAC3B,OAAO;eACP;AAED,yBAAW,CAACC,KAAI,KAAK,SAAS,OAAM,GAAI;AACvC,yBAAS,MAAMA,OAAM,EAAE,QAAQ,KAAI,CAAE;cACtC;YACD,OAAO;AACN,mBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;gBAC1C,UAAU,SAAS;gBACnB,SACC,YAAY,SAAS,KAAK;gBAC3B,OAAO;eACP;AAED,uBAAS,MAAM,MAAM,EAAE,QAAQ,MAAK,CAAE;YACvC;UACD,OAAO;AACN,iBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;cAC1C,UAAU,SAAS;cACnB,SACC,+CAA+C,SAAS,KAAK;cAC9D,OAAO;aACP;UACF;QACD;AAKA,YAAI,KAAK,WAAW,2BAAe,OAAO,GAAG;AAC5C,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,UAAU,SAAS;YACnB,SAAS,YAAY,SAAS,KAAK,mBAClC,uBACC,2BAAe,OAAO,CAExB;YACA,OAAO;WACP;AAED,gBAAM,SAAS,MAAM,kBACpB,UACA,2BAAe,SACf,IAAI;AAEL,cAAI,OAAO,WAAW;AAAW,mBAAO;QACzC,OAAO;AACN,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,UAAU,SAAS;YACnB,SACC;YACD,OAAO;WACP;AAED,qBAAW,CAAC,MAAM,IAAI,KAAK,SAAS,OAAM,GAAI;AAC7C,gBACC,KAAK,eAEF,SAAS,2BAAe,OAC1B;AACD,uBAAS,MAAM,MAAM;gBACpB,aAAS,iCAAsB,IAAI;eACnC;YACF;UACD;QACD;AAIA,aAAK,8BAA8B,SAAS,KAAK;AAGjD,aAAK,iCAAiC,QAAQ;AAE9C,cAAM,yBAAyB,SAAS,sBAAsB;UAC7D,2BAAe;UACf,2BAAe,YAAY;UAC3B,2BAAe;UACf,2BAAe;SACf;AACD,YAAI;AACJ,YAAI;AACH,uCAAyB,6BACxB,sBAAsB;QAExB,QAAQ;AAEP,gBAAM,IAAI,uBACT,6FACA,4BAAgB,UAAU;QAE5B;AAEA,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,UAAU,SAAS;UACnB,SAAS,YAAY,SAAS,KAAK,qBAClC,uBACE,IAAI,CAAC,OAAO;WAAO,uBAAU,EAAE,CAAC,EAAE,EAClC,KAAK,EAAE,CACV;UACA,OAAO;SACP;AAGD,mBAAW,MAAM,wBAAwB;AACxC,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,UAAU,SAAS;YACnB,SAAS,YAAY,SAAS,KAAK,mBAClC,uBACC,EAAE,CAEJ;YACA,OAAO;WACP;AAED,gBAAM,SAAS,MAAM,kBAAkB,UAAU,EAAE;AACnD,cAAI,WAAW;AAAY;mBAClB,OAAO,WAAW;AAAW,mBAAO;QAC9C;MACD;AAGA,iBAAW,MAAM,kCAAkC;AAClD,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,8BAA0B,uBAAU,EAAE,CAAC,IACvC,OAAO;AAGR,cAAM,SAAS,MAAM,kBAAkB,MAAM,EAAE;AAC/C,YAAI,WAAW;AAAY;iBAClB,OAAO,WAAW;AAAW,iBAAO;MAC9C;AAIA,YAAM,SAAS,KAAK,cAAc;AAClC,UACC,CAAC,KAAK,sBAAsB,2BAAe,KAAK,KAC7C,KAAK,aAAa,2BAAe,KAAK,IAAI,GAC5C;AACD,YAAI,KAAK,kBAAiB,GAAI;AAG7B,eAAK,OAAO,cAAc,QACzB,KAAK,IACL,8BAA0B,uBAAU,2BAAe,KAAK,CAAC,IACzD,OAAO;AAGR,gBAAM,SAAS,MAAM,kBACpB,MACA,2BAAe,OACf,IAAI;AAEL,cAAI,OAAO,WAAW;AAAW,mBAAO;QACzC,OAAO;AAEN,cACC,QAAQ,mBAAmB,SACxB,QAAQ,gBAAgB,UAC1B;AAED,iBAAK,MAAM,2BAAe,OAAO,EAAE,cAAc,KAAI,CAAE;UACxD;QACD;MACD;AAGA,iBAAW,iBAAiB,KAAK,mBAAkB,GAAI;AACtD,cAAM,WAAW,KAAK,YAAY,aAAa;AAC/C,YAAI,CAAC;AAAU;AACf,YAAI,SAAS,sBAAsB,2BAAe,KAAK;AAAG;AAC1D,YAAI,SAAS,aAAa,2BAAe,KAAK,MAAM;AAAG;AAEvD,YAAI,SAAS,kBAAiB,GAAI;AAGjC,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,UAAU,SAAS;YACnB,SAAS,YAAY,SAAS,KAAK;YACnC,OAAO;WACP;AAED,gBAAM,SAAS,MAAM,kBACpB,UACA,2BAAe,OACf,IAAI;AAEL,cAAI,OAAO,WAAW;AAAW,mBAAO;QACzC,OAAO;AAEN,cACC,QAAQ,mBAAmB,SACxB,QAAQ,gBAAgB,UAC1B;AAED,qBAAS,MAAM,2BAAe,OAAO;cACpC,cAAc;aACd;UACF;QACD;MACD;AAEA,aAAO;IACR;;;;;IAMO,eAAe,UAA2B;AAChD,UAAI,KAAK,iBAAiB,6BAAe,UAAU;AAClD,mBAAW,MAAM,SAAS,cAAc;AACvC,cAAI,OAAO,2BAAe,OAAO;AAEhC;UACD;AACA,eAAK,MAAM,IAAI,EAAE,aAAa,KAAI,CAAE;QACrC;MACD;AAGA,WAAK,YAAW;AAShB,UACC,KAAK,mBAAmB,6BAAe,YACpC,CAAC,KAAK,WAAW,2BAAe,kBAAkB,CAAC,MAClD,CAAC,KAAK,QAAQ,SAAS,yCAAoB,YAAY,EAAE,KACzD,CAAC,UAAAC,MAAQ,2CACX,KAAK,QACL,IAAI,IAEL;AACD,cAAM,QAAQ,KAAK,cAAc,QAAQ,6BACrC;AACJ,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SACC,gFACC,QAAQ,IAAI,OAAO,KAAK,QAAQ,EACjC;SACD;AACD,mBAAW,MAAM,KAAK,cAAa,GAAI,KAAK;MAC7C;IACD;;;;;;;IAQO,MAAM,YAAY,IAAkB;AAC1C,YAAM,YAAY,KAAK,gBAAe;AAEtC,gBAAU,KAAK,UAAU,MAAK,CAAG;AACjC,iBAAW,YAAY,WAAW;AACjC,cAAM,WAAW,SAAS,uBAAuB,EAAE;AACnD,YAAI,UAAU;AACb,cAAI;AACH,kBAAM,SAAS,UAAU,KAAK,MAAM;UACrC,SAAS,GAAG;AACX,iBAAK,OAAO,cAAc,QACzB,KAAK,IACL,8BACC,uBAAU,EAAE,CACb,cAAc,SAAS,KAAK,SAAK,+BAAgB,CAAC,CAAC,IACnD,OAAO;UAET;QACD;MACD;IACD;;;;;IAMO,MAAM,gBAAgB,IAAkB;AAC9C,iBAAW,YAAY,KAAK,gBAAe,GAAI;AAC9C,cAAM,WAAW,SAAS,uBAAuB,EAAE;AACnD,YAAI,UAAU;AACb,cAAI;AACH,kBAAM,SAAS,cAAc,KAAK,MAAM;UACzC,SAAS,GAAG;AACX,iBAAK,OAAO,cAAc,QACzB,KAAK,IACL,oCACC,uBACC,EAAE,CAEJ,cAAc,SAAS,KAAK,SAAK,+BAAgB,CAAC,CAAC,IACnD,OAAO;UAET;QACD;MACD;IACD;;;;;IAMO,MAAM,gBAAa;AACzB,iBAAW,YAAY,KAAK,gBAAe,GAAI;AAC9C,mBAAW,MAAM,SAAS,wBAAuB,GAAI;AAEpD,cACC,CAAC,wBAAY,SAAS,GAAG,IAAI,KAC1B,CAAC,sBAAU,SAAS,GAAG,IAAI,GAC7B;AACD;UACD;AACA,cAAI;AACH,kBAAM,GAAG,cAAc,KAAK,MAAM;UACnC,SAAS,GAAG;AACX,iBAAK,OAAO,cAAc,QACzB,KAAK,IACL,oCACC,uBACC,GAAG,IAAI,CAET,cAAc,SAAS,KAAK,SAAK,+BAAgB,CAAC,CAAC,IACnD,OAAO;UAET;QACD;MACD;IACD;;;;;IAMO,MAAM,oBAAiB;AAE7B,UAAI,KAAK,WAAW,yBAAW;AAAM;AAErC,iBAAW,YAAY,KAAK,gBAAe,GAAI;AAC9C,mBACO,MAAM,SACV,wBAAuB,GAGxB;AACD,cAAI,CAAC,GAAG,oBAAoB,KAAK,MAAM;AAAG;AAE1C,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SAAS,OACR,uBACC,GAAG,IAAI,CAET;YACA,UAAU,SAAS;YACnB,WAAW;WACX;AAED,cAAI;AACH,kBAAM,GAAG,cAAc,KAAK,MAAM;UACnC,SAAS,GAAG;AACX,iBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;cAC1C,SAAS,oCACR,uBACC,GAAG,IAAI,CAET,YAAQ,+BAAgB,CAAC,CAAC;cAC1B,UAAU,SAAS;cACnB,OAAO;aACP;UACF;QACD;MACD;IACD;;;;;;IAOQ,8BAA8B,eAAsB;AAC3D,UAAI,KAAK,cAAc;AAEtB,cAAM,SAAS,KAAK,aAAa,QAAQ;AACzC,YAAI,QAAQ;AACX,qBAAW,CAAC,IAAI,EAAE,UAAS,CAAE,KAAK,QAAQ;AACzC,gBAAI,kBAAkB,QAAW;AAChC,yBAAW,CAAC,IAAI,IAAI,KAAK,WAAW;AACnC,qBAAK,YAAY,EAAE,GAAG,MAAM,IAAI,IAAI;cACrC;YACD,WAAW,UAAU,IAAI,aAAa,GAAG;AACxC,mBAAK,YAAY,aAAa,GAAG,MAChC,IACA,UAAU,IAAI,aAAa,CAAE;YAE/B;UACD;QACD;AAEA,cAAM,YAAY,KAAK,aAAa,QAAQ;AAC5C,YAAI,WAAW;AACd,qBAAW,CAAC,IAAI,SAAS,KAAK,WAAW;AACxC,gBAAI,cAAc,KAAK;AACtB,kBAAI,kBAAkB,QAAW;AAChC,2BAAW,MAAM,KAAK,gBAAe,GAAI;AACxC,qBAAG,SAAS,EAAE;gBACf;cACD,OAAO;AACN,qBAAK,YAAY,aAAa,GAAG,SAAS,EAAE;cAC7C;YACD,OAAO;AACN,kBAAI,kBAAkB,QAAW;AAChC,2BAAW,MAAM,WAAW;AAC3B,uBAAK,YAAY,EAAE,GAAG,SAAS,EAAE;gBAClC;cACD,WAAW,UAAU,SAAS,aAAa,GAAG;AAC7C,qBAAK,YAAY,aAAa,GAAG,SAAS,EAAE;cAC7C;YACD;UACD;QACD;MACD;IACD;;;;;IAMQ,iCAAiC,UAAkB;AAK1D,UACC,SAAS,WAAW,2BAAe,mBAAmB,CAAC,KACpD,SAAS,WAAW,2BAAe,iBAAiB,CAAC,GACvD;AACD,iBAAS,SAAS,2BAAe,mBAAmB,CAAC;MACtD;IACD;;IAGU,MAAM,kBAAe;AAC9B,UAAI,KAAK,kBAAkB;AAG1B,cAAM,KAAK,iBAAgB;MAC5B;AAEA,UAAI,KAAK,cAAc;AAEtB,cAAM,SAAS,KAAK,aAAa,QAAQ;AACzC,YAAI,QAAQ;AACX,qBAAW,CAAC,IAAI,EAAE,UAAS,CAAE,KAAK,QAAQ;AACzC,uBAAW,CAAC,IAAI,IAAI,KAAK,WAAW;AACnC,mBAAK,YAAY,EAAE,GAAG,MAAM,IAAI,IAAI;YACrC;UACD;QACD;AAEA,cAAM,YAAY,KAAK,aAAa,QAAQ;AAC5C,YAAI,WAAW;AACd,qBAAW,CAAC,IAAI,SAAS,KAAK,WAAW;AACxC,gBAAI,cAAc,KAAK;AACtB,yBAAW,MAAM,KAAK,gBAAe,GAAI;AACxC,mBAAG,SAAS,EAAE;cACf;YACD,OAAO;AACN,yBAAW,MAAM,WAAW;AAC3B,qBAAK,YAAY,EAAE,GAAG,SAAS,EAAE;cAClC;YACD;UACD;QACD;MACD;AAEA,WAAK,kBAAkB,6BAAe,eAAe;IACtD;;;;;IAMO,MAAM,cAAc,SAAqB;AAI/C,UACC,QAAQ,kBAAkB,KACvB,QAAQ,YAAY,KAAK,SAAS,QAAQ,KAC1C,KAAK,iBAAgB,KAAM,KAE3B,KAAK,eAAe,QAAQ,4BAA4B,QAC1D;AACD,cAAMC,YAAW,KAAK,YACrB,KAAK,eAAe,QAAQ,wBAAwB;AAErD,YAAIA,aAAYA,UAAS,WAAW,QAAQ,IAAI,GAAG;AAElD,eAAK,OAAO,cAAc,QACzB,KAAK,IACL,4DAA4DA,UAAS,KAAK,EAAE;AAE7E,kBAAQ,gBAAgBA,UAAS;AACjC,kBAAQ,cAAc,KAAK,MAAM;QAClC;MACD;AAGA,UACC,QAAQ,YAAY,KAAK,SAAS,KAAK,KAEpC,EAAE,mBAAmB,yCACrB,EAAE,mBAAmB,yCACvB;AACD,aAAK,YAAW;MACjB;AAGA,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa;AACvD,UAAI,UAAU,sBAAsB,QAAQ,IAAI,GAAG;AAClD,aAAK,OAAO,cAAc,QACzB,KAAK,IACL;UACC,UAAU,SAAS;UACnB,WAAW;UACX,SACC,YAAY,QAAQ,YAAY,IAAI;SACrC;AAEF;MACD;AAEA,UAAI,mBAAmB,wBAAS;AAC/B,eAAO,KAAK,mBAAmB,OAAO;MACvC,WAAW,mBAAmB,8CAAoB;AACjD,eAAO,KAAK,8BAA8B,OAAO;MAClD,WAAW,mBAAmB,yCAAmB;AAChD,eAAO,KAAK,0BAA0B,OAAO;MAC9C,WAAW,mBAAmB,6CAAqB;AAClD,eAAO,KAAK,4BAA4B,OAAO;MAChD,WAAW,mBAAmB,kDAA4B;AACzD,eAAO,KAAK,+BAA+B,OAAO;MACnD,WAAW,mBAAmB,4CAA4B;AACzD,eAAO,KAAK,yBAAwB;MACrC,WAAW,mBAAmB,4CAAsB;AACnD,eAAO,KAAK,yBAAyB,OAAO;MAC7C,WAAW,mBAAmB,8BAAe;AAC5C,eAAO,KAAK,kBAAkB,OAAO;MACtC,WAAW,mBAAmB,sCAAoB;AACjD,eAAO,KAAK,uBAAsB;MACnC,WAAW,mBAAmB,yCAAuB;AACpD,eAAO,KAAK,0BAA0B,OAAO;MAC9C,WAAW,mBAAmB,kDAAgC;AAC7D,eAAO,KAAK,mCAAmC,OAAO;MACvD,WAAW,mBAAmB,wCAAqB;AAClD,eAAO,KAAK,wBAAuB;MACpC,WAAW,mBAAmB,2CAAwB;AACrD,eAAO,KAAK,2BAA2B,OAAO;MAC/C,WAAW,mBAAmB,oDAAiC;AAC9D,eAAO,KAAK,oCAAoC,OAAO;MACxD,WAAW,mBAAmB,sBAAQ;AACrC,eAAO,KAAK,WAAW,OAAO;MAC/B,WAAW,mBAAmB,6DAA6B;AAC1D,eAAO,KAAK,kCAAkC,OAAO;MACtD,WAAW,mBAAmB,qEAAqC;AAClE,eAAO,KAAK,gCAAgC,OAAO;MACpD,WAAW,mBAAmB,kDAA4B;AACzD,eAAO,KAAK,+BAA+B,OAAO;MACnD,WAAW,mBAAmB,yBAAe;AAC5C,eAAO,KAAK,cAAc,OAAO;MAClC,WAAW,mBAAmB,yBAAe;AAC5C,eAAO,KAAK,cAAc,OAAO;MAClC,WAAW,mBAAmB,+BAAqB;AAClD,eAAO,KAAK,oBAAoB,OAAO;MACxC,WAAW,mBAAmB,mCAAgB;AAC7C,eAAO,KAAK,mBAAmB,OAAO;MACvC,WAAW,mBAAmB,+BAAc;AAC3C,eAAO,KAAK,iBAAiB,OAAO;MACrC,WAAW,mBAAmB,2CAA0B;AACvD,eAAO,KAAK,6BAA6B,OAAO;MACjD,WAAW,mBAAmB,2CAA0B;AACvD,eAAO,KAAK,6BAA6B,OAAO;MACjD,WAAW,mBAAmB,yDAA2B;AACxD,eAAO,KAAK,8BAA8B,OAAO;MAClD,WAAW,mBAAmB,6DAA+B;AAC5D,eAAO,KAAK,iBAAiB,OAAO;MACrC,WAAW,mBAAmB,6DAA+B;AAC5D,eAAO,KAAK,iBAAiB,OAAO;MACrC,WAAW,mBAAmB,oEAAsC;AACnE,eAAO,KAAK,wBAAwB,OAAO;MAC5C,WAAW,mBAAmB,yDAAoC;AACjE,eAAO,KAAK,uCAAuC,OAAO;MAC3D,WAAW,mBAAmB,uCAAkB;AAC/C,eAAO,KAAK,qBAAqB,OAAO;MACzC,WAAW,mBAAmB,uCAAkB;AAC/C,eAAO,KAAK,qBAAqB,OAAO;MACzC,WAAW,mBAAmB,0CAAqB;AAClD,eAAO,KAAK,wBAAwB,OAAO;MAC5C,WAAW,mBAAmB,oDAA+B;AAC5D,eAAO,KAAK,kCAAkC,OAAO;MACtD,WACC,mBAAmB,0DAClB;AACD,eAAO,KAAK,mDACX,OAAO;MAET,WAAW,mBAAmB,wCAA8B;AAC3D,eAAO,KAAK,iCAAiC,OAAO;MACrD,WAAW,mBAAmB,wCAA8B;AAC3D,eAAO,KAAK,iCAAiC,OAAO;MACrD,WAAW,mBAAmB,2CAAiC;AAC9D,eAAO,KAAK,oCAAoC,OAAO;MACxD,WAAW,mBAAmB,mCAAyB;AACtD,eAAO,KAAK,4BAA4B,OAAO;MAChD,WAAW,mBAAmB,0BAAgB;AAC7C,eAAO,KAAK,mBAAmB,OAAO;MACvC,WAAW,mBAAmB,0BAAgB;AAC7C,eAAO,KAAK,mBAAmB,OAAO;MACvC,WAAW,mBAAmB,qCAA2B;AACxD,eAAO,KAAK,8BAA8B,OAAO;MAClD,WAAW,mBAAmB,qCAAiB;AAC9C,eAAO,KAAK,oBAAoB,OAAO;MACxC,WAAW,mBAAmB,qCAAiB;AAC9C,eAAO,KAAK,oBAAoB,OAAO;MACxC,WAAW,mBAAmB,6CAAyB;AACtD,eAAO,KAAK,4BAA4B,OAAO;MAChD,WAAW,mBAAmB,6CAAyB;AACtD,eAAO,KAAK,4BAA4B,OAAO;MAChD,WAAW,mBAAmB,gDAA4B;AACzD,eAAO,KAAK,+BAA+B,OAAO;MACnD,WAAW,mBAAmB,4CAAkC;AAC/D,eAAO,KAAK,qCAAqC,OAAO;MACzD,WAAW,mBAAmB,yCAA+B;AAE5D,YACC,QAAQ,SAAS,kCAAwB,uBACxC;AACD,iBAAO,KAAK,OAAO,WACjB,2CACA,OAAO;QAEV;MACD,WAAW,mBAAmB,8CAAoC;AAEjE,mBAAW,OAAO,QAAQ,cAAc;AACvC,gBAAM,KAAK,cAAc,GAAG;QAC7B;AACA;MACD,WACC,mBAAmB,sDAChB,QAAQ,gBAAgB,QAC1B;AAED;MACD;AAGA,cAAQ,MAAM;;;;QAIb,KAAK,QAAQ,YAAY,KAAK,SAAS,QAAQ;;;QAI/C,KAAK,mBAAmB;AACvB;MACF;AAEA,WAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;QAC1C,SAAS;QACT,WAAW;OACX;AAED,UAAI,QAAQ,qBAAqB,+BAAmB,aAAa;AAEhE,cAAM,IAAI,uBACT,sCACA,4BAAgB,eAAe;MAEjC;IACD;IAEQ,wBAAwB;;;;;IAMzB,MAAM,yBAAsB;AAElC,UAAI,CAAC,KAAK,OAAO,iBAAiB;AACjC,YAAI,CAAC,KAAK,uBAAuB;AAChC,eAAK,wBAAwB;AAC7B,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SACC;YACD,WAAW;YACX,OAAO;WACP;QACF;AACA;MACD;AAGA,WAAK,MAAM,2BAAe,UAAU;QACnC,aAAa;QACb,SAAS;;QAET,QAAQ;OACR;AAGD,YAAM,gBAAgB,CAAC,MACtB,EAAE,QAAQ,UAAS,MAAO,KAAK,UAC5B,8BAAW,EAAE,OAAO,KACpB,EAAE,QAAQ,mBAAmB;AAEjC,UAAI,KAAK,OAAO,uBAAuB,aAAa,GAAG;AACtD,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SACC;UACD,OAAO;SACP;AACD;MACD;AAGA,WAAK,OAAO,gBAAgB,2BAA2B,KAAK,EAAE;AAG9D,UAAI;AACH,cAAM,KAAK,eAAe,SAAS,UAAS;MAC7C,SAAS,GAAG;AACX,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SAAS,6BAAyB,+BAAgB,CAAC,CAAC;UACpD,WAAW;SACX;MACF;IACD;;;;;IAMQ,0BAA0B,SAA8B;AAC/D,YAAM,SAAS,KAAK,OAAO;AAC3B,UAAI,CAAC;AAAQ;AAEb,aAAO,SACN;QACC,QAAQ,KAAK;QACb,SAAS,OAAO,WAAW,QAAQ,KAAK;SAEzC;QACC,OAAO,QAAQ;QACf,UAAU,KAAK,OAAO,WAAW;SAElC,EAAE,MAAM,KAAI,CAAE;IAEhB;;;;;IAMO,MAAM,0BAAuB;AAEnC,UAAI,CAAC,KAAK,OAAO,oBAAoB,KAAK,EAAE,GAAG;AAC9C,YAAI,CAAC,KAAK,uBAAuB;AAChC,eAAK,wBAAwB;AAC7B,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SACC;YACD,WAAW;YACX,OAAO;WACP;QACF;AACA;MACD;AAGA,WAAK,MAAM,2BAAe,YAAY,GAAG;QACxC,aAAa;QACb,SAAS;;QAET,QAAQ;OACR;AAGD,YAAM,gBAAgB,CAAC,MACtB,EAAE,QAAQ,UAAS,MAAO,KAAK,UAC5B,8BAAW,EAAE,OAAO,KACpB,EAAE,QAAQ,mBAAmB;AAEjC,UAAI,KAAK,OAAO,uBAAuB,aAAa,GAAG;AACtD,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SACC;UACD,OAAO;SACP;AACD;MACD;AAEA,UAAI;AACH,cAAM,KAAK,eAAe,YAAY,EAAE,UAAS;MAClD,SAAS,GAAG;AACX,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SAAS,6BAAyB,+BAAgB,CAAC,CAAC;UACpD,WAAW;SACX;MACF;IACD;;;;IAKQ,2BAA2B,UAAgC;AAclE,WAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;QAC1C,SACC;QACD,OAAO;QACP,WAAW;OACX;IACF;IAEQ,uBAAgC;IAChC,MAAM,WAAW,UAAgB;AAExC,WAAK,YAAW;AAEhB,UAAI,KAAK,sBAAsB;AAC9B,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SACC;SACD;AACD;MACD;AAEA,WAAK,uBAAuB;AAC5B,WAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;QAC1C,SACC;OACD;AACD,UAAI;AACH,cAAM,KAAK,cAAa;MACzB,QAAQ;MAER;AACA,WAAK,uBAAuB;IAC7B;;IAGQ;IAMA;IACA,0BAA0B;;IAG1B,+BACP,SAAmC;AAGnC,UACC,QAAQ,mBACH,KAAK,4CACT;AACD;MACD,OAAO;AACN,aAAK,6CACJ,QAAQ;MACV;AAmBA,YAAM,gBAAgB,CACrB,aACA,QACS;AACT,cAAM,UAAU,2CAAqB,MAAM,WAAW,EAAE;AACxD,aAAK,QAAQ,SAAS,SAAS,KAAK,EAAE,UAAU,MAAK,CAAE;MACxD;AAEA,YAAM,aAAa,MAAW;AAC7B,aAAK,0BAA0B;AAE/B,sBACC,KAAK,+BAAgC,aACrC,2BAAiB,WAAW;AAG7B,qBAAa,KAAK,+BAAgC,OAAO;AAEzD,aAAK,iCAAiC;MACvC;AAEA,UACC,KAAK,kCACF,KAAK,+BAA+B,gBAClC,QAAQ,aACZ;AAED,mBAAU;MACX;AAEA,YAAM,qBAAqB,2CAAqB,YAAY,SAC3D,KAAK,KAAK;AAEX,UAAI,QAAQ,iBAAiB,2BAAiB,aAAa;AAE1D,aAAK,0BAA0B;AAC/B,YAAI,KAAK,gCAAgC;AACxC,uBAAa,KAAK,+BAA+B,OAAO;QACzD;AAGA,cAAM,cAAc,QAAQ,eACxB,KAAK,QAAQ,SAAkB,kBAAkB;AACrD,aAAK,iCAAiC;UACrC,aAAa,QAAQ;;UAErB,SAAS,WACR,YACA,cAAc,MAAQ,GAAG,EACxB,MAAK;;MAET,WAAW,QAAQ,iBAAiB,2BAAiB,aAAa;AAEjE,YAAI,KAAK,gCAAgC;AACxC,uBAAa,KAAK,+BAA+B,OAAO;AACxD,eAAK,iCAAiC;QACvC,WAAW,KAAK,yBAAyB;AAGxC,eAAK,QAAQ,SAAS,oBAAoB,IAAI;AAE9C;QACD;MACD;AAEA,oBAAc,QAAQ,aAAa,QAAQ,YAAY;AACvD,WAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;QAC1C,SAAS,0CAAsC,yBAAU,OAAO,CAAC;QACjE,WAAW;OACX;IACF;;IAGQ;;IAGA,2BAAwB;AAC/B,WAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;QAC1C,SAAS;QACT,WAAW;OACX;AAKD,UAAI,KAAK,aAAa,2BAAe,SAAS,CAAC,MAAM,GAAG;AACvD,aAAK,MAAM,2BAAe,SAAS,GAAG;UACrC,aAAa;UACb,SAAS;SACT;MACF;AAEA,WAAK,YAAW;AAKhB,YAAM,MAAM,KAAK,IAAG;AACpB,UAAI,KAAK,YAAY;AAEpB,cAAM,iBACL,KAAK,SAAiB,+BAAe,eAAe,EAAE,KAAK;AAI5D,YACC,iBAAiB,MACb,MAAM,KAAK,cAAc,MAAO,iBAAiB,IAAI,IACxD;AACD,eAAK,eAAe,SAAS,EAAE,YAAW,EAAG,MAAM,MAAK;UAExD,CAAC;QACF;MACD;AACA,WAAK,aAAa;AAGlB,UAAI,KAAK,eAAe,QAAQ,eAAe;AAC9C,aAAK,KAAK,sBAAqB;MAChC,WAAW,CAAC,KAAK,eAAe,QAAQ,oBAAoB;AAE3D,aAAK,KAAK,kBAAiB,EAAG,MAAM,MAAK;QAEzC,CAAC;MACF;AAGA,WAAK,OAAO,wBAAwB,IAAI;IACzC;IAEQ,MAAM,wBAAqB;AAClC,UAAI,CAAC,KAAK,eAAe,QAAQ;AAAe;AAChD,WAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;QAC1C,SAAS;QACT,WAAW;OACX;AAED,iBACO,CAAC,QAAQ,WAAW,GAAG,IAAI,KAAK,KAAK,cAAc,OACvD,eACD;AACD,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SAAS,iBAAiB,MAAM,MAAM,SAAS,IAC9C,KACE,IAAI,CAAC,QAAQ,KAAK,UAAU,GAAG,CAAC,EAChC,KAAK,IAAI,CACZ;UACA,WAAW;SACX;AAGD,YAAI;AACJ,YAAI;AACH,gBACE,KAAK,eAAuB,MAAM,EAClC,YAAY;;YAEb,KAAK;;YAEL,iBAAiB;;YAEjB,QAAQ;WACR;QACF,QAAQ;AACP,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SAAS;YACT,WAAW;YACX,OAAO;WACP;AACD;QACD;AACA,YAAI,CAAC,IAAI,YAAW,GAAI;AACvB,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SAAS;YACT,WAAW;YACX,OAAO;WACP;AACD;QACD,WAAW,CAAE,IAAY,SAAS,GAAG;AACpC,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SACC,UAAU,SAAS;YACpB,WAAW;YACX,OAAO;WACP;AACD;QACD;AAIA,cAAM,SAAU,IAAY,SAAS,EAAE,KAAK,GAAG;AAE/C,cAAM,aAAa,KAAK,IAAa,CAAC,QAAO;AAC5C,kBAAI,4BAAS,GAAG,GAAG;AAClB,kBAAM,UAAU;cACf,cAAc,IAAI;cAClB,GAAG;;AAEJ,mBAAO,KAAK,SAAS,OAAO;UAC7B;AACA,iBAAO;QACR,CAAC;AAGD,YAAI;AACH,gBAAM,OAAO,GAAG,UAAU;AAC1B,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SAAS;YACT,WAAW;WACX;QACF,SAAS,GAAG;AACX,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SAAS,8BAA0B,+BAAgB,CAAC,CAAC;YACrD,WAAW;YACX,OAAO;WACP;AACD,kBACC,0BAAa,CAAC,KACX,EAAE,SAAS,4BAAgB,2BAC7B;AAED;UACD;QACD;MACD;IACD;;IAGQ,mBAAmB,SAAgB;AAE1C,YAAM,iBAAiB,KAAK,YAAY,QAAQ,iBAAiB,CAAC,KAC9D;AAGJ,UAAI;AACJ,cAAQ,eAAe,aAAa,QAAQ,KAAK;QAChD,KAAK;AACJ,2BAAiB,eAAe,uBAC/B,2BAAe,eAAe,CAAC;AAEhC;QACD,KAAK;AACJ,2BAAiB,eAAe,uBAC/B,2BAAe,eAAe,CAAC;AAEhC;QACD,KAAK;AACJ,2BAAiB,eAAe,uBAC/B,2BAAe,mBAAmB,CAAC;AAEpC;QACD,KAAK;AACJ,kBAAQ,eAAe,YAAY,SAAS,KAAK;YAChD,KAAK;AACJ,+BAAiB,eACf,uBACA,2BAAe,eAAe,CAAC;AAEjC;YACD,KAAK;AACJ,+BAAiB,eACf,uBACA,2BAAe,mBAAmB,CAAC;AAErC;UACF;MACF;AAEA,UAAI,mBAAmB,8BAAe;AAErC,cAAM,qBAAqB,KAAK,cAAc,QAAQ,kBAClD;AAEJ,YAAI,uBAAuB,iBAAiB;AAE3C,2BAAiB,eAAe,uBAC/B,2BAAe,eAAe,CAAC;AAEhC,cAAI,OAAO,QAAQ,iBAAiB,UAAU;AAC7C,gBAAI,gBAAgB;AACnB,mBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;gBAC1C,UAAU,QAAQ;gBAClB,SACC;eACD;AACD,6BAAe,oBACd,KAAK,QACL,QAAQ,YAAY;YAEtB,OAAO;AACN,mBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;gBAC1C,UAAU,QAAQ;gBAClB,SACC;gBACD,OAAO;eACP;YACF;UACD,OAAO;AACN,iBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;cAC1C,UAAU,QAAQ;cAClB,SACC;cACD,OAAO;aACP;UACF;QACD,WACC,uBAAuB,UAAU,uBAAuB,OACvD;AAED,gBAAM,oBACL,OAAO,QAAQ,iBAAiB,YAE7B,uBAAuB,UACvB,gBAAgB,oBAClB,KAAK,QACL,QAAQ,YAAY;AAItB,cAAI,CAAC,mBAAmB;AAEvB,oBAAQ,cAAc,KAAK,MAAM;UAClC;QACD;MACD,WAAW,mBAAmB,2BAAY;AAEzC,cAAM,kBAAkB,KAAK,cAAc,QAAQ,eAC/C;AAEJ,YAAI,oBAAoB,SAAS;AAEhC,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,UAAU,QAAQ;YAClB,SAAS;WACT;AACD,eAAK,SAAS,SACb,6BAAc,YAAY,SACzB,QAAQ,aAAa,GAEtB,QAAQ,aACR;YACC,UAAU;WACV;QAEH,WAAW,oBAAoB,iBAAiB;AAE/C,2BAAiB,eAAe,uBAC/B,2BAAe,eAAe,CAAC;AAEhC,cAAI,gBAAgB;AACnB,iBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;cAC1C,UAAU,QAAQ;cAClB,SACC;aACD;AACD,2BAAe,oBACd,KAAK,QACL,QAAQ,WAAW;UAErB,OAAO;AACN,iBAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;cAC1C,UAAU,QAAQ;cAClB,SACC;cACD,OAAO;aACP;UACF;QACD,WACC,CAAC,KAAK,cAAc,QAAQ,eACzB,CAAC,EAAE,QAAQ,qBACX,+BAAmB,cACrB;AAID,cACC,QAAQ,qBAAqB,+BAAmB,aAC/C;AACD,kBAAM,IAAI,uBACT,6BACA,4BAAgB,eAAe;UAEjC;QACD,WACC,oBAAoB,UAAU,oBAAoB,UACjD;AAGD,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,UAAU,QAAQ;YAClB,SAAS;WACT;AAGD,gBAAM,oBAAoB,oBAAoB,UAC1C,CAAC,CAAC,gBAAgB,oBACpB,KAAK,QACL,QAAQ,WAAW;AAIrB,cAAI,CAAC,mBAAmB;AAEvB,iBAAK,SAAS,SACb,6BAAc,aAAa,SAC1B,QAAQ,aAAa,GAEtB,QAAQ,WAAW;AAIpB,gBAAI,CAAC,eAAe,WAAW,2BAAe,KAAK,GAAG;AACrD,6BAAe,MAAM,2BAAe,OAAO;gBAC1C,cAAc;eACd;YACF;UACD;QACD;MACD;IACD;;IAGQ,8BAA8B,SAA2B;AAChE,YAAM,WAAW,KAAK,YAAY,QAAQ,iBAAiB,CAAC,KAAK;AAEjE,UAAI,mBAAmB,iDAAuB;AAC7C,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,UAAU,QAAQ;UAClB,SAAS;SACT;AACD,aAAK,SAAS,SACb,mDAAyB,YAAY,SACpC,QAAQ,aAAa,GAEtB,QAAQ,aACR;UACC,UAAU;SACV;MAEH,WAAW,mBAAmB,8DAAoC;AACjE,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,UAAU,QAAQ;UAClB,SACC;SACD;AACD,aAAK,KACJ,gBACA,UACA,2BAAe,mBAAmB,GAClC;UACC,WAAW,kCAAwB;UACnC,gBAAgB;UAChB,WAAW,QAAQ;SACnB;MAEH,WAAW,mBAAmB,6DAAmC;AAChE,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,UAAU,QAAQ;UAClB,SACC;SACD;AACD,aAAK,KACJ,gBACA,UACA,2BAAe,mBAAmB,GAClC;UACC,WAAW,kCAAwB;UACnC,gBAAgB;SAChB;MAEH;IACD;IAEQ,0BAA0B,SAAuB;AAExD,UACC,mBAAmB,2CAChB,KAAK,eAAe,QAAQ,kBAAkB,IAChD,QAAQ,YAAY,IAAI,GAExB;AACD,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,UAAU,QAAQ;UAClB,SAAS;SACT;AACD,aAAK,SAAS,SACb,2CAAqB,aAAa,SACjC,QAAQ,aAAa,GAEtB,QAAQ,WAAW;MAErB;IACD;IAEQ,4BAA4B,SAAyB;AAE5D,UACC,mBAAmB,+CAChB,KAAK,eAAe,QAAQ,kBAAkB,IAChD,QAAQ,YAAY,IAAI,GAExB;AACD,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,UAAU,QAAQ;UAClB,SAAS;SACT;AACD,aAAK,SAAS,SACb,+CAAuB,eAAe,SACrC,QAAQ,aAAa,GAEtB,QAAQ,IAAI;MAEd;IACD;IAEQ,MAAM,mBAAmB,SAAuB;AACvD,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,SACJ,UAAU,2BAAe,kBAAkB,GAAG,KAAK,EACnD,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB,EACA,WAAW;QACX,kBAAkB;QAClB,UAAU,4BAAkB;QAC5B,UAAU,4BAAkB;QAC5B,eAAe,KAAK,OAAO,QAAQ,QAAQ,iBACvC;;QACJ,UAAU,KAAK,OAAO,QAAQ,QAAQ,YAAY;;OAClD;IACH;IAEQ,MAAM,iBAAiB,SAAqB;AACnD,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,SAAS,KAAK,EACvC,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,uBAAmB,aAAAC,SAAY,0BAAY,EAAE,OAAO,KAAI,CAAE;AAEhE,YAAM,IAAI,WAAW;QACpB,aAAa,8BAAkB,mBAAmB;QAClD,iBAAiB,KAAK,OAAO,WAAW;QACxC,kBAAkB;;UAEjB,KAAK,OAAO,WAAW;;UAEvB,GAAG,iBAAiB,KAAK,IAAI,iBAAiB,KAAK,IAAI,iBAAiB,KAAK;;QAE9E,iBAAiB,KAAK,OAAO,QAAQ,QAAQ;OAC7C;IACF;IAEQ,MAAM,6BACb,SAAiC;AAEjC,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,SAAS,KAAK,EACvC,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,IAAI,gBAAgB,QAAQ,WAAW;IAC9C;IAEQ,MAAM,6BACb,SAAiC;AAEjC,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,SAAS,KAAK,EACvC,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,IAAI,mBAAkB;IAC7B;IAEQ,MAAM,8BACb,SAAkC;AAIlC,YAAM,MAAM,KACV,UAAU,2BAAe,uBAAuB,GAAG,KAAK,EACxD,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,IAAI,WAAW;QACpB,gBAAgB,KAAK,OAAO,QAAQ,QAAQ,kBACxC;;QACJ,aAAa,KAAK,OAAO,QAAQ,QAAQ,eAAe;QACxD,WAAW,KAAK,OAAO,QAAQ,QAAQ,aAAa;OACpD;IACF;IAEQ,MAAM,iBACb,SAAsC;AAEtC,UAAI,QAAQ,YAAY,GAAG;AAE1B;MACD;AAEA,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,+BAA+B,GAAG,KAAK,EAChE,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,IAAI,gBAAgB,GAAG,UAAU;IACxC;IAEQ,MAAM,iBACb,SAAsC;AAEtC,UAAI,CAAC,QAAQ,YAAY,QAAQ,YAAY,GAAG;AAE/C;MACD;AAEA,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,+BAA+B,GAAG,KAAK,EAChE,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,IAAI,gBAAgB;QACzB,YAAY,QAAQ,YAAY;QAChC,gBAAgB;QAChB,QAAQ;UACP;YACC,SAAS;YACT,WAAW;;YACX,SAAS,sCAA4B,mBAAmB;YACxD,MAAM;;;;OAGR;IACF;IAEQ,MAAM,wBACb,SAA6C;AAE7C,UAAI,QAAQ,YAAY,GAAG;AAE1B;MACD;AAEA,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,+BAA+B,GAAG,KAAK,EAChE,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,IAAI,eACT,QAAQ,SACR,oBAAI,IAAI;QACP;UACC,2BAAe,sBAAsB;UACrC,CAAC,oCAA0B,YAAY;;OAExC,CAAC;IAEJ;IAEQ,MAAM,uCACb,SAA2C;AAE3C,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,aAAa,KAAK,EAC3C,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAGF,YAAM,IAAI,iBAAiB,CAAC;IAC7B;IAEQ,MAAM,qBACb,SAAyB;AAGzB,YAAM,UAAU;AAEhB,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,aAAa,KAAK,EAC3C,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,UACL,KAAK,OAAO,WAAW,aAAa,OAAO,CAAC,MAC3C,EAAE,YAAY,MAAS,EAEtB,IAAI,CAAC,MAAM,EAAE,MAAM,KAAK,CAAA;AAE3B,YAAM,IAAI,WAAW;QACpB;QACA,UAAU;QACV;QACA,iBAAiB;OACjB;IACF;IAEQ,qBAAqB,SAAyB;AACrD,UAAI,QAAQ,YAAY,GAAG;AAE1B,cAAM,IAAI,uBACT,qBAAqB,QAAQ,OAAO,sBACpC,4BAAgB,kBAAkB;MAEpC;AAGA,YAAM,kBAAkB,QAAQ,QAAQ,OAAO,CAAC,cAC/C,CAAC,KAAK,OAAO,WAAW,aAAa,KACpC,CAAC,EAAE,QAAQ,SAAQ,MAClB,aAAa,UAAa,WAAW,SAAS,CAC/C,EACA,IAAI,CAAC,YAAY,EAAE,OAAM,EAAG;AAE9B,YAAM,eAAe,CAAC,GAAG,KAAK,OAAO,WAAW,YAAY;AAC5D,mBAAa,KAAK,GAAG,eAAe;AAGpC,UAAI,aAAa,SAAS,kBAAkB;AAC3C,cAAM,IAAI,uBACT,qBAAqB,QAAQ,OAAO,YACpC,4BAAgB,kBAAkB;MAEpC;AACA,WAAK,OAAO,WAAW,eAAe;IACvC;IAEQ,wBAAwB,SAA4B;AAE3D,UAAI,CAAC,CAAC,QAAQ,WAAW,QAAQ,YAAY,GAAG;AAE/C;MACD;AAEA,UAAI,CAAC,QAAQ,SAAS,QAAQ;AAE7B,aAAK,OAAO,WAAW,eAAe,CAAA;MACvC,OAAO;AACN,aAAK,OAAO,WAAW,eAAe,KAAK,OAAO,WAChD,aAAa,OACb,CAAC,EAAE,QAAQ,SAAQ,MAClB,aAAa,UACV,CAAC,QAAQ,QAAS,SAAS,MAAM,CAAC;MAEzC;IACD;IAEQ,MAAM,kCACb,SAAsC;AAEtC,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,aAAa,KAAK,EAC3C,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAIF,YAAM,IAAI,oBAAoB,CAAC;IAChC;IAEQ,MAAM,mDACb,SAAuD;AAEvD,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,2BAA2B,GAAG,KAAK,EAC5D,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAGF,YAAM,IAAI,iBAAiB,CAAC;IAC7B;IAEQ,MAAM,iCACb,SAAqC;AAGrC,YAAM,UAAU;AAEhB,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,2BAA2B,GAAG,KAAK,EAC5D,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,UACL,KAAK,OAAO,WAAW,aAAa,OAAO,CAAC,MAC3C,EAAE,YAAY,MAAS,EAEtB,IAAI,CAAC,MAAM,EAAE,MAAM,KAAK,CAAA;AAC3B,YAAM,YACL,KAAK,OAAO,WAAW,aAAa,OAAO,CAAC,MAC3C,EAAE,YAAY,MAAS,EAEtB,IAAI,CAAC,EAAE,QAAQ,UAAAD,UAAQ,OAAQ;QAC/B;QACA,UAAUA;QACT,KACC,CAAA;AAEL,YAAM,IAAI,WAAW;QACpB;QACA,UAAU;QACV;QACA;QACA,iBAAiB;OACjB;IACF;IAEQ,iCACP,SAAqC;AAErC,UAAI,QAAQ,YAAY,GAAG;AAE1B,cAAM,IAAI,uBACT,mCAAmC,QAAQ,OAAO,sBAClD,4BAAgB,kBAAkB;MAEpC;AAGA,YAAM,wBAAwB,QAAQ,QAAQ,OAAO,CAAC,cACrD,CAAC,KAAK,OAAO,WAAW,aAAa,KACpC,CAAC,EAAE,QAAQ,SAAQ,MAClB,aAAa,UAAa,WAAW,SAAS,CAC/C,EACA,IAAI,CAAC,YAAY,EAAE,OAAM,EAAG;AAC9B,YAAM,0BAA0B,QAAQ,UAAU,QACjD,CAAC,EAAE,QAAQ,SAAQ,MAAM;AACxB,YAAI,OAAO,aAAa,UAAU;AACjC,iBAAO,EAAE,QAAQ,SAAQ;QAC1B,OAAO;AACN,iBAAO,SAAS,IAAI,CAAC,OAAO,EAAE,QAAQ,UAAU,EAAC,EAAG;QACrD;MACD,CAAC,EACA,OAAO,CAAC,EAAE,QAAQ,WAAW,UAAU,YAAW,MACnD,CAAC,KAAK,OAAO,WAAW,aAAa,KAAK,CAAC,EAAE,QAAQ,SAAQ,MAC5D,WAAW,aAAa,aAAa,WAAW,CAChD;AAGF,YAAM,eAAe,CAAC,GAAG,KAAK,OAAO,WAAW,YAAY;AAC5D,mBAAa,KAAK,GAAG,uBAAuB,GAAG,uBAAuB;AAGtE,UAAI,aAAa,SAAS,kBAAkB;AAC3C,cAAM,IAAI,uBACT,mCAAmC,QAAQ,OAAO,YAClD,4BAAgB,kBAAkB;MAEpC;AAEA,WAAK,OAAO,WAAW,eAAe,aAAa,MAClD,GACA,gBAAgB;IAElB;IAEQ,oCACP,SAAwC;AAGxC,UAAI,CAAC,CAAC,QAAQ,WAAW,QAAQ,YAAY,GAAG;AAE/C;MACD;AAEA,UAAI,CAAC,QAAQ,SAAS,UAAU,CAAC,QAAQ,WAAW,QAAQ;AAE3D,aAAK,OAAO,WAAW,eAAe,CAAA;MACvC,OAAO;AACN,YAAI,eAAe,CAAC,GAAG,KAAK,OAAO,WAAW,YAAY;AAC1D,YAAI,QAAQ,SAAS,QAAQ;AAC5B,yBAAe,aAAa,OAC3B,CAAC,EAAE,QAAQ,SAAQ,MAClB,aAAa,UACV,CAAC,QAAQ,QAAS,SAAS,MAAM,CAAC;QAExC;AACA,YAAI,QAAQ,WAAW,QAAQ;AAC9B,yBAAe,aAAa,OAC3B,CAAC,EAAE,QAAQ,SAAQ,MAClB,CAAC,QAAQ,UAAW,KAAK,CAAC,SACzB,KAAK,WAAW,UAAU,KAAK,aAAa,QAAQ,CACpD;QAEJ;AACA,aAAK,OAAO,WAAW,eAAe;MACvC;IACD;IAEQ,4BACP,SAAgC;AAEhC,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,WAAW,KAAK,EACzC,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,cAAQ,QAAQ,aAAa;QAC5B,KAAK;;;QAGL,KAAK;AAEJ,iBAAO,IAAI,gBAAgB,IAAM,CAAC,GAAM,GAAM,CAAI,GAAG,CAAC;QACvD;AAGC,iBAAO,IAAI,gBAAgB,GAAG,CAAA,GAAI,CAAC;MACrC;IACD;IAEQ,mBAAmB,SAAuB;AAEjD,UAAI,QAAQ,QAAQ,WAAW;AAAG;AAClC,YAAM,CAAC,IAAI,IAAI,EAAE,IAAI,QAAQ;AAC7B,UAAI,GAAG,gBAAgB,MAAQ,GAAG,eAAe;AAAM;AACvD,UAAI,GAAG,gBAAgB,MAAQ,GAAG,eAAe;AAAM;AACvD,UAAI,GAAG,gBAAgB,MAAQ,GAAG,eAAe;AAAM;AAGvD,YAAM,QAAQ,KAAK,OAAO,WAAW;AACrC,YAAM,IAAI,IAAM,CAAC,IAAI,IAAI,EAAE,CAAC;AAE5B,WAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;QAC1C,SAAS;QACT,WAAW;OACX;AAED,WAAK,OAAO,WAAW,KAAK,YAAY,IAAI;IAC7C;IAEQ,MAAM,mBAAmB,SAAuB;AACvD,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,WAAW,KAAK,EACzC,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAGF,UAAI,QAAQ,gBAAgB,IAAM;AACjC,cAAM,SAAS,KAAK,OAAO,WAAW,gBAAgB,IAAI,EAAI,KAAK;UAClE,EAAE,aAAa,IAAM,YAAY,GAAM,OAAO,EAAC;UAC/C,EAAE,aAAa,IAAM,YAAY,GAAM,OAAO,EAAC;UAC/C,EAAE,aAAa,IAAM,YAAY,GAAM,OAAO,EAAC;;AAEhD,cAAM,IAAI,WAAW,EAAE,OAAM,CAAE;MAChC,WAAW,OAAO,QAAQ,gBAAgB,UAAU;AAEnD,cAAM,IAAI,WAAW;UACpB,QAAQ;YACP;cACC,aAAa,QAAQ;cACrB,YAAY;cACZ,OAAO;;;SAGT;MACF,OAAO;AAEN,cAAM,IAAI,WAAW,EAAE,OAAO,EAAC,CAAE;MAClC;IACD;IAEQ,MAAM,8BACb,SAAkC;AAElC,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,WAAW,KAAK,EACzC,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAKF,YAAM,IAAI,kBAAkB,QAAQ,aAAa,EAAE;IACpD;IAEQ,oBAAoB,SAAwB;AAEnD,UAAI,EAAE,QAAQ,cAAc,uBAAa;AACxC,cAAM,IAAI,uBACT,sBAAsB,QAAQ,UAAU,KACxC,4BAAgB,kBAAkB;MAEpC;AAKA,WAAK,OAAO,WAAW,aAAa;QACnC,YAAY,QAAQ;QACpB,OAAO,QAAQ,UACZ,IAAI,KAAK,KAAK,IAAG,IAAK,QAAQ,UAAU,GAAI,IAC5C,oBAAI,KAAI;;IAEb;IAEQ,MAAM,oBAAoB,SAAwB;AACzD,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,YAAY,KAAK,EAC1C,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,EAAE,YAAY,MAAK,IAAK,KAAK,OAAO,WAAW;AAErD;;QAEC,MAAM,QAAO,IAAK,KAAK,IAAG,KAEvB,eAAe,qBAAW,cAAc;QAC1C;AACD,cAAM,IAAI,iBAAiB;UAC1B,YAAY,qBAAW,cAAc;SACrC;MACF,OAAO;AACN,cAAM,iBAAiB,KAAK,IAC3B,GACA,KAAK,IACJ,KAAK,OAAO,MAAM,QAAO,IAAK,KAAK,IAAG,KAAM,GAAI,GAChD,GAAG,CACH;AAGF,cAAM,IAAI,iBAAiB;UAC1B;UACA,SAAS;SACT;MACF;IACD;IAEQ,MAAM,4BACb,SAAgC;AAGhC,UAAI,EAAE,QAAQ,cAAc,uBAAa;AACxC,cAAM,IAAI,uBACT,sBAAsB,QAAQ,UAAU,KACxC,4BAAgB,kBAAkB;MAEpC,WAAW,QAAQ,iBAAiB,GAAG;AACtC,cAAM,IAAI,uBACT,qCACA,4BAAgB,kBAAkB;MAEpC;AAEA,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,YAAY,KAAK,EAC1C,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,UAAI;AACH,cAAM,qBAAqB,MAAM,KAAK,OAAO,mBAC5C,QAAQ,YACR,QAAQ,YACR,QAAQ,cAAc;AAGvB,aAAK,IAAI,mBAAmB;UAC3B,QAAQ,qBAAqB,IAC1B,+BAAqB,UACrB,+BAAqB;UACxB,YAAY,QAAQ;UACpB;SACA,EAAE,MAAM,kBAAI;MACd,QAAQ;AAEP,aAAK,IAAI,mBAAmB;UAC3B,QAAQ,+BAAqB;UAC7B,YAAY,QAAQ;UACpB,oBAAoB;SACpB,EAAE,MAAM,kBAAI;MACd;IACD;IAEQ,MAAM,4BACb,SAAgC;AAEhC,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAI5D,YAAM,MAAM,SACV,UAAU,2BAAe,YAAY,KAAK,EAC1C,YAAY;;;QAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;OACvB;AAEF,YAAM,SAAS,KAAK,OAAO,sBAAqB;AAEhD,UAAI,QAAQ;AACX,cAAM,IAAI,mBAAmB;UAC5B,QAAQ,OAAO,aACZ,+BAAqB,aAAa,IAClC,OAAO,qBAAqB,IAC5B,+BAAqB,UACrB,+BAAqB;UACxB,GAAG;SACH;MACF,OAAO;AAEN,cAAM,IAAI,mBAAmB;UAC5B,QAAQ,+BAAqB;UAC7B,YAAY;UACZ,oBAAoB;SACpB;MACF;IACD;IAEQ,+BACP,SAAmC;AAGnC,WAAK,KACJ,gBACA,MACA,2BAAe,gBACf,oBAAK,SAAS,CAAC,cAAc,UAAU,oBAAoB,CAAC,CAAC;IAE/D;IAEQ,MAAM,mCACb,SAAuC;AAEvC,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAE5D,UAAI,KAAK,wBAAuB,MAAO,0BAAc,WAAW;AAC/D,cAAM,EAAE,aAAY,QAAK,0CAAY;AACrC,cAAM,SAAS,eAAe,SAAS;UACtC;;UAEA,CAAA;QAAE;MAEJ,OAAO;AAEN,cAAM,SAAS,eAAe,SAAS,wBACtC,CAAA,GACA,CAAA,CAAE;MAEJ;IACD;IAEQ,MAAM,oCACb,SAAwC;AAExC,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAE5D,YAAM,uBAAuB,KAAK,wBAAuB;AACzD,YAAM,sBACL,QAAQ,mBACP,2BAAe,YAAY,GAC3B,2BAAiB,oBAAoB,GAEpC;AAEH,UACC,yBAAyB,UACtB,yBAAyB,qBAC3B;AAGD,cAAM,iBAAiB,mBAAO,OAAO,CAAC,WACrC,iCAAsB,EAAE,IAAI,CAAC;AAI9B,cAAM,8BAA8B,6BAAiB,OACpD,CAAC,OACA,eAAe,SAAS,EAAE,KAKvB,OAAO,2BAAe,eAAe,CAAC;AAG3C,cAAM,eAAe,oBAAI,IAAI;;;;;;;;;;;;;;;;UAgB5B,2BAAe;UACf,2BAAe,+BAA+B;UAC9C,2BAAe,sBAAsB;UACrC,2BAAe,2BAA2B;UAC1C,2BAAe;UACf,2BAAe,uBAAuB;UACtC,2BAAe,2BAA2B;UAC1C,2BAAe;UACf,2BAAe;UACf,2BAAe,kBAAkB;;;UAIjC,2BAAe,sBAAsB;;UAGrC,GAAG,4BAA4B,OAC9B,CAAC;;;;YAIA,OAAO,2BAAe,YACnB,OAAO,2BAAe,YAAY,KAClC,OAAO,2BAAe,mBAAmB;WAAC;SAE/C;AAID,cAAM,gBAAgB,IAAI,QAAI,0CAAY,EAAG,YAAY;AACzD,cAAM,4BAA4B,CAAC,GAAG,YAAY,EAAE,OAAO,CAAC,OAC3D,CAAC,cAAc,IAAI,EAAE,CAAC;AAGvB,cAAM,SAAS,eAAe,YAAY,EAAE,wBAC3C,yBAAyB;MAE3B,eAAW,+BAAkB,mBAAmB,GAAG;AAElD,cAAM,SAAS,eAAe,YAAY,EACxC,YAAY;UACZ,yBAAyB;SACzB,EACA,wBAAwB,CAAA,CAAE;MAC7B,OAAO;MAEP;IACD;IAEQ,qCACP,KAAqC;AAErC,UAAI,IAAI,kBAAkB,GAAG;AAE5B,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SACC;UACD,WAAW;SACX;AACD;MACD;AAMA,iBAAW,YAAW;AACrB,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SAAS;UACT,WAAW;SACX;AAED,YAAI;AACH,gBAAM,KAAK,OAAO,WAAW,yBAC5B,KAAK,IACL,kCAAiB,KAAK;QAExB,SAAS,GAAG;AACX,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SAAS,iCACR,+BACC,CAAC,CAEH;YACA,OAAO;WACP;QACF;MACD,GAAG,GAAG;IACP;;;;IAKQ,2BAA2B,oBAAI,IAAG;;IAElC,8BACP,SACA,SAAmB;AAEnB,WAAK,2BAA2B,OAAO;AACvC,YAAM,UAAM,6BAAgB,OAAO;AACnC,WAAK,yBAAyB;QAC7B;;QAEA;UAAW;UAAS,IAAI,KAAK;;QAAoB,EAAE,MAAK;MAAE;IAE5D;;IAGQ,2BAA2B,SAAgB;AAClD,YAAM,UAAM,6BAAgB,OAAO;AACnC,UAAI,KAAK,yBAAyB,IAAI,GAAG,GAAG;AAC3C,qBAAa,KAAK,yBAAyB,IAAI,GAAG,CAAC;AACnD,aAAK,yBAAyB,OAAO,GAAG;MACzC;IACD;;;IAIQ,gCACP,SACA,cACA,aAA8B;AAE9B,YAAM,YAAY,KAAK,OAAO,sBAC7B,2BAAe,cACf,KAAK,IACL,KAAK,KAAK;AAEX,UAAI,cAAc,KAAK,CAAC,KAAK,QAAQ,YAAY,OAAO,GAAG;AAC1D,cAAM,eAAW,oDAChB,KAAK,QAAQ,YAAY,OAAO,GAGhC,cACA,WAAW;AAEZ,aAAK,QAAQ,YAAY,SAAS,QAAQ;MAC3C;IACD;IAaO,8BACN,2BACA,WACA,gBAAwB,GAAC;AAEzB,UAAI;AACJ,UAAI,OAAO,8BAA8B,UAAU;AAClD,2BAAmB;MACpB,OAAO;AACN,2BAAmB,KAAK,QAAQ,YAC/B,yBAAyB,GACvB,YAAY;AACf,YAAI,qBAAqB,QAAW;AACnC;QACD;AACA,oBAAY,KAAK,QAAQ,SAAS,yBAAyB;AAC3D,wBAAgB,0BAA0B,YAAY;MACvD;AAEA,UACC,CAAC,KAAK,YAAY,aAAa,GAAG,WACjC,2BAAe,YAAY,GAE3B;AACD;MACD;AAEA,YAAM,mBAAe,6BAAgB,gBAAgB;AACrD,UAAI,CAAC;AAAc;AAEnB,aAAO,KAAK,sCACX,cACA,WACA,aAAa;IAEf;;IAGQ,sCACP,cACA,WACA,eAAqB;AAErB,YAAM,kBAAc,kCAAqB,cAAc,SAAS;AAEhE,UAAI,CAAC,eAAe,YAAY,SAAS;AAAS;AAElD,UAAI,CAAC,YAAY;AAAM;AAEvB,YAAM,mBAAmB,aAAa;AACtC,YAAM,eAAe,YAAY;AACjC,YAAM,UAAU,2CAAqB,qBACpC,kBACA,YAAY,EACX,SAAS,aAAa;AAGxB,UAAI,KAAK,QAAQ,SAAS,OAAO,MAAM;AAAW;AAGlD,WAAK,2BAA2B,OAAO;AACvC,WAAK,gCACJ,SACA,cACA,WAAW;AAEZ,WAAK,QAAQ;QAAS;QAAS;;MAAY;IAC5C;;;;IAKQ,yBAAyB,SAA6B;AAC7D,UAAI,QAAQ,oBAAoB,QAAW;AAC1C,YAAI,QAAQ,aAAa,QAAW;AACnC,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SAAS,yCACR,yBACC,OAAO,CAET;YACA,WAAW;WACX;QACF;AACA;MACD;AAEA,YAAM,gBAAY,iCAAsB,KAAK,QAAQ,OAAO;AAG5D,YAAM,mBAAe,6BAAgB,QAAQ,gBAAgB;AAE7D,UAAI,cAAc;AAEjB,cAAM,mBAAmB,aAAa;AAEtC,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SACC,gDAAgD,gBAAgB;UACjE,OAAO;SACP;AAGD,cAAM,eAAe,CAAC,cAA2B;AAChD,eAAK,sCACJ,cACA,WACA,QAAQ,aAAa;QAEvB;AAEA,cAAM,sBAAsB,CAAC,cAAsB;AAElD,gBAAM,qCAAqC,2CACzC,4BACA,QAAQ,kBACR,gBAAgB,EACf,SAAS,QAAQ,aAAa;AACjC,gBAAM,eAAe,KAAK,QAAQ,SACjC,kCAAkC;AAGnC,cAAI,gBAAgB;AAAW;AAE/B,cAAI,aAAa,UAAa,iBAAiB,WAAW;AACzD,iBAAK,QAAQ,SACZ,oCACA,CAAC;UAEH;QACD;AAEA,cAAM,QAAQ,QAAQ;AACtB,YAAI,UAAU,GAAG;AAEhB,kBACC,4BAAa,QAAQ,eAAe,KACjC,QAAQ,gBAAgB,QAC1B;AAED,yBAAa,QAAQ,gBAAgB,CAAC,CAAC;AACvC,gCAAoB,QAAQ,gBAAgB,CAAC,CAAC;UAC/C,OAAO;AAEN,kBAAM,gBAAgB,KAAK,QACzB,UAAU,2BAAe,YAAY,EACrC,OACA,CAAC,OACC,EAAE,YAAY,OAAO,QAAQ,iBAC3B,EAAE,aAAa,oBACf,OAAO,EAAE,UAAU,YACnB,EAAE,UAAU,CAAC;AAEnB,uBAAW,KAAK,eAAe;AAC9B,2BAAa,EAAE,KAAe;YAC/B;AACA,gCAAmB;UACpB;AACA;QACD;AAGA,cAAM,kBAAc,kCAAqB,cAAc,KAAK;AAE5D,YAAI,aAAa;AAChB,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SAAS;WACH,YAAY,KAAK;IAEtB,YAAY,SAAS,UAClB,gBACA;kBACS,YAAY,YAAY,EACrC;YACA,OAAO;WACP;QACF,OAAO;AACN,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SACC;YACD,OAAO;WACP;QACF;AAGA,aAAK,wBAAwB,OAAO;AAEpC,YAAI;AACJ,YAAI,CAAC,aAAa;AAEjB,2BAAiB;QAClB,WAAW,YAAY,SAAS,SAAS;AACxC,2BAAiB,YAAY;QAC9B,OAAO;AAEN,gBAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KACnD;AACJ,eAAK,KACJ,gBACA,UACA,2BAAe,cACf;YACC,MAAM,QAAQ;YACd,OAAO;YACP,OAAO,aAAa;YACpB,YAAY,YAAY;YACxB,YAAY,QAAQ;WACpB;AAIF,cAAI,YAAY,eAAe,QAAQ;AACtC,uBAAW,YAAY,YAAY,eAAe;AACjD,2BAAa,QAAQ;YACtB;UACD;AACA;QACD;AAGA,YAAI;AACJ,YAAI,aAAa;AAChB,oBAAU,2CAAqB,qBAC9B,kBACA,YAAY,YAAY,EACvB,SAAS,QAAQ,aAAa;AAEhC,eAAK,gCACJ,SACA,cACA,WAAW;QAEb,OAAO;AAEN,gBAAM,eAAe,2CACnB,4BACA,QAAQ,kBACR,gBAAgB;AAElB,oBAAU,aAAa,SAAS,QAAQ,aAAa;AAErD,cAAI,aAAa,GAAG;AACnB,gBAAI,CAAC,KAAK,QAAQ,YAAY,OAAO,GAAG;AACvC,mBAAK,QAAQ,YAAY,SAAS,aAAa,IAAI;YACpD;UACD;QACD;AACA,YAAI,OAAO,QAAQ,oBAAoB,UAAU;AAIhD,gBAAM,eAAe,kBAClB,mDACD,cACA,WAAW,IAEV;AAEH,gBAAM,gBAAgB,iBAAiB,YACpC,QAAQ,sBACR,yDACD,OACA,QAAQ,eAAe;AAEzB,eAAK,QAAQ,SAAS,SAAS,aAAa;QAC7C,OAAO;AACN,eAAK,QAAQ,SAAS,SAAS,KAAK;QACrC;AAMA,YACC,kBACG,CAAC,CAAC,KAAK,eAAe,QAAQ,4BAChC;AACD,eAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;YAC1C,SAAS;YACT,OAAO;WACP;AACD,eAAK,8BACJ,SACA,MAAM,aAAa,KAAK,CAAC;QAE3B;MACD,OAAO;AAEN,cAAM,eAAe,2CAAqB,wBACzC,QAAQ,gBAAgB;AAEzB,cAAM,UAAU,aAAa,SAAS,QAAQ,aAAa;AAG3D,YAAI,aAAa,GAAG;AACnB,cAAI,CAAC,KAAK,QAAQ,YAAY,OAAO,GAAG;AACvC,iBAAK,QAAQ,YAAY,SAAS,aAAa,IAAI;UACpD;QACD;AAGA,aAAK,QAAQ,SAAS,SAAS,QAAQ,iBAAiB;MAEzD;IACD;IAEQ,wBAAwB,SAA6B;AAC5D,YAAM,aAAa,CAAC,GAAM,GAAM,GAAM,CAAI;AAC1C,YAAM,eAAe,CAAC,GAAM,GAAM,CAAI;AACtC,YAAM,mBAAmB;;QAExB;QACA;;QAEA;QACA;;AAED;;QAEC,QAAQ,qBAAqB,MACzB,WAAW,SAAS,QAAQ,iBAA2B,KACvD,aAAa,SAAS,QAAQ,iBAA2B,OACzD,KAAK,WAAW,2BAAe,WAAW,CAAC,KAC3C,KAAK,WAAW,2BAAe,IAAI;QACtC;AAMD,cAAM,WAAW,WAAW,SAC3B,QAAQ,iBAA2B;AAIpC,YAAI,KAAK,WAAW,2BAAe,WAAW,CAAC,GAAG;AACjD,eAAK,QAAQ,SACZ,mCAAiB,YAAY,SAC5B,QAAQ,aAAa,GAEtB,WAAW,uBAAa,UAAU,uBAAa,SAAS;QAE1D;AACA,YAAI,KAAK,WAAW,2BAAe,IAAI,GAAG;AACzC,eAAK,QAAQ,SACZ,2BAAa,OAAO,SAAS,QAAQ,aAAa,GAClD,QAAQ;QAEV;MACD,WACC,QAAQ,qBAAqB,KAC1B,iBAAiB,SAAS,QAAQ,iBAA2B,GAC/D;AASD,aAAK,QAAQ,SACZ,2CAAqB,gBAAgB,SACpC,QAAQ,aAAa,GAEtB,QAAQ,sBAAsB,KAAO,KAAO,EAAI;AAOjD,cAAM,YAAY,2CAAqB;AACvC,cAAM,cAAc,UAAU,SAAS,QAAQ,aAAa;AAC5D,YAAI,sBAAsB,KAAK,QAAQ,YAAY,WAAW;AAC9D,YAAI,QAAQ,oBAAoB,KAAQ,CAAC,qBAAqB;AAC7D,eAAK,QAAQ,YAAY,aAAa,UAAU,IAAI;AACpD,gCAAsB;QACvB;AACA,YAAI,qBAAqB;AACxB,eAAK,QAAQ,SACZ,aACA,QAAQ,oBAAoB,IAAO,IAAO,CAAI;QAEhD;MACD;IACD;IAEQ,mBAA4B;IAC5B,MAAM,kBAAkB,SAAsB;AACrD,UAAI,KAAK;AAAkB;AAU3B,UAAI,MAAM,oBAAI,KAAI;AAClB,YAAM,UAAU,IAAI,WAAU;AAC9B,UAAI,WAAW,IAAI;AAClB,cAAM,IAAI,KAAK,IAAI,QAAO,KAAM,KAAK,WAAW,GAAI;MACrD;AAGA,YAAM,QAAQ,IAAI,SAAQ;AAC1B,YAAM,UAAU,IAAI,WAAU;AAE9B,UAAI,UAAU,IAAI,OAAM;AACxB,UAAI,YAAY;AAAG,kBAAU;AAE7B,UACC,QAAQ,YAAY,WACjB,QAAQ,SAAS,SACjB,QAAQ,WAAW,SACrB;AACD,cAAM,WAAW,KAAK,OAAO,eAAe,OAAO;AACnD,YAAI,CAAC,UAA8D;AAElE;QACD;AAEA,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,0DAA0D;AAE3D,aAAK,mBAAmB;AACxB,YAAI;AACH,gBAAM,SAAS,eAAe,MAAM,IACnC,OACA,SACA,OAAO;QAET,QAAQ;QAER;AACA,aAAK,mBAAmB;MACzB;IACD;IAEQ,MAAM,cAAc,SAAsB;AACjD,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAE5D,YAAM,MAAM,oBAAI,KAAI;AACpB,YAAM,QAAQ,IAAI,SAAQ;AAC1B,YAAM,UAAU,IAAI,WAAU;AAC9B,YAAM,UAAU,IAAI,WAAU;AAE9B,UAAI;AAGH,cAAM,MAAM,SACV,UAAU,2BAAe,MAAM,KAAK,EACpC,YAAY;;;UAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;SACvB;AACF,cAAM,IAAI,WAAW,OAAO,SAAS,OAAO;MAC7C,SAAS,GAAQ;AAChB,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SAAS,EAAE;UACX,OAAO;SACP;MAEF;IACD;IAEQ,MAAM,cAAc,SAAsB;AACjD,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAE5D,YAAM,MAAM,oBAAI,KAAI;AACpB,YAAM,OAAO,IAAI,YAAW;AAC5B,YAAM,QAAQ,IAAI,SAAQ,IAAK;AAC/B,YAAM,MAAM,IAAI,QAAO;AAEvB,UAAI;AAGH,cAAM,MAAM,SACV,UAAU,2BAAe,MAAM,KAAK,EACpC,YAAY;;;UAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;SACvB;AACF,cAAM,IAAI,WAAW,MAAM,OAAO,GAAG;MACtC,SAAS,GAAQ;AAChB,aAAK,OAAO,cAAc,QAAQ,KAAK,IAAI;UAC1C,SAAS,EAAE;UACX,OAAO;SACP;MAEF;IACD;IAEQ,MAAM,oBACb,SAA4B;AAE5B,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAE5D,YAAM,eAAW,wBAAW,oBAAI,KAAI,CAAE;AAEtC,UAAI;AAGH,cAAM,MAAM,SACV,UAAU,2BAAe,MAAM,KAAK,EACpC,YAAY;;;UAGZ,oBAAoB,QAAQ,qBACzB,CAAC,+BAAmB;SACvB;AACF,cAAM,IAAI,eAAe,QAAQ;MAClC,QAAQ;MAER;IACD;;;;;IAMO,sCAAmC;AACzC,YAAM,qBAAqB,KAAK,SAC/B,+DAA+B,mBAAmB,EAAE;AAErD,YAAM,qBAAqB,KAAK,SAC/B,+DAA+B,mBAAmB,EAAE;AAErD,YAAM,sBAAsB,KAAK,SAChC,+DAA+B,oBAAoB,EAAE;AAEtD,YAAM,wBAAwB,KAAK,SAClC,+DAA+B,sBAAsB,EAAE;AAExD,YAAM,mBAAmB,KAAK,SAC7B,+DAA+B,iBAAiB,EAAE;AAEnD,YAAM,4BAA4B,KAAK,SACtC,+DAA+B,0BAA0B,EAAE;AAI5D,UACC,CAAC,sBACE,KAAC,2BAAQ,qBAAqB,GAChC;AACD,eAAO,EAAE,oBAAoB,MAAK;MACnC;AAEA,aAAO;QACN,oBAAoB;;QAEpB,iBAAiB,IAAI,MAAM,IAAI,sBAAsB,MAAM,EAAE,KAAK,CAAC,EACjE,IAAI,CAAC,GAAG,MAAM,CAAC;QACjB;QACA;QACA;QACA;;IAEF;;;;;IAMO,MAAM,gCAA6B;AAGzC,YAAM,MAAM,KAAK,eAAe,2BAA2B;AAC3D,YAAM,OAAO,MAAM,IAAI,YAAW;AAClC,UAAI,CAAC,MAAM;AACV,cAAM,IAAI,uBACT,qFACA,4BAAgB,sBAAsB;MAExC,WAAW,CAAC,KAAK,oBAAoB;AACpC,eAAO;UACN,oBAAoB;;MAEtB;AAEA,aAAO;QACN,oBAAoB;;QAEpB,iBAAiB,IAAI,MAAM,IAAI,KAAK,sBAAsB,MAAM,EAC9D,KAAK,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,CAAC;QACzB,qBAAqB,KAAK;QAC1B,oBAAoB,KAAK;QACzB,kBAAkB,KAAK;QACvB,2BAA2B,KAAK;;IAElC;IAEQ,gDAA0D,CAAA;IAC1D,+BACP,SAAmC;AAEnC,UACC,CAAC,KAAK,eAAe,QAAQ,yCAC5B;AACD,YACC,KAAK,8CAA8C,SAClD,QAAQ,cAAc,GAEtB;AACD,eAAK,OAAO,cAAc,QACzB,KAAK,IACL,kEAAkE,QAAQ,cAAc,kBACxF,MAAM;AAEP;QACD;AAGA,aAAK,8CAA8C,QAClD,QAAQ,cAAc;AAEvB,YAAI,KAAK,8CAA8C,SAAS,GAAG;AAClE,eAAK,8CAA8C,IAAG;QACvD;MACD;AAGA,YAAM,WAAW,KAAK,YAAY,QAAQ,aAAa,KAAK;AAC5D,WAAK,KAAK,gBAAgB,UAAU,2BAAe,eAAe,GAAG;QACpE,OAAG,oBAAK,SAAS,CAAC,aAAa,YAAY,WAAW,CAAC;QACvD,gBAAgB,sCAA4B,QAAQ,SAAS;QAC7D,mBAAe,iCACd,iCACA,QAAQ,QAAQ;OAEjB;IACF;;;;;IAMO,MAAM,cAAW;AACvB,UAAI,CAAC,KAAK,OAAO;AAAc;AAG/B,YAAM,KAAK,iBAAgB;AAG3B,UAAI,KAAK,mBAAmB,6BAAe,UAAU;AACpD,aAAK,aAAa,KAAK,oBAAoB;MAC5C;IACD;;;;;;IAOO,MAAM,eACZ,YACA,YACAE,4BACA,YAA0D;AAE1D,YAAM,MAAM,KAAK,eAAe;AAGhC,YAAM,eAAe,KAAK;AAC1B,UAAI,KAAK;AAAU,aAAK,YAAY;AACpC,YAAM,SAAS,CAAI,UAAY;AAE9B,aAAK,YAAY;AACjB,eAAO;MACR;AAGA,YAAM,IAAI,cACT,YACA,YACAA,0BAAyB;AAK1B,YAAM,qBAAqB,KAAK,MAC9BA,6BAA4B,IAAK,GAAI;AAMvC,YAAM,kBAAkB,sBAAsB,MAAQ,MAAQ;AAG9D,UAAI,mBAAmB;AAEvB,UAAI,mBAAmB;AACvB,aAAO,MAAM;AAEZ,cAAM,SAAS,MAAM,KAAK,OACxB,eACA,CAAC,OACA,GAAG,WAAW,KAAK,MAChB,cAAc,gDAClB,eAAe,EAEf,MAAM,MAAM,MAAS;AAEvB,cAAM,SAAS,aACZ,oBAAK,QAAQ,CAAC,UAAU,oBAAoB,CAAC,IAE7C,MAAM,IAAI,kBAAiB,EAAG,MAAM,MAAM,MAAS;AAItD,YACC,CAAC,UACG,OAAO,WAAW,+BAAqB,aAAa,KACpD,OAAO,uBAAuB,kBACjC;AACD,cAAI,mBAAmB;AAAG,mBAAO,OAAO,CAAC;AACzC;AACA;QACD,OAAO;AACN,6BAAmB,OAAO;AAC1B,6BAAmB;QACpB;AAEA,YAAI,OAAO,WAAW,+BAAqB,QAAQ;AAClD,iBAAO,OAAO,CAAC;QAChB,WAAW,OAAO,WAAW,+BAAqB,SAAS;AAC1D,iBAAO,OAAO,OAAO,kBAAkB;QACxC,WAAW,YAAY;AAEtB,qBACC,OAAO,oBACPA,0BAAyB;QAE3B;MACD;IACD;IAEQ,yBAAkC;;;;IAInC,0BAAuB;AAC7B,aAAO,KAAK;IACb;IAEQ,sBAA+B;IAC/B;;;;;;;;IASD,mBAAgB;AACtB,UAAI,CAAC,KAAK;AAAwB;AAClC,WAAK,sBAAsB;AAC3B,WAAK,0BAA0B,QAAO;IACvC;;;;IAKO,MAAM,oBACZ,SAAiB,GACjB,YAKS;AAET,UAAI,KAAK,wBAAwB;AAChC,cAAM,IAAI,uBACT,wDACA,4BAAgB,gBAAgB;MAElC;AAEA,UAAI,SAAS,MAAM,SAAS,GAAG;AAC9B,cAAM,IAAI,uBACT,+DACA,4BAAgB,gBAAgB;MAElC;AAEA,UAAI;AACH,aAAK,yBAAyB;AAC9B,aAAK,sBAAsB;AAC3B,aAAK,+BAA2B,+CAAqB;AAErD,eAAO,MAAM,KAAK,4BAA4B,QAAQ,UAAU;MACjE;AACC,aAAK,yBAAyB;AAC9B,aAAK,sBAAsB;AAC3B,aAAK,2BAA2B;MACjC;IACD;IAEQ,MAAM,4BACb,QACA,YAKS;AAGT,YAAM,QAAQ,KAAK,IAAG;AAGtB,YAAM,gBAAgB,CAAC,WAAqC;AAC3D,cAAM,cAAc,KAAK,IACxB,OAAO,yBAAyB,GAChC,OAAO,eAAe;AAEvB,cAAM,eAAe,OAAO;AAC5B,cAAM,gBAAgB,OAAO,iBAAiB,qBAAW,QAAQ;AACjE,cAAM,YAAY,OAAO,aAAa;AACtC,cAAM,UAAU,OAAO;AAEvB,YAAI,gBAAgB;AAAI,iBAAO;AAC/B,YAAI,cAAc;AAAG,iBAAO;AAC5B,YAAI,gBAAgB,KAAK,UAAU;AAAM,iBAAO;AAChD,YAAI,gBAAgB,KAAK,UAAU;AAAK,iBAAO;AAC/C,YAAI,UAAU;AAAK,iBAAO;AAC1B,YAAI,UAAU;AAAK,iBAAO;AAC1B,YAAI,gBAAgB,qBAAW,QAAQ,KAAK,YAAY,IAAI;AAE3D,cAAI,gBAAgB;AAAW,mBAAO;AACtC,iBAAO,eAAe,IAAI,IAAI;QAC/B;AACA,YAAI,gBAAgB,UAAa,gBAAgB;AAAG,iBAAO;AAC3D,YAAI,UAAU;AAAI,iBAAO;AACzB,eAAO;MACR;AAEA,WAAK,OAAO,cAAc,QACzB,KAAK,IACL,mCAAmC,MAAM,SACxC,WAAW,IAAI,MAAM,EACtB,MAAM;AAGP,YAAM,UAAuC,CAAA;AAC7C,YAAM,UAAU,MAAK;AACpB,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,+BAA+B;AAEhC,YAAI,QAAQ,WAAW,GAAG;AACzB,iBAAO;YACN,QAAQ;YACR,SAAS,CAAA;;QAEX,OAAO;AACN,iBAAO;YACN,QAAQ,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;YAChD;;QAEF;MACD;AAEA,UAAI,KAAK,YAAY,KAAK,WAAW,yBAAW,OAAO;AAEtD,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,gCAAgC;AAEjC,cAAM,QAAQ,KAAK;UAClB,KAAK,cAAa;UAClB,KAAK;SACL;AACD,YAAI,KAAK;AAAqB,iBAAO,QAAO;MAC7C;AAEA,eAAS,QAAQ,GAAG,SAAS,QAAQ,SAAS;AAC7C,YAAI,KAAK;AAAqB,iBAAO,QAAO;AAG5C,YAAI;AACJ,YAAI,KAAK,aAAa,sBAAU,OAAO;AACtC,0BAAgB,MAAM,KAAK,OAAO,WAAW,iBAC5C,KAAK,IACL,IAAI,GACF;QACJ;AAGA,YAAI;AACJ,YAAI;AACJ,YAAI;AACJ,YAAI;AACJ,YAAI;AACJ,YAAI,kBAAkB;AACtB,YAAI,UAAU;AACd,cAAM,UAAU,KAAK,eAAe,cAAc,EAAE,YAAY;;UAE/D,8BAA8B;;UAE9B,iBAAiB,4BAAgB,MAC9B,4BAAgB;;UAEnB,YAAY,CAAC,WAAU;AACtB,uBAAW;UACZ;SACA;AAED,iBAAS,IAAI,GAAG,KAAK,8CAA2B,KAAK;AACpD,cAAI,KAAK;AAAqB,mBAAO,QAAO;AAE5C,gBAAMC,SAAQ,KAAK,IAAG;AAEtB,qBAAW;AACX,gBAAM,aAAa,MAAM,QAAQ,KAAI,EAAG,KACvC,MAAM,MACN,MAAM,KAAK;AAEZ,gBAAM,MAAM,KAAK,IAAG,IAAKA;AACzB,oBAAU,KAAK,IACd,SACA,WAAW,SAAS,UAAU,KAAK,GAAG;AAEvC,cAAI,CAAC,YAAY;AAChB;UACD,WAAW,UAAU;AACpB,6BAAiB;AACjB,gBAAI,SAAS,kBAAkB,GAAG;AACjC;YACD;AAEA,gBACC,SAAS,WAAW,UACjB,KAAC,yBAAY,SAAS,OAAO,GAC/B;AAED,kBACC,SAAS,sBAAsB,UAC5B,KAAC,yBAAY,SAAS,kBAAkB,GAC1C;AACD,sBAAM,mBAAmB,SAAS,UAC/B,SAAS;AAEZ,oBACC,aAAa,UACV,mBAAmB,WACrB;AACD,8BAAY;gBACb;cACD;AAEA,kBAAI,QAAQ,UAAa,SAAS,UAAU,MAAM;AACjD,uBAAO,SAAS;AAChB,0BAAU,SAAS;cACpB;YACD;UACD;QACD;AAIA,YACC,aAAa,UACV,QAAQ,UACR,OAAO,sBAAU,oBACjB,WAAW,QACb;AACD,gBAAM,iBAAiB,MAAM,KAAK,OAAO,WACvC,kBAAiB;AACnB,cAAI,cAAc,OAAO,MAAM,gBAAgB;AAC9C,kBAAM,SAAU,eACf,cAAc,OAAO,EAAE;AAExB,oBAAI,yBAAY,MAAM,GAAG;AACxB,kBAAI,WAAW,sBAAU,mBAAmB;AAE3C,4BAAY;cACb,WAAW,WAAW,sBAAU,kBAAkB;AAEjD,4BAAY,OAAO;cACpB,OAAO;AACN,4BAAY;cACb;YACD,OAAO;AACN,0BAAY,OAAO;YACpB;UACD;QACD;AAEA,cAAM,MAAiC;UACtC;UACA;UACA;UACA;UACA;UACA,QAAQ;;AAIT,YAAI,KAAK,WAAW,2BAAe,UAAU,GAAG;AAE/C,cAAI,wBAAwB;AAE5B,gBAAM,WAAW,OAAO,eAA0B;AAEjD,gBAAI,KAAK;AAAqB,qBAAO;AAErC,iBAAK,OAAO,cAAc,QACzB,KAAK,IACL,WAAW,4CAAyB,+BACnC,iCACC,sBACA,UAAU,CAEZ,KAAK;AAEN,kBAAM,SAAS,MAAM,KAAK,eACzB,KAAK,OAAO,WAAW,WACvB,YACA,4CAAyB;AAE1B,oCAAwB,+CAA4B;AACpD,iBAAK,OAAO,cAAc,QACzB,KAAK,IACL,UACC,iCACC,sBACA,UAAU,CAEZ,KAAK,MAAM,IAAI,4CAAyB,6BAA6B;AAItE,sBAAM,mBAAK,GAAI;AAEf,mBAAO,0BAA0B;UAClC;AACA,cAAI;AACH,kBAAM,aAAa,UAAM;cACxB,qBAAW,cAAc;;cACzB,qBAAW,QAAQ;;cACnB;YAAQ;AAET,gBAAI,KAAK;AAAqB,qBAAO,QAAO;AAE5C,gBAAI,cAAc,QAAW;AAE5B,kBAAI,gBAAgB,qBAAW,cAAc;AAC7C,kBAAI,wBAAwB;YAC7B,OAAO;AACN,kBAAI,gBAAgB;YACrB;UACD,SAAS,GAAG;AACX,oBACC,0BAAa,CAAC,KACX,EAAE,SAAS,4BAAgB,wBAC7B;AAED,kBAAI,gBAAgB,qBAAW,cAAc;AAC7C,kBAAI,wBAAwB;YAC7B,OAAO;AACN,oBAAM;YACP;UACD;QACD;AAEA,YAAI,SAAS,cAAc,GAAG;AAC9B,gBAAQ,KAAK,GAAG;AAChB,qBAAa,OAAO,QAAQ,IAAI,QAAQ,EAAE,GAAG,IAAG,CAAE;MACnD;AAEA,YAAM,WAAW,KAAK,IAAG,IAAK;AAE9B,YAAM,SAAS,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AACvD,YAAM,UAAU,EAAE,SAAS,OAAM;AACjC,WAAK,OAAO,cAAc,QACzB,KAAK,IACL,qCAAqC,QAAQ;MAC9C,qDAAiC,OAAO,CAAC,EAAE;AAG3C,aAAO;IACR;;;;IAKO,MAAM,iBACZ,cACA,SAAiB,GACjB,YAKS;AAET,UAAI,KAAK,wBAAwB;AAChC,cAAM,IAAI,uBACT,wDACA,4BAAgB,gBAAgB;MAElC;AAEA,UAAI,SAAS,MAAM,SAAS,GAAG;AAC9B,cAAM,IAAI,uBACT,+DACA,4BAAgB,gBAAgB;MAElC;AAEA,UAAI;AACH,aAAK,yBAAyB;AAC9B,aAAK,sBAAsB;AAC3B,aAAK,+BAA2B,+CAAqB;AAErD,eAAO,MAAM,KAAK,yBACjB,cACA,QACA,UAAU;MAEZ;AACC,aAAK,yBAAyB;AAC9B,aAAK,sBAAsB;AAC3B,aAAK,2BAA2B;MACjC;IACD;IAEQ,MAAM,yBACb,cACA,QACA,YAKS;AAET,YAAM,YAAY,KAAK,OAAO,WAAW,MAAM,WAAW,YAAY;AAEtE,UAAI,KAAK,aAAa,sBAAU,gBAAgB;AAC/C,cAAM,IAAI,uBACT,yDAAyD,KAAK,EAAE,KAChE,4BAAgB,mCAAmC;MAErD,WAAW,UAAU,aAAa,sBAAU,gBAAgB;AAC3D,cAAM,IAAI,uBACT,yDAAyD,UAAU,EAAE,KACrE,4BAAgB,mCAAmC;MAErD;AAEA,UAAI,UAAU,UAAU;AACvB,cAAM,IAAI,uBACT,0EACA,4BAAgB,eAAe;MAEjC,WACC,KAAK,YACF,CAAC,KAAK,WAAW,2BAAe,UAAU,GAC5C;AACD,cAAM,IAAI,uBACT,+EACA,4BAAgB,eAAe;MAEjC,WACC,CAAC,KAAK,WAAW,2BAAe,UAAU,KACvC,CAAC,UAAU,WAAW,2BAAe,UAAU,GACjD;AACD,cAAM,IAAI,uBACT,mFACA,4BAAgB,eAAe;MAEjC;AAGA,YAAMD,6BAA4B;AAClC,YAAM,QAAQ,KAAK,IAAG;AAGtB,YAAM,gBAAgB,CAAC,WAAkC;AACxD,cAAM,cAAc,KAAK,IACxB,OAAO,uBAAuB,GAC9B,OAAO,uBAAuB,CAAC;AAEhC,cAAM,eAAe,OAAO;AAC5B,cAAM,gBAAgB,KAAK,IAC1B,OAAO,uBAAuB,qBAAW,QAAQ,GACjD,OAAO,uBAAuB,qBAAW,QAAQ,CAAC;AAGnD,YAAI,gBAAgB;AAAI,iBAAO;AAC/B,YAAI,cAAc;AAAG,iBAAO;AAC5B,YAAI,gBAAgB;AAAG,iBAAO;AAC9B,YAAI,gBAAgB;AAAG,iBAAO;AAC9B,YAAI,gBAAgB,qBAAW,QAAQ,GAAG;AAEzC,iBAAO,eAAe,IAAI,IAAI;QAC/B;AACA,YAAI,gBAAgB;AAAG,iBAAO;AAC9B,eAAO;MACR;AAEA,WAAK,OAAO,cAAc,QACzB,KAAK,IACL,uCAAuC,YAAY,KAAK,MAAM,SAC7D,WAAW,IAAI,MAAM,EACtB,MAAM;AAGP,YAAM,UAAoC,CAAA;AAC1C,YAAM,UAAU,MAAK;AACpB,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,8BAA8B,YAAY,UAAU;AAErD,YAAI,QAAQ,WAAW,GAAG;AACzB,iBAAO;YACN,QAAQ;YACR,SAAS,CAAA;;QAEX,OAAO;AACN,iBAAO;YACN,QAAQ,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;YAChD;;QAEF;MACD;AAEA,UAAI,KAAK,YAAY,KAAK,WAAW,yBAAW,OAAO;AAEtD,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,gCAAgC;AAEjC,cAAM,QAAQ,KAAK;UAClB,KAAK,cAAa;UAClB,KAAK;SACL;AACD,YAAI,KAAK;AAAqB,iBAAO,QAAO;MAC7C;AAEA,eAAS,QAAQ,GAAG,SAAS,QAAQ,SAAS;AAC7C,YAAI,KAAK;AAAqB,iBAAO,QAAO;AAI5C,cAAM,eAAe,KAAK,KAExB,MAAM,KAAK,OAAO,WAAW,iBAC5B,KAAK,IACL,IAAI,GAEJ,SAED,MAAM,KAAK,OAAO,WAAW,iBAC5B,cACA,IAAI,GAEJ,MAAM;AAGT,YAAI,cAAc;AAClB,YAAI;AACJ,YAAI;AACJ,YAAI;AACJ,YAAI;AACJ,cAAM,WACL,CAAC,MAAiBE,eAClB,OAAO,eAA0B;AAEhC,cAAI,KAAK;AAAqB,mBAAO;AAErC,eAAK,OAAO,cAAc,QACzB,KAAK,IACL,WAAWF,0BAAyB,kBAAkBE,WAAU,EAAE,WACjE,iCAAkB,sBAAY,UAAU,CACzC,KAAK;AAEN,gBAAM,SAAS,MAAM,KAAK,eACzBA,WAAU,IACV,YACAF,0BAAyB;AAE1B,wBAAcA,6BAA4B;AAC1C,eAAK,OAAO,cAAc,QACzB,KAAK,IACL,UACC,iCACC,sBACA,UAAU,CAEZ,KAAK,MAAM,IAAIA,0BAAyB,oCAAoCE,WAAU,EAAE,KAAK;AAI9F,oBAAM,mBAAK,GAAI;AAEf,iBAAO,gBAAgB;QACxB;AAGD,YAAI,KAAK,WAAW,2BAAe,UAAU,GAAG;AAC/C,cAAI;AAIH,kBAAM,aAAa,UAAM;cACxB,qBAAW,cAAc;;cACzB,qBAAW,QAAQ;;cACnB,SAAS,MAAM,SAAS;YAAC;AAE1B,gBAAI,KAAK;AAAqB,qBAAO,QAAO;AAE5C,gBAAI,cAAc,QAAW;AAE5B,oCAAsB,qBAAW,cAAc;AAC/C,oCAAsB;YACvB,OAAO;AACN,oCAAsB;AACtB,oCAAsB;YACvB;UACD,SAAS,GAAG;AACX,oBACC,0BAAa,CAAC,KACX,EAAE,SAAS,4BAAgB,wBAC7B;AAED,oCAAsB,qBAAW,cAAc;AAC/C,oCAAsBF;YACvB,OAAO;AACN,oBAAM;YACP;UACD;QACD;AAEA,YAAI,KAAK;AAAqB,iBAAO,QAAO;AAG5C,YACC,CAAC,KAAK,YACH,UAAU,WAAW,2BAAe,UAAU,GAChD;AACD,cAAI;AACH,kBAAM,aAAa,UAAM;cACxB,qBAAW,cAAc;;cACzB,qBAAW,QAAQ;;cACnB,SAAS,WAAW,IAAI;YAAC;AAE1B,gBAAI,cAAc,QAAW;AAE5B,oCAAsB,qBAAW,cAAc;AAC/C,oCAAsB;YACvB,OAAO;AACN,oCAAsB;AACtB,oCAAsB;YACvB;UACD,SAAS,GAAG;AACX,oBACC,0BAAa,CAAC,KACX,EAAE,SAAS,4BAAgB,wBAC7B;AAED,oCAAsB,qBAAW,cAAc;AAC/C,oCAAsBA;YACvB,OAAO;AACN,oBAAM;YACP;UACD;QACD;AAEA,cAAM,MAA8B;UACnC;UACA;UACA;UACA;UACA;UACA,QAAQ;;AAET,YAAI,SAAS,cAAc,GAAG;AAC9B,gBAAQ,KAAK,GAAG;AAChB,qBAAa,OAAO,QAAQ,IAAI,QAAQ,EAAE,GAAG,IAAG,CAAE;MACnD;AAEA,YAAM,WAAW,KAAK,IAAG,IAAK;AAE9B,YAAM,SAAS,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AACvD,YAAM,UAAU,EAAE,SAAS,OAAM;AACjC,WAAK,OAAO,cAAc,QACzB,KAAK,IACL,8BAA8B,UAAU,EAAE,gBAAgB,QAAQ;MACnE,kDAA8B,KAAK,IAAI,UAAU,IAAI,OAAO,CAAC,EAAE;AAG/D,aAAO;IACR;IAEQ,kCAA2C;;;;IAI5C,mCAAgC;AACtC,aAAO,KAAK;IACb;IAEQ,+BAAwC;IACxC;;;;;;;IAUD,4BAAyB;AAC/B,UAAI,CAAC,KAAK;AAAiC;AAC3C,WAAK,+BAA+B;AACpC,WAAK,mCAAmC,QAAO;IAChD;;;;IAKO,MAAM,qBACZ,SAAoC;AAEpC,UAAI,KAAK,iCAAiC;AACzC,cAAM,IAAI,uBACT,kEACA,4BAAgB,yBAAyB;MAE3C;AAEA,UAAI,OAAO,QAAQ,WAAW,YAAY,QAAQ,SAAS,GAAG;AAC7D,cAAM,IAAI,uBACT,4CACA,4BAAgB,gBAAgB;MAElC;AAEA,UAAI;AACH,aAAK,kCAAkC;AACvC,aAAK,+BAA+B;AACpC,aAAK,wCAAoC,+CAAqB;AAE9D,gBAAQ,QAAQ,MAAM;UACrB,KAAK,sCAAyB;AAC7B,mBAAO,MAAM,KAAK,kCACjB,OAAO;QAEV;MACD;AACC,aAAK,kCAAkC;AACvC,aAAK,+BAA+B;AACpC,aAAK,oCAAoC;MAC1C;IACD;IAEQ,MAAM,kCACb,SAAoC;AAEpC,WAAK,OAAO,cAAc,QACzB,KAAK,IACL,2DAA2D,QAAQ,MAAM,SACxE,QAAQ,WAAW,IAAI,MAAM,EAC9B,KAAK;AAGN,YAAM,iBAAiB,KAAK,WAAW,2BAAe,WAAW;AACjE,YAAM,SAAqC;QAC1C,QAAQ;QACR,cAAc;QACd,eAAe;QACf,kBAAkB,iBAAiB,IAAI;QACvC,SAAS;UACR,KAAK,OAAO;UACZ,KAAK;UACL,SAAS;;QAEV,KAAK;UACJ,KAAK,OAAO;UACZ,KAAK;UACL,SAAS;;QAEV,SAAS;UACR,KAAK;UACL,KAAK,OAAO;UACZ,SAAS,OAAO;;QAEjB,cAAc,iBACX;UACD,KAAK;UACL,KAAK,OAAO;UACZ,SAAS,OAAO;YAEf;;AAGJ,YAAM,UAAU,MAAK;AACpB,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,gCAAgC;AAEjC,eAAO;MACR;AAEA,UAAI,qBAAqB;AACzB,YAAM,iBAAiB,MAAK;AAC3B,YAAI,KAAK,IAAG,IAAK,sBAAsB,KAAK;AAC3C,kBAAQ,iBAAa,yBAAU,MAAM,CAAC;AACtC,+BAAqB,KAAK,IAAG;QAC9B;MACD;AAEA,UAAI,KAAK,YAAY,KAAK,WAAW,yBAAW,OAAO;AAEtD,aAAK,OAAO,cAAc,QACzB,KAAK,IACL,gCAAgC;AAEjC,cAAM,QAAQ,KAAK;UAClB,KAAK,cAAa;UAClB,KAAK;SACL;AACD,YAAI,KAAK;AAA8B,iBAAO,QAAO;MACtD;AAIA,UAAI;AAEJ,YAAM,cAAc,KAAK,eAAe,MAAM,YAAY;;QAEzD,8BAA8B;;QAE9B,iBAAiB,4BAAgB,MAC9B,4BAAgB;;QAEnB,kBAAkB;;QAElB,YAAY,CAAC,WAAU;AACtB,qBAAW;QACZ;OACA;AAED,UAAI;AACJ,eACK,QAAQ,GACZ,UAAU,QAAQ,UAAU,OAAO,oBACnC,SACC;AACD,YAAI,KAAK;AAA8B,iBAAO,QAAO;AAErD,eAAO,SAAS;AAEhB,oBAAY,KAAK,IAAG;AAEpB,mBAAW;AAEX,YAAI;AACH,gBAAM,YAAY,IACjB,QAAQ,MAAM,IAAI,MAAO,CAAI;AAG9B,iBAAO;AAGP,gBAAM,MAAM,KAAK,IAAG,IAAK;AACzB,iBAAO,IAAI,MAAM,KAAK,IAAI,OAAO,IAAI,KAAK,GAAG;AAC7C,iBAAO,IAAI,MAAM,KAAK,IAAI,OAAO,IAAI,KAAK,GAAG;AAE7C,iBAAO,IAAI,YAAY,MAAM,OAAO,IAAI,WAAW;AAEnD,cAAI,UAAU;AACb,kBAAM,UAAU,SAAS,UAAU;AACnC,gBAAI,OAAO,SAAS;AACnB,qBAAO,QAAQ,MAAM,KAAK,IACzB,OAAO,QAAQ,KACf,OAAO;AAER,qBAAO,QAAQ,MAAM,KAAK,IACzB,OAAO,QAAQ,KACf,OAAO;AAGR,qBAAO,QAAQ,YACb,UAAU,OAAO,QAAQ,WACxB;YACJ,OAAO;AACN,qBAAO,UAAU;gBAChB,KAAK;gBACL,KAAK;gBACL,SAAS;;YAEX;UACD;QACD,SAAS,GAAG;AACX,kBAAI,0BAAa,CAAC,GAAG;AACpB,gBACC,EAAE,SAAS,4BAAgB,0BACxB,EAAE,SAAS,4BAAgB,wBAC7B;AAED,qBAAO;YACR,WACC,EAAE,SAAS,4BAAgB,wBAC1B;AAGD,qBAAO,qBAAqB;AAC5B,qBAAO;YACR;UACD;QACD;AAEA,YACC,UAAU,WAAW,UAClB,KAAC,yBAAY,SAAS,OAAO,GAC/B;AACD,iBAAO,QAAQ,MAAM,KAAK,IACzB,OAAO,QAAQ,KACf,SAAS,OAAO;AAEjB,iBAAO,QAAQ,MAAM,KAAK,IACzB,OAAO,QAAQ,KACf,SAAS,OAAO;AAGjB,cAAI,OAAO,SAAS,OAAO,QAAQ,OAAO,GAAG;AAC5C,mBAAO,QAAQ,YACb,SAAS,UAAU,OAAO,QAAQ,WACjC;UACJ,OAAO;AACN,mBAAO,QAAQ,UAAU,SAAS;UACnC;QACD;AAIA,uBAAc;AAGd,cAAM,iBAAiB,KAAK,IAC3B,GACA,QAAQ,YAAY,KAAK,IAAG,IAAK,UAAU;AAE5C,cAAM,QAAQ,KAAK;cAClB,mBAAK,gBAAgB,IAAI;UACzB,KAAK;SACL;MACF;AAEA,aAAO;IACR;;;;;IAMO,UAAU,aAAoB;AACpC,UAAI,YAAY,KAAK;AACpB,cAAM,QAAQ,YAAY,MAAM;AAChC,aAAK,iBAAiB,CAAC,aAAa;UACnC,GAAG;UACH,KAAK,QAAQ,OAAO,aACjB,qBAAQ,QAAQ,MAAM,OAAO,QAAQ,MAAM,CAAC,QAC5C,qBAAQ,OAAO,CAAC;UAClB;MACH;IACD;;;;;IAMO,sBAAsB,UAAkB;AAC9C,WAAK,iBAAiB,CAAC,YAAW;AACjC,cAAM,MAAM,EAAE,GAAG,QAAO;AAExB,YAAI,SAAS,WAAW,QAAW;AAClC,cAAI,OACH,IAAI,QAAQ,cAAa,yBAAY,SAAS,OAAO,IAClD,SAAS,UACT,KAAK,MAAM,IAAI,OAAO,OAAO,SAAS,UAAU,IAAI;QACzD;AAGA,cAAM,WAA4B;UACjC,kBAAkB,SAAS;UAC3B,WAAY,SAAS,mBAAmB,CAAA;UACxC,MAAM,SAAS,WACX,IAAI,KAAK,QACT,sBAAU;;AAEf,YAAI,SAAS,mBAAmB,QAAW;AAC1C,mBAAS,eAAe,SAAS;QAClC;AACA,YACC,SAAS,mCACN,SAAS,qCACX;AACD,mBAAS,qBAAqB;YAC7B,SAAS;YACT,SAAS;;QAEX;AAEA,YAAI,IAAI,OAAO,KAAC,6CAAsB,IAAI,KAAK,QAAQ,GAAG;AAEzD,cAAI,OAAO,IAAI;QAChB;AACA,YAAI,MAAM;AACV,eAAO;MACR,CAAC;IACF;;;;;IAMO,MAAM,eAAe,MAAY,oBAAI,KAAI,GAAE;AAKjD,YAAM,oBAAoB,KAAK,eAAe,iBAAiB;AAC/D,YAAM,UAAU,KAAK,eAAe;AACpC,YAAM,WAAW,KAAK,eAAe;AACrC,YAAM,uBAAuB,KAAK,eAAe,qBAAqB;AAEtE,UACC,kBAAkB,YAAW,KAC1B,kBAAkB,gBAAgB,gCAAsB,GAAG,GAC7D;AACD,YAAI;AACH,gBAAM,SAAS,MAAM,kBAAkB,IAAI,GAAG;AAC9C,kBAAI,qCAAwB,MAAM;AAAG,mBAAO;QAC7C,QAAQ;AACP,iBAAO;QACR;MACD,WACC,SAAS,YAAW,KACjB,SAAS,gBAAgB,uBAAa,GAAG,GAC3C;AACD,YAAI;AAEH,gBAAM,QAAQ,IAAI,SAAQ;AAC1B,gBAAM,UAAU,IAAI,WAAU;AAE9B,cAAI,UAAU,IAAI,OAAM;AACxB,cAAI,YAAY;AAAG,sBAAU;AAE7B,gBAAM,SAAS,MAAM,SAAS,IAAI,OAAO,SAAS,OAAO;AACzD,kBAAI,qCAAwB,MAAM;AAAG,mBAAO;QAC7C,QAAQ;AACP,iBAAO;QACR;MACD,WACC,QAAQ,YAAW,KAChB,QAAQ,gBAAgB,sBAAY,UAAU,KAC9C,QAAQ,gBAAgB,sBAAY,UAAU,GAChD;AAKD,cAAM,MAAM,QAAQ,YAAY;UAC/B,gBAAgB;SAChB;AACD,YAAI;AAEH,gBAAM,OAAO,IAAI,YAAW;AAC5B,gBAAM,QAAQ,IAAI,SAAQ,IAAK;AAC/B,gBAAM,MAAM,IAAI,QAAO;AACvB,gBAAM,IAAI,WAAW,MAAM,OAAO,GAAG;AAErC,gBAAM,eAAe,MAAM,IAAI,QAAO;AACtC,cACC,CAAC,gBACE,aAAa,SAAS,QACtB,aAAa,UAAU,SACvB,aAAa,QAAQ,KACvB;AAED,mBAAO;UACR;QACD,QAAQ;AACP,iBAAO;QACR;AAEA,YAAI;AAEH,gBAAM,OAAO,IAAI,SAAQ;AACzB,gBAAM,SAAS,IAAI,WAAU;AAC7B,gBAAM,SAAS,IAAI,WAAU;AAC7B,gBAAM,IAAI,WAAW,MAAM,QAAQ,MAAM;AAEzC,gBAAM,eAAe,MAAM,IAAI,QAAO;AACtC,cAAI,CAAC;AAAc,mBAAO;AAE1B,gBAAM,gBAAgB,KAAK,KAAK;AAChC,gBAAM,WAAW,OAAO,KAAK,KAAK,SAAS,KAAK;AAChD,gBAAM,cAAc,WAAW;AAC/B,gBAAM,cAAc,WAAW;AAC/B,gBAAM,SAAS,aAAa,OAAO,KAAK,KACrC,aAAa,SAAS,KACtB,aAAa;AAEhB,cAAI,UAAU,eAAe,UAAU,aAAa;UAEpD,WACC,SAAS,iBAAiB,eACvB,SAAS,iBAAiB,aAC5B;UAEF,OAAO;AAEN,mBAAO;UACR;QACD,QAAQ;AACP,iBAAO;QACR;MACD;AAIA,YAAM,eAAW,wBAAW,GAAG;AAC/B,UACC,QAAQ,YAAW,KAChB,QAAQ,gBAAgB,sBAAY,aAAa,GACnD;AACD,YAAI;AACH,gBAAM,SAAS,MAAM,QAAQ,YAAY,QAAQ;AACjD,kBAAI,qCAAwB,MAAM;AAAG,mBAAO;QAC7C,QAAQ;AACP,iBAAO;QACR;MACD,WACC,qBAAqB,YAAW,KAC7B,qBAAqB,gBACvB,mCAAyB,aAAa,GAEtC;AACD,YAAI;AACH,gBAAM,SAAS,MAAM,qBAAqB,YAAY,QAAQ;AAC9D,kBAAI,qCAAwB,MAAM;AAAG,mBAAO;QAC7C,QAAQ;AACP,iBAAO;QACR;MACD;AAEA,aAAO;IACR;;;;IAKO,MAAM,iBAAc;AAC1B,YAAM,oBAAoB,KAAK,eAAe,iBAAiB;AAC/D,YAAM,UAAU,KAAK,eAAe;AACpC,YAAM,WAAW,KAAK,eAAe;AACrC,YAAM,uBAAuB,KAAK,eAAe,qBAAqB;AAEtE,YAAM,WAAwB,CAAA;AAE9B,UACC,kBAAkB,YAAW,KAC1B,kBAAkB,gBAAgB,gCAAsB,GAAG,GAC7D;AACD,YAAI;AACH,gBAAM,SAAS,MAAM,kBAAkB,IAAG;AAC1C,cAAI,QAAQ;AAEX,mBAAO,OAAO,UAAU;cACvB,MAAM,OAAO,YAAW;cACxB,QAAQ,OAAO,cAAa;cAC5B,QAAQ,OAAO,cAAa;cAC5B,gBAAgB;cAChB,WAAW;cACX,SAAS,OAAO,UAAS;cACzB,KAAK,OAAO,WAAU;cACtB,OAAO,OAAO,YAAW,IAAK;cAC9B,MAAM,OAAO,eAAc;aAC3B;UACF;AAEA,iBAAO;QACR,QAAQ;QAAC;MACV;AAEA,UACC,SAAS,YAAW,KACjB,SAAS,gBAAgB,uBAAa,GAAG,GAC3C;AACD,YAAI;AACH,gBAAM,SAAS,MAAM,SAAS,IAAG;AACjC,cAAI,QAAQ;AACX,mBAAO,OACN,UACA;cACC,MAAM,OAAO;cACb,QAAQ,OAAO;cACf,SAAS,OAAO;aACM;UAEzB;QACD,QAAQ;QAAC;MACV;AAEA,UACC,QAAQ,YAAW,KAChB,QAAQ,gBAAgB,sBAAY,OAAO,GAC7C;AACD,YAAI;AACH,gBAAM,SAAS,MAAM,QAAQ,QAAO;AACpC,cAAI,QAAQ;AACX,mBAAO,OACN,UACA;cACC,MAAM,OAAO;cACb,QAAQ,OAAO;cACf,QAAQ,OAAO;aACO;UAEzB;QACD,QAAQ;QAAC;MACV;AAEA,UACC,QAAQ,YAAW,KAChB,QAAQ,gBAAgB,sBAAY,OAAO,GAC7C;AACD,YAAI;AACH,gBAAM,SAAS,MAAM,QAAQ,QAAO;AACpC,cAAI,QAAQ;AACX,mBAAO,OACN,UACA;cACC,KAAK,OAAO;cACZ,OAAO,OAAO;cACd,MAAM,OAAO;aACS;UAEzB;QACD,QAAQ;QAAC;MACV;AAEA,UACC,QAAQ,YAAW,KAChB,QAAQ,gBAAgB,sBAAY,aAAa,GACnD;AACD,YAAI;AACH,gBAAM,SAAS,MAAM,QAAQ,YAAW;AACxC,cAAI,QAAQ;AACX,mBAAO,OACN,UACA;cACC,gBAAgB,OAAO;cACvB,WAAW,OAAO;aACI;UAEzB;QACD,QAAQ;QAAC;MACV;AAEA,UACC,qBAAqB,YAAW,KAC7B,qBAAqB,gBACvB,mCAAyB,aAAa,GAEtC;AACD,YAAI;AACH,gBAAM,SAAS,MAAM,qBAAqB,YAAW;AACrD,cAAI,QAAQ;AACX,mBAAO,OACN,UACA;cACC,gBAAgB,OAAO;cACvB,WAAW,OAAO;aACI;UAEzB;QACD,QAAQ;QAAC;MACV;AAEA,aAAO;IACR;IAEO,MAAM,+BAA4B;AAExC,YAAM,MAAM,KAAK,UAChB,2BAAe,sBAAsB,GACrC,KAAK;AAGN,YAAM,IAAI,iBAAgB;IAC3B;;;;;IAMO,yBAAsB;AAE5B,UAAI,KAAK,mBAAmB,6BAAe;AAAU,eAAO;AAG5D,UAAI,KAAK;AAAkB,eAAO;AAGlC,UAAI,KAAK,0BAA0B,QAAW;AAC7C,eAAO,KAAK,gBAAgB,SAAY,QAAQ;MACjD;AAGA,UAAI,KAAK,0BAA0B;AAClC,eAAO,CAAC,oBAAM,KAAK,KAAK,wBAAwB,EAAE,OACjD,KAAK,sBAAsB;MAE7B;AACA,aAAO;IACR;;IAGO,aAAU;AAChB,YAAM,EAAE,OAAO,GAAG,aAAY,IAAK,KAAK,mBAAkB;AAC1D,YAAM,MAAgB;QACrB,IAAI,KAAK;QACT,cAAc,KAAK,cAAc;QACjC,OAAO,KAAK;QACZ,aAAa,KAAK,cAAc;QAChC,aAAa;UACZ,gBAAgB,KAAK,kBAAkB,aACpC,wBAAS,KAAK,cAAc,IAC5B;UACH,aAAa,KAAK,eAAe,aAC9B,wBAAS,KAAK,WAAW,IACzB;UACH,WAAW,KAAK,aAAa,aAC1B,wBAAS,KAAK,SAAS,IACvB;UACH,iBAAiB,KAAK,mBAAmB;;QAE1C,oBAAgB,iCACf,8BACA,KAAK,cAAc;QAEpB,OAAO,KAAK;QAEZ,KAAK,KAAK,UAAM,yBAAY,KAAK,GAAG,IAAI;QACxC,iBAAiB,CAAA;QAEjB,aAAa,KAAK,eAAe;QACjC,qBAAqB,KAAK,uBAAuB;QACjD,WAAW,KAAK,aAAa;QAC7B,iBAAiB,KAAK,mBAAmB;QACzC,kBAAkB,KAAK,oBAAoB;QAC3C,cAAU,iCAAkB,uBAAW,KAAK,QAAQ;QACpD,oBAAoB,KAAK,OAAO,WAAW,qBAC1C,KAAK,EAAE,GACL,oBAAoB,IAAI,CAAC,UAAM,iCAAkB,uBAAW,CAAC,CAAC;QACjE,iBAAiB,KAAK,mBAAmB,aACtC,iCAAkB,6BAAiB,KAAK,eAAe,IACvD;QACH,YAAY,KAAK,cAAc;QAC/B,oBAAoB,KAAK,qBACtB,CAAC,GAAG,KAAK,kBAAkB,IAC3B;QAEH,GAAG;;AAGJ,UAAI,KAAK,mBAAmB,QAAW;AACtC,YAAI,YAAY,kBAAkB,KAAK;MACxC;AAEA,iBAAW,YAAY,gCAAoB;AAC1C,YACC,KAAK,aAAa,sBAAU,kBACzB,KAAC,sCAAyB,QAAQ,GACpC;AACD;QACD;AACA,YAAI,oBAAgB,iCAAkB,2BAAe,QAAQ,CAAC,IAC7D,KAAK,iBAAiB,QAAQ,KAAK;MACrC;AAEA,YAAM,cAAc,UAAU,2BAC7B,KAAK,QACL,MACA,IAAI;AAGL,YAAM,gBAAgB,CACrB,eACA,kBACG;AACH,mBAAW,WAAW,aAAa;AAClC,eAAK,QAAQ,YAAY,OAAO;AAAe;AAE/C,gBAAM,QAAQ,KAAK,SAAS,SAAS,OAAO;AAC5C,gBAAM,WAAW,KAAK,SAAS,YAAY,OAAO;AAClD,gBAAM,YAAY,KAAK,SAAS,aAAa,OAAO;AACpD,gBAAM,kBAAkB,YACrB,IAAI,KAAK,SAAS,EAAE,YAAW,IAC/B;AAEH,gBAAM,aAAa,uBAAa,wBAC/B,MACA,QAAQ,YAAY;AAErB,gBAAM,kBAAkB,YAAY,gBAAgB,OAAO;AAE3D,gBAAM,YAAuB;YAC5B,OAAG,oBAAK,SAAS;cAChB;cACA;aACA;YACD;YACA,OAAO,UAAU,SACd,mBACA,iCAAoB,KAAK;YAC5B,WAAW;;AAEZ,cAAI;AAAiB,sBAAU,WAAW;AAE1C,qBAAW,CAAC,MAAMG,MAAK,KAAK,OAAO,QAAQ,SAAS,GAAG;AAEtD,gBAAIA,WAAU;AAAW,qBAAO,UAAU,IAAI;UAC/C;AAEA,wBAAc,QAAQ,YAAY,GAAG,KAAK,SAAS;QACpD;MACD;AACA,oBAAc,GAAG,CAAC,SAAS,IAAI,mBAAe,uBAAU,IAAI,CAAC,GAAG,MAAM;AAEtE,iBAAW,YAAY,KAAK,gBAAe,GAAI;AAC9C,YAAI,SAAS,UAAU;AAAG;AAC1B,YAAI,cAAc,CAAA;AAClB,cAAMC,gBAAe,SAAS,mBAAkB;AAChD,sBACC,SAAS,OACT,CAAC,SAASA,cAAa,mBAAe,uBAAU,IAAI,CAAC,GAAG,MAAM;AAE/D,YAAI,UAAU,SAAS,KAAK,IAAIA;MACjC;AAEA,UAAI,KAAK,cAAc;AACtB,cAAM,eAAe,iBAAAC,QAAK,SACzB,kCACA,KAAK,aAAa,QAAQ;AAE3B,YAAI,aAAa,WAAW,IAAI,GAAG;AAElC,cAAI,iBAAiB,KAAK,aAAa;QACxC,OAAO;AACN,cAAI,iBAAiB;QACtB;AAEA,YAAI,KAAK,aAAa,QAAQ;AAE7B,cAAI,cAAc,KAAK,aAAa;QACrC;MACD;AACA,iBAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAEhD,YAAI,UAAU;AAAW,iBAAO,IAAI,IAAI;MACzC;AAEA,aAAO;IACR;IAEU,MACT,UACG,MAAuC;AAE1C,aAAO,KAAK,KAAK,OAAO,GAAG,IAAI;IAChC;IAEU,IACT,OACA,UAA+B;AAE/B,aAAO,KAAK,GAAG,OAAO,QAAQ;IAC/B;IAEU,MACT,OACA,UAA+B;AAE/B,aAAO,KAAK,KAAK,OAAO,QAAQ;IACjC;;;;",
6
6
  "names": ["import_serialapi", "import_Types", "options", "ccId", "ccUtils", "endpoint", "semverParse", "healthCheckTestFrameCount", "start", "otherNode", "value", "endpointDump", "path"]
7
7
  }