zwave-js 8.7.8 → 8.8.2
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 +179 -164
- 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 +246 -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 +283 -7
- package/build/lib/node/Node.js.map +1 -1
- package/build/lib/node/Types.d.ts +132 -1
- 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
|
@@ -69,6 +69,7 @@ const INodeQuery_1 = require("../node/INodeQuery");
|
|
|
69
69
|
const Types_1 = require("../node/Types");
|
|
70
70
|
const deviceConfig_1 = require("../telemetry/deviceConfig");
|
|
71
71
|
const statistics_1 = require("../telemetry/statistics");
|
|
72
|
+
const MessageGenerators_1 = require("./MessageGenerators");
|
|
72
73
|
const SendThreadMachine_1 = require("./SendThreadMachine");
|
|
73
74
|
const ThrottlePresets_1 = require("./ThrottlePresets");
|
|
74
75
|
const Transaction_1 = require("./Transaction");
|
|
@@ -104,7 +105,6 @@ const defaultOptions = {
|
|
|
104
105
|
openSerialPort: 10,
|
|
105
106
|
controller: 3,
|
|
106
107
|
sendData: 3,
|
|
107
|
-
retryAfterTransmitReport: false,
|
|
108
108
|
nodeInterview: 5,
|
|
109
109
|
},
|
|
110
110
|
preserveUnknownValues: false,
|
|
@@ -209,9 +209,6 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
209
209
|
this.retryNodeInterviewTimeouts = new Map();
|
|
210
210
|
this._statisticsEnabled = false;
|
|
211
211
|
this.isSoftResetting = false;
|
|
212
|
-
this._cleanupHandler = () => {
|
|
213
|
-
void this.destroy();
|
|
214
|
-
};
|
|
215
212
|
this.partialCCSessions = new Map();
|
|
216
213
|
this.lastCallbackId = 0xff;
|
|
217
214
|
this.lastSaveToCache = 0;
|
|
@@ -222,11 +219,6 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
222
219
|
this.options = (0, shared_1.mergeDeep)(options, defaultOptions);
|
|
223
220
|
// And make sure they contain valid values
|
|
224
221
|
checkOptions(this.options);
|
|
225
|
-
// register some cleanup handlers in case the program doesn't get closed cleanly
|
|
226
|
-
this._cleanupHandler = this._cleanupHandler.bind(this);
|
|
227
|
-
process.on("exit", this._cleanupHandler);
|
|
228
|
-
process.on("SIGINT", this._cleanupHandler);
|
|
229
|
-
process.on("uncaughtException", this._cleanupHandler);
|
|
230
222
|
// Initialize logging
|
|
231
223
|
this._logContainer = new core_1.ZWaveLogContainer(this.options.logConfig);
|
|
232
224
|
this._driverLog = new Driver_1.DriverLogger(this._logContainer);
|
|
@@ -291,10 +283,26 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
291
283
|
if (this.handleMissingNodeACK(transaction))
|
|
292
284
|
return;
|
|
293
285
|
}
|
|
294
|
-
transaction
|
|
286
|
+
// If the transaction was already started, we need to throw the error into the message generator
|
|
287
|
+
// so it correctly gets ended. Otherwise just reject the result promise
|
|
288
|
+
if (transaction.parts.self) {
|
|
289
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
290
|
+
transaction.parts.self.throw(error).catch(() => { });
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
transaction.promise.reject(error);
|
|
294
|
+
}
|
|
295
295
|
},
|
|
296
296
|
resolveTransaction: (transaction, result) => {
|
|
297
|
-
transaction
|
|
297
|
+
// If the transaction was already started, we need to end the message generator early by throwing
|
|
298
|
+
// the result. Otherwise just resolve the result promise
|
|
299
|
+
if (transaction.parts.self) {
|
|
300
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
301
|
+
transaction.parts.self.throw(result).catch(() => { });
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
transaction.promise.resolve(result);
|
|
305
|
+
}
|
|
298
306
|
},
|
|
299
307
|
logOutgoingMessage: (msg) => {
|
|
300
308
|
this.driverLog.logMessage(msg, {
|
|
@@ -318,15 +326,30 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
318
326
|
}
|
|
319
327
|
},
|
|
320
328
|
log: this.driverLog.print.bind(this.driverLog),
|
|
329
|
+
logQueue: this.driverLog.sendQueue.bind(this.driverLog),
|
|
321
330
|
}, (0, shared_1.pick)(this.options, ["timeouts", "attempts"]));
|
|
322
331
|
this.sendThread = (0, xstate_1.interpret)(sendThreadMachine);
|
|
323
332
|
// this.sendThread.onTransition((state) => {
|
|
324
333
|
// if (state.changed)
|
|
325
334
|
// this.driverLog.print(
|
|
326
|
-
// `send thread state: ${state.toStrings().
|
|
335
|
+
// `send thread state: ${state.toStrings().join("->")}`,
|
|
327
336
|
// "verbose",
|
|
328
337
|
// );
|
|
329
338
|
// });
|
|
339
|
+
// this.sendThread.onEvent((evt) => {
|
|
340
|
+
// if (evt.type === "forward") {
|
|
341
|
+
// this.driverLog.print(
|
|
342
|
+
// // @ts-ignore
|
|
343
|
+
// `forwarding event: ${evt.payload.type} from ${evt.from} to ${evt.to}`,
|
|
344
|
+
// "verbose",
|
|
345
|
+
// );
|
|
346
|
+
// } else {
|
|
347
|
+
// this.driverLog.print(
|
|
348
|
+
// `send thread event: ${evt.type}`,
|
|
349
|
+
// "verbose",
|
|
350
|
+
// );
|
|
351
|
+
// }
|
|
352
|
+
// });
|
|
330
353
|
}
|
|
331
354
|
ensureNodeSessions(nodeId) {
|
|
332
355
|
if (!this.nodeSessions.has(nodeId)) {
|
|
@@ -447,8 +470,14 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
447
470
|
// Everything async (inluding opening the serial port) must happen in the setImmediate callback
|
|
448
471
|
// asynchronously open the serial port
|
|
449
472
|
setImmediate(async () => {
|
|
450
|
-
|
|
473
|
+
try {
|
|
474
|
+
await this.openSerialport();
|
|
475
|
+
}
|
|
476
|
+
catch (e) {
|
|
477
|
+
spOpenPromise.reject(e);
|
|
478
|
+
void this.destroy();
|
|
451
479
|
return;
|
|
480
|
+
}
|
|
452
481
|
this.driverLog.print("serial port opened");
|
|
453
482
|
this._isOpen = true;
|
|
454
483
|
spOpenPromise.resolve();
|
|
@@ -507,13 +536,13 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
507
536
|
});
|
|
508
537
|
return spOpenPromise;
|
|
509
538
|
}
|
|
510
|
-
async
|
|
539
|
+
async openSerialport() {
|
|
511
540
|
let lastError;
|
|
512
541
|
// After a reset, the serial port may need a few seconds until we can open it - try a few times
|
|
513
542
|
for (let attempt = 1; attempt <= this.options.attempts.openSerialPort; attempt++) {
|
|
514
543
|
try {
|
|
515
544
|
await this.serial.open();
|
|
516
|
-
return
|
|
545
|
+
return;
|
|
517
546
|
}
|
|
518
547
|
catch (e) {
|
|
519
548
|
lastError = e;
|
|
@@ -524,15 +553,7 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
524
553
|
}
|
|
525
554
|
const message = `Failed to open the serial port: ${(0, shared_1.getErrorMessage)(lastError)}`;
|
|
526
555
|
this.driverLog.print(message, "error");
|
|
527
|
-
|
|
528
|
-
if (this._isOpen || !openPromise) {
|
|
529
|
-
this.emit("error", error);
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
openPromise === null || openPromise === void 0 ? void 0 : openPromise.reject(error);
|
|
533
|
-
}
|
|
534
|
-
void this.destroy();
|
|
535
|
-
return false;
|
|
556
|
+
throw new core_1.ZWaveError(message, core_1.ZWaveErrorCodes.Driver_Failed);
|
|
536
557
|
}
|
|
537
558
|
/** Indicates whether all nodes are ready, i.e. the "all nodes ready" event has been emitted */
|
|
538
559
|
get allNodesReady() {
|
|
@@ -1050,9 +1071,14 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1050
1071
|
}
|
|
1051
1072
|
/** Checks if there are any pending transactions that match the given predicate */
|
|
1052
1073
|
hasPendingTransactions(predicate) {
|
|
1053
|
-
const { queue,
|
|
1054
|
-
|
|
1055
|
-
|
|
1074
|
+
const { queue, activeTransactions } = this.sendThread.state.context;
|
|
1075
|
+
if (!!queue.find((t) => predicate(t)))
|
|
1076
|
+
return true;
|
|
1077
|
+
for (const { transaction } of activeTransactions.values()) {
|
|
1078
|
+
if (predicate(transaction))
|
|
1079
|
+
return true;
|
|
1080
|
+
}
|
|
1081
|
+
return false;
|
|
1056
1082
|
}
|
|
1057
1083
|
/**
|
|
1058
1084
|
* Retrieves the maximum version of a command class the given endpoint supports.
|
|
@@ -1139,6 +1165,14 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1139
1165
|
productId === 0x0001) {
|
|
1140
1166
|
return false;
|
|
1141
1167
|
}
|
|
1168
|
+
// Vision Gen5 USB Stick
|
|
1169
|
+
if (manufacturerId === 0x0109 &&
|
|
1170
|
+
productType === 0x1001 &&
|
|
1171
|
+
productId === 0x0201
|
|
1172
|
+
// firmware version 15.1 (GH#3730)
|
|
1173
|
+
) {
|
|
1174
|
+
return false;
|
|
1175
|
+
}
|
|
1142
1176
|
return true;
|
|
1143
1177
|
}
|
|
1144
1178
|
async rememberNoSoftReset() {
|
|
@@ -1200,6 +1234,7 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1200
1234
|
return this.softResetInternal(true);
|
|
1201
1235
|
}
|
|
1202
1236
|
async softResetInternal(destroyOnError) {
|
|
1237
|
+
var _a;
|
|
1203
1238
|
this.controllerLog.print("Performing soft reset...");
|
|
1204
1239
|
try {
|
|
1205
1240
|
this.isSoftResetting = true;
|
|
@@ -1221,8 +1256,11 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1221
1256
|
}
|
|
1222
1257
|
}
|
|
1223
1258
|
this.isSoftResetting = false;
|
|
1224
|
-
//
|
|
1259
|
+
// Resume sending
|
|
1225
1260
|
this.unpauseSendThread();
|
|
1261
|
+
// Soft-resetting disables any ongoing inclusion, so we need to reset
|
|
1262
|
+
// the state that is tracked in the controller
|
|
1263
|
+
(_a = this._controller) === null || _a === void 0 ? void 0 : _a.setInclusionState(Inclusion_1.InclusionState.Idle);
|
|
1226
1264
|
}
|
|
1227
1265
|
async ensureSerialAPI() {
|
|
1228
1266
|
// Wait 1.5 seconds after reset to ensure that the module is ready for communication again
|
|
@@ -1238,7 +1276,12 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1238
1276
|
// If the controller disconnected the serial port during the soft reset, we need to re-open it
|
|
1239
1277
|
if (!this.serial.isOpen) {
|
|
1240
1278
|
this.controllerLog.print("Re-opening serial port...");
|
|
1241
|
-
|
|
1279
|
+
try {
|
|
1280
|
+
await this.openSerialport();
|
|
1281
|
+
}
|
|
1282
|
+
catch {
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1242
1285
|
}
|
|
1243
1286
|
// Wait the configured amount of time for the Serial API started command to be received
|
|
1244
1287
|
this.controllerLog.print("Waiting for the Serial API to start...");
|
|
@@ -1324,27 +1367,12 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1324
1367
|
* Must be called under any circumstances.
|
|
1325
1368
|
*/
|
|
1326
1369
|
async destroy() {
|
|
1327
|
-
var _a, _b, _c
|
|
1370
|
+
var _a, _b, _c;
|
|
1328
1371
|
// Ensure this is only called once and all subsequent calls block
|
|
1329
1372
|
if (this._destroyPromise)
|
|
1330
1373
|
return this._destroyPromise;
|
|
1331
1374
|
this._destroyPromise = (0, deferred_promise_1.createDeferredPromise)();
|
|
1332
1375
|
this.driverLog.print("destroying driver instance...");
|
|
1333
|
-
// Disable inclusion before shutting down
|
|
1334
|
-
try {
|
|
1335
|
-
switch ((_a = this._controller) === null || _a === void 0 ? void 0 : _a.inclusionState) {
|
|
1336
|
-
case Inclusion_1.InclusionState.SmartStart:
|
|
1337
|
-
case Inclusion_1.InclusionState.Including:
|
|
1338
|
-
await this._controller.stopInclusionNoCallback();
|
|
1339
|
-
break;
|
|
1340
|
-
case Inclusion_1.InclusionState.Excluding:
|
|
1341
|
-
await this._controller.stopExclusionNoCallback();
|
|
1342
|
-
break;
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
catch {
|
|
1346
|
-
// ignore
|
|
1347
|
-
}
|
|
1348
1376
|
// First stop the send thread machine and close the serial port, so nothing happens anymore
|
|
1349
1377
|
if (this.sendThread.initialized)
|
|
1350
1378
|
this.sendThread.stop();
|
|
@@ -1364,8 +1392,8 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1364
1392
|
}
|
|
1365
1393
|
try {
|
|
1366
1394
|
// Attempt to close the value DBs
|
|
1367
|
-
await ((
|
|
1368
|
-
await ((
|
|
1395
|
+
await ((_a = this._valueDB) === null || _a === void 0 ? void 0 : _a.close());
|
|
1396
|
+
await ((_b = this._metadataDB) === null || _b === void 0 ? void 0 : _b.close());
|
|
1369
1397
|
}
|
|
1370
1398
|
catch (e) {
|
|
1371
1399
|
this.driverLog.print(`Closing the value DBs failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
|
|
@@ -1383,10 +1411,8 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1383
1411
|
clearTimeout(timeout);
|
|
1384
1412
|
}
|
|
1385
1413
|
// Destroy all nodes
|
|
1386
|
-
(
|
|
1387
|
-
|
|
1388
|
-
process.removeListener("SIGINT", this._cleanupHandler);
|
|
1389
|
-
process.removeListener("uncaughtException", this._cleanupHandler);
|
|
1414
|
+
(_c = this._controller) === null || _c === void 0 ? void 0 : _c.nodes.forEach((n) => n.destroy());
|
|
1415
|
+
this.driverLog.print(`driver instance destroyed`);
|
|
1390
1416
|
// destroy loggers as the very last thing
|
|
1391
1417
|
this._logContainer.destroy();
|
|
1392
1418
|
this._destroyPromise.resolve();
|
|
@@ -1693,11 +1719,10 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1693
1719
|
// If the node does not acknowledge our request, it is either asleep or dead
|
|
1694
1720
|
e.code === core_1.ZWaveErrorCodes.Controller_CallbackNOK &&
|
|
1695
1721
|
(transaction.message instanceof SendDataMessages_1.SendDataRequest ||
|
|
1696
|
-
transaction.message instanceof SendDataBridgeMessages_1.SendDataBridgeRequest)
|
|
1697
|
-
// Ignore pre-transmit handshakes because the actual transaction will be retried
|
|
1698
|
-
transaction.priority !== Constants_1.MessagePriority.PreTransmitHandshake);
|
|
1722
|
+
transaction.message instanceof SendDataBridgeMessages_1.SendDataBridgeRequest));
|
|
1699
1723
|
}
|
|
1700
1724
|
/**
|
|
1725
|
+
* @internal
|
|
1701
1726
|
* Handles the case that a node failed to respond in time.
|
|
1702
1727
|
* Returns `true` if the transaction failure was handled, `false` if it needs to be rejected.
|
|
1703
1728
|
*/
|
|
@@ -1714,15 +1739,8 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1714
1739
|
It is probably asleep, moving its messages to the wakeup queue.`, "warn");
|
|
1715
1740
|
// Mark the node as asleep
|
|
1716
1741
|
// The handler for the asleep status will move the messages to the wakeup queue
|
|
1717
|
-
// We need to re-add the current transaction if that is allowed because otherwise it will be dropped silently
|
|
1718
|
-
if (this.mayMoveToWakeupQueue(transaction)) {
|
|
1719
|
-
this.sendThread.send({ type: "add", transaction });
|
|
1720
|
-
}
|
|
1721
|
-
else {
|
|
1722
|
-
transaction.promise.reject(new core_1.ZWaveError(`The node is asleep`, core_1.ZWaveErrorCodes.Controller_MessageDropped));
|
|
1723
|
-
}
|
|
1724
1742
|
node.markAsAsleep();
|
|
1725
|
-
return
|
|
1743
|
+
return this.mayMoveToWakeupQueue(transaction);
|
|
1726
1744
|
}
|
|
1727
1745
|
else {
|
|
1728
1746
|
const errorMsg = `Node ${node.id} did not respond after ${transaction.message.maxSendAttempts} attempts, it is presumed dead`;
|
|
@@ -2013,9 +2031,6 @@ ${handlers.length} left`);
|
|
|
2013
2031
|
*/
|
|
2014
2032
|
async handleRequest(msg) {
|
|
2015
2033
|
let handlers;
|
|
2016
|
-
// For further actions, we are only interested in the innermost CC
|
|
2017
|
-
if ((0, ICommandClassContainer_1.isCommandClassContainer)(msg))
|
|
2018
|
-
this.unwrapCommands(msg);
|
|
2019
2034
|
if ((0, INodeQuery_1.isNodeQuery)(msg) || (0, ICommandClassContainer_1.isCommandClassContainer)(msg)) {
|
|
2020
2035
|
const node = msg.getNodeUnsafe();
|
|
2021
2036
|
if (node) {
|
|
@@ -2025,7 +2040,17 @@ ${handlers.length} left`);
|
|
|
2025
2040
|
}
|
|
2026
2041
|
}
|
|
2027
2042
|
}
|
|
2043
|
+
// Check if we have a dynamic handler waiting for this message
|
|
2044
|
+
for (const entry of this.awaitedMessages) {
|
|
2045
|
+
if (entry.predicate(msg)) {
|
|
2046
|
+
// resolve the promise - this will remove the entry from the list
|
|
2047
|
+
entry.promise.resolve(msg);
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2028
2051
|
if ((0, ICommandClassContainer_1.isCommandClassContainer)(msg)) {
|
|
2052
|
+
// For further actions, we are only interested in the innermost CC
|
|
2053
|
+
this.unwrapCommands(msg);
|
|
2029
2054
|
const node = msg.getNodeUnsafe();
|
|
2030
2055
|
// If we receive an encrypted message but assume the node is insecure, change our assumption
|
|
2031
2056
|
if ((node === null || node === void 0 ? void 0 : node.isSecure) === false &&
|
|
@@ -2039,6 +2064,7 @@ ${handlers.length} left`);
|
|
|
2039
2064
|
if (!this.mayHandleUnsolicitedCommand(msg.command))
|
|
2040
2065
|
return;
|
|
2041
2066
|
}
|
|
2067
|
+
// Otherwise go through the static handlers
|
|
2042
2068
|
if (msg instanceof ApplicationCommandRequest_1.ApplicationCommandRequest ||
|
|
2043
2069
|
msg instanceof BridgeApplicationCommandRequest_1.BridgeApplicationCommandRequest) {
|
|
2044
2070
|
// we handle ApplicationCommandRequests differently because they are handled by the nodes directly
|
|
@@ -2190,15 +2216,6 @@ ${handlers.length} left`);
|
|
|
2190
2216
|
}
|
|
2191
2217
|
}
|
|
2192
2218
|
else {
|
|
2193
|
-
// Check if we have a dynamic handler waiting for this message
|
|
2194
|
-
for (const entry of this.awaitedMessages) {
|
|
2195
|
-
if (entry.predicate(msg)) {
|
|
2196
|
-
// resolve the promise - this will remove the entry from the list
|
|
2197
|
-
entry.promise.resolve(msg);
|
|
2198
|
-
return;
|
|
2199
|
-
}
|
|
2200
|
-
}
|
|
2201
|
-
// Otherwise loop through the static handlers
|
|
2202
2219
|
// TODO: This deserves a nicer formatting
|
|
2203
2220
|
this.driverLog.print(`handling request ${Constants_1.FunctionType[msg.functionType]} (${msg.functionType})`);
|
|
2204
2221
|
handlers = this.requestHandlers.get(msg.functionType);
|
|
@@ -2291,13 +2308,62 @@ ${handlers.length} left`);
|
|
|
2291
2308
|
msg.command = unwrapped;
|
|
2292
2309
|
}
|
|
2293
2310
|
}
|
|
2311
|
+
/**
|
|
2312
|
+
* Gets called whenever any Serial API command succeeded or a SendData command had a negative callback.
|
|
2313
|
+
*/
|
|
2314
|
+
handleSerialAPICommandResult(msg, options, result) {
|
|
2315
|
+
var _a, _b;
|
|
2316
|
+
// Update statistics
|
|
2317
|
+
const node = msg.getNodeUnsafe();
|
|
2318
|
+
let success = true;
|
|
2319
|
+
if ((0, SendDataShared_1.isSendData)(msg)) {
|
|
2320
|
+
// This shouldn't happen, but just in case
|
|
2321
|
+
if (!node)
|
|
2322
|
+
return;
|
|
2323
|
+
if ((0, SendDataShared_1.isSendDataTransmitReport)(result)) {
|
|
2324
|
+
if (!result.isOK()) {
|
|
2325
|
+
success = false;
|
|
2326
|
+
node.incrementStatistics("commandsDroppedTX");
|
|
2327
|
+
}
|
|
2328
|
+
else {
|
|
2329
|
+
node.incrementStatistics("commandsTX");
|
|
2330
|
+
}
|
|
2331
|
+
// Notify listeners about the status report
|
|
2332
|
+
if ((0, SendDataShared_1.hasTXReport)(result)) {
|
|
2333
|
+
(_a = options.onTXReport) === null || _a === void 0 ? void 0 : _a.call(options, result.txReport);
|
|
2334
|
+
// TODO: Update statistics based on the TX report
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
else {
|
|
2339
|
+
(_b = this._controller) === null || _b === void 0 ? void 0 : _b.incrementStatistics("messagesTX");
|
|
2340
|
+
}
|
|
2341
|
+
// Track and potentially update the status of the node when communication succeeds
|
|
2342
|
+
if (node && success) {
|
|
2343
|
+
if (node.canSleep) {
|
|
2344
|
+
// Do not update the node status when we just responded to a nonce request
|
|
2345
|
+
if (options.priority !== Constants_1.MessagePriority.Handshake) {
|
|
2346
|
+
// If the node is not meant to be kept awake, try to send it back to sleep
|
|
2347
|
+
if (!node.keepAwake) {
|
|
2348
|
+
this.debounceSendNodeToSleep(node);
|
|
2349
|
+
}
|
|
2350
|
+
// The node must be awake because it answered
|
|
2351
|
+
node.markAsAwake();
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
else if (node.status !== Types_1.NodeStatus.Alive) {
|
|
2355
|
+
// The node status was unknown or dead - in either case it must be alive because it answered
|
|
2356
|
+
node.markAsAlive();
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2294
2360
|
/**
|
|
2295
2361
|
* Sends a message to the Z-Wave stick.
|
|
2296
2362
|
* @param msg The message to send
|
|
2297
2363
|
* @param options (optional) Options regarding the message transmission
|
|
2298
2364
|
*/
|
|
2299
2365
|
async sendMessage(msg, options = {}) {
|
|
2300
|
-
var _a
|
|
2366
|
+
var _a;
|
|
2301
2367
|
this.ensureReady();
|
|
2302
2368
|
let node;
|
|
2303
2369
|
// Don't send messages to dead nodes
|
|
@@ -2342,17 +2408,24 @@ ${handlers.length} left`);
|
|
|
2342
2408
|
!(msg instanceof SendDataMessages_1.SendDataMulticastRequest) &&
|
|
2343
2409
|
!(msg instanceof SendDataBridgeMessages_1.SendDataMulticastBridgeRequest) &&
|
|
2344
2410
|
// Handshake messages are meant to be sent immediately
|
|
2345
|
-
options.priority !== Constants_1.MessagePriority.Handshake
|
|
2346
|
-
options.priority !== Constants_1.MessagePriority.PreTransmitHandshake) {
|
|
2411
|
+
options.priority !== Constants_1.MessagePriority.Handshake) {
|
|
2347
2412
|
if (options.priority === Constants_1.MessagePriority.NodeQuery) {
|
|
2348
2413
|
// Remember that this transaction was part of an interview
|
|
2349
2414
|
options.tag = "interview";
|
|
2350
2415
|
}
|
|
2351
2416
|
options.priority = Constants_1.MessagePriority.WakeUp;
|
|
2352
2417
|
}
|
|
2353
|
-
//
|
|
2354
|
-
const
|
|
2355
|
-
|
|
2418
|
+
// Create the transaction
|
|
2419
|
+
const { generator, resultPromise } = (0, MessageGenerators_1.createMessageGenerator)(this, msg, (msg, _result) => {
|
|
2420
|
+
this.handleSerialAPICommandResult(msg, options, _result);
|
|
2421
|
+
});
|
|
2422
|
+
const transaction = new Transaction_1.Transaction(this, {
|
|
2423
|
+
message: msg,
|
|
2424
|
+
priority: options.priority,
|
|
2425
|
+
parts: generator,
|
|
2426
|
+
promise: resultPromise,
|
|
2427
|
+
});
|
|
2428
|
+
// Configure its options
|
|
2356
2429
|
if (options.changeNodeStatusOnMissingACK != undefined) {
|
|
2357
2430
|
transaction.changeNodeStatusOnTimeout =
|
|
2358
2431
|
options.changeNodeStatusOnMissingACK;
|
|
@@ -2362,7 +2435,7 @@ ${handlers.length} left`);
|
|
|
2362
2435
|
}
|
|
2363
2436
|
transaction.requestWakeUpOnDemand = !!options.requestWakeUpOnDemand;
|
|
2364
2437
|
transaction.tag = options.tag;
|
|
2365
|
-
//
|
|
2438
|
+
// And queue it
|
|
2366
2439
|
this.sendThread.send({ type: "add", transaction });
|
|
2367
2440
|
// If the transaction should expire, start the timeout
|
|
2368
2441
|
let expirationTimeout;
|
|
@@ -2383,36 +2456,7 @@ ${handlers.length} left`);
|
|
|
2383
2456
|
}, options.expire).unref();
|
|
2384
2457
|
}
|
|
2385
2458
|
try {
|
|
2386
|
-
|
|
2387
|
-
// The message was transmitted, so it can no longer expire
|
|
2388
|
-
if (expirationTimeout)
|
|
2389
|
-
clearTimeout(expirationTimeout);
|
|
2390
|
-
// Update statistics
|
|
2391
|
-
if ((0, SendDataShared_1.isSendData)(msg)) {
|
|
2392
|
-
node === null || node === void 0 ? void 0 : node.incrementStatistics("commandsTX");
|
|
2393
|
-
}
|
|
2394
|
-
else {
|
|
2395
|
-
(_a = this._controller) === null || _a === void 0 ? void 0 : _a.incrementStatistics("messagesTX");
|
|
2396
|
-
}
|
|
2397
|
-
// Track and potentially update the status of the node when communication succeeds
|
|
2398
|
-
if (node) {
|
|
2399
|
-
if (node.canSleep) {
|
|
2400
|
-
// Do not update the node status when we just responded to a nonce request
|
|
2401
|
-
if (options.priority !== Constants_1.MessagePriority.Handshake) {
|
|
2402
|
-
// If the node is not meant to be kept awake, try to send it back to sleep
|
|
2403
|
-
if (!node.keepAwake) {
|
|
2404
|
-
this.debounceSendNodeToSleep(node);
|
|
2405
|
-
}
|
|
2406
|
-
// The node must be awake because it answered
|
|
2407
|
-
node.markAsAwake();
|
|
2408
|
-
}
|
|
2409
|
-
}
|
|
2410
|
-
else if (node.status !== Types_1.NodeStatus.Alive) {
|
|
2411
|
-
// The node status was unknown or dead - in either case it must be alive because it answered
|
|
2412
|
-
node.markAsAlive();
|
|
2413
|
-
}
|
|
2414
|
-
}
|
|
2415
|
-
return ret;
|
|
2459
|
+
return (await resultPromise);
|
|
2416
2460
|
}
|
|
2417
2461
|
catch (e) {
|
|
2418
2462
|
if ((0, core_1.isZWaveError)(e)) {
|
|
@@ -2427,24 +2471,28 @@ ${handlers.length} left`);
|
|
|
2427
2471
|
e.context.functionType !== Constants_1.FunctionType.SendDataBridge &&
|
|
2428
2472
|
e.context.functionType !==
|
|
2429
2473
|
Constants_1.FunctionType.SendDataMulticastBridge) {
|
|
2430
|
-
(
|
|
2474
|
+
(_a = this._controller) === null || _a === void 0 ? void 0 : _a.incrementStatistics("messagesDroppedTX");
|
|
2431
2475
|
return e.context;
|
|
2432
2476
|
}
|
|
2433
2477
|
else if (e.code === core_1.ZWaveErrorCodes.Controller_NodeTimeout) {
|
|
2434
2478
|
// If the node failed to respond in time, remember this for the statistics
|
|
2435
2479
|
node === null || node === void 0 ? void 0 : node.incrementStatistics("timeoutResponse");
|
|
2436
2480
|
}
|
|
2481
|
+
// Enrich errors with the transaction's stack instead of the internal stack
|
|
2482
|
+
if (!e.transactionSource) {
|
|
2483
|
+
throw new core_1.ZWaveError(e.message, e.code, e.context, transaction.stack);
|
|
2484
|
+
}
|
|
2437
2485
|
}
|
|
2438
2486
|
throw e;
|
|
2439
2487
|
}
|
|
2488
|
+
finally {
|
|
2489
|
+
// The transaction was handled, so it can no longer expire
|
|
2490
|
+
if (expirationTimeout)
|
|
2491
|
+
clearTimeout(expirationTimeout);
|
|
2492
|
+
}
|
|
2440
2493
|
}
|
|
2441
|
-
/**
|
|
2442
|
-
|
|
2443
|
-
* If the command expects no response **or** the response times out, nothing will be returned
|
|
2444
|
-
* @param command The command to send. It will be encapsulated in a SendData[Multicast]Request.
|
|
2445
|
-
* @param options (optional) Options regarding the message transmission
|
|
2446
|
-
*/
|
|
2447
|
-
async sendCommand(command, options = {}) {
|
|
2494
|
+
/** Wraps a CC in the correct SendData message to use for sending */
|
|
2495
|
+
createSendDataMessage(command, options = {}) {
|
|
2448
2496
|
let msg;
|
|
2449
2497
|
if (command.isSinglecast()) {
|
|
2450
2498
|
if (this.controller.isFunctionSupported(Constants_1.FunctionType.SendDataBridge)) {
|
|
@@ -2456,7 +2504,7 @@ ${handlers.length} left`);
|
|
|
2456
2504
|
}
|
|
2457
2505
|
}
|
|
2458
2506
|
else if (command.isMulticast()) {
|
|
2459
|
-
if (this.controller.isFunctionSupported(Constants_1.FunctionType.
|
|
2507
|
+
if (this.controller.isFunctionSupported(Constants_1.FunctionType.SendDataMulticastBridge)) {
|
|
2460
2508
|
// Prioritize Bridge commands when they are supported
|
|
2461
2509
|
msg = new SendDataBridgeMessages_1.SendDataMulticastBridgeRequest(this, { command });
|
|
2462
2510
|
}
|
|
@@ -2482,6 +2530,16 @@ ${handlers.length} left`);
|
|
|
2482
2530
|
// Automatically encapsulate commands before sending
|
|
2483
2531
|
if (options.autoEncapsulate !== false)
|
|
2484
2532
|
this.encapsulateCommands(msg);
|
|
2533
|
+
return msg;
|
|
2534
|
+
}
|
|
2535
|
+
/**
|
|
2536
|
+
* Sends a command to a Z-Wave node. If the node returns a command in response, that command will be the return value.
|
|
2537
|
+
* If the command expects no response **or** the response times out, nothing will be returned
|
|
2538
|
+
* @param command The command to send. It will be encapsulated in a SendData[Multicast]Request.
|
|
2539
|
+
* @param options (optional) Options regarding the message transmission
|
|
2540
|
+
*/
|
|
2541
|
+
async sendCommand(command, options = {}) {
|
|
2542
|
+
const msg = this.createSendDataMessage(command, options);
|
|
2485
2543
|
try {
|
|
2486
2544
|
const resp = await this.sendMessage(msg, options);
|
|
2487
2545
|
// And unwrap the response if there was any
|
|
@@ -2568,7 +2626,7 @@ ${handlers.length} left`);
|
|
|
2568
2626
|
/**
|
|
2569
2627
|
* Waits until an unsolicited serial message is received or a timeout has elapsed. Returns the received message.
|
|
2570
2628
|
*
|
|
2571
|
-
* **Note:**
|
|
2629
|
+
* **Note:** To wait for a certain CommandClass, better use {@link waitForCommand}.
|
|
2572
2630
|
* @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected
|
|
2573
2631
|
* @param predicate A predicate function to test all incoming messages
|
|
2574
2632
|
*/
|
|
@@ -2639,8 +2697,6 @@ ${handlers.length} left`);
|
|
|
2639
2697
|
// so they must be dropped
|
|
2640
2698
|
case (0, NoOperationCC_1.messageIsPing)(msg):
|
|
2641
2699
|
case transaction.priority === Constants_1.MessagePriority.Handshake:
|
|
2642
|
-
// Outgoing handshake requests are very likely not valid after wakeup, so drop them too
|
|
2643
|
-
case transaction.priority === Constants_1.MessagePriority.PreTransmitHandshake:
|
|
2644
2700
|
// We also don't want to immediately send the node to sleep when it wakes up
|
|
2645
2701
|
case (0, ICommandClassContainer_1.isCommandClassContainer)(msg) &&
|
|
2646
2702
|
msg.command instanceof WakeUpCC_1.WakeUpCCNoMoreInformation:
|
|
@@ -2725,47 +2781,6 @@ ${handlers.length} left`);
|
|
|
2725
2781
|
sortSendQueue() {
|
|
2726
2782
|
this.sendThread.send("sortQueue");
|
|
2727
2783
|
}
|
|
2728
|
-
/** Re-sends the current command if it is S2 encapsulated */
|
|
2729
|
-
resendS2EncapsulatedCommand() {
|
|
2730
|
-
// If this is called, a receiving node couldn't decode the last message we sent it
|
|
2731
|
-
const { currentTransaction } = this.sendThread.state.context;
|
|
2732
|
-
if (currentTransaction &&
|
|
2733
|
-
(0, ICommandClassContainer_1.isCommandClassContainer)(currentTransaction.message) &&
|
|
2734
|
-
currentTransaction.message.command instanceof
|
|
2735
|
-
commandclass_1.Security2CCMessageEncapsulation) {
|
|
2736
|
-
const cmd = currentTransaction.message.command;
|
|
2737
|
-
if (cmd.wasRetriedAfterDecodeFailure) {
|
|
2738
|
-
this._controllerLog.logNode(cmd.nodeId, {
|
|
2739
|
-
message: `failed to decode the message after re-transmission with SPAN extension, dropping the message.`,
|
|
2740
|
-
direction: "none",
|
|
2741
|
-
level: "warn",
|
|
2742
|
-
});
|
|
2743
|
-
this.sendThread.send({
|
|
2744
|
-
type: "reduce",
|
|
2745
|
-
reducer: (_t, source) => {
|
|
2746
|
-
if (source === "current") {
|
|
2747
|
-
return {
|
|
2748
|
-
type: "reject",
|
|
2749
|
-
code: core_1.ZWaveErrorCodes.Security2CC_CannotDecode,
|
|
2750
|
-
message: "The node failed to decode the message.",
|
|
2751
|
-
};
|
|
2752
|
-
}
|
|
2753
|
-
else {
|
|
2754
|
-
return { type: "keep" };
|
|
2755
|
-
}
|
|
2756
|
-
},
|
|
2757
|
-
});
|
|
2758
|
-
}
|
|
2759
|
-
else {
|
|
2760
|
-
this._controllerLog.logNode(cmd.nodeId, {
|
|
2761
|
-
message: `failed to decode the message, retrying with SPAN extension...`,
|
|
2762
|
-
direction: "none",
|
|
2763
|
-
});
|
|
2764
|
-
cmd.prepareRetryAfterDecodeFailure();
|
|
2765
|
-
this.sendThread.send("resend");
|
|
2766
|
-
}
|
|
2767
|
-
}
|
|
2768
|
-
}
|
|
2769
2784
|
/**
|
|
2770
2785
|
* Does the work for saveNetworkToCache. This is not throttled, so any call
|
|
2771
2786
|
* to this method WILL save the network.
|