Files
devicemanager/ts/interfaces/homeassistant.interfaces.ts

667 lines
16 KiB
TypeScript
Raw Permalink Normal View History

/**
* 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;