zwave-js 11.0.0 → 11.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/lib/controller/Controller.d.ts +86 -3
- package/build/lib/controller/Controller.d.ts.map +1 -1
- package/build/lib/controller/Controller.js +391 -164
- package/build/lib/controller/Controller.js.map +1 -1
- package/build/lib/controller/Features.js +1 -1
- package/build/lib/controller/Features.js.map +1 -1
- package/build/lib/controller/Inclusion.js +6 -6
- package/build/lib/controller/Inclusion.js.map +1 -1
- package/build/lib/controller/MockControllerBehaviors.d.ts.map +1 -1
- package/build/lib/controller/MockControllerBehaviors.js +3 -2
- package/build/lib/controller/MockControllerBehaviors.js.map +1 -1
- package/build/lib/controller/MockControllerState.js +2 -2
- package/build/lib/controller/MockControllerState.js.map +1 -1
- package/build/lib/controller/NodeInformationFrame.d.ts.map +1 -1
- package/build/lib/controller/NodeInformationFrame.js +6 -1
- package/build/lib/controller/NodeInformationFrame.js.map +1 -1
- package/build/lib/controller/ZWaveSDKVersions.js +1 -1
- package/build/lib/controller/ZWaveSDKVersions.js.map +1 -1
- package/build/lib/controller/_Types.js +1 -1
- package/build/lib/controller/_Types.js.map +1 -1
- package/build/lib/driver/Bootloader.js +1 -1
- package/build/lib/driver/Bootloader.js.map +1 -1
- package/build/lib/driver/Driver.d.ts +44 -12
- package/build/lib/driver/Driver.d.ts.map +1 -1
- package/build/lib/driver/Driver.js +495 -207
- package/build/lib/driver/Driver.js.map +1 -1
- package/build/lib/driver/MessageGenerators.d.ts.map +1 -1
- package/build/lib/driver/MessageGenerators.js +4 -11
- package/build/lib/driver/MessageGenerators.js.map +1 -1
- package/build/lib/driver/NetworkCache.d.ts +5 -0
- package/build/lib/driver/NetworkCache.d.ts.map +1 -1
- package/build/lib/driver/NetworkCache.js +5 -0
- package/build/lib/driver/NetworkCache.js.map +1 -1
- package/build/lib/driver/SerialAPICommandMachine.d.ts +17 -11
- package/build/lib/driver/SerialAPICommandMachine.d.ts.map +1 -1
- package/build/lib/driver/SerialAPICommandMachine.js +16 -6
- package/build/lib/driver/SerialAPICommandMachine.js.map +1 -1
- package/build/lib/driver/StateMachineShared.d.ts +27 -52
- package/build/lib/driver/StateMachineShared.d.ts.map +1 -1
- package/build/lib/driver/StateMachineShared.js +16 -48
- package/build/lib/driver/StateMachineShared.js.map +1 -1
- package/build/lib/node/Node.d.ts +11 -0
- package/build/lib/node/Node.d.ts.map +1 -1
- package/build/lib/node/Node.js +223 -8
- package/build/lib/node/Node.js.map +1 -1
- package/build/lib/node/NodeReadyMachine.d.ts +2 -2
- package/build/lib/node/NodeReadyMachine.d.ts.map +1 -1
- package/build/lib/node/NodeReadyMachine.js.map +1 -1
- package/build/lib/node/NodeStatusMachine.d.ts +2 -2
- package/build/lib/node/NodeStatusMachine.d.ts.map +1 -1
- package/build/lib/node/NodeStatusMachine.js.map +1 -1
- package/build/lib/serialapi/_Types.js +1 -1
- package/build/lib/serialapi/_Types.js.map +1 -1
- package/build/lib/serialapi/application/ApplicationCommandRequest.js +3 -4
- package/build/lib/serialapi/application/ApplicationCommandRequest.js.map +1 -1
- package/build/lib/serialapi/application/ApplicationUpdateRequest.js +13 -21
- package/build/lib/serialapi/application/ApplicationUpdateRequest.js.map +1 -1
- package/build/lib/serialapi/application/BridgeApplicationCommandRequest.js +2 -3
- package/build/lib/serialapi/application/BridgeApplicationCommandRequest.js.map +1 -1
- package/build/lib/serialapi/application/SerialAPIStartedRequest.js +3 -4
- package/build/lib/serialapi/application/SerialAPIStartedRequest.js.map +1 -1
- package/build/lib/serialapi/application/ShutdownMessages.js +4 -6
- package/build/lib/serialapi/application/ShutdownMessages.js.map +1 -1
- package/build/lib/serialapi/capability/GetControllerCapabilitiesMessages.js +4 -6
- package/build/lib/serialapi/capability/GetControllerCapabilitiesMessages.js.map +1 -1
- package/build/lib/serialapi/capability/GetControllerVersionMessages.js +4 -6
- package/build/lib/serialapi/capability/GetControllerVersionMessages.js.map +1 -1
- package/build/lib/serialapi/capability/GetProtocolVersionMessages.js +4 -6
- package/build/lib/serialapi/capability/GetProtocolVersionMessages.js.map +1 -1
- package/build/lib/serialapi/capability/GetSerialApiCapabilitiesMessages.js +4 -6
- package/build/lib/serialapi/capability/GetSerialApiCapabilitiesMessages.js.map +1 -1
- package/build/lib/serialapi/capability/GetSerialApiInitDataMessages.js +4 -6
- package/build/lib/serialapi/capability/GetSerialApiInitDataMessages.js.map +1 -1
- package/build/lib/serialapi/capability/HardResetRequest.js +4 -6
- package/build/lib/serialapi/capability/HardResetRequest.js.map +1 -1
- package/build/lib/serialapi/capability/SerialAPISetupMessages.js +59 -88
- package/build/lib/serialapi/capability/SerialAPISetupMessages.js.map +1 -1
- package/build/lib/serialapi/capability/SetApplicationNodeInformationRequest.js +2 -3
- package/build/lib/serialapi/capability/SetApplicationNodeInformationRequest.js.map +1 -1
- package/build/lib/serialapi/memory/GetControllerIdMessages.js +4 -6
- package/build/lib/serialapi/memory/GetControllerIdMessages.js.map +1 -1
- package/build/lib/serialapi/misc/GetBackgroundRSSIMessages.js +4 -6
- package/build/lib/serialapi/misc/GetBackgroundRSSIMessages.js.map +1 -1
- package/build/lib/serialapi/misc/SetRFReceiveModeMessages.js +4 -6
- package/build/lib/serialapi/misc/SetRFReceiveModeMessages.js.map +1 -1
- package/build/lib/serialapi/misc/SetSerialApiTimeoutsMessages.js +4 -6
- package/build/lib/serialapi/misc/SetSerialApiTimeoutsMessages.js.map +1 -1
- package/build/lib/serialapi/misc/SoftResetRequest.js +2 -3
- package/build/lib/serialapi/misc/SoftResetRequest.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/AddNodeToNetworkRequest.js +6 -8
- package/build/lib/serialapi/network-mgmt/AddNodeToNetworkRequest.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/AssignPriorityReturnRouteMessages.js +6 -9
- package/build/lib/serialapi/network-mgmt/AssignPriorityReturnRouteMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/AssignPrioritySUCReturnRouteMessages.js +6 -9
- package/build/lib/serialapi/network-mgmt/AssignPrioritySUCReturnRouteMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/AssignReturnRouteMessages.js +6 -9
- package/build/lib/serialapi/network-mgmt/AssignReturnRouteMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/AssignSUCReturnRouteMessages.js +6 -9
- package/build/lib/serialapi/network-mgmt/AssignSUCReturnRouteMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/DeleteReturnRouteMessages.js +6 -9
- package/build/lib/serialapi/network-mgmt/DeleteReturnRouteMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/DeleteSUCReturnRouteMessages.js +6 -9
- package/build/lib/serialapi/network-mgmt/DeleteSUCReturnRouteMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/GetNodeProtocolInfoMessages.js +4 -6
- package/build/lib/serialapi/network-mgmt/GetNodeProtocolInfoMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/GetPriorityRouteMessages.js +4 -6
- package/build/lib/serialapi/network-mgmt/GetPriorityRouteMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/GetRoutingInfoMessages.js +4 -6
- package/build/lib/serialapi/network-mgmt/GetRoutingInfoMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/GetSUCNodeIdMessages.js +4 -6
- package/build/lib/serialapi/network-mgmt/GetSUCNodeIdMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/IsFailedNodeMessages.js +4 -6
- package/build/lib/serialapi/network-mgmt/IsFailedNodeMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/RemoveFailedNodeMessages.js +8 -11
- package/build/lib/serialapi/network-mgmt/RemoveFailedNodeMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/RemoveNodeFromNetworkRequest.js +6 -8
- package/build/lib/serialapi/network-mgmt/RemoveNodeFromNetworkRequest.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/ReplaceFailedNodeRequest.js +8 -11
- package/build/lib/serialapi/network-mgmt/ReplaceFailedNodeRequest.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/RequestNodeInfoMessages.js +4 -6
- package/build/lib/serialapi/network-mgmt/RequestNodeInfoMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/RequestNodeNeighborUpdateMessages.js +5 -7
- package/build/lib/serialapi/network-mgmt/RequestNodeNeighborUpdateMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/SetPriorityRouteMessages.js +4 -6
- package/build/lib/serialapi/network-mgmt/SetPriorityRouteMessages.js.map +1 -1
- package/build/lib/serialapi/network-mgmt/SetSUCNodeIDMessages.js +7 -10
- package/build/lib/serialapi/network-mgmt/SetSUCNodeIDMessages.js.map +1 -1
- package/build/lib/serialapi/nvm/ExtNVMReadLongBufferMessages.js +4 -6
- package/build/lib/serialapi/nvm/ExtNVMReadLongBufferMessages.js.map +1 -1
- package/build/lib/serialapi/nvm/ExtNVMReadLongByteMessages.js +4 -6
- package/build/lib/serialapi/nvm/ExtNVMReadLongByteMessages.js.map +1 -1
- package/build/lib/serialapi/nvm/ExtNVMWriteLongBufferMessages.js +4 -6
- package/build/lib/serialapi/nvm/ExtNVMWriteLongBufferMessages.js.map +1 -1
- package/build/lib/serialapi/nvm/ExtNVMWriteLongByteMessages.js +4 -6
- package/build/lib/serialapi/nvm/ExtNVMWriteLongByteMessages.js.map +1 -1
- package/build/lib/serialapi/nvm/FirmwareUpdateNVMMessages.js +29 -43
- package/build/lib/serialapi/nvm/FirmwareUpdateNVMMessages.js.map +1 -1
- package/build/lib/serialapi/nvm/GetNVMIdMessages.js +6 -8
- package/build/lib/serialapi/nvm/GetNVMIdMessages.js.map +1 -1
- package/build/lib/serialapi/nvm/NVMOperationsMessages.js +6 -8
- package/build/lib/serialapi/nvm/NVMOperationsMessages.js.map +1 -1
- package/build/lib/serialapi/transport/SendDataBridgeMessages.js +12 -18
- package/build/lib/serialapi/transport/SendDataBridgeMessages.js.map +1 -1
- package/build/lib/serialapi/transport/SendDataMessages.js +14 -21
- package/build/lib/serialapi/transport/SendDataMessages.js.map +1 -1
- package/package.json +17 -16
- package/build/lib/driver/CommandQueueMachine.d.ts +0 -60
- package/build/lib/driver/CommandQueueMachine.d.ts.map +0 -1
- package/build/lib/driver/CommandQueueMachine.js +0 -259
- package/build/lib/driver/CommandQueueMachine.js.map +0 -1
- package/build/lib/driver/SendThreadMachine.d.ts +0 -97
- package/build/lib/driver/SendThreadMachine.d.ts.map +0 -1
- package/build/lib/driver/SendThreadMachine.js +0 -286
- package/build/lib/driver/SendThreadMachine.js.map +0 -1
- package/build/lib/driver/TransactionMachine.d.ts +0 -30
- package/build/lib/driver/TransactionMachine.d.ts.map +0 -1
- package/build/lib/driver/TransactionMachine.js +0 -250
- package/build/lib/driver/TransactionMachine.js.map +0 -1
|
@@ -74,7 +74,7 @@ const NodeInformationFrame_1 = require("./NodeInformationFrame");
|
|
|
74
74
|
const ZWaveSDKVersions_1 = require("./ZWaveSDKVersions");
|
|
75
75
|
const _Types_3 = require("./_Types");
|
|
76
76
|
const utils_1 = require("./utils");
|
|
77
|
-
let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
77
|
+
let ZWaveController = exports.ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
78
78
|
/** @internal */
|
|
79
79
|
constructor(driver, bootloaderOnly = false) {
|
|
80
80
|
super();
|
|
@@ -644,6 +644,18 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
644
644
|
async hardReset() {
|
|
645
645
|
// begin the reset process
|
|
646
646
|
try {
|
|
647
|
+
const associations = this.nodes.get(this._ownNodeId)?.associations;
|
|
648
|
+
if (associations?.length) {
|
|
649
|
+
this.driver.controllerLog.print("Notifying associated nodes about reset...");
|
|
650
|
+
for (const nodeId of associations) {
|
|
651
|
+
const node = this.nodes.get(nodeId);
|
|
652
|
+
if (!node)
|
|
653
|
+
continue;
|
|
654
|
+
void node.sendResetLocallyNotification().catch(() => {
|
|
655
|
+
// ignore
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
}
|
|
647
659
|
this.driver.controllerLog.print("performing hard reset...");
|
|
648
660
|
await this.driver.sendMessage(new HardResetRequest_1.HardResetRequest(this.driver), {
|
|
649
661
|
supportCheck: false,
|
|
@@ -1031,11 +1043,8 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
1031
1043
|
direction: "inbound",
|
|
1032
1044
|
});
|
|
1033
1045
|
node.updateNodeInfo(msg.nodeInformation);
|
|
1034
|
-
//
|
|
1035
|
-
this.driver
|
|
1036
|
-
type: "NIF",
|
|
1037
|
-
nodeId: node.id,
|
|
1038
|
-
});
|
|
1046
|
+
// Resolve active pings that would fail otherwise
|
|
1047
|
+
this.driver.resolvePendingPings(node.id);
|
|
1039
1048
|
if (node.canSleep &&
|
|
1040
1049
|
node.supportsCC(core_1.CommandClasses["Wake Up"])) {
|
|
1041
1050
|
// In case this is a sleeping node and there are no messages in the queue, the node may go back to sleep very soon
|
|
@@ -1147,7 +1156,8 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
1147
1156
|
// No command received, bootstrap node by ourselves
|
|
1148
1157
|
this.driver.controllerLog.logNode(nodeId, "no initiate command received, bootstrapping node...");
|
|
1149
1158
|
// Assign SUC return route to make sure the node knows where to get its routes from
|
|
1150
|
-
newNode.hasSUCReturnRoute =
|
|
1159
|
+
newNode.hasSUCReturnRoute =
|
|
1160
|
+
await this.assignSUCReturnRoutes(newNode.id);
|
|
1151
1161
|
// Include using the default inclusion strategy:
|
|
1152
1162
|
// * Use S2 if possible,
|
|
1153
1163
|
// * only use S0 if necessary,
|
|
@@ -1184,8 +1194,6 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
1184
1194
|
}
|
|
1185
1195
|
}
|
|
1186
1196
|
}
|
|
1187
|
-
// Bootstrap the node's lifelines, so it knows where the controller is
|
|
1188
|
-
await this.bootstrapLifelineAndWakeup(newNode);
|
|
1189
1197
|
// We're done adding this node, notify listeners
|
|
1190
1198
|
const result = bootstrapFailure != undefined
|
|
1191
1199
|
? {
|
|
@@ -1200,7 +1208,9 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
1200
1208
|
const step = initiate.step;
|
|
1201
1209
|
newNode.once("ready", () => {
|
|
1202
1210
|
this.driver.controllerLog.logNode(nodeId, `Notifying node ${inclCtrlrId} of finished inclusion`);
|
|
1203
|
-
|
|
1211
|
+
// Create API without checking for support
|
|
1212
|
+
const api = inclCtrlr.createAPI(core_1.CommandClasses["Inclusion Controller"], false);
|
|
1213
|
+
void api
|
|
1204
1214
|
.completeStep(step, cc_1.InclusionControllerStatus.OK)
|
|
1205
1215
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
1206
1216
|
.catch(() => { });
|
|
@@ -1253,8 +1263,6 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
1253
1263
|
}
|
|
1254
1264
|
// Perform S0/S2 bootstrapping
|
|
1255
1265
|
const bootstrapFailure = await this.proxyBootstrap(newNode, inclCtrlr);
|
|
1256
|
-
// Bootstrap the node's lifelines, so it knows where the controller is
|
|
1257
|
-
await this.bootstrapLifelineAndWakeup(newNode);
|
|
1258
1266
|
// We're done adding this node, notify listeners
|
|
1259
1267
|
const result = bootstrapFailure != undefined
|
|
1260
1268
|
? {
|
|
@@ -1267,7 +1275,9 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
1267
1275
|
// And notify the inclusion controller after we're done interviewing
|
|
1268
1276
|
newNode.once("ready", () => {
|
|
1269
1277
|
this.driver.controllerLog.logNode(inclCtrlr.nodeId, `Notifying inclusion controller of finished inclusion`);
|
|
1270
|
-
|
|
1278
|
+
// Create API without checking for support
|
|
1279
|
+
const api = inclCtrlr.createAPI(core_1.CommandClasses["Inclusion Controller"], false);
|
|
1280
|
+
void api
|
|
1271
1281
|
.completeStep(initiate.step, cc_1.InclusionControllerStatus.OK)
|
|
1272
1282
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
1273
1283
|
.catch(() => { });
|
|
@@ -1869,97 +1879,6 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
1869
1879
|
this.cancelBootstrapS2Promise = undefined;
|
|
1870
1880
|
}
|
|
1871
1881
|
}
|
|
1872
|
-
/** Ensures that the node knows where to reach the controller */
|
|
1873
|
-
async bootstrapLifelineAndWakeup(node) {
|
|
1874
|
-
// If the node was bootstrapped with S2, all these requests must happen securely
|
|
1875
|
-
if ((0, core_1.securityClassIsS2)(node.getHighestSecurityClass())) {
|
|
1876
|
-
for (const cc of [
|
|
1877
|
-
core_1.CommandClasses["Wake Up"],
|
|
1878
|
-
core_1.CommandClasses.Association,
|
|
1879
|
-
core_1.CommandClasses["Multi Channel Association"],
|
|
1880
|
-
core_1.CommandClasses.Version,
|
|
1881
|
-
]) {
|
|
1882
|
-
if (node.supportsCC(cc)) {
|
|
1883
|
-
node.addCC(cc, { secure: true });
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
|
-
if (node.supportsCC(core_1.CommandClasses["Z-Wave Plus Info"])) {
|
|
1888
|
-
// SDS11846: The Z-Wave+ lifeline must be assigned to a node as the very first thing
|
|
1889
|
-
if (node.supportsCC(core_1.CommandClasses.Association) ||
|
|
1890
|
-
node.supportsCC(core_1.CommandClasses["Multi Channel Association"])) {
|
|
1891
|
-
this.driver.controllerLog.logNode(node.id, {
|
|
1892
|
-
message: `Configuring Z-Wave+ Lifeline association...`,
|
|
1893
|
-
direction: "none",
|
|
1894
|
-
});
|
|
1895
|
-
const ownNodeId = this.driver.controller.ownNodeId;
|
|
1896
|
-
try {
|
|
1897
|
-
if (node.supportsCC(core_1.CommandClasses.Association)) {
|
|
1898
|
-
await node.commandClasses.Association.addNodeIds(1, ownNodeId);
|
|
1899
|
-
}
|
|
1900
|
-
else {
|
|
1901
|
-
await node.commandClasses["Multi Channel Association"].addDestinations({
|
|
1902
|
-
groupId: 1,
|
|
1903
|
-
endpoints: [{ nodeId: ownNodeId, endpoint: 0 }],
|
|
1904
|
-
});
|
|
1905
|
-
}
|
|
1906
|
-
// After setting the association, make sure the node knows how to reach us
|
|
1907
|
-
await this.assignReturnRoute(node.id, ownNodeId);
|
|
1908
|
-
}
|
|
1909
|
-
catch (e) {
|
|
1910
|
-
if ((0, core_1.isTransmissionError)(e) || (0, core_1.isRecoverableZWaveError)(e)) {
|
|
1911
|
-
this.driver.controllerLog.logNode(node.id, {
|
|
1912
|
-
message: `Failed to configure Z-Wave+ Lifeline association: ${e.message}`,
|
|
1913
|
-
direction: "none",
|
|
1914
|
-
level: "warn",
|
|
1915
|
-
});
|
|
1916
|
-
}
|
|
1917
|
-
else {
|
|
1918
|
-
throw e;
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
else {
|
|
1923
|
-
this.driver.controllerLog.logNode(node.id, {
|
|
1924
|
-
message: `Cannot configure Z-Wave+ Lifeline association: Node does not support associations...`,
|
|
1925
|
-
direction: "none",
|
|
1926
|
-
level: "warn",
|
|
1927
|
-
});
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
if (node.supportsCC(core_1.CommandClasses["Wake Up"])) {
|
|
1931
|
-
try {
|
|
1932
|
-
// Query the version, so we can setup the wakeup destination correctly.
|
|
1933
|
-
let supportedVersion;
|
|
1934
|
-
if (node.supportsCC(core_1.CommandClasses.Version)) {
|
|
1935
|
-
supportedVersion =
|
|
1936
|
-
await node.commandClasses.Version.getCCVersion(core_1.CommandClasses["Wake Up"]);
|
|
1937
|
-
}
|
|
1938
|
-
// If querying the version can't be done, we should at least assume that it supports V1
|
|
1939
|
-
supportedVersion ?? (supportedVersion = 1);
|
|
1940
|
-
if (supportedVersion > 0) {
|
|
1941
|
-
node.addCC(core_1.CommandClasses["Wake Up"], {
|
|
1942
|
-
version: supportedVersion,
|
|
1943
|
-
});
|
|
1944
|
-
const instance = node.createCCInstance(core_1.CommandClasses["Wake Up"]);
|
|
1945
|
-
await instance.interview(this.driver);
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
catch (e) {
|
|
1949
|
-
if ((0, core_1.isTransmissionError)(e) || (0, core_1.isRecoverableZWaveError)(e)) {
|
|
1950
|
-
this.driver.controllerLog.logNode(node.id, {
|
|
1951
|
-
message: `Cannot configure wakeup destination: ${e.message}`,
|
|
1952
|
-
direction: "none",
|
|
1953
|
-
level: "warn",
|
|
1954
|
-
});
|
|
1955
|
-
}
|
|
1956
|
-
else {
|
|
1957
|
-
// we want to pass all other errors through
|
|
1958
|
-
throw e;
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
1882
|
/**
|
|
1964
1883
|
* Is called when an AddNode request is received from the controller.
|
|
1965
1884
|
* Handles and controls the inclusion process.
|
|
@@ -2066,7 +1985,7 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
2066
1985
|
// If it is actually a sleeping device, it will be marked as such later
|
|
2067
1986
|
newNode.markAsAlive();
|
|
2068
1987
|
// Assign SUC return route to make sure the node knows where to get its routes from
|
|
2069
|
-
newNode.hasSUCReturnRoute = await this.
|
|
1988
|
+
newNode.hasSUCReturnRoute = await this.assignSUCReturnRoutes(newNode.id);
|
|
2070
1989
|
const opts = this._inclusionOptions;
|
|
2071
1990
|
let bootstrapFailure;
|
|
2072
1991
|
let smartStartFailed = false;
|
|
@@ -2180,10 +2099,6 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
2180
2099
|
});
|
|
2181
2100
|
}
|
|
2182
2101
|
}
|
|
2183
|
-
else {
|
|
2184
|
-
// Bootstrap the node's lifelines, so it knows where the controller is
|
|
2185
|
-
await this.bootstrapLifelineAndWakeup(newNode);
|
|
2186
|
-
}
|
|
2187
2102
|
this.setInclusionState(Inclusion_1.InclusionState.Idle);
|
|
2188
2103
|
// We're done adding this node, notify listeners
|
|
2189
2104
|
const result = bootstrapFailure != undefined
|
|
@@ -2253,7 +2168,8 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
2253
2168
|
// If it is actually a sleeping device, it will be marked as such later
|
|
2254
2169
|
newNode.markAsAlive();
|
|
2255
2170
|
// Assign SUC return route to make sure the node knows where to get its routes from
|
|
2256
|
-
newNode.hasSUCReturnRoute =
|
|
2171
|
+
newNode.hasSUCReturnRoute =
|
|
2172
|
+
await this.assignSUCReturnRoutes(newNode.id);
|
|
2257
2173
|
// Try perform the security bootstrap process. When replacing a node, we don't know any supported CCs
|
|
2258
2174
|
// yet, so we need to trust the chosen inclusion strategy.
|
|
2259
2175
|
const strategy = this._inclusionOptions.strategy;
|
|
@@ -2287,8 +2203,6 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
2287
2203
|
newNode.securityClasses.set(secClass, false);
|
|
2288
2204
|
}
|
|
2289
2205
|
}
|
|
2290
|
-
// Bootstrap the node's lifelines, so it knows where the controller is
|
|
2291
|
-
await this.bootstrapLifelineAndWakeup(newNode);
|
|
2292
2206
|
// We're done adding this node, notify listeners. This also kicks off the node interview
|
|
2293
2207
|
const result = bootstrapFailure != undefined
|
|
2294
2208
|
? {
|
|
@@ -2575,18 +2489,9 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
2575
2489
|
message: `refreshing neighbor list (attempt ${attempt})...`,
|
|
2576
2490
|
direction: "outbound",
|
|
2577
2491
|
});
|
|
2578
|
-
// During inclusion, the timeout is mainly required for the node to detect all neighbors
|
|
2579
|
-
// We do the same here, so we just reuse the timeout
|
|
2580
|
-
const discoveryTimeout = (0, AddNodeToNetworkRequest_1.computeNeighborDiscoveryTimeout)(this.driver,
|
|
2581
|
-
// Controllers take longer, just assume the worst case here
|
|
2582
|
-
core_1.NodeType.Controller);
|
|
2583
2492
|
try {
|
|
2584
|
-
const
|
|
2585
|
-
|
|
2586
|
-
discoveryTimeout,
|
|
2587
|
-
}));
|
|
2588
|
-
if (resp.updateStatus ===
|
|
2589
|
-
RequestNodeNeighborUpdateMessages_1.NodeNeighborUpdateStatus.UpdateDone) {
|
|
2493
|
+
const result = await this.discoverNodeNeighbors(nodeId);
|
|
2494
|
+
if (result) {
|
|
2590
2495
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2591
2496
|
message: "neighbor list refreshed...",
|
|
2592
2497
|
direction: "inbound",
|
|
@@ -2595,7 +2500,6 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
2595
2500
|
break;
|
|
2596
2501
|
}
|
|
2597
2502
|
else {
|
|
2598
|
-
// UpdateFailed
|
|
2599
2503
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2600
2504
|
message: "refreshing neighbor list failed...",
|
|
2601
2505
|
direction: "inbound",
|
|
@@ -2616,24 +2520,16 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
2616
2520
|
}
|
|
2617
2521
|
}
|
|
2618
2522
|
// 2. re-create the SUC return route, just in case
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
}
|
|
2622
|
-
node.hasSUCReturnRoute = await this.assignSUCReturnRoute(nodeId);
|
|
2623
|
-
// 3. delete all return routes so we can assign new ones
|
|
2523
|
+
node.hasSUCReturnRoute || (node.hasSUCReturnRoute = await this.assignSUCReturnRoutes(nodeId));
|
|
2524
|
+
// 3. delete all return routes to get rid of potential priority return routes
|
|
2624
2525
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
2625
2526
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2626
2527
|
message: `deleting return routes (attempt ${attempt})...`,
|
|
2627
2528
|
direction: "outbound",
|
|
2628
2529
|
});
|
|
2629
|
-
|
|
2630
|
-
await this.driver.sendMessage(new DeleteReturnRouteMessages_1.DeleteReturnRouteRequest(this.driver, { nodeId }));
|
|
2631
|
-
// this step was successful, continue with the next
|
|
2530
|
+
if (await this.deleteReturnRoutes(nodeId)) {
|
|
2632
2531
|
break;
|
|
2633
2532
|
}
|
|
2634
|
-
catch (e) {
|
|
2635
|
-
this.driver.controllerLog.logNode(nodeId, `deleting return routes failed: ${(0, shared_1.getErrorMessage)(e)}`, "warn");
|
|
2636
|
-
}
|
|
2637
2533
|
if (attempt === maxAttempts) {
|
|
2638
2534
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2639
2535
|
message: `failed to delete return routes after ${maxAttempts} attempts, healing failed`,
|
|
@@ -2643,22 +2539,18 @@ supported CCs: ${nodeInfo.supportedCCs
|
|
|
2643
2539
|
return false;
|
|
2644
2540
|
}
|
|
2645
2541
|
}
|
|
2646
|
-
// 4. Assign
|
|
2542
|
+
// 4. Assign return routes to all association destinations.
|
|
2647
2543
|
let associatedNodes = [];
|
|
2648
|
-
const maxReturnRoutes = 4;
|
|
2649
2544
|
try {
|
|
2650
2545
|
associatedNodes = (0, arrays_1.distinct)((0, shared_1.flatMap)([...this.getAssociations({ nodeId }).values()], (assocs) => assocs.map((a) => a.nodeId))).sort();
|
|
2651
2546
|
}
|
|
2652
2547
|
catch {
|
|
2653
2548
|
/* ignore */
|
|
2654
2549
|
}
|
|
2655
|
-
//
|
|
2550
|
+
// One of those should probably be the controller. Not sure if the SUC return route is enough.
|
|
2656
2551
|
if (!associatedNodes.includes(this._ownNodeId)) {
|
|
2657
2552
|
associatedNodes.unshift(this._ownNodeId);
|
|
2658
2553
|
}
|
|
2659
|
-
if (associatedNodes.length > maxReturnRoutes) {
|
|
2660
|
-
associatedNodes = associatedNodes.slice(0, maxReturnRoutes);
|
|
2661
|
-
}
|
|
2662
2554
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2663
2555
|
message: `assigning return routes to the following nodes:
|
|
2664
2556
|
${associatedNodes.join(", ")}`,
|
|
@@ -2670,17 +2562,10 @@ ${associatedNodes.join(", ")}`,
|
|
|
2670
2562
|
message: `assigning return route to node ${destinationNodeId} (attempt ${attempt})...`,
|
|
2671
2563
|
direction: "outbound",
|
|
2672
2564
|
});
|
|
2673
|
-
|
|
2674
|
-
await this.driver.sendMessage(new AssignReturnRouteMessages_1.AssignReturnRouteRequest(this.driver, {
|
|
2675
|
-
nodeId,
|
|
2676
|
-
destinationNodeId,
|
|
2677
|
-
}));
|
|
2565
|
+
if (await this.assignReturnRoutes(nodeId, destinationNodeId)) {
|
|
2678
2566
|
// this step was successful, continue with the next
|
|
2679
2567
|
break;
|
|
2680
2568
|
}
|
|
2681
|
-
catch (e) {
|
|
2682
|
-
this.driver.controllerLog.logNode(nodeId, `assigning return route failed: ${(0, shared_1.getErrorMessage)(e)}`, "warn");
|
|
2683
|
-
}
|
|
2684
2569
|
if (attempt === maxAttempts) {
|
|
2685
2570
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2686
2571
|
message: `failed to assign return route after ${maxAttempts} attempts, healing failed`,
|
|
@@ -2715,23 +2600,145 @@ ${associatedNodes.join(", ")}`,
|
|
|
2715
2600
|
}));
|
|
2716
2601
|
return result.isOK();
|
|
2717
2602
|
}
|
|
2718
|
-
|
|
2603
|
+
// After a lot of experimenting, it seems to make sense to document how assigning return routes works in the controller.
|
|
2604
|
+
// Each node has a list of 4 return routes per destination (and probably a separate list for the SUC):
|
|
2605
|
+
// - #0, repeaters..., speed, wakeup
|
|
2606
|
+
// - #1, repeaters..., speed, wakeup
|
|
2607
|
+
// - #2, repeaters..., speed, wakeup
|
|
2608
|
+
// - #3, repeaters..., speed, wakeup
|
|
2609
|
+
//
|
|
2610
|
+
// Empty slots are filled with 0 repeaters, 9.6kbit/s, no wakeup
|
|
2611
|
+
//
|
|
2612
|
+
// Calling assignReturnRoute will assign all 4 slots, some of which may be empty.
|
|
2613
|
+
// Calling deleteReturnRoute will assign an empty route to all 4 slots.
|
|
2614
|
+
//
|
|
2615
|
+
// Priority return routes are indicated by a separate "pointer" byte which tells the node which route is the priority.
|
|
2616
|
+
// Calling assignPriorityReturnRoute will first assign 4 routes, one of which is then marked as priority.
|
|
2617
|
+
// This is not fully understood yet, but it seems that the priority route is actually the last non-empty route.
|
|
2618
|
+
// If the priority byte points to an empty route, it is ignored.
|
|
2619
|
+
//
|
|
2620
|
+
// Calling assignReturnRoute after having assigned a priority return route will not clear that pointer byte. This
|
|
2621
|
+
// means that a previously-assigned priority route can randomly change if assignReturnRoute assigns enough routes.
|
|
2622
|
+
// deleteReturnRoute does also clear the priority byte.
|
|
2623
|
+
/** @deprecated Use {@link assignSUCReturnRoutes} instead */
|
|
2624
|
+
assignSUCReturnRoute(nodeId) {
|
|
2625
|
+
return this.assignSUCReturnRoutes(nodeId);
|
|
2626
|
+
}
|
|
2627
|
+
/**
|
|
2628
|
+
* Instructs the controller to assign static routes from the given end node to the SUC.
|
|
2629
|
+
* This will assign up to 4 routes, depending on the network topology (that the controller knows about).
|
|
2630
|
+
*/
|
|
2631
|
+
async assignSUCReturnRoutes(nodeId) {
|
|
2719
2632
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2720
2633
|
message: `Assigning SUC return route...`,
|
|
2721
2634
|
direction: "outbound",
|
|
2722
2635
|
});
|
|
2636
|
+
// Since there is only one SUC, we can do the right thing here and delete all routes first, which clears any dangling priority return routes.
|
|
2637
|
+
// Afterwards, we'll set up all routes again anyways.
|
|
2638
|
+
await this.deleteSUCReturnRoutes(nodeId);
|
|
2723
2639
|
try {
|
|
2724
2640
|
const result = await this.driver.sendMessage(new AssignSUCReturnRouteMessages_1.AssignSUCReturnRouteRequest(this.driver, {
|
|
2725
2641
|
nodeId,
|
|
2726
2642
|
}));
|
|
2727
|
-
|
|
2643
|
+
const success = this.handleRouteAssignmentTransmitReport(result, nodeId);
|
|
2644
|
+
if (success) {
|
|
2645
|
+
// Custom assigned are no longer valid
|
|
2646
|
+
this.setCustomSUCReturnRoutesCached(nodeId, undefined);
|
|
2647
|
+
}
|
|
2648
|
+
return success;
|
|
2728
2649
|
}
|
|
2729
2650
|
catch (e) {
|
|
2730
2651
|
this.driver.controllerLog.logNode(nodeId, `Assigning SUC return route failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
|
|
2731
2652
|
return false;
|
|
2732
2653
|
}
|
|
2733
2654
|
}
|
|
2734
|
-
|
|
2655
|
+
/**
|
|
2656
|
+
* Returns which custom static routes are currently assigned from the given end node to the SUC.
|
|
2657
|
+
*
|
|
2658
|
+
* **Note:** This only considers routes that were assigned using {@link assignCustomSUCReturnRoutes}.
|
|
2659
|
+
* If another controller has assigned routes in the meantime, this information may be out of date.
|
|
2660
|
+
*/
|
|
2661
|
+
getCustomSUCReturnRoutesCached(nodeId) {
|
|
2662
|
+
return (this.driver.cacheGet(NetworkCache_1.cacheKeys.node(nodeId).customSUCReturnRoutes) ?? []);
|
|
2663
|
+
}
|
|
2664
|
+
setCustomSUCReturnRoutesCached(nodeId, routes) {
|
|
2665
|
+
this.driver.cacheSet(NetworkCache_1.cacheKeys.node(nodeId).customSUCReturnRoutes, routes);
|
|
2666
|
+
}
|
|
2667
|
+
/**
|
|
2668
|
+
* Assigns static routes from the given end node to the SUC. Unlike {@link assignSUCReturnRoutes}, this method assigns
|
|
2669
|
+
* the given routes instead of having the controller calculate them. At most 4 routes can be assigned. If less are
|
|
2670
|
+
* specified, the remaining routes are cleared.
|
|
2671
|
+
*
|
|
2672
|
+
* **Note:** Calling {@link assignSUCReturnRoutes} or {@link deleteSUCReturnRoutes} will override the custom routes.
|
|
2673
|
+
*/
|
|
2674
|
+
async assignCustomSUCReturnRoutes(nodeId, routes) {
|
|
2675
|
+
this.driver.controllerLog.logNode(nodeId, {
|
|
2676
|
+
message: `Assigning custom SUC return routes...`,
|
|
2677
|
+
direction: "outbound",
|
|
2678
|
+
});
|
|
2679
|
+
// Since there is only one SUC, we can do the right thing here and delete all routes first, which clears the priority return routes.
|
|
2680
|
+
await this.deleteSUCReturnRoutes(nodeId);
|
|
2681
|
+
let result = true;
|
|
2682
|
+
const MAX_ROUTES = 4;
|
|
2683
|
+
// Keep track of which routes have been assigned
|
|
2684
|
+
const assignedRoutes = this.getCustomSUCReturnRoutesCached(nodeId);
|
|
2685
|
+
while (assignedRoutes.length < MAX_ROUTES) {
|
|
2686
|
+
assignedRoutes.push(core_1.EMPTY_ROUTE);
|
|
2687
|
+
}
|
|
2688
|
+
for (let i = 0; i < MAX_ROUTES; i++) {
|
|
2689
|
+
const route = routes[i] ?? core_1.EMPTY_ROUTE;
|
|
2690
|
+
const isEmpty = (0, core_1.isEmptyRoute)(route);
|
|
2691
|
+
// We are always listening
|
|
2692
|
+
const targetWakeup = false;
|
|
2693
|
+
const cc = new cc_1.ZWaveProtocolCCAssignSUCReturnRoute(this.driver, {
|
|
2694
|
+
nodeId,
|
|
2695
|
+
// Empty routes are marked with a nodeId of 0
|
|
2696
|
+
destinationNodeId: isEmpty ? 0 : this.ownNodeId ?? 1,
|
|
2697
|
+
routeIndex: i,
|
|
2698
|
+
repeaters: route.repeaters,
|
|
2699
|
+
destinationSpeed: route.routeSpeed,
|
|
2700
|
+
destinationWakeUp: (0, cc_1.FLiRS2WakeUpTime)(targetWakeup ?? false),
|
|
2701
|
+
});
|
|
2702
|
+
try {
|
|
2703
|
+
// TODO: add a better method to send ZWaveProtocolCC
|
|
2704
|
+
await this.driver.sendCommand(cc, {
|
|
2705
|
+
priority: core_1.MessagePriority.MultistepController,
|
|
2706
|
+
autoEncapsulate: false,
|
|
2707
|
+
changeNodeStatusOnMissingACK: false,
|
|
2708
|
+
maxSendAttempts: 1,
|
|
2709
|
+
useSupervision: false,
|
|
2710
|
+
transmitOptions: core_1.TransmitOptions.AutoRoute | core_1.TransmitOptions.ACK,
|
|
2711
|
+
});
|
|
2712
|
+
// Remember that this route has been assigned
|
|
2713
|
+
assignedRoutes[i] = route;
|
|
2714
|
+
}
|
|
2715
|
+
catch (e) {
|
|
2716
|
+
this.driver.controllerLog.logNode(nodeId, {
|
|
2717
|
+
message: `Assigning custom SUC return route #${i} failed`,
|
|
2718
|
+
direction: "outbound",
|
|
2719
|
+
level: "warn",
|
|
2720
|
+
});
|
|
2721
|
+
result = false;
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
// Trim empty routes off the end. We may end up with empty routes in the middle
|
|
2725
|
+
// if an assignment fails.
|
|
2726
|
+
while (assignedRoutes.length > 0 &&
|
|
2727
|
+
(0, core_1.isEmptyRoute)(assignedRoutes[assignedRoutes.length - 1])) {
|
|
2728
|
+
assignedRoutes.pop();
|
|
2729
|
+
}
|
|
2730
|
+
this.setCustomSUCReturnRoutesCached(nodeId, assignedRoutes);
|
|
2731
|
+
return result;
|
|
2732
|
+
}
|
|
2733
|
+
/** @deprecated use {@link deleteSUCReturnRoutes} instead */
|
|
2734
|
+
deleteSUCReturnRoute(nodeId) {
|
|
2735
|
+
return this.deleteReturnRoutes(nodeId);
|
|
2736
|
+
}
|
|
2737
|
+
/**
|
|
2738
|
+
* Instructs the controller to assign static routes from the given end node to the SUC.
|
|
2739
|
+
* This will assign up to 4 routes, depending on the network topology (that the controller knows about).
|
|
2740
|
+
*/
|
|
2741
|
+
async deleteSUCReturnRoutes(nodeId) {
|
|
2735
2742
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2736
2743
|
message: `Deleting SUC return route...`,
|
|
2737
2744
|
direction: "outbound",
|
|
@@ -2740,16 +2747,52 @@ ${associatedNodes.join(", ")}`,
|
|
|
2740
2747
|
const result = await this.driver.sendMessage(new DeleteSUCReturnRouteMessages_1.DeleteSUCReturnRouteRequest(this.driver, {
|
|
2741
2748
|
nodeId,
|
|
2742
2749
|
}));
|
|
2743
|
-
|
|
2750
|
+
const success = this.handleRouteAssignmentTransmitReport(result, nodeId);
|
|
2751
|
+
if (success) {
|
|
2752
|
+
// Custom assigned and priority return routes are no longer valid
|
|
2753
|
+
this.setPrioritySUCReturnRouteCached(nodeId, undefined);
|
|
2754
|
+
this.setCustomSUCReturnRoutesCached(nodeId, undefined);
|
|
2755
|
+
}
|
|
2756
|
+
return success;
|
|
2744
2757
|
}
|
|
2745
2758
|
catch (e) {
|
|
2746
2759
|
this.driver.controllerLog.logNode(nodeId, `Deleting SUC return route failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
|
|
2747
2760
|
return false;
|
|
2748
2761
|
}
|
|
2749
2762
|
}
|
|
2750
|
-
|
|
2763
|
+
/**
|
|
2764
|
+
* Returns which custom static routes are currently assigned between the given end nodes.
|
|
2765
|
+
*
|
|
2766
|
+
* **Note:** This only considers routes that were assigned using {@link assignCustomReturnRoutes}.
|
|
2767
|
+
* If another controller has assigned routes in the meantime, this information may be out of date.
|
|
2768
|
+
*/
|
|
2769
|
+
getCustomReturnRoutesCached(nodeId, destinationNodeId) {
|
|
2770
|
+
return (this.driver.cacheGet(NetworkCache_1.cacheKeys.node(nodeId).customReturnRoutes(destinationNodeId)) ?? []);
|
|
2771
|
+
}
|
|
2772
|
+
setCustomReturnRoutesCached(nodeId, destinationNodeId, routes) {
|
|
2773
|
+
this.driver.cacheSet(NetworkCache_1.cacheKeys.node(nodeId).customReturnRoutes(destinationNodeId), routes);
|
|
2774
|
+
}
|
|
2775
|
+
clearCustomReturnRoutesCached(nodeId) {
|
|
2776
|
+
// This is a bit ugly, but the best we can do right now.
|
|
2777
|
+
for (let dest = 1; dest <= core_1.MAX_NODES; dest++) {
|
|
2778
|
+
this.setCustomReturnRoutesCached(nodeId, dest, undefined);
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
/** @deprecated use {@link assignReturnRoutes} instead */
|
|
2782
|
+
assignReturnRoute(nodeId, destinationNodeId) {
|
|
2783
|
+
return this.assignReturnRoutes(nodeId, destinationNodeId);
|
|
2784
|
+
}
|
|
2785
|
+
/**
|
|
2786
|
+
* Instructs the controller to assign static routes between the two given end nodes.
|
|
2787
|
+
* This will assign up to 4 routes, depending on the network topology (that the controller knows about).
|
|
2788
|
+
*/
|
|
2789
|
+
async assignReturnRoutes(nodeId, destinationNodeId) {
|
|
2790
|
+
// Make sure this is not misused by passing the controller's node ID
|
|
2791
|
+
if (destinationNodeId === this.ownNodeId) {
|
|
2792
|
+
return this.assignSUCReturnRoutes(nodeId);
|
|
2793
|
+
}
|
|
2751
2794
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2752
|
-
message: `Assigning return
|
|
2795
|
+
message: `Assigning return routes to node ${destinationNodeId}...`,
|
|
2753
2796
|
direction: "outbound",
|
|
2754
2797
|
});
|
|
2755
2798
|
try {
|
|
@@ -2757,14 +2800,105 @@ ${associatedNodes.join(", ")}`,
|
|
|
2757
2800
|
nodeId,
|
|
2758
2801
|
destinationNodeId,
|
|
2759
2802
|
}));
|
|
2760
|
-
|
|
2803
|
+
const success = this.handleRouteAssignmentTransmitReport(result, nodeId);
|
|
2804
|
+
if (success) {
|
|
2805
|
+
// Custom assigned are no longer valid
|
|
2806
|
+
this.setCustomReturnRoutesCached(nodeId, destinationNodeId, undefined);
|
|
2807
|
+
// The priority route probably is invalid too now, but it may also point to a random route
|
|
2808
|
+
if (this.hasPriorityReturnRouteCached(nodeId, destinationNodeId) !== false) {
|
|
2809
|
+
this.setPriorityReturnRouteCached(nodeId, destinationNodeId, core_1.UNKNOWN_STATE);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
return success;
|
|
2761
2813
|
}
|
|
2762
2814
|
catch (e) {
|
|
2763
|
-
this.driver.controllerLog.logNode(nodeId, `Assigning return
|
|
2815
|
+
this.driver.controllerLog.logNode(nodeId, `Assigning return routes failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
|
|
2764
2816
|
return false;
|
|
2765
2817
|
}
|
|
2766
2818
|
}
|
|
2767
|
-
|
|
2819
|
+
/**
|
|
2820
|
+
* Assigns static routes between the two given end nodes. Unlike {@link assignReturnRoutes}, this method assigns
|
|
2821
|
+
* the given routes instead of having the controller calculate them. At most 4 routes can be assigned. If less are
|
|
2822
|
+
* specified, the remaining routes are cleared.
|
|
2823
|
+
*
|
|
2824
|
+
* **Note:** Calling {@link assignReturnRoutes} or {@link deleteReturnRoutes} will override the custom routes.
|
|
2825
|
+
*/
|
|
2826
|
+
async assignCustomReturnRoutes(nodeId, destinationNodeId, routes) {
|
|
2827
|
+
// Make sure this is not misused by passing the controller's node ID
|
|
2828
|
+
if (destinationNodeId === this.ownNodeId) {
|
|
2829
|
+
return this.assignCustomSUCReturnRoutes(nodeId, routes);
|
|
2830
|
+
}
|
|
2831
|
+
this.driver.controllerLog.logNode(nodeId, {
|
|
2832
|
+
message: `Assigning custom return routes to node ${destinationNodeId}...`,
|
|
2833
|
+
direction: "outbound",
|
|
2834
|
+
});
|
|
2835
|
+
let result = true;
|
|
2836
|
+
const MAX_ROUTES = 4;
|
|
2837
|
+
// Keep track of which routes have been assigned
|
|
2838
|
+
const assignedRoutes = this.getCustomReturnRoutesCached(nodeId, destinationNodeId);
|
|
2839
|
+
while (assignedRoutes.length < MAX_ROUTES) {
|
|
2840
|
+
assignedRoutes.push(core_1.EMPTY_ROUTE);
|
|
2841
|
+
}
|
|
2842
|
+
for (let i = 0; i < MAX_ROUTES; i++) {
|
|
2843
|
+
const route = routes[i] ?? core_1.EMPTY_ROUTE;
|
|
2844
|
+
const isEmpty = (0, core_1.isEmptyRoute)(route);
|
|
2845
|
+
const targetWakeup = !isEmpty
|
|
2846
|
+
? this.nodes.get(destinationNodeId)?.isFrequentListening
|
|
2847
|
+
: undefined;
|
|
2848
|
+
const cc = new cc_1.ZWaveProtocolCCAssignReturnRoute(this.driver, {
|
|
2849
|
+
nodeId,
|
|
2850
|
+
// Empty routes are marked with a nodeId of 0
|
|
2851
|
+
destinationNodeId: isEmpty ? 0 : destinationNodeId,
|
|
2852
|
+
routeIndex: i,
|
|
2853
|
+
repeaters: route.repeaters,
|
|
2854
|
+
destinationSpeed: route.routeSpeed,
|
|
2855
|
+
destinationWakeUp: (0, cc_1.FLiRS2WakeUpTime)(targetWakeup ?? false),
|
|
2856
|
+
});
|
|
2857
|
+
try {
|
|
2858
|
+
// TODO: add a better method to send ZWaveProtocolCC
|
|
2859
|
+
await this.driver.sendCommand(cc, {
|
|
2860
|
+
priority: core_1.MessagePriority.MultistepController,
|
|
2861
|
+
autoEncapsulate: false,
|
|
2862
|
+
changeNodeStatusOnMissingACK: false,
|
|
2863
|
+
maxSendAttempts: 1,
|
|
2864
|
+
useSupervision: false,
|
|
2865
|
+
transmitOptions: core_1.TransmitOptions.AutoRoute | core_1.TransmitOptions.ACK,
|
|
2866
|
+
});
|
|
2867
|
+
// Remember that this route has been assigned
|
|
2868
|
+
assignedRoutes[i] = route;
|
|
2869
|
+
}
|
|
2870
|
+
catch (e) {
|
|
2871
|
+
this.driver.controllerLog.logNode(nodeId, {
|
|
2872
|
+
message: `Assigning custom return route #${i} failed`,
|
|
2873
|
+
direction: "outbound",
|
|
2874
|
+
level: "warn",
|
|
2875
|
+
});
|
|
2876
|
+
result = false;
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
// Trim empty routes off the end. We may end up with empty routes in the middle
|
|
2880
|
+
// if an assignment fails.
|
|
2881
|
+
while (assignedRoutes.length > 0 &&
|
|
2882
|
+
(0, core_1.isEmptyRoute)(assignedRoutes[assignedRoutes.length - 1])) {
|
|
2883
|
+
assignedRoutes.pop();
|
|
2884
|
+
}
|
|
2885
|
+
this.setCustomReturnRoutesCached(nodeId, destinationNodeId, assignedRoutes);
|
|
2886
|
+
// The priority route is probably invalid now, but it may also point to a random route
|
|
2887
|
+
if (this.hasPriorityReturnRouteCached(nodeId, destinationNodeId) !==
|
|
2888
|
+
false) {
|
|
2889
|
+
this.setPriorityReturnRouteCached(nodeId, destinationNodeId, core_1.UNKNOWN_STATE);
|
|
2890
|
+
}
|
|
2891
|
+
return result;
|
|
2892
|
+
}
|
|
2893
|
+
/** @deprecated use {@link deleteReturnRoutes} instead */
|
|
2894
|
+
deleteReturnRoute(nodeId) {
|
|
2895
|
+
return this.deleteReturnRoutes(nodeId);
|
|
2896
|
+
}
|
|
2897
|
+
/**
|
|
2898
|
+
* Instructs the controller to delete all static routes between the given node and all
|
|
2899
|
+
* other end nodes, including the priority return routes.
|
|
2900
|
+
*/
|
|
2901
|
+
async deleteReturnRoutes(nodeId) {
|
|
2768
2902
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2769
2903
|
message: `Deleting all return routes...`,
|
|
2770
2904
|
direction: "outbound",
|
|
@@ -2773,7 +2907,13 @@ ${associatedNodes.join(", ")}`,
|
|
|
2773
2907
|
const result = await this.driver.sendMessage(new DeleteReturnRouteMessages_1.DeleteReturnRouteRequest(this.driver, {
|
|
2774
2908
|
nodeId,
|
|
2775
2909
|
}));
|
|
2776
|
-
|
|
2910
|
+
const success = this.handleRouteAssignmentTransmitReport(result, nodeId);
|
|
2911
|
+
if (success) {
|
|
2912
|
+
// All custom assigned routes are no longer valid
|
|
2913
|
+
this.clearPriorityReturnRoutesCached(nodeId);
|
|
2914
|
+
this.clearCustomReturnRoutesCached(nodeId);
|
|
2915
|
+
}
|
|
2916
|
+
return success;
|
|
2777
2917
|
}
|
|
2778
2918
|
catch (e) {
|
|
2779
2919
|
this.driver.controllerLog.logNode(nodeId, `Deleting return routes failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
|
|
@@ -2788,6 +2928,10 @@ ${associatedNodes.join(", ")}`,
|
|
|
2788
2928
|
* @param routeSpeed The transmission speed to use for the route
|
|
2789
2929
|
*/
|
|
2790
2930
|
async assignPriorityReturnRoute(nodeId, destinationNodeId, repeaters, routeSpeed) {
|
|
2931
|
+
// Make sure this is not misused by passing the controller's node ID
|
|
2932
|
+
if (destinationNodeId === this.ownNodeId) {
|
|
2933
|
+
return this.assignPrioritySUCReturnRoute(nodeId, repeaters, routeSpeed);
|
|
2934
|
+
}
|
|
2791
2935
|
this.driver.controllerLog.logNode(nodeId, {
|
|
2792
2936
|
message: `Assigning priority return route to node ${destinationNodeId}...`,
|
|
2793
2937
|
direction: "outbound",
|
|
@@ -2799,13 +2943,45 @@ ${associatedNodes.join(", ")}`,
|
|
|
2799
2943
|
repeaters,
|
|
2800
2944
|
routeSpeed,
|
|
2801
2945
|
}));
|
|
2802
|
-
|
|
2946
|
+
const success = this.handleRouteAssignmentTransmitReport(result, nodeId);
|
|
2947
|
+
if (success) {
|
|
2948
|
+
// Update the cached priority route
|
|
2949
|
+
this.setPriorityReturnRouteCached(nodeId, destinationNodeId, {
|
|
2950
|
+
repeaters,
|
|
2951
|
+
routeSpeed,
|
|
2952
|
+
});
|
|
2953
|
+
}
|
|
2954
|
+
return success;
|
|
2803
2955
|
}
|
|
2804
2956
|
catch (e) {
|
|
2805
2957
|
this.driver.controllerLog.logNode(nodeId, `Assigning priority return route failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
|
|
2806
2958
|
return false;
|
|
2807
2959
|
}
|
|
2808
2960
|
}
|
|
2961
|
+
hasPriorityReturnRouteCached(nodeId, destinationNodeId) {
|
|
2962
|
+
const ret = this.driver.cacheGet(NetworkCache_1.cacheKeys.node(nodeId).priorityReturnRoute(destinationNodeId));
|
|
2963
|
+
if (ret === core_1.UNKNOWN_STATE)
|
|
2964
|
+
return core_1.UNKNOWN_STATE;
|
|
2965
|
+
return ret !== undefined;
|
|
2966
|
+
}
|
|
2967
|
+
setPriorityReturnRouteCached(nodeId, destinationNodeId, route) {
|
|
2968
|
+
this.driver.cacheSet(NetworkCache_1.cacheKeys.node(nodeId).priorityReturnRoute(destinationNodeId), route);
|
|
2969
|
+
}
|
|
2970
|
+
clearPriorityReturnRoutesCached(nodeId) {
|
|
2971
|
+
// This is a bit ugly, but the best we can do right now.
|
|
2972
|
+
for (let dest = 1; dest <= core_1.MAX_NODES; dest++) {
|
|
2973
|
+
this.setPriorityReturnRouteCached(nodeId, dest, undefined);
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
/**
|
|
2977
|
+
* Returns which priority route is currently assigned between the given end nodes.
|
|
2978
|
+
*
|
|
2979
|
+
* **Note:** This is using cached information, since there's no way to query priority routes from a node.
|
|
2980
|
+
* If another controller has assigned routes in the meantime, this information may be out of date.
|
|
2981
|
+
*/
|
|
2982
|
+
getPriorityReturnRouteCached(nodeId, destinationNodeId) {
|
|
2983
|
+
return this.driver.cacheGet(NetworkCache_1.cacheKeys.node(nodeId).priorityReturnRoute(destinationNodeId));
|
|
2984
|
+
}
|
|
2809
2985
|
/**
|
|
2810
2986
|
* Assigns a priority route from an end node to the SUC. This route will always be used for the first transmission attempt.
|
|
2811
2987
|
* @param nodeId The ID of the end node for which to assign the route
|
|
@@ -2823,13 +2999,36 @@ ${associatedNodes.join(", ")}`,
|
|
|
2823
2999
|
repeaters,
|
|
2824
3000
|
routeSpeed,
|
|
2825
3001
|
}));
|
|
2826
|
-
|
|
3002
|
+
const success = this.handleRouteAssignmentTransmitReport(result, nodeId);
|
|
3003
|
+
if (success) {
|
|
3004
|
+
// Update the cached priority route
|
|
3005
|
+
this.setPrioritySUCReturnRouteCached(nodeId, {
|
|
3006
|
+
repeaters,
|
|
3007
|
+
routeSpeed,
|
|
3008
|
+
});
|
|
3009
|
+
// The command above assigns a full set of new routes, so
|
|
3010
|
+
// custom SUC return routes are no longer valid
|
|
3011
|
+
this.setCustomSUCReturnRoutesCached(nodeId, undefined);
|
|
3012
|
+
}
|
|
3013
|
+
return success;
|
|
2827
3014
|
}
|
|
2828
3015
|
catch (e) {
|
|
2829
3016
|
this.driver.controllerLog.logNode(nodeId, `Assigning priority SUC return route failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
|
|
2830
3017
|
return false;
|
|
2831
3018
|
}
|
|
2832
3019
|
}
|
|
3020
|
+
setPrioritySUCReturnRouteCached(nodeId, route) {
|
|
3021
|
+
this.driver.cacheSet(NetworkCache_1.cacheKeys.node(nodeId).prioritySUCReturnRoute, route);
|
|
3022
|
+
}
|
|
3023
|
+
/**
|
|
3024
|
+
* Returns which priority route is currently assigned from the given end node to the SUC.
|
|
3025
|
+
*
|
|
3026
|
+
* **Note:** This is using cached information, since there's no way to query priority routes from a node.
|
|
3027
|
+
* If another controller has assigned routes in the meantime, this information may be out of date.
|
|
3028
|
+
*/
|
|
3029
|
+
getPrioritySUCReturnRouteCached(nodeId) {
|
|
3030
|
+
return this.driver.cacheGet(NetworkCache_1.cacheKeys.node(nodeId).prioritySUCReturnRoute);
|
|
3031
|
+
}
|
|
2833
3032
|
handleRouteAssignmentTransmitReport(msg, nodeId) {
|
|
2834
3033
|
switch (msg.transmitStatus) {
|
|
2835
3034
|
case core_1.TransmitStatus.OK:
|
|
@@ -2980,7 +3179,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
2980
3179
|
// Nodes need a return route to be able to send commands to other nodes
|
|
2981
3180
|
const destinationNodeIDs = (0, arrays_1.distinct)(destinations.map((d) => d.nodeId)).filter((id) => id !== this.ownNodeId);
|
|
2982
3181
|
for (const id of destinationNodeIDs) {
|
|
2983
|
-
await this.
|
|
3182
|
+
await this.assignReturnRoutes(source.nodeId, id);
|
|
2984
3183
|
}
|
|
2985
3184
|
}
|
|
2986
3185
|
/**
|
|
@@ -3243,6 +3442,35 @@ ${associatedNodes.join(", ")}`,
|
|
|
3243
3442
|
}
|
|
3244
3443
|
return result.maxPayloadSize;
|
|
3245
3444
|
}
|
|
3445
|
+
/**
|
|
3446
|
+
* Instructs a node to (re-)discover its neighbors.
|
|
3447
|
+
*
|
|
3448
|
+
* **WARNING:** On some controllers, this can cause new SUC return routes to be assigned.
|
|
3449
|
+
*
|
|
3450
|
+
* @returns `true` if the update was successful and the new neighbors can be retrieved using
|
|
3451
|
+
* {@link getKnownNodeNeighbors}. `false` if the update failed.
|
|
3452
|
+
*/
|
|
3453
|
+
async discoverNodeNeighbors(nodeId) {
|
|
3454
|
+
// TODO: Consider making this not block the send queue.
|
|
3455
|
+
// However, I haven't actually seen a UpdateStarted callback in the wild,
|
|
3456
|
+
// so we don't know if that would even work.
|
|
3457
|
+
// During inclusion, the timeout is mainly required for the node to detect all neighbors
|
|
3458
|
+
// We do the same here, so we just reuse the timeout
|
|
3459
|
+
const discoveryTimeout = (0, AddNodeToNetworkRequest_1.computeNeighborDiscoveryTimeout)(this.driver,
|
|
3460
|
+
// Controllers take longer, just assume the worst case here
|
|
3461
|
+
core_1.NodeType.Controller);
|
|
3462
|
+
const resp = await this.driver.sendMessage(new RequestNodeNeighborUpdateMessages_1.RequestNodeNeighborUpdateRequest(this.driver, {
|
|
3463
|
+
nodeId,
|
|
3464
|
+
discoveryTimeout,
|
|
3465
|
+
}));
|
|
3466
|
+
const success = resp.updateStatus === RequestNodeNeighborUpdateMessages_1.NodeNeighborUpdateStatus.UpdateDone;
|
|
3467
|
+
if (success) {
|
|
3468
|
+
// Not sure why, but Zniffer traces show that a node neighbor update can cause the controller to
|
|
3469
|
+
// also do AssignSUCReturnRoute. As a result, we need to invalidate our route cache.
|
|
3470
|
+
this.setCustomSUCReturnRoutesCached(nodeId, undefined);
|
|
3471
|
+
}
|
|
3472
|
+
return success;
|
|
3473
|
+
}
|
|
3246
3474
|
/**
|
|
3247
3475
|
* Returns the known list of neighbors for a node
|
|
3248
3476
|
*/
|
|
@@ -4199,8 +4427,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
4199
4427
|
}
|
|
4200
4428
|
}
|
|
4201
4429
|
};
|
|
4202
|
-
ZWaveController = __decorate([
|
|
4430
|
+
exports.ZWaveController = ZWaveController = __decorate([
|
|
4203
4431
|
(0, shared_1.Mixin)([ControllerStatistics_1.ControllerStatisticsHost])
|
|
4204
4432
|
], ZWaveController);
|
|
4205
|
-
exports.ZWaveController = ZWaveController;
|
|
4206
4433
|
//# sourceMappingURL=Controller.js.map
|