import { Converter } from '../../Converters.js'; import type { ISYNode } from '../../ISYNode.js'; import type { Driver } from './Drivers.js'; import { UnitOfMeasure } from './UOM.js'; /*export class Command extends Function { constructor(public readonly label: string) { super(); } public async execute(...args: any[]): Promise { return this(...args); } }*/ export interface Parameter { id?: P; name?: string; label?: string; value?: T; uom: UnitOfMeasure; serverUom?: UnitOfMeasure; converter?: (value: T) => T; driver: string; } type ParameterCollection = { [K: string]: Parameter }; function isParameterCollection(value: any): value is ParameterCollection { return value !== undefined && value.name === undefined; } export type Command< C extends string, N = C, L = N, P extends { [K: string]: Parameter } | Parameter = null, > = P extends null ? { id: C; label: L; name: N } : P extends Parameter ? { id: C; label: L; name: N; uom: P['uom']; serverUom?: P['serverUom']; converter?: (value: P['value']) => P['value']; driver: P['driver']; } : P extends ParameterCollection ? { // #region Properties (8) (params: { [x in keyof P]: P[x]['value'] }): Promise; id: C; label: L; name: N; parameters: P; // #endregion Properties (8) // #region Public Methods (2) // #endregion Public Methods (2) //override on('change', listener: (value: any) => void): this; } : never; export namespace Command { export type Signature< F extends { name } = CallableFunction, L extends string = string, N extends string = string, > = F & { label: L; name: N }; export type Signatures = { [K in C]: Signature<(...args: any[]) => Promise, string, string>; }; export type NoExtraKeys = U extends infer F ? F : never; export type NoExtend = F extends T ? F : never; export type For = T extends Signature ? F : T; //type test2 = ForAll> //extends CallableFunction ? Omit | T : never; type test = For<'beep', 'Beep', Commands['BEEP']>; //type test2 = keyof Commands["BEEP"] export type ForAll> = { [K in keyof T]: For; }; function getCommandFunctionSignature< P extends { [x: string]: Parameter } | Parameter | null, >(command: string, node: ISYNode, parameters?: P): Function { if (!parameters) { return () => { return node.sendCommand(command); }; } else if (isParameterCollection(parameters)) { let cmd: any = (params: { [x in keyof P]: (typeof parameters)[x]['value'] }) => { for (let key in params) { let p = parameters[key]; if (p.converter) { params[key] = p.converter(params[key]); } } return node.sendCommand(command, params); }; Object.defineProperty(cmd, 'parameters', {}); for (let key in parameters) { let p = parameters[key]; let srvUom = node.drivers[p.driver]?.serverUom; if (srvUom) { if (srvUom !== p.uom) { cmd.parameters[key].converter = Converter.get(p.uom, srvUom).to; cmd.parameters[key].serverUom = srvUom; } } } return cmd; } else if (parameters.name) { let cmd = function (value): Promise { if (parameters.converter) { value = parameters.converter(value); } return node.sendCommand(command, { value: value }); } as any; let srvUom = node.drivers[parameters.driver]?.serverUom; if (srvUom) { cmd.serverUom = srvUom; cmd.converter = Converter.get(parameters.uom, srvUom).to; } cmd.uom = parameters.uom; return cmd; } } export function create< C extends string, N = string, L = string, P extends ParameterCollection | Parameter | null = null, >(command: C, node: ISYNode, label: L, name: N, parameters: P = null): Command { let cmd: Command = getCommandFunctionSignature(command, node, parameters) as Command; cmd.label = label; cmd.name = name; cmd.id = command; return cmd; } type Commands = { BEEP: ((value: number) => Promise) & { label: 'Beep'; name: 'beep' }; BL: ((value: number) => Promise) & { label: 'Backlight'; name: 'backlight' }; WDU: (() => Promise) & { label: 'Backlight'; name: 'backlight' }; }; }