import { isTruly, isFunction, rgb2hsv } from '@lumiastream/lumia-rgb-utils'; class SuperState { protected _values: Record = {}; // In Milliseconds // 0.0 – infinity seconds transition = (value: number) => { this._values['transition_period'] = value; return this; }; duration = this.transition; getValues = () => { return this._values; }; } export class TapoLightState extends SuperState { constructor(values?: { on: boolean }) { super(); if (values) this.create(values); } create = (values?: { [x: string]: any }): this => { if (values) { Object.keys(values).forEach((value: any) => { let fn; if (this.hasOwnProperty(value)) { fn = (this as any)[value]; if (isFunction(fn) && isTruly(values[value])) { fn.apply(this, [values[value]]); } } }); } return this; }; on = (value = true): this => { this._values['device_on'] = value; return this; }; turnOn = this.on; off = (): this => { this.on(false); return this; }; turnOff = this.off; mode = (value = 'normal'): this => { if (value === 'normal') { this._values['color_temp'] = 0; } this._values['mode'] = value; return this; }; // 0 - 360 hue = (value: number): this => { this._values['hue'] = value; this._values['color_temp'] = 0; return this; }; // Bri 0-100 bri = (value: number): this => { this._values['brightness'] = value; return this; }; brightness = this.bri; sat = (value: number): this => { this._values['saturation'] = value; return this; }; saturation = this.sat; // Color Temperature 2500 - 9000 in kelvin // Will override hue if passed temp = (value: number): this => { this._values['color_temp'] = value; return this; }; colorTemperature = this.temp; // Can take in an array or object hsv = (value: [number, number, number] | { h: number; s: number; v: number }) => { let convertedHsv: any[] = []; if (Array.isArray(value)) { convertedHsv = value; } else { convertedHsv = [value.h, value.s, value.v]; } this.hue(convertedHsv[0]); this.sat(convertedHsv[1]); this.bri(convertedHsv[2]); return this; }; // Can take in an array or object rgb = (value: [number, number, number] | { r: number; g: number; b: number }): this => { let hsv; if (Array.isArray(value)) { hsv = rgb2hsv(value); } else { hsv = rgb2hsv([value.r, value.g, value.b]); } return this.hsv(hsv); }; // Can take in an object will first check for ct and if it exists and the light will worki with it it'll use color temp color = (value: { ct?: number; r?: number; g?: number; b?: number }): this => { if (Array.isArray(value)) { value = { r: value[0], g: value[1], b: value[2] }; } if (value.ct) { return this.temp(value.ct); } else { return this.rgb({ r: value.r!, g: value.g!, b: value.b! }); } }; // HSL color control with value normalization hsl = (h: number, s: number, l: number): this => { // Normalize values to expected ranges const normalizedH = Math.max(0, Math.min(360, h)); const normalizedS = Math.max(0, Math.min(100, s)); const normalizedL = Math.max(0, Math.min(100, l)); // Convert HSL to HSV for internal representation const hsv = this.hslToHsv(normalizedH, normalizedS / 100, normalizedL / 100); return this.hsv([hsv.h, hsv.s * 100, hsv.v * 100]); }; // HSL to HSV conversion helper private hslToHsv(h: number, s: number, l: number): { h: number; s: number; v: number } { const v = l + s * Math.min(l, 1 - l); const sNew = v === 0 ? 0 : 2 * (1 - l / v); return { h: h, s: sNew, v: v }; } // Preset color methods white = (): this => { this._values['color_temp'] = 2700; // Warm white this._values['hue'] = 0; this._values['saturation'] = 0; return this; }; red = (): this => { return this.hsl(0, 100, 50); }; green = (): this => { return this.hsl(120, 100, 50); }; blue = (): this => { return this.hsl(240, 100, 50); }; yellow = (): this => { return this.hsl(60, 100, 50); }; orange = (): this => { return this.hsl(30, 100, 50); }; purple = (): this => { return this.hsl(270, 100, 50); }; pink = (): this => { return this.hsl(330, 100, 70); }; }