mirror of
https://github.com/koush/scrypted.git
synced 2026-02-03 14:13:28 +00:00
zwave: additional menu options (#240)
* zwave: add node info fields * zwave: add controller info fields * zwave: maintain statistics, log sleep/wake
This commit is contained in:
@@ -2,7 +2,7 @@ import sdk, { ScryptedDeviceBase, Device, Refresh, Setting, Settings } from "@sc
|
||||
import { getHash } from "../Types";
|
||||
import { CommandClassInfo, getCommandClassIndex, getCommandClass } from ".";
|
||||
import { ZwaveControllerProvider, NodeLiveness } from "../main";
|
||||
import { Endpoint, ValueID, ZWaveController, ZWaveNode, ZWaveNodeValueUpdatedArgs } from "zwave-js";
|
||||
import { Endpoint, ValueID, ZWaveController, ZWaveNode, ZWaveNodeValueUpdatedArgs, NodeStatus, InterviewStage, ZWavePlusRoleType, NodeStatistics } from "zwave-js";
|
||||
import { CommandClasses, ValueMetadataNumeric } from "@zwave-js/core"
|
||||
|
||||
const { deviceManager } = sdk;
|
||||
@@ -36,10 +36,22 @@ export class ZwaveDeviceBase extends ScryptedDeviceBase implements Refresh, Sett
|
||||
commandClasses: CommandClassInfo[] = [];
|
||||
zwaveController: ZwaveControllerProvider;
|
||||
transientState: TransientState = {};
|
||||
statistics: NodeStatistics;
|
||||
|
||||
constructor(controller: ZWaveController, instance: Endpoint) {
|
||||
super(getHash(controller, instance));
|
||||
this.instance = instance;
|
||||
|
||||
const node = this.instance.getNodeUnsafe()
|
||||
node.on('wake up', node => {
|
||||
this.console.log(`[${node.id}] woke up`)
|
||||
});
|
||||
node.on('sleep', node => {
|
||||
this.console.log(`[${node.id}] sleeping`)
|
||||
});
|
||||
node.on('statistics updated', (node: ZWaveNode, statistics: NodeStatistics) => {
|
||||
this.statistics = statistics;
|
||||
});
|
||||
}
|
||||
|
||||
getValueId(valueId: ValueID): ValueID {
|
||||
@@ -110,13 +122,91 @@ export class ZwaveDeviceBase extends ScryptedDeviceBase implements Refresh, Sett
|
||||
return this.putZWaveSetting(key, value);
|
||||
}
|
||||
async getZWaveSettings(): Promise<Setting[]> {
|
||||
const node = this.instance.getNodeUnsafe();
|
||||
return [
|
||||
{
|
||||
group: 'Z-Wave Node Management',
|
||||
group: 'Info',
|
||||
title: 'Device Info',
|
||||
key: 'zwave:deviceInfo',
|
||||
readonly: true,
|
||||
value: node.deviceDatabaseUrl,
|
||||
},
|
||||
{
|
||||
group: 'Info',
|
||||
title: 'ID',
|
||||
key: 'zwave:nodeId',
|
||||
readonly: true,
|
||||
value: node.id,
|
||||
},
|
||||
{
|
||||
group: 'Info',
|
||||
title: 'Status',
|
||||
key: 'zwave:nodeStatus',
|
||||
readonly: true,
|
||||
value: NodeStatus[node.status],
|
||||
},
|
||||
{
|
||||
group: 'Info',
|
||||
title: 'Interview Stage',
|
||||
key: 'zwave:interviewStage',
|
||||
readonly: true,
|
||||
value: InterviewStage[node.interviewStage],
|
||||
},
|
||||
{
|
||||
group: 'Info',
|
||||
title: 'Device Class',
|
||||
key: 'zwave:deviceClass',
|
||||
readonly: true,
|
||||
value: node.deviceClass.specific.label,
|
||||
},
|
||||
{
|
||||
group: 'Info',
|
||||
title: 'ZWave+ Role Type',
|
||||
key: 'zwave:roleType',
|
||||
readonly: true,
|
||||
value: ZWavePlusRoleType[node.zwavePlusRoleType] || "n/a",
|
||||
},
|
||||
{
|
||||
group: 'Info',
|
||||
title: 'Firmware',
|
||||
key: 'zwave:firmware',
|
||||
readonly: true,
|
||||
value: node.firmwareVersion,
|
||||
},
|
||||
{
|
||||
group: 'Info',
|
||||
title: 'Force Remove Node',
|
||||
key: 'zwave:forceRemove',
|
||||
placeholder: `Confirm Node ID to remove: ${this.instance.nodeId}`,
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
group: 'Info',
|
||||
title: 'Refresh Info',
|
||||
key: 'zwave:refreshInfo',
|
||||
type: 'button',
|
||||
description: 'Resets (almost) all information about this node and forces a fresh interview.'
|
||||
},
|
||||
{
|
||||
group: 'Statistics',
|
||||
title: 'Commands (RX/TX)',
|
||||
key: 'zwave:commands',
|
||||
readonly: true,
|
||||
value: this.statistics ? `${this.statistics.commandsRX} / ${this.statistics.commandsTX}` : 'n/a',
|
||||
},
|
||||
{
|
||||
group: 'Statistics',
|
||||
title: 'Commands Dropped (RX/TX)',
|
||||
key: 'zwave:commandsDropped',
|
||||
readonly: true,
|
||||
value: this.statistics ? `${this.statistics.commandsDroppedRX} / ${this.statistics.commandsDroppedTX}` : 'n/a',
|
||||
},
|
||||
{
|
||||
group: 'Statistics',
|
||||
title: 'RTT',
|
||||
key: 'zwave:rtt',
|
||||
readonly: true,
|
||||
value: this.statistics ? `${this.statistics.rtt}ms` : 'n/a',
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -126,5 +216,15 @@ export class ZwaveDeviceBase extends ScryptedDeviceBase implements Refresh, Sett
|
||||
this.zwaveController.controller.removeFailedNode(this.instance.nodeId);
|
||||
deviceManager.onDeviceRemoved(this.nativeId);
|
||||
}
|
||||
if (key === 'zwave:refreshInfo') {
|
||||
this.console.log(`[${this.name}] Refreshing Info`)
|
||||
if (!this.instance.getNodeUnsafe().ready) {
|
||||
this.console.log(`${this.name} Refresh failed, device not ready`);
|
||||
return
|
||||
}
|
||||
this.instance.getNodeUnsafe().refreshInfo().then((r) => {
|
||||
this.console.log(`[${this.name}] Refresh Completed`)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, InclusionUserCallbacks, InclusionGrant, InclusionStrategy, NodeStatus } from "zwave-js";
|
||||
import { Driver, Endpoint, ZWaveController, ZWaveNode, InclusionUserCallbacks, InclusionGrant, InclusionStrategy, NodeStatus, InclusionState } from "zwave-js";
|
||||
import { ValueID, CommandClasses } from "@zwave-js/core"
|
||||
import { randomBytes } from "crypto";
|
||||
import path from "path";
|
||||
@@ -169,6 +169,48 @@ export class ZwaveControllerProvider extends ScryptedDeviceBase implements Devic
|
||||
async getSettings(): Promise<Setting[]> {
|
||||
return [
|
||||
{
|
||||
group: 'Inclusion',
|
||||
title: 'Inclusion State',
|
||||
key: 'inclusionState',
|
||||
readonly: true,
|
||||
value: InclusionState[this.controller.inclusionState],
|
||||
},
|
||||
{
|
||||
group: 'Inclusion',
|
||||
title: 'Exclude Device',
|
||||
key: 'exclusion',
|
||||
type: 'button',
|
||||
description: 'Enter exclusion mode and remove devices.',
|
||||
},
|
||||
{
|
||||
group: 'Inclusion',
|
||||
title: 'Include Device',
|
||||
key: 'inclusion',
|
||||
type: 'button',
|
||||
description: 'Enter inclusion mode and add devices.',
|
||||
},
|
||||
{
|
||||
group: 'Inclusion',
|
||||
key: 'confirmPin',
|
||||
title: 'Confirm PIN',
|
||||
description: 'Some devices will require confirmation of a PIN while including them. Enter the PIN here when prompted.',
|
||||
},
|
||||
{
|
||||
group: 'Network',
|
||||
title: 'Healing State',
|
||||
key: 'healingState',
|
||||
readonly: true,
|
||||
value: this.controller.isHealNetworkActive ? 'Healing' : 'Not Healing',
|
||||
},
|
||||
{
|
||||
group: 'Network',
|
||||
title: 'Heal Network',
|
||||
key: 'heal',
|
||||
type: 'button',
|
||||
description: 'Heal the Z-Wave Network. This operation may take a long time and the network may become unreponsive while in progress.',
|
||||
},
|
||||
{
|
||||
group: 'Adapter',
|
||||
title: 'Serial Port',
|
||||
key: 'serialPort',
|
||||
value: this.storage.getItem('serialPort'),
|
||||
@@ -176,52 +218,33 @@ export class ZwaveControllerProvider extends ScryptedDeviceBase implements Devic
|
||||
placeholder: '/dev/tty.usbmodem14501',
|
||||
},
|
||||
{
|
||||
group: 'Adapter',
|
||||
title: 'Network Key',
|
||||
key: 'networkKey',
|
||||
value: this.storage.getItem('networkKey'),
|
||||
description: 'The 16 byte hex encoded Network Security Key',
|
||||
},
|
||||
{
|
||||
group: 'Adapter',
|
||||
title: 'S2 Access Control Key',
|
||||
key: 's2AccessControlKey',
|
||||
value: this.storage.getItem('s2AccessControlKey'),
|
||||
description: 'The 16 byte hex encoded S2 Access Control Key',
|
||||
},
|
||||
{
|
||||
group: 'Adapter',
|
||||
title: 'S2 Authenticated Key',
|
||||
key: 's2AuthenticatedKey',
|
||||
value: this.storage.getItem('s2AuthenticatedKey'),
|
||||
description: 'The 16 byte hex encoded S2 Authenticated Key',
|
||||
},
|
||||
{
|
||||
group: 'Adapter',
|
||||
title: 'S2 Unauthenticated Key',
|
||||
key: 's2UnauthenticatedKey',
|
||||
value: this.storage.getItem('s2UnauthenticatedKey'),
|
||||
description: 'The 16 byte hex encoded S2 Unauthenticated Key',
|
||||
},
|
||||
{
|
||||
title: 'Heal Network',
|
||||
key: 'heal',
|
||||
type: 'button',
|
||||
description: 'Heal the Z-Wave Network. This operation may take a long time and the network may become unreponsive while in progress.',
|
||||
},
|
||||
{
|
||||
title: 'Exclude Device',
|
||||
key: 'exclusion',
|
||||
type: 'button',
|
||||
description: 'Enter exclusion mode and remove devices.',
|
||||
},
|
||||
{
|
||||
title: 'Include Device',
|
||||
key: 'inclusion',
|
||||
type: 'button',
|
||||
description: 'Enter inclusion mode and add devices.',
|
||||
},
|
||||
{
|
||||
key: 'confirmPin',
|
||||
title: 'Confirm PIN',
|
||||
description: 'Some devices will require confirmation of a PIN while including them. Enter the PIN here when prompted.',
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user