zwave-js 15.24.2 → 15.25.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.
Files changed (88) hide show
  1. package/build/cjs/lib/_version.d.ts +1 -1
  2. package/build/cjs/lib/_version.js +1 -1
  3. package/build/cjs/lib/_version.js.map +1 -1
  4. package/build/cjs/lib/driver/Driver.d.ts +9 -1
  5. package/build/cjs/lib/driver/Driver.js +223 -132
  6. package/build/cjs/lib/driver/Driver.js.map +3 -3
  7. package/build/cjs/lib/driver/DriverMock.d.ts +10 -1
  8. package/build/cjs/lib/driver/DriverMock.js +54 -5
  9. package/build/cjs/lib/driver/DriverMock.js.map +3 -3
  10. package/build/cjs/lib/driver/PartialCCSessionManager.d.ts +69 -0
  11. package/build/cjs/lib/driver/PartialCCSessionManager.js +161 -0
  12. package/build/cjs/lib/driver/PartialCCSessionManager.js.map +7 -0
  13. package/build/cjs/lib/driver/ZWaveOptions.d.ts +5 -0
  14. package/build/cjs/lib/driver/ZWaveOptions.js.map +2 -2
  15. package/build/cjs/lib/node/MockNodeBehaviors.js +2 -1
  16. package/build/cjs/lib/node/MockNodeBehaviors.js.map +2 -2
  17. package/build/cjs/lib/node/Node.d.ts +0 -1
  18. package/build/cjs/lib/node/Node.js +51 -12
  19. package/build/cjs/lib/node/Node.js.map +2 -2
  20. package/build/cjs/lib/node/VirtualNode.js +4 -4
  21. package/build/cjs/lib/node/VirtualNode.js.map +2 -2
  22. package/build/cjs/lib/node/_Types.d.ts +5 -3
  23. package/build/cjs/lib/node/_Types.js +1 -0
  24. package/build/cjs/lib/node/_Types.js.map +2 -2
  25. package/build/cjs/lib/node/feature-apis/AccessControl.js +44 -18
  26. package/build/cjs/lib/node/feature-apis/AccessControl.js.map +2 -2
  27. package/build/cjs/lib/node/mixins/90_InterviewProgress.d.ts +94 -0
  28. package/build/cjs/lib/node/mixins/90_InterviewProgress.js +280 -0
  29. package/build/cjs/lib/node/mixins/90_InterviewProgress.js.map +7 -0
  30. package/build/cjs/lib/node/mixins/index.d.ts +2 -2
  31. package/build/cjs/lib/node/mixins/index.js +2 -2
  32. package/build/cjs/lib/node/mixins/index.js.map +2 -2
  33. package/build/cjs/lib/test/integrationTestSuite.d.ts +8 -1
  34. package/build/cjs/lib/test/integrationTestSuite.js +7 -3
  35. package/build/cjs/lib/test/integrationTestSuite.js.map +2 -2
  36. package/build/cjs/lib/test/integrationTestSuiteShared.d.ts +1 -1
  37. package/build/cjs/lib/test/integrationTestSuiteShared.js +2 -1
  38. package/build/cjs/lib/test/integrationTestSuiteShared.js.map +2 -2
  39. package/build/esm/lib/_version.d.ts +1 -1
  40. package/build/esm/lib/_version.js +1 -1
  41. package/build/esm/lib/driver/Driver.d.ts +9 -1
  42. package/build/esm/lib/driver/Driver.d.ts.map +1 -1
  43. package/build/esm/lib/driver/Driver.js +274 -151
  44. package/build/esm/lib/driver/Driver.js.map +1 -1
  45. package/build/esm/lib/driver/DriverMock.d.ts +10 -1
  46. package/build/esm/lib/driver/DriverMock.d.ts.map +1 -1
  47. package/build/esm/lib/driver/DriverMock.js +64 -15
  48. package/build/esm/lib/driver/DriverMock.js.map +1 -1
  49. package/build/esm/lib/driver/PartialCCSessionManager.d.ts +69 -0
  50. package/build/esm/lib/driver/PartialCCSessionManager.d.ts.map +1 -0
  51. package/build/esm/lib/driver/PartialCCSessionManager.js +169 -0
  52. package/build/esm/lib/driver/PartialCCSessionManager.js.map +1 -0
  53. package/build/esm/lib/driver/ZWaveOptions.d.ts +5 -0
  54. package/build/esm/lib/driver/ZWaveOptions.d.ts.map +1 -1
  55. package/build/esm/lib/driver/ZWaveOptions.js.map +1 -1
  56. package/build/esm/lib/node/MockNodeBehaviors.d.ts.map +1 -1
  57. package/build/esm/lib/node/MockNodeBehaviors.js +6 -0
  58. package/build/esm/lib/node/MockNodeBehaviors.js.map +1 -1
  59. package/build/esm/lib/node/Node.d.ts +0 -1
  60. package/build/esm/lib/node/Node.d.ts.map +1 -1
  61. package/build/esm/lib/node/Node.js +113 -21
  62. package/build/esm/lib/node/Node.js.map +1 -1
  63. package/build/esm/lib/node/VirtualNode.js +6 -6
  64. package/build/esm/lib/node/VirtualNode.js.map +1 -1
  65. package/build/esm/lib/node/_Types.d.ts +5 -3
  66. package/build/esm/lib/node/_Types.d.ts.map +1 -1
  67. package/build/esm/lib/node/_Types.js +2 -1
  68. package/build/esm/lib/node/_Types.js.map +1 -1
  69. package/build/esm/lib/node/feature-apis/AccessControl.d.ts.map +1 -1
  70. package/build/esm/lib/node/feature-apis/AccessControl.js +62 -19
  71. package/build/esm/lib/node/feature-apis/AccessControl.js.map +1 -1
  72. package/build/esm/lib/node/mixins/90_InterviewProgress.d.ts +94 -0
  73. package/build/esm/lib/node/mixins/90_InterviewProgress.d.ts.map +1 -0
  74. package/build/esm/lib/node/mixins/90_InterviewProgress.js +283 -0
  75. package/build/esm/lib/node/mixins/90_InterviewProgress.js.map +1 -0
  76. package/build/esm/lib/node/mixins/index.d.ts +2 -2
  77. package/build/esm/lib/node/mixins/index.d.ts.map +1 -1
  78. package/build/esm/lib/node/mixins/index.js +2 -2
  79. package/build/esm/lib/node/mixins/index.js.map +1 -1
  80. package/build/esm/lib/test/integrationTestSuite.d.ts +8 -1
  81. package/build/esm/lib/test/integrationTestSuite.d.ts.map +1 -1
  82. package/build/esm/lib/test/integrationTestSuite.js +8 -3
  83. package/build/esm/lib/test/integrationTestSuite.js.map +1 -1
  84. package/build/esm/lib/test/integrationTestSuiteShared.d.ts +1 -1
  85. package/build/esm/lib/test/integrationTestSuiteShared.d.ts.map +1 -1
  86. package/build/esm/lib/test/integrationTestSuiteShared.js +2 -1
  87. package/build/esm/lib/test/integrationTestSuiteShared.js.map +1 -1
  88. package/package.json +9 -9
@@ -1,3 +1,3 @@
1
- export declare const PACKAGE_VERSION = "15.24.2";
1
+ export declare const PACKAGE_VERSION = "15.25.0";
2
2
  export declare const PACKAGE_NAME = "zwave-js";
3
3
  //# sourceMappingURL=_version.d.ts.map
@@ -22,7 +22,7 @@ __export(version_exports, {
22
22
  PACKAGE_VERSION: () => PACKAGE_VERSION
23
23
  });
24
24
  module.exports = __toCommonJS(version_exports);
25
- const PACKAGE_VERSION = "15.24.2";
25
+ const PACKAGE_VERSION = "15.25.0";
26
26
  const PACKAGE_NAME = "zwave-js";
27
27
  // Annotate the CommonJS export names for ESM import in node:
28
28
  0 && (module.exports = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/_version.ts"],
4
- "sourcesContent": ["// This file is auto-generated by the codegen maintenance script\nexport const PACKAGE_VERSION = \"15.24.2\";\nexport const PACKAGE_NAME = \"zwave-js\";\n"],
4
+ "sourcesContent": ["// This file is auto-generated by the codegen maintenance script\nexport const PACKAGE_VERSION = \"15.25.0\";\nexport const PACKAGE_NAME = \"zwave-js\";\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;;;;;;AACO,MAAM,kBAAkB;AACxB,MAAM,eAAe;",
6
6
  "names": []
7
7
  }
@@ -416,8 +416,16 @@ export declare class Driver extends TypedEventTarget<DriverEventCallbacks> imple
416
416
  private isMissingNodeACK;
417
417
  private shouldRequestWakeupOnDemand;
418
418
  private partialCCSessions;
419
- private getPartialCCSession;
419
+ /**
420
+ * Determines the request a new partial CC session is the response to, if any.
421
+ * This is used to re-request the response when segments are missing.
422
+ */
423
+ private findPartialCCSessionRequest;
424
+ /** Re-requests the response a partial CC session belongs to because segments are missing */
425
+ private rerequestPartialCCSession;
420
426
  private assemblePartialCCs;
427
+ /** Refreshes the timeouts of awaited messages which a partial message may belong to */
428
+ private refreshAwaitedMessageTimers;
421
429
  private handleTransportServiceCommand;
422
430
  private handleUnsolicitedMessage;
423
431
  private handleSerialAPIStartedUnexpectedly;
@@ -62,6 +62,7 @@ var import_DriverMode = require("./DriverMode.js");
62
62
  var import_EndDeviceCLI = require("./EndDeviceCLI.js");
63
63
  var import_MessageGenerators = require("./MessageGenerators.js");
64
64
  var import_NetworkCache = require("./NetworkCache.js");
65
+ var import_PartialCCSessionManager = require("./PartialCCSessionManager.js");
65
66
  var import_Queue = require("./Queue.js");
66
67
  var import_SerialAPICommandMachine = require("./SerialAPICommandMachine.js");
67
68
  var import_StateMachineShared = require("./StateMachineShared.js");
@@ -117,7 +118,8 @@ const defaultOptions = {
117
118
  sendDataJammed: 5,
118
119
  nodeInterview: 5,
119
120
  smartStartInclusion: 5,
120
- firmwareUpdateOTW: 3
121
+ firmwareUpdateOTW: 3,
122
+ partialReports: 2
121
123
  },
122
124
  disableOptimisticValueUpdate: false,
123
125
  features: {
@@ -210,6 +212,9 @@ function checkOptions(options) {
210
212
  if (options.attempts.firmwareUpdateOTW < 1 || options.attempts.firmwareUpdateOTW > 5) {
211
213
  throw new import_core.ZWaveError(`The OTW firmware update attempts must be between 1 and 5!`, import_core.ZWaveErrorCodes.Driver_InvalidOptions);
212
214
  }
215
+ if (options.attempts.partialReports < 0 || options.attempts.partialReports > 3) {
216
+ throw new import_core.ZWaveError(`The partial reports attempts must be between 0 and 3!`, import_core.ZWaveErrorCodes.Driver_InvalidOptions);
217
+ }
213
218
  if (options.inclusionUserCallbacks) {
214
219
  if (!(0, import_typeguards.isObject)(options.inclusionUserCallbacks)) {
215
220
  throw new import_core.ZWaveError(`The inclusionUserCallbacks must be an object!`, import_core.ZWaveErrorCodes.Driver_InvalidOptions);
@@ -1943,7 +1948,7 @@ class Driver extends import_shared.TypedEventTarget {
1943
1948
  }
1944
1949
  if (!await this.ensureSerialAPI()) {
1945
1950
  if (destroyOnError) {
1946
- await this.destroy();
1951
+ await this.destroyWithMessage("The Serial API did not respond after soft-reset");
1947
1952
  } else {
1948
1953
  throw new import_core.ZWaveError("The Serial API did not respond after soft-reset", import_core.ZWaveErrorCodes.Driver_Failed);
1949
1954
  }
@@ -2218,6 +2223,7 @@ class Driver extends import_shared.TypedEventTarget {
2218
2223
  ]) {
2219
2224
  timeout?.clear();
2220
2225
  }
2226
+ this.partialCCSessions.clear();
2221
2227
  }
2222
2228
  async handleSerialData(serial) {
2223
2229
  try {
@@ -2346,7 +2352,6 @@ class Driver extends import_shared.TypedEventTarget {
2346
2352
  if (!this._controller && (0, import_serialapi.containsCC)(msg))
2347
2353
  return;
2348
2354
  if (msg) {
2349
- let wasMessageLogged = false;
2350
2355
  if ((0, import_serialapi.isCommandRequest)(msg) && (0, import_serialapi.containsCC)(msg)) {
2351
2356
  if (msg.command instanceof import_cc.SecurityCCCommandEncapsulationNonceGet) {
2352
2357
  const node = this.tryGetNode(msg);
@@ -2359,29 +2364,35 @@ class Driver extends import_shared.TypedEventTarget {
2359
2364
  secondaryTags: ["partial"],
2360
2365
  direction: "inbound"
2361
2366
  });
2362
- wasMessageLogged = true;
2363
- void this.handleTransportServiceCommand(msg.command).catch(() => {
2367
+ const reassembled = await this.handleTransportServiceCommand(msg.command);
2368
+ if (!reassembled) {
2369
+ this.refreshAwaitedMessageTimers(msg);
2370
+ return;
2371
+ }
2372
+ msg.command = reassembled;
2373
+ }
2374
+ if (this.isSecurityLevelTooLow(msg.command)) {
2375
+ this.driverLog.logMessage(msg, {
2376
+ direction: "inbound",
2377
+ secondaryTags: ["discarded"]
2364
2378
  });
2379
+ return;
2365
2380
  }
2366
- if (!await this.assemblePartialCCs(msg)) {
2367
- for (const entry of this.awaitedMessages) {
2368
- if (entry.refreshPredicate?.(msg)) {
2369
- entry.timeout?.refresh();
2370
- }
2371
- }
2381
+ const completeMsg = await this.assemblePartialCCs(msg);
2382
+ if (!completeMsg) {
2383
+ this.refreshAwaitedMessageTimers(msg);
2372
2384
  return;
2373
2385
  }
2374
- if (this.isSecurityLevelTooLow(msg.command) || this.shouldDiscardCC(msg.command)) {
2375
- if (!wasMessageLogged) {
2376
- this.driverLog.logMessage(msg, {
2377
- direction: "inbound",
2378
- secondaryTags: ["discarded"]
2379
- });
2380
- }
2386
+ msg = completeMsg;
2387
+ if (this.shouldDiscardCC(completeMsg.command)) {
2388
+ this.driverLog.logMessage(msg, {
2389
+ direction: "inbound",
2390
+ secondaryTags: ["discarded"]
2391
+ });
2381
2392
  return;
2382
2393
  }
2383
2394
  try {
2384
- this.persistCCValues(msg.command);
2395
+ this.persistCCValues(completeMsg.command);
2385
2396
  } catch (e) {
2386
2397
  if ((0, import_core.isZWaveError)(e) && e.code === import_core.ZWaveErrorCodes.PacketFormat_InvalidPayload) {
2387
2398
  this.driverLog.print(`dropping CC with invalid values${typeof e.context === "string" ? ` (Reason: ${e.context})` : ""}`, "warn");
@@ -2390,19 +2401,13 @@ class Driver extends import_shared.TypedEventTarget {
2390
2401
  throw e;
2391
2402
  }
2392
2403
  }
2393
- if ((0, import_cc.isTransportServiceEncapsulation)(msg.command)) {
2394
- msg.command = msg.command.encapsulated;
2395
- wasMessageLogged = false;
2396
- }
2397
2404
  }
2398
- if (!wasMessageLogged) {
2399
- try {
2400
- this.driverLog.logMessage(msg, {
2401
- direction: "inbound"
2402
- });
2403
- } catch (e) {
2404
- this.driverLog.print(`Logging a message failed: ${(0, import_shared.getErrorMessage)(e)}`);
2405
- }
2405
+ try {
2406
+ this.driverLog.logMessage(msg, {
2407
+ direction: "inbound"
2408
+ });
2409
+ } catch (e) {
2410
+ this.driverLog.print(`Logging a message failed: ${(0, import_shared.getErrorMessage)(e)}`);
2406
2411
  }
2407
2412
  void this.handleUnsolicitedMessage(msg);
2408
2413
  }
@@ -2612,7 +2617,12 @@ ${(0, import_shared.buffer2hex)(data)}`, "warn");
2612
2617
  if (this.serial.isOpen)
2613
2618
  await this.serial.close();
2614
2619
  await (0, import_async.wait)(1e3);
2615
- await this.openSerialport();
2620
+ try {
2621
+ await this.openSerialport();
2622
+ } catch (e) {
2623
+ void this.destroyWithMessage((0, import_shared.getErrorMessage)(e));
2624
+ return;
2625
+ }
2616
2626
  this.driverLog.print("Serial port reopened. Returning to normal operation and hoping for the best...", "warn");
2617
2627
  this._controller?.setStatus(import_core.ControllerStatus.Ready);
2618
2628
  this._recoveryPhase = 0;
@@ -2761,71 +2771,111 @@ ${(0, import_shared.buffer2hex)(data)}`, "warn");
2761
2771
  shouldRequestWakeupOnDemand(node) {
2762
2772
  return !!node.supportsWakeUpOnDemand && node.status === import_Types2.NodeStatus.Asleep && this.hasPendingTransactions((t) => t.requestWakeUpOnDemand && t.message.getNodeId() === node.id);
2763
2773
  }
2764
- partialCCSessions = /* @__PURE__ */ new Map();
2765
- getPartialCCSession(command, createIfMissing) {
2766
- const sessionId = command.getPartialCCSessionId();
2767
- if (sessionId) {
2768
- const partialSessionKey = JSON.stringify({
2769
- nodeId: command.nodeId,
2770
- ccId: command.ccId,
2771
- ccCommand: command.ccCommand,
2772
- ...sessionId
2774
+ partialCCSessions = new import_PartialCCSessionManager.PartialCCSessionManager({
2775
+ getSegmentTimeout: /* @__PURE__ */ __name(() => this._options.timeouts.report, "getSegmentTimeout"),
2776
+ getMaxReRequestAttempts: /* @__PURE__ */ __name(() => this._options.attempts.partialReports, "getMaxReRequestAttempts"),
2777
+ findRequestCC: /* @__PURE__ */ __name((command) => this.findPartialCCSessionRequest(command), "findRequestCC"),
2778
+ rerequestSession: /* @__PURE__ */ __name((session) => this.rerequestPartialCCSession(session), "rerequestSession"),
2779
+ onSessionLost: /* @__PURE__ */ __name((session) => {
2780
+ this.controllerLog.logNode(session.nodeId, {
2781
+ message: `Dropping incomplete partial CC session`,
2782
+ level: "warn",
2783
+ direction: "inbound"
2773
2784
  });
2774
- if (createIfMissing && !this.partialCCSessions.has(partialSessionKey)) {
2775
- this.partialCCSessions.set(partialSessionKey, []);
2776
- }
2777
- return {
2778
- partialSessionKey,
2779
- session: this.partialCCSessions.get(partialSessionKey)
2780
- };
2785
+ }, "onSessionLost")
2786
+ });
2787
+ /**
2788
+ * Determines the request a new partial CC session is the response to, if any.
2789
+ * This is used to re-request the response when segments are missing.
2790
+ */
2791
+ findPartialCCSessionRequest(command) {
2792
+ const currentMessage = this.queue.currentTransaction?.getCurrentMessage();
2793
+ if (!currentMessage || !(0, import_serialapi.containsCC)(currentMessage) || currentMessage.getNodeId() !== command.nodeId || !currentMessage.expectsNodeUpdate(this)) {
2794
+ return;
2795
+ }
2796
+ const request = (0, import_cc.getInnermostCommandClass)(currentMessage.command);
2797
+ if (!request.isExpectedCCResponse(this, (0, import_cc.getInnermostCommandClass)(command))) {
2798
+ return;
2781
2799
  }
2800
+ return request;
2801
+ }
2802
+ /** Re-requests the response a partial CC session belongs to because segments are missing */
2803
+ rerequestPartialCCSession(session) {
2804
+ this.refreshAwaitedMessageTimers(session.lastSegmentMsg);
2805
+ this.controllerLog.logNode(session.nodeId, {
2806
+ message: `Some expected reports were not received, requesting the response again...`,
2807
+ level: "warn",
2808
+ direction: "outbound"
2809
+ });
2810
+ void this.sendCommand(session.requestCC, {
2811
+ priority: import_core.MessagePriority.Immediate,
2812
+ maxSendAttempts: 1,
2813
+ ignoreNodeUpdate: true
2814
+ }).catch(import_shared.noop);
2782
2815
  }
2783
2816
  /**
2784
- * Assembles partial CCs of in a message body. Returns `true` when the message is complete and can be handled further.
2785
- * If the message expects another partial one, this returns `false`.
2817
+ * Assembles partial CCs in a message body. Returns the message containing the complete
2818
+ * command to continue handling. This is not necessarily the passed message, since responses
2819
+ * may be split into multiple messages.
2820
+ * If the message contains a partial CC and more segments are expected, this returns `undefined`.
2786
2821
  */
2787
2822
  async assemblePartialCCs(msg) {
2788
2823
  let command = msg.command;
2789
2824
  while (true) {
2790
- const { partialSessionKey, session } = this.getPartialCCSession(command, true) ?? {};
2791
- if (session) {
2792
- if (command.expectMoreMessages(session)) {
2793
- session.push(command);
2794
- if (!(0, import_cc.isTransportServiceEncapsulation)(msg.command)) {
2795
- this.driverLog.logMessage(msg, {
2796
- secondaryTags: ["partial"],
2825
+ const update = this.partialCCSessions.handleCommand(command, msg);
2826
+ if (update?.type === "segment") {
2827
+ if (update.duplicate) {
2828
+ this.controllerLog.logNode(command.nodeId, {
2829
+ message: `Ignoring duplicate partial CC segment:`,
2830
+ level: "debug",
2831
+ direction: "inbound"
2832
+ });
2833
+ }
2834
+ this.driverLog.logMessage(msg, {
2835
+ secondaryTags: ["partial"],
2836
+ direction: "inbound"
2837
+ });
2838
+ return void 0;
2839
+ } else if (update?.type === "complete") {
2840
+ if (update.partials.length > 0) {
2841
+ this.driverLog.logMessage(msg, {
2842
+ secondaryTags: ["partial"],
2843
+ direction: "inbound"
2844
+ });
2845
+ }
2846
+ try {
2847
+ await update.command.mergePartialCCs(update.partials, {
2848
+ ...this.getCCParsingContext(),
2849
+ sourceNodeId: update.command.nodeId,
2850
+ frameType: update.finalMsg.frameType
2851
+ });
2852
+ assertValidCCs(update.finalMsg);
2853
+ if (update.partials.length > 0) {
2854
+ this.controllerLog.logNode(update.command.nodeId, {
2855
+ message: `Assembled partial CC from ${update.partials.length + 1} segments`,
2856
+ level: "debug",
2797
2857
  direction: "inbound"
2798
2858
  });
2799
2859
  }
2800
- return false;
2801
- } else {
2802
- this.partialCCSessions.delete(partialSessionKey);
2803
- try {
2804
- await command.mergePartialCCs(session, {
2805
- ...this.getCCParsingContext(),
2806
- sourceNodeId: msg.command.nodeId,
2807
- frameType: msg.frameType
2808
- });
2809
- assertValidCCs(msg);
2810
- } catch (e) {
2811
- if ((0, import_core.isZWaveError)(e)) {
2812
- switch (e.code) {
2813
- case import_core.ZWaveErrorCodes.Deserialization_NotImplemented:
2814
- case import_core.ZWaveErrorCodes.CC_NotImplemented:
2815
- this.driverLog.print(`Dropping message because it could not be deserialized: ${e.message}`, "warn");
2816
- return false;
2817
- case import_core.ZWaveErrorCodes.PacketFormat_InvalidPayload:
2818
- this.driverLog.print(`Could not assemble partial CCs because the payload is invalid. Dropping them.`, "warn");
2819
- return false;
2820
- case import_core.ZWaveErrorCodes.Driver_NotReady:
2821
- this.driverLog.print(`Could not assemble partial CCs because the driver is not ready yet. Dropping them`, "warn");
2822
- return false;
2823
- }
2860
+ } catch (e) {
2861
+ if ((0, import_core.isZWaveError)(e)) {
2862
+ switch (e.code) {
2863
+ case import_core.ZWaveErrorCodes.Deserialization_NotImplemented:
2864
+ case import_core.ZWaveErrorCodes.CC_NotImplemented:
2865
+ this.driverLog.print(`Dropping message because it could not be deserialized: ${e.message}`, "warn");
2866
+ return void 0;
2867
+ case import_core.ZWaveErrorCodes.PacketFormat_InvalidPayload:
2868
+ this.driverLog.print(`Could not assemble partial CCs because the payload is invalid. Dropping them.`, "warn");
2869
+ return void 0;
2870
+ case import_core.ZWaveErrorCodes.Driver_NotReady:
2871
+ this.driverLog.print(`Could not assemble partial CCs because the driver is not ready yet. Dropping them`, "warn");
2872
+ return void 0;
2824
2873
  }
2825
- throw e;
2826
2874
  }
2875
+ throw e;
2827
2876
  }
2828
- } else {
2877
+ msg = update.finalMsg;
2878
+ command = update.command;
2829
2879
  }
2830
2880
  if ((0, import_cc.isEncapsulatingCommandClass)(command)) {
2831
2881
  command = command.encapsulated;
@@ -2833,19 +2883,40 @@ ${(0, import_shared.buffer2hex)(data)}`, "warn");
2833
2883
  break;
2834
2884
  }
2835
2885
  }
2836
- return true;
2886
+ return msg;
2837
2887
  }
2838
- /** Is called when a Transport Service command is received */
2888
+ /** Refreshes the timeouts of awaited messages which a partial message may belong to */
2889
+ refreshAwaitedMessageTimers(msg) {
2890
+ for (const entry of this.awaitedMessages) {
2891
+ if (entry.refreshPredicate?.(msg)) {
2892
+ entry.timeout?.refresh();
2893
+ }
2894
+ }
2895
+ }
2896
+ /**
2897
+ * Is called when a Transport Service command is received.
2898
+ * When the command completes a datagram, the reassembled command is returned.
2899
+ */
2839
2900
  async handleTransportServiceCommand(command) {
2840
2901
  const nodeSessions = this.ensureNodeSessions(command.nodeId);
2841
2902
  const missingSegmentTimeout = import_cc.TransportServiceTimeouts.requestMissingSegmentR2;
2842
- const advanceTransportServiceSession = /* @__PURE__ */ __name(async (session, input) => {
2843
- const machine = session.machine;
2903
+ const sendSegmentComplete = /* @__PURE__ */ __name(() => {
2904
+ const cc = new import_cc.TransportServiceCCSegmentComplete({
2905
+ nodeId: command.nodeId,
2906
+ sessionId: command.sessionId
2907
+ });
2908
+ void this.sendCommand(cc, {
2909
+ maxSendAttempts: 1,
2910
+ priority: import_core.MessagePriority.Immediate
2911
+ }).catch(import_shared.noop);
2912
+ }, "sendSegmentComplete");
2913
+ const advanceTransportServiceSession = /* @__PURE__ */ __name((session2, input) => {
2914
+ const machine = session2.machine;
2844
2915
  const transition = machine.next(input);
2845
2916
  if (transition) {
2846
2917
  machine.transition(transition.newState);
2847
2918
  if (machine.state.value === "receive") {
2848
- startMissingSegmentTimeout(session);
2919
+ startMissingSegmentTimeout(session2);
2849
2920
  } else if (machine.state.value === "requestMissing") {
2850
2921
  this.controllerLog.logNode(command.nodeId, {
2851
2922
  message: `Transport Service RX session #${command.sessionId}: Segment with offset ${machine.state.offset} missing - requesting it...`,
@@ -2857,11 +2928,10 @@ ${(0, import_shared.buffer2hex)(data)}`, "warn");
2857
2928
  sessionId: command.sessionId,
2858
2929
  datagramOffset: machine.state.offset
2859
2930
  });
2860
- await this.sendCommand(cc, {
2931
+ this.sendCommand(cc, {
2861
2932
  maxSendAttempts: 1,
2862
2933
  priority: import_core.MessagePriority.Immediate
2863
- }).catch(import_shared.noop);
2864
- startMissingSegmentTimeout(session);
2934
+ }).catch(import_shared.noop).finally(() => startMissingSegmentTimeout(session2));
2865
2935
  } else if (machine.state.value === "failure") {
2866
2936
  this.controllerLog.logNode(command.nodeId, {
2867
2937
  message: `Transport Service RX session #${command.sessionId} failed`,
@@ -2869,42 +2939,39 @@ ${(0, import_shared.buffer2hex)(data)}`, "warn");
2869
2939
  direction: "none"
2870
2940
  });
2871
2941
  nodeSessions.transportService.delete(command.sessionId);
2872
- if (session.timeout) {
2873
- clearTimeout(session.timeout);
2942
+ if (session2.timeout) {
2943
+ clearTimeout(session2.timeout);
2874
2944
  }
2945
+ } else if (machine.state.value === "success") {
2946
+ this.controllerLog.logNode(command.nodeId, {
2947
+ message: `Transport Service RX session #${command.sessionId} complete`,
2948
+ level: "debug",
2949
+ direction: "inbound"
2950
+ });
2951
+ if (session2.timeout) {
2952
+ clearTimeout(session2.timeout);
2953
+ }
2954
+ sendSegmentComplete();
2955
+ return "completed";
2875
2956
  }
2876
- }
2877
- if (machine.state.value === "success") {
2878
- this.controllerLog.logNode(command.nodeId, {
2879
- message: `Transport Service RX session #${command.sessionId} complete`,
2880
- level: "debug",
2881
- direction: "inbound"
2882
- });
2883
- if (session.timeout) {
2884
- clearTimeout(session.timeout);
2885
- }
2886
- const cc = new import_cc.TransportServiceCCSegmentComplete({
2887
- nodeId: command.nodeId,
2888
- sessionId: command.sessionId
2889
- });
2890
- await this.sendCommand(cc, {
2891
- maxSendAttempts: 1,
2892
- priority: import_core.MessagePriority.Immediate
2893
- }).catch(import_shared.noop);
2957
+ } else if (machine.state.value === "success") {
2958
+ sendSegmentComplete();
2894
2959
  }
2895
2960
  }, "advanceTransportServiceSession");
2896
- function startMissingSegmentTimeout(session) {
2897
- if (session.timeout) {
2898
- clearTimeout(session.timeout);
2961
+ function startMissingSegmentTimeout(session2) {
2962
+ if (session2.timeout) {
2963
+ clearTimeout(session2.timeout);
2899
2964
  }
2900
- session.timeout = setTimeout(() => {
2901
- session.timeout = void 0;
2902
- void advanceTransportServiceSession(session, {
2965
+ session2.timeout = setTimeout(() => {
2966
+ session2.timeout = void 0;
2967
+ advanceTransportServiceSession(session2, {
2903
2968
  value: "timeout"
2904
2969
  });
2905
2970
  }, missingSegmentTimeout);
2906
2971
  }
2907
2972
  __name(startMissingSegmentTimeout, "startMissingSegmentTimeout");
2973
+ let session;
2974
+ let datagramOffset;
2908
2975
  if (command instanceof import_cc.TransportServiceCCFirstSegment) {
2909
2976
  nodeSessions.transportService.clear();
2910
2977
  this.controllerLog.logNode(command.nodeId, {
@@ -2913,21 +2980,15 @@ ${(0, import_shared.buffer2hex)(data)}`, "warn");
2913
2980
  direction: "inbound"
2914
2981
  });
2915
2982
  const machine = (0, import_TransportServiceMachine.createTransportServiceRXMachine)(command.datagramSize, command.partialDatagram.length);
2916
- const session = {
2917
- fragmentSize: command.partialDatagram.length,
2918
- machine
2983
+ session = {
2984
+ machine,
2985
+ datagram: new import_shared.Bytes(command.datagramSize)
2919
2986
  };
2920
2987
  nodeSessions.transportService.set(command.sessionId, session);
2921
- startMissingSegmentTimeout(session);
2988
+ datagramOffset = 0;
2922
2989
  } else {
2923
- const transportSession = nodeSessions.transportService.get(command.sessionId);
2924
- if (transportSession) {
2925
- await advanceTransportServiceSession(transportSession, {
2926
- value: "segment",
2927
- offset: command.datagramOffset,
2928
- length: command.partialDatagram.length
2929
- });
2930
- } else {
2990
+ session = nodeSessions.transportService.get(command.sessionId);
2991
+ if (!session) {
2931
2992
  const cc = new import_cc.TransportServiceCCSegmentWait({
2932
2993
  nodeId: command.nodeId,
2933
2994
  pendingSegments: 0
@@ -2936,7 +2997,36 @@ ${(0, import_shared.buffer2hex)(data)}`, "warn");
2936
2997
  maxSendAttempts: 1,
2937
2998
  priority: import_core.MessagePriority.Immediate
2938
2999
  }).catch(import_shared.noop);
3000
+ return;
2939
3001
  }
3002
+ datagramOffset = command.datagramOffset;
3003
+ }
3004
+ if (datagramOffset + command.partialDatagram.length > session.datagram.length) {
3005
+ this.controllerLog.logNode(command.nodeId, {
3006
+ message: `Transport Service RX session #${command.sessionId}: Ignoring segment because it is incompatible with the datagram length`,
3007
+ level: "warn",
3008
+ direction: "inbound"
3009
+ });
3010
+ return;
3011
+ }
3012
+ session.datagram.set(command.partialDatagram, datagramOffset);
3013
+ const result = advanceTransportServiceSession(session, {
3014
+ value: "segment",
3015
+ offset: datagramOffset,
3016
+ length: command.partialDatagram.length
3017
+ });
3018
+ if (result !== "completed")
3019
+ return;
3020
+ try {
3021
+ return await import_cc.CommandClass.parse(session.datagram, {
3022
+ ...this.getCCParsingContext(),
3023
+ sourceNodeId: command.nodeId,
3024
+ // Transport Service is only used for singlecast commands
3025
+ frameType: command.frameType ?? "singlecast"
3026
+ });
3027
+ } catch (e) {
3028
+ this.driverLog.print(`Dropping Transport Service datagram because the contained command could not be deserialized: ${(0, import_shared.getErrorMessage)(e)}`, "warn");
3029
+ return;
2940
3030
  }
2941
3031
  }
2942
3032
  /**
@@ -3031,8 +3121,6 @@ ${handlers.length} left`);
3031
3121
  this.controllerLog.logNode(cc.nodeId, `is unknown - discarding received command...`, "warn");
3032
3122
  return true;
3033
3123
  }
3034
- if (cc instanceof import_cc.TransportServiceCC)
3035
- return false;
3036
3124
  if (cc instanceof import_cc.CRC16CCCommandEncapsulation) {
3037
3125
  return this.isSecurityLevelTooLow(cc.encapsulated);
3038
3126
  }
@@ -3088,7 +3176,7 @@ ${handlers.length} left`);
3088
3176
  }
3089
3177
  }
3090
3178
  if (requiresSecurity && !isSecure) {
3091
- this.controllerLog.logNode(cc.nodeId, `command was received at a lower security level than expected - discarding it...`, "warn");
3179
+ this.controllerLog.logNode(cc.nodeId, `command was received at a lower security level than expected - discarding it:`, "warn");
3092
3180
  return true;
3093
3181
  }
3094
3182
  return false;
@@ -4116,6 +4204,9 @@ ${handlers.length} left`);
4116
4204
  if (!!options.reportTimeoutMs) {
4117
4205
  msg.nodeUpdateTimeout = options.reportTimeoutMs;
4118
4206
  }
4207
+ if (options.ignoreNodeUpdate) {
4208
+ msg.ignoreNodeUpdate = true;
4209
+ }
4119
4210
  return msg;
4120
4211
  }
4121
4212
  /**