zwave-js 13.3.1 → 13.5.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 +7 -2
- package/build/lib/controller/Controller.d.ts.map +1 -1
- package/build/lib/controller/Controller.js +255 -196
- package/build/lib/controller/Controller.js.map +1 -1
- package/build/lib/controller/MockControllerBehaviors.js +1 -1
- package/build/lib/controller/MockControllerBehaviors.js.map +1 -1
- package/build/lib/controller/NVMIO.d.ts +48 -0
- package/build/lib/controller/NVMIO.d.ts.map +1 -0
- package/build/lib/controller/NVMIO.js +168 -0
- package/build/lib/controller/NVMIO.js.map +1 -0
- package/build/lib/controller/utils.d.ts +3 -0
- package/build/lib/controller/utils.d.ts.map +1 -1
- package/build/lib/controller/utils.js +19 -13
- package/build/lib/controller/utils.js.map +1 -1
- package/build/lib/driver/Driver.d.ts +3 -0
- package/build/lib/driver/Driver.d.ts.map +1 -1
- package/build/lib/driver/Driver.js +11 -2
- 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 +6 -5
- package/build/lib/driver/MessageGenerators.js.map +1 -1
- package/build/lib/driver/NetworkCache.js +12 -12
- package/build/lib/driver/NetworkCache.js.map +1 -1
- package/build/lib/driver/SerialAPICommandMachine.d.ts +8 -8
- package/build/lib/driver/SerialAPICommandMachine.d.ts.map +1 -1
- package/build/lib/driver/SerialAPICommandMachine.js.map +1 -1
- package/build/lib/driver/Task.d.ts +128 -0
- package/build/lib/driver/Task.d.ts.map +1 -0
- package/build/lib/driver/Task.js +406 -0
- package/build/lib/driver/Task.js.map +1 -0
- package/build/lib/driver/TransportServiceMachine.d.ts +6 -6
- package/build/lib/driver/TransportServiceMachine.d.ts.map +1 -1
- package/build/lib/driver/TransportServiceMachine.js.map +1 -1
- package/build/lib/driver/UpdateConfig.js +3 -3
- package/build/lib/driver/UpdateConfig.js.map +1 -1
- package/build/lib/driver/ZWaveOptions.d.ts +9 -0
- package/build/lib/driver/ZWaveOptions.d.ts.map +1 -1
- package/build/lib/driver/ZWaveOptions.js +9 -0
- package/build/lib/driver/ZWaveOptions.js.map +1 -1
- package/build/lib/node/Node.js +9 -9
- package/build/lib/node/Node.js.map +1 -1
- package/build/lib/node/NodeReadyMachine.d.ts +3 -3
- 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 +5 -5
- package/build/lib/node/NodeStatusMachine.d.ts.map +1 -1
- package/build/lib/node/NodeStatusMachine.js +0 -1
- package/build/lib/node/NodeStatusMachine.js.map +1 -1
- package/build/lib/node/_Types.d.ts +2 -2
- package/build/lib/node/_Types.d.ts.map +1 -1
- package/build/lib/node/_Types.js.map +1 -1
- package/build/lib/zniffer/CCParsingContext.d.ts +3 -3
- package/build/lib/zniffer/CCParsingContext.d.ts.map +1 -1
- package/build/lib/zniffer/CCParsingContext.js.map +1 -1
- package/build/lib/zniffer/MPDU.js +12 -12
- package/build/lib/zniffer/MPDU.js.map +1 -1
- package/package.json +14 -14
|
@@ -22,6 +22,7 @@ const math_1 = require("alcalzone-shared/math");
|
|
|
22
22
|
const typeguards_1 = require("alcalzone-shared/typeguards");
|
|
23
23
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
24
24
|
const NetworkCache_1 = require("../driver/NetworkCache");
|
|
25
|
+
const Task_1 = require("../driver/Task");
|
|
25
26
|
const DeviceClass_1 = require("../node/DeviceClass");
|
|
26
27
|
const Node_1 = require("../node/Node");
|
|
27
28
|
const VirtualNode_1 = require("../node/VirtualNode");
|
|
@@ -74,6 +75,7 @@ const ControllerStatistics_1 = require("./ControllerStatistics");
|
|
|
74
75
|
const Features_1 = require("./Features");
|
|
75
76
|
const FirmwareUpdateService_1 = require("./FirmwareUpdateService");
|
|
76
77
|
const Inclusion_1 = require("./Inclusion");
|
|
78
|
+
const NVMIO_1 = require("./NVMIO");
|
|
77
79
|
const NodeInformationFrame_1 = require("./NodeInformationFrame");
|
|
78
80
|
const ZWaveSDKVersions_1 = require("./ZWaveSDKVersions");
|
|
79
81
|
const _Types_3 = require("./_Types");
|
|
@@ -430,10 +432,9 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
430
432
|
* Remembers the indicator values set by another node
|
|
431
433
|
*/
|
|
432
434
|
indicatorValues = new Map();
|
|
433
|
-
_isRebuildingRoutes = false;
|
|
434
435
|
/** Returns whether the routes are currently being rebuilt for one or more nodes. */
|
|
435
436
|
get isRebuildingRoutes() {
|
|
436
|
-
return this.
|
|
437
|
+
return !!this.driver.scheduler.findTask(utils_1.isRebuildRoutesTask);
|
|
437
438
|
}
|
|
438
439
|
/**
|
|
439
440
|
* Returns a reference to the (virtual) broadcast node, which allows sending commands to all nodes.
|
|
@@ -2874,7 +2875,7 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
2874
2875
|
* The information is the same as in the `"rebuild routes progress"` event.
|
|
2875
2876
|
*/
|
|
2876
2877
|
get rebuildRoutesProgress() {
|
|
2877
|
-
if (!this.
|
|
2878
|
+
if (!this.isRebuildingRoutes)
|
|
2878
2879
|
return undefined;
|
|
2879
2880
|
return new Map(this._rebuildRoutesProgress);
|
|
2880
2881
|
}
|
|
@@ -2888,9 +2889,9 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
2888
2889
|
*/
|
|
2889
2890
|
beginRebuildingRoutes(options = {}) {
|
|
2890
2891
|
// Don't start the process twice
|
|
2891
|
-
|
|
2892
|
+
const existingTask = this.driver.scheduler.findTask((t) => t.tag?.id === "rebuild-routes");
|
|
2893
|
+
if (existingTask)
|
|
2892
2894
|
return false;
|
|
2893
|
-
this._isRebuildingRoutes = true;
|
|
2894
2895
|
options.includeSleeping ??= true;
|
|
2895
2896
|
this.driver.controllerLog.print(`rebuilding routes${options.includeSleeping ? "" : " for mains-powered nodes"}...`);
|
|
2896
2897
|
// Reset the progress for all nodes
|
|
@@ -2917,14 +2918,15 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
2917
2918
|
}
|
|
2918
2919
|
}
|
|
2919
2920
|
// Rebuild routes in the background
|
|
2920
|
-
void this.
|
|
2921
|
-
/* ignore errors */
|
|
2922
|
-
});
|
|
2921
|
+
void this.rebuildRoutesInternal(options).catch(shared_1.noop);
|
|
2923
2922
|
// And update the progress once at the start
|
|
2924
2923
|
this.emit("rebuild routes progress", new Map(this._rebuildRoutesProgress));
|
|
2925
2924
|
return true;
|
|
2926
2925
|
}
|
|
2927
|
-
|
|
2926
|
+
rebuildRoutesInternal(options) {
|
|
2927
|
+
return this.driver.scheduler.queueTask(this.getRebuildRoutesTask(options));
|
|
2928
|
+
}
|
|
2929
|
+
getRebuildRoutesTask(options) {
|
|
2928
2930
|
const pendingNodes = new Set([...this._rebuildRoutesProgress]
|
|
2929
2931
|
.filter(([, status]) => status === "pending")
|
|
2930
2932
|
.map(([nodeId]) => nodeId));
|
|
@@ -2949,74 +2951,100 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
2949
2951
|
}
|
|
2950
2952
|
}
|
|
2951
2953
|
};
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2954
|
+
const self = this;
|
|
2955
|
+
return {
|
|
2956
|
+
priority: Task_1.TaskPriority.Lower,
|
|
2957
|
+
tag: { id: "rebuild-routes" },
|
|
2958
|
+
task: async function* rebuildRoutesTask() {
|
|
2959
|
+
// We work our way outwards from the controller and start with non-sleeping nodes, one by one
|
|
2960
|
+
try {
|
|
2961
|
+
const neighbors = await self.getNodeNeighbors(self._ownNodeId);
|
|
2962
|
+
neighbors.forEach((id) => addTodo(id));
|
|
2963
|
+
}
|
|
2964
|
+
catch {
|
|
2965
|
+
// ignore
|
|
2966
|
+
}
|
|
2967
|
+
yield; // Give the task scheduler time to do something else
|
|
2968
|
+
async function* doRebuildRoutes(nodeId) {
|
|
2969
|
+
// Await the process for each node and convert errors to a non-successful result
|
|
2970
|
+
let result;
|
|
2971
|
+
try {
|
|
2972
|
+
const node = self.nodes.getOrThrow(nodeId);
|
|
2973
|
+
result = yield () => self.getRebuildNodeRoutesTask(node);
|
|
2974
|
+
}
|
|
2975
|
+
catch {
|
|
2976
|
+
result = false;
|
|
2977
|
+
}
|
|
2978
|
+
// Track the success in a map
|
|
2979
|
+
self._rebuildRoutesProgress.set(nodeId, result ? "done" : "failed");
|
|
2980
|
+
// Notify listeners about the progress
|
|
2981
|
+
self.emit("rebuild routes progress", new Map(self._rebuildRoutesProgress));
|
|
2982
|
+
yield; // Give the task scheduler time to do something else
|
|
2983
|
+
// Figure out which nodes to do next
|
|
2984
|
+
try {
|
|
2985
|
+
const neighbors = await self.getNodeNeighbors(nodeId);
|
|
2986
|
+
neighbors.forEach((id) => addTodo(id));
|
|
2987
|
+
}
|
|
2988
|
+
catch {
|
|
2989
|
+
// ignore
|
|
2990
|
+
}
|
|
2991
|
+
yield; // Give the task scheduler time to do something else
|
|
2992
|
+
}
|
|
2993
|
+
// First try to rebuild routes for as many nodes as possible one by one
|
|
2994
|
+
while (todoListening.length > 0) {
|
|
2995
|
+
const nodeId = todoListening.shift();
|
|
2996
|
+
yield* doRebuildRoutes(nodeId);
|
|
2997
|
+
}
|
|
2998
|
+
// We might end up with a few unconnected listening nodes, try to rebuild routes for them too
|
|
2999
|
+
pendingNodes.forEach((nodeId) => addTodo(nodeId));
|
|
3000
|
+
while (todoListening.length > 0) {
|
|
3001
|
+
const nodeId = todoListening.shift();
|
|
3002
|
+
yield* doRebuildRoutes(nodeId);
|
|
3003
|
+
}
|
|
3004
|
+
if (options.includeSleeping) {
|
|
3005
|
+
// Now do all sleeping nodes at once
|
|
3006
|
+
self.driver.controllerLog.print("Rebuilding routes for sleeping nodes when they wake up");
|
|
3007
|
+
const sleepingNodes = todoSleeping.map((nodeId) => self.nodes.get(nodeId)).filter((node) => node != undefined);
|
|
3008
|
+
const wakeupPromises = new Map(sleepingNodes.map((node) => [
|
|
3009
|
+
node.id,
|
|
3010
|
+
node.waitForWakeup().then(() => node),
|
|
3011
|
+
]));
|
|
3012
|
+
// As long as there are sleeping nodes that haven't had their routes rebuilt yet,
|
|
3013
|
+
// wait for any of them to wake up
|
|
3014
|
+
while (wakeupPromises.size > 0) {
|
|
3015
|
+
const wakeUpPromise = Promise.race(wakeupPromises.values());
|
|
3016
|
+
const wokenUpNode = (yield () => wakeUpPromise);
|
|
3017
|
+
if (wokenUpNode.status === _Types_1.NodeStatus.Asleep) {
|
|
3018
|
+
// The node has gone to sleep again since the promise was resolved. Wait again
|
|
3019
|
+
wakeupPromises.set(wokenUpNode.id, wokenUpNode.waitForWakeup().then(() => wokenUpNode));
|
|
3020
|
+
continue;
|
|
3021
|
+
}
|
|
3022
|
+
// Once the node has woken up, remove it from the list and rebuild its routes
|
|
3023
|
+
wakeupPromises.delete(wokenUpNode.id);
|
|
3024
|
+
yield* doRebuildRoutes(wokenUpNode.id);
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
self.driver.controllerLog.print("rebuilding routes completed");
|
|
3028
|
+
self.emit("rebuild routes done", new Map(self._rebuildRoutesProgress));
|
|
3029
|
+
// We're done!
|
|
3030
|
+
self._rebuildRoutesProgress.clear();
|
|
3031
|
+
},
|
|
2977
3032
|
};
|
|
2978
|
-
// First try to rebuild routes for as many nodes as possible one by one
|
|
2979
|
-
while (todoListening.length > 0) {
|
|
2980
|
-
const nodeId = todoListening.shift();
|
|
2981
|
-
await doRebuildRoutes(nodeId);
|
|
2982
|
-
if (!this._isRebuildingRoutes)
|
|
2983
|
-
return;
|
|
2984
|
-
}
|
|
2985
|
-
// We might end up with a few unconnected listening nodes, try to rebuild routes for them too
|
|
2986
|
-
pendingNodes.forEach((nodeId) => addTodo(nodeId));
|
|
2987
|
-
while (todoListening.length > 0) {
|
|
2988
|
-
const nodeId = todoListening.shift();
|
|
2989
|
-
await doRebuildRoutes(nodeId);
|
|
2990
|
-
if (!this._isRebuildingRoutes)
|
|
2991
|
-
return;
|
|
2992
|
-
}
|
|
2993
|
-
if (options.includeSleeping) {
|
|
2994
|
-
// Now do all sleeping nodes at once
|
|
2995
|
-
this.driver.controllerLog.print("Rebuilding routes for sleeping nodes when they wake up");
|
|
2996
|
-
const tasks = todoSleeping.map((nodeId) => doRebuildRoutes(nodeId));
|
|
2997
|
-
await Promise.all(tasks);
|
|
2998
|
-
}
|
|
2999
|
-
// Only emit the done event when the process wasn't stopped in the meantime
|
|
3000
|
-
if (this._isRebuildingRoutes) {
|
|
3001
|
-
this.driver.controllerLog.print("rebuilding routes completed");
|
|
3002
|
-
this.emit("rebuild routes done", new Map(this._rebuildRoutesProgress));
|
|
3003
|
-
}
|
|
3004
|
-
else {
|
|
3005
|
-
this.driver.controllerLog.print("rebuilding routes aborted");
|
|
3006
|
-
}
|
|
3007
|
-
// We're done!
|
|
3008
|
-
this._isRebuildingRoutes = false;
|
|
3009
|
-
this._rebuildRoutesProgress.clear();
|
|
3010
3033
|
}
|
|
3011
3034
|
/**
|
|
3012
3035
|
* Stops the route rebuilding process. Resolves false if the process was not active, true otherwise.
|
|
3013
3036
|
*/
|
|
3014
3037
|
stopRebuildingRoutes() {
|
|
3038
|
+
const hasTasks = !!this.driver.scheduler.findTask(utils_1.isRebuildRoutesTask);
|
|
3015
3039
|
// don't stop it twice
|
|
3016
|
-
if (!
|
|
3040
|
+
if (!hasTasks)
|
|
3017
3041
|
return false;
|
|
3018
|
-
this._isRebuildingRoutes = false;
|
|
3019
3042
|
this.driver.controllerLog.print(`stopping route rebuilding process...`);
|
|
3043
|
+
// Stop all tasks that are part of the route rebuilding process
|
|
3044
|
+
// FIXME: This should be an async function that waits for the task removal
|
|
3045
|
+
void this.driver.scheduler.removeTasks(utils_1.isRebuildRoutesTask).then(() => {
|
|
3046
|
+
this.driver.controllerLog.print("rebuilding routes aborted");
|
|
3047
|
+
});
|
|
3020
3048
|
// Cancel all transactions that were created by the route rebuilding process
|
|
3021
3049
|
this.driver.rejectTransactions((t) => t.message instanceof RequestNodeNeighborUpdateMessages_1.RequestNodeNeighborUpdateRequest
|
|
3022
3050
|
|| t.message instanceof DeleteReturnRouteMessages_1.DeleteReturnRouteRequest
|
|
@@ -3041,12 +3069,6 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
3041
3069
|
if (node.protocol == core_1.Protocols.ZWaveLongRange) {
|
|
3042
3070
|
throw new core_1.ZWaveError(`Cannot rebuild routes for nodes using Z-Wave Long Range!`, core_1.ZWaveErrorCodes.Argument_Invalid);
|
|
3043
3071
|
}
|
|
3044
|
-
// Don't start the process twice
|
|
3045
|
-
if (this._isRebuildingRoutes) {
|
|
3046
|
-
this.driver.controllerLog.logNode(nodeId, `Cannot rebuild routes because another rebuilding process is in progress.`);
|
|
3047
|
-
return false;
|
|
3048
|
-
}
|
|
3049
|
-
this._isRebuildingRoutes = true;
|
|
3050
3072
|
// Figure out if nodes are responsive before attempting to rebuild routes
|
|
3051
3073
|
if (
|
|
3052
3074
|
// The node is known to be dead
|
|
@@ -3060,139 +3082,159 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
3060
3082
|
return false;
|
|
3061
3083
|
}
|
|
3062
3084
|
}
|
|
3063
|
-
|
|
3064
|
-
return await this.rebuildNodeRoutesInternal(nodeId);
|
|
3065
|
-
}
|
|
3066
|
-
finally {
|
|
3067
|
-
this._isRebuildingRoutes = false;
|
|
3068
|
-
}
|
|
3085
|
+
return this.rebuildNodeRoutesInternal(nodeId);
|
|
3069
3086
|
}
|
|
3070
|
-
|
|
3087
|
+
rebuildNodeRoutesInternal(nodeId) {
|
|
3071
3088
|
const node = this.nodes.getOrThrow(nodeId);
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3089
|
+
const task = this.getRebuildNodeRoutesTask(node);
|
|
3090
|
+
if (task instanceof Promise)
|
|
3091
|
+
return task;
|
|
3092
|
+
return this.driver.scheduler.queueTask(task);
|
|
3093
|
+
}
|
|
3094
|
+
getRebuildNodeRoutesTask(node) {
|
|
3095
|
+
// This task should only run once at a time
|
|
3096
|
+
const existingTask = this.driver.scheduler.findTask((t) => t.tag?.id === "rebuild-node-routes" && t.tag.nodeId === node.id);
|
|
3097
|
+
if (existingTask)
|
|
3098
|
+
return existingTask;
|
|
3099
|
+
const self = this;
|
|
3100
|
+
let keepAwake;
|
|
3101
|
+
return {
|
|
3102
|
+
// This task is executed by users and by the network-wide route rebuilding process.
|
|
3103
|
+
priority: Task_1.TaskPriority.Lower,
|
|
3104
|
+
tag: { id: "rebuild-node-routes", nodeId: node.id },
|
|
3105
|
+
task: async function* rebuildNodeRoutesTask() {
|
|
3106
|
+
// Keep battery powered nodes awake during the process
|
|
3107
|
+
keepAwake = node.keepAwake;
|
|
3108
|
+
node.keepAwake = true;
|
|
3109
|
+
if (node.canSleep && node.supportsCC(core_1.CommandClasses["Wake Up"])) {
|
|
3110
|
+
yield () => node.waitForWakeup();
|
|
3111
|
+
}
|
|
3112
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3113
|
+
message: `Rebuilding routes...`,
|
|
3114
|
+
direction: "none",
|
|
3091
3115
|
});
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3116
|
+
// The process consists of four steps, each step is tried up to 5 times before it is considered failed
|
|
3117
|
+
const maxAttempts = 5;
|
|
3118
|
+
// 1. command the node to refresh its neighbor list
|
|
3119
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3120
|
+
yield; // Give the task scheduler time to do something else
|
|
3121
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3122
|
+
message: `refreshing neighbor list (attempt ${attempt})...`,
|
|
3123
|
+
direction: "outbound",
|
|
3124
|
+
});
|
|
3125
|
+
try {
|
|
3126
|
+
const result = await self.discoverNodeNeighbors(node.id);
|
|
3127
|
+
if (result) {
|
|
3128
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3129
|
+
message: "neighbor list refreshed...",
|
|
3130
|
+
direction: "inbound",
|
|
3131
|
+
});
|
|
3132
|
+
// this step was successful, continue with the next
|
|
3133
|
+
break;
|
|
3134
|
+
}
|
|
3135
|
+
else {
|
|
3136
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3137
|
+
message: "refreshing neighbor list failed...",
|
|
3138
|
+
direction: "inbound",
|
|
3139
|
+
level: "warn",
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
catch (e) {
|
|
3144
|
+
self.driver.controllerLog.logNode(node.id, `refreshing neighbor list failed: ${(0, shared_1.getErrorMessage)(e)}`, "warn");
|
|
3145
|
+
}
|
|
3146
|
+
if (attempt === maxAttempts) {
|
|
3147
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3148
|
+
message: `rebuilding routes failed: could not update the neighbor list after ${maxAttempts} attempts`,
|
|
3149
|
+
level: "warn",
|
|
3150
|
+
direction: "none",
|
|
3098
3151
|
});
|
|
3099
|
-
|
|
3152
|
+
return false;
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
yield; // Give the task scheduler time to do something else
|
|
3156
|
+
// 2. re-create the SUC return route, just in case
|
|
3157
|
+
node.hasSUCReturnRoute = await self.assignSUCReturnRoutes(node.id);
|
|
3158
|
+
// 3. delete all return routes to get rid of potential priority return routes
|
|
3159
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3160
|
+
yield; // Give the task scheduler time to do something else
|
|
3161
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3162
|
+
message: `deleting return routes (attempt ${attempt})...`,
|
|
3163
|
+
direction: "outbound",
|
|
3164
|
+
});
|
|
3165
|
+
if (await self.deleteReturnRoutes(node.id)) {
|
|
3100
3166
|
break;
|
|
3101
3167
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
message:
|
|
3105
|
-
direction: "inbound",
|
|
3168
|
+
if (attempt === maxAttempts) {
|
|
3169
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3170
|
+
message: `rebuilding routes failed: failed to delete return routes after ${maxAttempts} attempts`,
|
|
3106
3171
|
level: "warn",
|
|
3172
|
+
direction: "none",
|
|
3107
3173
|
});
|
|
3174
|
+
return false;
|
|
3108
3175
|
}
|
|
3109
3176
|
}
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3177
|
+
// 4. Assign return routes to all association destinations...
|
|
3178
|
+
let associatedNodes = [];
|
|
3179
|
+
try {
|
|
3180
|
+
associatedNodes = (0, arrays_1.distinct)((0, shared_1.flatMap)([
|
|
3181
|
+
...self.getAssociations({ nodeId: node.id })
|
|
3182
|
+
.values(),
|
|
3183
|
+
], (assocs) => assocs.map((a) => a.nodeId)))
|
|
3184
|
+
// ...except the controller itself, which was handled by step 2
|
|
3185
|
+
.filter((id) => id !== self._ownNodeId)
|
|
3186
|
+
// ...and the node itself
|
|
3187
|
+
.filter((id) => id !== node.id)
|
|
3188
|
+
.sort();
|
|
3120
3189
|
}
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
node.hasSUCReturnRoute = await this.assignSUCReturnRoutes(nodeId);
|
|
3124
|
-
// 3. delete all return routes to get rid of potential priority return routes
|
|
3125
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3126
|
-
this.driver.controllerLog.logNode(nodeId, {
|
|
3127
|
-
message: `deleting return routes (attempt ${attempt})...`,
|
|
3128
|
-
direction: "outbound",
|
|
3129
|
-
});
|
|
3130
|
-
if (await this.deleteReturnRoutes(nodeId)) {
|
|
3131
|
-
break;
|
|
3190
|
+
catch {
|
|
3191
|
+
// ignore
|
|
3132
3192
|
}
|
|
3133
|
-
if (
|
|
3134
|
-
|
|
3135
|
-
message: `
|
|
3136
|
-
|
|
3137
|
-
direction: "
|
|
3193
|
+
if (associatedNodes.length > 0) {
|
|
3194
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3195
|
+
message: `assigning return routes to the following nodes:
|
|
3196
|
+
${associatedNodes.join(", ")}`,
|
|
3197
|
+
direction: "outbound",
|
|
3138
3198
|
});
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
associatedNodes = (0, arrays_1.distinct)((0, shared_1.flatMap)([...this.getAssociations({ nodeId }).values()], (assocs) => assocs.map((a) => a.nodeId)))
|
|
3146
|
-
// ...except the controller itself, which was handled by step 2
|
|
3147
|
-
.filter((id) => id !== this._ownNodeId)
|
|
3148
|
-
// ...and the node itself
|
|
3149
|
-
.filter((id) => id !== nodeId)
|
|
3150
|
-
.sort();
|
|
3151
|
-
}
|
|
3152
|
-
catch {
|
|
3153
|
-
/* ignore */
|
|
3154
|
-
}
|
|
3155
|
-
if (associatedNodes.length > 0) {
|
|
3156
|
-
this.driver.controllerLog.logNode(nodeId, {
|
|
3157
|
-
message: `assigning return routes to the following nodes:
|
|
3158
|
-
${associatedNodes.join(", ")}`,
|
|
3159
|
-
direction: "outbound",
|
|
3160
|
-
});
|
|
3161
|
-
for (const destinationNodeId of associatedNodes) {
|
|
3162
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3163
|
-
this.driver.controllerLog.logNode(nodeId, {
|
|
3164
|
-
message: `assigning return route to node ${destinationNodeId} (attempt ${attempt})...`,
|
|
3165
|
-
direction: "outbound",
|
|
3166
|
-
});
|
|
3167
|
-
if (await this.assignReturnRoutes(nodeId, destinationNodeId)) {
|
|
3168
|
-
// this step was successful, continue with the next
|
|
3169
|
-
break;
|
|
3170
|
-
}
|
|
3171
|
-
if (attempt === maxAttempts) {
|
|
3172
|
-
this.driver.controllerLog.logNode(nodeId, {
|
|
3173
|
-
message: `rebuilding routes failed: failed to assign return route after ${maxAttempts} attempts`,
|
|
3174
|
-
level: "warn",
|
|
3175
|
-
direction: "none",
|
|
3199
|
+
for (const destinationNodeId of associatedNodes) {
|
|
3200
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3201
|
+
yield; // Give the task scheduler time to do something else
|
|
3202
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3203
|
+
message: `assigning return route to node ${destinationNodeId} (attempt ${attempt})...`,
|
|
3204
|
+
direction: "outbound",
|
|
3176
3205
|
});
|
|
3177
|
-
|
|
3206
|
+
if (await self.assignReturnRoutes(node.id, destinationNodeId)) {
|
|
3207
|
+
// this step was successful, continue with the next
|
|
3208
|
+
break;
|
|
3209
|
+
}
|
|
3210
|
+
if (attempt === maxAttempts) {
|
|
3211
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3212
|
+
message: `rebuilding routes failed: failed to assign return route after ${maxAttempts} attempts`,
|
|
3213
|
+
level: "warn",
|
|
3214
|
+
direction: "none",
|
|
3215
|
+
});
|
|
3216
|
+
return false;
|
|
3217
|
+
}
|
|
3178
3218
|
}
|
|
3179
3219
|
}
|
|
3180
3220
|
}
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
direction: "none",
|
|
3185
|
-
});
|
|
3186
|
-
return true;
|
|
3187
|
-
}
|
|
3188
|
-
finally {
|
|
3189
|
-
node.keepAwake = keepAwake;
|
|
3190
|
-
if (!keepAwake) {
|
|
3191
|
-
setImmediate(() => {
|
|
3192
|
-
this.driver.debounceSendNodeToSleep(node);
|
|
3221
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3222
|
+
message: `rebuilt routes successfully`,
|
|
3223
|
+
direction: "none",
|
|
3193
3224
|
});
|
|
3194
|
-
|
|
3195
|
-
|
|
3225
|
+
return true;
|
|
3226
|
+
},
|
|
3227
|
+
cleanup: () => {
|
|
3228
|
+
// Make sure that the keepAwake flag gets reset at the end
|
|
3229
|
+
node.keepAwake = keepAwake;
|
|
3230
|
+
if (!keepAwake) {
|
|
3231
|
+
setImmediate(() => {
|
|
3232
|
+
this.driver.debounceSendNodeToSleep(node);
|
|
3233
|
+
});
|
|
3234
|
+
}
|
|
3235
|
+
return Promise.resolve();
|
|
3236
|
+
},
|
|
3237
|
+
};
|
|
3196
3238
|
}
|
|
3197
3239
|
/** Configures the given Node to be SUC/SIS or not */
|
|
3198
3240
|
async configureSUC(nodeId, enableSUC, enableSIS) {
|
|
@@ -3324,7 +3366,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
3324
3366
|
if (i !== priorityRouteIndex)
|
|
3325
3367
|
assignedRoutes[i] = route;
|
|
3326
3368
|
}
|
|
3327
|
-
catch
|
|
3369
|
+
catch {
|
|
3328
3370
|
this.driver.controllerLog.logNode(nodeId, {
|
|
3329
3371
|
message: `Assigning custom SUC return route #${i} failed`,
|
|
3330
3372
|
direction: "outbound",
|
|
@@ -3343,7 +3385,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
3343
3385
|
try {
|
|
3344
3386
|
await this.driver.sendZWaveProtocolCC(cc);
|
|
3345
3387
|
}
|
|
3346
|
-
catch
|
|
3388
|
+
catch {
|
|
3347
3389
|
this.driver.controllerLog.logNode(nodeId, {
|
|
3348
3390
|
message: `Marking custom SUC return route as priority failed`,
|
|
3349
3391
|
direction: "outbound",
|
|
@@ -3512,7 +3554,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
3512
3554
|
if (i !== priorityRouteIndex)
|
|
3513
3555
|
assignedRoutes[i] = route;
|
|
3514
3556
|
}
|
|
3515
|
-
catch
|
|
3557
|
+
catch {
|
|
3516
3558
|
this.driver.controllerLog.logNode(nodeId, {
|
|
3517
3559
|
message: `Assigning custom return route #${i} failed`,
|
|
3518
3560
|
direction: "outbound",
|
|
@@ -3531,7 +3573,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
3531
3573
|
try {
|
|
3532
3574
|
await this.driver.sendZWaveProtocolCC(cc);
|
|
3533
3575
|
}
|
|
3534
|
-
catch
|
|
3576
|
+
catch {
|
|
3535
3577
|
this.driver.controllerLog.logNode(nodeId, {
|
|
3536
3578
|
message: `Marking custom return route as priority failed`,
|
|
3537
3579
|
direction: "outbound",
|
|
@@ -4487,6 +4529,23 @@ ${associatedNodes.join(", ")}`,
|
|
|
4487
4529
|
return false;
|
|
4488
4530
|
}
|
|
4489
4531
|
}
|
|
4532
|
+
_nvm;
|
|
4533
|
+
/** Provides access to the controller's non-volatile memory */
|
|
4534
|
+
get nvm() {
|
|
4535
|
+
if (!this._nvm) {
|
|
4536
|
+
if (this.sdkVersionGte("7.0")) {
|
|
4537
|
+
const io = new nvmedit_1.BufferedNVMReader(new NVMIO_1.SerialNVMIO700(this));
|
|
4538
|
+
const nvm3 = new nvmedit_1.NVM3(io);
|
|
4539
|
+
this._nvm = new nvmedit_1.NVM3Adapter(nvm3);
|
|
4540
|
+
}
|
|
4541
|
+
else {
|
|
4542
|
+
const io = new nvmedit_1.BufferedNVMReader(new NVMIO_1.SerialNVMIO500(this));
|
|
4543
|
+
const nvm = new nvmedit_1.NVM500(io);
|
|
4544
|
+
this._nvm = new nvmedit_1.NVM500Adapter(nvm);
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
return this._nvm;
|
|
4548
|
+
}
|
|
4490
4549
|
/**
|
|
4491
4550
|
* **Z-Wave 500 series only**
|
|
4492
4551
|
*
|
|
@@ -4932,7 +4991,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
4932
4991
|
else {
|
|
4933
4992
|
targetNVM = await this.backupNVMRaw500(convertProgress);
|
|
4934
4993
|
}
|
|
4935
|
-
const convertedNVM = (0, nvmedit_1.migrateNVM)(nvmData, targetNVM);
|
|
4994
|
+
const convertedNVM = await (0, nvmedit_1.migrateNVM)(nvmData, targetNVM);
|
|
4936
4995
|
this.driver.controllerLog.print("Restoring NVM backup...");
|
|
4937
4996
|
if (this.sdkVersionGte("7.0")) {
|
|
4938
4997
|
await this.restoreNVMRaw700(convertedNVM, restoreProgress);
|
|
@@ -5457,7 +5516,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
5457
5516
|
try {
|
|
5458
5517
|
result = await this.driver.waitForBootloaderChunk((c) => c.type === serial_1.BootloaderChunkType.FlowControl, 1000);
|
|
5459
5518
|
}
|
|
5460
|
-
catch
|
|
5519
|
+
catch {
|
|
5461
5520
|
this.driver.controllerLog.print("OTW update failed: The bootloader did not acknowledge the start of transfer.", "error");
|
|
5462
5521
|
const result = {
|
|
5463
5522
|
success: false,
|
|
@@ -5532,7 +5591,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
5532
5591
|
this.driver.waitForBootloaderChunk((c) => c.type === serial_1.BootloaderChunkType.Menu, 1000),
|
|
5533
5592
|
]);
|
|
5534
5593
|
}
|
|
5535
|
-
catch
|
|
5594
|
+
catch {
|
|
5536
5595
|
this.driver.controllerLog.print("OTW update failed: The bootloader did not acknowledge the end of transfer.", "error");
|
|
5537
5596
|
const result = {
|
|
5538
5597
|
success: false,
|