@webarray/esphome-native-api
Version:
TypeScript/Node.js client for ESPHome native API with encryption and deep sleep support
479 lines • 15.5 kB
TypeScript
/**
* Core types for the ESPHome Native API
*/
/**
* Custom logger interface for integrating with different logging systems
*/
export interface Logger {
(namespace: string, message: string, ...args: any[]): void;
}
/**
* Timer factory for custom timer implementations
* Useful for platforms like Homey that require custom timer handling
*/
export interface TimerFactory {
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
clearTimeout(timeoutId: any): void;
clearInterval(intervalId: any): void;
}
export interface ConnectionOptions {
host: string;
port?: number;
password?: string;
clientInfo?: string;
reconnect?: boolean;
reconnectInterval?: number;
pingInterval?: number;
pingTimeout?: number;
connectTimeout?: number;
encryptionKey?: string;
expectedServerName?: string;
respondToTimeRequests?: boolean;
logger?: Logger;
timerFactory?: TimerFactory;
}
export interface DeviceInfo {
usesPassword: boolean;
name: string;
macAddress: string;
esphomeVersion: string;
compilationTime: string;
model: string;
hasDeepSleep: boolean;
projectName?: string;
projectVersion?: string;
webserverPort?: number;
bluetoothProxyVersion?: number;
manufacturer?: string;
friendlyName?: string;
voiceAssistantVersion?: number;
suggestedArea?: string;
}
export declare enum EntityDomain {
BINARY_SENSOR = "binary_sensor",
COVER = "cover",
FAN = "fan",
LIGHT = "light",
SENSOR = "sensor",
SWITCH = "switch",
TEXT_SENSOR = "text_sensor",
CAMERA = "camera",
CLIMATE = "climate",
NUMBER = "number",
SELECT = "select",
SIREN = "siren",
LOCK = "lock",
BUTTON = "button",
MEDIA_PLAYER = "media_player",
ALARM_CONTROL_PANEL = "alarm_control_panel",
TEXT = "text",
DATE = "date",
TIME = "time",
EVENT = "event",
VALVE = "valve",
DATETIME = "datetime",
UPDATE = "update"
}
export interface EntityInfo {
objectId: string;
key: number;
name: string;
type: EntityDomain;
uniqueId?: string;
disabled?: boolean;
icon?: string;
entityCategory?: EntityCategory;
}
export declare enum EntityCategory {
NONE = 0,
CONFIG = 1,
DIAGNOSTIC = 2
}
export interface BinarySensorInfo extends EntityInfo {
deviceClass?: string;
isStatusBinarySensor?: boolean;
}
export interface SensorInfo extends EntityInfo {
deviceClass?: string;
unitOfMeasurement?: string;
accuracyDecimals?: number;
forceUpdate?: boolean;
stateClass?: StateClass;
}
export declare enum StateClass {
NONE = 0,
MEASUREMENT = 1,
TOTAL_INCREASING = 2,
TOTAL = 3
}
export interface SwitchInfo extends EntityInfo {
assumedState?: boolean;
deviceClass?: string;
}
export interface LightInfo extends EntityInfo {
supportedColorModes?: number[];
minMireds?: number;
maxMireds?: number;
effects?: string[];
}
export interface StateUpdate {
key: number;
state?: any;
missingState?: boolean;
}
export interface BinarySensorState extends StateUpdate {
state: boolean;
}
export interface SensorState extends StateUpdate {
state: number;
}
export interface SwitchState extends StateUpdate {
state: boolean;
}
export interface TextSensorState extends StateUpdate {
state: string;
}
export interface ServiceCall {
key: number;
args: Record<string, any>;
}
/**
* Log entry received from ESPHome device
*/
export interface LogEntry {
level: LogLevel;
message: string;
sendFailed?: boolean;
}
/**
* ESPHome log levels matching the device's logging system
*
* @example
* ```typescript
* import { LogLevel } from 'esphome-native-api';
*
* // Subscribe to INFO level and above
* client.subscribeLogs(LogLevel.INFO);
*
* // Subscribe to DEBUG level for troubleshooting
* client.subscribeLogs(LogLevel.DEBUG);
* ```
*/
export declare enum LogLevel {
/** No logging */
NONE = 0,
/** Error messages only */
ERROR = 1,
/** Warnings and errors */
WARN = 2,
/** Informational messages (default) */
INFO = 3,
/** Configuration messages */
CONFIG = 4,
/** Debug messages */
DEBUG = 5,
/** Verbose debug messages */
VERBOSE = 6,
/** Very verbose debug messages (all logs) */
VERY_VERBOSE = 7
}
/**
* Log level constants for convenient usage
*
* These constants provide easier-to-use names for log levels.
*
* @example
* ```typescript
* import { LOG_LEVEL_INFO, LOG_LEVEL_DEBUG } from 'esphome-native-api';
*
* // Use constants instead of enum
* client.subscribeLogs(LOG_LEVEL_INFO);
* client.subscribeLogs(LOG_LEVEL_DEBUG);
* ```
*/
export declare const LOG_LEVEL_NONE = LogLevel.NONE;
export declare const LOG_LEVEL_ERROR = LogLevel.ERROR;
export declare const LOG_LEVEL_WARN = LogLevel.WARN;
export declare const LOG_LEVEL_INFO = LogLevel.INFO;
export declare const LOG_LEVEL_CONFIG = LogLevel.CONFIG;
export declare const LOG_LEVEL_DEBUG = LogLevel.DEBUG;
export declare const LOG_LEVEL_VERBOSE = LogLevel.VERBOSE;
export declare const LOG_LEVEL_VERY_VERBOSE = LogLevel.VERY_VERBOSE;
export interface DiscoveredDevice {
name: string;
host: string;
port: number;
macAddress?: string;
version?: string;
}
export type MessageHandler<T = any> = (message: T) => void;
export interface ConnectionState {
connected: boolean;
authenticated: boolean;
apiVersion?: {
major: number;
minor: number;
};
serverInfo?: string;
}
export interface HealthMetrics {
isConnected: boolean;
isAuthenticated: boolean;
lastPingTime?: number;
lastPongTime?: number;
pingLatency?: number;
averagePingLatency?: number;
reconnectCount: number;
connectionUptime?: number;
connectionEstablishedAt?: number;
lastDisconnectTime?: number;
lastDisconnectReason?: string;
messagesSent: number;
messagesReceived: number;
bytesReceived: number;
bytesSent: number;
}
export interface ConnectionHealth {
status: 'healthy' | 'degraded' | 'unhealthy' | 'disconnected';
metrics: HealthMetrics;
issues: string[];
}
export declare enum MessageType {
HelloRequest = 1,
HelloResponse = 2,
ConnectRequest = 3,
ConnectResponse = 4,
DisconnectRequest = 5,
DisconnectResponse = 6,
AuthenticationRequest = 3,// Same as ConnectRequest for compatibility
AuthenticationResponse = 4,// Same as ConnectResponse for compatibility
PingRequest = 7,
PingResponse = 8,
DeviceInfoRequest = 9,
DeviceInfoResponse = 10,
ListEntitiesRequest = 11,
ListEntitiesBinarySensorResponse = 12,
ListEntitiesCoverResponse = 13,
ListEntitiesFanResponse = 14,
ListEntitiesLightResponse = 15,
ListEntitiesSensorResponse = 16,
ListEntitiesSwitchResponse = 17,
ListEntitiesTextSensorResponse = 18,
ListEntitiesDoneResponse = 19,
SubscribeStatesRequest = 20,
BinarySensorStateResponse = 21,
CoverStateResponse = 22,
FanStateResponse = 23,
LightStateResponse = 24,
SensorStateResponse = 25,
SwitchStateResponse = 26,
TextSensorStateResponse = 27,
SubscribeLogsRequest = 28,
SubscribeLogsResponse = 29,
CoverCommandRequest = 30,
FanCommandRequest = 31,
LightCommandRequest = 32,
SwitchCommandRequest = 33,
SubscribeHomeassistantServicesRequest = 34,
HomeassistantServiceResponse = 35,
GetTimeRequest = 36,
GetTimeResponse = 37,
SubscribeHomeAssistantStatesRequest = 38,
SubscribeHomeAssistantStateResponse = 39,
HomeAssistantStateResponse = 40,
ListEntitiesServicesResponse = 41,
ExecuteServiceRequest = 42,
ListEntitiesCameraResponse = 43,
CameraImageResponse = 44,
CameraImageRequest = 45,
ListEntitiesClimateResponse = 46,
ClimateStateResponse = 47,
ClimateCommandRequest = 48,
ListEntitiesNumberResponse = 49,
NumberStateResponse = 50,
NumberCommandRequest = 51,
ListEntitiesSelectResponse = 52,
SelectStateResponse = 53,
SelectCommandRequest = 54,
ListEntitiesSirenResponse = 55,
SirenStateResponse = 56,
SirenCommandRequest = 57,
ListEntitiesLockResponse = 58,
LockStateResponse = 59,
LockCommandRequest = 60,
ListEntitiesButtonResponse = 61,
ButtonCommandRequest = 62,
ListEntitiesMediaPlayerResponse = 63,
MediaPlayerStateResponse = 64,
MediaPlayerCommandRequest = 65,
SubscribeBluetoothLEAdvertisementsRequest = 66,
BluetoothLEAdvertisementResponse = 67,
BluetoothDeviceRequest = 68,
BluetoothDeviceConnectionResponse = 69,
BluetoothGATTGetServicesRequest = 70,
BluetoothGATTGetServicesResponse = 71,
BluetoothGATTGetServicesDoneResponse = 72,
BluetoothGATTReadRequest = 73,
BluetoothGATTReadResponse = 74,
BluetoothGATTWriteRequest = 75,
BluetoothGATTReadDescriptorRequest = 76,
BluetoothGATTWriteDescriptorRequest = 77,
BluetoothGATTNotifyRequest = 78,
BluetoothGATTNotifyDataResponse = 79,
SubscribeBluetoothConnectionsFreeRequest = 80,
BluetoothConnectionsFreeResponse = 81,
BluetoothGATTErrorResponse = 82,
BluetoothGATTWriteResponse = 83,
BluetoothGATTNotifyResponse = 84,
BluetoothDevicePairingResponse = 85,
BluetoothDeviceUnpairingResponse = 86,
UnsubscribeBluetoothLEAdvertisementsRequest = 87,
BluetoothDeviceClearCacheResponse = 88,
SubscribeVoiceAssistantRequest = 89,
VoiceAssistantRequest = 90,
VoiceAssistantResponse = 91,
VoiceAssistantEventResponse = 92,
BluetoothLERawAdvertisementsResponse = 93,
ListEntitiesAlarmControlPanelResponse = 94,
AlarmControlPanelStateResponse = 95,
AlarmControlPanelCommandRequest = 96,
ListEntitiesTextResponse = 97,
TextStateResponse = 98,
TextCommandRequest = 99,
ListEntitiesDateResponse = 100,
DateStateResponse = 101,
DateCommandRequest = 102,
ListEntitiesTimeResponse = 103,
TimeStateResponse = 104,
TimeCommandRequest = 105,
VoiceAssistantAudioSettings = 106,
ListEntitiesEventResponse = 107,
EventResponse = 108,
ListEntitiesValveResponse = 109,
ValveStateResponse = 110,
ValveCommandRequest = 111,
ListEntitiesDateTimeResponse = 112,
DateTimeStateResponse = 113,
DateTimeCommandRequest = 114,
ListEntitiesUpdateResponse = 115,
UpdateStateResponse = 116,
UpdateCommandRequest = 117
}
export declare enum ErrorCode {
CONNECTION_TIMEOUT = "CONNECTION_TIMEOUT",
CONNECTION_REFUSED = "CONNECTION_REFUSED",
CONNECTION_RESET = "CONNECTION_RESET",
CONNECTION_LOST = "CONNECTION_LOST",
NETWORK_ERROR = "NETWORK_ERROR",
INVALID_PASSWORD = "INVALID_PASSWORD",
INVALID_ENCRYPTION_KEY = "INVALID_ENCRYPTION_KEY",
AUTHENTICATION_REQUIRED = "AUTHENTICATION_REQUIRED",
AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED",
PROTOCOL_VERSION_MISMATCH = "PROTOCOL_VERSION_MISMATCH",
INVALID_MESSAGE = "INVALID_MESSAGE",
HANDSHAKE_FAILED = "HANDSHAKE_FAILED",
UNEXPECTED_MESSAGE = "UNEXPECTED_MESSAGE",
ENTITY_NOT_FOUND = "ENTITY_NOT_FOUND",
INVALID_ENTITY_STATE = "INVALID_ENTITY_STATE",
TIMEOUT = "TIMEOUT",
INVALID_ARGUMENT = "INVALID_ARGUMENT",
UNKNOWN_ERROR = "UNKNOWN_ERROR"
}
export declare class ESPHomeError extends Error {
readonly code: ErrorCode;
readonly suggestion?: string;
readonly context?: Record<string, any>;
constructor(message: string, code?: ErrorCode, suggestion?: string, context?: Record<string, any>);
toString(): string;
}
export declare class ConnectionError extends ESPHomeError {
constructor(message: string, code?: ErrorCode, suggestion?: string, context?: Record<string, any>);
static timeout(host: string, port: number, timeout: number): ConnectionError;
static refused(host: string, port: number): ConnectionError;
static reset(host: string, port: number): ConnectionError;
static lost(host: string, port: number): ConnectionError;
}
export declare class AuthenticationError extends ESPHomeError {
constructor(message: string, code?: ErrorCode, suggestion?: string, context?: Record<string, any>);
static invalidPassword(): AuthenticationError;
static invalidEncryptionKey(): AuthenticationError;
static required(): AuthenticationError;
}
export declare class ProtocolError extends ESPHomeError {
constructor(message: string, code?: ErrorCode, suggestion?: string, context?: Record<string, any>);
static versionMismatch(clientVersion: string, deviceVersion: string): ProtocolError;
static invalidMessage(messageType: number): ProtocolError;
static handshakeFailed(reason: string): ProtocolError;
}
export declare class EntityError extends ESPHomeError {
constructor(message: string, code?: ErrorCode, suggestion?: string, context?: Record<string, any>);
static notFound(entityKey: number | string): EntityError;
}
export declare class TimeoutError extends ESPHomeError {
constructor(message: string, timeout: number, context?: Record<string, any>);
}
export type { EntityType, EntityTypeMap, EntityStateMap, EntityInfoByType, EntityStateByType, EntityWithType, AnyEntity, } from './generated-entity-types';
export { ALL_ENTITY_TYPES, isValidEntityType } from './generated-entity-types';
/**
* Type guard to check if an entity is of a specific type
* Note: This is a type-level utility. Actual runtime type checking
* would require additional type markers in the entity objects.
*/
export declare function isEntityType<T extends import('./generated-entity-types').EntityType>(_entity: EntityInfo, _type: T): _entity is import('./generated-entity-types').EntityInfoByType<T>;
/**
* Type-safe entity filter result
*/
export type EntitiesOfType<T extends import('./generated-entity-types').EntityType> = Array<import('./generated-entity-types').EntityInfoByType<T>>;
/**
* Strongly typed connection options with strict validation
*/
export type StrictConnectionOptions = Required<Pick<ConnectionOptions, 'host'>> & Partial<Omit<ConnectionOptions, 'host'>>;
/**
* Extract keys from entity info that have specific types
*/
export type EntityKeys = Pick<EntityInfo, 'key' | 'objectId' | 'name' | 'uniqueId'>;
/**
* Make certain properties required
*/
export type RequiredKeys<T, K extends keyof T> = T & Required<Pick<T, K>>;
/**
* Exclude keys from a type
*/
export type OmitStrict<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
/**
* Deep partial type
*/
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
/**
* Deep readonly type
*/
export type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
/**
* Extract promise type
*/
export type Awaited<T> = T extends Promise<infer U> ? U : T;
/**
* Non-nullable type helper
*/
export type NonNullableFields<T> = {
[P in keyof T]: NonNullable<T[P]>;
};
/**
* Type predicate for connection state
*/
export declare function isConnected(state: ConnectionState): state is ConnectionState & {
connected: true;
};
/**
* Type predicate for authenticated state
*/
export declare function isAuthenticated(state: ConnectionState): state is ConnectionState & {
authenticated: true;
connected: true;
};
//# sourceMappingURL=index.d.ts.map