UNPKG

inventoresed

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

308 lines (288 loc) 8.33 kB
import { CommandClasses } from "@zwave-js/core/safe"; import { JSONObject, num2hex } from "@zwave-js/shared/safe"; import { distinct } from "alcalzone-shared/arrays"; import { isArray, isObject } from "alcalzone-shared/typeguards"; import { hexKeyRegexNDigits, throwInvalidConfig } from "./utils_safe"; export type BasicDeviceClassMap = ReadonlyMap<number, string>; export type GenericDeviceClassMap = ReadonlyMap<number, GenericDeviceClass>; 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.supportedCCs != undefined) { if ( !isArray(definition.supportedCCs) || !definition.supportedCCs.every( (cc: any) => typeof cc === "string", ) ) { throwInvalidConfig( "device classes", `supportedCCs in device class ${this.label} (${num2hex( this.key, )}) is not a string array!`, ); } const supportedCCs: CommandClasses[] = []; for (const ccName of definition.supportedCCs) { if (!(ccName in CommandClasses)) { throwInvalidConfig( "device classes", `Found unknown CC "${ccName}" in supportedCCs of device class ${ this.label } (${num2hex(this.key)})!`, ); } supportedCCs.push((CommandClasses as any)[ccName]); } this.supportedCCs = supportedCCs; } else { this.supportedCCs = []; } if (definition.controlledCCs != undefined) { if ( !isArray(definition.controlledCCs) || !definition.controlledCCs.every( (cc: any) => typeof cc === "string", ) ) { throwInvalidConfig( "device classes", `controlledCCs in device class ${this.label} (${num2hex( this.key, )}) is not a string array!`, ); } const controlledCCs: CommandClasses[] = []; for (const ccName of definition.controlledCCs) { if (!(ccName in CommandClasses)) { throwInvalidConfig( "device classes", `Found unknown CC "${ccName}" in controlledCCs of device class ${ this.label } (${num2hex(this.key)})!`, ); } controlledCCs.push((CommandClasses as any)[ccName]); } this.controlledCCs = controlledCCs; } else { this.controlledCCs = []; } const specific = new Map<number, SpecificDeviceClass>(); 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 supportedCCs: readonly CommandClasses[]; public readonly controlledCCs: readonly CommandClasses[]; public readonly specific: ReadonlyMap<number, SpecificDeviceClass>; } 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.supportedCCs != undefined) { if ( !isArray(definition.supportedCCs) || !definition.supportedCCs.every( (cc: any) => typeof cc === "string", ) ) { throwInvalidConfig( "device classes", `supportedCCs in device class ${generic.label} -> ${ this.label } (${num2hex(this.key)}) is not a string array!`, ); } const supportedCCs: CommandClasses[] = []; for (const ccName of definition.supportedCCs) { if (!(ccName in CommandClasses)) { throwInvalidConfig( "device classes", `Found unknown CC "${ccName}" in supportedCCs of device class ${ generic.label } -> ${this.label} (${num2hex(this.key)})!`, ); } supportedCCs.push((CommandClasses as any)[ccName]); } this.supportedCCs = supportedCCs; } else { this.supportedCCs = []; } this.supportedCCs = distinct([ ...generic.supportedCCs, ...this.supportedCCs, ]); if (definition.controlledCCs != undefined) { if ( !isArray(definition.controlledCCs) || !definition.controlledCCs.every( (cc: any) => typeof cc === "string", ) ) { throwInvalidConfig( "device classes", `controlledCCs in device class ${generic.label} -> ${ this.label } (${num2hex(this.key)}) is not a string array!`, ); } const controlledCCs: CommandClasses[] = []; for (const ccName of definition.controlledCCs) { if (!(ccName in CommandClasses)) { throwInvalidConfig( "device classes", `Found unknown CC "${ccName}" in controlledCCs of device class ${ generic.label } -> ${this.label} (${num2hex(this.key)})!`, ); } controlledCCs.push((CommandClasses as any)[ccName]); } this.controlledCCs = controlledCCs; } else { this.controlledCCs = []; } this.controlledCCs = distinct([ ...generic.controlledCCs, ...this.controlledCCs, ]); } public readonly key: number; public readonly label: string; public readonly zwavePlusDeviceType?: string; public readonly requiresSecurity?: boolean; public readonly supportedCCs: readonly CommandClasses[]; public readonly controlledCCs: readonly CommandClasses[]; }