zwave-js 14.1.0 → 14.2.0
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.
- package/build/cjs/Driver.d.ts +1 -1
- package/build/cjs/Driver.js.map +1 -1
- package/build/cjs/lib/_version.d.ts +1 -1
- package/build/cjs/lib/_version.js +1 -1
- package/build/cjs/lib/_version.js.map +1 -1
- package/build/cjs/lib/controller/MockControllerBehaviors.js +2 -2
- package/build/cjs/lib/controller/MockControllerBehaviors.js.map +2 -2
- package/build/cjs/lib/driver/Driver.d.ts +1 -1
- package/build/cjs/lib/driver/Driver.js +9 -8
- package/build/cjs/lib/driver/Driver.js.map +2 -2
- package/build/cjs/lib/driver/MessageGenerators.js +4 -4
- package/build/cjs/lib/driver/MessageGenerators.js.map +2 -2
- package/build/cjs/lib/driver/NetworkCache.d.ts +1 -2
- package/build/cjs/lib/driver/NetworkCache.js.map +2 -2
- package/build/cjs/lib/driver/UpdateConfig.js +6 -4
- package/build/cjs/lib/driver/UpdateConfig.js.map +3 -3
- package/build/cjs/lib/driver/ZWaveOptions.d.ts +2 -2
- package/build/cjs/lib/driver/ZWaveOptions.js.map +2 -2
- package/build/cjs/lib/node/Node.js +2 -2
- package/build/cjs/lib/node/Node.js.map +3 -3
- package/build/cjs/lib/zniffer/Zniffer.js +4 -4
- package/build/cjs/lib/zniffer/Zniffer.js.map +2 -2
- package/build/esm/Driver.d.ts +1 -1
- package/build/esm/Driver.d.ts.map +1 -1
- package/build/esm/lib/_version.d.ts +1 -1
- package/build/esm/lib/_version.js +1 -1
- package/build/esm/lib/controller/MockControllerBehaviors.js +2 -2
- package/build/esm/lib/controller/MockControllerBehaviors.js.map +1 -1
- package/build/esm/lib/driver/Driver.d.ts +1 -1
- package/build/esm/lib/driver/Driver.d.ts.map +1 -1
- package/build/esm/lib/driver/Driver.js +9 -9
- package/build/esm/lib/driver/Driver.js.map +1 -1
- package/build/esm/lib/driver/MessageGenerators.d.ts.map +1 -1
- package/build/esm/lib/driver/MessageGenerators.js +4 -4
- package/build/esm/lib/driver/MessageGenerators.js.map +1 -1
- package/build/esm/lib/driver/NetworkCache.d.ts +1 -2
- package/build/esm/lib/driver/NetworkCache.d.ts.map +1 -1
- package/build/esm/lib/driver/NetworkCache.js.map +1 -1
- package/build/esm/lib/driver/UpdateConfig.d.ts.map +1 -1
- package/build/esm/lib/driver/UpdateConfig.js +6 -4
- package/build/esm/lib/driver/UpdateConfig.js.map +1 -1
- package/build/esm/lib/driver/ZWaveOptions.d.ts +2 -2
- package/build/esm/lib/driver/ZWaveOptions.d.ts.map +1 -1
- package/build/esm/lib/driver/ZWaveOptions.js.map +1 -1
- package/build/esm/lib/node/Node.js +2 -2
- package/build/esm/lib/node/Node.js.map +1 -1
- package/build/esm/lib/zniffer/Zniffer.d.ts.map +1 -1
- package/build/esm/lib/zniffer/Zniffer.js +4 -4
- package/build/esm/lib/zniffer/Zniffer.js.map +1 -1
- package/package.json +9 -9
|
@@ -61,14 +61,14 @@ function getNodeUpdateTimeout(driver, msg, additionalCommandTimeoutMs = 0) {
|
|
|
61
61
|
return commandTimeMs + driver.getReportTimeout(msg) + additionalCommandTimeoutMs;
|
|
62
62
|
}
|
|
63
63
|
const simpleMessageGenerator = async function* (driver, ctx, msg, onMessageSent, additionalCommandTimeoutMs = 0) {
|
|
64
|
-
if ((0, import_serialapi.isSendData)(msg) && driver.exceedsMaxPayloadLength(msg)) {
|
|
64
|
+
if ((0, import_serialapi.isSendData)(msg) && await driver.exceedsMaxPayloadLength(msg)) {
|
|
65
65
|
let fail2 = function() {
|
|
66
66
|
throw new import_core.ZWaveError("Cannot send this message because it would exceed the maximum payload length!", import_core.ZWaveErrorCodes.Controller_MessageTooLarge);
|
|
67
67
|
};
|
|
68
68
|
var fail = fail2;
|
|
69
69
|
if (msg.transmitOptions & import_core.TransmitOptions.Explore) {
|
|
70
70
|
msg.transmitOptions &= ~import_core.TransmitOptions.Explore;
|
|
71
|
-
if (driver.exceedsMaxPayloadLength(msg)) {
|
|
71
|
+
if (await driver.exceedsMaxPayloadLength(msg)) {
|
|
72
72
|
fail2();
|
|
73
73
|
}
|
|
74
74
|
driver.controllerLog.logNode(msg.getNodeId(), {
|
|
@@ -106,10 +106,10 @@ const maybeTransportServiceGenerator = async function* (driver, ctx, msg, onMess
|
|
|
106
106
|
}
|
|
107
107
|
const node = msg.tryGetNode(driver);
|
|
108
108
|
const mayUseTransportService = node?.supportsCC(import_core.CommandClasses["Transport Service"]) && node.getCCVersion(import_core.CommandClasses["Transport Service"]) >= 2;
|
|
109
|
-
if (!mayUseTransportService || !driver.exceedsMaxPayloadLength(msg)) {
|
|
109
|
+
if (!mayUseTransportService || !await driver.exceedsMaxPayloadLength(msg)) {
|
|
110
110
|
return yield* simpleMessageGenerator(driver, ctx, msg, onMessageSent, additionalCommandTimeoutMs);
|
|
111
111
|
}
|
|
112
|
-
const payload = msg.
|
|
112
|
+
const payload = await msg.serializeCCAsync(ctx);
|
|
113
113
|
const numSegments = Math.ceil(payload.length / import_TransportServiceCC.MAX_SEGMENT_SIZE);
|
|
114
114
|
const segmentDelay = numSegments > import_TransportServiceCC.RELAXED_TIMING_THRESHOLD ? import_TransportServiceCC.TransportServiceTimeouts.relaxedTimingDelayR2 : 0;
|
|
115
115
|
const sessionId = driver.getNextTransportServiceSessionId();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/driver/MessageGenerators.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\ttype CommandClass,\n\tMGRPExtension,\n\tMPANExtension,\n\tSecurity2CCMessageEncapsulation,\n\tSecurity2CCNonceGet,\n\tSecurity2CCNonceReport,\n\tSupervisionCC,\n\tSupervisionCCReport,\n\tSupervisionCommand,\n\tgetInnermostCommandClass,\n} from \"@zwave-js/cc\";\nimport {\n\tSecurityCCCommandEncapsulation,\n\tSecurityCCNonceGet,\n\ttype SecurityCCNonceReport,\n} from \"@zwave-js/cc/SecurityCC\";\nimport {\n\tMAX_SEGMENT_SIZE,\n\tRELAXED_TIMING_THRESHOLD,\n\ttype TransportServiceCC,\n\tTransportServiceCCFirstSegment,\n\tTransportServiceCCSegmentComplete,\n\tTransportServiceCCSegmentRequest,\n\tTransportServiceCCSegmentWait,\n\tTransportServiceCCSubsequentSegment,\n\tTransportServiceTimeouts,\n} from \"@zwave-js/cc/TransportServiceCC\";\nimport {\n\tCommandClasses,\n\tEncapsulationFlags,\n\tMessagePriority,\n\tNODE_ID_BROADCAST,\n\tSPANState,\n\tSecurityClass,\n\ttype SendCommandOptions,\n\ttype SupervisionResult,\n\tSupervisionStatus,\n\tTransmitOptions,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tmergeSupervisionResults,\n} from \"@zwave-js/core\";\nimport { type CCEncodingContext } from \"@zwave-js/host\";\nimport type { Message } from \"@zwave-js/serial\";\nimport {\n\ttype SendDataMessage,\n\tisSendData,\n\tisTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport { type ContainsCC, containsCC } from \"@zwave-js/serial/serialapi\";\nimport { getErrorMessage } from \"@zwave-js/shared\";\nimport { wait } from \"alcalzone-shared/async\";\nimport {\n\ttype DeferredPromise,\n\tcreateDeferredPromise,\n} from \"alcalzone-shared/deferred-promise\";\nimport type { Driver } from \"./Driver.js\";\nimport type { MessageGenerator } from \"./Transaction.js\";\n\nexport type MessageGeneratorImplementation<T extends Message> = (\n\t/** A reference to the driver */\n\tdriver: Driver,\n\tctx: CCEncodingContext,\n\t/** The \"primary\" message */\n\tmessage: T,\n\t/**\n\t * A hook to get notified about each sent message and the result of the Serial API call\n\t * without waiting for the message generator to finish completely.\n\t */\n\tonMessageSent: (msg: Message, result: Message | undefined) => void,\n\t/** Can be used to extend the timeout waiting for a response from a node to the sent message */\n\tadditionalCommandTimeoutMs?: number,\n) => AsyncGenerator<Message, Message, Message>;\n\nfunction maybePartialNodeUpdate(sent: Message, received: Message): boolean {\n\t// Some commands are returned in multiple segments, which may take longer than\n\t// the configured timeout.\n\tif (!containsCC(sent) || !containsCC(received)) {\n\t\treturn false;\n\t}\n\n\tif (sent.getNodeId() !== received.getNodeId()) return false;\n\n\tif (received.command.ccId === CommandClasses[\"Transport Service\"]) {\n\t\t// We don't know what's in there. It may be the expected update\n\t\treturn true;\n\t}\n\n\t// Let the sent CC test if the received one is a match.\n\t// These predicates don't check if the received CC is complete, we can use them here.\n\t// This also doesn't check for correct encapsulation, but that is good enough to refresh the timer.\n\tconst sentCommand = getInnermostCommandClass(sent.command);\n\tconst receivedCommand = getInnermostCommandClass(received.command);\n\treturn sentCommand.isExpectedCCResponse(receivedCommand);\n}\n\nexport async function waitForNodeUpdate<T extends Message>(\n\tdriver: Driver,\n\tmsg: Message,\n\ttimeoutMs: number,\n): Promise<T> {\n\ttry {\n\t\treturn await driver.waitForMessage<T>(\n\t\t\t(received) => msg.isExpectedNodeUpdate(received),\n\t\t\ttimeoutMs,\n\t\t\t(received) => maybePartialNodeUpdate(msg, received),\n\t\t);\n\t} catch {\n\t\tthrow new ZWaveError(\n\t\t\t`Timed out while waiting for a response from the node`,\n\t\t\tZWaveErrorCodes.Controller_NodeTimeout,\n\t\t);\n\t}\n}\n\nfunction getNodeUpdateTimeout(\n\tdriver: Driver,\n\tmsg: Message,\n\tadditionalCommandTimeoutMs = 0,\n): number {\n\tconst commandTimeMs = Math.ceil((msg.rtt ?? 0) / 1e6);\n\treturn (\n\t\tcommandTimeMs\n\t\t+ driver.getReportTimeout(msg)\n\t\t+ additionalCommandTimeoutMs\n\t);\n}\n\n/** A simple message generator that simply sends a message, waits for the ACK (and the response if one is expected) */\nexport const simpleMessageGenerator: MessageGeneratorImplementation<Message> =\n\tasync function*(\n\t\tdriver,\n\t\tctx,\n\t\tmsg,\n\t\tonMessageSent,\n\t\tadditionalCommandTimeoutMs = 0,\n\t) {\n\t\t// Make sure we can send this message\n\t\tif (isSendData(msg) && driver.exceedsMaxPayloadLength(msg)) {\n\t\t\t// We use explorer frames by default, but this reduces the maximum payload length by 2 bytes compared to AUTO_ROUTE\n\t\t\t// Try disabling explorer frames for this message and see if it fits now.\n\t\t\tfunction fail(): never {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"Cannot send this message because it would exceed the maximum payload length!\",\n\t\t\t\t\tZWaveErrorCodes.Controller_MessageTooLarge,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (msg.transmitOptions & TransmitOptions.Explore) {\n\t\t\t\tmsg.transmitOptions &= ~TransmitOptions.Explore;\n\t\t\t\tif (driver.exceedsMaxPayloadLength(msg)) {\n\t\t\t\t\t// Still too large\n\t\t\t\t\tfail();\n\t\t\t\t}\n\t\t\t\tdriver.controllerLog.logNode(msg.getNodeId()!, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Disabling explorer frames for this message due to its size\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tfail();\n\t\t\t}\n\t\t}\n\n\t\t// Pass this message to the send thread and wait for it to be sent\n\t\tlet result: Message;\n\t\t// At this point we can't have received a premature update\n\t\tmsg.prematureNodeUpdate = undefined;\n\n\t\ttry {\n\t\t\t// The yield can throw and must be handled here\n\t\t\tresult = yield msg;\n\n\t\t\t// Figure out how long the message took to be handled\n\t\t\tmsg.markAsCompleted();\n\n\t\t\tonMessageSent(msg, result);\n\t\t} catch (e) {\n\t\t\tmsg.markAsCompleted();\n\t\t\tthrow e;\n\t\t}\n\n\t\t// If the message was sent to a node and came back with a NOK callback,\n\t\t// we want to inspect the callback, for example to look at TX statistics\n\t\t// or update the node status.\n\t\t//\n\t\t// We now need to throw because the callback was passed through so we could inspect it.\n\t\tif (isTransmitReport(result) && !result.isOK()) {\n\t\t\t// Throw the message in order to short-circuit all possible generators\n\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\tthrow result;\n\t\t}\n\n\t\t// If the sent message expects an update from the node, wait for it\n\t\tif (msg.expectsNodeUpdate()) {\n\t\t\t// We might have received the update prematurely. In that case, return it.\n\t\t\tif (msg.prematureNodeUpdate) return msg.prematureNodeUpdate;\n\n\t\t\t// CommandTime is measured by the application\n\t\t\t// ReportTime timeout SHOULD be set to CommandTime + 1 second.\n\t\t\tconst timeout = getNodeUpdateTimeout(\n\t\t\t\tdriver,\n\t\t\t\tmsg,\n\t\t\t\tadditionalCommandTimeoutMs,\n\t\t\t);\n\t\t\treturn waitForNodeUpdate(driver, msg, timeout);\n\t\t}\n\n\t\treturn result;\n\t};\n\n/** A generator for singlecast SendData messages that automatically uses Transport Service when necessary */\nexport const maybeTransportServiceGenerator: MessageGeneratorImplementation<\n\tSendDataMessage & ContainsCC\n> = async function*(\n\tdriver,\n\tctx,\n\tmsg,\n\tonMessageSent,\n\tadditionalCommandTimeoutMs,\n) {\n\t// Make sure we can send this message\n\t/*if (!isSendData(msg) || !containsCC(msg)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Cannot use the Transport Service message generator for messages that are not SendData!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t} else*/ if (typeof msg.command.nodeId !== \"number\") {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the Transport Service message generator for multicast commands!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\tconst node = msg.tryGetNode(driver);\n\tconst mayUseTransportService =\n\t\tnode?.supportsCC(CommandClasses[\"Transport Service\"])\n\t\t&& node.getCCVersion(CommandClasses[\"Transport Service\"]) >= 2;\n\n\tif (!mayUseTransportService || !driver.exceedsMaxPayloadLength(msg)) {\n\t\t// Transport Service isn't needed for this message\n\t\treturn yield* simpleMessageGenerator(\n\t\t\tdriver,\n\t\t\tctx,\n\t\t\tmsg,\n\t\t\tonMessageSent,\n\t\t\tadditionalCommandTimeoutMs,\n\t\t);\n\t}\n\n\t// Send the command split into multiple segments\n\tconst payload = msg.serializeCC(ctx);\n\tconst numSegments = Math.ceil(payload.length / MAX_SEGMENT_SIZE);\n\tconst segmentDelay = numSegments > RELAXED_TIMING_THRESHOLD\n\t\t? TransportServiceTimeouts.relaxedTimingDelayR2\n\t\t: 0;\n\tconst sessionId = driver.getNextTransportServiceSessionId();\n\tconst nodeId = msg.command.nodeId;\n\n\t// Since the command is never logged, we do it here\n\tdriver.driverLog.print(\n\t\t\"The following message is too large, using Transport Service to transmit it:\",\n\t);\n\tdriver.driverLog.logMessage(msg, {\n\t\tdirection: \"outbound\",\n\t});\n\n\t// I don't see an elegant way to wait for possible responses, so we just register a handler in the driver\n\t// and remember the received commands\n\tlet unhandledResponses: TransportServiceCC[] = [];\n\tconst { unregister: unregisterHandler } = driver.registerCommandHandler(\n\t\t(cc) =>\n\t\t\tcc.nodeId === nodeId\n\t\t\t&& (cc instanceof TransportServiceCCSegmentWait\n\t\t\t\t|| (cc instanceof TransportServiceCCSegmentRequest\n\t\t\t\t\t&& cc.sessionId === sessionId)),\n\t\t(cc) => {\n\t\t\tunhandledResponses.push(cc as TransportServiceCC);\n\t\t},\n\t);\n\n\tconst receivedSegmentWait = () => {\n\t\tconst index = unhandledResponses.findIndex(\n\t\t\t(cc) => cc instanceof TransportServiceCCSegmentWait,\n\t\t);\n\t\tif (index >= 0) {\n\t\t\tconst cc = unhandledResponses[\n\t\t\t\tindex\n\t\t\t] as TransportServiceCCSegmentWait;\n\t\t\tunhandledResponses.splice(index, 1);\n\t\t\treturn cc;\n\t\t}\n\t};\n\n\tconst receivedSegmentRequest = () => {\n\t\tconst index = unhandledResponses.findIndex(\n\t\t\t(cc) => cc instanceof TransportServiceCCSegmentRequest,\n\t\t);\n\t\tif (index >= 0) {\n\t\t\tconst cc = unhandledResponses[\n\t\t\t\tindex\n\t\t\t] as TransportServiceCCSegmentRequest;\n\t\t\tunhandledResponses.splice(index, 1);\n\t\t\treturn cc;\n\t\t}\n\t};\n\n\t// We have to deal with multiple messages, but can only return a single result.\n\t// Therefore we use the last one as the result.\n\tlet result!: Message;\n\n\ttry {\n\t\tattempts: for (let attempt = 1; attempt <= 2; attempt++) {\n\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Beginning Transport Service TX session #${sessionId}...`,\n\t\t\t\tlevel: \"debug\",\n\t\t\t\tdirection: \"outbound\",\n\t\t\t});\n\n\t\t\t// Clear the list of unhandled responses\n\t\t\tunhandledResponses = [];\n\t\t\t// Fill the list of unsent segments\n\t\t\tconst unsentSegments = new Array(numSegments)\n\t\t\t\t.fill(0)\n\t\t\t\t.map((_, i) => i);\n\t\t\tlet didRetryLastSegment = false;\n\t\t\tlet isFirstTransferredSegment = true;\n\n\t\t\twhile (unsentSegments.length > 0) {\n\t\t\t\t// Wait if necessary\n\t\t\t\tif (isFirstTransferredSegment) {\n\t\t\t\t\tisFirstTransferredSegment = false;\n\t\t\t\t} else if (segmentDelay) {\n\t\t\t\t\tawait wait(segmentDelay, true);\n\t\t\t\t}\n\t\t\t\tconst segment = unsentSegments.shift()!;\n\n\t\t\t\tconst chunk = payload.subarray(\n\t\t\t\t\tsegment * MAX_SEGMENT_SIZE,\n\t\t\t\t\t(segment + 1) * MAX_SEGMENT_SIZE,\n\t\t\t\t);\n\t\t\t\tlet cc: TransportServiceCC;\n\t\t\t\tif (segment === 0) {\n\t\t\t\t\tcc = new TransportServiceCCFirstSegment({\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tdatagramSize: payload.length,\n\t\t\t\t\t\tpartialDatagram: chunk,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tcc = new TransportServiceCCSubsequentSegment({\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tdatagramSize: payload.length,\n\t\t\t\t\t\tdatagramOffset: segment * MAX_SEGMENT_SIZE,\n\t\t\t\t\t\tpartialDatagram: chunk,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst tmsg = driver.createSendDataMessage(cc, {\n\t\t\t\t\tautoEncapsulate: false,\n\t\t\t\t\tmaxSendAttempts: msg.maxSendAttempts,\n\t\t\t\t\ttransmitOptions: msg.transmitOptions,\n\t\t\t\t});\n\t\t\t\tresult = yield* simpleMessageGenerator(\n\t\t\t\t\tdriver,\n\t\t\t\t\tctx,\n\t\t\t\t\ttmsg,\n\t\t\t\t\tonMessageSent,\n\t\t\t\t);\n\n\t\t\t\tlet segmentComplete:\n\t\t\t\t\t| TransportServiceCCSegmentComplete\n\t\t\t\t\t| undefined = undefined;\n\t\t\t\t// After sending the last segment, wait for a SegmentComplete response, at the same time\n\t\t\t\t// give the node a chance to send a SegmentWait or SegmentRequest(s)\n\t\t\t\tif (segment === numSegments - 1) {\n\t\t\t\t\tsegmentComplete = await driver\n\t\t\t\t\t\t.waitForCommand<TransportServiceCCSegmentComplete>(\n\t\t\t\t\t\t\t(cc) =>\n\t\t\t\t\t\t\t\tcc.nodeId === nodeId\n\t\t\t\t\t\t\t\t&& cc\n\t\t\t\t\t\t\t\t\tinstanceof TransportServiceCCSegmentComplete\n\t\t\t\t\t\t\t\t&& cc.sessionId === sessionId,\n\t\t\t\t\t\t\tTransportServiceTimeouts.segmentCompleteR2,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t}\n\n\t\t\t\tif (segmentComplete) {\n\t\t\t\t\t// We're done!\n\t\t\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Transport Service TX session #${sessionId} complete`,\n\t\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t});\n\t\t\t\t\tbreak attempts;\n\t\t\t\t}\n\n\t\t\t\t// If we received a SegmentWait, we need to wait and restart\n\t\t\t\tconst segmentWait = receivedSegmentWait();\n\t\t\t\tif (segmentWait) {\n\t\t\t\t\tconst waitTime = segmentWait.pendingSegments * 100;\n\t\t\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Restarting Transport Service TX session #${sessionId} in ${waitTime} ms...`,\n\t\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\t});\n\n\t\t\t\t\tawait wait(waitTime, true);\n\t\t\t\t\tcontinue attempts;\n\t\t\t\t}\n\n\t\t\t\t// If the node requested missing segments, add them to the list of unsent segments and continue transmitting\n\t\t\t\tlet segmentRequest:\n\t\t\t\t\t| TransportServiceCCSegmentRequest\n\t\t\t\t\t| undefined = undefined;\n\t\t\t\tlet readdedSegments = false;\n\t\t\t\twhile ((segmentRequest = receivedSegmentRequest())) {\n\t\t\t\t\tunsentSegments.push(\n\t\t\t\t\t\tsegmentRequest.datagramOffset / MAX_SEGMENT_SIZE,\n\t\t\t\t\t);\n\t\t\t\t\treaddedSegments = true;\n\t\t\t\t}\n\t\t\t\tif (readdedSegments) continue;\n\n\t\t\t\t// If we didn't receive anything after sending the last segment, retry the last segment\n\t\t\t\tif (segment === numSegments - 1) {\n\t\t\t\t\tif (didRetryLastSegment) {\n\t\t\t\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`Transport Service TX session #${sessionId} failed`,\n\t\t\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tbreak attempts;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Try the last segment again\n\t\t\t\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`Transport Service TX session #${sessionId}: Segment Complete missing - re-transmitting last segment...`,\n\t\t\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tdidRetryLastSegment = true;\n\t\t\t\t\t\tunsentSegments.unshift(segment);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} finally {\n\t\t// We're done, unregister the handler\n\t\tunregisterHandler();\n\t}\n\n\t// Transport Service CCs do not expect a node update and have no knowledge about the encapsulated CC.\n\t// Therefore we need to replicate the waiting from simpleMessageGenerator here\n\n\t// If the sent message expects an update from the node, wait for it\n\tif (msg.expectsNodeUpdate()) {\n\t\t// TODO: Figure out if we can handle premature updates with Transport Service CC\n\t\tconst timeout = getNodeUpdateTimeout(\n\t\t\tdriver,\n\t\t\tmsg,\n\t\t\tadditionalCommandTimeoutMs,\n\t\t);\n\t\treturn waitForNodeUpdate(driver, msg, timeout);\n\t}\n\n\treturn result;\n};\n\n/** A simple (internal) generator that simply sends a command, and optionally returns the response command */\nasync function* sendCommandGenerator<\n\tTResponse extends CommandClass = CommandClass,\n>(\n\tdriver: Driver,\n\tctx: CCEncodingContext,\n\tcommand: CommandClass,\n\tonMessageSent: (msg: Message, result: Message | undefined) => void,\n\toptions?: SendCommandOptions,\n) {\n\tconst msg = driver.createSendDataMessage(command, options);\n\n\tconst resp = yield* maybeTransportServiceGenerator(\n\t\tdriver,\n\t\tctx,\n\t\tmsg,\n\t\tonMessageSent,\n\t);\n\tif (resp && containsCC(resp)) {\n\t\tdriver.unwrapCommands(resp);\n\t\treturn resp.command as TResponse;\n\t}\n}\n\n/** A message generator for security encapsulated messages (S0) */\nexport const secureMessageGeneratorS0: MessageGeneratorImplementation<\n\tSendDataMessage & ContainsCC\n> = async function*(driver, ctx, msg, onMessageSent) {\n\t/*if (!isSendData(msg)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Cannot use the S0 message generator for a command that's not a SendData message!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t} else*/ if (typeof msg.command.nodeId !== \"number\") {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S0 message generator for multicast commands!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t} else if (!(msg.command instanceof SecurityCCCommandEncapsulation)) {\n\t\tthrow new ZWaveError(\n\t\t\t\"The S0 message generator can only be used for Security S0 command encapsulation!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\t// Step 1: Acquire a nonce\n\tconst secMan = driver.securityManager!;\n\tconst nodeId = msg.command.nodeId;\n\tlet additionalTimeoutMs: number | undefined;\n\n\t// Try to get a free nonce before requesting a new one\n\tlet nonce: Uint8Array | undefined = secMan.getFreeNonce(nodeId);\n\tif (!nonce) {\n\t\t// No free nonce, request a new one\n\t\tconst cc = new SecurityCCNonceGet({\n\t\t\tnodeId: nodeId,\n\t\t\tendpointIndex: msg.command.endpointIndex,\n\t\t});\n\t\tconst nonceResp = yield* sendCommandGenerator<\n\t\t\tSecurityCCNonceReport\n\t\t>(\n\t\t\tdriver,\n\t\t\tctx,\n\t\t\tcc,\n\t\t\t(msg, result) => {\n\t\t\t\tadditionalTimeoutMs = Math.ceil(msg.rtt! / 1e6);\n\t\t\t\tonMessageSent(msg, result);\n\t\t\t},\n\t\t\t{\n\t\t\t\t// Only try getting a nonce once\n\t\t\t\tmaxSendAttempts: 1,\n\t\t\t},\n\t\t);\n\t\tif (!nonceResp) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"No nonce received from the node, cannot send secure command!\",\n\t\t\t\tZWaveErrorCodes.SecurityCC_NoNonce,\n\t\t\t);\n\t\t}\n\t\tnonce = nonceResp.nonce;\n\t}\n\tmsg.command.nonce = nonce;\n\n\t// Now send the actual secure command\n\treturn yield* simpleMessageGenerator(\n\t\tdriver,\n\t\tctx,\n\t\tmsg,\n\t\tonMessageSent,\n\t\tadditionalTimeoutMs,\n\t);\n};\n\n/** A message generator for security encapsulated messages (S2) */\nexport const secureMessageGeneratorS2: MessageGeneratorImplementation<\n\tSendDataMessage & ContainsCC\n> = async function*(driver, ctx, msg, onMessageSent) {\n\tif (!isSendData(msg) || !containsCC(msg)) {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S2 message generator for a command that's not a SendData message!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t} else if (typeof msg.command.nodeId !== \"number\") {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S2 message generator for multicast commands!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t} else if (!(msg.command instanceof Security2CCMessageEncapsulation)) {\n\t\tthrow new ZWaveError(\n\t\t\t\"The S2 message generator can only be used for Security S2 command encapsulation!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\tconst nodeId = msg.command.nodeId;\n\tconst secMan = driver.getSecurityManager2(nodeId)!;\n\tconst spanState = secMan.getSPANState(nodeId);\n\tlet additionalTimeoutMs: number | undefined;\n\n\t// We need a new nonce when there is no shared SPAN state, or the SPAN state is for a lower security class\n\t// than the command we want to send\n\tconst expectedSecurityClass = msg.command.securityClass\n\t\t?? driver.getHighestSecurityClass(nodeId);\n\n\tif (\n\t\tspanState.type === SPANState.None\n\t\t|| spanState.type === SPANState.LocalEI\n\t\t|| (spanState.type === SPANState.SPAN\n\t\t\t&& spanState.securityClass !== SecurityClass.Temporary\n\t\t\t&& spanState.securityClass !== expectedSecurityClass)\n\t) {\n\t\t// Request a new nonce\n\n\t\t// No free nonce, request a new one\n\t\tconst cc = new Security2CCNonceGet({\n\t\t\tnodeId: nodeId,\n\t\t\tendpointIndex: msg.command.endpointIndex,\n\t\t});\n\t\tconst nonceResp = yield* sendCommandGenerator<\n\t\t\tSecurity2CCNonceReport\n\t\t>(\n\t\t\tdriver,\n\t\t\tctx,\n\t\t\tcc,\n\t\t\t(msg, result) => {\n\t\t\t\tadditionalTimeoutMs = Math.ceil(msg.rtt! / 1e6);\n\t\t\t\tonMessageSent(msg, result);\n\t\t\t},\n\t\t\t{\n\t\t\t\t// Only try getting a nonce once\n\t\t\t\tmaxSendAttempts: 1,\n\t\t\t},\n\t\t);\n\t\tif (!nonceResp) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"No nonce received from the node, cannot send secure command!\",\n\t\t\t\tZWaveErrorCodes.Security2CC_NoSPAN,\n\t\t\t);\n\t\t}\n\n\t\t// Storing the nonce is not necessary, this will be done automatically when the nonce is received\n\t}\n\n\t// Now send the actual secure command\n\tlet response = yield* maybeTransportServiceGenerator(\n\t\tdriver,\n\t\tctx,\n\t\tmsg,\n\t\tonMessageSent,\n\t\tadditionalTimeoutMs,\n\t);\n\n\t// If we want to make sure that a node understood a SET-type S2-encapsulated message, we either need to use\n\t// Supervision and wait for the Supervision Report (handled by the simpleMessageGenerator), or we need to add a\n\t// short delay between commands and wait if a NonceReport is received.\n\t// However, in situations where timing is critical (e.g. S2 bootstrapping), verifyDelivery is set to false, and we don't do this.\n\tlet nonceReport: Security2CCNonceReport | undefined;\n\tif (\n\t\tisTransmitReport(response)\n\t\t&& msg.command.verifyDelivery\n\t\t&& !msg.command.expectsCCResponse()\n\t\t&& !msg.command.getEncapsulatedCC(\n\t\t\tCommandClasses.Supervision,\n\t\t\tSupervisionCommand.Get,\n\t\t)\n\t) {\n\t\tnonceReport = await driver\n\t\t\t.waitForCommand<Security2CCNonceReport>(\n\t\t\t\t(cc) =>\n\t\t\t\t\tcc.nodeId === nodeId\n\t\t\t\t\t&& cc instanceof Security2CCNonceReport,\n\t\t\t\t500,\n\t\t\t)\n\t\t\t.catch(() => undefined);\n\t} else if (\n\t\tcontainsCC(response)\n\t\t&& response.command instanceof Security2CCNonceReport\n\t) {\n\t\tnonceReport = response.command;\n\t}\n\n\tif (nonceReport) {\n\t\tif (nonceReport.SOS && nonceReport.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(nodeId, nonceReport.receiverEI);\n\t\t}\n\t\tif (nonceReport.MOS) {\n\t\t\tconst multicastGroupId = msg.command.getMulticastGroupId();\n\t\t\tif (multicastGroupId != undefined) {\n\t\t\t\t// The node couldn't decrypt the previous S2 multicast. Tell it the MPAN (again)\n\t\t\t\tconst mpan = secMan.getInnerMPANState(multicastGroupId);\n\t\t\t\tif (mpan) {\n\t\t\t\t\t// Replace the MGRP extension with an MPAN extension\n\t\t\t\t\tmsg.command.extensions = msg.command.extensions.filter(\n\t\t\t\t\t\t(e) => !(e instanceof MGRPExtension),\n\t\t\t\t\t);\n\t\t\t\t\tmsg.command.extensions.push(\n\t\t\t\t\t\tnew MPANExtension({\n\t\t\t\t\t\t\tgroupId: multicastGroupId,\n\t\t\t\t\t\t\tinnerMPANState: mpan,\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\tdriver.controllerLog.logNode(nodeId, {\n\t\t\tmessage:\n\t\t\t\t`failed to decode the message, retrying with SPAN extension...`,\n\t\t\tdirection: \"none\",\n\t\t});\n\n\t\t// Send the message again\n\t\tmsg.prepareRetransmission();\n\t\tresponse = yield* maybeTransportServiceGenerator(\n\t\t\tdriver,\n\t\t\tctx,\n\t\t\tmsg,\n\t\t\tonMessageSent,\n\t\t\tadditionalTimeoutMs,\n\t\t);\n\n\t\tif (\n\t\t\tcontainsCC(response)\n\t\t\t&& response.command instanceof Security2CCNonceReport\n\t\t) {\n\t\t\t// No dice\n\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\tmessage:\n\t\t\t\t\t`failed to decode the message after re-transmission with SPAN extension, dropping the message.`,\n\t\t\t\tdirection: \"none\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The node failed to decode the message.\",\n\t\t\t\tZWaveErrorCodes.Security2CC_CannotDecode,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn response;\n};\n\n/** A message generator for security encapsulated messages (S2 Multicast) */\nexport const secureMessageGeneratorS2Multicast: MessageGeneratorImplementation<\n\tSendDataMessage & ContainsCC\n> = async function*(driver, ctx, msg, onMessageSent) {\n\tif (!isSendData(msg) || !containsCC(msg)) {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S2 multicast message generator for a command that's not a SendData message!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t} else if (msg.command.isSinglecast()) {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S2 multicast message generator for singlecast commands!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t} else if (!(msg.command instanceof Security2CCMessageEncapsulation)) {\n\t\tthrow new ZWaveError(\n\t\t\t\"The S2 multicast message generator can only be used for Security S2 command encapsulation!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\tconst groupId = msg.command.getMulticastGroupId();\n\tif (groupId == undefined) {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S2 multicast message generator without a multicast group ID!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\tconst secMan = driver.getSecurityManager2(msg.command.nodeId)!;\n\tconst group = secMan.getMulticastGroup(groupId);\n\tif (!group) {\n\t\tthrow new ZWaveError(\n\t\t\t`Multicast group ${groupId} does not exist!`,\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\t// Send the multicast command. We remember the transmit report and treat it as the result of the multicast command\n\tconst response = yield* simpleMessageGenerator(\n\t\tdriver,\n\t\tctx,\n\t\tmsg,\n\t\tonMessageSent,\n\t);\n\n\t// If a node in the group is out of sync, we need to transfer the MPAN state we're going to use for the next command.\n\t// Therefore increment the MPAN state now and not after the followups like the specs mention\n\tsecMan.tryIncrementMPAN(groupId);\n\n\t// Unwrap the command again, so we can make the following encapsulation depend on the target node\n\tdriver.unwrapCommands(msg);\n\tconst command = msg.command;\n\t// Remember the original encapsulation flags\n\tconst encapsulationFlags = command.encapsulationFlags;\n\n\t// In case someone sneaked a node ID into the group multiple times, remove duplicates for the singlecast followups\n\t// Otherwise, the node will increase its MPAN multiple times, going out of sync.\n\tconst distinctNodeIDs = [...new Set(group.nodeIDs)];\n\n\tconst supervisionResults: (SupervisionResult | undefined)[] = [];\n\n\t// Now do singlecast followups with every node in the group\n\tfor (const nodeId of distinctNodeIDs) {\n\t\t// Point the CC at the target node\n\t\t(command.nodeId as number) = nodeId;\n\t\t// Figure out if supervision should be used\n\t\tcommand.encapsulationFlags = encapsulationFlags;\n\t\tcommand.toggleEncapsulationFlag(\n\t\t\tEncapsulationFlags.Supervision,\n\t\t\tSupervisionCC.mayUseSupervision(driver, command),\n\t\t);\n\n\t\tconst scMsg = driver.createSendDataMessage(command, {\n\t\t\ttransmitOptions: msg.transmitOptions,\n\t\t\tmaxSendAttempts: msg.maxSendAttempts,\n\t\t});\n\t\t// The outermost command is a Security2CCMessageEncapsulation, we need to set the MGRP extension on this again\n\t\t(scMsg.command as Security2CCMessageEncapsulation).extensions.push(\n\t\t\tnew MGRPExtension({ groupId }),\n\t\t);\n\n\t\t// Reuse the S2 singlecast message generator for sending this new message\n\t\ttry {\n\t\t\tconst scResponse = yield* secureMessageGeneratorS2(\n\t\t\t\tdriver,\n\t\t\t\tctx,\n\t\t\t\tscMsg,\n\t\t\t\tonMessageSent,\n\t\t\t);\n\t\t\tif (\n\t\t\t\tcontainsCC(scResponse)\n\t\t\t\t&& scResponse.command\n\t\t\t\t\tinstanceof Security2CCMessageEncapsulation\n\t\t\t\t&& scResponse.command.hasMOSExtension()\n\t\t\t) {\n\t\t\t\t// The node understood the S2 singlecast followup, but told us that its MPAN is out of sync\n\n\t\t\t\tconst innerMPANState = secMan.getInnerMPANState(groupId);\n\t\t\t\t// This should always be defined, but better not throw unnecessarily here\n\t\t\t\tif (innerMPANState) {\n\t\t\t\t\tconst cc = new Security2CCMessageEncapsulation({\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\textensions: [\n\t\t\t\t\t\t\tnew MPANExtension({\n\t\t\t\t\t\t\t\tgroupId,\n\t\t\t\t\t\t\t\tinnerMPANState,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\n\t\t\t\t\t// Send it the MPAN\n\t\t\t\t\tyield* sendCommandGenerator(\n\t\t\t\t\t\tdriver,\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\tcc,\n\t\t\t\t\t\tonMessageSent,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Seems we need these options or some nodes won't accept the nonce\n\t\t\t\t\t\t\ttransmitOptions: TransmitOptions.ACK\n\t\t\t\t\t\t\t\t| TransmitOptions.AutoRoute,\n\t\t\t\t\t\t\t// Only try sending a nonce once\n\t\t\t\t\t\t\tmaxSendAttempts: 1,\n\t\t\t\t\t\t\t// Nonce requests must be handled immediately\n\t\t\t\t\t\t\tpriority: MessagePriority.Immediate,\n\t\t\t\t\t\t\t// We don't want failures causing us to treat the node as asleep or dead\n\t\t\t\t\t\t\tchangeNodeStatusOnMissingACK: false,\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// Collect supervision results if possible\n\t\t\tif (containsCC(scResponse)) {\n\t\t\t\tconst supervisionReport = scResponse.command\n\t\t\t\t\t.getEncapsulatedCC(\n\t\t\t\t\t\tCommandClasses.Supervision,\n\t\t\t\t\t\tSupervisionCommand.Report,\n\t\t\t\t\t) as SupervisionCCReport | undefined;\n\n\t\t\t\tsupervisionResults.push(\n\t\t\t\t\tsupervisionReport?.toSupervisionResult(),\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tdriver.driverLog.print(getErrorMessage(e), \"error\");\n\t\t\t// TODO: Figure out how we got here, and what to do now.\n\t\t\t// In any case, keep going with the next nodes\n\t\t\t// Report that there was a failure, so the application can show it\n\t\t\tsupervisionResults.push({\n\t\t\t\tstatus: SupervisionStatus.Fail,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst finalSupervisionResult = mergeSupervisionResults(\n\t\tsupervisionResults,\n\t);\n\tif (finalSupervisionResult) {\n\t\t// We can return return information about the success of this multicast - so we should\n\t\t// TODO: Not sure if we need to \"wrap\" the response for something. For now, try faking it\n\t\tconst cc = new SupervisionCCReport({\n\t\t\tnodeId: NODE_ID_BROADCAST,\n\t\t\tsessionId: 0, // fake\n\t\t\tmoreUpdatesFollow: false, // fake\n\t\t\t...(finalSupervisionResult as any),\n\t\t});\n\t\tconst ret = new (driver.getSendDataSinglecastConstructor())({\n\t\t\tsourceNodeId: driver.ownNodeId,\n\t\t\tcommand: cc,\n\t\t});\n\t\treturn ret;\n\t} else {\n\t\treturn response;\n\t}\n};\n\nexport function createMessageGenerator<TResponse extends Message = Message>(\n\tdriver: Driver,\n\tctx: CCEncodingContext,\n\tmsg: Message,\n\tonMessageSent: (msg: Message, result: Message | undefined) => void,\n): {\n\tgenerator: MessageGenerator;\n\tresultPromise: DeferredPromise<TResponse>;\n} {\n\tconst resultPromise = createDeferredPromise<TResponse>();\n\n\tconst generator: MessageGenerator = {\n\t\tparent: undefined as any, // The transaction will set this field on creation\n\t\tcurrent: undefined,\n\t\tself: undefined,\n\t\treset: () => {\n\t\t\tgenerator.current = undefined;\n\t\t\tgenerator.self = undefined;\n\t\t},\n\t\tstart: () => {\n\t\t\tasync function* gen() {\n\t\t\t\t// Determine which message generator implementation should be used\n\t\t\t\tlet implementation: MessageGeneratorImplementation<Message> =\n\t\t\t\t\tsimpleMessageGenerator;\n\t\t\t\tif (isSendData(msg)) {\n\t\t\t\t\tif (!containsCC(msg)) {\n\t\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\t\"Cannot create a message generator for a message that doesn't contain a command class\",\n\t\t\t\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\tmsg.command instanceof Security2CCMessageEncapsulation\n\t\t\t\t\t) {\n\t\t\t\t\t\timplementation = (\n\t\t\t\t\t\t\tmsg.command.isSinglecast()\n\t\t\t\t\t\t\t\t? secureMessageGeneratorS2\n\t\t\t\t\t\t\t\t: secureMessageGeneratorS2Multicast\n\t\t\t\t\t\t) as MessageGeneratorImplementation<Message>;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\tmsg.command instanceof SecurityCCCommandEncapsulation\n\t\t\t\t\t) {\n\t\t\t\t\t\timplementation =\n\t\t\t\t\t\t\tsecureMessageGeneratorS0 as MessageGeneratorImplementation<\n\t\t\t\t\t\t\t\tMessage\n\t\t\t\t\t\t\t>;\n\t\t\t\t\t} else if (msg.command.isSinglecast()) {\n\t\t\t\t\t\timplementation =\n\t\t\t\t\t\t\tmaybeTransportServiceGenerator as MessageGeneratorImplementation<\n\t\t\t\t\t\t\t\tMessage\n\t\t\t\t\t\t\t>;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Step through the generator so we can easily cancel it and don't\n\t\t\t\t// accidentally forget to unset this.current at the end\n\t\t\t\tconst gen = implementation(driver, ctx, msg, onMessageSent);\n\t\t\t\tlet sendResult: Message | undefined;\n\t\t\t\tlet result: Message | undefined;\n\t\t\t\twhile (true) {\n\t\t\t\t\t// This call passes the previous send result (if it exists already) to the generator and saves the\n\t\t\t\t\t// generated or returned message in `value`. When `done` is true, `value` contains the returned result of the message generator\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { value, done } = await gen.next(sendResult!);\n\t\t\t\t\t\tif (done) {\n\t\t\t\t\t\t\tresult = value;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Pass the generated message to the driver and remember the result for the next iteration\n\t\t\t\t\t\tgenerator.current = value;\n\t\t\t\t\t\tsendResult = yield generator.current;\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (e instanceof Error) {\n\t\t\t\t\t\t\t// There was an actual error, reject the transaction\n\t\t\t\t\t\t\tresultPromise.reject(e);\n\t\t\t\t\t\t} else if (isTransmitReport(e) && !e.isOK()) {\n\t\t\t\t\t\t\t// The generator was prematurely ended by throwing a NOK transmit report.\n\t\t\t\t\t\t\t// The driver may want to retry it, so reset the generator\n\t\t\t\t\t\t\tgenerator.reset();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// The generator was prematurely ended by throwing a Message\n\t\t\t\t\t\t\tresultPromise.resolve(e as TResponse);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresultPromise.resolve(result as TResponse);\n\t\t\t\tgenerator.reset();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tgenerator.self = gen();\n\t\t\treturn generator.self;\n\t\t},\n\t};\n\treturn { resultPromise, generator };\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import {\n\ttype CommandClass,\n\tMGRPExtension,\n\tMPANExtension,\n\tSecurity2CCMessageEncapsulation,\n\tSecurity2CCNonceGet,\n\tSecurity2CCNonceReport,\n\tSupervisionCC,\n\tSupervisionCCReport,\n\tSupervisionCommand,\n\tgetInnermostCommandClass,\n} from \"@zwave-js/cc\";\nimport {\n\tSecurityCCCommandEncapsulation,\n\tSecurityCCNonceGet,\n\ttype SecurityCCNonceReport,\n} from \"@zwave-js/cc/SecurityCC\";\nimport {\n\tMAX_SEGMENT_SIZE,\n\tRELAXED_TIMING_THRESHOLD,\n\ttype TransportServiceCC,\n\tTransportServiceCCFirstSegment,\n\tTransportServiceCCSegmentComplete,\n\tTransportServiceCCSegmentRequest,\n\tTransportServiceCCSegmentWait,\n\tTransportServiceCCSubsequentSegment,\n\tTransportServiceTimeouts,\n} from \"@zwave-js/cc/TransportServiceCC\";\nimport {\n\tCommandClasses,\n\tEncapsulationFlags,\n\tMessagePriority,\n\tNODE_ID_BROADCAST,\n\tSPANState,\n\tSecurityClass,\n\ttype SendCommandOptions,\n\ttype SupervisionResult,\n\tSupervisionStatus,\n\tTransmitOptions,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tmergeSupervisionResults,\n} from \"@zwave-js/core\";\nimport { type CCEncodingContext } from \"@zwave-js/host\";\nimport type { Message } from \"@zwave-js/serial\";\nimport {\n\ttype SendDataMessage,\n\tisSendData,\n\tisTransmitReport,\n} from \"@zwave-js/serial/serialapi\";\nimport { type ContainsCC, containsCC } from \"@zwave-js/serial/serialapi\";\nimport { getErrorMessage } from \"@zwave-js/shared\";\nimport { wait } from \"alcalzone-shared/async\";\nimport {\n\ttype DeferredPromise,\n\tcreateDeferredPromise,\n} from \"alcalzone-shared/deferred-promise\";\nimport type { Driver } from \"./Driver.js\";\nimport type { MessageGenerator } from \"./Transaction.js\";\n\nexport type MessageGeneratorImplementation<T extends Message> = (\n\t/** A reference to the driver */\n\tdriver: Driver,\n\tctx: CCEncodingContext,\n\t/** The \"primary\" message */\n\tmessage: T,\n\t/**\n\t * A hook to get notified about each sent message and the result of the Serial API call\n\t * without waiting for the message generator to finish completely.\n\t */\n\tonMessageSent: (msg: Message, result: Message | undefined) => void,\n\t/** Can be used to extend the timeout waiting for a response from a node to the sent message */\n\tadditionalCommandTimeoutMs?: number,\n) => AsyncGenerator<Message, Message, Message>;\n\nfunction maybePartialNodeUpdate(sent: Message, received: Message): boolean {\n\t// Some commands are returned in multiple segments, which may take longer than\n\t// the configured timeout.\n\tif (!containsCC(sent) || !containsCC(received)) {\n\t\treturn false;\n\t}\n\n\tif (sent.getNodeId() !== received.getNodeId()) return false;\n\n\tif (received.command.ccId === CommandClasses[\"Transport Service\"]) {\n\t\t// We don't know what's in there. It may be the expected update\n\t\treturn true;\n\t}\n\n\t// Let the sent CC test if the received one is a match.\n\t// These predicates don't check if the received CC is complete, we can use them here.\n\t// This also doesn't check for correct encapsulation, but that is good enough to refresh the timer.\n\tconst sentCommand = getInnermostCommandClass(sent.command);\n\tconst receivedCommand = getInnermostCommandClass(received.command);\n\treturn sentCommand.isExpectedCCResponse(receivedCommand);\n}\n\nexport async function waitForNodeUpdate<T extends Message>(\n\tdriver: Driver,\n\tmsg: Message,\n\ttimeoutMs: number,\n): Promise<T> {\n\ttry {\n\t\treturn await driver.waitForMessage<T>(\n\t\t\t(received) => msg.isExpectedNodeUpdate(received),\n\t\t\ttimeoutMs,\n\t\t\t(received) => maybePartialNodeUpdate(msg, received),\n\t\t);\n\t} catch {\n\t\tthrow new ZWaveError(\n\t\t\t`Timed out while waiting for a response from the node`,\n\t\t\tZWaveErrorCodes.Controller_NodeTimeout,\n\t\t);\n\t}\n}\n\nfunction getNodeUpdateTimeout(\n\tdriver: Driver,\n\tmsg: Message,\n\tadditionalCommandTimeoutMs = 0,\n): number {\n\tconst commandTimeMs = Math.ceil((msg.rtt ?? 0) / 1e6);\n\treturn (\n\t\tcommandTimeMs\n\t\t+ driver.getReportTimeout(msg)\n\t\t+ additionalCommandTimeoutMs\n\t);\n}\n\n/** A simple message generator that simply sends a message, waits for the ACK (and the response if one is expected) */\nexport const simpleMessageGenerator: MessageGeneratorImplementation<Message> =\n\tasync function*(\n\t\tdriver,\n\t\tctx,\n\t\tmsg,\n\t\tonMessageSent,\n\t\tadditionalCommandTimeoutMs = 0,\n\t) {\n\t\t// Make sure we can send this message\n\t\tif (isSendData(msg) && await driver.exceedsMaxPayloadLength(msg)) {\n\t\t\t// We use explorer frames by default, but this reduces the maximum payload length by 2 bytes compared to AUTO_ROUTE\n\t\t\t// Try disabling explorer frames for this message and see if it fits now.\n\t\t\tfunction fail(): never {\n\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\"Cannot send this message because it would exceed the maximum payload length!\",\n\t\t\t\t\tZWaveErrorCodes.Controller_MessageTooLarge,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (msg.transmitOptions & TransmitOptions.Explore) {\n\t\t\t\tmsg.transmitOptions &= ~TransmitOptions.Explore;\n\t\t\t\tif (await driver.exceedsMaxPayloadLength(msg)) {\n\t\t\t\t\t// Still too large\n\t\t\t\t\tfail();\n\t\t\t\t}\n\t\t\t\tdriver.controllerLog.logNode(msg.getNodeId()!, {\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Disabling explorer frames for this message due to its size\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tfail();\n\t\t\t}\n\t\t}\n\n\t\t// Pass this message to the send thread and wait for it to be sent\n\t\tlet result: Message;\n\t\t// At this point we can't have received a premature update\n\t\tmsg.prematureNodeUpdate = undefined;\n\n\t\ttry {\n\t\t\t// The yield can throw and must be handled here\n\t\t\tresult = yield msg;\n\n\t\t\t// Figure out how long the message took to be handled\n\t\t\tmsg.markAsCompleted();\n\n\t\t\tonMessageSent(msg, result);\n\t\t} catch (e) {\n\t\t\tmsg.markAsCompleted();\n\t\t\tthrow e;\n\t\t}\n\n\t\t// If the message was sent to a node and came back with a NOK callback,\n\t\t// we want to inspect the callback, for example to look at TX statistics\n\t\t// or update the node status.\n\t\t//\n\t\t// We now need to throw because the callback was passed through so we could inspect it.\n\t\tif (isTransmitReport(result) && !result.isOK()) {\n\t\t\t// Throw the message in order to short-circuit all possible generators\n\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\tthrow result;\n\t\t}\n\n\t\t// If the sent message expects an update from the node, wait for it\n\t\tif (msg.expectsNodeUpdate()) {\n\t\t\t// We might have received the update prematurely. In that case, return it.\n\t\t\tif (msg.prematureNodeUpdate) return msg.prematureNodeUpdate;\n\n\t\t\t// CommandTime is measured by the application\n\t\t\t// ReportTime timeout SHOULD be set to CommandTime + 1 second.\n\t\t\tconst timeout = getNodeUpdateTimeout(\n\t\t\t\tdriver,\n\t\t\t\tmsg,\n\t\t\t\tadditionalCommandTimeoutMs,\n\t\t\t);\n\t\t\treturn waitForNodeUpdate(driver, msg, timeout);\n\t\t}\n\n\t\treturn result;\n\t};\n\n/** A generator for singlecast SendData messages that automatically uses Transport Service when necessary */\nexport const maybeTransportServiceGenerator: MessageGeneratorImplementation<\n\tSendDataMessage & ContainsCC\n> = async function*(\n\tdriver,\n\tctx,\n\tmsg,\n\tonMessageSent,\n\tadditionalCommandTimeoutMs,\n) {\n\t// Make sure we can send this message\n\t/*if (!isSendData(msg) || !containsCC(msg)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Cannot use the Transport Service message generator for messages that are not SendData!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t} else*/ if (typeof msg.command.nodeId !== \"number\") {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the Transport Service message generator for multicast commands!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\tconst node = msg.tryGetNode(driver);\n\tconst mayUseTransportService =\n\t\tnode?.supportsCC(CommandClasses[\"Transport Service\"])\n\t\t&& node.getCCVersion(CommandClasses[\"Transport Service\"]) >= 2;\n\n\tif (\n\t\t!mayUseTransportService || !(await driver.exceedsMaxPayloadLength(msg))\n\t) {\n\t\t// Transport Service isn't needed for this message\n\t\treturn yield* simpleMessageGenerator(\n\t\t\tdriver,\n\t\t\tctx,\n\t\t\tmsg,\n\t\t\tonMessageSent,\n\t\t\tadditionalCommandTimeoutMs,\n\t\t);\n\t}\n\n\t// Send the command split into multiple segments\n\tconst payload = await msg.serializeCCAsync(ctx);\n\tconst numSegments = Math.ceil(payload.length / MAX_SEGMENT_SIZE);\n\tconst segmentDelay = numSegments > RELAXED_TIMING_THRESHOLD\n\t\t? TransportServiceTimeouts.relaxedTimingDelayR2\n\t\t: 0;\n\tconst sessionId = driver.getNextTransportServiceSessionId();\n\tconst nodeId = msg.command.nodeId;\n\n\t// Since the command is never logged, we do it here\n\tdriver.driverLog.print(\n\t\t\"The following message is too large, using Transport Service to transmit it:\",\n\t);\n\tdriver.driverLog.logMessage(msg, {\n\t\tdirection: \"outbound\",\n\t});\n\n\t// I don't see an elegant way to wait for possible responses, so we just register a handler in the driver\n\t// and remember the received commands\n\tlet unhandledResponses: TransportServiceCC[] = [];\n\tconst { unregister: unregisterHandler } = driver.registerCommandHandler(\n\t\t(cc) =>\n\t\t\tcc.nodeId === nodeId\n\t\t\t&& (cc instanceof TransportServiceCCSegmentWait\n\t\t\t\t|| (cc instanceof TransportServiceCCSegmentRequest\n\t\t\t\t\t&& cc.sessionId === sessionId)),\n\t\t(cc) => {\n\t\t\tunhandledResponses.push(cc as TransportServiceCC);\n\t\t},\n\t);\n\n\tconst receivedSegmentWait = () => {\n\t\tconst index = unhandledResponses.findIndex(\n\t\t\t(cc) => cc instanceof TransportServiceCCSegmentWait,\n\t\t);\n\t\tif (index >= 0) {\n\t\t\tconst cc = unhandledResponses[\n\t\t\t\tindex\n\t\t\t] as TransportServiceCCSegmentWait;\n\t\t\tunhandledResponses.splice(index, 1);\n\t\t\treturn cc;\n\t\t}\n\t};\n\n\tconst receivedSegmentRequest = () => {\n\t\tconst index = unhandledResponses.findIndex(\n\t\t\t(cc) => cc instanceof TransportServiceCCSegmentRequest,\n\t\t);\n\t\tif (index >= 0) {\n\t\t\tconst cc = unhandledResponses[\n\t\t\t\tindex\n\t\t\t] as TransportServiceCCSegmentRequest;\n\t\t\tunhandledResponses.splice(index, 1);\n\t\t\treturn cc;\n\t\t}\n\t};\n\n\t// We have to deal with multiple messages, but can only return a single result.\n\t// Therefore we use the last one as the result.\n\tlet result!: Message;\n\n\ttry {\n\t\tattempts: for (let attempt = 1; attempt <= 2; attempt++) {\n\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\tmessage:\n\t\t\t\t\t`Beginning Transport Service TX session #${sessionId}...`,\n\t\t\t\tlevel: \"debug\",\n\t\t\t\tdirection: \"outbound\",\n\t\t\t});\n\n\t\t\t// Clear the list of unhandled responses\n\t\t\tunhandledResponses = [];\n\t\t\t// Fill the list of unsent segments\n\t\t\tconst unsentSegments = new Array(numSegments)\n\t\t\t\t.fill(0)\n\t\t\t\t.map((_, i) => i);\n\t\t\tlet didRetryLastSegment = false;\n\t\t\tlet isFirstTransferredSegment = true;\n\n\t\t\twhile (unsentSegments.length > 0) {\n\t\t\t\t// Wait if necessary\n\t\t\t\tif (isFirstTransferredSegment) {\n\t\t\t\t\tisFirstTransferredSegment = false;\n\t\t\t\t} else if (segmentDelay) {\n\t\t\t\t\tawait wait(segmentDelay, true);\n\t\t\t\t}\n\t\t\t\tconst segment = unsentSegments.shift()!;\n\n\t\t\t\tconst chunk = payload.subarray(\n\t\t\t\t\tsegment * MAX_SEGMENT_SIZE,\n\t\t\t\t\t(segment + 1) * MAX_SEGMENT_SIZE,\n\t\t\t\t);\n\t\t\t\tlet cc: TransportServiceCC;\n\t\t\t\tif (segment === 0) {\n\t\t\t\t\tcc = new TransportServiceCCFirstSegment({\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tdatagramSize: payload.length,\n\t\t\t\t\t\tpartialDatagram: chunk,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tcc = new TransportServiceCCSubsequentSegment({\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tdatagramSize: payload.length,\n\t\t\t\t\t\tdatagramOffset: segment * MAX_SEGMENT_SIZE,\n\t\t\t\t\t\tpartialDatagram: chunk,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst tmsg = driver.createSendDataMessage(cc, {\n\t\t\t\t\tautoEncapsulate: false,\n\t\t\t\t\tmaxSendAttempts: msg.maxSendAttempts,\n\t\t\t\t\ttransmitOptions: msg.transmitOptions,\n\t\t\t\t});\n\t\t\t\tresult = yield* simpleMessageGenerator(\n\t\t\t\t\tdriver,\n\t\t\t\t\tctx,\n\t\t\t\t\ttmsg,\n\t\t\t\t\tonMessageSent,\n\t\t\t\t);\n\n\t\t\t\tlet segmentComplete:\n\t\t\t\t\t| TransportServiceCCSegmentComplete\n\t\t\t\t\t| undefined = undefined;\n\t\t\t\t// After sending the last segment, wait for a SegmentComplete response, at the same time\n\t\t\t\t// give the node a chance to send a SegmentWait or SegmentRequest(s)\n\t\t\t\tif (segment === numSegments - 1) {\n\t\t\t\t\tsegmentComplete = await driver\n\t\t\t\t\t\t.waitForCommand<TransportServiceCCSegmentComplete>(\n\t\t\t\t\t\t\t(cc) =>\n\t\t\t\t\t\t\t\tcc.nodeId === nodeId\n\t\t\t\t\t\t\t\t&& cc\n\t\t\t\t\t\t\t\t\tinstanceof TransportServiceCCSegmentComplete\n\t\t\t\t\t\t\t\t&& cc.sessionId === sessionId,\n\t\t\t\t\t\t\tTransportServiceTimeouts.segmentCompleteR2,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t}\n\n\t\t\t\tif (segmentComplete) {\n\t\t\t\t\t// We're done!\n\t\t\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Transport Service TX session #${sessionId} complete`,\n\t\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t});\n\t\t\t\t\tbreak attempts;\n\t\t\t\t}\n\n\t\t\t\t// If we received a SegmentWait, we need to wait and restart\n\t\t\t\tconst segmentWait = receivedSegmentWait();\n\t\t\t\tif (segmentWait) {\n\t\t\t\t\tconst waitTime = segmentWait.pendingSegments * 100;\n\t\t\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t`Restarting Transport Service TX session #${sessionId} in ${waitTime} ms...`,\n\t\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\t});\n\n\t\t\t\t\tawait wait(waitTime, true);\n\t\t\t\t\tcontinue attempts;\n\t\t\t\t}\n\n\t\t\t\t// If the node requested missing segments, add them to the list of unsent segments and continue transmitting\n\t\t\t\tlet segmentRequest:\n\t\t\t\t\t| TransportServiceCCSegmentRequest\n\t\t\t\t\t| undefined = undefined;\n\t\t\t\tlet readdedSegments = false;\n\t\t\t\twhile ((segmentRequest = receivedSegmentRequest())) {\n\t\t\t\t\tunsentSegments.push(\n\t\t\t\t\t\tsegmentRequest.datagramOffset / MAX_SEGMENT_SIZE,\n\t\t\t\t\t);\n\t\t\t\t\treaddedSegments = true;\n\t\t\t\t}\n\t\t\t\tif (readdedSegments) continue;\n\n\t\t\t\t// If we didn't receive anything after sending the last segment, retry the last segment\n\t\t\t\tif (segment === numSegments - 1) {\n\t\t\t\t\tif (didRetryLastSegment) {\n\t\t\t\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`Transport Service TX session #${sessionId} failed`,\n\t\t\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tbreak attempts;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Try the last segment again\n\t\t\t\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`Transport Service TX session #${sessionId}: Segment Complete missing - re-transmitting last segment...`,\n\t\t\t\t\t\t\tlevel: \"debug\",\n\t\t\t\t\t\t\tdirection: \"outbound\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tdidRetryLastSegment = true;\n\t\t\t\t\t\tunsentSegments.unshift(segment);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} finally {\n\t\t// We're done, unregister the handler\n\t\tunregisterHandler();\n\t}\n\n\t// Transport Service CCs do not expect a node update and have no knowledge about the encapsulated CC.\n\t// Therefore we need to replicate the waiting from simpleMessageGenerator here\n\n\t// If the sent message expects an update from the node, wait for it\n\tif (msg.expectsNodeUpdate()) {\n\t\t// TODO: Figure out if we can handle premature updates with Transport Service CC\n\t\tconst timeout = getNodeUpdateTimeout(\n\t\t\tdriver,\n\t\t\tmsg,\n\t\t\tadditionalCommandTimeoutMs,\n\t\t);\n\t\treturn waitForNodeUpdate(driver, msg, timeout);\n\t}\n\n\treturn result;\n};\n\n/** A simple (internal) generator that simply sends a command, and optionally returns the response command */\nasync function* sendCommandGenerator<\n\tTResponse extends CommandClass = CommandClass,\n>(\n\tdriver: Driver,\n\tctx: CCEncodingContext,\n\tcommand: CommandClass,\n\tonMessageSent: (msg: Message, result: Message | undefined) => void,\n\toptions?: SendCommandOptions,\n) {\n\tconst msg = driver.createSendDataMessage(command, options);\n\n\tconst resp = yield* maybeTransportServiceGenerator(\n\t\tdriver,\n\t\tctx,\n\t\tmsg,\n\t\tonMessageSent,\n\t);\n\tif (resp && containsCC(resp)) {\n\t\tdriver.unwrapCommands(resp);\n\t\treturn resp.command as TResponse;\n\t}\n}\n\n/** A message generator for security encapsulated messages (S0) */\nexport const secureMessageGeneratorS0: MessageGeneratorImplementation<\n\tSendDataMessage & ContainsCC\n> = async function*(driver, ctx, msg, onMessageSent) {\n\t/*if (!isSendData(msg)) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"Cannot use the S0 message generator for a command that's not a SendData message!\",\n\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t);\n\t\t} else*/ if (typeof msg.command.nodeId !== \"number\") {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S0 message generator for multicast commands!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t} else if (!(msg.command instanceof SecurityCCCommandEncapsulation)) {\n\t\tthrow new ZWaveError(\n\t\t\t\"The S0 message generator can only be used for Security S0 command encapsulation!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\t// Step 1: Acquire a nonce\n\tconst secMan = driver.securityManager!;\n\tconst nodeId = msg.command.nodeId;\n\tlet additionalTimeoutMs: number | undefined;\n\n\t// Try to get a free nonce before requesting a new one\n\tlet nonce: Uint8Array | undefined = secMan.getFreeNonce(nodeId);\n\tif (!nonce) {\n\t\t// No free nonce, request a new one\n\t\tconst cc = new SecurityCCNonceGet({\n\t\t\tnodeId: nodeId,\n\t\t\tendpointIndex: msg.command.endpointIndex,\n\t\t});\n\t\tconst nonceResp = yield* sendCommandGenerator<\n\t\t\tSecurityCCNonceReport\n\t\t>(\n\t\t\tdriver,\n\t\t\tctx,\n\t\t\tcc,\n\t\t\t(msg, result) => {\n\t\t\t\tadditionalTimeoutMs = Math.ceil(msg.rtt! / 1e6);\n\t\t\t\tonMessageSent(msg, result);\n\t\t\t},\n\t\t\t{\n\t\t\t\t// Only try getting a nonce once\n\t\t\t\tmaxSendAttempts: 1,\n\t\t\t},\n\t\t);\n\t\tif (!nonceResp) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"No nonce received from the node, cannot send secure command!\",\n\t\t\t\tZWaveErrorCodes.SecurityCC_NoNonce,\n\t\t\t);\n\t\t}\n\t\tnonce = nonceResp.nonce;\n\t}\n\tmsg.command.nonce = nonce;\n\n\t// Now send the actual secure command\n\treturn yield* simpleMessageGenerator(\n\t\tdriver,\n\t\tctx,\n\t\tmsg,\n\t\tonMessageSent,\n\t\tadditionalTimeoutMs,\n\t);\n};\n\n/** A message generator for security encapsulated messages (S2) */\nexport const secureMessageGeneratorS2: MessageGeneratorImplementation<\n\tSendDataMessage & ContainsCC\n> = async function*(driver, ctx, msg, onMessageSent) {\n\tif (!isSendData(msg) || !containsCC(msg)) {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S2 message generator for a command that's not a SendData message!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t} else if (typeof msg.command.nodeId !== \"number\") {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S2 message generator for multicast commands!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t} else if (!(msg.command instanceof Security2CCMessageEncapsulation)) {\n\t\tthrow new ZWaveError(\n\t\t\t\"The S2 message generator can only be used for Security S2 command encapsulation!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\tconst nodeId = msg.command.nodeId;\n\tconst secMan = driver.getSecurityManager2(nodeId)!;\n\tconst spanState = secMan.getSPANState(nodeId);\n\tlet additionalTimeoutMs: number | undefined;\n\n\t// We need a new nonce when there is no shared SPAN state, or the SPAN state is for a lower security class\n\t// than the command we want to send\n\tconst expectedSecurityClass = msg.command.securityClass\n\t\t?? driver.getHighestSecurityClass(nodeId);\n\n\tif (\n\t\tspanState.type === SPANState.None\n\t\t|| spanState.type === SPANState.LocalEI\n\t\t|| (spanState.type === SPANState.SPAN\n\t\t\t&& spanState.securityClass !== SecurityClass.Temporary\n\t\t\t&& spanState.securityClass !== expectedSecurityClass)\n\t) {\n\t\t// Request a new nonce\n\n\t\t// No free nonce, request a new one\n\t\tconst cc = new Security2CCNonceGet({\n\t\t\tnodeId: nodeId,\n\t\t\tendpointIndex: msg.command.endpointIndex,\n\t\t});\n\t\tconst nonceResp = yield* sendCommandGenerator<\n\t\t\tSecurity2CCNonceReport\n\t\t>(\n\t\t\tdriver,\n\t\t\tctx,\n\t\t\tcc,\n\t\t\t(msg, result) => {\n\t\t\t\tadditionalTimeoutMs = Math.ceil(msg.rtt! / 1e6);\n\t\t\t\tonMessageSent(msg, result);\n\t\t\t},\n\t\t\t{\n\t\t\t\t// Only try getting a nonce once\n\t\t\t\tmaxSendAttempts: 1,\n\t\t\t},\n\t\t);\n\t\tif (!nonceResp) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"No nonce received from the node, cannot send secure command!\",\n\t\t\t\tZWaveErrorCodes.Security2CC_NoSPAN,\n\t\t\t);\n\t\t}\n\n\t\t// Storing the nonce is not necessary, this will be done automatically when the nonce is received\n\t}\n\n\t// Now send the actual secure command\n\tlet response = yield* maybeTransportServiceGenerator(\n\t\tdriver,\n\t\tctx,\n\t\tmsg,\n\t\tonMessageSent,\n\t\tadditionalTimeoutMs,\n\t);\n\n\t// If we want to make sure that a node understood a SET-type S2-encapsulated message, we either need to use\n\t// Supervision and wait for the Supervision Report (handled by the simpleMessageGenerator), or we need to add a\n\t// short delay between commands and wait if a NonceReport is received.\n\t// However, in situations where timing is critical (e.g. S2 bootstrapping), verifyDelivery is set to false, and we don't do this.\n\tlet nonceReport: Security2CCNonceReport | undefined;\n\tif (\n\t\tisTransmitReport(response)\n\t\t&& msg.command.verifyDelivery\n\t\t&& !msg.command.expectsCCResponse()\n\t\t&& !msg.command.getEncapsulatedCC(\n\t\t\tCommandClasses.Supervision,\n\t\t\tSupervisionCommand.Get,\n\t\t)\n\t) {\n\t\tnonceReport = await driver\n\t\t\t.waitForCommand<Security2CCNonceReport>(\n\t\t\t\t(cc) =>\n\t\t\t\t\tcc.nodeId === nodeId\n\t\t\t\t\t&& cc instanceof Security2CCNonceReport,\n\t\t\t\t500,\n\t\t\t)\n\t\t\t.catch(() => undefined);\n\t} else if (\n\t\tcontainsCC(response)\n\t\t&& response.command instanceof Security2CCNonceReport\n\t) {\n\t\tnonceReport = response.command;\n\t}\n\n\tif (nonceReport) {\n\t\tif (nonceReport.SOS && nonceReport.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(nodeId, nonceReport.receiverEI);\n\t\t}\n\t\tif (nonceReport.MOS) {\n\t\t\tconst multicastGroupId = msg.command.getMulticastGroupId();\n\t\t\tif (multicastGroupId != undefined) {\n\t\t\t\t// The node couldn't decrypt the previous S2 multicast. Tell it the MPAN (again)\n\t\t\t\tconst mpan = secMan.getInnerMPANState(multicastGroupId);\n\t\t\t\tif (mpan) {\n\t\t\t\t\t// Replace the MGRP extension with an MPAN extension\n\t\t\t\t\tmsg.command.extensions = msg.command.extensions.filter(\n\t\t\t\t\t\t(e) => !(e instanceof MGRPExtension),\n\t\t\t\t\t);\n\t\t\t\t\tmsg.command.extensions.push(\n\t\t\t\t\t\tnew MPANExtension({\n\t\t\t\t\t\t\tgroupId: multicastGroupId,\n\t\t\t\t\t\t\tinnerMPANState: mpan,\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\tdriver.controllerLog.logNode(nodeId, {\n\t\t\tmessage:\n\t\t\t\t`failed to decode the message, retrying with SPAN extension...`,\n\t\t\tdirection: \"none\",\n\t\t});\n\n\t\t// Send the message again\n\t\tmsg.prepareRetransmission();\n\t\tresponse = yield* maybeTransportServiceGenerator(\n\t\t\tdriver,\n\t\t\tctx,\n\t\t\tmsg,\n\t\t\tonMessageSent,\n\t\t\tadditionalTimeoutMs,\n\t\t);\n\n\t\tif (\n\t\t\tcontainsCC(response)\n\t\t\t&& response.command instanceof Security2CCNonceReport\n\t\t) {\n\t\t\t// No dice\n\t\t\tdriver.controllerLog.logNode(nodeId, {\n\t\t\t\tmessage:\n\t\t\t\t\t`failed to decode the message after re-transmission with SPAN extension, dropping the message.`,\n\t\t\t\tdirection: \"none\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t});\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The node failed to decode the message.\",\n\t\t\t\tZWaveErrorCodes.Security2CC_CannotDecode,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn response;\n};\n\n/** A message generator for security encapsulated messages (S2 Multicast) */\nexport const secureMessageGeneratorS2Multicast: MessageGeneratorImplementation<\n\tSendDataMessage & ContainsCC\n> = async function*(driver, ctx, msg, onMessageSent) {\n\tif (!isSendData(msg) || !containsCC(msg)) {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S2 multicast message generator for a command that's not a SendData message!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t} else if (msg.command.isSinglecast()) {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S2 multicast message generator for singlecast commands!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t} else if (!(msg.command instanceof Security2CCMessageEncapsulation)) {\n\t\tthrow new ZWaveError(\n\t\t\t\"The S2 multicast message generator can only be used for Security S2 command encapsulation!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\tconst groupId = msg.command.getMulticastGroupId();\n\tif (groupId == undefined) {\n\t\tthrow new ZWaveError(\n\t\t\t\"Cannot use the S2 multicast message generator without a multicast group ID!\",\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\tconst secMan = driver.getSecurityManager2(msg.command.nodeId)!;\n\tconst group = secMan.getMulticastGroup(groupId);\n\tif (!group) {\n\t\tthrow new ZWaveError(\n\t\t\t`Multicast group ${groupId} does not exist!`,\n\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t);\n\t}\n\n\t// Send the multicast command. We remember the transmit report and treat it as the result of the multicast command\n\tconst response = yield* simpleMessageGenerator(\n\t\tdriver,\n\t\tctx,\n\t\tmsg,\n\t\tonMessageSent,\n\t);\n\n\t// If a node in the group is out of sync, we need to transfer the MPAN state we're going to use for the next command.\n\t// Therefore increment the MPAN state now and not after the followups like the specs mention\n\tsecMan.tryIncrementMPAN(groupId);\n\n\t// Unwrap the command again, so we can make the following encapsulation depend on the target node\n\tdriver.unwrapCommands(msg);\n\tconst command = msg.command;\n\t// Remember the original encapsulation flags\n\tconst encapsulationFlags = command.encapsulationFlags;\n\n\t// In case someone sneaked a node ID into the group multiple times, remove duplicates for the singlecast followups\n\t// Otherwise, the node will increase its MPAN multiple times, going out of sync.\n\tconst distinctNodeIDs = [...new Set(group.nodeIDs)];\n\n\tconst supervisionResults: (SupervisionResult | undefined)[] = [];\n\n\t// Now do singlecast followups with every node in the group\n\tfor (const nodeId of distinctNodeIDs) {\n\t\t// Point the CC at the target node\n\t\t(command.nodeId as number) = nodeId;\n\t\t// Figure out if supervision should be used\n\t\tcommand.encapsulationFlags = encapsulationFlags;\n\t\tcommand.toggleEncapsulationFlag(\n\t\t\tEncapsulationFlags.Supervision,\n\t\t\tSupervisionCC.mayUseSupervision(driver, command),\n\t\t);\n\n\t\tconst scMsg = driver.createSendDataMessage(command, {\n\t\t\ttransmitOptions: msg.transmitOptions,\n\t\t\tmaxSendAttempts: msg.maxSendAttempts,\n\t\t});\n\t\t// The outermost command is a Security2CCMessageEncapsulation, we need to set the MGRP extension on this again\n\t\t(scMsg.command as Security2CCMessageEncapsulation).extensions.push(\n\t\t\tnew MGRPExtension({ groupId }),\n\t\t);\n\n\t\t// Reuse the S2 singlecast message generator for sending this new message\n\t\ttry {\n\t\t\tconst scResponse = yield* secureMessageGeneratorS2(\n\t\t\t\tdriver,\n\t\t\t\tctx,\n\t\t\t\tscMsg,\n\t\t\t\tonMessageSent,\n\t\t\t);\n\t\t\tif (\n\t\t\t\tcontainsCC(scResponse)\n\t\t\t\t&& scResponse.command\n\t\t\t\t\tinstanceof Security2CCMessageEncapsulation\n\t\t\t\t&& scResponse.command.hasMOSExtension()\n\t\t\t) {\n\t\t\t\t// The node understood the S2 singlecast followup, but told us that its MPAN is out of sync\n\n\t\t\t\tconst innerMPANState = secMan.getInnerMPANState(groupId);\n\t\t\t\t// This should always be defined, but better not throw unnecessarily here\n\t\t\t\tif (innerMPANState) {\n\t\t\t\t\tconst cc = new Security2CCMessageEncapsulation({\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\textensions: [\n\t\t\t\t\t\t\tnew MPANExtension({\n\t\t\t\t\t\t\t\tgroupId,\n\t\t\t\t\t\t\t\tinnerMPANState,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\n\t\t\t\t\t// Send it the MPAN\n\t\t\t\t\tyield* sendCommandGenerator(\n\t\t\t\t\t\tdriver,\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\tcc,\n\t\t\t\t\t\tonMessageSent,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Seems we need these options or some nodes won't accept the nonce\n\t\t\t\t\t\t\ttransmitOptions: TransmitOptions.ACK\n\t\t\t\t\t\t\t\t| TransmitOptions.AutoRoute,\n\t\t\t\t\t\t\t// Only try sending a nonce once\n\t\t\t\t\t\t\tmaxSendAttempts: 1,\n\t\t\t\t\t\t\t// Nonce requests must be handled immediately\n\t\t\t\t\t\t\tpriority: MessagePriority.Immediate,\n\t\t\t\t\t\t\t// We don't want failures causing us to treat the node as asleep or dead\n\t\t\t\t\t\t\tchangeNodeStatusOnMissingACK: false,\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// Collect supervision results if possible\n\t\t\tif (containsCC(scResponse)) {\n\t\t\t\tconst supervisionReport = scResponse.command\n\t\t\t\t\t.getEncapsulatedCC(\n\t\t\t\t\t\tCommandClasses.Supervision,\n\t\t\t\t\t\tSupervisionCommand.Report,\n\t\t\t\t\t) as SupervisionCCReport | undefined;\n\n\t\t\t\tsupervisionResults.push(\n\t\t\t\t\tsupervisionReport?.toSupervisionResult(),\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tdriver.driverLog.print(getErrorMessage(e), \"error\");\n\t\t\t// TODO: Figure out how we got here, and what to do now.\n\t\t\t// In any case, keep going with the next nodes\n\t\t\t// Report that there was a failure, so the application can show it\n\t\t\tsupervisionResults.push({\n\t\t\t\tstatus: SupervisionStatus.Fail,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst finalSupervisionResult = mergeSupervisionResults(\n\t\tsupervisionResults,\n\t);\n\tif (finalSupervisionResult) {\n\t\t// We can return return information about the success of this multicast - so we should\n\t\t// TODO: Not sure if we need to \"wrap\" the response for something. For now, try faking it\n\t\tconst cc = new SupervisionCCReport({\n\t\t\tnodeId: NODE_ID_BROADCAST,\n\t\t\tsessionId: 0, // fake\n\t\t\tmoreUpdatesFollow: false, // fake\n\t\t\t...(finalSupervisionResult as any),\n\t\t});\n\t\tconst ret = new (driver.getSendDataSinglecastConstructor())({\n\t\t\tsourceNodeId: driver.ownNodeId,\n\t\t\tcommand: cc,\n\t\t});\n\t\treturn ret;\n\t} else {\n\t\treturn response;\n\t}\n};\n\nexport function createMessageGenerator<TResponse extends Message = Message>(\n\tdriver: Driver,\n\tctx: CCEncodingContext,\n\tmsg: Message,\n\tonMessageSent: (msg: Message, result: Message | undefined) => void,\n): {\n\tgenerator: MessageGenerator;\n\tresultPromise: DeferredPromise<TResponse>;\n} {\n\tconst resultPromise = createDeferredPromise<TResponse>();\n\n\tconst generator: MessageGenerator = {\n\t\tparent: undefined as any, // The transaction will set this field on creation\n\t\tcurrent: undefined,\n\t\tself: undefined,\n\t\treset: () => {\n\t\t\tgenerator.current = undefined;\n\t\t\tgenerator.self = undefined;\n\t\t},\n\t\tstart: () => {\n\t\t\tasync function* gen() {\n\t\t\t\t// Determine which message generator implementation should be used\n\t\t\t\tlet implementation: MessageGeneratorImplementation<Message> =\n\t\t\t\t\tsimpleMessageGenerator;\n\t\t\t\tif (isSendData(msg)) {\n\t\t\t\t\tif (!containsCC(msg)) {\n\t\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\t\"Cannot create a message generator for a message that doesn't contain a command class\",\n\t\t\t\t\t\t\tZWaveErrorCodes.Argument_Invalid,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\tmsg.command instanceof Security2CCMessageEncapsulation\n\t\t\t\t\t) {\n\t\t\t\t\t\timplementation = (\n\t\t\t\t\t\t\tmsg.command.isSinglecast()\n\t\t\t\t\t\t\t\t? secureMessageGeneratorS2\n\t\t\t\t\t\t\t\t: secureMessageGeneratorS2Multicast\n\t\t\t\t\t\t) as MessageGeneratorImplementation<Message>;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\tmsg.command instanceof SecurityCCCommandEncapsulation\n\t\t\t\t\t) {\n\t\t\t\t\t\timplementation =\n\t\t\t\t\t\t\tsecureMessageGeneratorS0 as MessageGeneratorImplementation<\n\t\t\t\t\t\t\t\tMessage\n\t\t\t\t\t\t\t>;\n\t\t\t\t\t} else if (msg.command.isSinglecast()) {\n\t\t\t\t\t\timplementation =\n\t\t\t\t\t\t\tmaybeTransportServiceGenerator as MessageGeneratorImplementation<\n\t\t\t\t\t\t\t\tMessage\n\t\t\t\t\t\t\t>;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Step through the generator so we can easily cancel it and don't\n\t\t\t\t// accidentally forget to unset this.current at the end\n\t\t\t\tconst gen = implementation(driver, ctx, msg, onMessageSent);\n\t\t\t\tlet sendResult: Message | undefined;\n\t\t\t\tlet result: Message | undefined;\n\t\t\t\twhile (true) {\n\t\t\t\t\t// This call passes the previous send result (if it exists already) to the generator and saves the\n\t\t\t\t\t// generated or returned message in `value`. When `done` is true, `value` contains the returned result of the message generator\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { value, done } = await gen.next(sendResult!);\n\t\t\t\t\t\tif (done) {\n\t\t\t\t\t\t\tresult = value;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Pass the generated message to the driver and remember the result for the next iteration\n\t\t\t\t\t\tgenerator.current = value;\n\t\t\t\t\t\tsendResult = yield generator.current;\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (e instanceof Error) {\n\t\t\t\t\t\t\t// There was an actual error, reject the transaction\n\t\t\t\t\t\t\tresultPromise.reject(e);\n\t\t\t\t\t\t} else if (isTransmitReport(e) && !e.isOK()) {\n\t\t\t\t\t\t\t// The generator was prematurely ended by throwing a NOK transmit report.\n\t\t\t\t\t\t\t// The driver may want to retry it, so reset the generator\n\t\t\t\t\t\t\tgenerator.reset();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// The generator was prematurely ended by throwing a Message\n\t\t\t\t\t\t\tresultPromise.resolve(e as TResponse);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresultPromise.resolve(result as TResponse);\n\t\t\t\tgenerator.reset();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tgenerator.self = gen();\n\t\t\treturn generator.self;\n\t\t},\n\t};\n\treturn { resultPromise, generator };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_serialapi", "fail", "cc", "msg", "gen"]
|
|
7
7
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { JsonlDB } from "@alcalzone/jsonl-db";
|
|
2
|
-
import { type CommandClasses, SecurityClass } from "@zwave-js/core";
|
|
3
|
-
import type { FileSystem } from "@zwave-js/host";
|
|
2
|
+
import { type CommandClasses, type FileSystem, SecurityClass } from "@zwave-js/core";
|
|
4
3
|
/**
|
|
5
4
|
* Defines the keys that are used to store certain properties in the network cache.
|
|
6
5
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/driver/NetworkCache.ts"],
|
|
4
|
-
"sourcesContent": ["import type { JsonlDB } from \"@alcalzone/jsonl-db\";\nimport { type AssociationAddress } from \"@zwave-js/cc\";\nimport {\n\ttype CommandClasses,\n\tNodeType,\n\tProtocols,\n\tSecurityClass,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tdskFromString,\n\tdskToString,\n\tsecurityClassOrder,\n} from \"@zwave-js/core\";\nimport type { FileSystem } from \"@zwave-js/host\";\nimport { Bytes, getEnumMemberName, num2hex, pickDeep } from \"@zwave-js/shared\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport path from \"node:path\";\nimport {\n\tProvisioningEntryStatus,\n\ttype SmartStartProvisioningEntry,\n} from \"../controller/Inclusion.js\";\nimport { DeviceClass } from \"../node/DeviceClass.js\";\nimport { InterviewStage } from \"../node/_Types.js\";\n\n/**\n * Defines the keys that are used to store certain properties in the network cache.\n */\nexport const cacheKeys = {\n\tcontroller: {\n\t\tprovisioningList: \"controller.provisioningList\",\n\t\tassociations: (groupId: number) => `controller.associations.${groupId}`,\n\t\tsecurityKeys: (secClass: SecurityClass) =>\n\t\t\t`controller.securityKeys.${\n\t\t\t\tgetEnumMemberName(\n\t\t\t\t\tSecurityClass,\n\t\t\t\t\tsecClass,\n\t\t\t\t)\n\t\t\t}`,\n\t\tsecurityKeysLongRange: (secClass: SecurityClass) =>\n\t\t\t`controller.securityKeyLongRange.${\n\t\t\t\tgetEnumMemberName(\n\t\t\t\t\tSecurityClass,\n\t\t\t\t\tsecClass,\n\t\t\t\t)\n\t\t\t}`,\n\t\tprivateKey: \"controller.privateKey\",\n\t},\n\t// TODO: somehow these functions should be combined with the pattern matching below\n\tnode: (nodeId: number) => {\n\t\tconst nodeBaseKey = `node.${nodeId}.`;\n\t\treturn {\n\t\t\t_baseKey: nodeBaseKey,\n\t\t\t_securityClassBaseKey: `${nodeBaseKey}securityClasses`,\n\t\t\t_priorityReturnRouteBaseKey: `${nodeBaseKey}priorityReturnRoute`,\n\t\t\tinterviewStage: `${nodeBaseKey}interviewStage`,\n\t\t\tdeviceClass: `${nodeBaseKey}deviceClass`,\n\t\t\tisListening: `${nodeBaseKey}isListening`,\n\t\t\tisFrequentListening: `${nodeBaseKey}isFrequentListening`,\n\t\t\tisRouting: `${nodeBaseKey}isRouting`,\n\t\t\tsupportedDataRates: `${nodeBaseKey}supportedDataRates`,\n\t\t\tprotocolVersion: `${nodeBaseKey}protocolVersion`,\n\t\t\tnodeType: `${nodeBaseKey}nodeType`,\n\t\t\tsupportsSecurity: `${nodeBaseKey}supportsSecurity`,\n\t\t\tsupportsBeaming: `${nodeBaseKey}supportsBeaming`,\n\t\t\tsecurityClass: (secClass: SecurityClass) =>\n\t\t\t\t`${nodeBaseKey}securityClasses.${\n\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\tSecurityClass,\n\t\t\t\t\t\tsecClass,\n\t\t\t\t\t)\n\t\t\t\t}`,\n\t\t\tdsk: `${nodeBaseKey}dsk`,\n\t\t\tendpoint: (index: number) => {\n\t\t\t\tconst endpointBaseKey = `${nodeBaseKey}endpoint.${index}.`;\n\t\t\t\tconst ccBaseKey = `${endpointBaseKey}commandClass.`;\n\t\t\t\treturn {\n\t\t\t\t\t_baseKey: endpointBaseKey,\n\t\t\t\t\t_ccBaseKey: ccBaseKey,\n\t\t\t\t\tcommandClass: (ccId: CommandClasses) => {\n\t\t\t\t\t\tconst ccAsHex = num2hex(ccId);\n\t\t\t\t\t\treturn `${ccBaseKey}${ccAsHex}`;\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t},\n\t\t\thasSUCReturnRoute: `${nodeBaseKey}hasSUCReturnRoute`,\n\t\t\tpriorityReturnRoute: (destinationNodeId: number) =>\n\t\t\t\t`${nodeBaseKey}priorityReturnRoute.${destinationNodeId}`,\n\t\t\tprioritySUCReturnRoute: `${nodeBaseKey}priorityReturnRoute.SUC`,\n\t\t\tcustomReturnRoutes: (destinationNodeId: number) =>\n\t\t\t\t`${nodeBaseKey}customReturnRoutes.${destinationNodeId}`,\n\t\t\tcustomSUCReturnRoutes: `${nodeBaseKey}customReturnRoutes.SUC`,\n\t\t\tdefaultTransitionDuration:\n\t\t\t\t`${nodeBaseKey}defaultTransitionDuration`,\n\t\t\tdefaultVolume: `${nodeBaseKey}defaultVolume`,\n\t\t\tlastSeen: `${nodeBaseKey}lastSeen`,\n\t\t\tdeviceConfigHash: `${nodeBaseKey}deviceConfigHash`,\n\t\t};\n\t},\n} as const;\n\nexport const cacheKeyUtils = {\n\tnodeIdFromKey: (key: string): number | undefined => {\n\t\tconst match = /^node\\.(?<nodeId>\\d+)\\./.exec(key);\n\t\tif (match) {\n\t\t\treturn parseInt(match.groups!.nodeId, 10);\n\t\t}\n\t},\n\tnodePropertyFromKey: (key: string): string | undefined => {\n\t\tconst match = /^node\\.\\d+\\.(?<property>[^\\.]+)$/.exec(key);\n\t\treturn match?.groups?.property;\n\t},\n\tisEndpointKey: (key: string): boolean => {\n\t\treturn /endpoints\\.(?<index>\\d+)$/.test(key);\n\t},\n\tendpointIndexFromKey: (key: string): number | undefined => {\n\t\tconst match = /endpoints\\.(?<index>\\d+)$/.exec(key);\n\t\tif (match) {\n\t\t\treturn parseInt(match.groups!.index, 10);\n\t\t}\n\t},\n\tdestinationFromPriorityReturnRouteKey: (\n\t\tkey: string,\n\t): number | undefined => {\n\t\tconst match = /\\.priorityReturnRoute\\.(?<nodeId>\\d+)$/.exec(key);\n\t\tif (match) {\n\t\t\treturn parseInt(match.groups!.nodeId, 10);\n\t\t}\n\t},\n} as const;\n\nfunction tryParseInterviewStage(value: unknown): InterviewStage | undefined {\n\tif (\n\t\t(typeof value === \"string\" || typeof value === \"number\")\n\t\t&& value in InterviewStage\n\t) {\n\t\treturn typeof value === \"number\"\n\t\t\t? value\n\t\t\t: (InterviewStage as any)[value];\n\t}\n}\n\nfunction tryParseDeviceClass(value: unknown): DeviceClass | undefined {\n\tif (isObject(value)) {\n\t\tconst { basic, generic, specific } = value;\n\t\tif (\n\t\t\ttypeof basic === \"number\"\n\t\t\t&& typeof generic === \"number\"\n\t\t\t&& typeof specific === \"number\"\n\t\t) {\n\t\t\treturn new DeviceClass(\n\t\t\t\tbasic,\n\t\t\t\tgeneric,\n\t\t\t\tspecific,\n\t\t\t);\n\t\t}\n\t}\n}\n\nfunction tryParseSecurityClasses(\n\tvalue: unknown,\n): Map<SecurityClass, boolean> | undefined {\n\tif (isObject(value)) {\n\t\tconst ret = new Map<SecurityClass, boolean>();\n\t\tfor (const [key, val] of Object.entries(value)) {\n\t\t\tif (\n\t\t\t\tkey in SecurityClass\n\t\t\t\t&& typeof (SecurityClass as any)[key] === \"number\"\n\t\t\t\t&& typeof val === \"boolean\"\n\t\t\t) {\n\t\t\t\tret.set((SecurityClass as any)[key] as SecurityClass, val);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n}\n\nfunction tryParseNodeType(value: unknown): NodeType | undefined {\n\tif (typeof value === \"string\" && value in NodeType) {\n\t\treturn (NodeType as any)[value];\n\t}\n}\n\nfunction tryParseProvisioningList(\n\tvalue: unknown,\n): SmartStartProvisioningEntry[] | undefined {\n\tconst ret: SmartStartProvisioningEntry[] = [];\n\tif (!isArray(value)) return;\n\tfor (const entry of value) {\n\t\tif (\n\t\t\tisObject(entry)\n\t\t\t&& typeof entry.dsk === \"string\"\n\t\t\t&& isArray(entry.securityClasses)\n\t\t\t// securityClasses are stored as strings, not the enum values\n\t\t\t&& entry.securityClasses.every((s) => isSerializedSecurityClass(s))\n\t\t\t&& (entry.requestedSecurityClasses == undefined\n\t\t\t\t|| (isArray(entry.requestedSecurityClasses)\n\t\t\t\t\t&& entry.requestedSecurityClasses.every((s) =>\n\t\t\t\t\t\tisSerializedSecurityClass(s)\n\t\t\t\t\t)))\n\t\t\t// protocol and supportedProtocols are (supposed to be) stored as strings, not the enum values\n\t\t\t&& (entry.protocol == undefined\n\t\t\t\t|| isSerializedProtocol(entry.protocol))\n\t\t\t&& (entry.supportedProtocols == undefined || (\n\t\t\t\tisArray(entry.supportedProtocols)\n\t\t\t\t&& entry.supportedProtocols.every((s) =>\n\t\t\t\t\tisSerializedProtocol(s)\n\t\t\t\t)\n\t\t\t))\n\t\t\t&& (entry.status == undefined\n\t\t\t\t|| isSerializedProvisioningEntryStatus(entry.status))\n\t\t) {\n\t\t\t// This is at least a PlannedProvisioningEntry, maybe it is an IncludedProvisioningEntry\n\t\t\tif (\"nodeId\" in entry && typeof entry.nodeId !== \"number\") {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst parsed = {\n\t\t\t\t...entry,\n\t\t\t} as unknown as SmartStartProvisioningEntry;\n\t\t\tparsed.securityClasses = entry.securityClasses\n\t\t\t\t.map((s) => tryParseSerializedSecurityClass(s))\n\t\t\t\t.filter((s) => s !== undefined);\n\t\t\tif (entry.requestedSecurityClasses) {\n\t\t\t\tparsed.requestedSecurityClasses = (\n\t\t\t\t\tentry.requestedSecurityClasses as any[]\n\t\t\t\t)\n\t\t\t\t\t.map((s) => tryParseSerializedSecurityClass(s))\n\t\t\t\t\t.filter((s) => s !== undefined);\n\t\t\t}\n\t\t\tif (entry.status != undefined) {\n\t\t\t\tparsed.status = ProvisioningEntryStatus[\n\t\t\t\t\tentry.status as any\n\t\t\t\t] as any as ProvisioningEntryStatus;\n\t\t\t}\n\t\t\tif (entry.protocol != undefined) {\n\t\t\t\tparsed.protocol = tryParseSerializedProtocol(entry.protocol);\n\t\t\t}\n\t\t\tif (entry.supportedProtocols) {\n\t\t\t\tparsed.supportedProtocols = (\n\t\t\t\t\tentry.supportedProtocols as any[]\n\t\t\t\t)\n\t\t\t\t\t.map((s) => tryParseSerializedProtocol(s))\n\t\t\t\t\t.filter((s) => s !== undefined);\n\t\t\t}\n\t\t\tret.push(parsed);\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\t}\n\treturn ret;\n}\n\nfunction isSerializedSecurityClass(value: unknown): boolean {\n\t// There was an error in previous iterations of the migration code, so we\n\t// now have to deal with the following variants:\n\t// 1. plain numbers representing a valid Security Class: 1\n\t// 2. strings representing a valid Security Class: \"S2_Unauthenticated\"\n\t// 3. strings represending a mis-formatted Security Class: \"unknown (0xS2_Unauthenticated)\"\n\tif (typeof value === \"number\" && value in SecurityClass) return true;\n\tif (typeof value === \"string\") {\n\t\tif (value.startsWith(\"unknown (0x\") && value.endsWith(\")\")) {\n\t\t\tvalue = value.slice(11, -1);\n\t\t}\n\t\tif (\n\t\t\t(value as any) in SecurityClass\n\t\t\t&& typeof SecurityClass[value as any] === \"number\"\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nfunction tryParseSerializedSecurityClass(\n\tvalue: unknown,\n): SecurityClass | undefined {\n\t// There was an error in previous iterations of the migration code, so we\n\t// now have to deal with the following variants:\n\t// 1. plain numbers representing a valid Security Class: 1\n\t// 2. strings representing a valid Security Class: \"S2_Unauthenticated\"\n\t// 3. strings represending a mis-formatted Security Class: \"unknown (0xS2_Unauthenticated)\"\n\n\tif (typeof value === \"number\" && value in SecurityClass) return value;\n\tif (typeof value === \"string\") {\n\t\tif (value.startsWith(\"unknown (0x\") && value.endsWith(\")\")) {\n\t\t\tvalue = value.slice(11, -1);\n\t\t}\n\t\tif (\n\t\t\t(value as any) in SecurityClass\n\t\t\t&& typeof SecurityClass[value as any] === \"number\"\n\t\t) {\n\t\t\treturn (SecurityClass as any)[value as any];\n\t\t}\n\t}\n}\n\nfunction isSerializedProvisioningEntryStatus(\n\ts: unknown,\n): s is keyof typeof ProvisioningEntryStatus {\n\treturn (\n\t\ttypeof s === \"string\"\n\t\t&& s in ProvisioningEntryStatus\n\t\t&& typeof ProvisioningEntryStatus[s as any] === \"number\"\n\t);\n}\n\nfunction isSerializedProtocol(\n\ts: unknown,\n): boolean {\n\t// The list of supported protocols has been around since before we started\n\t// saving them as their stringified variant, so we\n\t// now have to deal with the following variants:\n\t// 1. plain numbers representing a valid Protocol: 0\n\t// 2. strings representing a valid Protocols: \"ZWave\"\n\tif (typeof s === \"number\" && s in Protocols) return true;\n\treturn (\n\t\ttypeof s === \"string\"\n\t\t&& s in Protocols\n\t\t&& typeof Protocols[s as any] === \"number\"\n\t);\n}\n\nfunction tryParseSerializedProtocol(\n\tvalue: unknown,\n): Protocols | undefined {\n\t// The list of supported protocols has been around since before we started\n\t// saving them as their stringified variant, so we\n\t// now have to deal with the following variants:\n\t// 1. plain numbers representing a valid Protocol: 0\n\t// 2. strings representing a valid Protocols: \"ZWave\"\n\n\tif (typeof value === \"number\" && value in Protocols) return value;\n\tif (typeof value === \"string\") {\n\t\tif (\n\t\t\t(value as any) in Protocols\n\t\t\t&& typeof Protocols[value as any] === \"number\"\n\t\t) {\n\t\t\treturn (Protocols as any)[value as any];\n\t\t}\n\t}\n}\n\nfunction tryParseDate(value: unknown): Date | undefined {\n\t// Dates are stored as timestamps\n\tif (typeof value === \"number\") {\n\t\tconst ret = new Date(value);\n\t\tif (!isNaN(ret.getTime())) return ret;\n\t}\n}\n\nfunction tryParseAssociationAddress(\n\tvalue: unknown,\n): AssociationAddress | undefined {\n\tif (isObject(value)) {\n\t\tconst { nodeId, endpoint } = value;\n\t\tif (typeof nodeId !== \"number\") return;\n\t\tif (endpoint !== undefined && typeof endpoint !== \"number\") return;\n\n\t\treturn { nodeId, endpoint };\n\t}\n}\n\nfunction tryParseBuffer(\n\tvalue: unknown,\n): Uint8Array | undefined {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn Bytes.from(value, \"hex\");\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n}\n\nexport function deserializeNetworkCacheValue(\n\tkey: string,\n\tvalue: unknown,\n): unknown {\n\tfunction ensureType<T extends \"boolean\" | \"number\" | \"string\">(\n\t\tvalue: any,\n\t\ttype: T,\n\t): T | boolean {\n\t\tif (typeof value === type) return value as T;\n\t\tthrow new ZWaveError(\n\t\t\t`Incorrect type ${typeof value} for property \"${key}\"`,\n\t\t\tZWaveErrorCodes.Driver_InvalidCache,\n\t\t);\n\t}\n\n\tfunction fail(): never {\n\t\tthrow new ZWaveError(\n\t\t\t`Failed to deserialize property \"${key}\"`,\n\t\t\tZWaveErrorCodes.Driver_InvalidCache,\n\t\t);\n\t}\n\n\tswitch (cacheKeyUtils.nodePropertyFromKey(key)) {\n\t\tcase \"interviewStage\": {\n\t\t\tvalue = tryParseInterviewStage(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\t\tcase \"deviceClass\": {\n\t\t\tvalue = tryParseDeviceClass(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\t\tcase \"isListening\":\n\t\tcase \"isRouting\":\n\t\tcase \"hasSUCReturnRoute\":\n\t\t\treturn ensureType(value, \"boolean\");\n\n\t\tcase \"isFrequentListening\": {\n\t\t\tswitch (value) {\n\t\t\t\tcase \"1000ms\":\n\t\t\t\tcase true:\n\t\t\t\t\treturn \"1000ms\";\n\t\t\t\tcase \"250ms\":\n\t\t\t\t\treturn \"250ms\";\n\t\t\t\tcase false:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfail();\n\t\t}\n\n\t\tcase \"dsk\": {\n\t\t\tif (typeof value === \"string\") {\n\t\t\t\treturn dskFromString(value);\n\t\t\t}\n\t\t\tfail();\n\t\t}\n\n\t\tcase \"supportsSecurity\":\n\t\t\treturn ensureType(value, \"boolean\");\n\t\tcase \"supportsBeaming\":\n\t\t\ttry {\n\t\t\t\treturn ensureType(value, \"boolean\");\n\t\t\t} catch {\n\t\t\t\treturn ensureType(value, \"string\");\n\t\t\t}\n\t\tcase \"protocolVersion\":\n\t\t\treturn ensureType(value, \"number\");\n\n\t\tcase \"nodeType\": {\n\t\t\tvalue = tryParseNodeType(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\n\t\tcase \"supportedDataRates\": {\n\t\t\tif (\n\t\t\t\tisArray(value)\n\t\t\t\t&& value.every((r: unknown) => typeof r === \"number\")\n\t\t\t) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t\tfail();\n\t\t}\n\n\t\tcase \"lastSeen\": {\n\t\t\tvalue = tryParseDate(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\n\t\tcase \"deviceConfigHash\": {\n\t\t\tvalue = tryParseBuffer(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\t}\n\n\t// Other properties\n\tif (key.startsWith(\"controller.associations.\")) {\n\t\tvalue = tryParseAssociationAddress(value);\n\t\tif (value) return value;\n\t\tfail();\n\t} else if (key.startsWith(\"controller.securityKeys.\")) {\n\t\tvalue = tryParseBuffer(value);\n\t\tif (value) return value;\n\t\tfail();\n\t}\n\n\tswitch (key) {\n\t\tcase cacheKeys.controller.provisioningList: {\n\t\t\tvalue = tryParseProvisioningList(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\t\tcase cacheKeys.controller.privateKey: {\n\t\t\tvalue = tryParseBuffer(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\t}\n\n\treturn value;\n}\n\nexport function serializeNetworkCacheValue(\n\tkey: string,\n\tvalue: unknown,\n): unknown {\n\t// Node-specific properties\n\tswitch (cacheKeyUtils.nodePropertyFromKey(key)) {\n\t\tcase \"interviewStage\": {\n\t\t\treturn InterviewStage[value as any];\n\t\t}\n\t\tcase \"deviceClass\": {\n\t\t\tconst deviceClass = value as DeviceClass;\n\t\t\treturn {\n\t\t\t\tbasic: deviceClass.basic,\n\t\t\t\tgeneric: deviceClass.generic.key,\n\t\t\t\tspecific: deviceClass.specific.key,\n\t\t\t};\n\t\t}\n\t\tcase \"nodeType\": {\n\t\t\treturn NodeType[value as any];\n\t\t}\n\t\tcase \"securityClasses\": {\n\t\t\tconst ret: Record<string, boolean> = {};\n\t\t\t// Save security classes where they are known\n\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\tif (secClass in (value as any)) {\n\t\t\t\t\tret[SecurityClass[secClass]] = (value as any)[secClass];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t\tcase \"dsk\": {\n\t\t\treturn dskToString(value as Uint8Array);\n\t\t}\n\t\tcase \"lastSeen\": {\n\t\t\t// Dates are stored as timestamps\n\t\t\treturn (value as Date).getTime();\n\t\t}\n\n\t\tcase \"deviceConfigHash\": {\n\t\t\treturn Bytes.view(value as Uint8Array).toString(\"hex\");\n\t\t}\n\t}\n\n\t// Other dynamic properties\n\tif (key.startsWith(\"controller.securityKeys.\")) {\n\t\treturn Bytes.view(value as Uint8Array).toString(\"hex\");\n\t}\n\n\t// Other fixed properties\n\tswitch (key) {\n\t\tcase cacheKeys.controller.provisioningList: {\n\t\t\tconst ret: any = [];\n\t\t\tfor (const entry of value as SmartStartProvisioningEntry[]) {\n\t\t\t\tconst serialized: Record<string, any> = { ...entry };\n\t\t\t\tserialized.securityClasses = entry.securityClasses.map((c) =>\n\t\t\t\t\tgetEnumMemberName(SecurityClass, c)\n\t\t\t\t);\n\t\t\t\tif (entry.requestedSecurityClasses) {\n\t\t\t\t\tserialized.requestedSecurityClasses = entry\n\t\t\t\t\t\t.requestedSecurityClasses.map((c) =>\n\t\t\t\t\t\t\tgetEnumMemberName(SecurityClass, c)\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (entry.status != undefined) {\n\t\t\t\t\tserialized.status = getEnumMemberName(\n\t\t\t\t\t\tProvisioningEntryStatus,\n\t\t\t\t\t\tentry.status,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (entry.protocol != undefined) {\n\t\t\t\t\tserialized.protocol = getEnumMemberName(\n\t\t\t\t\t\tProtocols,\n\t\t\t\t\t\tentry.protocol,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (entry.supportedProtocols != undefined) {\n\t\t\t\t\tserialized.supportedProtocols = entry.supportedProtocols\n\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t(p) => getEnumMemberName(Protocols, p),\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tret.push(serialized);\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t\tcase cacheKeys.controller.privateKey: {\n\t\t\treturn Bytes.view(value as Uint8Array).toString(\"hex\");\n\t\t}\n\t}\n\n\treturn value;\n}\n\n/** Defines the JSON paths that were used to store certain properties in the legacy network cache */\nconst legacyPaths = {\n\t// These seem to duplicate the ones in cacheKeys, but this allows us to change\n\t// something in the future without breaking migration\n\tcontroller: {\n\t\tprovisioningList: \"controller.provisioningList\",\n\t},\n\tnode: {\n\t\t// These are relative to the node object\n\t\tinterviewStage: `interviewStage`,\n\t\tdeviceClass: `deviceClass`,\n\t\tisListening: `isListening`,\n\t\tisFrequentListening: `isFrequentListening`,\n\t\tisRouting: `isRouting`,\n\t\tsupportedDataRates: `supportedDataRates`,\n\t\tprotocolVersion: `protocolVersion`,\n\t\tnodeType: `nodeType`,\n\t\tsupportsSecurity: `supportsSecurity`,\n\t\tsupportsBeaming: `supportsBeaming`,\n\t\tsecurityClasses: `securityClasses`,\n\t\tdsk: `dsk`,\n\t},\n\tcommandClass: {\n\t\t// These are relative to the commandClasses object\n\t\tname: `name`,\n\t\tendpoint: (index: number) => `endpoints.${index}`,\n\t},\n} as const;\n\nexport async function migrateLegacyNetworkCache(\n\thomeId: number,\n\tnetworkCache: JsonlDB,\n\tvalueDB: JsonlDB,\n\tstorageDriver: FileSystem,\n\tcacheDir: string,\n): Promise<void> {\n\tconst cacheFile = path.join(cacheDir, `${homeId.toString(16)}.json`);\n\tif (!(await storageDriver.pathExists(cacheFile))) return;\n\tconst legacy = JSON.parse(await storageDriver.readFile(cacheFile, \"utf8\"));\n\n\tconst jsonl = networkCache;\n\tfunction tryMigrate(\n\t\ttargetKey: string,\n\t\tsource: Record<string, any>,\n\t\tsourcePath: string,\n\t\tconverter?: (value: any) => any,\n\t): void {\n\t\tlet val = pickDeep(source, sourcePath);\n\t\tif (val != undefined && converter) val = converter(val);\n\t\tif (val != undefined) jsonl.set(targetKey, val);\n\t}\n\n\t// Translate all possible entries\n\n\t// Controller provisioning list\n\ttryMigrate(\n\t\tcacheKeys.controller.provisioningList,\n\t\tlegacy,\n\t\tlegacyPaths.controller.provisioningList,\n\t\ttryParseProvisioningList,\n\t);\n\n\t// All nodes, ...\n\tif (isObject(legacy.nodes)) {\n\t\tfor (const node of Object.values<any>(legacy.nodes)) {\n\t\t\tif (!isObject(node) || typeof node.id !== \"number\") continue;\n\t\t\tconst nodeCacheKeys = cacheKeys.node(node.id);\n\n\t\t\t// ... their properties\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.interviewStage,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.interviewStage,\n\t\t\t\ttryParseInterviewStage,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.deviceClass,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.deviceClass,\n\t\t\t\t(v) => tryParseDeviceClass(v),\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.isListening,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.isListening,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.isFrequentListening,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.isFrequentListening,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.isRouting,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.isRouting,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.supportedDataRates,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.supportedDataRates,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.protocolVersion,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.protocolVersion,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.nodeType,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.nodeType,\n\t\t\t\ttryParseNodeType,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.supportsSecurity,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.supportsSecurity,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.supportsBeaming,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.supportsBeaming,\n\t\t\t);\n\t\t\t// Convert security classes to single entries\n\t\t\tconst securityClasses = tryParseSecurityClasses(\n\t\t\t\tpickDeep(node, legacyPaths.node.securityClasses),\n\t\t\t);\n\t\t\tif (securityClasses) {\n\t\t\t\tfor (const [secClass, val] of securityClasses) {\n\t\t\t\t\tjsonl.set(nodeCacheKeys.securityClass(secClass), val);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.dsk,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.dsk,\n\t\t\t\tdskFromString,\n\t\t\t);\n\n\t\t\t// ... and command classes\n\t\t\t// The nesting was inverted from the legacy cache: node -> EP -> CCs\n\t\t\t// as opposed to node -> CC -> EPs\n\t\t\tif (isObject(node.commandClasses)) {\n\t\t\t\tfor (\n\t\t\t\t\tconst [ccIdHex, cc] of Object.entries<any>(\n\t\t\t\t\t\tnode.commandClasses,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tconst ccId = parseInt(ccIdHex, 16);\n\t\t\t\t\tif (isObject(cc.endpoints)) {\n\t\t\t\t\t\tfor (const endpointId of Object.keys(cc.endpoints)) {\n\t\t\t\t\t\t\tconst endpointIndex = parseInt(endpointId, 10);\n\n\t\t\t\t\t\t\tconst cacheKey = nodeCacheKeys\n\t\t\t\t\t\t\t\t.endpoint(endpointIndex)\n\t\t\t\t\t\t\t\t.commandClass(ccId);\n\n\t\t\t\t\t\t\ttryMigrate(\n\t\t\t\t\t\t\t\tcacheKey,\n\t\t\t\t\t\t\t\tcc,\n\t\t\t\t\t\t\t\tlegacyPaths.commandClass.endpoint(\n\t\t\t\t\t\t\t\t\tendpointIndex,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// In addition, try to move the hacky value ID for hasSUCReturnRoute from the value DB to the network cache\n\t\t\tconst dbKey = JSON.stringify({\n\t\t\t\tnodeId: node.id,\n\t\t\t\tcommandClass: -1,\n\t\t\t\tendpoint: 0,\n\t\t\t\tproperty: \"hasSUCReturnRoute\",\n\t\t\t});\n\t\t\tif (valueDB.has(dbKey)) {\n\t\t\t\tconst hasSUCReturnRoute = valueDB.get(dbKey);\n\t\t\t\tvalueDB.delete(dbKey);\n\t\t\t\tjsonl.set(nodeCacheKeys.hasSUCReturnRoute, hasSUCReturnRoute);\n\t\t\t}\n\t\t}\n\t}\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA;;;;;;;;;AAAA,
|
|
4
|
+
"sourcesContent": ["import type { JsonlDB } from \"@alcalzone/jsonl-db\";\nimport { type AssociationAddress } from \"@zwave-js/cc\";\nimport {\n\ttype CommandClasses,\n\ttype FileSystem,\n\tNodeType,\n\tProtocols,\n\tSecurityClass,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tdskFromString,\n\tdskToString,\n\tsecurityClassOrder,\n} from \"@zwave-js/core\";\nimport { Bytes, getEnumMemberName, num2hex, pickDeep } from \"@zwave-js/shared\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport path from \"node:path\";\nimport {\n\tProvisioningEntryStatus,\n\ttype SmartStartProvisioningEntry,\n} from \"../controller/Inclusion.js\";\nimport { DeviceClass } from \"../node/DeviceClass.js\";\nimport { InterviewStage } from \"../node/_Types.js\";\n\n/**\n * Defines the keys that are used to store certain properties in the network cache.\n */\nexport const cacheKeys = {\n\tcontroller: {\n\t\tprovisioningList: \"controller.provisioningList\",\n\t\tassociations: (groupId: number) => `controller.associations.${groupId}`,\n\t\tsecurityKeys: (secClass: SecurityClass) =>\n\t\t\t`controller.securityKeys.${\n\t\t\t\tgetEnumMemberName(\n\t\t\t\t\tSecurityClass,\n\t\t\t\t\tsecClass,\n\t\t\t\t)\n\t\t\t}`,\n\t\tsecurityKeysLongRange: (secClass: SecurityClass) =>\n\t\t\t`controller.securityKeyLongRange.${\n\t\t\t\tgetEnumMemberName(\n\t\t\t\t\tSecurityClass,\n\t\t\t\t\tsecClass,\n\t\t\t\t)\n\t\t\t}`,\n\t\tprivateKey: \"controller.privateKey\",\n\t},\n\t// TODO: somehow these functions should be combined with the pattern matching below\n\tnode: (nodeId: number) => {\n\t\tconst nodeBaseKey = `node.${nodeId}.`;\n\t\treturn {\n\t\t\t_baseKey: nodeBaseKey,\n\t\t\t_securityClassBaseKey: `${nodeBaseKey}securityClasses`,\n\t\t\t_priorityReturnRouteBaseKey: `${nodeBaseKey}priorityReturnRoute`,\n\t\t\tinterviewStage: `${nodeBaseKey}interviewStage`,\n\t\t\tdeviceClass: `${nodeBaseKey}deviceClass`,\n\t\t\tisListening: `${nodeBaseKey}isListening`,\n\t\t\tisFrequentListening: `${nodeBaseKey}isFrequentListening`,\n\t\t\tisRouting: `${nodeBaseKey}isRouting`,\n\t\t\tsupportedDataRates: `${nodeBaseKey}supportedDataRates`,\n\t\t\tprotocolVersion: `${nodeBaseKey}protocolVersion`,\n\t\t\tnodeType: `${nodeBaseKey}nodeType`,\n\t\t\tsupportsSecurity: `${nodeBaseKey}supportsSecurity`,\n\t\t\tsupportsBeaming: `${nodeBaseKey}supportsBeaming`,\n\t\t\tsecurityClass: (secClass: SecurityClass) =>\n\t\t\t\t`${nodeBaseKey}securityClasses.${\n\t\t\t\t\tgetEnumMemberName(\n\t\t\t\t\t\tSecurityClass,\n\t\t\t\t\t\tsecClass,\n\t\t\t\t\t)\n\t\t\t\t}`,\n\t\t\tdsk: `${nodeBaseKey}dsk`,\n\t\t\tendpoint: (index: number) => {\n\t\t\t\tconst endpointBaseKey = `${nodeBaseKey}endpoint.${index}.`;\n\t\t\t\tconst ccBaseKey = `${endpointBaseKey}commandClass.`;\n\t\t\t\treturn {\n\t\t\t\t\t_baseKey: endpointBaseKey,\n\t\t\t\t\t_ccBaseKey: ccBaseKey,\n\t\t\t\t\tcommandClass: (ccId: CommandClasses) => {\n\t\t\t\t\t\tconst ccAsHex = num2hex(ccId);\n\t\t\t\t\t\treturn `${ccBaseKey}${ccAsHex}`;\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t},\n\t\t\thasSUCReturnRoute: `${nodeBaseKey}hasSUCReturnRoute`,\n\t\t\tpriorityReturnRoute: (destinationNodeId: number) =>\n\t\t\t\t`${nodeBaseKey}priorityReturnRoute.${destinationNodeId}`,\n\t\t\tprioritySUCReturnRoute: `${nodeBaseKey}priorityReturnRoute.SUC`,\n\t\t\tcustomReturnRoutes: (destinationNodeId: number) =>\n\t\t\t\t`${nodeBaseKey}customReturnRoutes.${destinationNodeId}`,\n\t\t\tcustomSUCReturnRoutes: `${nodeBaseKey}customReturnRoutes.SUC`,\n\t\t\tdefaultTransitionDuration:\n\t\t\t\t`${nodeBaseKey}defaultTransitionDuration`,\n\t\t\tdefaultVolume: `${nodeBaseKey}defaultVolume`,\n\t\t\tlastSeen: `${nodeBaseKey}lastSeen`,\n\t\t\tdeviceConfigHash: `${nodeBaseKey}deviceConfigHash`,\n\t\t};\n\t},\n} as const;\n\nexport const cacheKeyUtils = {\n\tnodeIdFromKey: (key: string): number | undefined => {\n\t\tconst match = /^node\\.(?<nodeId>\\d+)\\./.exec(key);\n\t\tif (match) {\n\t\t\treturn parseInt(match.groups!.nodeId, 10);\n\t\t}\n\t},\n\tnodePropertyFromKey: (key: string): string | undefined => {\n\t\tconst match = /^node\\.\\d+\\.(?<property>[^\\.]+)$/.exec(key);\n\t\treturn match?.groups?.property;\n\t},\n\tisEndpointKey: (key: string): boolean => {\n\t\treturn /endpoints\\.(?<index>\\d+)$/.test(key);\n\t},\n\tendpointIndexFromKey: (key: string): number | undefined => {\n\t\tconst match = /endpoints\\.(?<index>\\d+)$/.exec(key);\n\t\tif (match) {\n\t\t\treturn parseInt(match.groups!.index, 10);\n\t\t}\n\t},\n\tdestinationFromPriorityReturnRouteKey: (\n\t\tkey: string,\n\t): number | undefined => {\n\t\tconst match = /\\.priorityReturnRoute\\.(?<nodeId>\\d+)$/.exec(key);\n\t\tif (match) {\n\t\t\treturn parseInt(match.groups!.nodeId, 10);\n\t\t}\n\t},\n} as const;\n\nfunction tryParseInterviewStage(value: unknown): InterviewStage | undefined {\n\tif (\n\t\t(typeof value === \"string\" || typeof value === \"number\")\n\t\t&& value in InterviewStage\n\t) {\n\t\treturn typeof value === \"number\"\n\t\t\t? value\n\t\t\t: (InterviewStage as any)[value];\n\t}\n}\n\nfunction tryParseDeviceClass(value: unknown): DeviceClass | undefined {\n\tif (isObject(value)) {\n\t\tconst { basic, generic, specific } = value;\n\t\tif (\n\t\t\ttypeof basic === \"number\"\n\t\t\t&& typeof generic === \"number\"\n\t\t\t&& typeof specific === \"number\"\n\t\t) {\n\t\t\treturn new DeviceClass(\n\t\t\t\tbasic,\n\t\t\t\tgeneric,\n\t\t\t\tspecific,\n\t\t\t);\n\t\t}\n\t}\n}\n\nfunction tryParseSecurityClasses(\n\tvalue: unknown,\n): Map<SecurityClass, boolean> | undefined {\n\tif (isObject(value)) {\n\t\tconst ret = new Map<SecurityClass, boolean>();\n\t\tfor (const [key, val] of Object.entries(value)) {\n\t\t\tif (\n\t\t\t\tkey in SecurityClass\n\t\t\t\t&& typeof (SecurityClass as any)[key] === \"number\"\n\t\t\t\t&& typeof val === \"boolean\"\n\t\t\t) {\n\t\t\t\tret.set((SecurityClass as any)[key] as SecurityClass, val);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n}\n\nfunction tryParseNodeType(value: unknown): NodeType | undefined {\n\tif (typeof value === \"string\" && value in NodeType) {\n\t\treturn (NodeType as any)[value];\n\t}\n}\n\nfunction tryParseProvisioningList(\n\tvalue: unknown,\n): SmartStartProvisioningEntry[] | undefined {\n\tconst ret: SmartStartProvisioningEntry[] = [];\n\tif (!isArray(value)) return;\n\tfor (const entry of value) {\n\t\tif (\n\t\t\tisObject(entry)\n\t\t\t&& typeof entry.dsk === \"string\"\n\t\t\t&& isArray(entry.securityClasses)\n\t\t\t// securityClasses are stored as strings, not the enum values\n\t\t\t&& entry.securityClasses.every((s) => isSerializedSecurityClass(s))\n\t\t\t&& (entry.requestedSecurityClasses == undefined\n\t\t\t\t|| (isArray(entry.requestedSecurityClasses)\n\t\t\t\t\t&& entry.requestedSecurityClasses.every((s) =>\n\t\t\t\t\t\tisSerializedSecurityClass(s)\n\t\t\t\t\t)))\n\t\t\t// protocol and supportedProtocols are (supposed to be) stored as strings, not the enum values\n\t\t\t&& (entry.protocol == undefined\n\t\t\t\t|| isSerializedProtocol(entry.protocol))\n\t\t\t&& (entry.supportedProtocols == undefined || (\n\t\t\t\tisArray(entry.supportedProtocols)\n\t\t\t\t&& entry.supportedProtocols.every((s) =>\n\t\t\t\t\tisSerializedProtocol(s)\n\t\t\t\t)\n\t\t\t))\n\t\t\t&& (entry.status == undefined\n\t\t\t\t|| isSerializedProvisioningEntryStatus(entry.status))\n\t\t) {\n\t\t\t// This is at least a PlannedProvisioningEntry, maybe it is an IncludedProvisioningEntry\n\t\t\tif (\"nodeId\" in entry && typeof entry.nodeId !== \"number\") {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst parsed = {\n\t\t\t\t...entry,\n\t\t\t} as unknown as SmartStartProvisioningEntry;\n\t\t\tparsed.securityClasses = entry.securityClasses\n\t\t\t\t.map((s) => tryParseSerializedSecurityClass(s))\n\t\t\t\t.filter((s) => s !== undefined);\n\t\t\tif (entry.requestedSecurityClasses) {\n\t\t\t\tparsed.requestedSecurityClasses = (\n\t\t\t\t\tentry.requestedSecurityClasses as any[]\n\t\t\t\t)\n\t\t\t\t\t.map((s) => tryParseSerializedSecurityClass(s))\n\t\t\t\t\t.filter((s) => s !== undefined);\n\t\t\t}\n\t\t\tif (entry.status != undefined) {\n\t\t\t\tparsed.status = ProvisioningEntryStatus[\n\t\t\t\t\tentry.status as any\n\t\t\t\t] as any as ProvisioningEntryStatus;\n\t\t\t}\n\t\t\tif (entry.protocol != undefined) {\n\t\t\t\tparsed.protocol = tryParseSerializedProtocol(entry.protocol);\n\t\t\t}\n\t\t\tif (entry.supportedProtocols) {\n\t\t\t\tparsed.supportedProtocols = (\n\t\t\t\t\tentry.supportedProtocols as any[]\n\t\t\t\t)\n\t\t\t\t\t.map((s) => tryParseSerializedProtocol(s))\n\t\t\t\t\t.filter((s) => s !== undefined);\n\t\t\t}\n\t\t\tret.push(parsed);\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\t}\n\treturn ret;\n}\n\nfunction isSerializedSecurityClass(value: unknown): boolean {\n\t// There was an error in previous iterations of the migration code, so we\n\t// now have to deal with the following variants:\n\t// 1. plain numbers representing a valid Security Class: 1\n\t// 2. strings representing a valid Security Class: \"S2_Unauthenticated\"\n\t// 3. strings represending a mis-formatted Security Class: \"unknown (0xS2_Unauthenticated)\"\n\tif (typeof value === \"number\" && value in SecurityClass) return true;\n\tif (typeof value === \"string\") {\n\t\tif (value.startsWith(\"unknown (0x\") && value.endsWith(\")\")) {\n\t\t\tvalue = value.slice(11, -1);\n\t\t}\n\t\tif (\n\t\t\t(value as any) in SecurityClass\n\t\t\t&& typeof SecurityClass[value as any] === \"number\"\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nfunction tryParseSerializedSecurityClass(\n\tvalue: unknown,\n): SecurityClass | undefined {\n\t// There was an error in previous iterations of the migration code, so we\n\t// now have to deal with the following variants:\n\t// 1. plain numbers representing a valid Security Class: 1\n\t// 2. strings representing a valid Security Class: \"S2_Unauthenticated\"\n\t// 3. strings represending a mis-formatted Security Class: \"unknown (0xS2_Unauthenticated)\"\n\n\tif (typeof value === \"number\" && value in SecurityClass) return value;\n\tif (typeof value === \"string\") {\n\t\tif (value.startsWith(\"unknown (0x\") && value.endsWith(\")\")) {\n\t\t\tvalue = value.slice(11, -1);\n\t\t}\n\t\tif (\n\t\t\t(value as any) in SecurityClass\n\t\t\t&& typeof SecurityClass[value as any] === \"number\"\n\t\t) {\n\t\t\treturn (SecurityClass as any)[value as any];\n\t\t}\n\t}\n}\n\nfunction isSerializedProvisioningEntryStatus(\n\ts: unknown,\n): s is keyof typeof ProvisioningEntryStatus {\n\treturn (\n\t\ttypeof s === \"string\"\n\t\t&& s in ProvisioningEntryStatus\n\t\t&& typeof ProvisioningEntryStatus[s as any] === \"number\"\n\t);\n}\n\nfunction isSerializedProtocol(\n\ts: unknown,\n): boolean {\n\t// The list of supported protocols has been around since before we started\n\t// saving them as their stringified variant, so we\n\t// now have to deal with the following variants:\n\t// 1. plain numbers representing a valid Protocol: 0\n\t// 2. strings representing a valid Protocols: \"ZWave\"\n\tif (typeof s === \"number\" && s in Protocols) return true;\n\treturn (\n\t\ttypeof s === \"string\"\n\t\t&& s in Protocols\n\t\t&& typeof Protocols[s as any] === \"number\"\n\t);\n}\n\nfunction tryParseSerializedProtocol(\n\tvalue: unknown,\n): Protocols | undefined {\n\t// The list of supported protocols has been around since before we started\n\t// saving them as their stringified variant, so we\n\t// now have to deal with the following variants:\n\t// 1. plain numbers representing a valid Protocol: 0\n\t// 2. strings representing a valid Protocols: \"ZWave\"\n\n\tif (typeof value === \"number\" && value in Protocols) return value;\n\tif (typeof value === \"string\") {\n\t\tif (\n\t\t\t(value as any) in Protocols\n\t\t\t&& typeof Protocols[value as any] === \"number\"\n\t\t) {\n\t\t\treturn (Protocols as any)[value as any];\n\t\t}\n\t}\n}\n\nfunction tryParseDate(value: unknown): Date | undefined {\n\t// Dates are stored as timestamps\n\tif (typeof value === \"number\") {\n\t\tconst ret = new Date(value);\n\t\tif (!isNaN(ret.getTime())) return ret;\n\t}\n}\n\nfunction tryParseAssociationAddress(\n\tvalue: unknown,\n): AssociationAddress | undefined {\n\tif (isObject(value)) {\n\t\tconst { nodeId, endpoint } = value;\n\t\tif (typeof nodeId !== \"number\") return;\n\t\tif (endpoint !== undefined && typeof endpoint !== \"number\") return;\n\n\t\treturn { nodeId, endpoint };\n\t}\n}\n\nfunction tryParseBuffer(\n\tvalue: unknown,\n): Uint8Array | undefined {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn Bytes.from(value, \"hex\");\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n}\n\nexport function deserializeNetworkCacheValue(\n\tkey: string,\n\tvalue: unknown,\n): unknown {\n\tfunction ensureType<T extends \"boolean\" | \"number\" | \"string\">(\n\t\tvalue: any,\n\t\ttype: T,\n\t): T | boolean {\n\t\tif (typeof value === type) return value as T;\n\t\tthrow new ZWaveError(\n\t\t\t`Incorrect type ${typeof value} for property \"${key}\"`,\n\t\t\tZWaveErrorCodes.Driver_InvalidCache,\n\t\t);\n\t}\n\n\tfunction fail(): never {\n\t\tthrow new ZWaveError(\n\t\t\t`Failed to deserialize property \"${key}\"`,\n\t\t\tZWaveErrorCodes.Driver_InvalidCache,\n\t\t);\n\t}\n\n\tswitch (cacheKeyUtils.nodePropertyFromKey(key)) {\n\t\tcase \"interviewStage\": {\n\t\t\tvalue = tryParseInterviewStage(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\t\tcase \"deviceClass\": {\n\t\t\tvalue = tryParseDeviceClass(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\t\tcase \"isListening\":\n\t\tcase \"isRouting\":\n\t\tcase \"hasSUCReturnRoute\":\n\t\t\treturn ensureType(value, \"boolean\");\n\n\t\tcase \"isFrequentListening\": {\n\t\t\tswitch (value) {\n\t\t\t\tcase \"1000ms\":\n\t\t\t\tcase true:\n\t\t\t\t\treturn \"1000ms\";\n\t\t\t\tcase \"250ms\":\n\t\t\t\t\treturn \"250ms\";\n\t\t\t\tcase false:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfail();\n\t\t}\n\n\t\tcase \"dsk\": {\n\t\t\tif (typeof value === \"string\") {\n\t\t\t\treturn dskFromString(value);\n\t\t\t}\n\t\t\tfail();\n\t\t}\n\n\t\tcase \"supportsSecurity\":\n\t\t\treturn ensureType(value, \"boolean\");\n\t\tcase \"supportsBeaming\":\n\t\t\ttry {\n\t\t\t\treturn ensureType(value, \"boolean\");\n\t\t\t} catch {\n\t\t\t\treturn ensureType(value, \"string\");\n\t\t\t}\n\t\tcase \"protocolVersion\":\n\t\t\treturn ensureType(value, \"number\");\n\n\t\tcase \"nodeType\": {\n\t\t\tvalue = tryParseNodeType(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\n\t\tcase \"supportedDataRates\": {\n\t\t\tif (\n\t\t\t\tisArray(value)\n\t\t\t\t&& value.every((r: unknown) => typeof r === \"number\")\n\t\t\t) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t\tfail();\n\t\t}\n\n\t\tcase \"lastSeen\": {\n\t\t\tvalue = tryParseDate(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\n\t\tcase \"deviceConfigHash\": {\n\t\t\tvalue = tryParseBuffer(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\t}\n\n\t// Other properties\n\tif (key.startsWith(\"controller.associations.\")) {\n\t\tvalue = tryParseAssociationAddress(value);\n\t\tif (value) return value;\n\t\tfail();\n\t} else if (key.startsWith(\"controller.securityKeys.\")) {\n\t\tvalue = tryParseBuffer(value);\n\t\tif (value) return value;\n\t\tfail();\n\t}\n\n\tswitch (key) {\n\t\tcase cacheKeys.controller.provisioningList: {\n\t\t\tvalue = tryParseProvisioningList(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\t\tcase cacheKeys.controller.privateKey: {\n\t\t\tvalue = tryParseBuffer(value);\n\t\t\tif (value) return value;\n\t\t\tfail();\n\t\t}\n\t}\n\n\treturn value;\n}\n\nexport function serializeNetworkCacheValue(\n\tkey: string,\n\tvalue: unknown,\n): unknown {\n\t// Node-specific properties\n\tswitch (cacheKeyUtils.nodePropertyFromKey(key)) {\n\t\tcase \"interviewStage\": {\n\t\t\treturn InterviewStage[value as any];\n\t\t}\n\t\tcase \"deviceClass\": {\n\t\t\tconst deviceClass = value as DeviceClass;\n\t\t\treturn {\n\t\t\t\tbasic: deviceClass.basic,\n\t\t\t\tgeneric: deviceClass.generic.key,\n\t\t\t\tspecific: deviceClass.specific.key,\n\t\t\t};\n\t\t}\n\t\tcase \"nodeType\": {\n\t\t\treturn NodeType[value as any];\n\t\t}\n\t\tcase \"securityClasses\": {\n\t\t\tconst ret: Record<string, boolean> = {};\n\t\t\t// Save security classes where they are known\n\t\t\tfor (const secClass of securityClassOrder) {\n\t\t\t\tif (secClass in (value as any)) {\n\t\t\t\t\tret[SecurityClass[secClass]] = (value as any)[secClass];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t\tcase \"dsk\": {\n\t\t\treturn dskToString(value as Uint8Array);\n\t\t}\n\t\tcase \"lastSeen\": {\n\t\t\t// Dates are stored as timestamps\n\t\t\treturn (value as Date).getTime();\n\t\t}\n\n\t\tcase \"deviceConfigHash\": {\n\t\t\treturn Bytes.view(value as Uint8Array).toString(\"hex\");\n\t\t}\n\t}\n\n\t// Other dynamic properties\n\tif (key.startsWith(\"controller.securityKeys.\")) {\n\t\treturn Bytes.view(value as Uint8Array).toString(\"hex\");\n\t}\n\n\t// Other fixed properties\n\tswitch (key) {\n\t\tcase cacheKeys.controller.provisioningList: {\n\t\t\tconst ret: any = [];\n\t\t\tfor (const entry of value as SmartStartProvisioningEntry[]) {\n\t\t\t\tconst serialized: Record<string, any> = { ...entry };\n\t\t\t\tserialized.securityClasses = entry.securityClasses.map((c) =>\n\t\t\t\t\tgetEnumMemberName(SecurityClass, c)\n\t\t\t\t);\n\t\t\t\tif (entry.requestedSecurityClasses) {\n\t\t\t\t\tserialized.requestedSecurityClasses = entry\n\t\t\t\t\t\t.requestedSecurityClasses.map((c) =>\n\t\t\t\t\t\t\tgetEnumMemberName(SecurityClass, c)\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (entry.status != undefined) {\n\t\t\t\t\tserialized.status = getEnumMemberName(\n\t\t\t\t\t\tProvisioningEntryStatus,\n\t\t\t\t\t\tentry.status,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (entry.protocol != undefined) {\n\t\t\t\t\tserialized.protocol = getEnumMemberName(\n\t\t\t\t\t\tProtocols,\n\t\t\t\t\t\tentry.protocol,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (entry.supportedProtocols != undefined) {\n\t\t\t\t\tserialized.supportedProtocols = entry.supportedProtocols\n\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t(p) => getEnumMemberName(Protocols, p),\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tret.push(serialized);\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t\tcase cacheKeys.controller.privateKey: {\n\t\t\treturn Bytes.view(value as Uint8Array).toString(\"hex\");\n\t\t}\n\t}\n\n\treturn value;\n}\n\n/** Defines the JSON paths that were used to store certain properties in the legacy network cache */\nconst legacyPaths = {\n\t// These seem to duplicate the ones in cacheKeys, but this allows us to change\n\t// something in the future without breaking migration\n\tcontroller: {\n\t\tprovisioningList: \"controller.provisioningList\",\n\t},\n\tnode: {\n\t\t// These are relative to the node object\n\t\tinterviewStage: `interviewStage`,\n\t\tdeviceClass: `deviceClass`,\n\t\tisListening: `isListening`,\n\t\tisFrequentListening: `isFrequentListening`,\n\t\tisRouting: `isRouting`,\n\t\tsupportedDataRates: `supportedDataRates`,\n\t\tprotocolVersion: `protocolVersion`,\n\t\tnodeType: `nodeType`,\n\t\tsupportsSecurity: `supportsSecurity`,\n\t\tsupportsBeaming: `supportsBeaming`,\n\t\tsecurityClasses: `securityClasses`,\n\t\tdsk: `dsk`,\n\t},\n\tcommandClass: {\n\t\t// These are relative to the commandClasses object\n\t\tname: `name`,\n\t\tendpoint: (index: number) => `endpoints.${index}`,\n\t},\n} as const;\n\nexport async function migrateLegacyNetworkCache(\n\thomeId: number,\n\tnetworkCache: JsonlDB,\n\tvalueDB: JsonlDB,\n\tstorageDriver: FileSystem,\n\tcacheDir: string,\n): Promise<void> {\n\tconst cacheFile = path.join(cacheDir, `${homeId.toString(16)}.json`);\n\tif (!(await storageDriver.pathExists(cacheFile))) return;\n\tconst legacy = JSON.parse(await storageDriver.readFile(cacheFile, \"utf8\"));\n\n\tconst jsonl = networkCache;\n\tfunction tryMigrate(\n\t\ttargetKey: string,\n\t\tsource: Record<string, any>,\n\t\tsourcePath: string,\n\t\tconverter?: (value: any) => any,\n\t): void {\n\t\tlet val = pickDeep(source, sourcePath);\n\t\tif (val != undefined && converter) val = converter(val);\n\t\tif (val != undefined) jsonl.set(targetKey, val);\n\t}\n\n\t// Translate all possible entries\n\n\t// Controller provisioning list\n\ttryMigrate(\n\t\tcacheKeys.controller.provisioningList,\n\t\tlegacy,\n\t\tlegacyPaths.controller.provisioningList,\n\t\ttryParseProvisioningList,\n\t);\n\n\t// All nodes, ...\n\tif (isObject(legacy.nodes)) {\n\t\tfor (const node of Object.values<any>(legacy.nodes)) {\n\t\t\tif (!isObject(node) || typeof node.id !== \"number\") continue;\n\t\t\tconst nodeCacheKeys = cacheKeys.node(node.id);\n\n\t\t\t// ... their properties\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.interviewStage,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.interviewStage,\n\t\t\t\ttryParseInterviewStage,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.deviceClass,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.deviceClass,\n\t\t\t\t(v) => tryParseDeviceClass(v),\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.isListening,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.isListening,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.isFrequentListening,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.isFrequentListening,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.isRouting,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.isRouting,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.supportedDataRates,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.supportedDataRates,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.protocolVersion,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.protocolVersion,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.nodeType,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.nodeType,\n\t\t\t\ttryParseNodeType,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.supportsSecurity,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.supportsSecurity,\n\t\t\t);\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.supportsBeaming,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.supportsBeaming,\n\t\t\t);\n\t\t\t// Convert security classes to single entries\n\t\t\tconst securityClasses = tryParseSecurityClasses(\n\t\t\t\tpickDeep(node, legacyPaths.node.securityClasses),\n\t\t\t);\n\t\t\tif (securityClasses) {\n\t\t\t\tfor (const [secClass, val] of securityClasses) {\n\t\t\t\t\tjsonl.set(nodeCacheKeys.securityClass(secClass), val);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttryMigrate(\n\t\t\t\tnodeCacheKeys.dsk,\n\t\t\t\tnode,\n\t\t\t\tlegacyPaths.node.dsk,\n\t\t\t\tdskFromString,\n\t\t\t);\n\n\t\t\t// ... and command classes\n\t\t\t// The nesting was inverted from the legacy cache: node -> EP -> CCs\n\t\t\t// as opposed to node -> CC -> EPs\n\t\t\tif (isObject(node.commandClasses)) {\n\t\t\t\tfor (\n\t\t\t\t\tconst [ccIdHex, cc] of Object.entries<any>(\n\t\t\t\t\t\tnode.commandClasses,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tconst ccId = parseInt(ccIdHex, 16);\n\t\t\t\t\tif (isObject(cc.endpoints)) {\n\t\t\t\t\t\tfor (const endpointId of Object.keys(cc.endpoints)) {\n\t\t\t\t\t\t\tconst endpointIndex = parseInt(endpointId, 10);\n\n\t\t\t\t\t\t\tconst cacheKey = nodeCacheKeys\n\t\t\t\t\t\t\t\t.endpoint(endpointIndex)\n\t\t\t\t\t\t\t\t.commandClass(ccId);\n\n\t\t\t\t\t\t\ttryMigrate(\n\t\t\t\t\t\t\t\tcacheKey,\n\t\t\t\t\t\t\t\tcc,\n\t\t\t\t\t\t\t\tlegacyPaths.commandClass.endpoint(\n\t\t\t\t\t\t\t\t\tendpointIndex,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// In addition, try to move the hacky value ID for hasSUCReturnRoute from the value DB to the network cache\n\t\t\tconst dbKey = JSON.stringify({\n\t\t\t\tnodeId: node.id,\n\t\t\t\tcommandClass: -1,\n\t\t\t\tendpoint: 0,\n\t\t\t\tproperty: \"hasSUCReturnRoute\",\n\t\t\t});\n\t\t\tif (valueDB.has(dbKey)) {\n\t\t\t\tconst hasSUCReturnRoute = valueDB.get(dbKey);\n\t\t\t\tvalueDB.delete(dbKey);\n\t\t\t\tjsonl.set(nodeCacheKeys.hasSUCReturnRoute, hasSUCReturnRoute);\n\t\t\t}\n\t\t}\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["value", "path"]
|
|
7
7
|
}
|
|
@@ -40,7 +40,9 @@ var import_promises = __toESM(require("node:fs/promises"), 1);
|
|
|
40
40
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
41
41
|
var path = __toESM(require("node:path"), 1);
|
|
42
42
|
var lockfile = __toESM(require("proper-lockfile"), 1);
|
|
43
|
-
var
|
|
43
|
+
var import_inc = __toESM(require("semver/functions/inc.js"), 1);
|
|
44
|
+
var import_valid = __toESM(require("semver/functions/valid.js"), 1);
|
|
45
|
+
var import_max_satisfying = __toESM(require("semver/ranges/max-satisfying.js"), 1);
|
|
44
46
|
async function checkForConfigUpdates(currentVersion) {
|
|
45
47
|
const { got } = await import("got");
|
|
46
48
|
let registry;
|
|
@@ -52,9 +54,9 @@ async function checkForConfigUpdates(currentVersion) {
|
|
|
52
54
|
if (!(0, import_typeguards.isObject)(registry) || !(0, import_typeguards.isObject)(registry.versions)) {
|
|
53
55
|
throw new import_core.ZWaveError(`Could not check for config updates: Downloaded package information does not contain version information!`, import_core.ZWaveErrorCodes.Config_Update_RegistryError);
|
|
54
56
|
}
|
|
55
|
-
const allVersions = Object.keys(registry.versions).filter((v) => !!
|
|
56
|
-
const updateRange = `>${currentVersion} <${
|
|
57
|
-
const updateVersion =
|
|
57
|
+
const allVersions = Object.keys(registry.versions).filter((v) => !!(0, import_valid.default)(v)).filter((v) => /\-\d{8}$/.test(v));
|
|
58
|
+
const updateRange = `>${currentVersion} <${(0, import_inc.default)(currentVersion, "patch")}`;
|
|
59
|
+
const updateVersion = (0, import_max_satisfying.default)(allVersions, updateRange, {
|
|
58
60
|
includePrerelease: true
|
|
59
61
|
});
|
|
60
62
|
if (updateVersion)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/driver/UpdateConfig.ts"],
|
|
4
|
-
"sourcesContent": ["import { ZWaveError, ZWaveErrorCodes } from \"@zwave-js/core\";\nimport { copyFilesRecursive, getErrorMessage } from \"@zwave-js/shared\";\nimport { isObject } from \"alcalzone-shared/typeguards\";\nimport execa from \"execa\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport * as path from \"node:path\";\nimport * as lockfile from \"proper-lockfile\";\nimport
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;AAAA,kBAA4C;AAC5C,oBAAoD;AACpD,wBAAyB;AACzB,mBAAkB;AAClB,sBAAe;AACf,qBAAe;AACf,WAAsB;AACtB,eAA0B;AAC1B,
|
|
6
|
-
"names": ["fs", "os", "path", "execa"]
|
|
4
|
+
"sourcesContent": ["import { ZWaveError, ZWaveErrorCodes } from \"@zwave-js/core\";\nimport { copyFilesRecursive, getErrorMessage } from \"@zwave-js/shared\";\nimport { isObject } from \"alcalzone-shared/typeguards\";\nimport execa from \"execa\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport * as path from \"node:path\";\nimport * as lockfile from \"proper-lockfile\";\nimport semverInc from \"semver/functions/inc.js\";\nimport semverValid from \"semver/functions/valid.js\";\nimport semverMaxSatisfying from \"semver/ranges/max-satisfying.js\";\n\n/**\n * Checks whether there is a compatible update for the currently installed config package.\n * Returns the new version if there is an update, `undefined` otherwise.\n * Throws if the update check failed.\n */\nexport async function checkForConfigUpdates(\n\tcurrentVersion: string,\n): Promise<string | undefined> {\n\tconst { got } = await import(\"got\");\n\tlet registry: Record<string, unknown>;\n\n\ttry {\n\t\tregistry = await got\n\t\t\t.get(\"https://registry.npmjs.org/@zwave-js/config\")\n\t\t\t.json();\n\t} catch {\n\t\tthrow new ZWaveError(\n\t\t\t`Could not check for config updates: Failed to download package information!`,\n\t\t\tZWaveErrorCodes.Config_Update_RegistryError,\n\t\t);\n\t}\n\n\tif (!isObject(registry) || !isObject(registry.versions)) {\n\t\tthrow new ZWaveError(\n\t\t\t`Could not check for config updates: Downloaded package information does not contain version information!`,\n\t\t\tZWaveErrorCodes.Config_Update_RegistryError,\n\t\t);\n\t}\n\n\t// Find the highest possible prepatch update (e.g. 7.2.4 -> 7.2.5-20200424)\n\tconst allVersions = Object.keys(registry.versions)\n\t\t.filter((v) => !!semverValid(v))\n\t\t.filter((v) => /\\-\\d{8}$/.test(v));\n\tconst updateRange = `>${currentVersion} <${\n\t\tsemverInc(\n\t\t\tcurrentVersion,\n\t\t\t\"patch\",\n\t\t)\n\t}`;\n\tconst updateVersion = semverMaxSatisfying(allVersions, updateRange, {\n\t\tincludePrerelease: true,\n\t});\n\tif (updateVersion) return updateVersion;\n}\n\n/**\n * Downloads and installs the given configuration update.\n * This only works if an external configuation directory is used.\n */\nexport async function installConfigUpdate(\n\tnewVersion: string,\n\texternal: {\n\t\tconfigDir: string;\n\t\tcacheDir: string;\n\t},\n): Promise<void> {\n\tconst { got } = await import(\"got\");\n\n\tlet registryInfo: any;\n\ttry {\n\t\tregistryInfo = await got\n\t\t\t.get(`https://registry.npmjs.org/@zwave-js/config/${newVersion}`)\n\t\t\t.json();\n\t} catch {\n\t\tthrow new ZWaveError(\n\t\t\t`Config update failed: Could not fetch package info from npm registry!`,\n\t\t\tZWaveErrorCodes.Config_Update_InstallFailed,\n\t\t);\n\t}\n\n\tconst url = registryInfo?.dist?.tarball;\n\tif (typeof url !== \"string\") {\n\t\tthrow new ZWaveError(\n\t\t\t`Config update failed: Could not fetch package tarball URL from npm registry!`,\n\t\t\tZWaveErrorCodes.Config_Update_InstallFailed,\n\t\t);\n\t}\n\n\tconst lockfilePath = external.cacheDir;\n\tconst lockfileOptions: lockfile.LockOptions = {\n\t\tlockfilePath: path.join(external.cacheDir, \"config-update.lock\"),\n\t};\n\n\ttry {\n\t\tawait lockfile.lock(lockfilePath, {\n\t\t\t...lockfileOptions,\n\t\t\tonCompromised: () => {\n\t\t\t\t// do nothing\n\t\t\t},\n\t\t});\n\t} catch {\n\t\tthrow new ZWaveError(\n\t\t\t`Config update failed: Another installation is already in progress!`,\n\t\t\tZWaveErrorCodes.Config_Update_InstallFailed,\n\t\t);\n\t}\n\n\tconst freeLock = async () => {\n\t\ttry {\n\t\t\tif (await lockfile.check(lockfilePath, lockfileOptions)) {\n\t\t\t\tawait lockfile.unlock(lockfilePath, lockfileOptions);\n\t\t\t}\n\t\t} catch {\n\t\t\t// whatever - just don't crash\n\t\t}\n\t};\n\n\t// Download tarball to a temporary directory\n\tlet tmpDir: string;\n\ttry {\n\t\ttmpDir = await fs.mkdtemp(\n\t\t\tpath.join(os.tmpdir(), \"zjs-config-update-\"),\n\t\t);\n\t} catch (e) {\n\t\tawait freeLock();\n\t\tthrow new ZWaveError(\n\t\t\t`Config update failed: Could not create temporary directory. Reason: ${\n\t\t\t\tgetErrorMessage(\n\t\t\t\t\te,\n\t\t\t\t)\n\t\t\t}`,\n\t\t\tZWaveErrorCodes.Config_Update_InstallFailed,\n\t\t);\n\t}\n\n\t// Download the package tarball into the temporary directory\n\tconst tarFilename = path.join(tmpDir, \"zjs-config-update.tgz\");\n\ttry {\n\t\tconst handle = await fs.open(tarFilename, \"w\");\n\t\tconst fstream = handle.createWriteStream({ autoClose: true });\n\t\tconst response = got.stream.get(url);\n\t\tresponse.pipe(fstream);\n\n\t\tawait new Promise((resolve, reject) => {\n\t\t\tresponse.on(\"error\", reject);\n\t\t\tresponse.on(\"end\", resolve);\n\t\t});\n\t} catch (e) {\n\t\tawait freeLock();\n\t\tthrow new ZWaveError(\n\t\t\t`Config update failed: Could not download tarball. Reason: ${\n\t\t\t\tgetErrorMessage(\n\t\t\t\t\te,\n\t\t\t\t)\n\t\t\t}`,\n\t\t\tZWaveErrorCodes.Config_Update_InstallFailed,\n\t\t);\n\t}\n\n\t// This should not be necessary in Docker. Leaving it here anyways in case\n\t// we want to use this method on Windows at some point\n\tfunction normalizeToUnixStyle(path: string): string {\n\t\tpath = path.replaceAll(\":\", \"\");\n\t\tpath = path.replaceAll(\"\\\\\", \"/\");\n\t\tif (!path.startsWith(\"/\")) path = `/${path}`;\n\t\treturn path;\n\t}\n\n\tconst extractedDir = path.join(tmpDir, \"extracted\");\n\ttry {\n\t\t// Extract the tarball in the temporary folder\n\t\tawait fs.rm(extractedDir, { recursive: true, force: true });\n\t\tawait fs.mkdir(extractedDir, { recursive: true });\n\t\tawait execa(\"tar\", [\n\t\t\t\"--strip-components=1\",\n\t\t\t\"-xzf\",\n\t\t\tnormalizeToUnixStyle(tarFilename),\n\t\t\t\"-C\",\n\t\t\tnormalizeToUnixStyle(extractedDir),\n\t\t]);\n\n\t\t// then overwrite the files in the external config directory\n\t\tawait fs.rm(external.configDir, { recursive: true, force: true });\n\t\tawait fs.mkdir(external.configDir, { recursive: true });\n\t\tawait copyFilesRecursive(\n\t\t\tpath.join(extractedDir, \"config\"),\n\t\t\texternal.configDir,\n\t\t\t(src) => src.endsWith(\".json\"),\n\t\t);\n\t\tconst externalVersionFilename = path.join(\n\t\t\texternal.configDir,\n\t\t\t\"version\",\n\t\t);\n\t\tawait fs.writeFile(externalVersionFilename, newVersion, \"utf8\");\n\t} catch {\n\t\tawait freeLock();\n\t\tthrow new ZWaveError(\n\t\t\t`Config update failed: Could not extract tarball`,\n\t\t\tZWaveErrorCodes.Config_Update_InstallFailed,\n\t\t);\n\t}\n\n\t// Clean up the temp dir and ignore errors\n\tvoid fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {\n\t\t// ignore\n\t});\n\n\t// Free the lock\n\tawait freeLock();\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["semverValid", "semverInc", "semverMaxSatisfying", "fs", "os", "path", "execa"]
|
|
7
7
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { LogConfig, LongRangeChannel, RFRegion } from "@zwave-js/core";
|
|
2
|
-
import type {
|
|
1
|
+
import type { FileSystem, LogConfig, LongRangeChannel, RFRegion } from "@zwave-js/core";
|
|
2
|
+
import type { ZWaveHostOptions } from "@zwave-js/host";
|
|
3
3
|
import type { ZWaveSerialPortBase } from "@zwave-js/serial";
|
|
4
4
|
import { type DeepPartial, type Expand } from "@zwave-js/shared";
|
|
5
5
|
import type { SerialPort } from "serialport";
|