import type { AxiosInstance } from 'axios'; import axios from 'axios'; import { v4 as uuid } from 'uuid'; import type { ColorInput, Effect, EffectOrInput, Options, Program, ProgramInput, ProgramOrInput, ScheduledEvent, Sequence, SequenceInput, Status, Zone, } from './types'; import { colorHex, validate } from './util'; import { programInputSchema, sequenceInputValidation } from './validation'; import { ZoneHelper } from './zone-helper'; export class EverLights { static isEffect(effectOrInputEffect: EffectOrInput): effectOrInputEffect is Effect { return 'effectType' in effectOrInputEffect; } static normalizeColor(color: ColorInput): string { return colorHex(color); } static normalizePattern(colors: ColorInput[]): string[] { return colors.map((color) => EverLights.normalizeColor(color)); } static normalizeEffect(effect: EffectOrInput): Effect { if (EverLights.isEffect(effect)) { return effect; } return { effectType: effect.type, value: effect.speed, }; } static normalizeEffects(effects: EffectOrInput[]): Effect[] { return effects.map((effect) => EverLights.normalizeEffect(effect)); } static normalizeSequence(sequenceId: string, sequence: SequenceInput | Sequence): Sequence { const groups = sequence.groups ? EverLights.normalizeGroupNames(sequence.groups) : ['Personal/']; return { ...sequence, id: sequenceId, pattern: EverLights.normalizePattern(sequence.pattern), effects: sequence.effects ? EverLights.normalizeEffects(sequence.effects) : [], accountId: sequence.accountId ?? undefined!, lastChanged: new Date().toISOString(), group: groups[0], // this field appears to have no real effect groups, }; } static normalizeGroupName(group: string): string { if (group.toLowerCase() === 'personal' || group === '/' || !group) { return 'Personal/'; } return group.replace(/^\/*(?:Personal\/$)?(.*?)\/*$/i, 'Personal/$1/').replace(/\/+$/, '/'); } static normalizeGroupNames(groups: string[]): string[] { return groups.map((group) => EverLights.normalizeGroupName(group)); } public readonly api: AxiosInstance = axios.create({ baseURL: `http://${this.options.host}/v1`, headers: { Accept: 'application/json', }, }); constructor(private readonly options: Options) {} makeZoneHelper(zoneSerial: string): ZoneHelper { return new ZoneHelper(zoneSerial, this); } async getStatus(): Promise> { return (await this.api.get('')).data; } async getHeartbeat(): Promise> { return (await this.api.get('available')).data; } async getZones(): Promise[]> { return (await this.api.get('zones')).data; } async getZone(zoneSerial: string): Promise> { return (await this.api.get(`zones/${zoneSerial}`)).data; } async getProgram(zoneSerial: string): Promise> { return (await this.api.get(`zones/${zoneSerial}/sequence`)).data; } async startProgram(zoneSerial: string, pattern: ProgramOrInput): Promise>; async startProgram(zoneSerial: string, pattern: ColorInput[], effects?: EffectOrInput[]): Promise>; async startProgram( zoneSerial: string, patternOrProgram: ProgramOrInput | ColorInput[], effects?: EffectOrInput[], ): Promise>; async startProgram( zoneSerial: string, patternOrProgram: ProgramOrInput | ColorInput[], effects: EffectOrInput[] = [], ): Promise> { const input: ProgramInput = Array.isArray(patternOrProgram) ? { pattern: patternOrProgram, effects, } : { pattern: patternOrProgram.pattern, effects: patternOrProgram.effects, }; const validValue = validate(programInputSchema, input); const payload: Program = { pattern: EverLights.normalizePattern(validValue.pattern), effects: EverLights.normalizeEffects(validValue.effects), }; return (await this.api.post(`zones/${zoneSerial}/sequence`, payload)).data; } async stopProgram(zoneSerial: string): Promise { await this.api.delete(`zones/${zoneSerial}/sequence`); } async getSequences(): Promise { return (await this.api.get(`sequences`)).data; } async getSequence(sequenceId: string): Promise> { return (await this.api.get(`sequences/${sequenceId}`)).data; } async createSequence(sequence: Sequence | SequenceInput): Promise> { const validValue = validate(sequenceInputValidation, sequence); const payload: Sequence = EverLights.normalizeSequence(uuid(), validValue); return (await this.api.post(`sequences`, payload)).data; } async updateSequence(sequenceId: string, sequence: Sequence | SequenceInput): Promise> { const validValue = validate(sequenceInputValidation, sequence); const payload: Sequence = EverLights.normalizeSequence(sequenceId, validValue); return (await this.api.put(`sequences/${sequenceId}`, payload)).data; } async deleteSequence(sequenceId: string): Promise { await this.api.delete(`sequences/${sequenceId}`); } async getEvents(): Promise[]> { return (await this.api.get(`events`)).data; } async getEvent(eventId: string): Promise> { return (await this.api.get(`events/${eventId}`)).data; } async createEvent(eventId: string, event: ScheduledEvent): Promise> { return (await this.api.post(`events/${eventId}`, event)).data; } async updateEvent(eventId: string, event: ScheduledEvent): Promise> { return (await this.api.put(`events/${eventId}`, event)).data; } async deleteEvent(eventId: string): Promise { await this.api.delete(`events/${eventId}`); } async updateTime(time: string): Promise { await this.api.put<{ time: string }>(`time`, { time }); } async reboot(): Promise { const str = (await this.api.get('reboot')).data; console.log(str); } }