zwave-js 13.4.0 → 13.6.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 +10 -2
- package/build/lib/controller/Controller.d.ts.map +1 -1
- package/build/lib/controller/Controller.js +263 -195
- 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/_Types.d.ts +2 -0
- package/build/lib/controller/_Types.d.ts.map +1 -1
- package/build/lib/controller/_Types.js.map +1 -1
- 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 +86 -4
- 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.d.ts +2 -0
- package/build/lib/driver/NetworkCache.d.ts.map +1 -1
- package/build/lib/driver/NetworkCache.js +19 -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 +131 -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/node/MockNodeBehaviors.d.ts.map +1 -1
- package/build/lib/node/MockNodeBehaviors.js +2 -0
- package/build/lib/node/MockNodeBehaviors.js.map +1 -1
- package/build/lib/node/Node.d.ts +17 -17
- package/build/lib/node/Node.d.ts.map +1 -1
- package/build/lib/node/Node.js +370 -327
- 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 +3 -2
- package/build/lib/node/_Types.d.ts.map +1 -1
- package/build/lib/node/_Types.js.map +1 -1
- package/build/lib/node/mockCCBehaviors/BinarySwitch.d.ts.map +1 -1
- package/build/lib/node/mockCCBehaviors/BinarySwitch.js +8 -0
- package/build/lib/node/mockCCBehaviors/BinarySwitch.js.map +1 -1
- package/build/lib/node/mockCCBehaviors/MultilevelSwitch.d.ts +3 -0
- package/build/lib/node/mockCCBehaviors/MultilevelSwitch.d.ts.map +1 -0
- package/build/lib/node/mockCCBehaviors/MultilevelSwitch.js +81 -0
- package/build/lib/node/mockCCBehaviors/MultilevelSwitch.js.map +1 -0
- package/build/lib/node/mockCCBehaviors/ThermostatMode.d.ts.map +1 -1
- package/build/lib/node/mockCCBehaviors/ThermostatMode.js +0 -1
- package/build/lib/node/mockCCBehaviors/ThermostatMode.js.map +1 -1
- package/build/lib/node/utils.d.ts +3 -0
- package/build/lib/node/utils.d.ts.map +1 -1
- package/build/lib/node/utils.js +5 -0
- package/build/lib/node/utils.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 +22 -22
|
@@ -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");
|
|
@@ -431,10 +432,9 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
431
432
|
* Remembers the indicator values set by another node
|
|
432
433
|
*/
|
|
433
434
|
indicatorValues = new Map();
|
|
434
|
-
_isRebuildingRoutes = false;
|
|
435
435
|
/** Returns whether the routes are currently being rebuilt for one or more nodes. */
|
|
436
436
|
get isRebuildingRoutes() {
|
|
437
|
-
return this.
|
|
437
|
+
return !!this.driver.scheduler.findTask(utils_1.isRebuildRoutesTask);
|
|
438
438
|
}
|
|
439
439
|
/**
|
|
440
440
|
* Returns a reference to the (virtual) broadcast node, which allows sending commands to all nodes.
|
|
@@ -1496,6 +1496,7 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
1496
1496
|
node.lastSeen = new Date();
|
|
1497
1497
|
// Resolve active pings that would fail otherwise
|
|
1498
1498
|
this.driver.resolvePendingPings(node.id);
|
|
1499
|
+
node.emit("node info received", node);
|
|
1499
1500
|
if (node.canSleep
|
|
1500
1501
|
&& node.supportsCC(core_1.CommandClasses["Wake Up"])) {
|
|
1501
1502
|
// In case this is a sleeping node and there are no messages in the queue, the node may go back to sleep very soon
|
|
@@ -2875,7 +2876,7 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
2875
2876
|
* The information is the same as in the `"rebuild routes progress"` event.
|
|
2876
2877
|
*/
|
|
2877
2878
|
get rebuildRoutesProgress() {
|
|
2878
|
-
if (!this.
|
|
2879
|
+
if (!this.isRebuildingRoutes)
|
|
2879
2880
|
return undefined;
|
|
2880
2881
|
return new Map(this._rebuildRoutesProgress);
|
|
2881
2882
|
}
|
|
@@ -2889,10 +2890,11 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
2889
2890
|
*/
|
|
2890
2891
|
beginRebuildingRoutes(options = {}) {
|
|
2891
2892
|
// Don't start the process twice
|
|
2892
|
-
|
|
2893
|
+
const existingTask = this.driver.scheduler.findTask((t) => t.tag?.id === "rebuild-routes");
|
|
2894
|
+
if (existingTask)
|
|
2893
2895
|
return false;
|
|
2894
|
-
this._isRebuildingRoutes = true;
|
|
2895
2896
|
options.includeSleeping ??= true;
|
|
2897
|
+
options.deletePriorityReturnRoutes ??= false;
|
|
2896
2898
|
this.driver.controllerLog.print(`rebuilding routes${options.includeSleeping ? "" : " for mains-powered nodes"}...`);
|
|
2897
2899
|
// Reset the progress for all nodes
|
|
2898
2900
|
this._rebuildRoutesProgress.clear();
|
|
@@ -2911,6 +2913,14 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
2911
2913
|
this._rebuildRoutesProgress.set(id, "skipped");
|
|
2912
2914
|
}
|
|
2913
2915
|
else if (!options.includeSleeping && node.canSleep) {
|
|
2916
|
+
this.driver.controllerLog.logNode(id, `Skipping route rebuild because the node is sleeping.`);
|
|
2917
|
+
this._rebuildRoutesProgress.set(id, "skipped");
|
|
2918
|
+
}
|
|
2919
|
+
else if (!options.deletePriorityReturnRoutes
|
|
2920
|
+
&& (this.getPrioritySUCReturnRouteCached(id)
|
|
2921
|
+
|| Object.keys(this.getPriorityReturnRoutesCached(id))
|
|
2922
|
+
.length > 0)) {
|
|
2923
|
+
this.driver.controllerLog.logNode(id, `Skipping route rebuild because the node has priority return routes.`);
|
|
2914
2924
|
this._rebuildRoutesProgress.set(id, "skipped");
|
|
2915
2925
|
}
|
|
2916
2926
|
else {
|
|
@@ -2918,14 +2928,15 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
2918
2928
|
}
|
|
2919
2929
|
}
|
|
2920
2930
|
// Rebuild routes in the background
|
|
2921
|
-
void this.
|
|
2922
|
-
/* ignore errors */
|
|
2923
|
-
});
|
|
2931
|
+
void this.rebuildRoutesInternal(options).catch(shared_1.noop);
|
|
2924
2932
|
// And update the progress once at the start
|
|
2925
2933
|
this.emit("rebuild routes progress", new Map(this._rebuildRoutesProgress));
|
|
2926
2934
|
return true;
|
|
2927
2935
|
}
|
|
2928
|
-
|
|
2936
|
+
rebuildRoutesInternal(options) {
|
|
2937
|
+
return this.driver.scheduler.queueTask(this.getRebuildRoutesTask(options));
|
|
2938
|
+
}
|
|
2939
|
+
getRebuildRoutesTask(options) {
|
|
2929
2940
|
const pendingNodes = new Set([...this._rebuildRoutesProgress]
|
|
2930
2941
|
.filter(([, status]) => status === "pending")
|
|
2931
2942
|
.map(([nodeId]) => nodeId));
|
|
@@ -2950,74 +2961,100 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
2950
2961
|
}
|
|
2951
2962
|
}
|
|
2952
2963
|
};
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2964
|
+
const self = this;
|
|
2965
|
+
return {
|
|
2966
|
+
priority: Task_1.TaskPriority.Lower,
|
|
2967
|
+
tag: { id: "rebuild-routes" },
|
|
2968
|
+
task: async function* rebuildRoutesTask() {
|
|
2969
|
+
// We work our way outwards from the controller and start with non-sleeping nodes, one by one
|
|
2970
|
+
try {
|
|
2971
|
+
const neighbors = await self.getNodeNeighbors(self._ownNodeId);
|
|
2972
|
+
neighbors.forEach((id) => addTodo(id));
|
|
2973
|
+
}
|
|
2974
|
+
catch {
|
|
2975
|
+
// ignore
|
|
2976
|
+
}
|
|
2977
|
+
yield; // Give the task scheduler time to do something else
|
|
2978
|
+
async function* doRebuildRoutes(nodeId) {
|
|
2979
|
+
// Await the process for each node and convert errors to a non-successful result
|
|
2980
|
+
let result;
|
|
2981
|
+
try {
|
|
2982
|
+
const node = self.nodes.getOrThrow(nodeId);
|
|
2983
|
+
result = yield () => self.getRebuildNodeRoutesTask(node);
|
|
2984
|
+
}
|
|
2985
|
+
catch {
|
|
2986
|
+
result = false;
|
|
2987
|
+
}
|
|
2988
|
+
// Track the success in a map
|
|
2989
|
+
self._rebuildRoutesProgress.set(nodeId, result ? "done" : "failed");
|
|
2990
|
+
// Notify listeners about the progress
|
|
2991
|
+
self.emit("rebuild routes progress", new Map(self._rebuildRoutesProgress));
|
|
2992
|
+
yield; // Give the task scheduler time to do something else
|
|
2993
|
+
// Figure out which nodes to do next
|
|
2994
|
+
try {
|
|
2995
|
+
const neighbors = await self.getNodeNeighbors(nodeId);
|
|
2996
|
+
neighbors.forEach((id) => addTodo(id));
|
|
2997
|
+
}
|
|
2998
|
+
catch {
|
|
2999
|
+
// ignore
|
|
3000
|
+
}
|
|
3001
|
+
yield; // Give the task scheduler time to do something else
|
|
3002
|
+
}
|
|
3003
|
+
// First try to rebuild routes for as many nodes as possible one by one
|
|
3004
|
+
while (todoListening.length > 0) {
|
|
3005
|
+
const nodeId = todoListening.shift();
|
|
3006
|
+
yield* doRebuildRoutes(nodeId);
|
|
3007
|
+
}
|
|
3008
|
+
// We might end up with a few unconnected listening nodes, try to rebuild routes for them too
|
|
3009
|
+
pendingNodes.forEach((nodeId) => addTodo(nodeId));
|
|
3010
|
+
while (todoListening.length > 0) {
|
|
3011
|
+
const nodeId = todoListening.shift();
|
|
3012
|
+
yield* doRebuildRoutes(nodeId);
|
|
3013
|
+
}
|
|
3014
|
+
if (options.includeSleeping) {
|
|
3015
|
+
// Now do all sleeping nodes at once
|
|
3016
|
+
self.driver.controllerLog.print("Rebuilding routes for sleeping nodes when they wake up");
|
|
3017
|
+
const sleepingNodes = todoSleeping.map((nodeId) => self.nodes.get(nodeId)).filter((node) => node != undefined);
|
|
3018
|
+
const wakeupPromises = new Map(sleepingNodes.map((node) => [
|
|
3019
|
+
node.id,
|
|
3020
|
+
node.waitForWakeup().then(() => node),
|
|
3021
|
+
]));
|
|
3022
|
+
// As long as there are sleeping nodes that haven't had their routes rebuilt yet,
|
|
3023
|
+
// wait for any of them to wake up
|
|
3024
|
+
while (wakeupPromises.size > 0) {
|
|
3025
|
+
const wakeUpPromise = Promise.race(wakeupPromises.values());
|
|
3026
|
+
const wokenUpNode = (yield () => wakeUpPromise);
|
|
3027
|
+
if (wokenUpNode.status === _Types_1.NodeStatus.Asleep) {
|
|
3028
|
+
// The node has gone to sleep again since the promise was resolved. Wait again
|
|
3029
|
+
wakeupPromises.set(wokenUpNode.id, wokenUpNode.waitForWakeup().then(() => wokenUpNode));
|
|
3030
|
+
continue;
|
|
3031
|
+
}
|
|
3032
|
+
// Once the node has woken up, remove it from the list and rebuild its routes
|
|
3033
|
+
wakeupPromises.delete(wokenUpNode.id);
|
|
3034
|
+
yield* doRebuildRoutes(wokenUpNode.id);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
self.driver.controllerLog.print("rebuilding routes completed");
|
|
3038
|
+
self.emit("rebuild routes done", new Map(self._rebuildRoutesProgress));
|
|
3039
|
+
// We're done!
|
|
3040
|
+
self._rebuildRoutesProgress.clear();
|
|
3041
|
+
},
|
|
2978
3042
|
};
|
|
2979
|
-
// First try to rebuild routes for as many nodes as possible one by one
|
|
2980
|
-
while (todoListening.length > 0) {
|
|
2981
|
-
const nodeId = todoListening.shift();
|
|
2982
|
-
await doRebuildRoutes(nodeId);
|
|
2983
|
-
if (!this._isRebuildingRoutes)
|
|
2984
|
-
return;
|
|
2985
|
-
}
|
|
2986
|
-
// We might end up with a few unconnected listening nodes, try to rebuild routes for them too
|
|
2987
|
-
pendingNodes.forEach((nodeId) => addTodo(nodeId));
|
|
2988
|
-
while (todoListening.length > 0) {
|
|
2989
|
-
const nodeId = todoListening.shift();
|
|
2990
|
-
await doRebuildRoutes(nodeId);
|
|
2991
|
-
if (!this._isRebuildingRoutes)
|
|
2992
|
-
return;
|
|
2993
|
-
}
|
|
2994
|
-
if (options.includeSleeping) {
|
|
2995
|
-
// Now do all sleeping nodes at once
|
|
2996
|
-
this.driver.controllerLog.print("Rebuilding routes for sleeping nodes when they wake up");
|
|
2997
|
-
const tasks = todoSleeping.map((nodeId) => doRebuildRoutes(nodeId));
|
|
2998
|
-
await Promise.all(tasks);
|
|
2999
|
-
}
|
|
3000
|
-
// Only emit the done event when the process wasn't stopped in the meantime
|
|
3001
|
-
if (this._isRebuildingRoutes) {
|
|
3002
|
-
this.driver.controllerLog.print("rebuilding routes completed");
|
|
3003
|
-
this.emit("rebuild routes done", new Map(this._rebuildRoutesProgress));
|
|
3004
|
-
}
|
|
3005
|
-
else {
|
|
3006
|
-
this.driver.controllerLog.print("rebuilding routes aborted");
|
|
3007
|
-
}
|
|
3008
|
-
// We're done!
|
|
3009
|
-
this._isRebuildingRoutes = false;
|
|
3010
|
-
this._rebuildRoutesProgress.clear();
|
|
3011
3043
|
}
|
|
3012
3044
|
/**
|
|
3013
3045
|
* Stops the route rebuilding process. Resolves false if the process was not active, true otherwise.
|
|
3014
3046
|
*/
|
|
3015
3047
|
stopRebuildingRoutes() {
|
|
3048
|
+
const hasTasks = !!this.driver.scheduler.findTask(utils_1.isRebuildRoutesTask);
|
|
3016
3049
|
// don't stop it twice
|
|
3017
|
-
if (!
|
|
3050
|
+
if (!hasTasks)
|
|
3018
3051
|
return false;
|
|
3019
|
-
this._isRebuildingRoutes = false;
|
|
3020
3052
|
this.driver.controllerLog.print(`stopping route rebuilding process...`);
|
|
3053
|
+
// Stop all tasks that are part of the route rebuilding process
|
|
3054
|
+
// FIXME: This should be an async function that waits for the task removal
|
|
3055
|
+
void this.driver.scheduler.removeTasks(utils_1.isRebuildRoutesTask).then(() => {
|
|
3056
|
+
this.driver.controllerLog.print("rebuilding routes aborted");
|
|
3057
|
+
});
|
|
3021
3058
|
// Cancel all transactions that were created by the route rebuilding process
|
|
3022
3059
|
this.driver.rejectTransactions((t) => t.message instanceof RequestNodeNeighborUpdateMessages_1.RequestNodeNeighborUpdateRequest
|
|
3023
3060
|
|| t.message instanceof DeleteReturnRouteMessages_1.DeleteReturnRouteRequest
|
|
@@ -3042,12 +3079,6 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
3042
3079
|
if (node.protocol == core_1.Protocols.ZWaveLongRange) {
|
|
3043
3080
|
throw new core_1.ZWaveError(`Cannot rebuild routes for nodes using Z-Wave Long Range!`, core_1.ZWaveErrorCodes.Argument_Invalid);
|
|
3044
3081
|
}
|
|
3045
|
-
// Don't start the process twice
|
|
3046
|
-
if (this._isRebuildingRoutes) {
|
|
3047
|
-
this.driver.controllerLog.logNode(nodeId, `Cannot rebuild routes because another rebuilding process is in progress.`);
|
|
3048
|
-
return false;
|
|
3049
|
-
}
|
|
3050
|
-
this._isRebuildingRoutes = true;
|
|
3051
3082
|
// Figure out if nodes are responsive before attempting to rebuild routes
|
|
3052
3083
|
if (
|
|
3053
3084
|
// The node is known to be dead
|
|
@@ -3061,139 +3092,159 @@ let ZWaveController = class ZWaveController extends shared_1.TypedEventEmitter {
|
|
|
3061
3092
|
return false;
|
|
3062
3093
|
}
|
|
3063
3094
|
}
|
|
3064
|
-
|
|
3065
|
-
return await this.rebuildNodeRoutesInternal(nodeId);
|
|
3066
|
-
}
|
|
3067
|
-
finally {
|
|
3068
|
-
this._isRebuildingRoutes = false;
|
|
3069
|
-
}
|
|
3095
|
+
return this.rebuildNodeRoutesInternal(nodeId);
|
|
3070
3096
|
}
|
|
3071
|
-
|
|
3097
|
+
rebuildNodeRoutesInternal(nodeId) {
|
|
3072
3098
|
const node = this.nodes.getOrThrow(nodeId);
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3099
|
+
const task = this.getRebuildNodeRoutesTask(node);
|
|
3100
|
+
if (task instanceof Promise)
|
|
3101
|
+
return task;
|
|
3102
|
+
return this.driver.scheduler.queueTask(task);
|
|
3103
|
+
}
|
|
3104
|
+
getRebuildNodeRoutesTask(node) {
|
|
3105
|
+
// This task should only run once at a time
|
|
3106
|
+
const existingTask = this.driver.scheduler.findTask((t) => t.tag?.id === "rebuild-node-routes" && t.tag.nodeId === node.id);
|
|
3107
|
+
if (existingTask)
|
|
3108
|
+
return existingTask;
|
|
3109
|
+
const self = this;
|
|
3110
|
+
let keepAwake;
|
|
3111
|
+
return {
|
|
3112
|
+
// This task is executed by users and by the network-wide route rebuilding process.
|
|
3113
|
+
priority: Task_1.TaskPriority.Lower,
|
|
3114
|
+
tag: { id: "rebuild-node-routes", nodeId: node.id },
|
|
3115
|
+
task: async function* rebuildNodeRoutesTask() {
|
|
3116
|
+
// Keep battery powered nodes awake during the process
|
|
3117
|
+
keepAwake = node.keepAwake;
|
|
3118
|
+
node.keepAwake = true;
|
|
3119
|
+
if (node.canSleep && node.supportsCC(core_1.CommandClasses["Wake Up"])) {
|
|
3120
|
+
yield () => node.waitForWakeup();
|
|
3121
|
+
}
|
|
3122
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3123
|
+
message: `Rebuilding routes...`,
|
|
3124
|
+
direction: "none",
|
|
3092
3125
|
});
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3126
|
+
// The process consists of four steps, each step is tried up to 5 times before it is considered failed
|
|
3127
|
+
const maxAttempts = 5;
|
|
3128
|
+
// 1. command the node to refresh its neighbor list
|
|
3129
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3130
|
+
yield; // Give the task scheduler time to do something else
|
|
3131
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3132
|
+
message: `refreshing neighbor list (attempt ${attempt})...`,
|
|
3133
|
+
direction: "outbound",
|
|
3134
|
+
});
|
|
3135
|
+
try {
|
|
3136
|
+
const result = await self.discoverNodeNeighbors(node.id);
|
|
3137
|
+
if (result) {
|
|
3138
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3139
|
+
message: "neighbor list refreshed...",
|
|
3140
|
+
direction: "inbound",
|
|
3141
|
+
});
|
|
3142
|
+
// this step was successful, continue with the next
|
|
3143
|
+
break;
|
|
3144
|
+
}
|
|
3145
|
+
else {
|
|
3146
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3147
|
+
message: "refreshing neighbor list failed...",
|
|
3148
|
+
direction: "inbound",
|
|
3149
|
+
level: "warn",
|
|
3150
|
+
});
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
catch (e) {
|
|
3154
|
+
self.driver.controllerLog.logNode(node.id, `refreshing neighbor list failed: ${(0, shared_1.getErrorMessage)(e)}`, "warn");
|
|
3155
|
+
}
|
|
3156
|
+
if (attempt === maxAttempts) {
|
|
3157
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3158
|
+
message: `rebuilding routes failed: could not update the neighbor list after ${maxAttempts} attempts`,
|
|
3159
|
+
level: "warn",
|
|
3160
|
+
direction: "none",
|
|
3099
3161
|
});
|
|
3100
|
-
|
|
3162
|
+
return false;
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
yield; // Give the task scheduler time to do something else
|
|
3166
|
+
// 2. re-create the SUC return route, just in case
|
|
3167
|
+
node.hasSUCReturnRoute = await self.assignSUCReturnRoutes(node.id);
|
|
3168
|
+
// 3. delete all return routes to get rid of potential priority return routes
|
|
3169
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3170
|
+
yield; // Give the task scheduler time to do something else
|
|
3171
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3172
|
+
message: `deleting return routes (attempt ${attempt})...`,
|
|
3173
|
+
direction: "outbound",
|
|
3174
|
+
});
|
|
3175
|
+
if (await self.deleteReturnRoutes(node.id)) {
|
|
3101
3176
|
break;
|
|
3102
3177
|
}
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
message:
|
|
3106
|
-
direction: "inbound",
|
|
3178
|
+
if (attempt === maxAttempts) {
|
|
3179
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3180
|
+
message: `rebuilding routes failed: failed to delete return routes after ${maxAttempts} attempts`,
|
|
3107
3181
|
level: "warn",
|
|
3182
|
+
direction: "none",
|
|
3108
3183
|
});
|
|
3184
|
+
return false;
|
|
3109
3185
|
}
|
|
3110
3186
|
}
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3187
|
+
// 4. Assign return routes to all association destinations...
|
|
3188
|
+
let associatedNodes = [];
|
|
3189
|
+
try {
|
|
3190
|
+
associatedNodes = (0, arrays_1.distinct)((0, shared_1.flatMap)([
|
|
3191
|
+
...self.getAssociations({ nodeId: node.id })
|
|
3192
|
+
.values(),
|
|
3193
|
+
], (assocs) => assocs.map((a) => a.nodeId)))
|
|
3194
|
+
// ...except the controller itself, which was handled by step 2
|
|
3195
|
+
.filter((id) => id !== self._ownNodeId)
|
|
3196
|
+
// ...and the node itself
|
|
3197
|
+
.filter((id) => id !== node.id)
|
|
3198
|
+
.sort();
|
|
3121
3199
|
}
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
node.hasSUCReturnRoute = await this.assignSUCReturnRoutes(nodeId);
|
|
3125
|
-
// 3. delete all return routes to get rid of potential priority return routes
|
|
3126
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3127
|
-
this.driver.controllerLog.logNode(nodeId, {
|
|
3128
|
-
message: `deleting return routes (attempt ${attempt})...`,
|
|
3129
|
-
direction: "outbound",
|
|
3130
|
-
});
|
|
3131
|
-
if (await this.deleteReturnRoutes(nodeId)) {
|
|
3132
|
-
break;
|
|
3200
|
+
catch {
|
|
3201
|
+
// ignore
|
|
3133
3202
|
}
|
|
3134
|
-
if (
|
|
3135
|
-
|
|
3136
|
-
message: `
|
|
3137
|
-
|
|
3138
|
-
direction: "
|
|
3203
|
+
if (associatedNodes.length > 0) {
|
|
3204
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3205
|
+
message: `assigning return routes to the following nodes:
|
|
3206
|
+
${associatedNodes.join(", ")}`,
|
|
3207
|
+
direction: "outbound",
|
|
3139
3208
|
});
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
associatedNodes = (0, arrays_1.distinct)((0, shared_1.flatMap)([...this.getAssociations({ nodeId }).values()], (assocs) => assocs.map((a) => a.nodeId)))
|
|
3147
|
-
// ...except the controller itself, which was handled by step 2
|
|
3148
|
-
.filter((id) => id !== this._ownNodeId)
|
|
3149
|
-
// ...and the node itself
|
|
3150
|
-
.filter((id) => id !== nodeId)
|
|
3151
|
-
.sort();
|
|
3152
|
-
}
|
|
3153
|
-
catch {
|
|
3154
|
-
/* ignore */
|
|
3155
|
-
}
|
|
3156
|
-
if (associatedNodes.length > 0) {
|
|
3157
|
-
this.driver.controllerLog.logNode(nodeId, {
|
|
3158
|
-
message: `assigning return routes to the following nodes:
|
|
3159
|
-
${associatedNodes.join(", ")}`,
|
|
3160
|
-
direction: "outbound",
|
|
3161
|
-
});
|
|
3162
|
-
for (const destinationNodeId of associatedNodes) {
|
|
3163
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3164
|
-
this.driver.controllerLog.logNode(nodeId, {
|
|
3165
|
-
message: `assigning return route to node ${destinationNodeId} (attempt ${attempt})...`,
|
|
3166
|
-
direction: "outbound",
|
|
3167
|
-
});
|
|
3168
|
-
if (await this.assignReturnRoutes(nodeId, destinationNodeId)) {
|
|
3169
|
-
// this step was successful, continue with the next
|
|
3170
|
-
break;
|
|
3171
|
-
}
|
|
3172
|
-
if (attempt === maxAttempts) {
|
|
3173
|
-
this.driver.controllerLog.logNode(nodeId, {
|
|
3174
|
-
message: `rebuilding routes failed: failed to assign return route after ${maxAttempts} attempts`,
|
|
3175
|
-
level: "warn",
|
|
3176
|
-
direction: "none",
|
|
3209
|
+
for (const destinationNodeId of associatedNodes) {
|
|
3210
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3211
|
+
yield; // Give the task scheduler time to do something else
|
|
3212
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3213
|
+
message: `assigning return route to node ${destinationNodeId} (attempt ${attempt})...`,
|
|
3214
|
+
direction: "outbound",
|
|
3177
3215
|
});
|
|
3178
|
-
|
|
3216
|
+
if (await self.assignReturnRoutes(node.id, destinationNodeId)) {
|
|
3217
|
+
// this step was successful, continue with the next
|
|
3218
|
+
break;
|
|
3219
|
+
}
|
|
3220
|
+
if (attempt === maxAttempts) {
|
|
3221
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3222
|
+
message: `rebuilding routes failed: failed to assign return route after ${maxAttempts} attempts`,
|
|
3223
|
+
level: "warn",
|
|
3224
|
+
direction: "none",
|
|
3225
|
+
});
|
|
3226
|
+
return false;
|
|
3227
|
+
}
|
|
3179
3228
|
}
|
|
3180
3229
|
}
|
|
3181
3230
|
}
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
direction: "none",
|
|
3186
|
-
});
|
|
3187
|
-
return true;
|
|
3188
|
-
}
|
|
3189
|
-
finally {
|
|
3190
|
-
node.keepAwake = keepAwake;
|
|
3191
|
-
if (!keepAwake) {
|
|
3192
|
-
setImmediate(() => {
|
|
3193
|
-
this.driver.debounceSendNodeToSleep(node);
|
|
3231
|
+
self.driver.controllerLog.logNode(node.id, {
|
|
3232
|
+
message: `rebuilt routes successfully`,
|
|
3233
|
+
direction: "none",
|
|
3194
3234
|
});
|
|
3195
|
-
|
|
3196
|
-
|
|
3235
|
+
return true;
|
|
3236
|
+
},
|
|
3237
|
+
cleanup: () => {
|
|
3238
|
+
// Make sure that the keepAwake flag gets reset at the end
|
|
3239
|
+
node.keepAwake = keepAwake;
|
|
3240
|
+
if (!keepAwake) {
|
|
3241
|
+
setImmediate(() => {
|
|
3242
|
+
this.driver.debounceSendNodeToSleep(node);
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
return Promise.resolve();
|
|
3246
|
+
},
|
|
3247
|
+
};
|
|
3197
3248
|
}
|
|
3198
3249
|
/** Configures the given Node to be SUC/SIS or not */
|
|
3199
3250
|
async configureSUC(nodeId, enableSUC, enableSIS) {
|
|
@@ -3325,7 +3376,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
3325
3376
|
if (i !== priorityRouteIndex)
|
|
3326
3377
|
assignedRoutes[i] = route;
|
|
3327
3378
|
}
|
|
3328
|
-
catch
|
|
3379
|
+
catch {
|
|
3329
3380
|
this.driver.controllerLog.logNode(nodeId, {
|
|
3330
3381
|
message: `Assigning custom SUC return route #${i} failed`,
|
|
3331
3382
|
direction: "outbound",
|
|
@@ -3344,7 +3395,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
3344
3395
|
try {
|
|
3345
3396
|
await this.driver.sendZWaveProtocolCC(cc);
|
|
3346
3397
|
}
|
|
3347
|
-
catch
|
|
3398
|
+
catch {
|
|
3348
3399
|
this.driver.controllerLog.logNode(nodeId, {
|
|
3349
3400
|
message: `Marking custom SUC return route as priority failed`,
|
|
3350
3401
|
direction: "outbound",
|
|
@@ -3513,7 +3564,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
3513
3564
|
if (i !== priorityRouteIndex)
|
|
3514
3565
|
assignedRoutes[i] = route;
|
|
3515
3566
|
}
|
|
3516
|
-
catch
|
|
3567
|
+
catch {
|
|
3517
3568
|
this.driver.controllerLog.logNode(nodeId, {
|
|
3518
3569
|
message: `Assigning custom return route #${i} failed`,
|
|
3519
3570
|
direction: "outbound",
|
|
@@ -3532,7 +3583,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
3532
3583
|
try {
|
|
3533
3584
|
await this.driver.sendZWaveProtocolCC(cc);
|
|
3534
3585
|
}
|
|
3535
|
-
catch
|
|
3586
|
+
catch {
|
|
3536
3587
|
this.driver.controllerLog.logNode(nodeId, {
|
|
3537
3588
|
message: `Marking custom return route as priority failed`,
|
|
3538
3589
|
direction: "outbound",
|
|
@@ -3650,6 +3701,23 @@ ${associatedNodes.join(", ")}`,
|
|
|
3650
3701
|
getPriorityReturnRouteCached(nodeId, destinationNodeId) {
|
|
3651
3702
|
return this.driver.cacheGet(NetworkCache_1.cacheKeys.node(nodeId).priorityReturnRoute(destinationNodeId));
|
|
3652
3703
|
}
|
|
3704
|
+
/**
|
|
3705
|
+
* For the given node, returns all end node destinations and the priority routes to them.
|
|
3706
|
+
*
|
|
3707
|
+
* **Note:** This is using cached information, since there's no way to query priority routes from a node.
|
|
3708
|
+
* If another controller has assigned routes in the meantime, this information may be out of date.
|
|
3709
|
+
*/
|
|
3710
|
+
getPriorityReturnRoutesCached(nodeId) {
|
|
3711
|
+
const ret = {};
|
|
3712
|
+
const routes = this.driver.cacheList(NetworkCache_1.cacheKeys.node(nodeId)._priorityReturnRouteBaseKey);
|
|
3713
|
+
for (const [key, route] of Object.entries(routes)) {
|
|
3714
|
+
const destination = NetworkCache_1.cacheKeyUtils
|
|
3715
|
+
.destinationFromPriorityReturnRouteKey(key);
|
|
3716
|
+
if (destination !== undefined)
|
|
3717
|
+
ret[destination] = route;
|
|
3718
|
+
}
|
|
3719
|
+
return ret;
|
|
3720
|
+
}
|
|
3653
3721
|
/**
|
|
3654
3722
|
* Assigns a priority route from an end node to the SUC. This route will always be used for the first transmission attempt.
|
|
3655
3723
|
* @param nodeId The ID of the end node for which to assign the route
|
|
@@ -5475,7 +5543,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
5475
5543
|
try {
|
|
5476
5544
|
result = await this.driver.waitForBootloaderChunk((c) => c.type === serial_1.BootloaderChunkType.FlowControl, 1000);
|
|
5477
5545
|
}
|
|
5478
|
-
catch
|
|
5546
|
+
catch {
|
|
5479
5547
|
this.driver.controllerLog.print("OTW update failed: The bootloader did not acknowledge the start of transfer.", "error");
|
|
5480
5548
|
const result = {
|
|
5481
5549
|
success: false,
|
|
@@ -5550,7 +5618,7 @@ ${associatedNodes.join(", ")}`,
|
|
|
5550
5618
|
this.driver.waitForBootloaderChunk((c) => c.type === serial_1.BootloaderChunkType.Menu, 1000),
|
|
5551
5619
|
]);
|
|
5552
5620
|
}
|
|
5553
|
-
catch
|
|
5621
|
+
catch {
|
|
5554
5622
|
this.driver.controllerLog.print("OTW update failed: The bootloader did not acknowledge the end of transfer.", "error");
|
|
5555
5623
|
const result = {
|
|
5556
5624
|
success: false,
|