zwave-js 10.0.2 → 10.0.3

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.
@@ -1234,6 +1234,7 @@ protocol version: ${this.protocolVersion}`;
1234
1234
  const securityClass = this.getHighestSecurityClass();
1235
1235
  if (securityClass == undefined ||
1236
1236
  (0, core_1.securityClassIsS2)(securityClass)) {
1237
+ this.driver.controllerLog.logNode(this.nodeId, "Root device interview: Security S2", "silly");
1237
1238
  if (!this.driver.securityManager2) {
1238
1239
  if (!this._hasEmittedNoS2NetworkKeyError) {
1239
1240
  // Cannot interview a secure device securely without a network key
@@ -1265,6 +1266,7 @@ protocol version: ${this.protocolVersion}`;
1265
1266
  this.addCC(core_1.CommandClasses.Security, { secure: true });
1266
1267
  // Query supported CCs unless we know for sure that the node wasn't assigned the S0 security class
1267
1268
  if (this.hasSecurityClass(core_1.SecurityClass.S0_Legacy) !== false) {
1269
+ this.driver.controllerLog.logNode(this.nodeId, "Root device interview: Security S0", "silly");
1268
1270
  if (!this.driver.securityManager) {
1269
1271
  if (!this._hasEmittedNoS0NetworkKeyError) {
1270
1272
  // Cannot interview a secure device securely without a network key
@@ -1289,9 +1291,11 @@ protocol version: ${this.protocolVersion}`;
1289
1291
  // Manufacturer Specific and Version CC need to be handled before the other CCs because they are needed to
1290
1292
  // identify the device and apply device configurations
1291
1293
  if (this.supportsCC(core_1.CommandClasses["Manufacturer Specific"])) {
1294
+ this.driver.controllerLog.logNode(this.nodeId, "Root device interview: Manufacturer Specific", "silly");
1292
1295
  await interviewEndpoint(this, core_1.CommandClasses["Manufacturer Specific"]);
1293
1296
  }
1294
1297
  if (this.supportsCC(core_1.CommandClasses.Version)) {
1298
+ this.driver.controllerLog.logNode(this.nodeId, "Root device interview: Version", "silly");
1295
1299
  await interviewEndpoint(this, core_1.CommandClasses.Version);
1296
1300
  // After the version CC interview of the root endpoint, we have enough info to load the correct device config file
1297
1301
  await this.loadDeviceConfig();
@@ -1338,8 +1342,15 @@ protocol version: ${this.protocolVersion}`;
1338
1342
  // This interview cannot be done
1339
1343
  throw new core_1.ZWaveError("The CC interview cannot be completed because there are circular dependencies between CCs!", core_1.ZWaveErrorCodes.CC_Invalid);
1340
1344
  }
1345
+ this.driver.controllerLog.logNode(this.nodeId, `Root device interviews before endpoints: ${rootInterviewOrderBeforeEndpoints
1346
+ .map((cc) => `\n· ${(0, core_1.getCCName)(cc)}`)
1347
+ .join("")}`, "silly");
1348
+ this.driver.controllerLog.logNode(this.nodeId, `Root device interviews after endpoints: ${rootInterviewOrderAfterEndpoints
1349
+ .map((cc) => `\n· ${(0, core_1.getCCName)(cc)}`)
1350
+ .join("")}`, "silly");
1341
1351
  // Now that we know the correct order, do the interview in sequence
1342
1352
  for (const cc of rootInterviewOrderBeforeEndpoints) {
1353
+ this.driver.controllerLog.logNode(this.nodeId, `Root device interview: ${(0, core_1.getCCName)(cc)}`, "silly");
1343
1354
  const action = await interviewEndpoint(this, cc);
1344
1355
  if (action === "continue")
1345
1356
  continue;
@@ -1361,9 +1372,13 @@ protocol version: ${this.protocolVersion}`;
1361
1372
  // Security S2 is always supported *securely*
1362
1373
  endpoint.addCC(core_1.CommandClasses["Security 2"], { secure: true });
1363
1374
  // If S2 is the highest security class, interview it for the endpoint
1364
- if (securityClass != undefined &&
1365
- (0, core_1.securityClassIsS2)(securityClass) &&
1375
+ if ((0, core_1.securityClassIsS2)(securityClass) &&
1366
1376
  !!this.driver.securityManager2) {
1377
+ this.driver.controllerLog.logNode(this.nodeId, {
1378
+ endpoint: endpoint.index,
1379
+ message: `Endpoint ${endpoint.index} interview: Security S2`,
1380
+ level: "silly",
1381
+ });
1367
1382
  const action = await interviewEndpoint(endpoint, core_1.CommandClasses["Security 2"]);
1368
1383
  if (typeof action === "boolean")
1369
1384
  return action;
@@ -1375,19 +1390,92 @@ protocol version: ${this.protocolVersion}`;
1375
1390
  // If S0 is the highest security class, interview it for the endpoint
1376
1391
  if (securityClass === core_1.SecurityClass.S0_Legacy &&
1377
1392
  !!this.driver.securityManager) {
1393
+ this.driver.controllerLog.logNode(this.nodeId, {
1394
+ endpoint: endpoint.index,
1395
+ message: `Endpoint ${endpoint.index} interview: Security S0`,
1396
+ level: "silly",
1397
+ });
1378
1398
  const action = await interviewEndpoint(endpoint, core_1.CommandClasses.Security);
1379
1399
  if (typeof action === "boolean")
1380
1400
  return action;
1381
1401
  }
1382
1402
  }
1383
- if (endpoint.supportsCC(core_1.CommandClasses.Security) &&
1384
- // The root endpoint has been interviewed, so we know if the device supports security
1385
- this.hasSecurityClass(core_1.SecurityClass.S0_Legacy) === true &&
1386
- // Only interview SecurityCC if the network key was set
1387
- this.driver.securityManager) {
1388
- // Security is always supported *securely*
1389
- endpoint.addCC(core_1.CommandClasses.Security, { secure: true });
1403
+ // It has been found that legacy nodes do not always advertise the S0 Command Class in their Multi
1404
+ // Channel Capability Report and still accept all their Command Class using S0 encapsulation.
1405
+ // A controlling node SHOULD try to control End Points with S0 encapsulation even if S0 is not
1406
+ // listed in the Multi Channel Capability Report.
1407
+ const endpointMissingS0 = securityClass === core_1.SecurityClass.S0_Legacy &&
1408
+ this.supportsCC(core_1.CommandClasses.Security) &&
1409
+ !endpoint.supportsCC(core_1.CommandClasses.Security);
1410
+ if (endpointMissingS0) {
1411
+ this.driver.controllerLog.logNode(this.nodeId, {
1412
+ endpoint: endpoint.index,
1413
+ message: `is included using Security S0, but endpoint ${endpoint.index} does not list the CC. Testing if it accepts secure commands anyways.`,
1414
+ level: "silly",
1415
+ });
1416
+ // Define which CCs we can use to test this - and if supported, how
1417
+ const tests = [
1418
+ {
1419
+ ccId: core_1.CommandClasses["Z-Wave Plus Info"],
1420
+ test: () => endpoint.commandClasses["Z-Wave Plus Info"].get(),
1421
+ },
1422
+ {
1423
+ ccId: core_1.CommandClasses["Binary Switch"],
1424
+ test: () => endpoint.commandClasses["Binary Switch"].get(),
1425
+ },
1426
+ {
1427
+ ccId: core_1.CommandClasses["Binary Sensor"],
1428
+ test: () => endpoint.commandClasses["Binary Sensor"].get(),
1429
+ },
1430
+ {
1431
+ ccId: core_1.CommandClasses["Multilevel Switch"],
1432
+ test: () => endpoint.commandClasses["Multilevel Switch"].get(),
1433
+ },
1434
+ {
1435
+ ccId: core_1.CommandClasses["Multilevel Sensor"],
1436
+ test: () => endpoint.commandClasses["Multilevel Sensor"].get(),
1437
+ },
1438
+ // TODO: add other tests if necessary
1439
+ ];
1440
+ for (const { ccId, test } of tests) {
1441
+ if (!endpoint.supportsCC(ccId))
1442
+ continue;
1443
+ // Temporarily mark the CC as secure so we can use it to test
1444
+ endpoint.addCC(ccId, { secure: true });
1445
+ // Perform the test and treat errors as negative results
1446
+ const success = !!(await test().catch(() => false));
1447
+ if (success) {
1448
+ this.driver.controllerLog.logNode(this.nodeId, {
1449
+ endpoint: endpoint.index,
1450
+ message: `Endpoint ${endpoint.index} accepts/expects secure commands`,
1451
+ level: "silly",
1452
+ });
1453
+ // Mark all endpoint CCs as secure
1454
+ for (const [ccId] of endpoint.getCCs()) {
1455
+ endpoint.addCC(ccId, { secure: true });
1456
+ }
1457
+ }
1458
+ else {
1459
+ this.driver.controllerLog.logNode(this.nodeId, {
1460
+ endpoint: endpoint.index,
1461
+ message: `Endpoint ${endpoint.index} is actually not using S0`,
1462
+ level: "silly",
1463
+ });
1464
+ // Mark the CC as not secure again
1465
+ endpoint.addCC(ccId, { secure: false });
1466
+ }
1467
+ }
1390
1468
  }
1469
+ // if (
1470
+ // endpoint.supportsCC(CommandClasses.Security) &&
1471
+ // // The root endpoint has been interviewed, so we know if the device supports security
1472
+ // this.hasSecurityClass(SecurityClass.S0_Legacy) === true &&
1473
+ // // Only interview SecurityCC if the network key was set
1474
+ // this.driver.securityManager
1475
+ // ) {
1476
+ // // Security is always supported *securely*
1477
+ // endpoint.addCC(CommandClasses.Security, { secure: true });
1478
+ // }
1391
1479
  // The Security S0/S2 CC adds new CCs to the endpoint, so we need to once more remove those
1392
1480
  // that aren't actually properly supported by the device.
1393
1481
  this.applyCommandClassesCompatFlag(endpoint.index);
@@ -1408,8 +1496,20 @@ protocol version: ${this.protocolVersion}`;
1408
1496
  // This interview cannot be done
1409
1497
  throw new core_1.ZWaveError("The CC interview cannot be completed because there are circular dependencies between CCs!", core_1.ZWaveErrorCodes.CC_Invalid);
1410
1498
  }
1499
+ this.driver.controllerLog.logNode(this.nodeId, {
1500
+ endpoint: endpoint.index,
1501
+ message: `Endpoint ${endpoint.index} interview order: ${endpointInterviewOrder
1502
+ .map((cc) => `\n· ${(0, core_1.getCCName)(cc)}`)
1503
+ .join("")}`,
1504
+ level: "silly",
1505
+ });
1411
1506
  // Now that we know the correct order, do the interview in sequence
1412
1507
  for (const cc of endpointInterviewOrder) {
1508
+ this.driver.controllerLog.logNode(this.nodeId, {
1509
+ endpoint: endpoint.index,
1510
+ message: `Endpoint ${endpoint.index} interview: ${(0, core_1.getCCName)(cc)}`,
1511
+ level: "silly",
1512
+ });
1413
1513
  const action = await interviewEndpoint(endpoint, cc);
1414
1514
  if (action === "continue")
1415
1515
  continue;
@@ -1419,6 +1519,7 @@ protocol version: ${this.protocolVersion}`;
1419
1519
  }
1420
1520
  // Continue with the application CCs for the root endpoint
1421
1521
  for (const cc of rootInterviewOrderAfterEndpoints) {
1522
+ this.driver.controllerLog.logNode(this.nodeId, `Root device interview: ${(0, core_1.getCCName)(cc)}`, "silly");
1422
1523
  const action = await interviewEndpoint(this, cc);
1423
1524
  if (action === "continue")
1424
1525
  continue;