zwave-js 15.2.0 → 15.3.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 (65) hide show
  1. package/build/cjs/index_browser.js.map +2 -2
  2. package/build/cjs/lib/_version.d.ts +1 -1
  3. package/build/cjs/lib/_version.js +1 -1
  4. package/build/cjs/lib/_version.js.map +1 -1
  5. package/build/cjs/lib/controller/Controller.js +5 -4
  6. package/build/cjs/lib/controller/Controller.js.map +2 -2
  7. package/build/cjs/lib/driver/Driver.d.ts +12 -2
  8. package/build/cjs/lib/driver/Driver.js +141 -108
  9. package/build/cjs/lib/driver/Driver.js.map +2 -2
  10. package/build/cjs/lib/driver/DriverMock.js +1 -1
  11. package/build/cjs/lib/driver/DriverMock.js.map +2 -2
  12. package/build/cjs/lib/driver/Queue.d.ts +1 -1
  13. package/build/cjs/lib/driver/Queue.js +1 -0
  14. package/build/cjs/lib/driver/Queue.js.map +2 -2
  15. package/build/cjs/lib/driver/mDNSDiscovery/node.d.ts +3 -0
  16. package/build/cjs/lib/driver/mDNSDiscovery/node.js +97 -0
  17. package/build/cjs/lib/driver/mDNSDiscovery/node.js.map +7 -0
  18. package/build/cjs/lib/driver/mDNSDiscovery/stub.d.ts +3 -0
  19. package/build/cjs/lib/driver/mDNSDiscovery/stub.js +33 -0
  20. package/build/cjs/lib/driver/mDNSDiscovery/stub.js.map +7 -0
  21. package/build/cjs/lib/driver/mDNSDiscovery.d.ts +1 -1
  22. package/build/cjs/lib/driver/mDNSDiscovery.js +2 -70
  23. package/build/cjs/lib/driver/mDNSDiscovery.js.map +3 -3
  24. package/build/cjs/lib/node/Node.js +3 -1
  25. package/build/cjs/lib/node/Node.js.map +2 -2
  26. package/build/cjs/lib/zniffer/Zniffer.js +3 -3
  27. package/build/cjs/lib/zniffer/Zniffer.js.map +2 -2
  28. package/build/cjs/package.json +30 -1
  29. package/build/esm/index_browser.d.ts.map +1 -1
  30. package/build/esm/index_browser.js +1 -0
  31. package/build/esm/index_browser.js.map +1 -1
  32. package/build/esm/lib/_version.d.ts +1 -1
  33. package/build/esm/lib/_version.js +1 -1
  34. package/build/esm/lib/controller/Controller.d.ts.map +1 -1
  35. package/build/esm/lib/controller/Controller.js +7 -7
  36. package/build/esm/lib/controller/Controller.js.map +1 -1
  37. package/build/esm/lib/driver/Driver.d.ts +12 -2
  38. package/build/esm/lib/driver/Driver.d.ts.map +1 -1
  39. package/build/esm/lib/driver/Driver.js +186 -146
  40. package/build/esm/lib/driver/Driver.js.map +1 -1
  41. package/build/esm/lib/driver/DriverMock.js +1 -1
  42. package/build/esm/lib/driver/DriverMock.js.map +1 -1
  43. package/build/esm/lib/driver/Queue.d.ts +1 -1
  44. package/build/esm/lib/driver/Queue.d.ts.map +1 -1
  45. package/build/esm/lib/driver/Queue.js +1 -0
  46. package/build/esm/lib/driver/Queue.js.map +1 -1
  47. package/build/esm/lib/driver/mDNSDiscovery/node.d.ts +3 -0
  48. package/build/esm/lib/driver/mDNSDiscovery/node.d.ts.map +1 -0
  49. package/build/esm/lib/driver/mDNSDiscovery/node.js +70 -0
  50. package/build/esm/lib/driver/mDNSDiscovery/node.js.map +1 -0
  51. package/build/esm/lib/driver/mDNSDiscovery/stub.d.ts +3 -0
  52. package/build/esm/lib/driver/mDNSDiscovery/stub.d.ts.map +1 -0
  53. package/build/esm/lib/driver/mDNSDiscovery/stub.js +5 -0
  54. package/build/esm/lib/driver/mDNSDiscovery/stub.js.map +1 -0
  55. package/build/esm/lib/driver/mDNSDiscovery.d.ts +1 -1
  56. package/build/esm/lib/driver/mDNSDiscovery.d.ts.map +1 -1
  57. package/build/esm/lib/driver/mDNSDiscovery.js +1 -69
  58. package/build/esm/lib/driver/mDNSDiscovery.js.map +1 -1
  59. package/build/esm/lib/node/Node.d.ts.map +1 -1
  60. package/build/esm/lib/node/Node.js +6 -2
  61. package/build/esm/lib/node/Node.js.map +1 -1
  62. package/build/esm/lib/zniffer/Zniffer.js +3 -3
  63. package/build/esm/lib/zniffer/Zniffer.js.map +1 -1
  64. package/build/esm/package.json +30 -1
  65. package/package.json +40 -11
@@ -64,11 +64,15 @@ export declare class Driver extends TypedEventTarget<DriverEventCallbacks> imple
64
64
  private serialAPIQueue;
65
65
  /** Gives access to the transaction queues, ordered by priority */
66
66
  private get queues();
67
+ private initTransactionQueues;
68
+ private destroyTransactionQueues;
67
69
  private _scheduler;
68
70
  get scheduler(): TaskScheduler;
69
71
  private queuePaused;
70
72
  /** Used to immediately abort ongoing Serial API commands */
71
73
  private abortSerialAPICommand;
74
+ private initSerialAPIQueue;
75
+ private destroySerialAPIQueue;
72
76
  private _queuesBusyFlags;
73
77
  private _queueIdle;
74
78
  /** Whether the queue is currently idle */
@@ -359,6 +363,8 @@ export declare class Driver extends TypedEventTarget<DriverEventCallbacks> imple
359
363
  */
360
364
  softReset(): Promise<void>;
361
365
  private softResetInternal;
366
+ /** Soft-reset the Z-Wave module and restart the driver instance */
367
+ softResetAndRestart(): Promise<void>;
362
368
  /**
363
369
  * Checks whether recovering an unresponsive controller is enabled
364
370
  * and whether the driver is in a state where it makes sense.
@@ -394,6 +400,10 @@ export declare class Driver extends TypedEventTarget<DriverEventCallbacks> imple
394
400
  * Must be called under any circumstances.
395
401
  */
396
402
  destroy(): Promise<void>;
403
+ /** Cleanly destroy the controller instance, but not the entire driver */
404
+ private destroyController;
405
+ private closeDatabases;
406
+ private clearAllTimeouts;
397
407
  private handleSerialData;
398
408
  /**
399
409
  * Is called when the serial port has received a single-byte message or a complete message buffer
@@ -653,9 +663,9 @@ export declare class Driver extends TypedEventTarget<DriverEventCallbacks> imple
653
663
  private enterBootloaderFromCLI;
654
664
  private leaveBootloaderInternal;
655
665
  /**
656
- * Leaves the bootloader and destroys the driver instance if desired
666
+ * Leaves the bootloader by running the application.
657
667
  */
658
- leaveBootloader(destroy?: boolean): Promise<void>;
668
+ leaveBootloader(): Promise<void>;
659
669
  private serialport_onBootloaderData;
660
670
  /**
661
671
  * Waits until a specific chunk is received from the bootloader or a timeout has elapsed. Returns the received chunk.
@@ -305,24 +305,6 @@ class Driver extends import_shared.TypedEventTarget {
305
305
  },
306
306
  getSupportedCCVersion: /* @__PURE__ */ __name((cc, nodeId, endpointIndex) => this.getSupportedCCVersion(cc, nodeId, endpointIndex), "getSupportedCCVersion")
307
307
  };
308
- this.immediateQueue = new import_Queue.TransactionQueue({
309
- name: "immediate",
310
- mayStartNextTransaction: /* @__PURE__ */ __name((t) => {
311
- if (this.controller.status === import_core.ControllerStatus.Unresponsive) {
312
- return t.message instanceof import_serialapi.SoftResetRequest || t.message instanceof import_serialapi.GetControllerVersionRequest;
313
- }
314
- if (this.controller.status === import_core.ControllerStatus.Jammed) {
315
- return t.message instanceof import_serialapi.SoftResetRequest;
316
- }
317
- return !this.queuePaused && this.controller.status === import_core.ControllerStatus.Ready;
318
- }, "mayStartNextTransaction")
319
- });
320
- this.queue = new import_Queue.TransactionQueue({
321
- name: "normal",
322
- mayStartNextTransaction: /* @__PURE__ */ __name((t) => this.mayStartTransaction(t), "mayStartNextTransaction")
323
- });
324
- this.serialAPIQueue = new import_shared.AsyncQueue();
325
- this._queueIdle = false;
326
308
  this._scheduler = new import_Task.TaskScheduler();
327
309
  }
328
310
  serialFactory;
@@ -342,8 +324,10 @@ class Driver extends import_shared.TypedEventTarget {
342
324
  getDeviceConfig: /* @__PURE__ */ __name((nodeId) => this.getDeviceConfig(nodeId), "getDeviceConfig"),
343
325
  sdkVersion: this._controller?.sdkVersion,
344
326
  requestStorage: this._requestStorage,
345
- ownNodeId: this.controller.ownNodeId,
346
- homeId: this.controller.homeId,
327
+ ownNodeId: this._controller?.ownNodeId ?? 0,
328
+ // Unspecified node ID
329
+ homeId: this._controller?.homeId ?? 1431655765,
330
+ // Invalid home ID
347
331
  nodeIdType: this._controller?.nodeIdType ?? import_core.NodeIDType.Short
348
332
  };
349
333
  }
@@ -357,14 +341,51 @@ class Driver extends import_shared.TypedEventTarget {
357
341
  // We have multiple queues to achieve multiple "layers" of communication priority:
358
342
  // The default queue for most messages
359
343
  queue;
344
+ // Is initialized in initTransactionQueues()
360
345
  // An immediate queue for handling queries that need to be handled ASAP, e.g. Nonce Get
361
346
  immediateQueue;
347
+ // Is initialized in initTransactionQueues()
362
348
  // And all of them feed into the serial API queue, which contains commands that will be sent ASAP
363
349
  serialAPIQueue;
350
+ // Is initialized in initControllerAndNodes()
364
351
  /** Gives access to the transaction queues, ordered by priority */
365
352
  get queues() {
366
353
  return [this.immediateQueue, this.queue];
367
354
  }
355
+ initTransactionQueues() {
356
+ this.immediateQueue = new import_Queue.TransactionQueue({
357
+ name: "immediate",
358
+ mayStartNextTransaction: /* @__PURE__ */ __name((t) => {
359
+ if (this.controller.status === import_core.ControllerStatus.Unresponsive) {
360
+ return t.message instanceof import_serialapi.SoftResetRequest || t.message instanceof import_serialapi.GetControllerVersionRequest;
361
+ }
362
+ if (this.controller.status === import_core.ControllerStatus.Jammed) {
363
+ return t.message instanceof import_serialapi.SoftResetRequest;
364
+ }
365
+ return !this.queuePaused && this.controller.status === import_core.ControllerStatus.Ready;
366
+ }, "mayStartNextTransaction")
367
+ });
368
+ this.queue = new import_Queue.TransactionQueue({
369
+ name: "normal",
370
+ mayStartNextTransaction: /* @__PURE__ */ __name((t) => this.mayStartTransaction(t), "mayStartNextTransaction")
371
+ });
372
+ this._queueIdle = false;
373
+ for (const queue of this.queues) {
374
+ void this.drainTransactionQueue(queue);
375
+ }
376
+ }
377
+ async destroyTransactionQueues(reason, errorCode) {
378
+ for (const queue of this.queues) {
379
+ if (!queue)
380
+ return;
381
+ }
382
+ if ((0, import_shared.getenv)("NODE_ENV") !== "test") {
383
+ await this.rejectTransactions((_t) => true, reason, errorCode ?? import_core.ZWaveErrorCodes.Driver_TaskRemoved);
384
+ }
385
+ for (const queue of this.queues) {
386
+ queue.abort();
387
+ }
388
+ }
368
389
  _scheduler;
369
390
  get scheduler() {
370
391
  return this._scheduler;
@@ -372,9 +393,19 @@ class Driver extends import_shared.TypedEventTarget {
372
393
  queuePaused = false;
373
394
  /** Used to immediately abort ongoing Serial API commands */
374
395
  abortSerialAPICommand;
396
+ initSerialAPIQueue() {
397
+ this.serialAPIQueue = new import_shared.AsyncQueue();
398
+ void this.drainSerialAPIQueue();
399
+ }
400
+ destroySerialAPIQueue(reason, errorCode) {
401
+ if (!this.serialAPIQueue)
402
+ return;
403
+ this.serialAPIQueue.abort();
404
+ this.abortSerialAPICommand?.reject(new import_core.ZWaveError(reason, errorCode ?? import_core.ZWaveErrorCodes.Driver_Destroyed));
405
+ }
375
406
  // Keep track of which queues are currently busy
376
407
  _queuesBusyFlags = 0;
377
- _queueIdle;
408
+ _queueIdle = false;
378
409
  /** Whether the queue is currently idle */
379
410
  get queueIdle() {
380
411
  return this._queueIdle;
@@ -668,7 +699,11 @@ class Driver extends import_shared.TypedEventTarget {
668
699
  */
669
700
  static async enumerateSerialPorts({ local = true, remote = true } = {}) {
670
701
  const ret = [];
671
- const bindings = (await import("@zwave-js/serial/bindings/node")).serial;
702
+ const bindings = (
703
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
704
+ // @ts-ignore - For some reason, VSCode does not like this import, although tsc is fine with it
705
+ (await import("#default_bindings/serial")).serial
706
+ );
672
707
  if (local && typeof bindings.list === "function") {
673
708
  for (const port of await bindings.list()) {
674
709
  if (port.type === "custom")
@@ -742,10 +777,10 @@ class Driver extends import_shared.TypedEventTarget {
742
777
  return Promise.resolve();
743
778
  this._wasStarted = true;
744
779
  this.bindings = {
745
- fs: this._options.host?.fs ?? (await import("@zwave-js/core/bindings/fs/node")).fs,
746
- serial: this._options.host?.serial ?? (await import("@zwave-js/serial/bindings/node")).serial,
747
- db: this._options.host?.db ?? (await import("@zwave-js/core/bindings/db/jsonl")).db,
748
- log: this._options.host?.log ?? (await import("@zwave-js/core/bindings/log/node")).log
780
+ fs: this._options.host?.fs ?? (await import("#default_bindings/fs")).fs,
781
+ serial: this._options.host?.serial ?? (await import("#default_bindings/serial")).serial,
782
+ db: this._options.host?.db ?? (await import("#default_bindings/db")).db,
783
+ log: this._options.host?.log ?? (await import("#default_bindings/log")).log
749
784
  };
750
785
  this._logContainer = this.bindings.log(this._options.logConfig);
751
786
  this._driverLog = new import_Driver.DriverLogger(this, this._logContainer);
@@ -791,10 +826,6 @@ class Driver extends import_shared.TypedEventTarget {
791
826
  this.driverLog.print("serial port opened");
792
827
  this._isOpen = true;
793
828
  spOpenPromise.resolve();
794
- for (const queue of this.queues) {
795
- void this.drainTransactionQueue(queue);
796
- }
797
- void this.drainSerialAPIQueue();
798
829
  this._scheduler.start();
799
830
  if (typeof this._options.testingHooks?.onSerialPortOpen === "function") {
800
831
  await this._options.testingHooks.onSerialPortOpen(this.serial);
@@ -999,10 +1030,13 @@ class Driver extends import_shared.TypedEventTarget {
999
1030
  * adds event handlers and starts the interview process.
1000
1031
  */
1001
1032
  async initializeControllerAndNodes() {
1002
- if (this._controller == void 0) {
1003
- this._controller = new import_Controller.ZWaveController(this);
1004
- this._controller.on("node found", this.onNodeFound.bind(this)).on("node added", this.onNodeAdded.bind(this)).on("node removed", this.onNodeRemoved.bind(this)).on("status changed", this.onControllerStatusChanged.bind(this)).on("network found", this.onNetworkFound.bind(this)).on("network joined", this.onNetworkJoined.bind(this)).on("network left", this.onNetworkLeft.bind(this));
1033
+ if (this._controller) {
1034
+ throw new import_core.ZWaveError("The controller was already initialized!", import_core.ZWaveErrorCodes.Driver_Failed);
1005
1035
  }
1036
+ this._controller = new import_Controller.ZWaveController(this);
1037
+ this._controller.on("node found", this.onNodeFound.bind(this)).on("node added", this.onNodeAdded.bind(this)).on("node removed", this.onNodeRemoved.bind(this)).on("status changed", this.onControllerStatusChanged.bind(this)).on("network found", this.onNetworkFound.bind(this)).on("network joined", this.onNetworkJoined.bind(this)).on("network left", this.onNetworkLeft.bind(this));
1038
+ this.initTransactionQueues();
1039
+ this.initSerialAPIQueue();
1006
1040
  if (!this._options.testingHooks?.skipControllerIdentification) {
1007
1041
  const { nodeIds } = await this.controller.queryCapabilities();
1008
1042
  await this.controller.queryAndConfigureRF();
@@ -1231,7 +1265,7 @@ class Driver extends import_shared.TypedEventTarget {
1231
1265
  this.retryNodeInterviewTimeouts.get(node.id)?.clear();
1232
1266
  this.retryNodeInterviewTimeouts.delete(node.id);
1233
1267
  }
1234
- this.rejectTransactions((t) => t.message.getNodeId() === node.id && (t.priority === import_core.MessagePriority.NodeQuery || t.tag === "interview"), "The interview was restarted", import_core.ZWaveErrorCodes.Controller_InterviewRestarted);
1268
+ await this.rejectTransactions((t) => t.message.getNodeId() === node.id && (t.priority === import_core.MessagePriority.NodeQuery || t.tag === "interview"), "The interview was restarted", import_core.ZWaveErrorCodes.Controller_InterviewRestarted);
1235
1269
  const maxInterviewAttempts = this._options.attempts.nodeInterview;
1236
1270
  try {
1237
1271
  if (!await node.interviewInternal()) {
@@ -1296,7 +1330,7 @@ class Driver extends import_shared.TypedEventTarget {
1296
1330
  onNodeWakeUp(node, oldStatus) {
1297
1331
  this.controllerLog.logNode(node.id, `The node is ${oldStatus === import_Types.NodeStatus.Unknown ? "" : "now "}awake.`);
1298
1332
  if (oldStatus === import_Types.NodeStatus.Asleep) {
1299
- this.reduceQueues(({ message }) => {
1333
+ void this.reduceQueues(({ message }) => {
1300
1334
  if (message.getNodeId() !== node.id)
1301
1335
  return { type: "keep" };
1302
1336
  if (messageIsPing(message)) {
@@ -1519,7 +1553,7 @@ class Driver extends import_shared.TypedEventTarget {
1519
1553
  this.securityManager?.deleteAllNoncesForReceiver(node.id);
1520
1554
  this.securityManager2?.deleteNonce(node.id);
1521
1555
  this.securityManagerLR?.deleteNonce(node.id);
1522
- this.rejectAllTransactionsForNode(node.id, "The node was removed from the network", import_core.ZWaveErrorCodes.Controller_NodeRemoved);
1556
+ void this.rejectAllTransactionsForNode(node.id, "The node was removed from the network", import_core.ZWaveErrorCodes.Controller_NodeRemoved);
1523
1557
  const replaced = reason === import_Inclusion.RemoveNodeReason.Replaced || reason === import_Inclusion.RemoveNodeReason.ProxyReplaced;
1524
1558
  if (!replaced) {
1525
1559
  this.controller.removeNodeFromAllAssociations(node.id).catch((err) => {
@@ -1822,8 +1856,8 @@ class Driver extends import_shared.TypedEventTarget {
1822
1856
  this.unpauseSendQueue();
1823
1857
  }
1824
1858
  }
1825
- /** @internal */
1826
- async softResetAndRestart(restartLogMessage, restartReason) {
1859
+ /** Soft-reset the Z-Wave module and restart the driver instance */
1860
+ async softResetAndRestart() {
1827
1861
  this.controllerLog.print("Performing soft reset...");
1828
1862
  try {
1829
1863
  this.isSoftResetting = true;
@@ -1834,12 +1868,12 @@ class Driver extends import_shared.TypedEventTarget {
1834
1868
  } catch (e) {
1835
1869
  this.controllerLog.print(`Soft reset failed: ${(0, import_shared.getErrorMessage)(e)}`, "error");
1836
1870
  }
1871
+ if (!await this.ensureSerialAPI()) {
1872
+ await this.destroyWithMessage("The Serial API did not respond after soft-reset");
1873
+ }
1837
1874
  this.isSoftResetting = false;
1838
- this.controllerLog.print(restartLogMessage);
1839
- await this.destroy();
1840
- process.nextTick(() => {
1841
- this.emit("error", new import_core.ZWaveError(restartReason, import_core.ZWaveErrorCodes.Driver_Failed));
1842
- });
1875
+ await this.destroyController();
1876
+ void this.initializeControllerAndNodes();
1843
1877
  }
1844
1878
  /**
1845
1879
  * Checks whether recovering an unresponsive controller is enabled
@@ -1856,7 +1890,9 @@ class Driver extends import_shared.TypedEventTarget {
1856
1890
  let waitResult = await this.waitForMessage((msg) => msg.functionType === import_serial.FunctionType.SerialAPIStarted, 1500).catch(() => false);
1857
1891
  if (waitResult) {
1858
1892
  this.controllerLog.print("reconnected and restarted");
1859
- this.controller["_supportsLongRange"] = waitResult.supportsLongRange;
1893
+ if (this._controller) {
1894
+ this._controller["_supportsLongRange"] = waitResult.supportsLongRange;
1895
+ }
1860
1896
  return true;
1861
1897
  }
1862
1898
  if (!this.serial.isOpen) {
@@ -1873,7 +1909,9 @@ class Driver extends import_shared.TypedEventTarget {
1873
1909
  }, this._options.timeouts.serialAPIStarted).catch(() => false);
1874
1910
  if (waitResult) {
1875
1911
  this.controllerLog.print("Serial API started");
1876
- this.controller["_supportsLongRange"] = waitResult.supportsLongRange;
1912
+ if (this._controller) {
1913
+ this._controller["_supportsLongRange"] = waitResult.supportsLongRange;
1914
+ }
1877
1915
  return true;
1878
1916
  }
1879
1917
  this.controllerLog.print("Did not receive notification that Serial API has started, checking if it responds...");
@@ -1944,20 +1982,7 @@ class Driver extends import_shared.TypedEventTarget {
1944
1982
  await this.scheduler.removeTasks(() => true, new import_core.ZWaveError("The controller is being hard-reset", import_core.ZWaveErrorCodes.Driver_TaskRemoved));
1945
1983
  await this.controller.setControllerNIF();
1946
1984
  await this.controller.hardReset();
1947
- this.rejectTransactions(() => true, `The controller was hard-reset`);
1948
- this.sendNodeToSleepTimers.forEach((timeout) => timeout.clear());
1949
- this.sendNodeToSleepTimers.clear();
1950
- for (const timer of this.retryNodeInterviewTimeouts.values()) {
1951
- timer.clear();
1952
- }
1953
- this.retryNodeInterviewTimeouts.clear();
1954
- for (const timer of this.autoRefreshNodeValueTimers.values()) {
1955
- timer.clear();
1956
- }
1957
- this.autoRefreshNodeValueTimers.clear();
1958
- this.pollBackgroundRSSITimer?.clear();
1959
- this.pollBackgroundRSSITimer = void 0;
1960
- this._controllerInterviewed = false;
1985
+ await this.destroyController();
1961
1986
  void this.initializeControllerAndNodes();
1962
1987
  if (oldPrivateKey) {
1963
1988
  this.once("driver ready", () => {
@@ -2023,15 +2048,36 @@ class Driver extends import_shared.TypedEventTarget {
2023
2048
  this._destroyPromise = (0, import_deferred_promise.createDeferredPromise)();
2024
2049
  this.driverLog.print("destroying driver instance...");
2025
2050
  await this._scheduler.stop();
2026
- this.serialAPIQueue.abort();
2027
- for (const queue of this.queues) {
2028
- queue.abort();
2029
- }
2051
+ await this.destroyTransactionQueues("driver instance destroyed", import_core.ZWaveErrorCodes.Driver_Destroyed);
2052
+ this.destroySerialAPIQueue("driver instance destroyed", import_core.ZWaveErrorCodes.Driver_Destroyed);
2030
2053
  if (this.serial != void 0) {
2031
2054
  if (this.serial.isOpen)
2032
2055
  await this.serial.close();
2033
2056
  this.serial = void 0;
2034
2057
  }
2058
+ await this.destroyController();
2059
+ this.driverLog.print(`driver instance destroyed`);
2060
+ this._logContainer.destroy();
2061
+ this._destroyPromise.resolve();
2062
+ }
2063
+ /** Cleanly destroy the controller instance, but not the entire driver */
2064
+ // FIXME: Too much overlap with destroy()
2065
+ async destroyController() {
2066
+ await this.scheduler.removeTasks(() => true, new import_core.ZWaveError("The controller instance is being destroyed", import_core.ZWaveErrorCodes.Driver_TaskRemoved));
2067
+ await this.destroyTransactionQueues("The controller instance is being destroyed", import_core.ZWaveErrorCodes.Driver_TaskRemoved);
2068
+ this.destroySerialAPIQueue("The controller instance is being destroyed", import_core.ZWaveErrorCodes.Driver_TaskRemoved);
2069
+ this.requestHandlers.clear();
2070
+ await this.closeDatabases();
2071
+ this.clearAllTimeouts();
2072
+ if (this._controller) {
2073
+ this._controller.destroy();
2074
+ this._controller = void 0;
2075
+ }
2076
+ this._controllerInterviewed = false;
2077
+ this._nodesReady.clear();
2078
+ this._nodesReadyEventEmitted = false;
2079
+ }
2080
+ async closeDatabases() {
2035
2081
  try {
2036
2082
  await this._valueDB?.close();
2037
2083
  } catch (e) {
@@ -2047,6 +2093,8 @@ class Driver extends import_shared.TypedEventTarget {
2047
2093
  } catch (e) {
2048
2094
  this.driverLog.print(`Closing the network cache failed: ${(0, import_shared.getErrorMessage)(e)}`, "error");
2049
2095
  }
2096
+ }
2097
+ clearAllTimeouts() {
2050
2098
  for (const timeout of [
2051
2099
  this._powerlevelTestNodeContext?.timeout
2052
2100
  ]) {
@@ -2067,13 +2115,6 @@ class Driver extends import_shared.TypedEventTarget {
2067
2115
  ]) {
2068
2116
  timeout?.clear();
2069
2117
  }
2070
- if (this._controller) {
2071
- this._controller.destroy();
2072
- this._controller = void 0;
2073
- }
2074
- this.driverLog.print(`driver instance destroyed`);
2075
- this._logContainer.destroy();
2076
- this._destroyPromise.resolve();
2077
2118
  }
2078
2119
  async handleSerialData(serial) {
2079
2120
  try {
@@ -2437,7 +2478,7 @@ ${(0, import_shared.buffer2hex)(data)}`, "warn");
2437
2478
  reason: errorMsg
2438
2479
  });
2439
2480
  transaction.abort(error);
2440
- this.rejectAllTransactionsForNode(node.id, errorMsg);
2481
+ void this.rejectAllTransactionsForNode(node.id, errorMsg);
2441
2482
  return true;
2442
2483
  }
2443
2484
  }
@@ -2521,7 +2562,7 @@ ${(0, import_shared.buffer2hex)(data)}`, "warn");
2521
2562
  reason: errorMsg
2522
2563
  });
2523
2564
  transaction.abort(error);
2524
- this.rejectAllTransactionsForNode(node.id, errorMsg);
2565
+ void this.rejectAllTransactionsForNode(node.id, errorMsg);
2525
2566
  handled = true;
2526
2567
  }
2527
2568
  if (this._recoveryPhase === 4) {
@@ -3578,8 +3619,6 @@ ${handlers.length} left`);
3578
3619
  } else {
3579
3620
  throw e;
3580
3621
  }
3581
- } else if ((0, import_serialapi.isTransmitReport)(e.context) && e.context.transmitStatus === import_core.TransmitStatus.NoAck) {
3582
- waitDurationMs = 100;
3583
3622
  } else if (e.code === import_core.ZWaveErrorCodes.Controller_MessageDropped) {
3584
3623
  throw e;
3585
3624
  }
@@ -3610,6 +3649,7 @@ ${handlers.length} left`);
3610
3649
  async drainSerialAPIQueue() {
3611
3650
  for await (const item of this.serialAPIQueue) {
3612
3651
  const { msg, transactionSource, result } = item;
3652
+ this._currentSerialAPICommandPromise = result;
3613
3653
  attempts: for (let attempt = 1; ; attempt++) {
3614
3654
  try {
3615
3655
  const ret = await this.executeSerialAPICommand(msg, transactionSource);
@@ -3638,7 +3678,10 @@ ${handlers.length} left`);
3638
3678
  this.serialAPIQueue.add({
3639
3679
  msg,
3640
3680
  transactionSource,
3641
- result
3681
+ result,
3682
+ [Symbol.dispose]: () => {
3683
+ result.reject(new import_core.ZWaveError("The message has been removed from the queue", import_core.ZWaveErrorCodes.Controller_MessageDropped, void 0, transactionSource));
3684
+ }
3642
3685
  });
3643
3686
  return result;
3644
3687
  }
@@ -3830,7 +3873,7 @@ ${handlers.length} left`);
3830
3873
  let expirationTimeout;
3831
3874
  if (options.expire) {
3832
3875
  expirationTimeout = (0, import_shared.setTimer)(() => {
3833
- this.reduceQueues((t, _source) => {
3876
+ void this.reduceQueues((t, _source) => {
3834
3877
  if (t === transaction) {
3835
3878
  return {
3836
3879
  type: "reject",
@@ -4217,7 +4260,7 @@ ${handlers.length} left`);
4217
4260
  ...requeue,
4218
4261
  tag: "interview"
4219
4262
  };
4220
- this.reduceQueues((transaction, _source) => {
4263
+ void this.reduceQueues((transaction, _source) => {
4221
4264
  const msg = transaction.message;
4222
4265
  if (msg.getNodeId() !== nodeId)
4223
4266
  return { type: "keep" };
@@ -4229,7 +4272,7 @@ ${handlers.length} left`);
4229
4272
  * Rejects all pending transactions that match a predicate and removes them from the send queue
4230
4273
  */
4231
4274
  rejectTransactions(predicate, errorMsg = `The message has been removed from the queue`, errorCode = import_core.ZWaveErrorCodes.Controller_MessageDropped) {
4232
- this.reduceQueues((transaction, _source) => {
4275
+ return this.reduceQueues((transaction, _source) => {
4233
4276
  if (predicate(transaction)) {
4234
4277
  return {
4235
4278
  type: "reject",
@@ -4246,7 +4289,7 @@ ${handlers.length} left`);
4246
4289
  * Rejects all pending transactions for a node and removes them from the send queue
4247
4290
  */
4248
4291
  rejectAllTransactionsForNode(nodeId, errorMsg = `The node is dead`, errorCode = import_core.ZWaveErrorCodes.Controller_MessageDropped) {
4249
- this.rejectTransactions((t) => t.message.getNodeId() === nodeId, errorMsg, errorCode);
4292
+ return this.rejectTransactions((t) => t.message.getNodeId() === nodeId, errorMsg, errorCode);
4250
4293
  }
4251
4294
  /**
4252
4295
  * Pauses the send queue, avoiding commands to be sent to the controller
@@ -4262,9 +4305,7 @@ ${handlers.length} left`);
4262
4305
  this.triggerQueues();
4263
4306
  }
4264
4307
  reduceQueues(reducer) {
4265
- for (const queue of this.queues) {
4266
- this.reduceQueue(queue, reducer);
4267
- }
4308
+ return Promise.all(this.queues.map((queue) => this.reduceQueue(queue, reducer))).then(import_shared.noop);
4268
4309
  }
4269
4310
  reduceQueue(queue, reducer) {
4270
4311
  const dropQueued = [];
@@ -4329,8 +4370,9 @@ ${handlers.length} left`);
4329
4370
  t.setProgress({ state: import_core.TransactionState.Queued });
4330
4371
  }
4331
4372
  if ((0, import_serialapi.isSendData)(stopActive?.message)) {
4332
- void this.abortSendData();
4373
+ return this.abortSendData();
4333
4374
  }
4375
+ return Promise.resolve();
4334
4376
  }
4335
4377
  /** @internal */
4336
4378
  resolvePendingPings(nodeId) {
@@ -4615,7 +4657,8 @@ ${handlers.length} left`);
4615
4657
  } else if (this.controller.sdkVersionGte("6.50.0") && this.controller.supportedFunctionTypes?.includes(import_serial.FunctionType.FirmwareUpdateNVM)) {
4616
4658
  const wasUpdated = await this.firmwareUpdateOTW500(data);
4617
4659
  if (wasUpdated.success) {
4618
- await this.softResetAndRestart("Activating new firmware and restarting driver...", "Controller firmware updates require a driver restart!");
4660
+ this.driverLog.print("Activating new firmware and restarting driver...");
4661
+ await this.softResetAndRestart();
4619
4662
  }
4620
4663
  return wasUpdated;
4621
4664
  }
@@ -4684,7 +4727,6 @@ ${handlers.length} left`);
4684
4727
  }
4685
4728
  async firmwareUpdateOTW700(data) {
4686
4729
  this._otwFirmwareUpdateInProgress = true;
4687
- let destroy = false;
4688
4730
  try {
4689
4731
  await this.enterBootloader();
4690
4732
  this.controllerLog.print("Beginning firmware upload");
@@ -4734,7 +4776,6 @@ ${handlers.length} left`);
4734
4776
  progress: (0, import_math.roundTo)(fragment / numFragments * 100, 2)
4735
4777
  };
4736
4778
  this.emit("firmware update progress", progress);
4737
- destroy = true;
4738
4779
  continue transfer;
4739
4780
  }
4740
4781
  case import_serial.XModemMessageHeaders.NAK:
@@ -4791,7 +4832,7 @@ ${handlers.length} left`);
4791
4832
  this.emit("firmware update finished", result);
4792
4833
  return result;
4793
4834
  } finally {
4794
- await this.leaveBootloader(destroy);
4835
+ await this.leaveBootloader();
4795
4836
  this._otwFirmwareUpdateInProgress = false;
4796
4837
  }
4797
4838
  }
@@ -4824,12 +4865,10 @@ ${handlers.length} left`);
4824
4865
  }
4825
4866
  }
4826
4867
  async enterBootloaderFromSerialAPI() {
4827
- await this.scheduler.removeTasks(() => true, new import_core.ZWaveError("The controller is entering bootloader mode.", import_core.ZWaveErrorCodes.Driver_TaskRemoved));
4828
- this.rejectTransactions((_t) => true, "The controller is entering bootloader mode.");
4829
- await this.trySoftReset();
4830
- this.pauseSendQueue();
4831
- this.rejectTransactions((_t) => true, "The controller is entering bootloader mode.");
4832
- const promise = this.writeSerial(import_shared.Bytes.from("01030027db", "hex"));
4868
+ const ctx = this.getEncodingContext();
4869
+ await this.destroyController();
4870
+ const msg = new import_serialapi.EnterBootloaderRequest();
4871
+ const promise = this.writeSerial(await msg.serialize(ctx));
4833
4872
  this.serial.mode = import_serial.ZWaveSerialMode.Bootloader;
4834
4873
  await promise;
4835
4874
  }
@@ -4844,27 +4883,21 @@ ${handlers.length} left`);
4844
4883
  return promise;
4845
4884
  }
4846
4885
  /**
4847
- * Leaves the bootloader and destroys the driver instance if desired
4886
+ * Leaves the bootloader by running the application.
4848
4887
  */
4849
- async leaveBootloader(destroy = false) {
4888
+ async leaveBootloader() {
4850
4889
  this.controllerLog.print("Leaving bootloader...");
4851
4890
  await this.leaveBootloaderInternal();
4852
- await (0, import_async.wait)(1e3);
4853
- if (this.mode === import_DriverMode.DriverMode.CLI) {
4891
+ const isSerialAPI = await this.waitForMessage((msg) => msg.functionType === import_serial.FunctionType.SerialAPIStarted, 1e3).then(() => true).catch(() => false);
4892
+ if (isSerialAPI) {
4893
+ await this.initializeControllerAndNodes();
4894
+ return;
4895
+ } else if (this.mode === import_DriverMode.DriverMode.CLI) {
4854
4896
  await this.ensureCLIReady();
4855
4897
  return;
4856
4898
  }
4857
- if (destroy) {
4858
- const restartReason = "Restarting driver after OTW update...";
4859
- this.controllerLog.print(restartReason);
4860
- await this.destroy();
4861
- setImmediate(() => {
4862
- this.emit("error", new import_core.ZWaveError(restartReason, import_core.ZWaveErrorCodes.Driver_Failed));
4863
- });
4864
- } else {
4865
- this.unpauseSendQueue();
4866
- await this.ensureSerialAPI();
4867
- }
4899
+ this.unpauseSendQueue();
4900
+ await this.ensureSerialAPI();
4868
4901
  }
4869
4902
  serialport_onBootloaderData(data) {
4870
4903
  switch (data.type) {