zwave-js 13.2.0 → 13.3.1

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 (48) hide show
  1. package/bin/mock-server.js +2 -0
  2. package/build/lib/controller/Controller.d.ts +119 -9
  3. package/build/lib/controller/Controller.d.ts.map +1 -1
  4. package/build/lib/controller/Controller.js +1187 -81
  5. package/build/lib/controller/Controller.js.map +1 -1
  6. package/build/lib/controller/Inclusion.d.ts +44 -0
  7. package/build/lib/controller/Inclusion.d.ts.map +1 -1
  8. package/build/lib/controller/Inclusion.js +42 -1
  9. package/build/lib/controller/Inclusion.js.map +1 -1
  10. package/build/lib/driver/Driver.d.ts +16 -1
  11. package/build/lib/driver/Driver.d.ts.map +1 -1
  12. package/build/lib/driver/Driver.js +290 -97
  13. package/build/lib/driver/Driver.js.map +1 -1
  14. package/build/lib/driver/NetworkCache.d.ts +3 -0
  15. package/build/lib/driver/NetworkCache.d.ts.map +1 -1
  16. package/build/lib/driver/NetworkCache.js +37 -9
  17. package/build/lib/driver/NetworkCache.js.map +1 -1
  18. package/build/lib/driver/ZWaveOptions.d.ts +10 -3
  19. package/build/lib/driver/ZWaveOptions.d.ts.map +1 -1
  20. package/build/lib/driver/ZWaveOptions.js.map +1 -1
  21. package/build/lib/node/Node.d.ts +4 -3
  22. package/build/lib/node/Node.d.ts.map +1 -1
  23. package/build/lib/node/Node.js +130 -34
  24. package/build/lib/node/Node.js.map +1 -1
  25. package/build/lib/serialapi/application/ApplicationUpdateRequest.d.ts +5 -0
  26. package/build/lib/serialapi/application/ApplicationUpdateRequest.d.ts.map +1 -1
  27. package/build/lib/serialapi/application/ApplicationUpdateRequest.js +24 -1
  28. package/build/lib/serialapi/application/ApplicationUpdateRequest.js.map +1 -1
  29. package/build/lib/serialapi/capability/GetControllerCapabilitiesMessages.d.ts +2 -0
  30. package/build/lib/serialapi/capability/GetControllerCapabilitiesMessages.d.ts.map +1 -1
  31. package/build/lib/serialapi/capability/GetControllerCapabilitiesMessages.js +6 -0
  32. package/build/lib/serialapi/capability/GetControllerCapabilitiesMessages.js.map +1 -1
  33. package/build/lib/serialapi/capability/GetSerialApiInitDataMessages.d.ts +2 -9
  34. package/build/lib/serialapi/capability/GetSerialApiInitDataMessages.d.ts.map +1 -1
  35. package/build/lib/serialapi/capability/GetSerialApiInitDataMessages.js.map +1 -1
  36. package/build/lib/serialapi/capability/SerialAPISetupMessages.d.ts +27 -1
  37. package/build/lib/serialapi/capability/SerialAPISetupMessages.d.ts.map +1 -1
  38. package/build/lib/serialapi/capability/SerialAPISetupMessages.js +96 -1
  39. package/build/lib/serialapi/capability/SerialAPISetupMessages.js.map +1 -1
  40. package/build/lib/serialapi/network-mgmt/SetLearnModeMessages.d.ts +47 -0
  41. package/build/lib/serialapi/network-mgmt/SetLearnModeMessages.d.ts.map +1 -0
  42. package/build/lib/serialapi/network-mgmt/SetLearnModeMessages.js +137 -0
  43. package/build/lib/serialapi/network-mgmt/SetLearnModeMessages.js.map +1 -0
  44. package/build/lib/serialapi/nvm/ExtendedNVMOperationsMessages.d.ts +60 -0
  45. package/build/lib/serialapi/nvm/ExtendedNVMOperationsMessages.d.ts.map +1 -0
  46. package/build/lib/serialapi/nvm/ExtendedNVMOperationsMessages.js +201 -0
  47. package/build/lib/serialapi/nvm/ExtendedNVMOperationsMessages.js.map +1 -0
  48. package/package.json +9 -9
@@ -209,6 +209,17 @@ function checkOptions(options) {
209
209
  throw new core_1.ZWaveError(`The inclusionUserCallbacks must contain the following functions: grantSecurityClasses, validateDSKAndEnterPIN, abort!`, core_1.ZWaveErrorCodes.Driver_InvalidOptions);
210
210
  }
211
211
  }
212
+ if (options.joinNetworkUserCallbacks) {
213
+ if (!(0, typeguards_1.isObject)(options.joinNetworkUserCallbacks)) {
214
+ throw new core_1.ZWaveError(`The joinNetworkUserCallbacks must be an object!`, core_1.ZWaveErrorCodes.Driver_InvalidOptions);
215
+ }
216
+ else if (typeof options.joinNetworkUserCallbacks.showDSK
217
+ !== "function"
218
+ || typeof options.joinNetworkUserCallbacks.done
219
+ !== "function") {
220
+ throw new core_1.ZWaveError(`The joinNetworkUserCallbacks must contain the following functions: showDSK, done!`, core_1.ZWaveErrorCodes.Driver_InvalidOptions);
221
+ }
222
+ }
212
223
  if (options.rf != undefined) {
213
224
  if (options.rf.region != undefined) {
214
225
  if (typeof options.rf.region !== "number"
@@ -450,6 +461,24 @@ class Driver extends shared_1.TypedEventEmitter {
450
461
  const isLongRange = (0, core_1.isLongRangeNodeId)(nodeId);
451
462
  return isLongRange ? this.securityManagerLR : this.securityManager2;
452
463
  }
464
+ _learnModeAuthenticatedKeyPair;
465
+ /** @internal */
466
+ getLearnModeAuthenticatedKeyPair() {
467
+ if (this._learnModeAuthenticatedKeyPair == undefined) {
468
+ // Try restoring from cache
469
+ const privateKey = this.cacheGet(NetworkCache_1.cacheKeys.controller.privateKey);
470
+ if (privateKey) {
471
+ this._learnModeAuthenticatedKeyPair =
472
+ (0, core_1.keyPairFromRawECDHPrivateKey)(privateKey);
473
+ }
474
+ else {
475
+ // Not found in cache, create a new one and cache it
476
+ this._learnModeAuthenticatedKeyPair = (0, core_1.generateECDHKeyPair)();
477
+ this.cacheSet(NetworkCache_1.cacheKeys.controller.privateKey, (0, core_1.extractRawECDHPrivateKey)(this._learnModeAuthenticatedKeyPair.privateKey));
478
+ }
479
+ }
480
+ return this._learnModeAuthenticatedKeyPair;
481
+ }
453
482
  /**
454
483
  * **!!! INTERNAL !!!**
455
484
  *
@@ -604,13 +633,16 @@ class Driver extends shared_1.TypedEventEmitter {
604
633
  "disableOptimisticValueUpdate",
605
634
  "emitValueUpdateAfterSetValue",
606
635
  "inclusionUserCallbacks",
636
+ "joinNetworkUserCallbacks",
607
637
  "interview",
608
638
  "preferences",
609
639
  "vendor",
610
640
  ]);
611
641
  // Create a new deep-merged copy of the options so we can check them for validity
612
- // without affecting our own options
613
- const newOptions = (0, shared_1.mergeDeep)((0, shared_1.cloneDeep)(this._options), safeOptions, true);
642
+ // without affecting our own options. logConfig is potentially unsafe to clone, so just preserve it.
643
+ const { logConfig, ...rest } = this._options;
644
+ const newOptions = (0, shared_1.mergeDeep)((0, shared_1.cloneDeep)(rest), safeOptions, true);
645
+ newOptions.logConfig = logConfig;
614
646
  checkOptions(newOptions);
615
647
  if (options.userAgent && !(0, typeguards_1.isObject)(options.userAgent)) {
616
648
  throw new core_1.ZWaveError(`The userAgent property must be an object!`, core_1.ZWaveErrorCodes.Driver_InvalidOptions);
@@ -903,7 +935,10 @@ class Driver extends shared_1.TypedEventEmitter {
903
935
  this._controller
904
936
  .on("node added", this.onNodeAdded.bind(this))
905
937
  .on("node removed", this.onNodeRemoved.bind(this))
906
- .on("status changed", this.onControllerStatusChanged.bind(this));
938
+ .on("status changed", this.onControllerStatusChanged.bind(this))
939
+ .on("network found", this.onNetworkFound.bind(this))
940
+ .on("network joined", this.onNetworkJoined.bind(this))
941
+ .on("network left", this.onNetworkLeft.bind(this));
907
942
  }
908
943
  if (!this._options.testingHooks?.skipControllerIdentification) {
909
944
  // Determine what the controller can do
@@ -942,66 +977,128 @@ class Driver extends shared_1.TypedEventEmitter {
942
977
  await this.initValueDBs(this.controller.homeId);
943
978
  await this.performCacheMigration();
944
979
  // Initialize all nodes and restore the data from cache
945
- await this._controller.initNodes(nodeIds, lrNodeIds ?? [], async () => {
980
+ await this.controller.initNodes(nodeIds, lrNodeIds ?? [], async () => {
946
981
  // Try to restore the network information from the cache
947
982
  if (process.env.NO_CACHE !== "true") {
948
983
  await this.restoreNetworkStructureFromCache();
949
984
  }
950
985
  });
951
- // Auto-enable smart start inclusion
952
- this._controller.autoProvisionSmartStart();
953
- }
954
- // Set up the S0 security manager. We can only do that after the controller
955
- // interview because we need to know the controller node id.
956
- const S0Key = this._options.securityKeys?.S0_Legacy;
957
- if (S0Key) {
958
- this.driverLog.print("Network key for S0 configured, enabling S0 security manager...");
959
- this._securityManager = new core_1.SecurityManager({
960
- networkKey: S0Key,
961
- ownNodeId: this._controller.ownNodeId,
962
- nonceTimeout: this._options.timeouts.nonce,
963
- });
986
+ if (this.controller.role === core_1.ControllerRole.Primary) {
987
+ // Auto-enable smart start inclusion
988
+ this.controller.autoProvisionSmartStart();
989
+ }
964
990
  }
965
991
  else {
966
- this.driverLog.print("No network key for S0 configured, communication with secure (S0) devices won't work!", "warn");
967
- }
968
- // The S2 security manager could be initialized earlier, but we do it here for consistency
969
- if (this._options.securityKeys
970
- // Only set it up if we have security keys for at least one S2 security class
971
- && Object.keys(this._options.securityKeys).some((key) => key.startsWith("S2_")
972
- && key in core_1.SecurityClass
973
- && (0, core_1.securityClassIsS2)(core_1.SecurityClass[key]))) {
974
- this.driverLog.print("At least one network key for S2 configured, enabling S2 security manager...");
975
- this._securityManager2 = new core_1.SecurityManager2();
976
- // Set up all keys
977
- for (const secClass of [
978
- "S2_Unauthenticated",
979
- "S2_Authenticated",
980
- "S2_AccessControl",
981
- "S0_Legacy",
982
- ]) {
983
- const key = this._options.securityKeys[secClass];
984
- if (key) {
985
- this._securityManager2.setKey(core_1.SecurityClass[secClass], key);
992
+ // When skipping the controller identification, set the flags to consider the controller a primary
993
+ this.controller["_wasRealPrimary"] = true;
994
+ this.controller["_isSUC"] = true;
995
+ this.controller["_isSISPresent"] = true;
996
+ this.controller["_sucNodeId"] = 1;
997
+ }
998
+ if (this.controller.role === core_1.ControllerRole.Primary) {
999
+ // Set up the S0 security manager. We can only do that after the controller
1000
+ // interview because we need to know the controller node id.
1001
+ const S0Key = this._options.securityKeys?.S0_Legacy;
1002
+ if (S0Key) {
1003
+ this.driverLog.print("Network key for S0 configured, enabling S0 security manager...");
1004
+ this._securityManager = new core_1.SecurityManager({
1005
+ networkKey: S0Key,
1006
+ ownNodeId: this._controller.ownNodeId,
1007
+ nonceTimeout: this._options.timeouts.nonce,
1008
+ });
1009
+ }
1010
+ else {
1011
+ this.driverLog.print("No network key for S0 configured, communication with secure (S0) devices won't work!", "warn");
1012
+ }
1013
+ // The S2 security manager could be initialized earlier, but we do it here for consistency
1014
+ if (this._options.securityKeys
1015
+ // Only set it up if we have security keys for at least one S2 security class
1016
+ && Object.keys(this._options.securityKeys).some((key) => key.startsWith("S2_")
1017
+ && key in core_1.SecurityClass
1018
+ && (0, core_1.securityClassIsS2)(core_1.SecurityClass[key]))) {
1019
+ this.driverLog.print("At least one network key for S2 configured, enabling S2 security manager...");
1020
+ this._securityManager2 = new core_1.SecurityManager2();
1021
+ // Set up all keys
1022
+ for (const secClass of [
1023
+ "S2_Unauthenticated",
1024
+ "S2_Authenticated",
1025
+ "S2_AccessControl",
1026
+ "S0_Legacy",
1027
+ ]) {
1028
+ const key = this._options.securityKeys[secClass];
1029
+ if (key) {
1030
+ this._securityManager2.setKey(core_1.SecurityClass[secClass], key);
1031
+ }
986
1032
  }
987
1033
  }
988
- }
989
- else {
990
- this.driverLog.print("No network key for S2 configured, communication with secure (S2) devices won't work!", "warn");
991
- }
992
- if (this._options.securityKeysLongRange?.S2_AccessControl
993
- || this._options.securityKeysLongRange?.S2_Authenticated) {
994
- this.driverLog.print("At least one network key for Z-Wave Long Range configured, enabling security manager...");
995
- this._securityManagerLR = new core_1.SecurityManager2();
996
- if (this._options.securityKeysLongRange?.S2_AccessControl) {
997
- this._securityManagerLR.setKey(core_1.SecurityClass.S2_AccessControl, this._options.securityKeysLongRange.S2_AccessControl);
1034
+ else {
1035
+ this.driverLog.print("No network key for S2 configured, communication with secure (S2) devices won't work!", "warn");
1036
+ }
1037
+ if (this._options.securityKeysLongRange?.S2_AccessControl
1038
+ || this._options.securityKeysLongRange?.S2_Authenticated) {
1039
+ this.driverLog.print("At least one network key for Z-Wave Long Range configured, enabling security manager...");
1040
+ this._securityManagerLR = new core_1.SecurityManager2();
1041
+ if (this._options.securityKeysLongRange?.S2_AccessControl) {
1042
+ this._securityManagerLR.setKey(core_1.SecurityClass.S2_AccessControl, this._options.securityKeysLongRange.S2_AccessControl);
1043
+ }
1044
+ if (this._options.securityKeysLongRange?.S2_Authenticated) {
1045
+ this._securityManagerLR.setKey(core_1.SecurityClass.S2_Authenticated, this._options.securityKeysLongRange.S2_Authenticated);
1046
+ }
998
1047
  }
999
- if (this._options.securityKeysLongRange?.S2_Authenticated) {
1000
- this._securityManagerLR.setKey(core_1.SecurityClass.S2_Authenticated, this._options.securityKeysLongRange.S2_Authenticated);
1048
+ else {
1049
+ this.driverLog.print("No network key for Z-Wave Long Range configured, communication won't work!", "warn");
1001
1050
  }
1002
1051
  }
1003
1052
  else {
1004
- this.driverLog.print("No network key for Z-Wave Long Range configured, communication won't work!", "warn");
1053
+ // Secondary controller - load security keys from cache.
1054
+ // Either LR or S2+S0, not both
1055
+ if ((0, core_1.isLongRangeNodeId)(this.controller.ownNodeId)) {
1056
+ const securityKeysLongRange = [
1057
+ core_1.SecurityClass.S2_AccessControl,
1058
+ core_1.SecurityClass.S2_Authenticated,
1059
+ ].map((sc) => [
1060
+ sc,
1061
+ this.cacheGet(NetworkCache_1.cacheKeys.controller.securityKeysLongRange(sc)),
1062
+ ]).filter((v) => v[1] != undefined);
1063
+ if (securityKeysLongRange.length) {
1064
+ this.driverLog.print("At least one network key for Z-Wave Long Range found in cache, enabling security manager...");
1065
+ this._securityManagerLR = new core_1.SecurityManager2();
1066
+ for (const [sc, key] of securityKeysLongRange) {
1067
+ this._securityManagerLR.setKey(sc, key);
1068
+ }
1069
+ }
1070
+ else {
1071
+ this.driverLog.print("No network key for Z-Wave Long Range configured, communication won't work!", "warn");
1072
+ }
1073
+ }
1074
+ else {
1075
+ const s0Key = this.cacheGet(NetworkCache_1.cacheKeys.controller.securityKeys(core_1.SecurityClass.S0_Legacy));
1076
+ if (s0Key) {
1077
+ this.driverLog.print("Network key for S0 found in cache, enabling S0 security manager...");
1078
+ this._securityManager = new core_1.SecurityManager({
1079
+ networkKey: s0Key,
1080
+ ownNodeId: this._controller.ownNodeId,
1081
+ nonceTimeout: this._options.timeouts.nonce,
1082
+ });
1083
+ }
1084
+ else {
1085
+ this.driverLog.print("No network key for S0 found in cache, communication with secure (S0) devices won't work!", "warn");
1086
+ }
1087
+ const securityKeys = core_1.securityClassOrder.map((sc) => [
1088
+ sc,
1089
+ this.cacheGet(NetworkCache_1.cacheKeys.controller.securityKeys(sc)),
1090
+ ]).filter((v) => v[1] != undefined);
1091
+ if (securityKeys.length) {
1092
+ this.driverLog.print("At least one network key for S2 found in cache, enabling S2 security manager...");
1093
+ this._securityManager2 = new core_1.SecurityManager2();
1094
+ for (const [sc, key] of securityKeys) {
1095
+ this._securityManager2.setKey(sc, key);
1096
+ }
1097
+ }
1098
+ else {
1099
+ this.driverLog.print("No network key for S2 found in cache, communication with secure (S2) devices won't work!", "warn");
1100
+ }
1101
+ }
1005
1102
  }
1006
1103
  // in any case we need to emit the driver ready event here
1007
1104
  this._controllerInterviewed = true;
@@ -1011,56 +1108,96 @@ class Driver extends shared_1.TypedEventEmitter {
1011
1108
  for (const node of this._controller.nodes.values()) {
1012
1109
  this.addNodeEventHandlers(node);
1013
1110
  }
1014
- // Before interviewing nodes reset our knowledge about their ready state
1015
- this._nodesReady.clear();
1016
- this._nodesReadyEventEmitted = false;
1017
- if (!this._options.testingHooks?.skipNodeInterview) {
1018
- // Now interview all nodes
1019
- // First complete the controller interview
1020
- const controllerNode = this._controller.nodes.get(this._controller.ownNodeId);
1021
- await this.interviewNodeInternal(controllerNode);
1022
- // The controller node is always alive
1023
- controllerNode.markAsAlive();
1024
- // Then do all the nodes in parallel, but prioritize nodes that are more likely to be ready
1025
- const nodeInterviewOrder = [...this._controller.nodes.values()]
1026
- .filter((n) => n.id !== this._controller.ownNodeId)
1027
- .sort((a, b) =>
1028
- // Fully-interviewed devices first (need the least amount of communication now)
1029
- (b.interviewStage - a.interviewStage)
1030
- // Always listening -> FLiRS -> sleeping
1031
- || ((b.isListening ? 2 : b.isFrequentListening ? 1 : 0)
1032
- - (a.isListening ? 2 : a.isFrequentListening ? 1 : 0))
1033
- // Then by last seen, more recently first
1034
- || ((b.lastSeen?.getTime() ?? 0)
1035
- - (a.lastSeen?.getTime() ?? 0))
1036
- // Lastly ascending by node ID
1037
- || (a.id - b.id));
1038
- this.controllerLog.print(`Interviewing nodes and/or determining their status: ${nodeInterviewOrder.map((n) => n.id).join(", ")}`);
1039
- for (const node of nodeInterviewOrder) {
1040
- if (node.id === this._controller.ownNodeId) {
1041
- continue;
1111
+ if (this.controller.role === core_1.ControllerRole.Primary) {
1112
+ // Before interviewing nodes reset our knowledge about their ready state
1113
+ this._nodesReady.clear();
1114
+ this._nodesReadyEventEmitted = false;
1115
+ if (!this._options.testingHooks?.skipNodeInterview) {
1116
+ // Now interview all nodes
1117
+ // First complete the controller interview
1118
+ const controllerNode = this._controller.nodes.get(this._controller.ownNodeId);
1119
+ await this.interviewNodeInternal(controllerNode);
1120
+ // The controller node is always alive
1121
+ controllerNode.markAsAlive();
1122
+ // Then do all the nodes in parallel, but prioritize nodes that are more likely to be ready
1123
+ const nodeInterviewOrder = [...this._controller.nodes.values()]
1124
+ .filter((n) => n.id !== this._controller.ownNodeId)
1125
+ .sort((a, b) =>
1126
+ // Fully-interviewed devices first (need the least amount of communication now)
1127
+ (b.interviewStage - a.interviewStage)
1128
+ // Always listening -> FLiRS -> sleeping
1129
+ || ((b.isListening ? 2 : b.isFrequentListening ? 1 : 0)
1130
+ - (a.isListening
1131
+ ? 2
1132
+ : a.isFrequentListening
1133
+ ? 1
1134
+ : 0))
1135
+ // Then by last seen, more recently first
1136
+ || ((b.lastSeen?.getTime() ?? 0)
1137
+ - (a.lastSeen?.getTime() ?? 0))
1138
+ // Lastly ascending by node ID
1139
+ || (a.id - b.id));
1140
+ if (nodeInterviewOrder.length) {
1141
+ this.controllerLog.print(`Interviewing nodes and/or determining their status: ${nodeInterviewOrder.map((n) => n.id).join(", ")}`);
1142
+ for (const node of nodeInterviewOrder) {
1143
+ if (node.canSleep) {
1144
+ // A node that can sleep should be assumed to be sleeping after resuming from cache
1145
+ node.markAsAsleep();
1146
+ }
1147
+ void (async () => {
1148
+ // Continue the interview if necessary. If that is not necessary, at least
1149
+ // determine the node's status
1150
+ if (node.interviewStage < _Types_1.InterviewStage.Complete) {
1151
+ await this.interviewNodeInternal(node);
1152
+ }
1153
+ else if (node.isListening || node.isFrequentListening) {
1154
+ // Ping non-sleeping nodes to determine their status
1155
+ await node.ping();
1156
+ }
1157
+ })();
1158
+ }
1042
1159
  }
1043
- else if (node.canSleep) {
1044
- // A node that can sleep should be assumed to be sleeping after resuming from cache
1045
- node.markAsAsleep();
1160
+ }
1161
+ }
1162
+ else {
1163
+ if (!this._options.testingHooks?.skipNodeInterview) {
1164
+ // We're a secondary controller. Just determine if nodes are ready and do the interview at another time.
1165
+ // First complete the controller "interview"
1166
+ const controllerNode = this._controller.nodes.get(this._controller.ownNodeId);
1167
+ await this.interviewNodeInternal(controllerNode);
1168
+ // The controller node is always alive
1169
+ controllerNode.markAsAlive();
1170
+ // Query the protocol information from the controller
1171
+ for (const node of this._controller.nodes.values()) {
1172
+ if (node.isControllerNode)
1173
+ continue;
1174
+ await node["queryProtocolInfo"]();
1046
1175
  }
1047
- void (async () => {
1048
- // Continue the interview if necessary. If that is not necessary, at least
1049
- // determine the node's status
1050
- if (node.interviewStage < _Types_1.InterviewStage.Complete) {
1051
- await this.interviewNodeInternal(node);
1052
- }
1053
- else if (node.isListening || node.isFrequentListening) {
1054
- // Ping non-sleeping nodes to determine their status
1055
- await node.ping();
1176
+ // Then ping (frequently) listening nodes to determine their status
1177
+ const nodeInterviewOrder = [...this._controller.nodes.values()]
1178
+ .filter((n) => n.id !== this._controller.ownNodeId)
1179
+ .filter((n) => n.isListening || n.isFrequentListening)
1180
+ .sort((a, b) =>
1181
+ // Always listening -> FLiRS
1182
+ ((b.isListening ? 1 : 0)
1183
+ - (a.isListening ? 1 : 0))
1184
+ // Then by last seen, more recently first
1185
+ || ((b.lastSeen?.getTime() ?? 0)
1186
+ - (a.lastSeen?.getTime() ?? 0))
1187
+ // Lastly ascending by node ID
1188
+ || (a.id - b.id));
1189
+ if (nodeInterviewOrder.length) {
1190
+ this.controllerLog.print(`Determining node status: ${nodeInterviewOrder.map((n) => n.id).join(", ")}`);
1191
+ for (const node of nodeInterviewOrder) {
1192
+ void node.ping();
1056
1193
  }
1057
- })();
1194
+ }
1058
1195
  }
1059
- // If we only have sleeping nodes or a controller-only network, the send
1060
- // thread is idle before the driver gets marked ready, the idle tasks won't be triggered.
1061
- // So do it manually.
1062
- this.handleQueueIdleChange(this.queueIdle);
1063
1196
  }
1197
+ // If we only have sleeping nodes or a controller-only network, the send
1198
+ // thread is idle before the driver gets marked ready, the idle tasks won't be triggered.
1199
+ // So do it manually.
1200
+ this.handleQueueIdleChange(this.queueIdle);
1064
1201
  }
1065
1202
  autoRefreshNodeValueTimers = new Map();
1066
1203
  retryNodeInterviewTimeouts = new Map();
@@ -1451,6 +1588,36 @@ class Driver extends shared_1.TypedEventEmitter {
1451
1588
  onControllerStatusChanged(_status) {
1452
1589
  this.triggerQueues();
1453
1590
  }
1591
+ async onNetworkFound(homeId, _ownNodeId) {
1592
+ try {
1593
+ this.driverLog.print(`Joined network with home ID ${(0, shared_1.num2hex)(homeId)}, switching to new network cache...`);
1594
+ await this.recreateNetworkCacheAndValueDBs();
1595
+ }
1596
+ catch (e) {
1597
+ this.driverLog.print(`Recreating the network cache and value DBs failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
1598
+ }
1599
+ }
1600
+ onNetworkJoined() {
1601
+ this.driverLog.print(`Finished joining network`);
1602
+ }
1603
+ async onNetworkLeft() {
1604
+ try {
1605
+ this.driverLog.print(`Left the previous network, switching network cache to new home ID ${(0, shared_1.num2hex)(this.controller.homeId)}...`);
1606
+ await this.recreateNetworkCacheAndValueDBs();
1607
+ }
1608
+ catch (e) {
1609
+ this.driverLog.print(`Recreating the network cache and value DBs failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
1610
+ }
1611
+ }
1612
+ async recreateNetworkCacheAndValueDBs() {
1613
+ await this._networkCache?.close();
1614
+ await this._valueDB?.close();
1615
+ await this._metadataDB?.close();
1616
+ // Reopen with the new home ID
1617
+ await this.initNetworkCache(this.controller.homeId);
1618
+ await this.initValueDBs(this.controller.homeId);
1619
+ await this.performCacheMigration();
1620
+ }
1454
1621
  /**
1455
1622
  * Returns the time in seconds to actually wait after a firmware upgrade, depending on what the device said.
1456
1623
  * This number will always be a bit greater than the advertised duration, because devices have been found to take longer to actually reboot.
@@ -1897,6 +2064,8 @@ class Driver extends shared_1.TypedEventEmitter {
1897
2064
  this.controllerLog.print(message, "error");
1898
2065
  throw new core_1.ZWaveError(message, core_1.ZWaveErrorCodes.FirmwareUpdateCC_NetworkBusy);
1899
2066
  }
2067
+ // Preserve the private key for the authenticated learn mode ECDH key pair
2068
+ const oldPrivateKey = this.cacheGet(NetworkCache_1.cacheKeys.controller.privateKey);
1900
2069
  // Update the controller NIF prior to hard resetting
1901
2070
  await this.controller.setControllerNIF();
1902
2071
  await this.controller.hardReset();
@@ -1914,6 +2083,12 @@ class Driver extends shared_1.TypedEventEmitter {
1914
2083
  }
1915
2084
  this._controllerInterviewed = false;
1916
2085
  void this.initializeControllerAndNodes();
2086
+ // Save the key pair in the new cache again
2087
+ if (oldPrivateKey) {
2088
+ this.once("driver ready", () => {
2089
+ this.cacheSet(NetworkCache_1.cacheKeys.controller.privateKey, oldPrivateKey);
2090
+ });
2091
+ }
1917
2092
  }
1918
2093
  /**
1919
2094
  * Instructs the Z-Wave API to shut down in order to safely remove the power.
@@ -4681,7 +4856,7 @@ ${handlers.length} left`);
4681
4856
  }
4682
4857
  }
4683
4858
  /** Computes the maximum net CC payload size for the given CC or SendDataRequest */
4684
- computeNetCCPayloadSize(commandOrMsg) {
4859
+ computeNetCCPayloadSize(commandOrMsg, ignoreEncapsulation = false) {
4685
4860
  // Recreate the correct encapsulation structure
4686
4861
  let msg;
4687
4862
  if ((0, SendDataShared_1.isSendDataSinglecast)(commandOrMsg)) {
@@ -4691,7 +4866,9 @@ ${handlers.length} left`);
4691
4866
  const SendDataConstructor = this.getSendDataSinglecastConstructor();
4692
4867
  msg = new SendDataConstructor(this, { command: commandOrMsg });
4693
4868
  }
4694
- msg.command = this.encapsulateCommands(msg.command);
4869
+ if (!ignoreEncapsulation) {
4870
+ msg.command = this.encapsulateCommands(msg.command);
4871
+ }
4695
4872
  return msg.command.getMaxPayloadLength(this.getMaxPayloadLength(msg));
4696
4873
  }
4697
4874
  /** Computes the maximum payload size that can be transmitted with the given message */
@@ -5062,6 +5239,22 @@ ${handlers.length} left`);
5062
5239
  };
5063
5240
  }
5064
5241
  }
5242
+ /**
5243
+ * Resets the S2 singlecast encryption state (SPAN) for the given node, which forces
5244
+ * a re-synchronization on the next communication attempt.
5245
+ */
5246
+ resetSPAN(nodeId) {
5247
+ this.getSecurityManager2(nodeId)?.deleteNonce(nodeId);
5248
+ }
5249
+ /**
5250
+ * Resets the S2 singlecast encryption state (SPAN) for all nodes, which forces
5251
+ * a re-synchronization on the next communication attempt.
5252
+ */
5253
+ resetAllSPANs() {
5254
+ for (const nodeId of this.controller.nodes.keys()) {
5255
+ this.resetSPAN(nodeId);
5256
+ }
5257
+ }
5065
5258
  }
5066
5259
  exports.Driver = Driver;
5067
5260
  //# sourceMappingURL=Driver.js.map