Files
integrations/ts/integrations/tplink/tplink.classes.client.ts
T

94 lines
2.9 KiB
TypeScript

import type {
ITplinkClientCommand,
ITplinkCommandResult,
ITplinkConfig,
ITplinkEvent,
ITplinkSnapshot,
} from './tplink.types.js';
import { TplinkMapper } from './tplink.mapper.js';
type TTplinkEventHandler = (eventArg: ITplinkEvent) => void;
export class TplinkClient {
private readonly events: ITplinkEvent[] = [];
private readonly eventHandlers = new Set<TTplinkEventHandler>();
constructor(private readonly config: ITplinkConfig) {}
public async getSnapshot(): Promise<ITplinkSnapshot> {
return TplinkMapper.toSnapshot(this.config, undefined, this.events);
}
public onEvent(handlerArg: TTplinkEventHandler): () => void {
this.eventHandlers.add(handlerArg);
return () => this.eventHandlers.delete(handlerArg);
}
public async sendCommand(commandArg: ITplinkClientCommand): Promise<ITplinkCommandResult> {
this.emit({
type: 'command_mapped',
command: commandArg,
deviceId: commandArg.deviceId,
entityId: commandArg.entityId,
uniqueId: commandArg.uniqueId,
timestamp: Date.now(),
});
if (this.config.commandExecutor) {
const result = this.commandResult(await this.config.commandExecutor(commandArg), commandArg);
this.emit({
type: result.success ? 'command_executed' : 'command_failed',
command: commandArg,
data: result,
deviceId: commandArg.deviceId,
entityId: commandArg.entityId,
uniqueId: commandArg.uniqueId,
timestamp: Date.now(),
});
return result;
}
const result: ITplinkCommandResult = {
success: false,
error: this.unsupportedLiveControlMessage(),
data: { command: commandArg },
};
this.emit({
type: 'command_failed',
command: commandArg,
data: result,
deviceId: commandArg.deviceId,
entityId: commandArg.entityId,
uniqueId: commandArg.uniqueId,
timestamp: Date.now(),
});
return result;
}
public async destroy(): Promise<void> {
this.eventHandlers.clear();
}
private emit(eventArg: ITplinkEvent): void {
this.events.push(eventArg);
for (const handler of this.eventHandlers) {
handler(eventArg);
}
}
private commandResult(resultArg: unknown, commandArg: ITplinkClientCommand): ITplinkCommandResult {
if (this.isCommandResult(resultArg)) {
return resultArg;
}
return { success: true, data: resultArg ?? { command: commandArg } };
}
private isCommandResult(valueArg: unknown): valueArg is ITplinkCommandResult {
return typeof valueArg === 'object' && valueArg !== null && 'success' in valueArg;
}
private unsupportedLiveControlMessage(): string {
return 'TP-Link Kasa/Tapo live local writes require full python-kasa-equivalent protocol selection and encrypted transports (legacy IOT XOR plus SMART AES/KLAP). This dependency-free TypeScript port is snapshot/manual unless commandExecutor is provided.';
}
}