zwave: update

This commit is contained in:
Koushik Dutta
2022-02-11 22:52:20 -08:00
parent a38a791e59
commit a882bb8e80
4 changed files with 144 additions and 72 deletions

View File

@@ -1,15 +1 @@
# Z-Wave USB Controller
## npm commands
* npm run scrypted-webpack
* npm run scrypted-deploy <ipaddress>
* npm run scrypted-debug <ipaddress>
## scrypted distribution via npm
1. Ensure package.json is set up properly for publishing on npm.
2. npm publish
## Visual Studio Code configuration
* If using a remote server, edit [.vscode/settings.json](blob/master/.vscode/settings.json) to specify the IP Address of the Scrypted server.
* Launch Scrypted Debugger from the launch menu.
# Z-Wave Plugin for Scrypted

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/zwave",
"version": "0.0.39",
"version": "0.0.41",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/zwave",
"version": "0.0.39",
"version": "0.0.41",
"license": "Apache",
"dependencies": {
"@scrypted/sdk": "file:../../sdk",
@@ -18,7 +18,7 @@
"@types/node": "^16.7.1"
},
"optionalDependencies": {
"zwave-js": "^8.11.3"
"zwave-js": "^8.11.5"
}
},
"../../sdk": {
@@ -67,10 +67,11 @@
"extraneous": true
},
"file-stream-rotator": {
"name": "@zwave-js/file-stream-rotator",
"version": "0.5.8-0",
"license": "MIT",
"dependencies": {
"moment": "^2.11.2"
"date-fns-tz": "^1.2.2"
}
},
"node_modules/@alcalzone/jsonl-db": {
@@ -420,9 +421,9 @@
"dev": true
},
"node_modules/@zwave-js/config": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/@zwave-js/config/-/config-8.11.3.tgz",
"integrity": "sha512-K5zXd5gPauWzpRqXMB1+9Xkx/wgZtnOnjWcgFV6cMiC7dzGLcQX6sCZXySMb2QFTu6J/DRrWTDWYBHACq09rVA==",
"version": "8.11.4",
"resolved": "https://registry.npmjs.org/@zwave-js/config/-/config-8.11.4.tgz",
"integrity": "sha512-BsMq2ecOXm3xGyRKvcwYX5Xn84Vuwa9nmIC1VR9k2IwfAHaG6wwhqRgjrZGoHpDnHc12YfyCKvGsvvBh38mFlg==",
"optional": true,
"dependencies": {
"@zwave-js/core": "8.11.3",
@@ -472,9 +473,9 @@
"link": true
},
"node_modules/@zwave-js/nvmedit": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/@zwave-js/nvmedit/-/nvmedit-8.11.3.tgz",
"integrity": "sha512-pa6PUkveZnHyU7DjM4yvdrwYlbWYVl/CZHp1lv90uxGSf1+TPM5ouaOVFEuWlhAiecUQgv6wcdDkx7O32WzCOA==",
"version": "8.11.5",
"resolved": "https://registry.npmjs.org/@zwave-js/nvmedit/-/nvmedit-8.11.5.tgz",
"integrity": "sha512-2dOVDGjT/ZaUcTMX92U/SUZPHbgtl1G02QxLx6GDBGNa1vZIeDTsAvcXdzr08XRl2wxjc8YrRCFxuR3ok3QiqA==",
"optional": true,
"dependencies": {
"@zwave-js/core": "8.11.3",
@@ -907,6 +908,27 @@
"node": ">= 8"
}
},
"node_modules/date-fns": {
"version": "2.28.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
"integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==",
"peer": true,
"engines": {
"node": ">=0.11"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/date-fns"
}
},
"node_modules/date-fns-tz": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.2.2.tgz",
"integrity": "sha512-vWtn44eEqnLbkACb7T5G5gPgKR4nY8NkNMOCyoY49NsRGHrcDmY2aysCyzDeA+u+vcDBn/w6nQqEDyouRs4m8w==",
"peerDependencies": {
"date-fns": ">=2.0.0"
}
},
"node_modules/dayjs": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
@@ -1378,14 +1400,6 @@
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
"optional": true
},
"node_modules/moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -2193,18 +2207,18 @@
}
},
"node_modules/zwave-js": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/zwave-js/-/zwave-js-8.11.3.tgz",
"integrity": "sha512-LKqj4j0i9MwkwhN1H+yZw4pAyuVnMcCYUpX+rSjtZkAZdsxp3n6QGac61da275t3SNBheiyzdsixrhVjxRVSiw==",
"version": "8.11.5",
"resolved": "https://registry.npmjs.org/zwave-js/-/zwave-js-8.11.5.tgz",
"integrity": "sha512-3uK7nwUec6cyC2q6U0cTCXNHDKGWnZPVwkYr2yCn3/vBHm7qB1GLHnT+k65JM3fsNV8Y6j03rlxLq/E9WatSMg==",
"optional": true,
"dependencies": {
"@alcalzone/jsonl-db": "^2.4.1",
"@alcalzone/pak": "^0.7.0",
"@sentry/integrations": "^6.17.3",
"@sentry/node": "^6.17.3",
"@zwave-js/config": "8.11.3",
"@zwave-js/config": "8.11.4",
"@zwave-js/core": "8.11.3",
"@zwave-js/nvmedit": "8.11.3",
"@zwave-js/nvmedit": "8.11.5",
"@zwave-js/serial": "8.11.3",
"@zwave-js/shared": "8.11.3",
"alcalzone-shared": "^4.0.1",
@@ -2507,9 +2521,9 @@
"dev": true
},
"@zwave-js/config": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/@zwave-js/config/-/config-8.11.3.tgz",
"integrity": "sha512-K5zXd5gPauWzpRqXMB1+9Xkx/wgZtnOnjWcgFV6cMiC7dzGLcQX6sCZXySMb2QFTu6J/DRrWTDWYBHACq09rVA==",
"version": "8.11.4",
"resolved": "https://registry.npmjs.org/@zwave-js/config/-/config-8.11.4.tgz",
"integrity": "sha512-BsMq2ecOXm3xGyRKvcwYX5Xn84Vuwa9nmIC1VR9k2IwfAHaG6wwhqRgjrZGoHpDnHc12YfyCKvGsvvBh38mFlg==",
"optional": true,
"requires": {
"@zwave-js/core": "8.11.3",
@@ -2545,13 +2559,13 @@
"@zwave-js/file-stream-rotator": {
"version": "file:file-stream-rotator",
"requires": {
"moment": "^2.11.2"
"date-fns-tz": "^1.2.2"
}
},
"@zwave-js/nvmedit": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/@zwave-js/nvmedit/-/nvmedit-8.11.3.tgz",
"integrity": "sha512-pa6PUkveZnHyU7DjM4yvdrwYlbWYVl/CZHp1lv90uxGSf1+TPM5ouaOVFEuWlhAiecUQgv6wcdDkx7O32WzCOA==",
"version": "8.11.5",
"resolved": "https://registry.npmjs.org/@zwave-js/nvmedit/-/nvmedit-8.11.5.tgz",
"integrity": "sha512-2dOVDGjT/ZaUcTMX92U/SUZPHbgtl1G02QxLx6GDBGNa1vZIeDTsAvcXdzr08XRl2wxjc8YrRCFxuR3ok3QiqA==",
"optional": true,
"requires": {
"@zwave-js/core": "8.11.3",
@@ -2884,6 +2898,18 @@
"which": "^2.0.1"
}
},
"date-fns": {
"version": "2.28.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
"integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==",
"peer": true
},
"date-fns-tz": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.2.2.tgz",
"integrity": "sha512-vWtn44eEqnLbkACb7T5G5gPgKR4nY8NkNMOCyoY49NsRGHrcDmY2aysCyzDeA+u+vcDBn/w6nQqEDyouRs4m8w==",
"requires": {}
},
"dayjs": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
@@ -3248,11 +3274,6 @@
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
"optional": true
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -3888,18 +3909,18 @@
"optional": true
},
"zwave-js": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/zwave-js/-/zwave-js-8.11.3.tgz",
"integrity": "sha512-LKqj4j0i9MwkwhN1H+yZw4pAyuVnMcCYUpX+rSjtZkAZdsxp3n6QGac61da275t3SNBheiyzdsixrhVjxRVSiw==",
"version": "8.11.5",
"resolved": "https://registry.npmjs.org/zwave-js/-/zwave-js-8.11.5.tgz",
"integrity": "sha512-3uK7nwUec6cyC2q6U0cTCXNHDKGWnZPVwkYr2yCn3/vBHm7qB1GLHnT+k65JM3fsNV8Y6j03rlxLq/E9WatSMg==",
"optional": true,
"requires": {
"@alcalzone/jsonl-db": "^2.4.1",
"@alcalzone/pak": "^0.7.0",
"@sentry/integrations": "^6.17.3",
"@sentry/node": "^6.17.3",
"@zwave-js/config": "8.11.3",
"@zwave-js/config": "8.11.4",
"@zwave-js/core": "8.11.3",
"@zwave-js/nvmedit": "8.11.3",
"@zwave-js/nvmedit": "8.11.5",
"@zwave-js/serial": "8.11.3",
"@zwave-js/shared": "8.11.3",
"alcalzone-shared": "^4.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/zwave",
"version": "0.0.39",
"version": "0.0.41",
"description": "Z-Wave USB Controller for Scrypted",
"author": "Scrypted",
"license": "Apache",
@@ -40,6 +40,6 @@
"@types/node": "^16.7.1"
},
"optionalDependencies": {
"zwave-js": "^8.11.3"
"zwave-js": "^8.11.5"
}
}

View File

@@ -4,7 +4,7 @@ import { CommandClassInfo, getCommandClass, getCommandClassIndex } from "./Comma
import { ZwaveDeviceBase } from "./CommandClasses/ZwaveDeviceBase";
import { getHash, getNodeHash, getInstanceHash } from "./Types";
import debounce from "lodash/debounce";
import { Driver, Endpoint, ZWaveController, ZWaveNode, CommandClass } from "zwave-js";
import { Driver, Endpoint, ZWaveController, ZWaveNode, CommandClass, InclusionUserCallbacks, InclusionGrant, InclusionStrategy, NodeStatus } from "zwave-js";
import { ValueID, CommandClasses } from "@zwave-js/core"
import { randomBytes } from "crypto";
import path from "path";
@@ -55,38 +55,38 @@ export class ZwaveControllerProvider extends ScryptedDeviceBase implements Devic
let s2AccessControlKey = this.storage.getItem('s2AccessControlKey');
let s2AuthenticatedKey = this.storage.getItem('s2AuthenticatedKey');
let s2UnauthenticatedKey = this.storage.getItem('s2UnauthenticatedKey');
// 1/17/2022: the network key was stored as base64, but for consistency with HA
// and others, it was switched to hex. this is the data migration.
if (!isHex(networkKey) && networkKey) {
networkKey = Buffer.from(networkKey, 'base64').toString('hex');
this.storage.setItem('networkKey', networkKey);
}
if (!networkKey) {
networkKey = randomBytes(16).toString('hex').toUpperCase();
this.storage.setItem('networkKey', networkKey);
this.log.a('No Network Key was present, so a random one was generated. You can change the Network Key in Settings.')
}
if (!s2AccessControlKey) {
s2AccessControlKey = randomBytes(16).toString('hex').toUpperCase();
this.storage.setItem('s2AccessControlKey', s2AccessControlKey);
this.log.a('No S2 Access Control Key was present, so a random one was generated. You can change the S2 Access Control Key in Settings.');
}
if (!s2AuthenticatedKey) {
s2AuthenticatedKey = randomBytes(16).toString('hex').toUpperCase();
this.storage.setItem('s2AuthenticatedKey', s2AuthenticatedKey);
this.log.a('No S2 Authenticated Key was present, so a random one was generated. You can change the S2 Access Control Key in Settings.');
this.log.a('No S2 Authenticated Key was present, so a random one was generated. You can change the S2 Access Control Key in Settings.');
}
if (!s2UnauthenticatedKey) {
s2UnauthenticatedKey = randomBytes(16).toString('hex').toUpperCase();
this.storage.setItem('s2UnauthenticatedKey', s2UnauthenticatedKey);
this.log.a('No S2 Unauthenticated Key was present, so a random one was generated. You can change the S2 Unauthenticated Key in Settings.')
}
const cacheDir = path.join(process.env['SCRYPTED_PLUGIN_VOLUME'], 'cache');
this.console.log(process.cwd());
@@ -103,12 +103,12 @@ export class ZwaveControllerProvider extends ScryptedDeviceBase implements Devic
});
this.driver = driver;
console.log(driver.cacheDir);
driver.on("error", (e) => {
console.error('driver error', e);
reject(e);
});
driver.once("driver ready", () => {
this.controller = driver.controller;
const rebuildNode = async (node: ZWaveNode) => {
@@ -116,10 +116,15 @@ export class ZwaveControllerProvider extends ScryptedDeviceBase implements Devic
await this.rebuildInstance(endpoint);
}
}
const bindNode = (node: ZWaveNode) => {
node.on('value added', node => rebuildNode(node));
node.on('value removed', node => rebuildNode(node));
node.on('value removed', node => {
// node is being removed
if (!this.controller.nodes.get(node.id))
return;
rebuildNode(node);
});
node.on('value updated', (node, valueId) => {
const dirtyKey = getInstanceHash(this.controller.homeId, node.id, valueId.endpoint);
const device: ZwaveDeviceBase = this.devices[dirtyKey];
@@ -131,7 +136,7 @@ export class ZwaveControllerProvider extends ScryptedDeviceBase implements Devic
});
node.on('interview completed', node => rebuildNode(node));
}
this.controller.on('node added', node => {
this.console.log('node added', node.nodeId);
bindNode(node);
@@ -140,7 +145,7 @@ export class ZwaveControllerProvider extends ScryptedDeviceBase implements Devic
this.controller.on('node removed', node => {
this.console.log('node removed', node?.nodeId);
})
driver.controller.nodes.forEach(node => {
this.console.log('node loaded', node.nodeId);
bindNode(node);
@@ -191,10 +196,67 @@ export class ZwaveControllerProvider extends ScryptedDeviceBase implements Devic
key: 's2UnauthenticatedKey',
value: this.storage.getItem('s2UnauthenticatedKey'),
description: 'The 16 byte hex encoded S2 Unauthenticated Key',
}
},
{
title: 'Include Device',
key: 'inclusion',
type: 'button',
description: 'Enter inclusion mode and add devices.',
},
{
title: 'Exclude Device',
key: 'exclusion',
type: 'button',
description: 'Enter exclusion mode and remove devices.',
},
]
}
async inclusion() {
const userCallbacks: InclusionUserCallbacks = {
grantSecurityClasses: async (requested: InclusionGrant): Promise<false | InclusionGrant> => {
this.console.log('grantSecurityClasses');
return requested;
},
validateDSKAndEnterPIN: async (dsk: string) => {
this.console.error('validateDSKAndEnterPIN not implemented in zwave plugin');
throw new Error("validateDSKAndEnterPIN Function not implemented.");
},
abort: function (): void {
this.console.log('abort');
}
}
await this.controller.stopExclusion();
await this.controller.stopInclusion();
const including = await this.driver.controller.beginInclusion({
userCallbacks,
strategy: InclusionStrategy.Default,
});
this.log.a('Including devices for 5 minutes.');
this.console.log('including', including);
setTimeout(() => this.driver.controller.stopInclusion(), 300000);
}
async exclusion() {
await this.controller.stopExclusion();
await this.controller.stopInclusion();
const excluding = await this.driver.controller.beginExclusion();
this.log.a('Excluding devices for 5 minutes.');
this.console.log('excluding', excluding);
setTimeout(() => this.driver.controller.stopExclusion(), 300000);
}
async putSetting(key: string, value: string | number | boolean) {
if (key === 'inclusion') {
this.inclusion();
return;
}
if (key === 'exclusion') {
this.exclusion();
return;
}
this.storage.setItem(key, value as string);
await this.driver?.destroy();
@@ -229,6 +291,9 @@ export class ZwaveControllerProvider extends ScryptedDeviceBase implements Devic
async rebuildInstance(instance: Endpoint) {
const nativeId = getHash(this.controller, instance);
let scryptedDevice: ZwaveDeviceBase = this.devices[nativeId];
if (this.controller.nodes.get(instance.nodeId).status === NodeStatus.Dead) {
scryptedDevice.log.a('Node is dead.');
}
if (!scryptedDevice) {
scryptedDevice = new ZwaveDeviceBase(this.controller, instance);
scryptedDevice.zwaveController = this;