esphome-client
is a comprehensive library that enables you to connect to and communicate with ESPHome devices using their native API protocol. ESPHome is an open-source system for controlling ESP8266/ESP32 microcontrollers using simple yet powerful configuration files and control them remotely through home automation systems.
Why use this library for ESPHome support?
In short - because I use it every day to support a very popular Homebridge plugin named homebridge-ratgdo that I maintain. This library has been extracted and refined from real-world usage to provide a robust foundation for ESPHome communication.
What makes this implementation unique is that it's completely Node-native - there are no external dependencies, no WebAssembly modules, and no native code compilation required. All encryption support is provided by Node.js's built-in crypto module, making installation and deployment straightforward and reliable across all platforms.
The library implements the complete Noise Protocol Framework (specifically Noise_NNpsk0_25519_ChaChaPoly_SHA256) for secure communication with ESPHome devices, with automatic fallback to plaintext connections when encryption is not required or available.
Finally - the most significant reason that you should use this library: it's well-tested in production, it is modern TypeScript, and most importantly, it just works. The library handles all the complexity of the ESPHome native API protocol, including automatic encryption negotiation, entity discovery, and real-time state updates.
Features
- Zero external dependencies - Uses only Node.js built-in modules
- Complete protocol implementation - Full support for all ESPHome entity types and message types
- Secure encryption - Complete Noise Protocol implementation using Node's native crypto
- Automatic encryption negotiation - Seamlessly handles both encrypted and plaintext connections
- Entity discovery - Automatically discovers all entities exposed by the ESPHome device
- Real-time updates - Receive instant telemetry updates for all entity states
- Voice assistant support - Full implementation of ESPHome's voice assistant features
- Type-safe API - Full TypeScript support with comprehensive type definitions
- Production tested - Battle-tested in the popular homebridge-ratgdo plugin
- Protocol-compliant - Carefully verified against the official ESPHome protocol specification
Supported Message Types
✅ Fully Implemented
This library provides complete support for the ESPHome native API protocol:
Core Protocol
HELLO_REQUEST
/HELLO_RESPONSE
- Initial handshake with protocol version verificationCONNECT_REQUEST
/CONNECT_RESPONSE
- Connection establishmentDISCONNECT_REQUEST
/DISCONNECT_RESPONSE
- Clean disconnectionPING_REQUEST
/PING_RESPONSE
- Keep-alive and latency monitoringDEVICE_INFO_REQUEST
/DEVICE_INFO_RESPONSE
- Complete device metadata retrieval
Entity Discovery (All Types Supported)
LIST_ENTITIES_ALARM_CONTROL_PANEL_RESPONSE
- Alarm panel discoveryLIST_ENTITIES_BINARY_SENSOR_RESPONSE
- Binary sensor discoveryLIST_ENTITIES_BUTTON_RESPONSE
- Button discoveryLIST_ENTITIES_CAMERA_RESPONSE
- Camera discoveryLIST_ENTITIES_CLIMATE_RESPONSE
- Climate/HVAC discoveryLIST_ENTITIES_COVER_RESPONSE
- Cover (garage door, blind, etc.) discoveryLIST_ENTITIES_DATE_RESPONSE
- Date entity discoveryLIST_ENTITIES_DATETIME_RESPONSE
- DateTime entity discoveryLIST_ENTITIES_EVENT_RESPONSE
- Event entity discoveryLIST_ENTITIES_FAN_RESPONSE
- Fan discovery with speed and oscillationLIST_ENTITIES_LIGHT_RESPONSE
- Light discovery with effects and color modesLIST_ENTITIES_LOCK_RESPONSE
- Lock discoveryLIST_ENTITIES_MEDIA_PLAYER_RESPONSE
- Media player discoveryLIST_ENTITIES_NUMBER_RESPONSE
- Number entity discoveryLIST_ENTITIES_REQUEST
/LIST_ENTITIES_DONE_RESPONSE
- Entity enumerationLIST_ENTITIES_SELECT_RESPONSE
- Select/dropdown discoveryLIST_ENTITIES_SENSOR_RESPONSE
- Sensor discoveryLIST_ENTITIES_SERVICES_RESPONSE
- User-defined service discoveryLIST_ENTITIES_SWITCH_RESPONSE
- Switch discoveryLIST_ENTITIES_TEXT_RESPONSE
- Text input discoveryLIST_ENTITIES_TEXT_SENSOR_RESPONSE
- Text sensor discoveryLIST_ENTITIES_TIME_RESPONSE
- Time entity discoveryLIST_ENTITIES_UPDATE_RESPONSE
- Update entity discoveryLIST_ENTITIES_VALVE_RESPONSE
- Valve discovery
State Updates (All Types Supported)
ALARM_CONTROL_PANEL_STATE_RESPONSE
- Alarm panel stateBINARY_SENSOR_STATE_RESPONSE
- Binary sensor state updatesBUTTON_STATE_RESPONSE
- Button state (not typically used)CLIMATE_STATE_RESPONSE
- Climate state with modes and temperaturesCOVER_STATE_RESPONSE
- Cover state with position and tiltDATE_STATE_RESPONSE
- Date value updatesDATETIME_STATE_RESPONSE
- DateTime value updatesEVENT_RESPONSE
- Event triggersFAN_STATE_RESPONSE
- Fan state with speed and oscillationLIGHT_STATE_RESPONSE
- Light state with color and effectsLOCK_STATE_RESPONSE
- Lock state updatesMEDIA_PLAYER_STATE_RESPONSE
- Media player stateNUMBER_STATE_RESPONSE
- Number value updatesSELECT_STATE_RESPONSE
- Select option updatesSENSOR_STATE_RESPONSE
- Sensor value updatesSUBSCRIBE_STATES_REQUEST
- Subscribe to state changesSWITCH_STATE_RESPONSE
- Switch state updatesTEXT_SENSOR_STATE_RESPONSE
- Text sensor updatesTEXT_STATE_RESPONSE
- Text value updatesTIME_STATE_RESPONSE
- Time value updatesUPDATE_STATE_RESPONSE
- Update availabilityVALVE_STATE_RESPONSE
- Valve state with position
Commands (All Types Supported)
ALARM_CONTROL_PANEL_COMMAND_REQUEST
- Alarm control with codesBUTTON_COMMAND_REQUEST
- Trigger button actionsCLIMATE_COMMAND_REQUEST
- Climate control (mode, temperature, fan, swing)COVER_COMMAND_REQUEST
- Cover control (open/close/stop, position, tilt)DATE_COMMAND_REQUEST
- Set date valuesDATETIME_COMMAND_REQUEST
- Set datetime valuesFAN_COMMAND_REQUEST
- Fan control (speed, oscillation, direction)LIGHT_COMMAND_REQUEST
- Full light control (on/off, brightness, color, effects)LOCK_COMMAND_REQUEST
- Lock control (lock/unlock with optional code)MEDIA_PLAYER_COMMAND_REQUEST
- Media player controlNUMBER_COMMAND_REQUEST
- Set number valuesSELECT_COMMAND_REQUEST
- Select optionsSWITCH_COMMAND_REQUEST
- Control switchesTEXT_COMMAND_REQUEST
- Set text valuesTIME_COMMAND_REQUEST
- Set time valuesUPDATE_COMMAND_REQUEST
- Trigger updatesVALVE_COMMAND_REQUEST
- Valve control (open/close, position)
Advanced Features
CAMERA_IMAGE_REQUEST
/CAMERA_IMAGE_RESPONSE
- Camera image captureEXECUTE_SERVICE_REQUEST
- Execute user-defined servicesGET_TIME_REQUEST
/GET_TIME_RESPONSE
- Time synchronizationSUBSCRIBE_LOGS_REQUEST
/SUBSCRIBE_LOGS_RESPONSE
- Device log streaming
Voice Assistant Support
SUBSCRIBE_VOICE_ASSISTANT_REQUEST
- Voice assistant subscriptionVOICE_ASSISTANT_ANNOUNCE_REQUEST
/VOICE_ASSISTANT_ANNOUNCE_FINISHED
- AnnouncementsVOICE_ASSISTANT_AUDIO
- Bidirectional audio streamingVOICE_ASSISTANT_CONFIGURATION_REQUEST
/VOICE_ASSISTANT_CONFIGURATION_RESPONSE
- ConfigurationVOICE_ASSISTANT_EVENT_RESPONSE
- Voice assistant eventsVOICE_ASSISTANT_REQUEST
- Voice requests from deviceVOICE_ASSISTANT_RESPONSE
- Voice responses to deviceVOICE_ASSISTANT_SET_CONFIGURATION
- Wake word configurationVOICE_ASSISTANT_TIMER_EVENT_RESPONSE
- Timer events
Security Features
NOISE_ENCRYPTION_SET_KEY_REQUEST
/NOISE_ENCRYPTION_SET_KEY_RESPONSE
- Dynamic key updates
Protocol Compliance
This implementation has been carefully verified against the official ESPHome protocol specification (api.proto
). All field numbers, data types, and message structures exactly match the protocol definition as of v1.12 of the native ESPHome protocol. The library correctly:
- Handles all required and optional fields
- Properly encodes/decodes varint, fixed32, and length-delimited fields
- Avoids all deprecated functionality (e.g., legacy cover commands, deprecated fan speed)
- Correctly implements device_id fields for multi-device support
- Properly handles missing state indicators for sensors
Installation
To use this library in Node, install it from the command line:
npm install esphome-client
Command Line Tool
The package includes espc
, a CLI utility for interacting with ESPHome devices:
# Display device information
espc --host 192.168.1.100 info
# List all entities
espc --host esp-device.local --psk MySecret123 list
# Control entities (auto-detects type)
espc --host 192.168.1.100 control bedroom_light on
espc --host 192.168.1.100 control garage_door open
# Monitor real-time telemetry
espc --host 192.168.1.100 monitor --duration 60
# Interactive mode for exploration
espc --host 192.168.1.100 -i
The CLI supports all ESPHome entity types including switches, lights, covers, fans, locks, climate controls, and more. Use espc --help
for full documentation.
Quick Start
Basic Connection
import { EspHomeClient, LogLevel } from 'esphome-client';
// Create a client with automatic reconnection
const client = new EspHomeClient({
host: '192.168.1.100',
port: 6053, // Default ESPHome API port
reconnect: true,
reconnectInterval: 15000,
connectionTimeout: 30000,
clientId: 'my-app',
logger: console // Or your custom logger
});
// Listen for connection events
client.on('connect', ({ encrypted }) => {
console.log(`Connected to ESPHome device (encrypted: ${encrypted})`);
// Subscribe to device logs
client.subscribeToLogs(LogLevel.INFO);
// Log all discovered entities
client.logAllEntityIds();
});
// Listen for discovered entities
client.on('entities', (entities) => {
console.log('Discovered entities:', entities);
});
// Listen for device information
client.on('deviceInfo', (info) => {
console.log(`Device: ${info.name} v${info.esphomeVersion}`);
console.log(`Model: ${info.model}, MAC: ${info.macAddress}`);
});
// Connect to the device
await client.connect();
Encrypted Connection
// Create a client with encryption
const client = new EspHomeClient({
host: '192.168.1.100',
port: 6053,
encryptionKey: 'your-base64-encoded-32-byte-key', // From your ESPHome YAML api.encryption.key
clientId: 'my-secure-app'
});
// The client will automatically use encryption if the key is provided
// and fall back to plaintext if the device doesn't support it
await client.connect();
Controlling Entities
// After entities are discovered...
// Control switches
await client.sendSwitchCommand('switch-garage_door', true);
// Control lights with full features
await client.sendLightCommand('light-living_room', {
state: true,
brightness: 0.8, // 80% brightness
rgb: { r: 255, g: 0, b: 128 }, // Pink color
colorTemperature: 3500, // Warm white
effect: 'rainbow', // Start effect
transition: 2.0 // 2 second transition
});
// Control covers (garage doors, blinds, etc.)
await client.sendCoverCommand('cover-garage', { command: 'open' });
await client.sendCoverCommand('cover-blind', { position: 0.5, tilt: 0.25 });
// Control climate/HVAC
await client.sendClimateCommand('climate-thermostat', {
mode: ClimateMode.HEAT_COOL,
targetTemperature: 22,
targetTemperatureLow: 20,
targetTemperatureHigh: 24,
fanMode: ClimateFanMode.AUTO
});
// Control fans
await client.sendFanCommand('fan-bedroom', {
state: true,
speedLevel: 75, // 75% speed
oscillating: true,
direction: 'forward'
});
// Control locks
await client.sendLockCommand('lock-front_door', LockCommand.LOCK);
await client.sendLockCommand('lock-front_door', LockCommand.UNLOCK, '1234');
// Control media players
await client.sendMediaPlayerCommand('media_player-living_room', {
command: MediaPlayerCommand.PLAY,
volume: 0.5,
mediaUrl: 'http://example.com/song.mp3'
});
// Execute user-defined services
await client.executeServiceByName('play_rtttl', [
{ stringValue: 'mario:d=4,o=5,b=100:16e6,16e6,32p,8e6' }
]);
Real-time State Monitoring
// Listen to specific entity types
client.on('sensor', (data) => {
if (!data.missingState) {
console.log(`${data.entity}: ${data.state} ${data.unitOfMeasurement || ''}`);
}
});
client.on('binary_sensor', (data) => {
console.log(`${data.entity}: ${data.state ? 'ON' : 'OFF'}`);
});
client.on('climate', (data) => {
console.log(`HVAC: ${ClimateMode[data.mode]}, Current: ${data.currentTemperature}°C, Target: ${data.targetTemperature}°C`);
});
// Listen to all telemetry updates
client.on('telemetry', (data) => {
console.log(`Update from ${data.entity}:`, data);
});
// Monitor device logs
client.on('log', (data) => {
console.log(`[${LogLevel[data.level]}] ${data.message}`);
});
Voice Assistant Integration
// Subscribe to voice assistant
client.subscribeVoiceAssistant(VoiceAssistantSubscribeFlag.API_AUDIO);
// Handle voice assistant requests
client.on('voiceAssistantRequest', (data) => {
if (data.start) {
console.log(`Voice session started: ${data.conversationId}`);
// Start audio streaming
const audioPort = 12345;
client.sendVoiceAssistantResponse(audioPort, false);
}
});
// Send voice assistant events
client.sendVoiceAssistantEvent(VoiceAssistantEvent.WAKE_WORD_START);
client.sendVoiceAssistantEvent(VoiceAssistantEvent.STT_END, [
{ name: 'text', value: 'Turn on the lights' }
]);
// Configure wake words
client.requestVoiceAssistantConfiguration();
client.on('voiceAssistantConfiguration', (config) => {
console.log('Available wake words:', config.availableWakeWords);
client.setVoiceAssistantConfiguration(['alexa', 'hey_google']);
});
Noise Protocol Encryption
This library includes a complete, Node-native implementation of the Noise Protocol Framework, specifically the Noise_NNpsk0_25519_ChaChaPoly_SHA256
handshake pattern used by ESPHome. The implementation:
- Uses only Node.js built-in crypto functions
- Supports X25519 key exchange
- Implements ChaCha20-Poly1305 AEAD encryption
- Handles the complete handshake and transport encryption
- Automatically falls back to plaintext when encryption is not available
Using the Noise Protocol Directly
import { createESPHomeHandshake } from 'esphome-client';
// Create a handshake for ESPHome communication
const handshake = createESPHomeHandshake({
role: 'initiator', // Clients are always initiators
psk: Buffer.from('your-32-byte-psk', 'base64'),
logger: console
});
// Perform the handshake...
const msg1 = handshake.writeMessage();
// Send msg1 to device, receive response...
handshake.readMessage(deviceResponse);
// After handshake completion, use cipher states for encryption
const encrypted = handshake.sendCipher.EncryptWithAd(Buffer.alloc(0), plaintext);
const decrypted = handshake.receiveCipher.DecryptWithAd(Buffer.alloc(0), encrypted);
API Documentation
Complete API documentation is available through TypeDoc. The library provides comprehensive TypeScript definitions for all message types, entity types, and protocol structures.
Key Classes and Interfaces
EspHomeClient
- Main client class for device communication- Event-driven architecture with TypeScript-typed events
- Automatic reconnection and error recovery
- Complete entity discovery and caching
- Type-safe command methods for all entity types
HandshakeState
- Noise protocol handshake implementation- Complete Noise_NNpsk0_25519_ChaChaPoly_SHA256 implementation
- Cipher state management for encrypted communication
- Automatic key derivation and rekeying support
createESPHomeHandshake
- Factory for ESPHome-specific Noise handshakes- Configures correct prologue for ESPHome compatibility
- Simplified API for common use cases
Event Types
All events are fully typed with TypeScript definitions:
- Connection events:
connect
,disconnect
,error
- Discovery events:
entities
,services
,deviceInfo
- State events: One for each entity type (e.g.,
switch
,sensor
,light
) - System events:
log
,heartbeat
,timeSync
- Voice events:
voiceAssistantRequest
,voiceAssistantConfiguration
For a real-world example of this library in action, check out homebridge-ratgdo, which uses this library to provide HomeKit integration for ratgdo garage door controllers.
Protocol Details
The ESPHome native API uses a binary protocol based on Protocol Buffers over TCP (default port 6053). Messages are framed with either:
- Plaintext:
[0x00][length_varint][type_varint][payload]
- Encrypted:
[0x01][size_high][size_low][encrypted_payload]
The library handles all protocol details automatically, including:
- Message framing and parsing
- Varint encoding/decoding
- Protocol buffer field encoding (without requiring protobuf libraries)
- Automatic encryption negotiation
- Connection state management
- Entity discovery and caching
- Proper handling of all field types (varint, fixed32, fixed64, length-delimited)
Contributing
Contributions are welcome! The library has complete protocol support, but there are always opportunities for improvement:
- Testing - Add unit tests and integration tests
- Documentation - Improve examples and API documentation
- Bug fixes - Report and fix any issues you encounter
Please ensure all code follows the existing style and includes appropriate TypeScript types.
Library Development Dashboard
This is mostly of interest to the true developer nerds amongst us.