zwave-js 8.5.1 → 8.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/Utils.d.ts +2 -2
- package/build/Utils.d.ts.map +1 -1
- package/build/Utils.js +3 -1
- package/build/Utils.js.map +1 -1
- package/build/lib/commandclass/API.d.ts +7 -3
- package/build/lib/commandclass/API.d.ts.map +1 -1
- package/build/lib/commandclass/API.js +9 -1
- package/build/lib/commandclass/API.js.map +1 -1
- package/build/lib/commandclass/AssociationCC.d.ts.map +1 -1
- package/build/lib/commandclass/AssociationCC.js +2 -4
- package/build/lib/commandclass/AssociationCC.js.map +1 -1
- package/build/lib/commandclass/BinarySwitchCC.d.ts.map +1 -1
- package/build/lib/commandclass/BinarySwitchCC.js +7 -3
- package/build/lib/commandclass/BinarySwitchCC.js.map +1 -1
- package/build/lib/commandclass/ColorSwitchCC.js +2 -2
- package/build/lib/commandclass/ColorSwitchCC.js.map +1 -1
- package/build/lib/commandclass/ConfigurationCC.d.ts.map +1 -1
- package/build/lib/commandclass/ConfigurationCC.js +3 -1
- package/build/lib/commandclass/ConfigurationCC.js.map +1 -1
- package/build/lib/commandclass/ManufacturerProprietaryCC.d.ts.map +1 -1
- package/build/lib/commandclass/ManufacturerProprietaryCC.js +15 -8
- package/build/lib/commandclass/ManufacturerProprietaryCC.js.map +1 -1
- package/build/lib/commandclass/MultiChannelAssociationCC.d.ts.map +1 -1
- package/build/lib/commandclass/MultiChannelAssociationCC.js +2 -4
- package/build/lib/commandclass/MultiChannelAssociationCC.js.map +1 -1
- package/build/lib/commandclass/MultilevelSwitchCC.d.ts.map +1 -1
- package/build/lib/commandclass/MultilevelSwitchCC.js +2 -4
- package/build/lib/commandclass/MultilevelSwitchCC.js.map +1 -1
- package/build/lib/commandclass/SoundSwitchCC.js +2 -2
- package/build/lib/commandclass/SoundSwitchCC.js.map +1 -1
- package/build/lib/commandclass/ThermostatFanModeCC.js +2 -2
- package/build/lib/commandclass/ThermostatFanModeCC.js.map +1 -1
- package/build/lib/commandclass/ThermostatModeCC.js +2 -2
- package/build/lib/commandclass/ThermostatModeCC.js.map +1 -1
- package/build/lib/commandclass/ThermostatSetpointCC.d.ts.map +1 -1
- package/build/lib/commandclass/ThermostatSetpointCC.js +2 -2
- package/build/lib/commandclass/ThermostatSetpointCC.js.map +1 -1
- package/build/lib/commandclass/UserCodeCC.js +2 -2
- package/build/lib/commandclass/UserCodeCC.js.map +1 -1
- package/build/lib/commandclass/WakeUpCC.js +2 -2
- package/build/lib/commandclass/WakeUpCC.js.map +1 -1
- package/build/lib/controller/AddNodeToNetworkRequest.d.ts +36 -8
- package/build/lib/controller/AddNodeToNetworkRequest.d.ts.map +1 -1
- package/build/lib/controller/AddNodeToNetworkRequest.js +111 -41
- package/build/lib/controller/AddNodeToNetworkRequest.js.map +1 -1
- package/build/lib/controller/ApplicationUpdateRequest.d.ts +16 -4
- package/build/lib/controller/ApplicationUpdateRequest.d.ts.map +1 -1
- package/build/lib/controller/ApplicationUpdateRequest.js +56 -15
- package/build/lib/controller/ApplicationUpdateRequest.js.map +1 -1
- package/build/lib/controller/Controller.d.ts +128 -23
- package/build/lib/controller/Controller.d.ts.map +1 -1
- package/build/lib/controller/Controller.js +882 -281
- package/build/lib/controller/Controller.js.map +1 -1
- package/build/lib/controller/Inclusion.d.ts +31 -4
- package/build/lib/controller/Inclusion.d.ts.map +1 -1
- package/build/lib/controller/Inclusion.js +15 -3
- package/build/lib/controller/Inclusion.js.map +1 -1
- package/build/lib/controller/RemoveNodeFromNetworkRequest.d.ts +15 -9
- package/build/lib/controller/RemoveNodeFromNetworkRequest.d.ts.map +1 -1
- package/build/lib/controller/RemoveNodeFromNetworkRequest.js +70 -41
- package/build/lib/controller/RemoveNodeFromNetworkRequest.js.map +1 -1
- package/build/lib/controller/SoftResetRequest.d.ts +4 -0
- package/build/lib/controller/SoftResetRequest.d.ts.map +1 -0
- package/build/lib/controller/SoftResetRequest.js +19 -0
- package/build/lib/controller/SoftResetRequest.js.map +1 -0
- package/build/lib/driver/Driver.d.ts +31 -2
- package/build/lib/driver/Driver.d.ts.map +1 -1
- package/build/lib/driver/Driver.js +478 -82
- package/build/lib/driver/Driver.js.map +1 -1
- package/build/lib/driver/SendThreadMachine.d.ts +4 -1
- package/build/lib/driver/SendThreadMachine.d.ts.map +1 -1
- package/build/lib/driver/SendThreadMachine.js +23 -0
- package/build/lib/driver/SendThreadMachine.js.map +1 -1
- package/build/lib/driver/Transaction.d.ts +2 -0
- package/build/lib/driver/Transaction.d.ts.map +1 -1
- package/build/lib/driver/Transaction.js +2 -0
- package/build/lib/driver/Transaction.js.map +1 -1
- package/build/lib/driver/UpdateConfig.js.map +1 -1
- package/build/lib/driver/ZWaveOptions.d.ts +18 -1
- package/build/lib/driver/ZWaveOptions.d.ts.map +1 -1
- package/build/lib/log/Controller.d.ts +12 -2
- package/build/lib/log/Controller.d.ts.map +1 -1
- package/build/lib/log/Controller.js +45 -0
- package/build/lib/log/Controller.js.map +1 -1
- package/build/lib/log/Driver.d.ts +5 -2
- package/build/lib/log/Driver.d.ts.map +1 -1
- package/build/lib/log/Driver.js +3 -0
- package/build/lib/log/Driver.js.map +1 -1
- package/build/lib/message/Constants.d.ts +3 -2
- package/build/lib/message/Constants.d.ts.map +1 -1
- package/build/lib/message/Constants.js +3 -2
- package/build/lib/message/Constants.js.map +1 -1
- package/build/lib/node/Node.d.ts +7 -1
- package/build/lib/node/Node.d.ts.map +1 -1
- package/build/lib/node/Node.js +35 -9
- package/build/lib/node/Node.js.map +1 -1
- package/build/lib/serialapi/misc/SerialAPIStartedRequest.d.ts +41 -0
- package/build/lib/serialapi/misc/SerialAPIStartedRequest.d.ts.map +1 -0
- package/build/lib/serialapi/misc/SerialAPIStartedRequest.js +91 -0
- package/build/lib/serialapi/misc/SerialAPIStartedRequest.js.map +1 -0
- package/build/lib/serialapi/nvm/NVMOperationsMessages.d.ts +60 -0
- package/build/lib/serialapi/nvm/NVMOperationsMessages.d.ts.map +1 -0
- package/build/lib/serialapi/nvm/NVMOperationsMessages.js +187 -0
- package/build/lib/serialapi/nvm/NVMOperationsMessages.js.map +1 -0
- package/package.json +14 -14
|
@@ -55,9 +55,12 @@ const ApplicationCommandRequest_1 = require("../controller/ApplicationCommandReq
|
|
|
55
55
|
const ApplicationUpdateRequest_1 = require("../controller/ApplicationUpdateRequest");
|
|
56
56
|
const BridgeApplicationCommandRequest_1 = require("../controller/BridgeApplicationCommandRequest");
|
|
57
57
|
const Controller_1 = require("../controller/Controller");
|
|
58
|
+
const GetControllerVersionMessages_1 = require("../controller/GetControllerVersionMessages");
|
|
59
|
+
const Inclusion_1 = require("../controller/Inclusion");
|
|
58
60
|
const SendDataBridgeMessages_1 = require("../controller/SendDataBridgeMessages");
|
|
59
61
|
const SendDataMessages_1 = require("../controller/SendDataMessages");
|
|
60
62
|
const SendDataShared_1 = require("../controller/SendDataShared");
|
|
63
|
+
const SoftResetRequest_1 = require("../controller/SoftResetRequest");
|
|
61
64
|
const Controller_2 = require("../log/Controller");
|
|
62
65
|
const Driver_1 = require("../log/Driver");
|
|
63
66
|
const Constants_1 = require("../message/Constants");
|
|
@@ -93,9 +96,12 @@ const defaultOptions = {
|
|
|
93
96
|
report: 10000,
|
|
94
97
|
nonce: 5000,
|
|
95
98
|
sendDataCallback: 65000,
|
|
96
|
-
refreshValue: 5000,
|
|
99
|
+
refreshValue: 5000,
|
|
100
|
+
refreshValueAfterTransition: 1000,
|
|
101
|
+
serialAPIStarted: 5000,
|
|
97
102
|
},
|
|
98
103
|
attempts: {
|
|
104
|
+
openSerialPort: 10,
|
|
99
105
|
controller: 3,
|
|
100
106
|
sendData: 3,
|
|
101
107
|
retryAfterTransmitReport: false,
|
|
@@ -103,6 +109,8 @@ const defaultOptions = {
|
|
|
103
109
|
},
|
|
104
110
|
preserveUnknownValues: false,
|
|
105
111
|
disableOptimisticValueUpdate: false,
|
|
112
|
+
// By default enable soft reset unless the env variable is set
|
|
113
|
+
enableSoftReset: !process.env.ZWAVEJS_DISABLE_SOFT_RESET,
|
|
106
114
|
interview: {
|
|
107
115
|
skipInterview: false,
|
|
108
116
|
queryAllUserCodes: false,
|
|
@@ -110,6 +118,7 @@ const defaultOptions = {
|
|
|
110
118
|
storage: {
|
|
111
119
|
driver: fs_extra_1.default,
|
|
112
120
|
cacheDir: path_1.default.resolve(libraryRootDir, "cache"),
|
|
121
|
+
lockDir: process.env.ZWAVEJS_LOCK_DIRECTORY,
|
|
113
122
|
throttle: "normal",
|
|
114
123
|
},
|
|
115
124
|
preferences: {
|
|
@@ -138,6 +147,10 @@ function checkOptions(options) {
|
|
|
138
147
|
if (options.timeouts.sendDataCallback < 10000) {
|
|
139
148
|
throw new core_1.ZWaveError(`The Send Data Callback timeout must be at least 10000 milliseconds!`, core_1.ZWaveErrorCodes.Driver_InvalidOptions);
|
|
140
149
|
}
|
|
150
|
+
if (options.timeouts.serialAPIStarted < 1000 ||
|
|
151
|
+
options.timeouts.serialAPIStarted > 30000) {
|
|
152
|
+
throw new core_1.ZWaveError(`The Serial API started timeout must be between 1000 and 30000 milliseconds!`, core_1.ZWaveErrorCodes.Driver_InvalidOptions);
|
|
153
|
+
}
|
|
141
154
|
if (options.securityKeys != undefined) {
|
|
142
155
|
if (options.networkKey != undefined) {
|
|
143
156
|
throw new core_1.ZWaveError(`The deprecated networkKey option may not be used together with the new securityKeys option!`, core_1.ZWaveErrorCodes.Driver_InvalidOptions);
|
|
@@ -182,6 +195,8 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
182
195
|
this.port = port;
|
|
183
196
|
/** A map of handlers for all sorts of requests */
|
|
184
197
|
this.requestHandlers = new Map();
|
|
198
|
+
/** A map of awaited messages */
|
|
199
|
+
this.awaitedMessages = [];
|
|
185
200
|
/** A map of awaited commands */
|
|
186
201
|
this.awaitedCommands = [];
|
|
187
202
|
/** A map of Node ID -> ongoing sessions */
|
|
@@ -193,6 +208,7 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
193
208
|
this._nodesReadyEventEmitted = false;
|
|
194
209
|
this.retryNodeInterviewTimeouts = new Map();
|
|
195
210
|
this._statisticsEnabled = false;
|
|
211
|
+
this.isSoftResetting = false;
|
|
196
212
|
this._cleanupHandler = () => {
|
|
197
213
|
void this.destroy();
|
|
198
214
|
};
|
|
@@ -217,6 +233,7 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
217
233
|
this._controllerLog = new Controller_2.ControllerLogger(this._logContainer);
|
|
218
234
|
// Initialize the cache
|
|
219
235
|
this.cacheDir = this.options.storage.cacheDir;
|
|
236
|
+
// TODO: Load provisioning list
|
|
220
237
|
// Initialize config manager
|
|
221
238
|
this.configManager = new config_1.ConfigManager({
|
|
222
239
|
logContainer: this._logContainer,
|
|
@@ -408,15 +425,19 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
408
425
|
this.serial
|
|
409
426
|
.on("data", this.serialport_onData.bind(this))
|
|
410
427
|
.on("error", (err) => {
|
|
428
|
+
var _a;
|
|
429
|
+
if (this.isSoftResetting && !((_a = this.serial) === null || _a === void 0 ? void 0 : _a.isOpen)) {
|
|
430
|
+
// A disconnection while soft resetting is to be expected
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
else if (!this._isOpen) {
|
|
434
|
+
// tryOpenSerialport takes care of error handling
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
411
437
|
const message = `Serial port errored: ${err.message}`;
|
|
412
438
|
this.driverLog.print(message, "error");
|
|
413
439
|
const error = new core_1.ZWaveError(message, core_1.ZWaveErrorCodes.Driver_Failed);
|
|
414
|
-
|
|
415
|
-
this.emit("error", error);
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
spOpenPromise.reject(error);
|
|
419
|
-
}
|
|
440
|
+
this.emit("error", error);
|
|
420
441
|
void this.destroy();
|
|
421
442
|
});
|
|
422
443
|
// If the port is already open, close it first
|
|
@@ -426,22 +447,16 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
426
447
|
// Everything async (inluding opening the serial port) must happen in the setImmediate callback
|
|
427
448
|
// asynchronously open the serial port
|
|
428
449
|
setImmediate(async () => {
|
|
429
|
-
|
|
430
|
-
await this.serial.open();
|
|
431
|
-
}
|
|
432
|
-
catch (e) {
|
|
433
|
-
const message = `Failed to open the serial port: ${(0, shared_1.getErrorMessage)(e)}`;
|
|
434
|
-
this.driverLog.print(message, "error");
|
|
435
|
-
spOpenPromise.reject(new core_1.ZWaveError(message, core_1.ZWaveErrorCodes.Driver_Failed));
|
|
436
|
-
void this.destroy();
|
|
450
|
+
if (!(await this.tryOpenSerialport(spOpenPromise)))
|
|
437
451
|
return;
|
|
438
|
-
}
|
|
439
452
|
this.driverLog.print("serial port opened");
|
|
440
453
|
this._isOpen = true;
|
|
441
454
|
spOpenPromise.resolve();
|
|
455
|
+
// Perform initialization sequence
|
|
442
456
|
await this.writeHeader(serial_1.MessageHeaders.NAK);
|
|
443
|
-
//
|
|
444
|
-
|
|
457
|
+
// Per the specs, this should be followed by a soft-reset but we need to be able
|
|
458
|
+
// to handle sticks that don't support the soft reset command. Therefore we do it
|
|
459
|
+
// after opening the value DBs
|
|
445
460
|
// Try to create the cache directory. This can fail, in which case we should expose a good error message
|
|
446
461
|
try {
|
|
447
462
|
await this.options.storage.driver.ensureDir(this.cacheDir);
|
|
@@ -482,7 +497,7 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
482
497
|
message = `Failed to initialize the driver, no response from the controller. Are you sure this is a Z-Wave controller?`;
|
|
483
498
|
}
|
|
484
499
|
else {
|
|
485
|
-
message = `Failed to initialize the driver: ${(0, shared_1.getErrorMessage)(e)}`;
|
|
500
|
+
message = `Failed to initialize the driver: ${(0, shared_1.getErrorMessage)(e, true)}`;
|
|
486
501
|
}
|
|
487
502
|
this.driverLog.print(message, "error");
|
|
488
503
|
this.emit("error", new core_1.ZWaveError(message, core_1.ZWaveErrorCodes.Driver_Failed));
|
|
@@ -492,10 +507,63 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
492
507
|
});
|
|
493
508
|
return spOpenPromise;
|
|
494
509
|
}
|
|
510
|
+
async tryOpenSerialport(openPromise) {
|
|
511
|
+
let lastError;
|
|
512
|
+
// After a reset, the serial port may need a few seconds until we can open it - try a few times
|
|
513
|
+
for (let attempt = 1; attempt <= this.options.attempts.openSerialPort; attempt++) {
|
|
514
|
+
try {
|
|
515
|
+
await this.serial.open();
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
catch (e) {
|
|
519
|
+
lastError = e;
|
|
520
|
+
}
|
|
521
|
+
if (attempt < this.options.attempts.openSerialPort) {
|
|
522
|
+
await (0, async_1.wait)(1000);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
const message = `Failed to open the serial port: ${(0, shared_1.getErrorMessage)(lastError)}`;
|
|
526
|
+
this.driverLog.print(message, "error");
|
|
527
|
+
const error = new core_1.ZWaveError(message, core_1.ZWaveErrorCodes.Driver_Failed);
|
|
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;
|
|
536
|
+
}
|
|
495
537
|
/** Indicates whether all nodes are ready, i.e. the "all nodes ready" event has been emitted */
|
|
496
538
|
get allNodesReady() {
|
|
497
539
|
return this._nodesReadyEventEmitted;
|
|
498
540
|
}
|
|
541
|
+
async initValueDBs(homeId) {
|
|
542
|
+
// Always start the value and metadata databases
|
|
543
|
+
const options = {
|
|
544
|
+
ignoreReadErrors: true,
|
|
545
|
+
...ThrottlePresets_1.throttlePresets[this.options.storage.throttle],
|
|
546
|
+
};
|
|
547
|
+
if (this.options.storage.lockDir) {
|
|
548
|
+
options.lockfileDirectory = this.options.storage.lockDir;
|
|
549
|
+
}
|
|
550
|
+
const valueDBFile = path_1.default.join(this.cacheDir, `${homeId.toString(16)}.values.jsonl`);
|
|
551
|
+
this._valueDB = new jsonl_db_1.JsonlDB(valueDBFile, {
|
|
552
|
+
...options,
|
|
553
|
+
reviver: (key, value) => (0, core_1.deserializeCacheValue)(value),
|
|
554
|
+
serializer: (key, value) => (0, core_1.serializeCacheValue)(value),
|
|
555
|
+
});
|
|
556
|
+
await this._valueDB.open();
|
|
557
|
+
const metadataDBFile = path_1.default.join(this.cacheDir, `${homeId.toString(16)}.metadata.jsonl`);
|
|
558
|
+
this._metadataDB = new jsonl_db_1.JsonlDB(metadataDBFile, options);
|
|
559
|
+
await this._metadataDB.open();
|
|
560
|
+
if (process.env.NO_CACHE === "true") {
|
|
561
|
+
// Since value/metadata DBs are append-only, we need to clear them
|
|
562
|
+
// if the cache should be ignored
|
|
563
|
+
this._valueDB.clear();
|
|
564
|
+
this._metadataDB.clear();
|
|
565
|
+
}
|
|
566
|
+
}
|
|
499
567
|
/**
|
|
500
568
|
* Initializes the variables for controller and nodes,
|
|
501
569
|
* adds event handlers and starts the interview process.
|
|
@@ -508,39 +576,56 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
508
576
|
.on("node added", this.onNodeAdded.bind(this))
|
|
509
577
|
.on("node removed", this.onNodeRemoved.bind(this));
|
|
510
578
|
}
|
|
511
|
-
const initValueDBs = async () => {
|
|
512
|
-
// Always start the value and metadata databases
|
|
513
|
-
const options = {
|
|
514
|
-
ignoreReadErrors: true,
|
|
515
|
-
...ThrottlePresets_1.throttlePresets[this.options.storage.throttle],
|
|
516
|
-
};
|
|
517
|
-
const valueDBFile = path_1.default.join(this.cacheDir, `${this._controller.homeId.toString(16)}.values.jsonl`);
|
|
518
|
-
this._valueDB = new jsonl_db_1.JsonlDB(valueDBFile, {
|
|
519
|
-
...options,
|
|
520
|
-
reviver: (key, value) => (0, core_1.deserializeCacheValue)(value),
|
|
521
|
-
serializer: (key, value) => (0, core_1.serializeCacheValue)(value),
|
|
522
|
-
});
|
|
523
|
-
await this._valueDB.open();
|
|
524
|
-
const metadataDBFile = path_1.default.join(this.cacheDir, `${this._controller.homeId.toString(16)}.metadata.jsonl`);
|
|
525
|
-
this._metadataDB = new jsonl_db_1.JsonlDB(metadataDBFile, options);
|
|
526
|
-
await this._metadataDB.open();
|
|
527
|
-
if (process.env.NO_CACHE === "true") {
|
|
528
|
-
// Since value/metadata DBs are append-only, we need to clear them
|
|
529
|
-
// if the cache should be ignored
|
|
530
|
-
this._valueDB.clear();
|
|
531
|
-
this._metadataDB.clear();
|
|
532
|
-
}
|
|
533
|
-
};
|
|
534
579
|
if (!this.options.interview.skipInterview) {
|
|
580
|
+
// Determine controller IDs to open the Value DBs
|
|
581
|
+
// We need to do this first because some older controllers, especially the UZB1 and
|
|
582
|
+
// some 500-series sticks in virtualized environments don't respond after a soft reset
|
|
583
|
+
// No need to initialize databases if skipInterview is true, because it is only used in some
|
|
584
|
+
// Driver unit tests that don't need access to them
|
|
585
|
+
// Identify the controller and determine if it supports soft reset
|
|
586
|
+
await this.controller.identify();
|
|
587
|
+
if (this.options.enableSoftReset && !(await this.maySoftReset())) {
|
|
588
|
+
this.driverLog.print(`Soft reset is enabled through config, but this stick does not support it.`, "warn");
|
|
589
|
+
this.options.enableSoftReset = false;
|
|
590
|
+
}
|
|
591
|
+
if (this.options.enableSoftReset) {
|
|
592
|
+
try {
|
|
593
|
+
await this.softResetInternal(false);
|
|
594
|
+
}
|
|
595
|
+
catch (e) {
|
|
596
|
+
if ((0, core_1.isZWaveError)(e) &&
|
|
597
|
+
e.code === core_1.ZWaveErrorCodes.Driver_Failed) {
|
|
598
|
+
// Remember that soft reset is not supported by this stick
|
|
599
|
+
await this.rememberNoSoftReset();
|
|
600
|
+
// Then fail the driver
|
|
601
|
+
await this.destroy();
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
// There are situations where a controller claims it has the ID 0,
|
|
607
|
+
// which isn't valid. In this case try again after having soft-reset the stick
|
|
608
|
+
if (this.controller.ownNodeId === 0 &&
|
|
609
|
+
this.options.enableSoftReset) {
|
|
610
|
+
this.driverLog.print(`Controller identification returned invalid node ID 0 - trying again...`, "warn");
|
|
611
|
+
await this.controller.identify();
|
|
612
|
+
}
|
|
613
|
+
if (this.controller.ownNodeId === 0) {
|
|
614
|
+
this.driverLog.print(`Controller identification returned invalid node ID 0`, "error");
|
|
615
|
+
await this.destroy();
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
// now that we know the home ID, we can open the databases
|
|
619
|
+
await this.initValueDBs(this.controller.homeId);
|
|
535
620
|
// Interview the controller.
|
|
536
|
-
await this._controller.interview(
|
|
621
|
+
await this._controller.interview(async () => {
|
|
537
622
|
// Try to restore the network information from the cache
|
|
538
623
|
if (process.env.NO_CACHE !== "true") {
|
|
539
624
|
await this.restoreNetworkStructureFromCache();
|
|
540
625
|
}
|
|
541
626
|
});
|
|
542
|
-
//
|
|
543
|
-
|
|
627
|
+
// Auto-enable smart start inclusion
|
|
628
|
+
this._controller.autoProvisionSmartStart();
|
|
544
629
|
}
|
|
545
630
|
// Set up the S0 security manager. We can only do that after the controller
|
|
546
631
|
// interview because we need to know the controller node id.
|
|
@@ -1017,6 +1102,182 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1017
1102
|
throw new core_1.ZWaveError("Cannot retrieve the version of a CC that is not implemented", core_1.ZWaveErrorCodes.CC_NotSupported);
|
|
1018
1103
|
}
|
|
1019
1104
|
}
|
|
1105
|
+
async maySoftReset() {
|
|
1106
|
+
var _a;
|
|
1107
|
+
// If we've previously determined a stick not to support soft reset, don't bother trying again
|
|
1108
|
+
let supportsSoftReset;
|
|
1109
|
+
if (this._controllerInterviewed) {
|
|
1110
|
+
supportsSoftReset = this.controller.supportsSoftReset;
|
|
1111
|
+
}
|
|
1112
|
+
else {
|
|
1113
|
+
// The controller wasn't interviewed yet, read the json file manually
|
|
1114
|
+
const fs = this.options.storage.driver;
|
|
1115
|
+
const cacheFile = path_1.default.join(this.cacheDir, this.controller.homeId.toString(16) + ".json");
|
|
1116
|
+
// Read it if it exists
|
|
1117
|
+
let json;
|
|
1118
|
+
if (await fs.pathExists(cacheFile)) {
|
|
1119
|
+
try {
|
|
1120
|
+
json = JSON.parse(await fs.readFile(cacheFile, "utf8"));
|
|
1121
|
+
}
|
|
1122
|
+
catch { }
|
|
1123
|
+
}
|
|
1124
|
+
supportsSoftReset = (_a = json === null || json === void 0 ? void 0 : json.controller) === null || _a === void 0 ? void 0 : _a.supportsSoftReset;
|
|
1125
|
+
}
|
|
1126
|
+
if (supportsSoftReset === false)
|
|
1127
|
+
return false;
|
|
1128
|
+
// Blacklist some sticks that are known to not support soft reset
|
|
1129
|
+
const { manufacturerId, productType, productId } = this.controller;
|
|
1130
|
+
// Z-Wave.me UZB1
|
|
1131
|
+
if (manufacturerId === 0x0115 &&
|
|
1132
|
+
productType === 0x0000 &&
|
|
1133
|
+
productId === 0x0000) {
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
// Z-Wave.me UZB
|
|
1137
|
+
if (manufacturerId === 0x0115 &&
|
|
1138
|
+
productType === 0x0400 &&
|
|
1139
|
+
productId === 0x0001) {
|
|
1140
|
+
return false;
|
|
1141
|
+
}
|
|
1142
|
+
return true;
|
|
1143
|
+
}
|
|
1144
|
+
async rememberNoSoftReset() {
|
|
1145
|
+
var _a;
|
|
1146
|
+
this.driverLog.print("Soft reset seems not to be supported by this stick, disabling it.", "warn");
|
|
1147
|
+
this.controller.supportsSoftReset = false;
|
|
1148
|
+
if (this._controllerInterviewed) {
|
|
1149
|
+
// We can use the normal method for this
|
|
1150
|
+
await this.saveNetworkToCacheInternal();
|
|
1151
|
+
}
|
|
1152
|
+
else {
|
|
1153
|
+
// saveNetworkToCache won't write anything, just edit the file "manually"
|
|
1154
|
+
// TODO: This is ugly, rework this when changing how the network data is saved
|
|
1155
|
+
const fs = this.options.storage.driver;
|
|
1156
|
+
await fs.ensureDir(this.cacheDir);
|
|
1157
|
+
const cacheFile = path_1.default.join(this.cacheDir, this.controller.homeId.toString(16) + ".json");
|
|
1158
|
+
// Read it if it exists
|
|
1159
|
+
let json;
|
|
1160
|
+
if (await fs.pathExists(cacheFile)) {
|
|
1161
|
+
try {
|
|
1162
|
+
json = JSON.parse(await fs.readFile(cacheFile, "utf8"));
|
|
1163
|
+
}
|
|
1164
|
+
catch { }
|
|
1165
|
+
}
|
|
1166
|
+
// Change the supportsSoftReset flag
|
|
1167
|
+
json !== null && json !== void 0 ? json : (json = {});
|
|
1168
|
+
(_a = json.controller) !== null && _a !== void 0 ? _a : (json.controller = {});
|
|
1169
|
+
json.controller.supportsSoftReset = false;
|
|
1170
|
+
// And save it again
|
|
1171
|
+
const jsonString = (0, shared_1.stringify)(json);
|
|
1172
|
+
await this.options.storage.driver.writeFile(cacheFile, jsonString, "utf8");
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Soft-resets the controller if the feature is enabled
|
|
1177
|
+
*/
|
|
1178
|
+
async trySoftReset() {
|
|
1179
|
+
if (this.options.enableSoftReset) {
|
|
1180
|
+
await this.softReset();
|
|
1181
|
+
}
|
|
1182
|
+
else {
|
|
1183
|
+
const message = `The soft reset feature is not enabled, skipping API call.`;
|
|
1184
|
+
this.controllerLog.print(message, "warn");
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Instruct the controller to soft-reset.
|
|
1189
|
+
*
|
|
1190
|
+
* **Warning:** USB modules will reconnect, meaning that they might get a new address.
|
|
1191
|
+
*
|
|
1192
|
+
* **Warning:** This call will throw if soft-reset is not enabled.
|
|
1193
|
+
*/
|
|
1194
|
+
async softReset() {
|
|
1195
|
+
if (!this.options.enableSoftReset) {
|
|
1196
|
+
const message = `The soft reset feature has been disabled with a config option or the ZWAVEJS_DISABLE_SOFT_RESET environment variable.`;
|
|
1197
|
+
this.controllerLog.print(message, "error");
|
|
1198
|
+
throw new core_1.ZWaveError(message, core_1.ZWaveErrorCodes.Driver_FeatureDisabled);
|
|
1199
|
+
}
|
|
1200
|
+
return this.softResetInternal(true);
|
|
1201
|
+
}
|
|
1202
|
+
async softResetInternal(destroyOnError) {
|
|
1203
|
+
this.controllerLog.print("Performing soft reset...");
|
|
1204
|
+
try {
|
|
1205
|
+
this.isSoftResetting = true;
|
|
1206
|
+
await this.sendMessage(new SoftResetRequest_1.SoftResetRequest(this), {
|
|
1207
|
+
supportCheck: false,
|
|
1208
|
+
pauseSendThread: true,
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
catch (e) {
|
|
1212
|
+
this.controllerLog.print(`Soft reset failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
|
|
1213
|
+
}
|
|
1214
|
+
// Make sure we're able to communicate with the controller again
|
|
1215
|
+
if (!(await this.ensureSerialAPI())) {
|
|
1216
|
+
if (destroyOnError) {
|
|
1217
|
+
await this.destroy();
|
|
1218
|
+
}
|
|
1219
|
+
else {
|
|
1220
|
+
throw new core_1.ZWaveError("The Serial API did not respond after soft-reset", core_1.ZWaveErrorCodes.Driver_Failed);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
this.isSoftResetting = false;
|
|
1224
|
+
// And resume sending
|
|
1225
|
+
this.unpauseSendThread();
|
|
1226
|
+
}
|
|
1227
|
+
async ensureSerialAPI() {
|
|
1228
|
+
// Wait 1.5 seconds after reset to ensure that the module is ready for communication again
|
|
1229
|
+
// Z-Wave 700 sticks are relatively fast, so we also wait for the Serial API started command
|
|
1230
|
+
// to bail early
|
|
1231
|
+
this.controllerLog.print("Waiting for the controller to reconnect...");
|
|
1232
|
+
let waitResult = await this.waitForMessage((msg) => msg.functionType === Constants_1.FunctionType.SerialAPIStarted, 1500).catch(() => false);
|
|
1233
|
+
if (waitResult) {
|
|
1234
|
+
// Serial API did start, maybe do something with the information?
|
|
1235
|
+
this.controllerLog.print("reconnected and restarted");
|
|
1236
|
+
return true;
|
|
1237
|
+
}
|
|
1238
|
+
// If the controller disconnected the serial port during the soft reset, we need to re-open it
|
|
1239
|
+
if (!this.serial.isOpen) {
|
|
1240
|
+
this.controllerLog.print("Re-opening serial port...");
|
|
1241
|
+
await this.tryOpenSerialport();
|
|
1242
|
+
}
|
|
1243
|
+
// Wait the configured amount of time for the Serial API started command to be received
|
|
1244
|
+
this.controllerLog.print("Waiting for the Serial API to start...");
|
|
1245
|
+
waitResult = await this.waitForMessage((msg) => msg.functionType === Constants_1.FunctionType.SerialAPIStarted, this.options.timeouts.serialAPIStarted).catch(() => false);
|
|
1246
|
+
if (waitResult) {
|
|
1247
|
+
// Serial API did start, maybe do something with the information?
|
|
1248
|
+
this.controllerLog.print("Serial API started");
|
|
1249
|
+
return true;
|
|
1250
|
+
}
|
|
1251
|
+
this.controllerLog.print("Did not receive notification that Serial API has started, checking if it responds...");
|
|
1252
|
+
// We don't need to use any specific command here. However we're going to use this one in the interview
|
|
1253
|
+
// anyways, so we might aswell use it here too
|
|
1254
|
+
const pollController = async () => {
|
|
1255
|
+
try {
|
|
1256
|
+
// And resume sending - this requires us to unpause the send thread
|
|
1257
|
+
this.unpauseSendThread();
|
|
1258
|
+
await this.sendMessage(new GetControllerVersionMessages_1.GetControllerVersionRequest(this), {
|
|
1259
|
+
supportCheck: false,
|
|
1260
|
+
});
|
|
1261
|
+
this.pauseSendThread();
|
|
1262
|
+
this.controllerLog.print("Serial API responded");
|
|
1263
|
+
return true;
|
|
1264
|
+
}
|
|
1265
|
+
catch {
|
|
1266
|
+
return false;
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
// Poll the controller with increasing backoff delay
|
|
1270
|
+
if (await pollController())
|
|
1271
|
+
return true;
|
|
1272
|
+
for (const backoff of [2, 5, 10, 15]) {
|
|
1273
|
+
this.controllerLog.print(`Serial API did not respond, trying again in ${backoff} seconds...`);
|
|
1274
|
+
await (0, async_1.wait)(backoff * 1000, true);
|
|
1275
|
+
if (await pollController())
|
|
1276
|
+
return true;
|
|
1277
|
+
}
|
|
1278
|
+
this.controllerLog.print("Serial API did not respond, giving up", "error");
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1020
1281
|
/**
|
|
1021
1282
|
* Performs a hard reset on the controller. This wipes out all configuration!
|
|
1022
1283
|
*
|
|
@@ -1063,12 +1324,27 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1063
1324
|
* Must be called under any circumstances.
|
|
1064
1325
|
*/
|
|
1065
1326
|
async destroy() {
|
|
1066
|
-
var _a, _b, _c;
|
|
1327
|
+
var _a, _b, _c, _d;
|
|
1067
1328
|
// Ensure this is only called once and all subsequent calls block
|
|
1068
1329
|
if (this._destroyPromise)
|
|
1069
1330
|
return this._destroyPromise;
|
|
1070
1331
|
this._destroyPromise = (0, deferred_promise_1.createDeferredPromise)();
|
|
1071
1332
|
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
|
+
}
|
|
1072
1348
|
// First stop the send thread machine and close the serial port, so nothing happens anymore
|
|
1073
1349
|
if (this.sendThread.initialized)
|
|
1074
1350
|
this.sendThread.stop();
|
|
@@ -1088,8 +1364,8 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1088
1364
|
}
|
|
1089
1365
|
try {
|
|
1090
1366
|
// Attempt to close the value DBs
|
|
1091
|
-
await ((
|
|
1092
|
-
await ((
|
|
1367
|
+
await ((_b = this._valueDB) === null || _b === void 0 ? void 0 : _b.close());
|
|
1368
|
+
await ((_c = this._metadataDB) === null || _c === void 0 ? void 0 : _c.close());
|
|
1093
1369
|
}
|
|
1094
1370
|
catch (e) {
|
|
1095
1371
|
this.driverLog.print(`Closing the value DBs failed: ${(0, shared_1.getErrorMessage)(e)}`, "error");
|
|
@@ -1105,7 +1381,7 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1105
1381
|
clearTimeout(timeout);
|
|
1106
1382
|
}
|
|
1107
1383
|
// Destroy all nodes
|
|
1108
|
-
(
|
|
1384
|
+
(_d = this._controller) === null || _d === void 0 ? void 0 : _d.nodes.forEach((n) => n.destroy());
|
|
1109
1385
|
process.removeListener("exit", this._cleanupHandler);
|
|
1110
1386
|
process.removeListener("SIGINT", this._cleanupHandler);
|
|
1111
1387
|
process.removeListener("uncaughtException", this._cleanupHandler);
|
|
@@ -1117,7 +1393,7 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1117
1393
|
* Is called when the serial port has received a single-byte message or a complete message buffer
|
|
1118
1394
|
*/
|
|
1119
1395
|
async serialport_onData(data) {
|
|
1120
|
-
var _a, _b, _c, _d, _e, _f, _g
|
|
1396
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1121
1397
|
if (typeof data === "number") {
|
|
1122
1398
|
switch (data) {
|
|
1123
1399
|
// single-byte messages - just forward them to the send thread
|
|
@@ -1145,10 +1421,14 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1145
1421
|
// Then ensure there are no errors
|
|
1146
1422
|
if ((0, ICommandClassContainer_1.isCommandClassContainer)(msg)) {
|
|
1147
1423
|
(0, CommandClass_1.assertValidCCs)(msg);
|
|
1148
|
-
(_c = msg.getNodeUnsafe()) === null || _c === void 0 ? void 0 : _c.incrementStatistics("commandsRX");
|
|
1149
1424
|
}
|
|
1150
|
-
|
|
1151
|
-
(
|
|
1425
|
+
if (!!this._controller) {
|
|
1426
|
+
if ((0, ICommandClassContainer_1.isCommandClassContainer)(msg)) {
|
|
1427
|
+
(_c = msg.getNodeUnsafe()) === null || _c === void 0 ? void 0 : _c.incrementStatistics("commandsRX");
|
|
1428
|
+
}
|
|
1429
|
+
else {
|
|
1430
|
+
this._controller.incrementStatistics("messagesRX");
|
|
1431
|
+
}
|
|
1152
1432
|
}
|
|
1153
1433
|
// all good, send ACK
|
|
1154
1434
|
await this.writeHeader(serial_1.MessageHeaders.ACK);
|
|
@@ -1162,25 +1442,27 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1162
1442
|
const response = this.handleDecodeError(e, data, msg);
|
|
1163
1443
|
if (response)
|
|
1164
1444
|
await this.writeHeader(response);
|
|
1165
|
-
if (
|
|
1166
|
-
(
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1445
|
+
if (!!this._controller) {
|
|
1446
|
+
if ((0, ICommandClassContainer_1.isCommandClassContainer)(msg)) {
|
|
1447
|
+
(_d = msg.getNodeUnsafe()) === null || _d === void 0 ? void 0 : _d.incrementStatistics("commandsDroppedRX");
|
|
1448
|
+
// Figure out if the command was received with supervision encapsulation
|
|
1449
|
+
const supervisionSessionId = SupervisionCC_1.SupervisionCC.getSessionId(msg.command);
|
|
1450
|
+
if (supervisionSessionId !== undefined &&
|
|
1451
|
+
msg.command instanceof CommandClass_1.InvalidCC) {
|
|
1452
|
+
// If it was, we need to notify the sender that we couldn't decode the command
|
|
1453
|
+
await ((_e = msg
|
|
1454
|
+
.getNodeUnsafe()) === null || _e === void 0 ? void 0 : _e.createAPI(core_1.CommandClasses.Supervision, false).sendReport({
|
|
1455
|
+
sessionId: supervisionSessionId,
|
|
1456
|
+
moreUpdatesFollow: false,
|
|
1457
|
+
status: SupervisionCC_1.SupervisionStatus.NoSupport,
|
|
1458
|
+
secure: msg.command.secure,
|
|
1459
|
+
}));
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
else {
|
|
1464
|
+
this._controller.incrementStatistics("messagesDroppedRX");
|
|
1180
1465
|
}
|
|
1181
|
-
}
|
|
1182
|
-
else {
|
|
1183
|
-
(_g = this._controller) === null || _g === void 0 ? void 0 : _g.incrementStatistics("messagesDroppedRX");
|
|
1184
1466
|
}
|
|
1185
1467
|
}
|
|
1186
1468
|
}
|
|
@@ -1192,7 +1474,7 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1192
1474
|
return;
|
|
1193
1475
|
}
|
|
1194
1476
|
// Print something, so we know what is wrong
|
|
1195
|
-
this._driverLog.print((
|
|
1477
|
+
this._driverLog.print((_f = ee.stack) !== null && _f !== void 0 ? _f : ee.message, "error");
|
|
1196
1478
|
}
|
|
1197
1479
|
}
|
|
1198
1480
|
// Don't keep handling the message
|
|
@@ -1207,7 +1489,7 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1207
1489
|
// and handle the encapsulation part normally
|
|
1208
1490
|
if (msg.command instanceof
|
|
1209
1491
|
SecurityCC_1.SecurityCCCommandEncapsulationNonceGet) {
|
|
1210
|
-
void ((
|
|
1492
|
+
void ((_g = msg.getNodeUnsafe()) === null || _g === void 0 ? void 0 : _g.handleSecurityNonceGet());
|
|
1211
1493
|
}
|
|
1212
1494
|
// Transport Service commands must be handled before assembling partial CCs
|
|
1213
1495
|
if ((0, TransportServiceCC_1.isTransportServiceEncapsulation)(msg.command)) {
|
|
@@ -1312,7 +1594,7 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1312
1594
|
throw e;
|
|
1313
1595
|
}
|
|
1314
1596
|
async handleSecurityS2DecodeError(e, msg) {
|
|
1315
|
-
var _a;
|
|
1597
|
+
var _a, _b;
|
|
1316
1598
|
if (!(0, core_1.isZWaveError)(e))
|
|
1317
1599
|
return false;
|
|
1318
1600
|
if ((e.code === core_1.ZWaveErrorCodes.Security2CC_NoSPAN ||
|
|
@@ -1344,13 +1626,42 @@ class Driver extends shared_1.TypedEventEmitter {
|
|
|
1344
1626
|
? "Message authentication failed"
|
|
1345
1627
|
: "No SPAN is established yet";
|
|
1346
1628
|
if (this.controller.bootstrappingS2NodeId === nodeId) {
|
|
1347
|
-
// The node is currently being bootstrapped.
|
|
1348
|
-
this.
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1629
|
+
// The node is currently being bootstrapped.
|
|
1630
|
+
if ((_b = this.securityManager2) === null || _b === void 0 ? void 0 : _b.tempKeys.has(nodeId)) {
|
|
1631
|
+
// The DSK has been verified, so we should be able to decode this command.
|
|
1632
|
+
// If this is the first attempt, we need to request a nonce first
|
|
1633
|
+
if (this.securityManager2.getSPANState(nodeId).type ===
|
|
1634
|
+
core_1.SPANState.None) {
|
|
1635
|
+
this.controllerLog.logNode(nodeId, {
|
|
1636
|
+
message: `${message}, cannot decode command. Requesting a nonce...`,
|
|
1637
|
+
level: "verbose",
|
|
1638
|
+
direction: "outbound",
|
|
1639
|
+
});
|
|
1640
|
+
// Send the node our nonce
|
|
1641
|
+
node.commandClasses["Security 2"]
|
|
1642
|
+
.sendNonce()
|
|
1643
|
+
.catch(() => {
|
|
1644
|
+
// Ignore errors
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
else {
|
|
1648
|
+
// Us repeatedly not being able to decode the command means we need to abort the bootstrapping process
|
|
1649
|
+
// because the PIN is wrong
|
|
1650
|
+
this.controllerLog.logNode(nodeId, {
|
|
1651
|
+
message: `${message}, cannot decode command. Aborting the S2 bootstrapping process...`,
|
|
1652
|
+
level: "error",
|
|
1653
|
+
direction: "inbound",
|
|
1654
|
+
});
|
|
1655
|
+
this.controller.cancelSecureBootstrapS2(shared_2.KEXFailType.BootstrappingCanceled);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
else {
|
|
1659
|
+
this.controllerLog.logNode(nodeId, {
|
|
1660
|
+
message: `Ignoring KEXSet because the DSK has not been verified yet`,
|
|
1661
|
+
level: "verbose",
|
|
1662
|
+
direction: "inbound",
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1354
1665
|
}
|
|
1355
1666
|
else if (!this.hasPendingTransactions(isS2NonceReport)) {
|
|
1356
1667
|
this.controllerLog.logNode(nodeId, {
|
|
@@ -1850,8 +2161,42 @@ ${handlers.length} left`);
|
|
|
1850
2161
|
return;
|
|
1851
2162
|
}
|
|
1852
2163
|
}
|
|
2164
|
+
else if (msg instanceof ApplicationUpdateRequest_1.ApplicationUpdateRequestSmartStartHomeIDReceived) {
|
|
2165
|
+
// the controller is in Smart Start learn mode and a node requests inclusion via Smart Start
|
|
2166
|
+
this.controllerLog.print("Received Smart Start inclusion request");
|
|
2167
|
+
if (this.controller.inclusionState !== Inclusion_1.InclusionState.Idle &&
|
|
2168
|
+
this.controller.inclusionState !== Inclusion_1.InclusionState.SmartStart) {
|
|
2169
|
+
this.controllerLog.print("Controller is busy and cannot handle this inclusion request right now...");
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
// Check if the node is on the provisioning list
|
|
2173
|
+
const provisioningEntry = this.controller.provisioningList.find((entry) => (0, core_1.nwiHomeIdFromDSK)((0, core_1.dskFromString)(entry.dsk)).equals(msg.nwiHomeId));
|
|
2174
|
+
if (!provisioningEntry) {
|
|
2175
|
+
this.controllerLog.print("NWI Home ID not found in provisioning list, ignoring request...");
|
|
2176
|
+
return;
|
|
2177
|
+
}
|
|
2178
|
+
this.controllerLog.print("NWI Home ID found in provisioning list, including node...");
|
|
2179
|
+
try {
|
|
2180
|
+
const result = await this.controller.beginInclusionSmartStart(provisioningEntry);
|
|
2181
|
+
if (!result) {
|
|
2182
|
+
this.controllerLog.print("Smart Start inclusion could not be started", "error");
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
catch (e) {
|
|
2186
|
+
this.controllerLog.print(`Smart Start inclusion could not be started: ${(0, shared_1.getErrorMessage)(e)}`, "error");
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
1853
2189
|
}
|
|
1854
2190
|
else {
|
|
2191
|
+
// Check if we have a dynamic handler waiting for this message
|
|
2192
|
+
for (const entry of this.awaitedMessages) {
|
|
2193
|
+
if (entry.predicate(msg)) {
|
|
2194
|
+
// resolve the promise - this will remove the entry from the list
|
|
2195
|
+
entry.promise.resolve(msg);
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
// Otherwise loop through the static handlers
|
|
1855
2200
|
// TODO: This deserves a nicer formatting
|
|
1856
2201
|
this.driverLog.print(`handling request ${Constants_1.FunctionType[msg.functionType]} (${msg.functionType})`);
|
|
1857
2202
|
handlers = this.requestHandlers.get(msg.functionType);
|
|
@@ -2010,6 +2355,9 @@ ${handlers.length} left`);
|
|
|
2010
2355
|
transaction.changeNodeStatusOnTimeout =
|
|
2011
2356
|
options.changeNodeStatusOnMissingACK;
|
|
2012
2357
|
}
|
|
2358
|
+
if (options.pauseSendThread === true) {
|
|
2359
|
+
transaction.pauseSendThread = true;
|
|
2360
|
+
}
|
|
2013
2361
|
transaction.requestWakeUpOnDemand = !!options.requestWakeUpOnDemand;
|
|
2014
2362
|
transaction.tag = options.tag;
|
|
2015
2363
|
// start sending now (maybe)
|
|
@@ -2216,7 +2564,41 @@ ${handlers.length} left`);
|
|
|
2216
2564
|
return (_a = this.serial) === null || _a === void 0 ? void 0 : _a.writeAsync(data);
|
|
2217
2565
|
}
|
|
2218
2566
|
/**
|
|
2219
|
-
* Waits until
|
|
2567
|
+
* Waits until an unsolicited serial message is received or a timeout has elapsed. Returns the received message.
|
|
2568
|
+
*
|
|
2569
|
+
* **Note:** This does not trigger for [Bridge]ApplicationUpdateRequests, which are handled differently. To wait for a certain CommandClass, use {@link waitForCommand}.
|
|
2570
|
+
* @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected
|
|
2571
|
+
* @param predicate A predicate function to test all incoming messages
|
|
2572
|
+
*/
|
|
2573
|
+
waitForMessage(predicate, timeout) {
|
|
2574
|
+
return new Promise((resolve, reject) => {
|
|
2575
|
+
const entry = {
|
|
2576
|
+
predicate,
|
|
2577
|
+
promise: (0, deferred_promise_1.createDeferredPromise)(),
|
|
2578
|
+
timeout: undefined,
|
|
2579
|
+
};
|
|
2580
|
+
this.awaitedMessages.push(entry);
|
|
2581
|
+
const removeEntry = () => {
|
|
2582
|
+
if (entry.timeout)
|
|
2583
|
+
clearTimeout(entry.timeout);
|
|
2584
|
+
const index = this.awaitedMessages.indexOf(entry);
|
|
2585
|
+
if (index !== -1)
|
|
2586
|
+
this.awaitedMessages.splice(index, 1);
|
|
2587
|
+
};
|
|
2588
|
+
// When the timeout elapses, remove the wait entry and reject the returned Promise
|
|
2589
|
+
entry.timeout = setTimeout(() => {
|
|
2590
|
+
removeEntry();
|
|
2591
|
+
reject(new core_1.ZWaveError(`Received no matching message within the provided timeout!`, core_1.ZWaveErrorCodes.Controller_Timeout));
|
|
2592
|
+
}, timeout).unref();
|
|
2593
|
+
// When the promise is resolved, remove the wait entry and resolve the returned Promise
|
|
2594
|
+
void entry.promise.then((cc) => {
|
|
2595
|
+
removeEntry();
|
|
2596
|
+
resolve(cc);
|
|
2597
|
+
});
|
|
2598
|
+
});
|
|
2599
|
+
}
|
|
2600
|
+
/**
|
|
2601
|
+
* Waits until a CommandClass is received or a timeout has elapsed. Returns the received command.
|
|
2220
2602
|
* @param timeout The number of milliseconds to wait. If the timeout elapses, the returned promise will be rejected
|
|
2221
2603
|
* @param predicate A predicate function to test all incoming command classes
|
|
2222
2604
|
*/
|
|
@@ -2323,6 +2705,20 @@ ${handlers.length} left`);
|
|
|
2323
2705
|
rejectAllTransactionsForNode(nodeId, errorMsg = `The node is dead`, errorCode = core_1.ZWaveErrorCodes.Controller_MessageDropped) {
|
|
2324
2706
|
this.rejectTransactions((t) => t.message.getNodeId() === nodeId, errorMsg, errorCode);
|
|
2325
2707
|
}
|
|
2708
|
+
/**
|
|
2709
|
+
* @internal
|
|
2710
|
+
* Pauses the send thread, avoiding commands to be sent to the controller
|
|
2711
|
+
*/
|
|
2712
|
+
pauseSendThread() {
|
|
2713
|
+
this.sendThread.send({ type: "pause" });
|
|
2714
|
+
}
|
|
2715
|
+
/**
|
|
2716
|
+
* @internal
|
|
2717
|
+
* Unpauses the send thread, allowing commands to be sent to the controller again
|
|
2718
|
+
*/
|
|
2719
|
+
unpauseSendThread() {
|
|
2720
|
+
this.sendThread.send({ type: "unpause" });
|
|
2721
|
+
}
|
|
2326
2722
|
/** Re-sorts the send queue */
|
|
2327
2723
|
sortSendQueue() {
|
|
2328
2724
|
this.sendThread.send("sortQueue");
|