From 445a88737d38a4a908cfca8ce50753c2a6a5e37e Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 13 Jun 2024 22:52:39 +0200 Subject: [PATCH] refactor: move device classes from config into code --- docs/api/config-manager.md | 26 - packages/cc/src/cc/MultiChannelCC.ts | 66 +- packages/config/config/deviceClasses.json | 447 -------------- packages/config/src/ConfigManager.ts | 190 ------ packages/config/src/DeviceClasses.ts | 213 ------- packages/config/src/index.ts | 1 - packages/config/src/index_safe.ts | 1 - packages/core/src/capabilities/NodeInfo.ts | 5 +- packages/core/src/index.ts | 1 + packages/core/src/index_safe.ts | 1 + packages/core/src/registries/DeviceClasses.ts | 564 ++++++++++++++++++ .../zwave-js/src/lib/controller/Controller.ts | 40 +- packages/zwave-js/src/lib/driver/Driver.ts | 7 +- .../zwave-js/src/lib/driver/NetworkCache.ts | 16 +- packages/zwave-js/src/lib/node/DeviceClass.ts | 26 +- packages/zwave-js/src/lib/node/Endpoint.ts | 10 +- packages/zwave-js/src/lib/node/Node.ts | 9 +- .../network-mgmt/AddNodeToNetworkRequest.ts | 3 +- 18 files changed, 660 insertions(+), 966 deletions(-) delete mode 100644 packages/config/config/deviceClasses.json delete mode 100644 packages/config/src/DeviceClasses.ts create mode 100644 packages/core/src/registries/DeviceClasses.ts diff --git a/docs/api/config-manager.md b/docs/api/config-manager.md index 23638c75b736..b84299101633 100644 --- a/docs/api/config-manager.md +++ b/docs/api/config-manager.md @@ -55,32 +55,6 @@ Looks up the property definition for a given indicator property id > [!NOTE] `loadIndicators` must be used first. -### `loadDeviceClasses` - -```ts -loadDeviceClasses(): Promise; -``` - -Loads the device classes config which is used to lookup basic, generic and specific device classes. - -### `lookupBasicDeviceClass` - -```ts -lookupBasicDeviceClass(basic: number): BasicDeviceClass; -``` - -### `lookupGenericDeviceClass` - -```ts -lookupGenericDeviceClass(generic: number): GenericDeviceClass; -``` - -### `lookupSpecificDeviceClass` - -```ts -lookupSpecificDeviceClass(generic: number, specific: number): SpecificDeviceClass; -``` - ### `loadDeviceIndex` ```ts diff --git a/packages/cc/src/cc/MultiChannelCC.ts b/packages/cc/src/cc/MultiChannelCC.ts index 6e5cbea47e63..2af8b2aaf408 100644 --- a/packages/cc/src/cc/MultiChannelCC.ts +++ b/packages/cc/src/cc/MultiChannelCC.ts @@ -1,17 +1,20 @@ -import type { GenericDeviceClass, SpecificDeviceClass } from "@zwave-js/config"; import { type ApplicationNodeInformation, CommandClasses, + type GenericDeviceClass, type IZWaveNode, type MaybeNotKnown, type MessageOrCCLogEntry, MessagePriority, type MessageRecord, + type SpecificDeviceClass, ZWaveError, ZWaveErrorCodes, encodeApplicationNodeInformation, encodeBitMask, getCCName, + getGenericDeviceClass, + getSpecificDeviceClass, parseApplicationNodeInformation, parseBitMask, validatePayload, @@ -248,15 +251,13 @@ export class MultiChannelCCAPI extends CCAPI { this.commandOptions, ); if (response) { - const generic = this.applHost.configManager - .lookupGenericDeviceClass( - response.genericDeviceClass, - ); - const specific = this.applHost.configManager - .lookupSpecificDeviceClass( - response.genericDeviceClass, - response.specificDeviceClass, - ); + const generic = getGenericDeviceClass( + response.genericDeviceClass, + ); + const specific = getSpecificDeviceClass( + response.genericDeviceClass, + response.specificDeviceClass, + ); return { isDynamic: response.isDynamic, wasRemoved: response.wasRemoved, @@ -968,19 +969,17 @@ export class MultiChannelCCCapabilityReport extends MultiChannelCC } public toLogEntry(applHost: ZWaveApplicationHost): MessageOrCCLogEntry { - const generic = applHost.configManager.lookupGenericDeviceClass( - this.genericDeviceClass, - ); - const specific = applHost.configManager.lookupSpecificDeviceClass( - this.genericDeviceClass, - this.specificDeviceClass, - ); return { ...super.toLogEntry(applHost), message: { "endpoint index": this.endpointIndex, - "generic device class": generic.label, - "specific device class": specific.label, + "generic device class": getGenericDeviceClass( + this.genericDeviceClass, + ).label, + "specific device class": getSpecificDeviceClass( + this.genericDeviceClass, + this.specificDeviceClass, + ).label, "is dynamic end point": this.isDynamic, "supported CCs": this.supportedCCs .map((cc) => `\n· ${getCCName(cc)}`) @@ -1120,15 +1119,13 @@ export class MultiChannelCCEndPointFindReport extends MultiChannelCC { return { ...super.toLogEntry(applHost), message: { - "generic device class": - applHost.configManager.lookupGenericDeviceClass( - this.genericClass, - ).label, - "specific device class": - applHost.configManager.lookupSpecificDeviceClass( - this.genericClass, - this.specificClass, - ).label, + "generic device class": getGenericDeviceClass( + this.genericClass, + ).label, + "specific device class": getSpecificDeviceClass( + this.genericClass, + this.specificClass, + ).label, "found endpoints": this.foundEndpoints.join(", "), "# of reports to follow": this.reportsToFollow, }, @@ -1175,14 +1172,11 @@ export class MultiChannelCCEndPointFind extends MultiChannelCC { ...super.toLogEntry(applHost), message: { "generic device class": - applHost.configManager.lookupGenericDeviceClass( - this.genericClass, - ).label, - "specific device class": - applHost.configManager.lookupSpecificDeviceClass( - this.genericClass, - this.specificClass, - ).label, + getGenericDeviceClass(this.genericClass).label, + "specific device class": getSpecificDeviceClass( + this.genericClass, + this.specificClass, + ).label, }, }; } diff --git a/packages/config/config/deviceClasses.json b/packages/config/config/deviceClasses.json deleted file mode 100644 index 36268c559271..000000000000 --- a/packages/config/config/deviceClasses.json +++ /dev/null @@ -1,447 +0,0 @@ -// This file defines the basic, generic and specific device classes of the -// legacy Z-Wave standard (SDS10242) -{ - "basic": { - // The GetNodeProtocolInfo messages rely on these values - double-check there when changing anything - "0x01": "Controller", - "0x02": "Static Controller", - "0x03": "Slave", - "0x04": "Routing Slave" - }, - "generic": { - "0x01": { - "label": "Remote Controller", - "maySupportBasicCC": false, - "specific": { - "0x01": { - "label": "Portable Remote Controller", - "zwavePlusDeviceType": "Remote Control - Multipurpose" - }, - "0x02": { - "label": "Portable Scene Controller" - }, - "0x03": { - "label": "Portable Installer Tool" - }, - "0x04": { - "label": "AV Remote Control", - "zwavePlusDeviceType": "Remote Control - AV" - }, - "0x06": { - "label": "Simple Remote Control", - "zwavePlusDeviceType": "Remote Control - Simple" - } - } - }, - "0x02": { - "label": "Static Controller", - "zwavePlusDeviceType": "Gateway", - "specific": { - "0x01": { - "label": "PC Controller", - "zwavePlusDeviceType": "Central Controller" - }, - "0x02": { - "label": "Scene Controller" - }, - "0x03": { - "label": "Static Installer Tool" - }, - "0x04": { - "label": "Set Top Box", - "zwavePlusDeviceType": "Set Top Box" - }, - "0x05": { - "label": "Sub System Controller", - "zwavePlusDeviceType": "Sub System Controller" - }, - "0x06": { - "label": "TV", - "zwavePlusDeviceType": "TV" - }, - "0x07": { - "label": "Gateway", - "zwavePlusDeviceType": "Gateway", - "maySupportBasicCC": false - } - } - }, - "0x03": { - "label": "AV Control Point", - "zwavePlusDeviceType": "AV Control Point", - "specific": { - "0x01": { - "label": "Sound Switch", - "zwavePlusDeviceType": "Sound Switch" - }, - "0x04": { - "label": "Satellite Receiver" - }, - "0x11": { - "label": "Satellite Receiver V2" - }, - "0x12": { - "label": "Doorbell" - } - } - }, - "0x04": { - "label": "Display", - "specific": { - "0x01": { - "label": "Simple Display", - "zwavePlusDeviceType": "Display - Simple" - } - } - }, - "0x05": { - "label": "Network Extender", - "specific": { - "0x01": { - "label": "Secure Extender" - } - } - }, - "0x06": { - "label": "Appliance", - "specific": { - "0x01": { - "label": "General Appliance" - }, - "0x02": { - "label": "Kitchen Appliance" - }, - "0x03": { - "label": "Laundry Appliance" - } - } - }, - "0x07": { - "label": "Notification Sensor", - "specific": { - "0x01": { - "label": "Notification Sensor", - "zwavePlusDeviceType": "Sensor - Notification", - "maySupportBasicCC": false - } - } - }, - "0x08": { - "label": "Thermostat", - "specific": { - "0x01": { - "label": "Heating Thermostat" - }, - "0x02": { - "label": "General Thermostat" - }, - "0x03": { - "label": "Setback Schedule Thermostat" - }, - "0x04": { - "label": "Setpoint Thermostat" - }, - "0x05": { - "label": "Setback Thermostat", - "zwavePlusDeviceType": "Thermostat - Setback" - }, - "0x06": { - "label": "General Thermostat V2", - "zwavePlusDeviceType": "Thermostat - HVAC" - } - } - }, - "0x09": { - "label": "Window Covering", - "specific": { - "0x01": { - "label": "Simple Window Covering Control" - } - } - }, - "0x0f": { - "label": "Repeater Slave", - "specific": { - "0x01": { - "label": "Repeater Slave", - "zwavePlusDeviceType": "Repeater", - "maySupportBasicCC": false - }, - "0x03": { - "label": "IR Repeater", - "zwavePlusDeviceType": "IR Repeater", - "maySupportBasicCC": false - } - } - }, - "0x10": { - "label": "Binary Switch", - "specific": { - "0x01": { - "label": "Binary Power Switch", - "zwavePlusDeviceType": "On/Off Power Switch" - }, - "0x02": { - "label": "Tunable Color Switch", - "zwavePlusDeviceType": "Color Switch" - }, - "0x03": { - "label": "Binary Scene Switch" - }, - "0x04": { - "label": "Power Strip Switch", - "zwavePlusDeviceType": "Power Strip" - }, - "0x05": { - "label": "Siren", - "zwavePlusDeviceType": "Siren" - }, - "0x06": { - "label": "Valve", - "zwavePlusDeviceType": "Valve (open/close)" - }, - "0x07": { - "label": "Irrigation Control", - "zwavePlusDeviceType": "Irrigation Control" - } - } - }, - "0x11": { - "label": "Multilevel Switch", - "specific": { - "0x01": { - "label": "Multilevel Power Switch", - "zwavePlusDeviceType": "Light Dimmer Switch" - }, - "0x02": { - "label": "Tunable Color Switch", - "zwavePlusDeviceType": "Color Switch" - }, - "0x03": { - "label": "Multiposition Motor" - }, - "0x04": { - "label": "Multilevel Scene Switch" - }, - "0x05": { - "label": "Motor Control Class A", - "zwavePlusDeviceType": "Window Covering - No Position/Endpoint" - }, - "0x06": { - "label": "Motor Control Class B", - "zwavePlusDeviceType": "Window Covering - Endpoint Aware" - }, - "0x07": { - "label": "Motor Control Class C", - "zwavePlusDeviceType": "Window Covering - Position/Endpoint Aware" - }, - "0x08": { - "label": "Fan Switch", - "zwavePlusDeviceType": "Fan Switch" - } - } - }, - "0x12": { - "label": "Remote Switch", - "specific": { - "0x01": { - "label": "Binary Remote Switch" - }, - "0x02": { - "label": "Multilevel Remote Switch" - }, - "0x03": { - "label": "Binary Toggle Remote Switch" - }, - "0x04": { - "label": "Multilevel Toggle Remote Switch" - } - } - }, - "0x13": { - "label": "Toggle Switch", - "specific": { - "0x01": { - "label": "Binary Toggle Switch" - }, - "0x02": { - "label": "Multilevel Toggle Switch" - } - } - }, - "0x15": { - "label": "Z/IP Node", - "specific": { - "0x01": { - "label": "Z/IP TUN Node" - }, - "0x02": { - "label": "Z/IP ADV Node" - } - } - }, - "0x16": { - "label": "Ventilation", - "specific": { - "0x01": { - "label": "Residential Heat Recovery Ventilation" - } - } - }, - "0x17": { - "label": "Security Panel", - "specific": { - "0x01": { - "label": "Zoned Security Panel" - } - } - }, - "0x18": { - "label": "Wall Controller", - "specific": { - "0x01": { - "label": "Basic Wall Controller", - "zwavePlusDeviceType": "Wall Controller", - "maySupportBasicCC": false - } - } - }, - "0x20": { - "label": "Binary Sensor", - "specific": { - "0x01": { - "label": "Routing Binary Sensor" - } - } - }, - "0x21": { - "label": "Multilevel Sensor", - "specific": { - "0x01": { - "label": "Routing Multilevel Sensor", - "zwavePlusDeviceType": "Sensor - Multilevel", - "maySupportBasicCC": false - } - } - }, - "0x30": { - "label": "Pulse Meter" - }, - "0x31": { - "label": "Meter", - "maySupportBasicCC": false, - "specific": { - "0x01": { - "label": "Simple Meter", - "zwavePlusDeviceType": "Sub Energy Meter" - }, - "0x02": { - "label": "Advanced Energy Control" - }, - "0x03": { - "label": "Simple Whole Home Meter", - "zwavePlusDeviceType": "Whole Home Meter - Simple" - } - } - }, - "0x40": { - "label": "Entry Control", - "specific": { - "0x01": { - "label": "Door Lock" - }, - "0x02": { - "label": "Advanced Door Lock" - }, - "0x03": { - "label": "Secure Keypad Door Lock", - "zwavePlusDeviceType": "Door Lock - Keypad", - "requiresSecurity": true - }, - "0x05": { - "label": "Secure Door", - "zwavePlusDeviceType": "Motorized Barrier - GDO", - "requiresSecurity": true - }, - "0x06": { - "label": "Secure Gate", - "zwavePlusDeviceType": "Motorized Barrier - Gate", - "requiresSecurity": true - }, - "0x07": { - "label": "Secure Barrier Add-on", - "zwavePlusDeviceType": "Motorized Barrier - Add-on", - "requiresSecurity": true - }, - "0x08": { - "label": "Secure Barrier Open only", - "zwavePlusDeviceType": "Motorized Barrier - Open only", - "requiresSecurity": true - }, - "0x09": { - "label": "Secure Barrier Close only", - "zwavePlusDeviceType": "Motorized Barrier - Close only", - "requiresSecurity": true - }, - "0x0a": { - "label": "Lockbox", - "zwavePlusDeviceType": "Lockbox", - "requiresSecurity": true - }, - "0x0b": { - "label": "Secure Keypad", - "zwavePlusDeviceType": "Entry Control Keypad", - "requiresSecurity": true, - "maySupportBasicCC": false - } - } - }, - "0x50": { - "label": "Semi-Interoperable", - "specific": { - "0x01": { - "label": "Energy Production" - } - } - }, - "0xa1": { - "label": "Alarm Sensor", - "specific": { - "0x01": { - "label": "Basic Routing Alarm Sensor" - }, - "0x02": { - "label": "Routing Alarm Sensor" - }, - "0x03": { - "label": "Basic Zensor Net Alarm Sensor" - }, - "0x04": { - "label": "Zensor Net Alarm Sensor" - }, - "0x05": { - "label": "Advanced Zensor Net Alarm Sensor" - }, - "0x06": { - "label": "Basic Routing Smoke Sensor" - }, - "0x07": { - "label": "Routing Smoke Sensor" - }, - "0x08": { - "label": "Basic Zensor Net Smoke Sensor" - }, - "0x09": { - "label": "Zensor Net Smoke Sensor" - }, - "0x0a": { - "label": "Advanced Zensor Net Smoke Sensor" - } - } - }, - "0xff": { - "label": "Non-Interoperable", - "specific": {} - } - } -} diff --git a/packages/config/src/ConfigManager.ts b/packages/config/src/ConfigManager.ts index d630138d18ba..f076319c1814 100644 --- a/packages/config/src/ConfigManager.ts +++ b/packages/config/src/ConfigManager.ts @@ -9,15 +9,6 @@ import { isObject } from "alcalzone-shared/typeguards"; import { pathExists, readFile } from "fs-extra"; import JSON5 from "json5"; import path from "node:path"; -import { - type BasicDeviceClass, - type BasicDeviceClassMap, - GenericDeviceClass, - type GenericDeviceClassMap, - type SpecificDeviceClass, - getDefaultGenericDeviceClass, - getDefaultSpecificDeviceClass, -} from "./DeviceClasses"; import { type IndicatorMap, type IndicatorPropertiesMap, @@ -105,28 +96,6 @@ export class ConfigManager { return this._manufacturers; } - private _basicDeviceClasses: BasicDeviceClassMap | undefined; - public get basicDeviceClasses(): BasicDeviceClassMap { - if (!this._basicDeviceClasses) { - throw new ZWaveError( - "The config has not been loaded yet!", - ZWaveErrorCodes.Driver_NotReady, - ); - } - return this._basicDeviceClasses; - } - - private _genericDeviceClasses: GenericDeviceClassMap | undefined; - public get genericDeviceClasses(): GenericDeviceClassMap { - if (!this._genericDeviceClasses) { - throw new ZWaveError( - "The config has not been loaded yet!", - ZWaveErrorCodes.Driver_NotReady, - ); - } - return this._genericDeviceClasses; - } - private deviceConfigPriorityDir: string | undefined; private index: DeviceConfigIndex | undefined; private fulltextIndex: FulltextDeviceConfigIndex | undefined; @@ -163,7 +132,6 @@ export class ConfigManager { } this.logger.print(`version ${this._configVersion}`, "info"); - await this.loadDeviceClasses(); await this.loadManufacturers(); await this.loadDeviceIndex(); await this.loadNotifications(); @@ -292,75 +260,6 @@ export class ConfigManager { return this._indicatorProperties.get(propertyId); } - public async loadDeviceClasses(): Promise { - try { - const config = await loadDeviceClassesInternal( - this._useExternalConfig, - ); - this._basicDeviceClasses = config.basicDeviceClasses; - this._genericDeviceClasses = config.genericDeviceClasses; - } catch (e) { - // If the config file is missing or invalid, don't try to find it again - if (isZWaveError(e) && e.code === ZWaveErrorCodes.Config_Invalid) { - if (process.env.NODE_ENV !== "test") { - this.logger.print( - `Could not load scales config: ${e.message}`, - "error", - ); - } - if (!this._basicDeviceClasses) { - this._basicDeviceClasses = new Map(); - } - if (!this._genericDeviceClasses) { - this._genericDeviceClasses = new Map(); - } - } else { - // This is an unexpected error - throw e; - } - } - } - - public lookupBasicDeviceClass(basic: number): BasicDeviceClass { - if (!this._basicDeviceClasses) { - throw new ZWaveError( - "The config has not been loaded yet!", - ZWaveErrorCodes.Driver_NotReady, - ); - } - - return { - key: basic, - label: this._basicDeviceClasses.get(basic) - ?? `UNKNOWN (${num2hex(basic)})`, - }; - } - - public lookupGenericDeviceClass(generic: number): GenericDeviceClass { - if (!this._genericDeviceClasses) { - throw new ZWaveError( - "The config has not been loaded yet!", - ZWaveErrorCodes.Driver_NotReady, - ); - } - - return ( - this._genericDeviceClasses.get(generic) - ?? getDefaultGenericDeviceClass(generic) - ); - } - - public lookupSpecificDeviceClass( - generic: number, - specific: number, - ): SpecificDeviceClass { - const genericClass = this.lookupGenericDeviceClass(generic); - return ( - genericClass.specific.get(specific) - ?? getDefaultSpecificDeviceClass(genericClass, specific) - ); - } - public async loadDeviceIndex(): Promise { try { // The index of config files included in this package @@ -580,95 +479,6 @@ export class ConfigManager { } } -/** @internal */ -export async function loadDeviceClassesInternal( - externalConfig?: boolean, -): Promise<{ - basicDeviceClasses: BasicDeviceClassMap; - genericDeviceClasses: GenericDeviceClassMap; -}> { - const configPath = path.join( - (externalConfig && externalConfigDir()) || configDir, - "deviceClasses.json", - ); - - if (!(await pathExists(configPath))) { - throw new ZWaveError( - "The device classes config file does not exist!", - ZWaveErrorCodes.Config_Invalid, - ); - } - - try { - const fileContents = await readFile(configPath, "utf8"); - const definition = JSON5.parse(fileContents); - if (!isObject(definition)) { - throwInvalidConfig( - "device classes", - `the dictionary is not an object`, - ); - } - - if (!isObject(definition.basic)) { - throwInvalidConfig( - "device classes", - `The "basic" property is not an object`, - ); - } - if (!isObject(definition.generic)) { - throwInvalidConfig( - "device classes", - `The "generic" property is not an object`, - ); - } - - const basicDeviceClasses = new Map(); - for (const [key, basicClass] of Object.entries(definition.basic)) { - if (!hexKeyRegexNDigits.test(key)) { - throwInvalidConfig( - "device classes", - `found invalid key "${key}" in the basic device class definition. Device classes must have lowercase hexadecimal IDs.`, - ); - } - if (typeof basicClass !== "string") { - throwInvalidConfig( - "device classes", - `basic device class "${key}" must be a string`, - ); - } - const keyNum = parseInt(key.slice(2), 16); - basicDeviceClasses.set(keyNum, basicClass); - } - - const genericDeviceClasses = new Map(); - for ( - const [key, genericDefinition] of Object.entries( - definition.generic, - ) - ) { - if (!hexKeyRegexNDigits.test(key)) { - throwInvalidConfig( - "device classes", - `found invalid key "${key}" in the generic device class definition. Device classes must have lowercase hexadecimal IDs.`, - ); - } - const keyNum = parseInt(key.slice(2), 16); - genericDeviceClasses.set( - keyNum, - new GenericDeviceClass(keyNum, genericDefinition as any), - ); - } - - return { basicDeviceClasses, genericDeviceClasses }; - } catch (e) { - if (isZWaveError(e)) { - throw e; - } else { - throwInvalidConfig("device classes"); - } - } -} - /** @internal */ export async function loadIndicatorsInternal( externalConfig?: boolean, diff --git a/packages/config/src/DeviceClasses.ts b/packages/config/src/DeviceClasses.ts deleted file mode 100644 index c1f655782bb9..000000000000 --- a/packages/config/src/DeviceClasses.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { type JSONObject, num2hex } from "@zwave-js/shared/safe"; -import { isObject } from "alcalzone-shared/typeguards"; -import { hexKeyRegexNDigits, throwInvalidConfig } from "./utils_safe"; - -export type BasicDeviceClassMap = ReadonlyMap; -export type GenericDeviceClassMap = ReadonlyMap; - -export function getDefaultGenericDeviceClass(key: number): GenericDeviceClass { - return new GenericDeviceClass(key, { - label: `UNKNOWN (${num2hex(key)})`, - }); -} - -export function getDefaultSpecificDeviceClass( - generic: GenericDeviceClass, - key: number, -): SpecificDeviceClass { - if (key === 0) { - return new SpecificDeviceClass( - 0x00, - { - label: "Unused", - }, - generic, - ); - } - return new SpecificDeviceClass( - key, - { - label: `UNKNOWN (${num2hex(key)})`, - }, - generic, - ); -} - -export interface BasicDeviceClass { - key: number; - label: string; -} - -export class GenericDeviceClass { - public constructor(key: number, definition: JSONObject) { - this.key = key; - - if (typeof definition.label !== "string") { - throwInvalidConfig( - "device classes", - `The label for generic device class ${ - num2hex( - key, - ) - } is not a string!`, - ); - } - this.label = definition.label; - - if (definition.zwavePlusDeviceType != undefined) { - if (typeof definition.zwavePlusDeviceType !== "string") { - throwInvalidConfig( - "device classes", - `The zwavePlusDeviceType for generic device class ${ - num2hex( - key, - ) - } is not a string!`, - ); - } else { - this.zwavePlusDeviceType = definition.zwavePlusDeviceType; - } - } - - if (definition.requiresSecurity != undefined) { - if (typeof definition.requiresSecurity !== "boolean") { - throwInvalidConfig( - "device classes", - `The requiresSecurity property for generic device class ${ - num2hex( - key, - ) - } is not a boolean!`, - ); - } else { - this.requiresSecurity = definition.requiresSecurity; - } - } - - if (definition.maySupportBasicCC != undefined) { - if (definition.maySupportBasicCC !== false) { - throwInvalidConfig( - "device classes", - `maySupportBasicCC in device class ${this.label} (${ - num2hex(this.key) - }) must be false or omitted (= true)!`, - ); - } else { - this.maySupportBasicCC = false; - } - } else { - this.maySupportBasicCC = true; - } - - const specific = new Map(); - if (isObject(definition.specific)) { - for ( - const [specificKey, specificDefinition] of Object.entries( - definition.specific, - ) - ) { - if (!hexKeyRegexNDigits.test(specificKey)) { - throwInvalidConfig( - "device classes", - `found invalid key "${specificKey}" in device class ${this.label} (${ - num2hex( - this.key, - ) - }). Device classes must have lowercase hexadecimal IDs.`, - ); - } - const specificKeyNum = parseInt(specificKey.slice(2), 16); - specific.set( - specificKeyNum, - new SpecificDeviceClass( - specificKeyNum, - specificDefinition as any, - this, - ), - ); - } - } - this.specific = specific; - } - - public readonly key: number; - public readonly label: string; - /** @internal */ - public readonly zwavePlusDeviceType?: string; - public readonly requiresSecurity?: boolean; - public readonly maySupportBasicCC: boolean; - public readonly specific: ReadonlyMap; -} - -export class SpecificDeviceClass { - public constructor( - key: number, - definition: JSONObject, - generic: GenericDeviceClass, - ) { - this.key = key; - - if (typeof definition.label !== "string") { - throwInvalidConfig( - "device classes", - `The label for device class ${generic.label} -> ${ - num2hex( - key, - ) - } is not a string!`, - ); - } - this.label = definition.label; - - if (definition.zwavePlusDeviceType != undefined) { - if (typeof definition.zwavePlusDeviceType !== "string") { - throwInvalidConfig( - "device classes", - `The zwavePlusDeviceType for device class ${generic.label} -> ${ - num2hex(key) - } is not a string!`, - ); - } else { - this.zwavePlusDeviceType = definition.zwavePlusDeviceType; - } - } else if (generic.zwavePlusDeviceType != undefined) { - this.zwavePlusDeviceType = generic.zwavePlusDeviceType; - } - - if (definition.requiresSecurity != undefined) { - if (typeof definition.requiresSecurity !== "boolean") { - throwInvalidConfig( - "device classes", - `The requiresSecurity property for device class ${generic.label} -> ${ - num2hex(key) - } is not a string!`, - ); - } else { - this.requiresSecurity = definition.requiresSecurity; - } - } else if (generic.requiresSecurity != undefined) { - this.requiresSecurity = generic.requiresSecurity; - } - - if (definition.maySupportBasicCC != undefined) { - if (definition.maySupportBasicCC !== false) { - throwInvalidConfig( - "device classes", - `maySupportBasicCC in device class ${generic.label} -> ${this.label} (${ - num2hex(this.key) - }) must be false or omitted (= true)!`, - ); - } else { - this.maySupportBasicCC = false; - } - } else { - this.maySupportBasicCC = true; - } - } - - public readonly key: number; - public readonly label: string; - public readonly zwavePlusDeviceType?: string; - public readonly requiresSecurity?: boolean; - public readonly maySupportBasicCC: boolean; -} diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index e55060fda1de..d0f5f86dc975 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -1,5 +1,4 @@ export * from "./ConfigManager"; -export * from "./DeviceClasses"; export * from "./Indicators"; export * from "./Logger_safe"; export * from "./Manufacturers"; diff --git a/packages/config/src/index_safe.ts b/packages/config/src/index_safe.ts index 5b7bbaeea4f4..20947ea408df 100644 --- a/packages/config/src/index_safe.ts +++ b/packages/config/src/index_safe.ts @@ -1,6 +1,5 @@ /* @forbiddenImports external */ -export * from "./DeviceClasses"; export * from "./Indicators"; export * from "./Logger_safe"; export * from "./Notifications"; diff --git a/packages/core/src/capabilities/NodeInfo.ts b/packages/core/src/capabilities/NodeInfo.ts index afd5dd328b70..3f74674b0148 100644 --- a/packages/core/src/capabilities/NodeInfo.ts +++ b/packages/core/src/capabilities/NodeInfo.ts @@ -1,5 +1,6 @@ import { sum } from "@zwave-js/shared/safe"; import { NodeIDType } from "../consts"; +import { type BasicDeviceClass } from "../index_safe"; import { validatePayload } from "../util/misc"; import { CommandClasses } from "./CommandClasses"; @@ -32,7 +33,7 @@ export function encodeApplicationNodeInformation( export interface NodeUpdatePayload extends ApplicationNodeInformation { nodeId: number; - basicDeviceClass: number; + basicDeviceClass: BasicDeviceClass; } export function parseNodeUpdatePayload( @@ -203,7 +204,7 @@ export interface NodeProtocolInfo { export interface NodeProtocolInfoAndDeviceClass extends Omit { - basicDeviceClass: number; + basicDeviceClass: BasicDeviceClass; genericDeviceClass: number; specificDeviceClass: number; } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c911941fdfbb..b2b40b5c8a71 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -14,6 +14,7 @@ export * from "./error/ZWaveError"; export * from "./log/Controller"; export * from "./log/shared"; export * from "./log/shared_safe"; +export * from "./registries/DeviceClasses"; export * from "./registries/Meters"; export * from "./registries/Scales"; export * from "./registries/Sensors"; diff --git a/packages/core/src/index_safe.ts b/packages/core/src/index_safe.ts index 6558dc97d1b6..626ee5dabbda 100644 --- a/packages/core/src/index_safe.ts +++ b/packages/core/src/index_safe.ts @@ -14,6 +14,7 @@ export * from "./capabilities/ZWaveChipTypes"; export * from "./consts"; export * from "./error/ZWaveError"; export * from "./log/shared_safe"; +export * from "./registries/DeviceClasses"; export * from "./registries/Meters"; export * from "./registries/Scales"; export * from "./registries/Sensors"; diff --git a/packages/core/src/registries/DeviceClasses.ts b/packages/core/src/registries/DeviceClasses.ts new file mode 100644 index 000000000000..ce86b71aee3b --- /dev/null +++ b/packages/core/src/registries/DeviceClasses.ts @@ -0,0 +1,564 @@ +import { num2hex } from "@zwave-js/shared/safe"; + +export enum BasicDeviceClass { + Controller = 0x01, + "Static Controller" = 0x02, + "End Node" = 0x03, + "Routing End Node" = 0x04, +} + +export interface GenericDeviceClassDefinition { + readonly label: string; + readonly zwavePlusDeviceType?: string; + readonly requiresSecurity?: boolean; + readonly maySupportBasicCC?: boolean; + readonly specific: Record; +} + +export interface SpecificDeviceClassDefinition { + readonly label: string; + readonly zwavePlusDeviceType?: string; + readonly requiresSecurity?: boolean; + readonly maySupportBasicCC?: boolean; +} + +export interface GenericDeviceClass { + readonly key: number; + readonly label: string; + readonly zwavePlusDeviceType?: string; + readonly requiresSecurity: boolean; + readonly maySupportBasicCC: boolean; +} + +export type SpecificDeviceClass = GenericDeviceClass; + +const deviceClasses = Object.freeze( + { + [0x01]: { + label: "Remote Controller", + maySupportBasicCC: false, + specific: { + [0x01]: { + label: "Portable Remote Controller", + zwavePlusDeviceType: "Remote Control - Multipurpose", + }, + [0x02]: { + label: "Portable Scene Controller", + }, + [0x03]: { + label: "Portable Installer Tool", + }, + [0x04]: { + label: "AV Remote Control", + zwavePlusDeviceType: "Remote Control - AV", + }, + [0x06]: { + label: "Simple Remote Control", + zwavePlusDeviceType: "Remote Control - Simple", + }, + }, + }, + [0x02]: { + label: "Static Controller", + zwavePlusDeviceType: "Gateway", + specific: { + [0x01]: { + label: "PC Controller", + zwavePlusDeviceType: "Central Controller", + }, + [0x02]: { + label: "Scene Controller", + }, + [0x03]: { + label: "Static Installer Tool", + }, + [0x04]: { + label: "Set Top Box", + zwavePlusDeviceType: "Set Top Box", + }, + [0x05]: { + label: "Sub System Controller", + zwavePlusDeviceType: "Sub System Controller", + }, + [0x06]: { + label: "TV", + zwavePlusDeviceType: "TV", + }, + [0x07]: { + label: "Gateway", + zwavePlusDeviceType: "Gateway", + maySupportBasicCC: false, + }, + }, + }, + [0x03]: { + label: "AV Control Point", + zwavePlusDeviceType: "AV Control Point", + specific: { + [0x01]: { + label: "Sound Switch", + zwavePlusDeviceType: "Sound Switch", + }, + [0x04]: { + label: "Satellite Receiver", + }, + [0x11]: { + label: "Satellite Receiver V2", + }, + [0x12]: { + label: "Doorbell", + }, + }, + }, + [0x04]: { + label: "Display", + specific: { + [0x01]: { + label: "Simple Display", + zwavePlusDeviceType: "Display - Simple", + }, + }, + }, + [0x05]: { + label: "Network Extender", + specific: { + [0x01]: { + label: "Secure Extender", + }, + }, + }, + [0x06]: { + label: "Appliance", + specific: { + [0x01]: { + label: "General Appliance", + }, + [0x02]: { + label: "Kitchen Appliance", + }, + [0x03]: { + label: "Laundry Appliance", + }, + }, + }, + [0x07]: { + label: "Notification Sensor", + specific: { + [0x01]: { + label: "Notification Sensor", + zwavePlusDeviceType: "Sensor - Notification", + maySupportBasicCC: false, + }, + }, + }, + [0x08]: { + label: "Thermostat", + specific: { + [0x01]: { + label: "Heating Thermostat", + }, + [0x02]: { + label: "General Thermostat", + }, + [0x03]: { + label: "Setback Schedule Thermostat", + }, + [0x04]: { + label: "Setpoint Thermostat", + }, + [0x05]: { + label: "Setback Thermostat", + zwavePlusDeviceType: "Thermostat - Setback", + }, + [0x06]: { + label: "General Thermostat V2", + zwavePlusDeviceType: "Thermostat - HVAC", + }, + }, + }, + [0x09]: { + label: "Window Covering", + specific: { + [0x01]: { + label: "Simple Window Covering Control", + }, + }, + }, + [0x0f]: { + label: "Repeater Slave", + specific: { + [0x01]: { + label: "Repeater Slave", + zwavePlusDeviceType: "Repeater", + maySupportBasicCC: false, + }, + [0x03]: { + label: "IR Repeater", + zwavePlusDeviceType: "IR Repeater", + maySupportBasicCC: false, + }, + }, + }, + [0x10]: { + label: "Binary Switch", + specific: { + [0x01]: { + label: "Binary Power Switch", + zwavePlusDeviceType: "On/Off Power Switch", + }, + [0x02]: { + label: "Tunable Color Switch", + zwavePlusDeviceType: "Color Switch", + }, + [0x03]: { + label: "Binary Scene Switch", + }, + [0x04]: { + label: "Power Strip Switch", + zwavePlusDeviceType: "Power Strip", + }, + [0x05]: { + label: "Siren", + zwavePlusDeviceType: "Siren", + }, + [0x06]: { + label: "Valve", + zwavePlusDeviceType: "Valve (open/close)", + }, + [0x07]: { + label: "Irrigation Control", + zwavePlusDeviceType: "Irrigation Control", + }, + }, + }, + [0x11]: { + label: "Multilevel Switch", + specific: { + [0x01]: { + label: "Multilevel Power Switch", + zwavePlusDeviceType: "Light Dimmer Switch", + }, + [0x02]: { + label: "Tunable Color Switch", + zwavePlusDeviceType: "Color Switch", + }, + [0x03]: { + label: "Multiposition Motor", + }, + [0x04]: { + label: "Multilevel Scene Switch", + }, + [0x05]: { + label: "Motor Control Class A", + zwavePlusDeviceType: + "Window Covering - No Position/Endpoint", + }, + [0x06]: { + label: "Motor Control Class B", + zwavePlusDeviceType: "Window Covering - Endpoint Aware", + }, + [0x07]: { + label: "Motor Control Class C", + zwavePlusDeviceType: + "Window Covering - Position/Endpoint Aware", + }, + [0x08]: { + label: "Fan Switch", + zwavePlusDeviceType: "Fan Switch", + }, + }, + }, + [0x12]: { + label: "Remote Switch", + specific: { + [0x01]: { + label: "Binary Remote Switch", + }, + [0x02]: { + label: "Multilevel Remote Switch", + }, + [0x03]: { + label: "Binary Toggle Remote Switch", + }, + [0x04]: { + label: "Multilevel Toggle Remote Switch", + }, + }, + }, + [0x13]: { + label: "Toggle Switch", + specific: { + [0x01]: { + label: "Binary Toggle Switch", + }, + [0x02]: { + label: "Multilevel Toggle Switch", + }, + }, + }, + [0x15]: { + label: "Z/IP Node", + specific: { + [0x01]: { + label: "Z/IP TUN Node", + }, + [0x02]: { + label: "Z/IP ADV Node", + }, + }, + }, + [0x16]: { + label: "Ventilation", + specific: { + [0x01]: { + label: "Residential Heat Recovery Ventilation", + }, + }, + }, + [0x17]: { + label: "Security Panel", + specific: { + [0x01]: { + label: "Zoned Security Panel", + }, + }, + }, + [0x18]: { + label: "Wall Controller", + specific: { + [0x01]: { + label: "Basic Wall Controller", + zwavePlusDeviceType: "Wall Controller", + maySupportBasicCC: false, + }, + }, + }, + [0x20]: { + label: "Binary Sensor", + specific: { + [0x01]: { + label: "Routing Binary Sensor", + }, + }, + }, + [0x21]: { + label: "Multilevel Sensor", + specific: { + [0x01]: { + label: "Routing Multilevel Sensor", + zwavePlusDeviceType: "Sensor - Multilevel", + maySupportBasicCC: false, + }, + }, + }, + [0x30]: { + label: "Pulse Meter", + specific: {}, + }, + [0x31]: { + label: "Meter", + maySupportBasicCC: false, + specific: { + [0x01]: { + label: "Simple Meter", + zwavePlusDeviceType: "Sub Energy Meter", + }, + [0x02]: { + label: "Advanced Energy Control", + }, + [0x03]: { + label: "Simple Whole Home Meter", + zwavePlusDeviceType: "Whole Home Meter - Simple", + }, + }, + }, + [0x40]: { + label: "Entry Control", + specific: { + [0x01]: { + label: "Door Lock", + }, + [0x02]: { + label: "Advanced Door Lock", + }, + [0x03]: { + label: "Secure Keypad Door Lock", + zwavePlusDeviceType: "Door Lock - Keypad", + requiresSecurity: true, + }, + [0x05]: { + label: "Secure Door", + zwavePlusDeviceType: "Motorized Barrier - GDO", + requiresSecurity: true, + }, + [0x06]: { + label: "Secure Gate", + zwavePlusDeviceType: "Motorized Barrier - Gate", + requiresSecurity: true, + }, + [0x07]: { + label: "Secure Barrier Add-on", + zwavePlusDeviceType: "Motorized Barrier - Add-on", + requiresSecurity: true, + }, + [0x08]: { + label: "Secure Barrier Open only", + zwavePlusDeviceType: "Motorized Barrier - Open only", + requiresSecurity: true, + }, + [0x09]: { + label: "Secure Barrier Close only", + zwavePlusDeviceType: "Motorized Barrier - Close only", + requiresSecurity: true, + }, + [0x0a]: { + label: "Lockbox", + zwavePlusDeviceType: "Lockbox", + requiresSecurity: true, + }, + [0x0b]: { + label: "Secure Keypad", + zwavePlusDeviceType: "Entry Control Keypad", + requiresSecurity: true, + maySupportBasicCC: false, + }, + }, + }, + [0x50]: { + label: "Semi-Interoperable", + specific: { + [0x01]: { + label: "Energy Production", + }, + }, + }, + [0xa1]: { + label: "Alarm Sensor", + specific: { + [0x01]: { + label: "Basic Routing Alarm Sensor", + }, + [0x02]: { + label: "Routing Alarm Sensor", + }, + [0x03]: { + label: "Basic Zensor Net Alarm Sensor", + }, + [0x04]: { + label: "Zensor Net Alarm Sensor", + }, + [0x05]: { + label: "Advanced Zensor Net Alarm Sensor", + }, + [0x06]: { + label: "Basic Routing Smoke Sensor", + }, + [0x07]: { + label: "Routing Smoke Sensor", + }, + [0x08]: { + label: "Basic Zensor Net Smoke Sensor", + }, + [0x09]: { + label: "Zensor Net Smoke Sensor", + }, + [0x0a]: { + label: "Advanced Zensor Net Smoke Sensor", + }, + }, + }, + [0xff]: { + label: "Non-Interoperable", + specific: {}, + }, + } as const satisfies Record, +); + +/** Returns the Generic Device Class for the given key */ +export function getGenericDeviceClass( + generic: Generic, +): Generic extends keyof typeof deviceClasses ? typeof deviceClasses[Generic] + : GenericDeviceClass +{ + const genericClass: GenericDeviceClassDefinition | undefined = + (deviceClasses as any)[generic]; + // @ts-expect-error We're in the false branch of the conditional type + if (!genericClass) return getUnknownGenericDeviceClass(generic); + + return { + key: generic, + label: genericClass.label, + zwavePlusDeviceType: genericClass.zwavePlusDeviceType, + requiresSecurity: genericClass.requiresSecurity ?? false, + maySupportBasicCC: genericClass.maySupportBasicCC ?? true, + } satisfies GenericDeviceClass as any; +} + +export function getUnknownGenericDeviceClass(key: number): GenericDeviceClass { + return { + key, + label: `UNKNOWN (${num2hex(key)})`, + requiresSecurity: false, + maySupportBasicCC: true, + }; +} + +/** Returns the Specific Device Class for the given key */ +export function getSpecificDeviceClass< + Generic extends number, + Specific extends number, +>( + generic: Generic, + specific: Specific, +): Generic extends keyof typeof deviceClasses + ? Specific extends keyof typeof deviceClasses[Generic]["specific"] + ? typeof deviceClasses[Generic]["specific"][Specific] + : SpecificDeviceClass + : SpecificDeviceClass +{ + const genericClass: GenericDeviceClassDefinition | undefined = + (deviceClasses as any)[generic]; + // @ts-expect-error We're in the false branch of the conditional type + if (!genericClass) return getUnknownSpecificDeviceClass(specific); + + const specificClass: SpecificDeviceClassDefinition | undefined = + genericClass.specific[specific]; + // @ts-expect-error We're in the false branch of the conditional type + if (!specificClass) return getUnknownSpecificDeviceClass(specific); + + return { + key: specific, + label: specificClass.label, + zwavePlusDeviceType: specificClass.zwavePlusDeviceType + ?? genericClass.zwavePlusDeviceType, + requiresSecurity: specificClass.requiresSecurity + ?? genericClass.requiresSecurity + ?? false, + maySupportBasicCC: specificClass.maySupportBasicCC + ?? genericClass.maySupportBasicCC + ?? true, + } satisfies SpecificDeviceClass as any; +} + +export function getUnknownSpecificDeviceClass( + genericClass: GenericDeviceClassDefinition, + specific: number, +): SpecificDeviceClass { + if (specific === 0) { + return { + key: specific, + label: "Unused", + zwavePlusDeviceType: genericClass.zwavePlusDeviceType, + requiresSecurity: genericClass.requiresSecurity ?? false, + maySupportBasicCC: genericClass.maySupportBasicCC ?? true, + }; + } else { + return { + key: specific, + label: `UNKNOWN (${num2hex(specific)})`, + zwavePlusDeviceType: genericClass.zwavePlusDeviceType, + requiresSecurity: genericClass.requiresSecurity ?? false, + maySupportBasicCC: genericClass.maySupportBasicCC ?? true, + }; + } +} diff --git a/packages/zwave-js/src/lib/controller/Controller.ts b/packages/zwave-js/src/lib/controller/Controller.ts index afe482fa49d4..cd7248c2d076 100644 --- a/packages/zwave-js/src/lib/controller/Controller.ts +++ b/packages/zwave-js/src/lib/controller/Controller.ts @@ -32,6 +32,7 @@ import { } from "@zwave-js/cc"; import { type IndicatorObject } from "@zwave-js/cc/IndicatorCC"; import { + BasicDeviceClass, CommandClasses, ControllerStatus, EMPTY_ROUTE, @@ -2578,7 +2579,6 @@ export class ZWaveController this.setInclusionState(InclusionState.Busy); const deviceClass = new DeviceClass( - this.driver.configManager, nodeInfo.basicDeviceClass, nodeInfo.genericDeviceClass, nodeInfo.specificDeviceClass, @@ -2603,11 +2603,20 @@ export class ZWaveController }); this.driver.controllerLog.print( - `Node ${newNode.id} was included by another controller: -basic device class: ${newNode.deviceClass?.basic.label} -generic device class: ${newNode.deviceClass?.generic.label} -specific device class: ${newNode.deviceClass?.specific.label} -supported CCs: ${ + `Node ${newNode.id} was included by another controller:${ + newNode.deviceClass + ? ` + basic device class: ${ + getEnumMemberName( + BasicDeviceClass, + newNode.deviceClass.basic, + ) + } + generic device class: ${newNode.deviceClass.generic.label} + specific device class: ${newNode.deviceClass.specific.label}` + : "" + } + supported CCs: ${ nodeInfo.supportedCCs .map((cc) => `\n · ${CommandClasses[cc]} (${num2hex(cc)})` @@ -2826,7 +2835,6 @@ supported CCs: ${ // TODO: Check if this stuff works for a normal replace too // eslint-disable-next-line @typescript-eslint/dot-notation newNode["deviceClass"] = new DeviceClass( - this.driver.configManager, requestedNodeInfo.basicDeviceClass, requestedNodeInfo.genericDeviceClass, requestedNodeInfo.specificDeviceClass, @@ -3764,7 +3772,6 @@ supported CCs: ${ msg.statusContext!.nodeId, this.driver, new DeviceClass( - this.driver.configManager, msg.statusContext!.basicDeviceClass!, msg.statusContext!.genericDeviceClass!, msg.statusContext!.specificDeviceClass!, @@ -3850,10 +3857,19 @@ supported CCs: ${ }); this.driver.controllerLog.print( - `finished adding node ${newNode.id}: - basic device class: ${newNode.deviceClass?.basic.label} - generic device class: ${newNode.deviceClass?.generic.label} - specific device class: ${newNode.deviceClass?.specific.label} + `finished adding node ${newNode.id}:${ + newNode.deviceClass + ? ` + basic device class: ${ + getEnumMemberName( + BasicDeviceClass, + newNode.deviceClass.basic, + ) + } + generic device class: ${newNode.deviceClass.generic.label} + specific device class: ${newNode.deviceClass.specific.label}` + : "" + } supported CCs: ${ supportedCCs .map((cc) => diff --git a/packages/zwave-js/src/lib/driver/Driver.ts b/packages/zwave-js/src/lib/driver/Driver.ts index 5e78da3dd3b5..9883dda345a7 100644 --- a/packages/zwave-js/src/lib/driver/Driver.ts +++ b/packages/zwave-js/src/lib/driver/Driver.ts @@ -1363,10 +1363,8 @@ export class Driver extends TypedEventEmitter ); this._networkCache = new JsonlDB(networkCacheFile, { ...options, - serializer: (key, value) => - serializeNetworkCacheValue(this, key, value), - reviver: (key, value) => - deserializeNetworkCacheValue(this, key, value), + serializer: serializeNetworkCacheValue, + reviver: deserializeNetworkCacheValue, }); await this._networkCache.open(); @@ -1425,7 +1423,6 @@ export class Driver extends TypedEventEmitter try { await migrateLegacyNetworkCache( - this, this.controller.homeId, this._networkCache, this._valueDB, diff --git a/packages/zwave-js/src/lib/driver/NetworkCache.ts b/packages/zwave-js/src/lib/driver/NetworkCache.ts index 095ba76b3098..1acc3a4e8fb4 100644 --- a/packages/zwave-js/src/lib/driver/NetworkCache.ts +++ b/packages/zwave-js/src/lib/driver/NetworkCache.ts @@ -21,7 +21,6 @@ import { } from "../controller/Inclusion"; import { DeviceClass } from "../node/DeviceClass"; import { InterviewStage } from "../node/_Types"; -import type { Driver } from "./Driver"; /** * Defines the keys that are used to store certain properties in the network cache. @@ -116,10 +115,7 @@ function tryParseInterviewStage(value: unknown): InterviewStage | undefined { } } -function tryParseDeviceClass( - driver: Driver, - value: unknown, -): DeviceClass | undefined { +function tryParseDeviceClass(value: unknown): DeviceClass | undefined { if (isObject(value)) { const { basic, generic, specific } = value; if ( @@ -128,7 +124,6 @@ function tryParseDeviceClass( && typeof specific === "number" ) { return new DeviceClass( - driver.configManager, basic, generic, specific, @@ -342,7 +337,6 @@ function tryParseAssociationAddress( } export function deserializeNetworkCacheValue( - driver: Driver, key: string, value: unknown, ): unknown { @@ -371,7 +365,7 @@ export function deserializeNetworkCacheValue( throw fail(); } case "deviceClass": { - value = tryParseDeviceClass(driver, value); + value = tryParseDeviceClass(value); if (value) return value; throw fail(); } @@ -462,7 +456,6 @@ export function deserializeNetworkCacheValue( } export function serializeNetworkCacheValue( - driver: Driver, key: string, value: unknown, ): unknown { @@ -474,7 +467,7 @@ export function serializeNetworkCacheValue( case "deviceClass": { const deviceClass = value as DeviceClass; return { - basic: deviceClass.basic.key, + basic: deviceClass.basic, generic: deviceClass.generic.key, specific: deviceClass.specific.key, }; @@ -577,7 +570,6 @@ const legacyPaths = { } as const; export async function migrateLegacyNetworkCache( - driver: Driver, homeId: number, networkCache: JsonlDB, valueDB: JsonlDB, @@ -627,7 +619,7 @@ export async function migrateLegacyNetworkCache( nodeCacheKeys.deviceClass, node, legacyPaths.node.deviceClass, - (v) => tryParseDeviceClass(driver, v), + (v) => tryParseDeviceClass(v), ); tryMigrate( nodeCacheKeys.isListening, diff --git a/packages/zwave-js/src/lib/node/DeviceClass.ts b/packages/zwave-js/src/lib/node/DeviceClass.ts index e683f11937cb..143a0e094982 100644 --- a/packages/zwave-js/src/lib/node/DeviceClass.ts +++ b/packages/zwave-js/src/lib/node/DeviceClass.ts @@ -1,22 +1,22 @@ -import type { +import { BasicDeviceClass, - ConfigManager, - GenericDeviceClass, - SpecificDeviceClass, -} from "@zwave-js/config"; -import { type CommandClasses } from "@zwave-js/core"; -import type { JSONObject } from "@zwave-js/shared"; + type CommandClasses, + type GenericDeviceClass, + type SpecificDeviceClass, + getGenericDeviceClass, + getSpecificDeviceClass, +} from "@zwave-js/core"; +import { type JSONObject, getEnumMemberName } from "@zwave-js/shared"; export class DeviceClass { public constructor( - configManager: ConfigManager, - basic: number, + basic: BasicDeviceClass, generic: number, specific: number, ) { - this.basic = configManager.lookupBasicDeviceClass(basic); - this.generic = configManager.lookupGenericDeviceClass(generic); - this.specific = configManager.lookupSpecificDeviceClass( + this.basic = basic; + this.generic = getGenericDeviceClass(generic); + this.specific = getSpecificDeviceClass( generic, specific, ); @@ -38,7 +38,7 @@ export class DeviceClass { public toJSON(): JSONObject { return { - basic: this.basic.label, + basic: getEnumMemberName(BasicDeviceClass, this.basic), generic: this.generic.label, specific: this.specific.label, }; diff --git a/packages/zwave-js/src/lib/node/Endpoint.ts b/packages/zwave-js/src/lib/node/Endpoint.ts index e6f738531418..3f8999060c53 100644 --- a/packages/zwave-js/src/lib/node/Endpoint.ts +++ b/packages/zwave-js/src/lib/node/Endpoint.ts @@ -12,6 +12,7 @@ import { import { ZWavePlusCCValues } from "@zwave-js/cc/ZWavePlusCC"; import type { IZWaveEndpoint, MaybeNotKnown } from "@zwave-js/core"; import { + BasicDeviceClass, CacheBackedMap, type CommandClassInfo, CommandClasses, @@ -21,7 +22,7 @@ import { actuatorCCs, getCCName, } from "@zwave-js/core"; -import { num2hex } from "@zwave-js/shared"; +import { getEnumMemberName, num2hex } from "@zwave-js/shared"; import { isDeepStrictEqual } from "node:util"; import type { Driver } from "../driver/Driver"; import { cacheKeys } from "../driver/NetworkCache"; @@ -504,8 +505,11 @@ export class Endpoint implements IZWaveEndpoint { if (this.deviceClass) { ret.deviceClass = { basic: { - key: this.deviceClass.basic.key, - label: this.deviceClass.basic.label, + key: this.deviceClass.basic, + label: getEnumMemberName( + BasicDeviceClass, + this.deviceClass.basic, + ), }, generic: { key: this.deviceClass.generic.key, diff --git a/packages/zwave-js/src/lib/node/Node.ts b/packages/zwave-js/src/lib/node/Node.ts index 2129b602d94d..77ae4ec98eb8 100644 --- a/packages/zwave-js/src/lib/node/Node.ts +++ b/packages/zwave-js/src/lib/node/Node.ts @@ -157,6 +157,7 @@ import { embeddedDevicesDir, } from "@zwave-js/config"; import { + BasicDeviceClass, CRC16_CCITT, CacheBackedMap, CommandClasses, @@ -1498,8 +1499,7 @@ export class ZWaveNode extends Endpoint ); if (deviceClass && this.deviceClass) { return new DeviceClass( - this.driver.configManager, - this.deviceClass.basic.key, + this.deviceClass.basic, deviceClass.generic, deviceClass.specific, ); @@ -1921,14 +1921,15 @@ export class ZWaveNode extends Endpoint this.supportsBeaming = resp.supportsBeaming; this.deviceClass = new DeviceClass( - this.driver.configManager, resp.basicDeviceClass, resp.genericDeviceClass, resp.specificDeviceClass, ); const logMessage = `received response for protocol info: -basic device class: ${this.deviceClass.basic.label} +basic device class: ${ + getEnumMemberName(BasicDeviceClass, this.deviceClass.basic) + } generic device class: ${this.deviceClass.generic.label} specific device class: ${this.deviceClass.specific.label} node type: ${getEnumMemberName(NodeType, this.nodeType)} diff --git a/packages/zwave-js/src/lib/serialapi/network-mgmt/AddNodeToNetworkRequest.ts b/packages/zwave-js/src/lib/serialapi/network-mgmt/AddNodeToNetworkRequest.ts index 2e9cbca15f72..56f6a609626e 100644 --- a/packages/zwave-js/src/lib/serialapi/network-mgmt/AddNodeToNetworkRequest.ts +++ b/packages/zwave-js/src/lib/serialapi/network-mgmt/AddNodeToNetworkRequest.ts @@ -1,4 +1,5 @@ import { + type BasicDeviceClass, type CommandClasses, type MessageOrCCLogEntry, MessagePriority, @@ -327,7 +328,7 @@ export class AddNodeToNetworkRequestStatusReport interface AddNodeStatusContext { nodeId: number; - basicDeviceClass?: number; + basicDeviceClass?: BasicDeviceClass; genericDeviceClass?: number; specificDeviceClass?: number; supportedCCs?: CommandClasses[];