inventoresed
Version:
Z-Wave driver written entirely in JavaScript/TypeScript
626 lines (571 loc) • 19.6 kB
text/typescript
import {
CommandClasses,
IZWaveEndpoint,
ValueID,
ValueMetadata,
} from "@zwave-js/core";
import type { ZWaveApplicationHost } from "@zwave-js/host";
import type { Overwrite } from "alcalzone-shared/types";
import type { ValueIDProperties } from "./API";
// HINT: To fully view types for definitions created by this, open
// node_modules/typescript/lib/tsserver.js and change the definition of
// ts.defaultMaximumTruncationLength = 160
// to something higher like
// ts.defaultMaximumTruncationLength = 1000
// Then restart TS Server
export interface CCValueOptions {
/**
* Whether the CC value is internal. Internal values are not exposed to the user.
*/
internal?: boolean;
/**
* The minimum CC version required for this value to exist.
*/
minVersion?: number;
/**
* Whether this value represents a state (`true`) or a notification/event (`false`). Default: `true`
*/
stateful?: boolean;
/**
* Omit this value from value logs. Default: `false`
*/
secret?: boolean;
/** Whether the CC value may exist on endpoints. Default: `true` */
supportsEndpoints?: boolean;
/**
* Can be used to dynamically decide if this value should be created automatically.
* This is ignored for dynamic values and defaults to `true` if not given.
*/
autoCreate?:
| boolean
| ((
applHost: ZWaveApplicationHost,
endpoint: IZWaveEndpoint,
) => boolean);
}
export const defaultCCValueOptions = {
internal: false,
minVersion: 1,
secret: false,
stateful: true,
supportsEndpoints: true,
autoCreate: true,
} as const;
type DefaultOptions = typeof defaultCCValueOptions;
// expands object types recursively
export type ExpandRecursively<T> =
// Split functions with properties into the function and object parts
T extends (...args: infer A) => infer R
? [keyof T] extends [never]
? (...args: A) => ExpandRecursively<R>
: ((...args: A) => ExpandRecursively<R>) & {
[]: ExpandRecursively<T[P]>;
}
: // Expand object types
T extends object
? T extends infer O
? { [K in keyof O]: ExpandRecursively<O[K]> }
: never
: // Fallback to the type itself if no match
T;
export type ExpandRecursivelySkipMeta<T> =
// Split functions with properties into the function and object parts
T extends (...args: infer A) => infer R
? [keyof T] extends [never]
? (...args: A) => ExpandRecursivelySkipMeta<R>
: ((...args: A) => ExpandRecursivelySkipMeta<R>) & {
[]: ExpandRecursivelySkipMeta<T[P]>;
}
: // Ignore the ValueMetadata type
T extends ValueMetadata
? T
: // Expand object types
T extends object
? T extends infer O
? { [K in keyof O]: ExpandRecursivelySkipMeta<O[K]> }
: never
: // Fallback to the type itself if no match
T;
type InferValueIDBase<
TCommandClass extends CommandClasses,
TBlueprint extends CCValueBlueprint,
TWorker = {
commandClass: TCommandClass;
} & Pick<TBlueprint, "property" | "propertyKey">,
> = {
[] ? never : K]: TWorker[K];
};
type ToStaticCCValues<
TCommandClass extends CommandClasses,
TValues extends Record<keyof TValues, CCValueBlueprint>,
> = Readonly<{
[]: ExpandRecursively<
InferStaticCCValue<TCommandClass, TValues[K]>
>;
}>;
type ToDynamicCCValues<
TCommandClass extends CommandClasses,
TValues extends Record<keyof TValues, DynamicCCValueBlueprint<any>>,
> = Readonly<{
[]: ExpandRecursively<
InferDynamicCCValue<TCommandClass, TValues[K]>
>;
}>;
type FnOrStatic<TArgs extends any[], TReturn> =
| ((...args: TArgs) => TReturn)
| TReturn;
type ReturnTypeOrStatic<T> = T extends (...args: any[]) => infer R ? R : T;
type InferArgs<T extends FnOrStatic<any, any>[]> = T extends [
(...args: infer A) => any,
...any,
]
? A
: T extends [any, ...infer R]
? InferArgs<R>
: [];
function evalOrStatic<T>(fnOrConst: T, ...args: any[]): ReturnTypeOrStatic<T> {
return typeof fnOrConst === "function" ? fnOrConst(...args) : fnOrConst;
}
/** Defines a single static CC values that belong to a CC */
function defineStaticCCValue<
TCommandClass extends CommandClasses,
TBlueprint extends CCValueBlueprint,
>(
commandClass: TCommandClass,
blueprint: TBlueprint,
): ExpandRecursively<InferStaticCCValue<TCommandClass, TBlueprint>> {
// Normalize value options
const _blueprint = {
...blueprint,
options: {
...defaultCCValueOptions,
...blueprint.options,
},
};
const valueId = {
commandClass,
property: _blueprint.property,
propertyKey: _blueprint.propertyKey,
};
const ret: InferStaticCCValue<TCommandClass, TBlueprint> = {
get id() {
return { ...valueId };
},
endpoint: (endpoint: number = 0) => {
if (!_blueprint.options.supportsEndpoints) endpoint = 0;
return { ...valueId, endpoint };
},
is: (testValueId) => {
return (
valueId.commandClass === testValueId.commandClass &&
valueId.property === testValueId.property &&
valueId.propertyKey === testValueId.propertyKey
);
},
get meta() {
return { ...ValueMetadata.Any, ..._blueprint.meta } as any;
},
get options() {
return { ..._blueprint.options } as any;
},
};
return ret as any;
}
/** Defines a single CC value which depends on one or more parameters */
function defineDynamicCCValue<
TCommandClass extends CommandClasses,
TBlueprint extends DynamicCCValueBlueprint<any[]>,
>(
commandClass: TCommandClass,
blueprint: TBlueprint,
): ExpandRecursively<InferDynamicCCValue<TCommandClass, TBlueprint>> {
const options = {
...defaultCCValueOptions,
...blueprint.options,
};
const ret: ExpandRecursively<
InferDynamicCCValue<TCommandClass, TBlueprint>
> = ((...args: Parameters<TBlueprint>) => {
const _blueprint = blueprint(...args);
// Normalize value options
const actualBlueprint = {
..._blueprint,
};
const valueId = {
commandClass,
property: actualBlueprint.property,
propertyKey: actualBlueprint.propertyKey,
};
const value: Omit<
InferStaticCCValue<TCommandClass, ReturnType<TBlueprint>>,
"options" | "is"
> = {
get id() {
return { ...valueId };
},
endpoint: (endpoint: number = 0) => {
if (!options.supportsEndpoints) endpoint = 0;
return { ...valueId, endpoint };
},
get meta() {
return { ...ValueMetadata.Any, ...actualBlueprint.meta } as any;
},
};
return value;
}) as any;
Object.defineProperty(ret, "options", {
configurable: false,
enumerable: true,
get() {
return { ...options };
},
});
Object.defineProperty(ret, "is", {
configurable: false,
enumerable: false,
writable: false,
value: (id: ValueID) =>
id.commandClass === commandClass && blueprint.is(id),
});
return ret;
}
// Namespace for utilities to define CC values
export const V = {
/** Defines multiple static CC values that belong to the same CC */
defineStaticCCValues<
TCommandClass extends CommandClasses,
TValues extends Record<keyof TValues, CCValueBlueprint>,
>(
commandClass: TCommandClass,
values: TValues,
): TValues extends Record<keyof TValues, CCValueBlueprint>
? ExpandRecursively<ToStaticCCValues<TCommandClass, TValues>>
: never {
return Object.fromEntries(
Object.entries<CCValueBlueprint>(values).map(([key, blueprint]) => [
key,
defineStaticCCValue(commandClass, blueprint),
]),
) as any;
},
/** Defines multiple static CC values that belong to the same CC */
defineDynamicCCValues<
TCommandClass extends CommandClasses,
TValues extends Record<keyof TValues, DynamicCCValueBlueprint<any>>,
>(
commandClass: TCommandClass,
values: TValues,
): TValues extends Record<keyof TValues, DynamicCCValueBlueprint<any>>
? ExpandRecursively<ToDynamicCCValues<TCommandClass, TValues>>
: never {
return Object.fromEntries(
Object.entries<DynamicCCValueBlueprint<any>>(values).map(
([key, blueprint]) => [
key,
defineDynamicCCValue(commandClass, blueprint),
],
),
) as any;
},
/** Returns a CC value definition that is named like the value `property` */
staticProperty<
TProp extends string | number,
TMeta extends ValueMetadata,
TOptions extends CCValueOptions,
>(
property: TProp,
meta?: TMeta,
options?: TOptions,
): {
[]: {
property: TProp;
meta: TMeta;
options: TOptions;
};
} {
return {
[]: {
property,
meta,
options,
},
} as any;
},
/** Returns a CC value definition with the given name and `property` */
staticPropertyWithName<
TName extends string,
TProp extends string | number,
TMeta extends ValueMetadata,
TOptions extends CCValueOptions,
>(
name: TName,
property: TProp,
meta?: TMeta,
options?: TOptions,
): {
[]: {
property: TProp;
meta: TMeta;
options: TOptions;
};
} {
return {
[]: {
property,
meta,
options,
},
} as any;
},
/** Returns a CC value definition with the given name, `property` and `propertyKey` */
staticPropertyAndKeyWithName<
TName extends string,
TProp extends string | number,
TKey extends string | number,
TMeta extends ValueMetadata,
TOptions extends CCValueOptions,
>(
name: TName,
property: TProp,
propertyKey: TKey,
meta?: TMeta,
options?: TOptions,
): {
[]: {
property: TProp;
propertyKey: TKey;
meta: TMeta;
options: TOptions;
};
} {
return {
[]: {
property,
propertyKey,
meta,
options,
},
} as any;
},
/** Returns a CC value definition with the given name and a dynamic `property` */
dynamicPropertyWithName<
TName extends string,
TProp extends FnOrStatic<any[], ValueIDProperties["property"]>,
TMeta extends FnOrStatic<any[], ValueMetadata> = ValueMetadata,
TOptions extends CCValueOptions = CCValueOptions,
>(
name: TName,
property: TProp,
is: PartialCCValuePredicate,
meta?: TMeta,
options?: TOptions | undefined,
): {
[]: {
(...args: InferArgs<[TProp, TMeta]>): {
property: ReturnTypeOrStatic<TProp>;
meta: ReturnTypeOrStatic<TMeta>;
};
is: PartialCCValuePredicate;
options: TOptions;
};
} {
return {
[]: Object.assign(
(...args: InferArgs<[TProp, TMeta]>) => ({
property: evalOrStatic(property, ...args),
meta: evalOrStatic(meta, ...args),
}),
{ is, options },
),
} as any;
},
/** Returns a CC value definition with the given name and a dynamic `property` */
dynamicPropertyAndKeyWithName<
TName extends string,
TProp extends FnOrStatic<any[], ValueIDProperties["property"]>,
TKey extends FnOrStatic<any[], ValueIDProperties["propertyKey"]>,
TMeta extends FnOrStatic<any[], ValueMetadata> = ValueMetadata,
TOptions extends FnOrStatic<any[], CCValueOptions> = CCValueOptions,
>(
name: TName,
property: TProp,
propertyKey: TKey,
is: PartialCCValuePredicate,
meta?: TMeta,
options?: TOptions | undefined,
): {
[]: {
(...args: InferArgs<[TProp, TKey, TMeta]>): {
property: ReturnTypeOrStatic<TProp>;
propertyKey: ReturnTypeOrStatic<TKey>;
meta: ReturnTypeOrStatic<TMeta>;
};
is: PartialCCValuePredicate;
options: TOptions;
};
} {
return {
[]: Object.assign(
(...args: any[]) => {
return {
property: evalOrStatic(property, ...args),
propertyKey: evalOrStatic(propertyKey, ...args),
meta: evalOrStatic(meta, ...args),
};
},
{ is, options },
),
} as any;
},
};
export interface CCValueBlueprint extends Readonly<ValueIDProperties> {
readonly meta?: Readonly<ValueMetadata>;
readonly options?: Readonly<CCValueOptions>;
}
export type CCValuePredicate = (valueId: ValueID) => boolean;
export type PartialCCValuePredicate = (
properties: ValueIDProperties,
) => boolean;
/** A blueprint for a CC value which depends on one or more parameters */
export interface DynamicCCValueBlueprint<TArgs extends any[]> {
(...args: TArgs): Omit<CCValueBlueprint, "options">;
is: PartialCCValuePredicate;
readonly options?: Readonly<CCValueOptions>;
}
type DropOptional<T> = {
[] extends [T[K]] ? never : K]: T[K];
};
type MergeOptions<TOptions extends CCValueOptions> = DropOptional<
CCValueOptions extends TOptions
? // When the type cannot be inferred exactly (not given), default to DefaultOptions
DefaultOptions
: Overwrite<DefaultOptions, TOptions>
>;
type MergeMeta<TMeta extends ValueMetadata> = DropOptional<
ValueMetadata extends TMeta
? // When the type cannot be inferred exactly (not given), default to ValueMetadata.Any
typeof ValueMetadata["Any"]
: Overwrite<typeof ValueMetadata["Any"], TMeta>
>;
/** The common base type of all CC value definitions */
export interface CCValue {
readonly id: Omit<ValueID, "endpoint">;
endpoint(endpoint?: number): ValueID;
readonly meta: ValueMetadata;
}
type AddCCValueProperties<
TCommandClass extends CommandClasses,
TBlueprint extends CCValueBlueprint,
ValueIDBase extends Record<string, any> = InferValueIDBase<
TCommandClass,
TBlueprint
>,
> = {
/** Returns the value ID of this CC value, without endpoint information */
get id(): ValueIDBase;
/** Returns the value ID, specialized to the given endpoint */
endpoint(
endpoint?: number,
): Readonly<
Pick<ValueIDBase, "commandClass"> & { endpoint: number } & Omit<
ValueIDBase,
"commandClass"
>
>;
/** Whether the given value ID matches this value definition */
is: CCValuePredicate;
/** Returns the metadata for this value ID */
get meta(): Readonly<MergeMeta<NonNullable<TBlueprint["meta"]>>>;
/** Returns the value options for this value ID */
get options(): Readonly<MergeOptions<NonNullable<TBlueprint["options"]>>>;
};
/** A CC value definition which depends on one or more parameters, transparently inferred from its arguments */
export type InferDynamicCCValue<
TCommandClass extends CommandClasses,
TBlueprint extends DynamicCCValueBlueprint<any[]>,
> = TBlueprint extends DynamicCCValueBlueprint<infer TArgs>
? {
(...args: TArgs): Omit<
InferStaticCCValue<TCommandClass, ReturnType<TBlueprint>>,
"options" | "is"
>;
/** Whether the given value ID matches this value definition */
is: CCValuePredicate;
readonly options: InferStaticCCValue<
TCommandClass,
ReturnType<TBlueprint> & { options: TBlueprint["options"] }
>["options"];
}
: never;
/** A static or evaluated CC value definition, transparently inferred from its arguments */
export type InferStaticCCValue<
TCommandClass extends CommandClasses,
TBlueprint extends CCValueBlueprint,
> = Readonly<AddCCValueProperties<TCommandClass, TBlueprint>>;
/** The generic variant of {@link InferStaticCCValue}, without inferring arguments */
export interface StaticCCValue extends CCValue {
/** Whether the given value ID matches this value definition */
is: CCValuePredicate;
readonly options: Required<CCValueOptions>;
}
/** The generic variant of {@link InferDynamicCCValue}, without inferring arguments */
export type DynamicCCValue<TArgs extends any[] = any[]> =
ExpandRecursivelySkipMeta<(...args: TArgs) => CCValue> & {
/** Whether the given value ID matches this value definition */
is: CCValuePredicate;
readonly options: Required<CCValueOptions>;
};
export type StaticCCValueFactory<T> = (self: T) => StaticCCValue;
// This interface is auto-generated by maintenance/generateCCValuesInterface.ts
// Do not edit it by hand or your changes will be lost
export interface CCValues {
// AUTO GENERATION BELOW
"Alarm Sensor": typeof import("../cc/AlarmSensorCC").AlarmSensorCCValues;
Association: typeof import("../cc/AssociationCC").AssociationCCValues;
"Association Group Information": typeof import("../cc/AssociationGroupInfoCC").AssociationGroupInfoCCValues;
"Barrier Operator": typeof import("../cc/BarrierOperatorCC").BarrierOperatorCCValues;
Basic: typeof import("../cc/BasicCC").BasicCCValues;
Battery: typeof import("../cc/BatteryCC").BatteryCCValues;
"Binary Sensor": typeof import("../cc/BinarySensorCC").BinarySensorCCValues;
"Binary Switch": typeof import("../cc/BinarySwitchCC").BinarySwitchCCValues;
"Central Scene": typeof import("../cc/CentralSceneCC").CentralSceneCCValues;
"Climate Control Schedule": typeof import("../cc/ClimateControlScheduleCC").ClimateControlScheduleCCValues;
"Color Switch": typeof import("../cc/ColorSwitchCC").ColorSwitchCCValues;
Configuration: typeof import("../cc/ConfigurationCC").ConfigurationCCValues;
"Door Lock": typeof import("../cc/DoorLockCC").DoorLockCCValues;
"Door Lock Logging": typeof import("../cc/DoorLockLoggingCC").DoorLockLoggingCCValues;
"Entry Control": typeof import("../cc/EntryControlCC").EntryControlCCValues;
"Firmware Update Meta Data": typeof import("../cc/FirmwareUpdateMetaDataCC").FirmwareUpdateMetaDataCCValues;
"Humidity Control Mode": typeof import("../cc/HumidityControlModeCC").HumidityControlModeCCValues;
"Humidity Control Operating State": typeof import("../cc/HumidityControlOperatingStateCC").HumidityControlOperatingStateCCValues;
"Humidity Control Setpoint": typeof import("../cc/HumidityControlSetpointCC").HumidityControlSetpointCCValues;
Indicator: typeof import("../cc/IndicatorCC").IndicatorCCValues;
Irrigation: typeof import("../cc/IrrigationCC").IrrigationCCValues;
Language: typeof import("../cc/LanguageCC").LanguageCCValues;
Lock: typeof import("../cc/LockCC").LockCCValues;
"Manufacturer Specific": typeof import("../cc/ManufacturerSpecificCC").ManufacturerSpecificCCValues;
Meter: typeof import("../cc/MeterCC").MeterCCValues;
"Multi Channel Association": typeof import("../cc/MultiChannelAssociationCC").MultiChannelAssociationCCValues;
"Multi Channel": typeof import("../cc/MultiChannelCC").MultiChannelCCValues;
"Multilevel Sensor": typeof import("../cc/MultilevelSensorCC").MultilevelSensorCCValues;
"Multilevel Switch": typeof import("../cc/MultilevelSwitchCC").MultilevelSwitchCCValues;
"Node Naming and Location": typeof import("../cc/NodeNamingCC").NodeNamingAndLocationCCValues;
Notification: typeof import("../cc/NotificationCC").NotificationCCValues;
Protection: typeof import("../cc/ProtectionCC").ProtectionCCValues;
"Scene Activation": typeof import("../cc/SceneActivationCC").SceneActivationCCValues;
"Scene Actuator Configuration": typeof import("../cc/SceneActuatorConfigurationCC").SceneActuatorConfigurationCCValues;
"Scene Controller Configuration": typeof import("../cc/SceneControllerConfigurationCC").SceneControllerConfigurationCCValues;
"Sound Switch": typeof import("../cc/SoundSwitchCC").SoundSwitchCCValues;
Supervision: typeof import("../cc/SupervisionCC").SupervisionCCValues;
"Thermostat Fan Mode": typeof import("../cc/ThermostatFanModeCC").ThermostatFanModeCCValues;
"Thermostat Fan State": typeof import("../cc/ThermostatFanStateCC").ThermostatFanStateCCValues;
"Thermostat Mode": typeof import("../cc/ThermostatModeCC").ThermostatModeCCValues;
"Thermostat Operating State": typeof import("../cc/ThermostatOperatingStateCC").ThermostatOperatingStateCCValues;
"Thermostat Setback": typeof import("../cc/ThermostatSetbackCC").ThermostatSetbackCCValues;
"Thermostat Setpoint": typeof import("../cc/ThermostatSetpointCC").ThermostatSetpointCCValues;
"Time Parameters": typeof import("../cc/TimeParametersCC").TimeParametersCCValues;
"User Code": typeof import("../cc/UserCodeCC").UserCodeCCValues;
Version: typeof import("../cc/VersionCC").VersionCCValues;
"Wake Up": typeof import("../cc/WakeUpCC").WakeUpCCValues;
"Z-Wave Plus Info": typeof import("../cc/ZWavePlusCC").ZWavePlusCCValues;
}