feat(smarthome): add smart home features and Home Assistant integration (WebSocket protocol, discovery, factories, interfaces)
This commit is contained in:
@@ -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
|
||||
;
|
||||
|
||||
/**
|
||||
|
||||
666
ts/interfaces/homeassistant.interfaces.ts
Normal file
666
ts/interfaces/homeassistant.interfaces.ts
Normal 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;
|
||||
@@ -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';
|
||||
|
||||
421
ts/interfaces/smarthome.interfaces.ts
Normal file
421
ts/interfaces/smarthome.interfaces.ts
Normal 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>;
|
||||
}
|
||||
Reference in New Issue
Block a user