zwave-js 8.7.7 → 8.8.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.
- package/build/CommandClass.js +1 -0
- package/build/CommandClass.js.map +1 -1
- package/build/Controller.d.ts +1 -0
- package/build/Controller.d.ts.map +1 -1
- package/build/Controller.js +4 -1
- package/build/Controller.js.map +1 -1
- package/build/Node.d.ts +1 -1
- package/build/Node.d.ts.map +1 -1
- package/build/Node.js.map +1 -1
- package/build/Utils.d.ts +1 -0
- package/build/Utils.d.ts.map +1 -1
- package/build/Utils.js +7 -1
- package/build/Utils.js.map +1 -1
- package/build/lib/commandclass/API.d.ts +15 -3
- package/build/lib/commandclass/API.d.ts.map +1 -1
- package/build/lib/commandclass/API.js +50 -0
- package/build/lib/commandclass/API.js.map +1 -1
- package/build/lib/commandclass/CommandClass.d.ts +0 -6
- package/build/lib/commandclass/CommandClass.d.ts.map +1 -1
- package/build/lib/commandclass/CommandClass.js +0 -12
- package/build/lib/commandclass/CommandClass.js.map +1 -1
- package/build/lib/commandclass/Security2CC.d.ts +1 -10
- package/build/lib/commandclass/Security2CC.d.ts.map +1 -1
- package/build/lib/commandclass/Security2CC.js +16 -48
- package/build/lib/commandclass/Security2CC.js.map +1 -1
- package/build/lib/commandclass/SecurityCC.d.ts +2 -9
- package/build/lib/commandclass/SecurityCC.d.ts.map +1 -1
- package/build/lib/commandclass/SecurityCC.js +9 -53
- package/build/lib/commandclass/SecurityCC.js.map +1 -1
- package/build/lib/commandclass/WakeUpCC.d.ts.map +1 -1
- package/build/lib/commandclass/WakeUpCC.js +1 -0
- package/build/lib/commandclass/WakeUpCC.js.map +1 -1
- package/build/lib/controller/BridgeApplicationCommandRequest.d.ts +2 -6
- package/build/lib/controller/BridgeApplicationCommandRequest.d.ts.map +1 -1
- package/build/lib/controller/BridgeApplicationCommandRequest.js +9 -19
- package/build/lib/controller/BridgeApplicationCommandRequest.js.map +1 -1
- package/build/lib/controller/Controller.d.ts +12 -2
- package/build/lib/controller/Controller.d.ts.map +1 -1
- package/build/lib/controller/Controller.js +14 -6
- package/build/lib/controller/Controller.js.map +1 -1
- package/build/lib/controller/Inclusion.d.ts +1 -1
- package/build/lib/controller/SendDataBridgeMessages.d.ts +5 -3
- package/build/lib/controller/SendDataBridgeMessages.d.ts.map +1 -1
- package/build/lib/controller/SendDataBridgeMessages.js +21 -8
- package/build/lib/controller/SendDataBridgeMessages.js.map +1 -1
- package/build/lib/controller/SendDataMessages.d.ts +5 -3
- package/build/lib/controller/SendDataMessages.d.ts.map +1 -1
- package/build/lib/controller/SendDataMessages.js +21 -9
- package/build/lib/controller/SendDataMessages.js.map +1 -1
- package/build/lib/controller/SendDataShared.d.ts +72 -2
- package/build/lib/controller/SendDataShared.d.ts.map +1 -1
- package/build/lib/controller/SendDataShared.js +195 -1
- package/build/lib/controller/SendDataShared.js.map +1 -1
- package/build/lib/driver/CommandQueueMachine.d.ts +7 -3
- package/build/lib/driver/CommandQueueMachine.d.ts.map +1 -1
- package/build/lib/driver/CommandQueueMachine.js +66 -29
- package/build/lib/driver/CommandQueueMachine.js.map +1 -1
- package/build/lib/driver/Driver.d.ts +16 -12
- package/build/lib/driver/Driver.d.ts.map +1 -1
- package/build/lib/driver/Driver.js +178 -163
- package/build/lib/driver/Driver.js.map +1 -1
- package/build/lib/driver/MessageGenerators.d.ts +26 -0
- package/build/lib/driver/MessageGenerators.d.ts.map +1 -0
- package/build/lib/driver/MessageGenerators.js +242 -0
- package/build/lib/driver/MessageGenerators.js.map +1 -0
- package/build/lib/driver/SendThreadMachine.d.ts +24 -53
- package/build/lib/driver/SendThreadMachine.d.ts.map +1 -1
- package/build/lib/driver/SendThreadMachine.js +160 -618
- package/build/lib/driver/SendThreadMachine.js.map +1 -1
- package/build/lib/driver/StateMachineShared.d.ts +2 -0
- package/build/lib/driver/StateMachineShared.d.ts.map +1 -1
- package/build/lib/driver/StateMachineShared.js +21 -10
- package/build/lib/driver/StateMachineShared.js.map +1 -1
- package/build/lib/driver/Transaction.d.ts +35 -3
- package/build/lib/driver/Transaction.d.ts.map +1 -1
- package/build/lib/driver/Transaction.js +20 -15
- package/build/lib/driver/Transaction.js.map +1 -1
- package/build/lib/driver/TransactionMachine.d.ts +30 -0
- package/build/lib/driver/TransactionMachine.d.ts.map +1 -0
- package/build/lib/driver/TransactionMachine.js +247 -0
- package/build/lib/driver/TransactionMachine.js.map +1 -0
- package/build/lib/driver/ZWaveOptions.d.ts +0 -2
- package/build/lib/driver/ZWaveOptions.d.ts.map +1 -1
- package/build/lib/message/Constants.d.ts +8 -9
- package/build/lib/message/Constants.d.ts.map +1 -1
- package/build/lib/message/Constants.js +9 -12
- package/build/lib/message/Constants.js.map +1 -1
- package/build/lib/message/Message.d.ts +5 -1
- package/build/lib/message/Message.d.ts.map +1 -1
- package/build/lib/message/Message.js +11 -0
- package/build/lib/message/Message.js.map +1 -1
- package/build/lib/node/HealthCheck.d.ts +8 -0
- package/build/lib/node/HealthCheck.d.ts.map +1 -0
- package/build/lib/node/HealthCheck.js +83 -0
- package/build/lib/node/HealthCheck.js.map +1 -0
- package/build/lib/node/Node.d.ts +11 -3
- package/build/lib/node/Node.d.ts.map +1 -1
- package/build/lib/node/Node.js +305 -21
- package/build/lib/node/Node.js.map +1 -1
- package/build/lib/node/Types.d.ts +133 -2
- package/build/lib/node/Types.d.ts.map +1 -1
- package/build/lib/node/Types.js.map +1 -1
- package/build/lib/serialapi/misc/GetBackgroundRSSIMessages.d.ts +14 -0
- package/build/lib/serialapi/misc/GetBackgroundRSSIMessages.d.ts.map +1 -0
- package/build/lib/serialapi/misc/GetBackgroundRSSIMessages.js +46 -0
- package/build/lib/serialapi/misc/GetBackgroundRSSIMessages.js.map +1 -0
- package/package.json +14 -14
package/build/lib/node/Node.js
CHANGED
|
@@ -38,10 +38,12 @@ const WakeUpCC_1 = require("../commandclass/WakeUpCC");
|
|
|
38
38
|
const ZWavePlusCC_1 = require("../commandclass/ZWavePlusCC");
|
|
39
39
|
const ApplicationUpdateRequest_1 = require("../controller/ApplicationUpdateRequest");
|
|
40
40
|
const GetNodeProtocolInfoMessages_1 = require("../controller/GetNodeProtocolInfoMessages");
|
|
41
|
+
const SendDataShared_1 = require("../controller/SendDataShared");
|
|
41
42
|
const StateMachineShared_1 = require("../driver/StateMachineShared");
|
|
42
43
|
const Constants_1 = require("../message/Constants");
|
|
43
44
|
const DeviceClass_1 = require("./DeviceClass");
|
|
44
45
|
const Endpoint_1 = require("./Endpoint");
|
|
46
|
+
const HealthCheck_1 = require("./HealthCheck");
|
|
45
47
|
const NodeReadyMachine_1 = require("./NodeReadyMachine");
|
|
46
48
|
const NodeStatistics_1 = require("./NodeStatistics");
|
|
47
49
|
const NodeStatusMachine_1 = require("./NodeStatusMachine");
|
|
@@ -104,6 +106,9 @@ let ZWaveNode = class ZWaveNode extends Endpoint_1.Endpoint {
|
|
|
104
106
|
// Also avoid verifying a value change for which we recently received an update
|
|
105
107
|
for (const event of ["value updated", "value removed"]) {
|
|
106
108
|
this._valueDB.on(event, (args) => {
|
|
109
|
+
// Value updates caused by the driver should never cancel a scheduled poll
|
|
110
|
+
if ("source" in args && args.source === "driver")
|
|
111
|
+
return;
|
|
107
112
|
if (this.cancelScheduledPoll(args)) {
|
|
108
113
|
this.driver.controllerLog.logNode(this.nodeId, "Scheduled poll canceled because value was updated", "verbose");
|
|
109
114
|
}
|
|
@@ -179,26 +184,31 @@ let ZWaveNode = class ZWaveNode extends Endpoint_1.Endpoint {
|
|
|
179
184
|
translateValueEvent(eventName, arg) {
|
|
180
185
|
// Try to retrieve the speaking CC name
|
|
181
186
|
const outArg = this.translateValueID(arg);
|
|
187
|
+
// @ts-expect-error This can happen for value updated events
|
|
188
|
+
if ("source" in outArg)
|
|
189
|
+
delete outArg.source;
|
|
182
190
|
// If this is a metadata event, make sure we return the merged metadata
|
|
183
191
|
if ("metadata" in outArg) {
|
|
184
192
|
outArg.metadata =
|
|
185
193
|
this.getValueMetadata(arg);
|
|
186
194
|
}
|
|
187
|
-
// Log the value change
|
|
188
195
|
const ccInstance = this.createCCInstanceInternal(arg.commandClass);
|
|
189
196
|
const isInternalValue = ccInstance && ccInstance.isInternalValue(arg.property);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
197
|
+
if (arg.source !== "driver") {
|
|
198
|
+
// Log the value change, except for updates caused by the driver itself
|
|
199
|
+
// I don't like the splitting and any but its the easiest solution here
|
|
200
|
+
const [changeTarget, changeType] = eventName.split(" ");
|
|
201
|
+
const logArgument = {
|
|
202
|
+
...outArg,
|
|
203
|
+
nodeId: this.nodeId,
|
|
204
|
+
internal: isInternalValue,
|
|
205
|
+
};
|
|
206
|
+
if (changeTarget === "value") {
|
|
207
|
+
this.driver.controllerLog.value(changeType, logArgument);
|
|
208
|
+
}
|
|
209
|
+
else if (changeTarget === "metadata") {
|
|
210
|
+
this.driver.controllerLog.metadataUpdated(logArgument);
|
|
211
|
+
}
|
|
202
212
|
}
|
|
203
213
|
//Don't expose value events for internal value IDs...
|
|
204
214
|
if (isInternalValue)
|
|
@@ -604,7 +614,7 @@ let ZWaveNode = class ZWaveNode extends Endpoint_1.Endpoint {
|
|
|
604
614
|
if (api.isSetValueOptimistic(valueId)) {
|
|
605
615
|
// If the call did not throw, assume that the call was successful and remember the new value
|
|
606
616
|
this._valueDB.setValue(valueId, value, !!this.driver.options.emitValueUpdateAfterSetValue
|
|
607
|
-
?
|
|
617
|
+
? { source: "driver" }
|
|
608
618
|
: { noEvent: true });
|
|
609
619
|
}
|
|
610
620
|
return true;
|
|
@@ -843,14 +853,18 @@ let ZWaveNode = class ZWaveNode extends Endpoint_1.Endpoint {
|
|
|
843
853
|
* **WARNING:** Take care NOT to call this method when the node is already being interviewed.
|
|
844
854
|
* Otherwise the node information may become inconsistent.
|
|
845
855
|
*/
|
|
846
|
-
async refreshInfo() {
|
|
856
|
+
async refreshInfo(options = {}) {
|
|
847
857
|
// It does not make sense to re-interview the controller. All important information is queried
|
|
848
858
|
// directly via the serial API
|
|
849
859
|
if (this.isControllerNode())
|
|
850
860
|
return;
|
|
861
|
+
const { resetSecurityClasses = false } = options;
|
|
851
862
|
// preserve the node name and location, since they might not be stored on the node
|
|
852
863
|
const name = this.name;
|
|
853
864
|
const location = this.location;
|
|
865
|
+
// Force a new detection of security classes if desired
|
|
866
|
+
if (resetSecurityClasses)
|
|
867
|
+
this.securityClasses.clear();
|
|
854
868
|
this._interviewAttempts = 0;
|
|
855
869
|
this.interviewStage = Types_1.InterviewStage.None;
|
|
856
870
|
this._ready = false;
|
|
@@ -1787,8 +1801,11 @@ protocol version: ${this._protocolVersion}`;
|
|
|
1787
1801
|
// the shared SPAN, since it did the same
|
|
1788
1802
|
secMan.storeRemoteEI(this.nodeId, command.receiverEI);
|
|
1789
1803
|
}
|
|
1790
|
-
|
|
1791
|
-
|
|
1804
|
+
this.driver.controllerLog.logNode(this.id, {
|
|
1805
|
+
message: `received S2 nonce, not sure what to do with it`,
|
|
1806
|
+
level: "warn",
|
|
1807
|
+
direction: "inbound",
|
|
1808
|
+
});
|
|
1792
1809
|
}
|
|
1793
1810
|
async handleHail(_command) {
|
|
1794
1811
|
// treat this as a sign that the node is awake
|
|
@@ -2959,7 +2976,7 @@ protocol version: ${this._protocolVersion}`;
|
|
|
2959
2976
|
*
|
|
2960
2977
|
* **Note:** Depending on the number of test frames, this may take a while
|
|
2961
2978
|
*/
|
|
2962
|
-
async testPowerlevel(testNodeId, powerlevel,
|
|
2979
|
+
async testPowerlevel(testNodeId, powerlevel, healthCheckTestFrameCount, onProgress) {
|
|
2963
2980
|
const api = this.commandClasses.Powerlevel;
|
|
2964
2981
|
// Keep sleeping nodes awake
|
|
2965
2982
|
const wasKeptAwake = this.keepAwake;
|
|
@@ -2971,10 +2988,10 @@ protocol version: ${this._protocolVersion}`;
|
|
|
2971
2988
|
return value;
|
|
2972
2989
|
};
|
|
2973
2990
|
// Start the process
|
|
2974
|
-
await api.startNodeTest(testNodeId, powerlevel,
|
|
2991
|
+
await api.startNodeTest(testNodeId, powerlevel, healthCheckTestFrameCount);
|
|
2975
2992
|
// Each frame will take a few ms to be sent, let's assume 5 per second
|
|
2976
2993
|
// to estimate how long the test will take
|
|
2977
|
-
const expectedDurationMs = Math.round((
|
|
2994
|
+
const expectedDurationMs = Math.round((healthCheckTestFrameCount / 5) * 1000);
|
|
2978
2995
|
// Poll the status of the test regularly
|
|
2979
2996
|
const pollFrequencyMs = expectedDurationMs >= 60000
|
|
2980
2997
|
? 10000
|
|
@@ -3011,10 +3028,277 @@ protocol version: ${this._protocolVersion}`;
|
|
|
3011
3028
|
}
|
|
3012
3029
|
else if (onProgress) {
|
|
3013
3030
|
// Notify the caller of the test progress
|
|
3014
|
-
onProgress(status.acknowledgedFrames,
|
|
3031
|
+
onProgress(status.acknowledgedFrames, healthCheckTestFrameCount);
|
|
3015
3032
|
}
|
|
3016
3033
|
}
|
|
3017
3034
|
}
|
|
3035
|
+
/**
|
|
3036
|
+
* Checks the health of connection between the controller and this node and returns the results.
|
|
3037
|
+
*/
|
|
3038
|
+
async checkLifelineHealth(rounds = 5, onProgress) {
|
|
3039
|
+
if (rounds > 10 || rounds < 1) {
|
|
3040
|
+
throw new core_1.ZWaveError("The number of health check rounds must be between 1 and 10!", core_1.ZWaveErrorCodes.Argument_Invalid);
|
|
3041
|
+
}
|
|
3042
|
+
// No. of pings per round
|
|
3043
|
+
const start = Date.now();
|
|
3044
|
+
/** Computes a health rating from a health check result */
|
|
3045
|
+
const computeRating = (result) => {
|
|
3046
|
+
var _a, _b, _c;
|
|
3047
|
+
const failedPings = Math.max((_a = result.failedPingsController) !== null && _a !== void 0 ? _a : 0, result.failedPingsNode);
|
|
3048
|
+
const numNeighbors = result.numNeighbors;
|
|
3049
|
+
const minPowerlevel = (_b = result.minPowerlevel) !== null && _b !== void 0 ? _b : PowerlevelCC_1.Powerlevel["-6 dBm"];
|
|
3050
|
+
const snrMargin = (_c = result.snrMargin) !== null && _c !== void 0 ? _c : 17;
|
|
3051
|
+
const latency = result.latency;
|
|
3052
|
+
if (failedPings === 10)
|
|
3053
|
+
return 0;
|
|
3054
|
+
if (failedPings > 2)
|
|
3055
|
+
return 1;
|
|
3056
|
+
if (failedPings === 2 || latency > 1000)
|
|
3057
|
+
return 2;
|
|
3058
|
+
if (failedPings === 1 || latency > 500)
|
|
3059
|
+
return 3;
|
|
3060
|
+
if (latency > 250)
|
|
3061
|
+
return 4;
|
|
3062
|
+
if (latency > 100)
|
|
3063
|
+
return 5;
|
|
3064
|
+
if (minPowerlevel < PowerlevelCC_1.Powerlevel["-6 dBm"] || snrMargin < 17) {
|
|
3065
|
+
// Lower powerlevel reductions (= higher power) have lower numeric values
|
|
3066
|
+
return numNeighbors > 2 ? 7 : 6;
|
|
3067
|
+
}
|
|
3068
|
+
if (numNeighbors <= 2)
|
|
3069
|
+
return 8;
|
|
3070
|
+
if (latency > 50)
|
|
3071
|
+
return 9;
|
|
3072
|
+
return 10;
|
|
3073
|
+
};
|
|
3074
|
+
this.driver.controllerLog.logNode(this.id, `Starting lifeline health check (${rounds} round${rounds !== 1 ? "s" : ""})...`);
|
|
3075
|
+
const results = [];
|
|
3076
|
+
for (let round = 1; round <= rounds; round++) {
|
|
3077
|
+
// Determine the number of repeating neighbors
|
|
3078
|
+
const numNeighbors = (await this.driver.controller.getNodeNeighbors(this.nodeId, true)).length;
|
|
3079
|
+
// Ping the node 10x, measuring the RSSI
|
|
3080
|
+
let txReport;
|
|
3081
|
+
let routeChanges;
|
|
3082
|
+
let rssi;
|
|
3083
|
+
let channel;
|
|
3084
|
+
let snrMargin;
|
|
3085
|
+
let failedPingsNode = 0;
|
|
3086
|
+
let latency = 0;
|
|
3087
|
+
const pingAPI = this.commandClasses["No Operation"].withOptions({
|
|
3088
|
+
onTXReport: (report) => {
|
|
3089
|
+
txReport = report;
|
|
3090
|
+
},
|
|
3091
|
+
});
|
|
3092
|
+
for (let i = 1; i <= HealthCheck_1.healthCheckTestFrameCount; i++) {
|
|
3093
|
+
const start = Date.now();
|
|
3094
|
+
const pingResult = await pingAPI.send().then(() => true, () => false);
|
|
3095
|
+
const rtt = Date.now() - start;
|
|
3096
|
+
latency = Math.max(latency, txReport ? txReport.txTicks * 10 : rtt);
|
|
3097
|
+
if (!pingResult) {
|
|
3098
|
+
failedPingsNode++;
|
|
3099
|
+
}
|
|
3100
|
+
else if (txReport) {
|
|
3101
|
+
routeChanges !== null && routeChanges !== void 0 ? routeChanges : (routeChanges = 0);
|
|
3102
|
+
if (txReport.routingAttempts > 1) {
|
|
3103
|
+
routeChanges++;
|
|
3104
|
+
}
|
|
3105
|
+
rssi = txReport.ackRSSI;
|
|
3106
|
+
channel = txReport.ackChannelNo;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
// If possible, compute the SNR margin from the test results
|
|
3110
|
+
if (rssi != undefined &&
|
|
3111
|
+
rssi < SendDataShared_1.RssiError.NoSignalDetected &&
|
|
3112
|
+
channel != undefined) {
|
|
3113
|
+
const backgroundRSSI = await this.driver.controller.getBackgroundRSSI();
|
|
3114
|
+
if (`rssiChannel${channel}` in backgroundRSSI) {
|
|
3115
|
+
snrMargin =
|
|
3116
|
+
rssi - backgroundRSSI[`rssiChannel${channel}`];
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
const ret = {
|
|
3120
|
+
latency,
|
|
3121
|
+
failedPingsNode,
|
|
3122
|
+
numNeighbors,
|
|
3123
|
+
routeChanges,
|
|
3124
|
+
snrMargin,
|
|
3125
|
+
rating: 0,
|
|
3126
|
+
};
|
|
3127
|
+
// Now instruct the node to ping the controller, figuring out the minimum powerlevel
|
|
3128
|
+
if (this.supportsCC(core_1.CommandClasses.Powerlevel)) {
|
|
3129
|
+
// Do a binary search and find the highest reduction in powerlevel for which there are no errors
|
|
3130
|
+
let failedPingsController = 0;
|
|
3131
|
+
const executor = async (powerlevel) => {
|
|
3132
|
+
this.driver.controllerLog.logNode(this.id, `Sending ${HealthCheck_1.healthCheckTestFrameCount} pings to controller at ${(0, shared_1.getEnumMemberName)(PowerlevelCC_1.Powerlevel, powerlevel)}...`);
|
|
3133
|
+
const result = await this.testPowerlevel(this.driver.controller.ownNodeId, powerlevel, HealthCheck_1.healthCheckTestFrameCount);
|
|
3134
|
+
failedPingsController = HealthCheck_1.healthCheckTestFrameCount - result;
|
|
3135
|
+
this.driver.controllerLog.logNode(this.id, `At ${(0, shared_1.getEnumMemberName)(PowerlevelCC_1.Powerlevel, powerlevel)}, ${result}/${HealthCheck_1.healthCheckTestFrameCount} pings were acknowledged...`);
|
|
3136
|
+
return failedPingsController === 0;
|
|
3137
|
+
};
|
|
3138
|
+
try {
|
|
3139
|
+
const powerlevel = await (0, shared_1.discreteBinarySearch)(PowerlevelCC_1.Powerlevel["Normal Power"], // minimum reduction
|
|
3140
|
+
PowerlevelCC_1.Powerlevel["-9 dBm"], // maximum reduction
|
|
3141
|
+
executor);
|
|
3142
|
+
if (powerlevel == undefined) {
|
|
3143
|
+
// There were still failures at normal power, report it
|
|
3144
|
+
ret.minPowerlevel = PowerlevelCC_1.Powerlevel["Normal Power"];
|
|
3145
|
+
ret.failedPingsController = failedPingsController;
|
|
3146
|
+
}
|
|
3147
|
+
else {
|
|
3148
|
+
ret.minPowerlevel = powerlevel;
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
catch (e) {
|
|
3152
|
+
if ((0, core_1.isZWaveError)(e) &&
|
|
3153
|
+
e.code === core_1.ZWaveErrorCodes.Controller_CallbackNOK) {
|
|
3154
|
+
// The node is dead, treat this as a failure
|
|
3155
|
+
ret.minPowerlevel = PowerlevelCC_1.Powerlevel["Normal Power"];
|
|
3156
|
+
ret.failedPingsController = HealthCheck_1.healthCheckTestFrameCount;
|
|
3157
|
+
}
|
|
3158
|
+
else {
|
|
3159
|
+
throw e;
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
ret.rating = computeRating(ret);
|
|
3164
|
+
results.push(ret);
|
|
3165
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(round, rounds, ret.rating);
|
|
3166
|
+
}
|
|
3167
|
+
const duration = Date.now() - start;
|
|
3168
|
+
const rating = Math.min(...results.map((r) => r.rating));
|
|
3169
|
+
const summary = { results, rating };
|
|
3170
|
+
this.driver.controllerLog.logNode(this.id, `Lifeline health check complete in ${duration} ms
|
|
3171
|
+
${(0, HealthCheck_1.formatLifelineHealthCheckSummary)(summary)}`);
|
|
3172
|
+
return summary;
|
|
3173
|
+
}
|
|
3174
|
+
/**
|
|
3175
|
+
* Checks the health of connection between this node and the target node and returns the results.
|
|
3176
|
+
*/
|
|
3177
|
+
async checkRouteHealth(targetNodeId, rounds = 5, onProgress) {
|
|
3178
|
+
if (rounds > 10 || rounds < 1) {
|
|
3179
|
+
throw new core_1.ZWaveError("The number of health check rounds must be between 1 and 10!", core_1.ZWaveErrorCodes.Argument_Invalid);
|
|
3180
|
+
}
|
|
3181
|
+
const otherNode = this.driver.controller.nodes.getOrThrow(targetNodeId);
|
|
3182
|
+
if (!this.supportsCC(core_1.CommandClasses.Powerlevel) &&
|
|
3183
|
+
!otherNode.supportsCC(core_1.CommandClasses.Powerlevel)) {
|
|
3184
|
+
throw new core_1.ZWaveError("For a route health check, at least one of the nodes must support Powerlevel CC!", core_1.ZWaveErrorCodes.CC_NotSupported);
|
|
3185
|
+
}
|
|
3186
|
+
// No. of pings per round
|
|
3187
|
+
const healthCheckTestFrameCount = 10;
|
|
3188
|
+
const start = Date.now();
|
|
3189
|
+
/** Computes a health rating from a health check result */
|
|
3190
|
+
const computeRating = (result) => {
|
|
3191
|
+
var _a, _b, _c, _d;
|
|
3192
|
+
const failedPings = Math.max((_a = result.failedPingsToSource) !== null && _a !== void 0 ? _a : 0, (_b = result.failedPingsToTarget) !== null && _b !== void 0 ? _b : 0);
|
|
3193
|
+
const numNeighbors = result.numNeighbors;
|
|
3194
|
+
const minPowerlevel = Math.max((_c = result.minPowerlevelSource) !== null && _c !== void 0 ? _c : PowerlevelCC_1.Powerlevel["-6 dBm"], (_d = result.minPowerlevelTarget) !== null && _d !== void 0 ? _d : PowerlevelCC_1.Powerlevel["-6 dBm"]);
|
|
3195
|
+
if (failedPings === 10)
|
|
3196
|
+
return 0;
|
|
3197
|
+
if (failedPings > 2)
|
|
3198
|
+
return 1;
|
|
3199
|
+
if (failedPings === 2)
|
|
3200
|
+
return 2;
|
|
3201
|
+
if (failedPings === 1)
|
|
3202
|
+
return 3;
|
|
3203
|
+
if (minPowerlevel < PowerlevelCC_1.Powerlevel["-6 dBm"]) {
|
|
3204
|
+
// Lower powerlevel reductions (= higher power) have lower numeric values
|
|
3205
|
+
return numNeighbors > 2 ? 7 : 6;
|
|
3206
|
+
}
|
|
3207
|
+
if (numNeighbors <= 2)
|
|
3208
|
+
return 8;
|
|
3209
|
+
return 10;
|
|
3210
|
+
};
|
|
3211
|
+
this.driver.controllerLog.logNode(this.id, `Starting route health check to node ${targetNodeId} (${rounds} round${rounds !== 1 ? "s" : ""})...`);
|
|
3212
|
+
const results = [];
|
|
3213
|
+
for (let round = 1; round <= rounds; round++) {
|
|
3214
|
+
// Determine the minimum number of repeating neighbors between the
|
|
3215
|
+
// source and target node
|
|
3216
|
+
const numNeighbors = Math.min((await this.driver.controller.getNodeNeighbors(this.nodeId, true)).length, (await this.driver.controller.getNodeNeighbors(targetNodeId, true)).length);
|
|
3217
|
+
let failedPings = 0;
|
|
3218
|
+
let failedPingsToSource;
|
|
3219
|
+
let minPowerlevelSource;
|
|
3220
|
+
let failedPingsToTarget;
|
|
3221
|
+
let minPowerlevelTarget;
|
|
3222
|
+
const executor = (node, otherNode) => async (powerlevel) => {
|
|
3223
|
+
this.driver.controllerLog.logNode(node.id, `Sending ${healthCheckTestFrameCount} pings to node ${otherNode.id} at ${(0, shared_1.getEnumMemberName)(PowerlevelCC_1.Powerlevel, powerlevel)}...`);
|
|
3224
|
+
const result = await node.testPowerlevel(otherNode.id, powerlevel, healthCheckTestFrameCount);
|
|
3225
|
+
failedPings = healthCheckTestFrameCount - result;
|
|
3226
|
+
this.driver.controllerLog.logNode(node.id, `At ${(0, shared_1.getEnumMemberName)(PowerlevelCC_1.Powerlevel, powerlevel)}, ${result}/${healthCheckTestFrameCount} pings were acknowledged by node ${otherNode.id}...`);
|
|
3227
|
+
return failedPings === 0;
|
|
3228
|
+
};
|
|
3229
|
+
// Now instruct this node to ping the other one, figuring out the minimum powerlevel
|
|
3230
|
+
if (this.supportsCC(core_1.CommandClasses.Powerlevel)) {
|
|
3231
|
+
try {
|
|
3232
|
+
const powerlevel = await (0, shared_1.discreteBinarySearch)(PowerlevelCC_1.Powerlevel["Normal Power"], // minimum reduction
|
|
3233
|
+
PowerlevelCC_1.Powerlevel["-9 dBm"], // maximum reduction
|
|
3234
|
+
executor(this, otherNode));
|
|
3235
|
+
if (powerlevel == undefined) {
|
|
3236
|
+
// There were still failures at normal power, report it
|
|
3237
|
+
minPowerlevelSource = PowerlevelCC_1.Powerlevel["Normal Power"];
|
|
3238
|
+
failedPingsToTarget = failedPings;
|
|
3239
|
+
}
|
|
3240
|
+
else {
|
|
3241
|
+
minPowerlevelSource = powerlevel;
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
catch (e) {
|
|
3245
|
+
if ((0, core_1.isZWaveError)(e) &&
|
|
3246
|
+
e.code === core_1.ZWaveErrorCodes.Controller_CallbackNOK) {
|
|
3247
|
+
// The node is dead, treat this as a failure
|
|
3248
|
+
minPowerlevelSource = PowerlevelCC_1.Powerlevel["Normal Power"];
|
|
3249
|
+
failedPingsToTarget = healthCheckTestFrameCount;
|
|
3250
|
+
}
|
|
3251
|
+
else {
|
|
3252
|
+
throw e;
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
// And do the same with the other node
|
|
3257
|
+
if (otherNode.supportsCC(core_1.CommandClasses.Powerlevel)) {
|
|
3258
|
+
try {
|
|
3259
|
+
const powerlevel = await (0, shared_1.discreteBinarySearch)(PowerlevelCC_1.Powerlevel["Normal Power"], // minimum reduction
|
|
3260
|
+
PowerlevelCC_1.Powerlevel["-9 dBm"], // maximum reduction
|
|
3261
|
+
executor(otherNode, this));
|
|
3262
|
+
if (powerlevel == undefined) {
|
|
3263
|
+
// There were still failures at normal power, report it
|
|
3264
|
+
minPowerlevelTarget = PowerlevelCC_1.Powerlevel["Normal Power"];
|
|
3265
|
+
failedPingsToSource = failedPings;
|
|
3266
|
+
}
|
|
3267
|
+
else {
|
|
3268
|
+
minPowerlevelTarget = powerlevel;
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
catch (e) {
|
|
3272
|
+
if ((0, core_1.isZWaveError)(e) &&
|
|
3273
|
+
e.code === core_1.ZWaveErrorCodes.Controller_CallbackNOK) {
|
|
3274
|
+
// The node is dead, treat this as a failure
|
|
3275
|
+
minPowerlevelTarget = PowerlevelCC_1.Powerlevel["Normal Power"];
|
|
3276
|
+
failedPingsToSource = healthCheckTestFrameCount;
|
|
3277
|
+
}
|
|
3278
|
+
else {
|
|
3279
|
+
throw e;
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
const ret = {
|
|
3284
|
+
numNeighbors,
|
|
3285
|
+
failedPingsToSource,
|
|
3286
|
+
failedPingsToTarget,
|
|
3287
|
+
minPowerlevelSource,
|
|
3288
|
+
minPowerlevelTarget,
|
|
3289
|
+
rating: 0,
|
|
3290
|
+
};
|
|
3291
|
+
ret.rating = computeRating(ret);
|
|
3292
|
+
results.push(ret);
|
|
3293
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(round, rounds, ret.rating);
|
|
3294
|
+
}
|
|
3295
|
+
const duration = Date.now() - start;
|
|
3296
|
+
const rating = Math.min(...results.map((r) => r.rating));
|
|
3297
|
+
const summary = { results, rating };
|
|
3298
|
+
this.driver.controllerLog.logNode(this.id, `Route health check to node ${otherNode.id} complete in ${duration} ms
|
|
3299
|
+
${(0, HealthCheck_1.formatRouteHealthCheckSummary)(this.id, otherNode.id, summary)}`);
|
|
3300
|
+
return summary;
|
|
3301
|
+
}
|
|
3018
3302
|
};
|
|
3019
3303
|
ZWaveNode = __decorate([
|
|
3020
3304
|
(0, shared_1.Mixin)([events_1.EventEmitter, NodeStatistics_1.NodeStatisticsHost])
|