/** * Switch Feature * Provides binary on/off control for smart switches, outlets, etc. */ import { Feature, type TDeviceReference } from './feature.abstract.js'; import type { IFeatureOptions } from '../interfaces/feature.interfaces.js'; import type { TSwitchProtocol, ISwitchCapabilities, ISwitchState, ISwitchFeatureInfo, ISwitchProtocolClient, } from '../interfaces/smarthome.interfaces.js'; /** * Options for creating a SwitchFeature */ export interface ISwitchFeatureOptions extends IFeatureOptions { /** Protocol type */ protocol: TSwitchProtocol; /** Entity ID (for Home Assistant) */ entityId: string; /** Protocol client for controlling the switch */ protocolClient: ISwitchProtocolClient; /** Device class */ deviceClass?: 'outlet' | 'switch'; } /** * Switch Feature - binary on/off control * * Protocol-agnostic: works with Home Assistant, MQTT, Tasmota, Tuya, etc. * * @example * ```typescript * const sw = device.getFeature('switch'); * if (sw) { * await sw.turnOn(); * await sw.toggle(); * console.log(`Switch is ${sw.isOn ? 'on' : 'off'}`); * } * ``` */ export class SwitchFeature extends Feature { public readonly type = 'switch' as const; public readonly protocol: TSwitchProtocol; /** Entity ID (e.g., "switch.living_room") */ public readonly entityId: string; /** Capabilities */ public readonly capabilities: ISwitchCapabilities; /** Current state */ protected _isOn: boolean = false; /** Protocol client for controlling the switch */ private protocolClient: ISwitchProtocolClient; constructor( device: TDeviceReference, port: number, options: ISwitchFeatureOptions ) { super(device, port, options); this.protocol = options.protocol; this.entityId = options.entityId; this.protocolClient = options.protocolClient; this.capabilities = { deviceClass: options.deviceClass, }; } // ============================================================================ // Properties // ============================================================================ /** * Get current on/off state (cached) */ public get isOn(): boolean { return this._isOn; } // ============================================================================ // Connection // ============================================================================ protected async doConnect(): Promise { // Fetch initial state try { const state = await this.protocolClient.getState(this.entityId); this._isOn = state.isOn; } catch { // Ignore errors fetching initial state } } protected async doDisconnect(): Promise { // Nothing to disconnect } // ============================================================================ // Switch Control // ============================================================================ /** * Turn on the switch */ public async turnOn(): Promise { await this.protocolClient.turnOn(this.entityId); this._isOn = true; this.emit('state:changed', { isOn: true }); } /** * Turn off the switch */ public async turnOff(): Promise { await this.protocolClient.turnOff(this.entityId); this._isOn = false; this.emit('state:changed', { isOn: false }); } /** * Toggle the switch */ public async toggle(): Promise { await this.protocolClient.toggle(this.entityId); this._isOn = !this._isOn; this.emit('state:changed', { isOn: this._isOn }); } /** * Refresh state from the device */ public async refreshState(): Promise { const state = await this.protocolClient.getState(this.entityId); this._isOn = state.isOn; return state; } /** * Update state from external source (e.g., state change event) */ public updateState(state: ISwitchState): void { const changed = this._isOn !== state.isOn; this._isOn = state.isOn; if (changed) { this.emit('state:changed', state); } } // ============================================================================ // Feature Info // ============================================================================ public getFeatureInfo(): ISwitchFeatureInfo { return { ...this.getBaseFeatureInfo(), type: 'switch', protocol: this.protocol, capabilities: this.capabilities, currentState: { isOn: this._isOn, }, }; } }