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