feat(smarthome): add smart home features and Home Assistant integration (WebSocket protocol, discovery, factories, interfaces)

This commit is contained in:
2026-01-09 16:20:54 +00:00
parent 7bcec69658
commit 38a6e5c250
23 changed files with 4786 additions and 5 deletions

View File

@@ -13,16 +13,29 @@ import type { IRetryOptions } from './index.js';
* All supported feature types
*/
export type TFeatureType =
// Document handling
| 'scan' // Can scan documents (eSCL, SANE)
| 'print' // Can print documents (IPP, JetDirect)
| 'fax' // Can send/receive fax
| 'copy' // Can copy (scan + print combined)
// Media playback
| 'playback' // Can play media (audio/video)
| 'volume' // Has volume control
// Infrastructure
| 'power' // Has power status (UPS, smart plug)
| 'snmp' // SNMP queryable
// DLNA
| 'dlna-render' // DLNA renderer
| 'dlna-serve' // DLNA server (content provider)
// Smart home (protocol-agnostic: home-assistant, hue, mqtt, etc.)
| 'light' // Brightness, color, effects
| 'climate' // Temperature, HVAC modes
| 'sensor' // Read-only state values
| 'camera' // Snapshots, streams
| 'cover' // Blinds, garage doors
| 'switch' // Binary on/off
| 'lock' // Lock/unlock
| 'fan' // Speed, oscillation
;
/**

View File

@@ -0,0 +1,666 @@
/**
* Home Assistant Specific Interfaces
* Types for Home Assistant WebSocket API, entities, and configuration
*/
// ============================================================================
// Configuration
// ============================================================================
/**
* Configuration for connecting to a Home Assistant instance
*/
export interface IHomeAssistantInstanceConfig {
/** Home Assistant host (IP or hostname) */
host: string;
/** Port number (default: 8123) */
port?: number;
/** Long-lived access token from HA */
token: string;
/** Use secure WebSocket (wss://) */
secure?: boolean;
/** Friendly name for this instance */
friendlyName?: string;
/** Auto-reconnect on disconnect (default: true) */
autoReconnect?: boolean;
/** Reconnect delay in ms (default: 5000) */
reconnectDelay?: number;
}
/**
* Home Assistant configuration in DeviceManager options
*/
export interface IHomeAssistantOptions {
/** Enable mDNS auto-discovery of HA instances */
autoDiscovery?: boolean;
/** Manually configured HA instances */
instances?: IHomeAssistantInstanceConfig[];
/** Filter: only discover these domains (default: all) */
enabledDomains?: THomeAssistantDomain[];
/** Auto-reconnect on disconnect (default: true) */
autoReconnect?: boolean;
/** Reconnect delay in ms (default: 5000) */
reconnectDelay?: number;
}
// ============================================================================
// Entity Types
// ============================================================================
/**
* Supported Home Assistant domains that map to features
*/
export type THomeAssistantDomain =
| 'light'
| 'switch'
| 'sensor'
| 'binary_sensor'
| 'climate'
| 'fan'
| 'cover'
| 'lock'
| 'camera'
| 'media_player';
/**
* Home Assistant entity state
*/
export interface IHomeAssistantEntity {
/** Entity ID (e.g., "light.living_room") */
entity_id: string;
/** Current state value (e.g., "on", "off", "25.5") */
state: string;
/** Additional attributes */
attributes: IHomeAssistantEntityAttributes;
/** Last changed timestamp */
last_changed: string;
/** Last updated timestamp */
last_updated: string;
/** Context information */
context: IHomeAssistantContext;
}
/**
* Common entity attributes
*/
export interface IHomeAssistantEntityAttributes {
/** Friendly name */
friendly_name?: string;
/** Device class */
device_class?: string;
/** Unit of measurement */
unit_of_measurement?: string;
/** Icon */
icon?: string;
/** Entity category */
entity_category?: string;
/** Assumed state (for optimistic updates) */
assumed_state?: boolean;
/** Supported features bitmask */
supported_features?: number;
/** Additional dynamic attributes */
[key: string]: unknown;
}
/**
* Light-specific attributes
*/
export interface IHomeAssistantLightAttributes extends IHomeAssistantEntityAttributes {
brightness?: number; // 0-255
color_temp?: number; // Mireds
color_temp_kelvin?: number; // Kelvin
hs_color?: [number, number]; // [hue 0-360, saturation 0-100]
rgb_color?: [number, number, number];
xy_color?: [number, number];
rgbw_color?: [number, number, number, number];
rgbww_color?: [number, number, number, number, number];
effect?: string;
effect_list?: string[];
color_mode?: string;
supported_color_modes?: string[];
min_mireds?: number;
max_mireds?: number;
min_color_temp_kelvin?: number;
max_color_temp_kelvin?: number;
}
/**
* Climate-specific attributes
*/
export interface IHomeAssistantClimateAttributes extends IHomeAssistantEntityAttributes {
hvac_modes?: string[];
hvac_action?: string;
current_temperature?: number;
target_temp_high?: number;
target_temp_low?: number;
temperature?: number;
preset_mode?: string;
preset_modes?: string[];
fan_mode?: string;
fan_modes?: string[];
swing_mode?: string;
swing_modes?: string[];
aux_heat?: boolean;
current_humidity?: number;
humidity?: number;
min_temp?: number;
max_temp?: number;
target_temp_step?: number;
min_humidity?: number;
max_humidity?: number;
}
/**
* Sensor-specific attributes
*/
export interface IHomeAssistantSensorAttributes extends IHomeAssistantEntityAttributes {
state_class?: 'measurement' | 'total' | 'total_increasing';
native_unit_of_measurement?: string;
native_value?: string | number;
}
/**
* Cover-specific attributes
*/
export interface IHomeAssistantCoverAttributes extends IHomeAssistantEntityAttributes {
current_position?: number; // 0-100
current_tilt_position?: number; // 0-100
}
/**
* Fan-specific attributes
*/
export interface IHomeAssistantFanAttributes extends IHomeAssistantEntityAttributes {
percentage?: number; // 0-100
percentage_step?: number;
preset_mode?: string;
preset_modes?: string[];
oscillating?: boolean;
direction?: 'forward' | 'reverse';
}
/**
* Lock-specific attributes
*/
export interface IHomeAssistantLockAttributes extends IHomeAssistantEntityAttributes {
is_locked?: boolean;
is_locking?: boolean;
is_unlocking?: boolean;
is_jammed?: boolean;
}
/**
* Camera-specific attributes
*/
export interface IHomeAssistantCameraAttributes extends IHomeAssistantEntityAttributes {
access_token?: string;
entity_picture?: string;
frontend_stream_type?: 'hls' | 'web_rtc';
is_streaming?: boolean;
motion_detection?: boolean;
}
/**
* Media player-specific attributes
*/
export interface IHomeAssistantMediaPlayerAttributes extends IHomeAssistantEntityAttributes {
volume_level?: number; // 0-1
is_volume_muted?: boolean;
media_content_id?: string;
media_content_type?: string;
media_duration?: number;
media_position?: number;
media_position_updated_at?: string;
media_title?: string;
media_artist?: string;
media_album_name?: string;
media_album_artist?: string;
media_track?: number;
media_series_title?: string;
media_season?: number;
media_episode?: number;
app_id?: string;
app_name?: string;
source?: string;
source_list?: string[];
sound_mode?: string;
sound_mode_list?: string[];
shuffle?: boolean;
repeat?: 'off' | 'all' | 'one';
entity_picture_local?: string;
}
/**
* Context for entity state changes
*/
export interface IHomeAssistantContext {
id: string;
parent_id?: string;
user_id?: string;
}
// ============================================================================
// WebSocket Message Types
// ============================================================================
/**
* Base message structure
*/
export interface IHomeAssistantMessage {
id?: number;
type: string;
}
/**
* Authentication required message
*/
export interface IHomeAssistantAuthRequired extends IHomeAssistantMessage {
type: 'auth_required';
ha_version: string;
}
/**
* Authentication message to send
*/
export interface IHomeAssistantAuth extends IHomeAssistantMessage {
type: 'auth';
access_token: string;
}
/**
* Authentication success
*/
export interface IHomeAssistantAuthOk extends IHomeAssistantMessage {
type: 'auth_ok';
ha_version: string;
}
/**
* Authentication invalid
*/
export interface IHomeAssistantAuthInvalid extends IHomeAssistantMessage {
type: 'auth_invalid';
message: string;
}
/**
* Result message
*/
export interface IHomeAssistantResult extends IHomeAssistantMessage {
id: number;
type: 'result';
success: boolean;
result?: unknown;
error?: {
code: string;
message: string;
};
}
/**
* Event message
*/
export interface IHomeAssistantEvent extends IHomeAssistantMessage {
id: number;
type: 'event';
event: {
event_type: string;
data: unknown;
origin: string;
time_fired: string;
context: IHomeAssistantContext;
};
}
/**
* State changed event data
*/
export interface IHomeAssistantStateChangedEvent {
entity_id: string;
old_state: IHomeAssistantEntity | null;
new_state: IHomeAssistantEntity | null;
}
/**
* Subscribe events request
*/
export interface IHomeAssistantSubscribeEvents extends IHomeAssistantMessage {
id: number;
type: 'subscribe_events';
event_type?: string;
}
/**
* Get states request
*/
export interface IHomeAssistantGetStates extends IHomeAssistantMessage {
id: number;
type: 'get_states';
}
/**
* Call service request
*/
export interface IHomeAssistantCallService extends IHomeAssistantMessage {
id: number;
type: 'call_service';
domain: string;
service: string;
target?: {
entity_id?: string | string[];
device_id?: string | string[];
area_id?: string | string[];
};
service_data?: Record<string, unknown>;
}
/**
* Get services request
*/
export interface IHomeAssistantGetServices extends IHomeAssistantMessage {
id: number;
type: 'get_services';
}
/**
* Get config request
*/
export interface IHomeAssistantGetConfig extends IHomeAssistantMessage {
id: number;
type: 'get_config';
}
/**
* Home Assistant config response
*/
export interface IHomeAssistantConfig {
latitude: number;
longitude: number;
elevation: number;
unit_system: {
length: string;
mass: string;
pressure: string;
temperature: string;
volume: string;
};
location_name: string;
time_zone: string;
components: string[];
config_dir: string;
allowlist_external_dirs: string[];
allowlist_external_urls: string[];
version: string;
config_source: string;
safe_mode: boolean;
state: 'NOT_RUNNING' | 'STARTING' | 'RUNNING' | 'STOPPING' | 'FINAL_WRITE';
external_url: string | null;
internal_url: string | null;
currency: string;
country: string;
language: string;
}
// ============================================================================
// Service Definitions
// ============================================================================
/**
* Light service data
*/
export interface IHomeAssistantLightServiceData {
brightness?: number; // 0-255
brightness_pct?: number; // 0-100
brightness_step?: number; // Step to increase/decrease
brightness_step_pct?: number; // Step percentage
color_temp?: number; // Mireds
color_temp_kelvin?: number; // Kelvin
hs_color?: [number, number]; // [hue, saturation]
rgb_color?: [number, number, number];
xy_color?: [number, number];
rgbw_color?: [number, number, number, number];
rgbww_color?: [number, number, number, number, number];
color_name?: string;
kelvin?: number;
effect?: string;
transition?: number; // Seconds
flash?: 'short' | 'long';
profile?: string;
[key: string]: unknown; // Index signature for Record<string, unknown>
}
/**
* Climate service data
*/
export interface IHomeAssistantClimateServiceData {
hvac_mode?: string;
temperature?: number;
target_temp_high?: number;
target_temp_low?: number;
humidity?: number;
fan_mode?: string;
swing_mode?: string;
preset_mode?: string;
aux_heat?: boolean;
[key: string]: unknown; // Index signature for Record<string, unknown>
}
/**
* Cover service data
*/
export interface IHomeAssistantCoverServiceData {
position?: number; // 0-100
tilt_position?: number; // 0-100
}
/**
* Fan service data
*/
export interface IHomeAssistantFanServiceData {
percentage?: number; // 0-100
percentage_step?: number;
preset_mode?: string;
direction?: 'forward' | 'reverse';
oscillating?: boolean;
[key: string]: unknown; // Index signature for Record<string, unknown>
}
/**
* Media player service data
*/
export interface IHomeAssistantMediaPlayerServiceData {
volume_level?: number; // 0-1
is_volume_muted?: boolean;
media_content_id?: string;
media_content_type?: string;
enqueue?: 'play' | 'next' | 'add' | 'replace';
seek_position?: number;
source?: string;
sound_mode?: string;
shuffle?: boolean;
repeat?: 'off' | 'all' | 'one';
}
// ============================================================================
// Discovery Types
// ============================================================================
/**
* Discovered Home Assistant instance via mDNS
*/
export interface IHomeAssistantDiscoveredInstance {
/** Instance ID (derived from host) */
id: string;
/** Host address */
host: string;
/** Port number */
port: number;
/** Base URL */
base_url: string;
/** mDNS TXT records */
txtRecords: Record<string, string>;
/** Whether connection requires token */
requires_api_password: boolean;
/** Friendly name from mDNS */
friendlyName?: string;
}
// ============================================================================
// Protocol Events
// ============================================================================
/**
* Events emitted by HomeAssistantProtocol
*/
export type THomeAssistantProtocolEvents = {
'connected': () => void;
'disconnected': () => void;
'reconnecting': (attempt: number) => void;
'authenticated': (config: IHomeAssistantConfig) => void;
'auth:failed': (message: string) => void;
'state:changed': (event: IHomeAssistantStateChangedEvent) => void;
'states:loaded': (entities: IHomeAssistantEntity[]) => void;
'error': (error: Error) => void;
};
/**
* Events emitted by HomeAssistantDiscovery
*/
export type THomeAssistantDiscoveryEvents = {
'instance:found': (instance: IHomeAssistantDiscoveredInstance) => void;
'instance:lost': (instanceId: string) => void;
'entity:found': (entity: IHomeAssistantEntity) => void;
'entity:updated': (entity: IHomeAssistantEntity) => void;
'entity:removed': (entityId: string) => void;
'error': (error: Error) => void;
};
// ============================================================================
// Helper Types
// ============================================================================
/**
* Extract domain from entity_id
*/
export function getEntityDomain(entityId: string): THomeAssistantDomain | null {
const domain = entityId.split('.')[0];
const validDomains: THomeAssistantDomain[] = [
'light', 'switch', 'sensor', 'binary_sensor', 'climate',
'fan', 'cover', 'lock', 'camera', 'media_player'
];
return validDomains.includes(domain as THomeAssistantDomain)
? domain as THomeAssistantDomain
: null;
}
/**
* Map HA domain to feature type
*/
export function domainToFeatureType(domain: THomeAssistantDomain): string {
const mapping: Record<THomeAssistantDomain, string> = {
'light': 'light',
'switch': 'switch',
'sensor': 'sensor',
'binary_sensor': 'sensor',
'climate': 'climate',
'fan': 'fan',
'cover': 'cover',
'lock': 'lock',
'camera': 'camera',
'media_player': 'playback',
};
return mapping[domain];
}
/**
* Supported light color modes in HA
*/
export type THomeAssistantColorMode =
| 'unknown'
| 'onoff'
| 'brightness'
| 'color_temp'
| 'hs'
| 'xy'
| 'rgb'
| 'rgbw'
| 'rgbww'
| 'white';
/**
* Light supported features bitmask
*/
export const LIGHT_SUPPORT = {
EFFECT: 4,
FLASH: 8,
TRANSITION: 32,
} as const;
/**
* Climate supported features bitmask
*/
export const CLIMATE_SUPPORT = {
TARGET_TEMPERATURE: 1,
TARGET_TEMPERATURE_RANGE: 2,
TARGET_HUMIDITY: 4,
FAN_MODE: 8,
PRESET_MODE: 16,
SWING_MODE: 32,
AUX_HEAT: 64,
} as const;
/**
* Cover supported features bitmask
*/
export const COVER_SUPPORT = {
OPEN: 1,
CLOSE: 2,
SET_POSITION: 4,
STOP: 8,
OPEN_TILT: 16,
CLOSE_TILT: 32,
STOP_TILT: 64,
SET_TILT_POSITION: 128,
} as const;
/**
* Fan supported features bitmask
*/
export const FAN_SUPPORT = {
SET_SPEED: 1,
OSCILLATE: 2,
DIRECTION: 4,
PRESET_MODE: 8,
} as const;
/**
* Lock supported features bitmask
*/
export const LOCK_SUPPORT = {
OPEN: 1,
} as const;
/**
* Media player supported features bitmask
*/
export const MEDIA_PLAYER_SUPPORT = {
PAUSE: 1,
SEEK: 2,
VOLUME_SET: 4,
VOLUME_MUTE: 8,
PREVIOUS_TRACK: 16,
NEXT_TRACK: 32,
TURN_ON: 128,
TURN_OFF: 256,
PLAY_MEDIA: 512,
VOLUME_STEP: 1024,
SELECT_SOURCE: 2048,
STOP: 4096,
CLEAR_PLAYLIST: 8192,
PLAY: 16384,
SHUFFLE_SET: 32768,
SELECT_SOUND_MODE: 65536,
BROWSE_MEDIA: 131072,
REPEAT_SET: 262144,
GROUPING: 524288,
} as const;

View File

@@ -376,3 +376,15 @@ export type TNetworkScannerEvents = {
// ============================================================================
export * from './feature.interfaces.js';
// ============================================================================
// Smart Home Types (Generic, Protocol-agnostic)
// ============================================================================
export * from './smarthome.interfaces.js';
// ============================================================================
// Home Assistant Specific Types
// ============================================================================
export * from './homeassistant.interfaces.js';

View File

@@ -0,0 +1,421 @@
/**
* Smart Home Device Interfaces
* Generic types for smart home features (lights, climate, sensors, etc.)
* Protocol-agnostic - can be implemented by Home Assistant, Hue, MQTT, etc.
*/
import type { TFeatureState, IFeatureInfo } from './feature.interfaces.js';
// ============================================================================
// Light Feature Types
// ============================================================================
export type TLightProtocol = 'home-assistant' | 'hue' | 'mqtt' | 'zigbee';
export interface ILightCapabilities {
supportsBrightness: boolean;
supportsColorTemp: boolean;
supportsRgb: boolean;
supportsHs: boolean; // Hue/Saturation
supportsXy: boolean; // CIE xy color
supportsEffects: boolean;
supportsTransition: boolean;
effects?: string[];
minMireds?: number;
maxMireds?: number;
minColorTempKelvin?: number;
maxColorTempKelvin?: number;
}
export interface ILightState {
isOn: boolean;
brightness?: number; // 0-255
colorTemp?: number; // Kelvin
colorTempMireds?: number; // Mireds (1000000/Kelvin)
rgbColor?: [number, number, number];
hsColor?: [number, number]; // [hue 0-360, saturation 0-100]
xyColor?: [number, number]; // CIE xy
effect?: string;
}
export interface ILightFeatureInfo extends IFeatureInfo {
type: 'light';
protocol: TLightProtocol;
capabilities: ILightCapabilities;
currentState: ILightState;
}
// ============================================================================
// Climate/Thermostat Feature Types
// ============================================================================
export type TClimateProtocol = 'home-assistant' | 'nest' | 'ecobee' | 'mqtt';
export type THvacMode =
| 'off'
| 'heat'
| 'cool'
| 'heat_cool' // Auto dual setpoint
| 'auto'
| 'dry'
| 'fan_only';
export type THvacAction =
| 'off'
| 'heating'
| 'cooling'
| 'drying'
| 'idle'
| 'fan';
export interface IClimateCapabilities {
hvacModes: THvacMode[];
presetModes?: string[]; // 'away', 'eco', 'boost', 'sleep'
fanModes?: string[]; // 'auto', 'low', 'medium', 'high'
swingModes?: string[]; // 'off', 'vertical', 'horizontal', 'both'
supportsTargetTemp: boolean;
supportsTargetTempRange: boolean; // For heat_cool mode
supportsHumidity: boolean;
supportsAuxHeat: boolean;
minTemp: number;
maxTemp: number;
tempStep: number; // Temperature increment (e.g., 0.5, 1)
minHumidity?: number;
maxHumidity?: number;
}
export interface IClimateState {
currentTemp?: number;
targetTemp?: number;
targetTempHigh?: number; // For heat_cool mode
targetTempLow?: number; // For heat_cool mode
hvacMode: THvacMode;
hvacAction?: THvacAction;
presetMode?: string;
fanMode?: string;
swingMode?: string;
humidity?: number;
targetHumidity?: number;
auxHeat?: boolean;
}
export interface IClimateFeatureInfo extends IFeatureInfo {
type: 'climate';
protocol: TClimateProtocol;
capabilities: IClimateCapabilities;
currentState: IClimateState;
}
// ============================================================================
// Sensor Feature Types
// ============================================================================
export type TSensorProtocol = 'home-assistant' | 'mqtt' | 'snmp';
export type TSensorDeviceClass =
| 'temperature'
| 'humidity'
| 'pressure'
| 'illuminance'
| 'battery'
| 'power'
| 'energy'
| 'voltage'
| 'current'
| 'frequency'
| 'gas'
| 'co2'
| 'pm25'
| 'pm10'
| 'signal_strength'
| 'timestamp'
| 'duration'
| 'distance'
| 'speed'
| 'weight'
| 'monetary'
| 'data_size'
| 'data_rate'
| 'water'
| 'irradiance'
| 'precipitation'
| 'precipitation_intensity'
| 'wind_speed';
export type TSensorStateClass =
| 'measurement' // Instantaneous reading
| 'total' // Cumulative total
| 'total_increasing'; // Monotonically increasing total
export interface ISensorCapabilities {
deviceClass?: TSensorDeviceClass;
stateClass?: TSensorStateClass;
unit?: string;
nativeUnit?: string;
precision?: number; // Decimal places
}
export interface ISensorState {
value: string | number | boolean;
numericValue?: number;
unit?: string;
lastUpdated: Date;
}
export interface ISensorFeatureInfo extends IFeatureInfo {
type: 'sensor';
protocol: TSensorProtocol;
capabilities: ISensorCapabilities;
currentState: ISensorState;
}
// ============================================================================
// Camera Feature Types
// ============================================================================
export type TCameraProtocol = 'home-assistant' | 'onvif' | 'rtsp';
export interface ICameraCapabilities {
supportsStream: boolean;
supportsPtz: boolean; // Pan-tilt-zoom
supportsSnapshot: boolean;
supportsMotionDetection: boolean;
frontendStreamType?: 'hls' | 'web_rtc';
streamUrl?: string;
}
export interface ICameraState {
isRecording: boolean;
isStreaming: boolean;
motionDetected: boolean;
}
export interface ICameraFeatureInfo extends IFeatureInfo {
type: 'camera';
protocol: TCameraProtocol;
capabilities: ICameraCapabilities;
currentState: ICameraState;
}
// ============================================================================
// Cover/Blind Feature Types
// ============================================================================
export type TCoverProtocol = 'home-assistant' | 'mqtt' | 'somfy';
export type TCoverDeviceClass =
| 'awning'
| 'blind'
| 'curtain'
| 'damper'
| 'door'
| 'garage'
| 'gate'
| 'shade'
| 'shutter'
| 'window';
export type TCoverState = 'open' | 'opening' | 'closed' | 'closing' | 'stopped' | 'unknown';
export interface ICoverCapabilities {
deviceClass?: TCoverDeviceClass;
supportsOpen: boolean;
supportsClose: boolean;
supportsStop: boolean;
supportsPosition: boolean; // set_cover_position
supportsTilt: boolean; // set_cover_tilt_position
}
export interface ICoverStateInfo {
state: TCoverState;
position?: number; // 0-100, 0 = closed, 100 = fully open
tiltPosition?: number; // 0-100
}
export interface ICoverFeatureInfo extends IFeatureInfo {
type: 'cover';
protocol: TCoverProtocol;
capabilities: ICoverCapabilities;
currentState: ICoverStateInfo;
}
// ============================================================================
// Switch Feature Types
// ============================================================================
export type TSwitchProtocol = 'home-assistant' | 'mqtt' | 'tasmota' | 'tuya';
export type TSwitchDeviceClass = 'outlet' | 'switch';
export interface ISwitchCapabilities {
deviceClass?: TSwitchDeviceClass;
}
export interface ISwitchState {
isOn: boolean;
}
export interface ISwitchFeatureInfo extends IFeatureInfo {
type: 'switch';
protocol: TSwitchProtocol;
capabilities: ISwitchCapabilities;
currentState: ISwitchState;
}
// ============================================================================
// Lock Feature Types
// ============================================================================
export type TLockProtocol = 'home-assistant' | 'mqtt' | 'august' | 'yale';
export type TLockState =
| 'locked'
| 'unlocked'
| 'locking'
| 'unlocking'
| 'jammed'
| 'unknown';
export interface ILockCapabilities {
supportsOpen: boolean; // Physical open (some locks can open the door)
}
export interface ILockStateInfo {
state: TLockState;
isLocked: boolean;
}
export interface ILockFeatureInfo extends IFeatureInfo {
type: 'lock';
protocol: TLockProtocol;
capabilities: ILockCapabilities;
currentState: ILockStateInfo;
}
// ============================================================================
// Fan Feature Types
// ============================================================================
export type TFanProtocol = 'home-assistant' | 'mqtt' | 'bond';
export type TFanDirection = 'forward' | 'reverse';
export interface IFanCapabilities {
supportsSpeed: boolean;
supportsOscillate: boolean;
supportsDirection: boolean;
supportsPresetModes: boolean;
presetModes?: string[];
speedCount?: number; // Number of discrete speed levels
}
export interface IFanState {
isOn: boolean;
percentage?: number; // 0-100 speed
presetMode?: string;
oscillating?: boolean;
direction?: TFanDirection;
}
export interface IFanFeatureInfo extends IFeatureInfo {
type: 'fan';
protocol: TFanProtocol;
capabilities: IFanCapabilities;
currentState: IFanState;
}
// ============================================================================
// Protocol Client Interfaces (for dependency injection)
// ============================================================================
/**
* Light protocol client interface
* Implemented by HomeAssistantProtocol, HueProtocol, etc.
*/
export interface ILightProtocolClient {
turnOn(entityId: string, options?: { brightness?: number; colorTemp?: number; rgb?: [number, number, number]; transition?: number }): Promise<void>;
turnOff(entityId: string, options?: { transition?: number }): Promise<void>;
toggle(entityId: string): Promise<void>;
setBrightness(entityId: string, brightness: number, transition?: number): Promise<void>;
setColorTemp(entityId: string, kelvin: number, transition?: number): Promise<void>;
setRgbColor(entityId: string, r: number, g: number, b: number, transition?: number): Promise<void>;
setEffect(entityId: string, effect: string): Promise<void>;
getState(entityId: string): Promise<ILightState>;
}
/**
* Climate protocol client interface
*/
export interface IClimateProtocolClient {
setHvacMode(entityId: string, mode: THvacMode): Promise<void>;
setTargetTemp(entityId: string, temp: number): Promise<void>;
setTargetTempRange(entityId: string, low: number, high: number): Promise<void>;
setPresetMode(entityId: string, preset: string): Promise<void>;
setFanMode(entityId: string, mode: string): Promise<void>;
setSwingMode(entityId: string, mode: string): Promise<void>;
setAuxHeat(entityId: string, enabled: boolean): Promise<void>;
getState(entityId: string): Promise<IClimateState>;
}
/**
* Sensor protocol client interface (read-only)
*/
export interface ISensorProtocolClient {
getState(entityId: string): Promise<ISensorState>;
}
/**
* Camera protocol client interface
*/
export interface ICameraProtocolClient {
getSnapshot(entityId: string): Promise<Buffer>;
getSnapshotUrl(entityId: string): Promise<string>;
getStreamUrl(entityId: string): Promise<string>;
getState(entityId: string): Promise<ICameraState>;
}
/**
* Cover protocol client interface
*/
export interface ICoverProtocolClient {
open(entityId: string): Promise<void>;
close(entityId: string): Promise<void>;
stop(entityId: string): Promise<void>;
setPosition(entityId: string, position: number): Promise<void>;
setTiltPosition(entityId: string, position: number): Promise<void>;
getState(entityId: string): Promise<ICoverStateInfo>;
}
/**
* Switch protocol client interface
*/
export interface ISwitchProtocolClient {
turnOn(entityId: string): Promise<void>;
turnOff(entityId: string): Promise<void>;
toggle(entityId: string): Promise<void>;
getState(entityId: string): Promise<ISwitchState>;
}
/**
* Lock protocol client interface
*/
export interface ILockProtocolClient {
lock(entityId: string): Promise<void>;
unlock(entityId: string): Promise<void>;
open(entityId: string): Promise<void>; // Physical open if supported
getState(entityId: string): Promise<ILockStateInfo>;
}
/**
* Fan protocol client interface
*/
export interface IFanProtocolClient {
turnOn(entityId: string, percentage?: number): Promise<void>;
turnOff(entityId: string): Promise<void>;
toggle(entityId: string): Promise<void>;
setPercentage(entityId: string, percentage: number): Promise<void>;
setPresetMode(entityId: string, mode: string): Promise<void>;
setOscillating(entityId: string, oscillating: boolean): Promise<void>;
setDirection(entityId: string, direction: TFanDirection): Promise<void>;
getState(entityId: string): Promise<IFanState>;
}