566 lines
13 KiB
Markdown
566 lines
13 KiB
Markdown
# Home Assistant Node.js API Reference
|
|
|
|
This document provides guidance on using the Home Assistant WebSocket API with Node.js using the `home-assistant-js-websocket` library.
|
|
|
|
**RECOMMENDED APPROACH**: For monitoring entity states, always prefer using `subscribeEntities` from `home-assistant-js-websocket` instead of manually subscribing to `state_changed` events. See [Subscribe to All Entity State Changes](#subscribe-to-all-entity-state-changes).
|
|
|
|
## Installation
|
|
|
|
For Node.js 22+, you only need to install the library (built-in WebSocket support):
|
|
|
|
```bash
|
|
npm install home-assistant-js-websocket
|
|
```
|
|
|
|
For older Node.js versions (< 22), also install the `ws` package:
|
|
|
|
```bash
|
|
npm install home-assistant-js-websocket ws
|
|
```
|
|
|
|
## Authentication with Long-Lived Access Token
|
|
|
|
```javascript
|
|
import {
|
|
createConnection,
|
|
createLongLivedTokenAuth,
|
|
subscribeEntities,
|
|
callService,
|
|
} from "home-assistant-js-websocket";
|
|
|
|
const auth = createLongLivedTokenAuth(
|
|
"http://homeassistant.local:8123",
|
|
"YOUR_LONG_LIVED_ACCESS_TOKEN"
|
|
);
|
|
|
|
const connection = await createConnection({ auth });
|
|
console.log("Connected to Home Assistant!");
|
|
```
|
|
|
|
## Connection Validation
|
|
|
|
### Get Home Assistant Configuration and Version
|
|
|
|
After connecting, you can retrieve configuration information including the Home Assistant version:
|
|
|
|
```javascript
|
|
import { getConfig } from "home-assistant-js-websocket";
|
|
|
|
const config = await getConfig(connection);
|
|
console.log(`Home Assistant Version: ${config.version}`);
|
|
console.log(`Location: ${config.location_name}`);
|
|
console.log(`Time Zone: ${config.time_zone}`);
|
|
console.log(`Components loaded: ${config.components.length}`);
|
|
```
|
|
|
|
The config object contains:
|
|
- `version`: Home Assistant version (e.g., "2024.1.0")
|
|
- `location_name`: Name of the instance
|
|
- `time_zone`: Configured time zone
|
|
- `unit_system`: Units for measurements (length, mass, temperature, volume)
|
|
- `components`: Array of all loaded integrations
|
|
- `latitude`, `longitude`, `elevation`: Location data
|
|
|
|
## Getting Entity States
|
|
|
|
### Subscribe to All Entity State Changes
|
|
|
|
**PREFERRED METHOD**: Use `subscribeEntities` for real-time entity state monitoring.
|
|
|
|
**Function Signature:**
|
|
```typescript
|
|
export const subscribeEntities = (
|
|
conn: Connection,
|
|
onChange: (state: HassEntities) => void,
|
|
): UnsubscribeFunc => entitiesColl(conn).subscribe(onChange);
|
|
```
|
|
|
|
**Why use subscribeEntities:**
|
|
- Automatically maintains a complete, up-to-date map of all entities
|
|
- More efficient than manually tracking state_changed events
|
|
- Handles entity additions, deletions, and updates automatically
|
|
- Provides clean HassEntities object indexed by entity_id
|
|
|
|
**Example:**
|
|
```javascript
|
|
import { subscribeEntities } from "home-assistant-js-websocket";
|
|
|
|
const unsubscribe = subscribeEntities(connection, (entities) => {
|
|
// Called whenever any entity state changes
|
|
// 'entities' is a complete map of all entity states
|
|
console.log("Entities updated:", entities);
|
|
|
|
// Access specific entity by ID
|
|
const light = entities["light.living_room"];
|
|
if (light) {
|
|
console.log(`Light state: ${light.state}`);
|
|
console.log(`Brightness: ${light.attributes.brightness}`);
|
|
}
|
|
|
|
// Monitor multiple entities
|
|
const temp = entities["sensor.temperature"];
|
|
const humidity = entities["sensor.humidity"];
|
|
if (temp && humidity) {
|
|
console.log(`Temp: ${temp.state}°C, Humidity: ${humidity.state}%`);
|
|
}
|
|
});
|
|
|
|
// To stop receiving updates
|
|
// unsubscribe();
|
|
```
|
|
|
|
### Get Current States Once
|
|
|
|
```javascript
|
|
import { getStates } from "home-assistant-js-websocket";
|
|
|
|
const states = await getStates(connection);
|
|
for (const state of states) {
|
|
console.log(`${state.entity_id}: ${state.state}`);
|
|
}
|
|
```
|
|
|
|
### Get Specific Entity State
|
|
|
|
```javascript
|
|
import { getStates } from "home-assistant-js-websocket";
|
|
|
|
const states = await getStates(connection);
|
|
const light = states.find(s => s.entity_id === "light.living_room");
|
|
if (light) {
|
|
console.log(`State: ${light.state}`);
|
|
console.log(`Attributes:`, light.attributes);
|
|
}
|
|
```
|
|
|
|
## Calling Services
|
|
|
|
### Turn on a Light
|
|
|
|
```javascript
|
|
await callService(connection, "light", "turn_on", {
|
|
entity_id: "light.living_room",
|
|
brightness: 255,
|
|
rgb_color: [255, 0, 0], // Red
|
|
});
|
|
```
|
|
|
|
### Turn off a Switch
|
|
|
|
```javascript
|
|
await callService(connection, "switch", "turn_off", {
|
|
entity_id: "switch.bedroom_fan",
|
|
});
|
|
```
|
|
|
|
### Set Thermostat Temperature
|
|
|
|
```javascript
|
|
await callService(connection, "climate", "set_temperature", {
|
|
entity_id: "climate.living_room",
|
|
temperature: 22,
|
|
});
|
|
```
|
|
|
|
### Send Notification
|
|
|
|
```javascript
|
|
await callService(connection, "notify", "notify", {
|
|
message: "Hello from Node.js!",
|
|
title: "Notification Title",
|
|
});
|
|
```
|
|
|
|
### Common Service Patterns
|
|
|
|
```javascript
|
|
// Light control
|
|
await callService(connection, "light", "turn_on", {
|
|
entity_id: "light.bedroom",
|
|
brightness_pct: 50,
|
|
});
|
|
|
|
// Switch control
|
|
await callService(connection, "switch", "toggle", {
|
|
entity_id: "switch.living_room_lamp",
|
|
});
|
|
|
|
// Cover control
|
|
await callService(connection, "cover", "open_cover", {
|
|
entity_id: "cover.garage_door",
|
|
});
|
|
|
|
// Media player control
|
|
await callService(connection, "media_player", "play_media", {
|
|
entity_id: "media_player.living_room",
|
|
media_content_id: "https://example.com/song.mp3",
|
|
media_content_type: "music",
|
|
});
|
|
```
|
|
|
|
## Automation Engine Commands (RECOMMENDED)
|
|
|
|
**IMPORTANT**: These commands form the **core** of how you should interact with Home Assistant. They leverage the automation engine and keep your code minimal by using native Home Assistant syntax.
|
|
|
|
### subscribe_trigger - Listen for Specific Events
|
|
|
|
**PREFERRED METHOD** for listening to specific state changes, time patterns, webhooks, etc.
|
|
|
|
```javascript
|
|
// Subscribe to a state trigger
|
|
const unsubscribe = await connection.subscribeMessage(
|
|
(message) => {
|
|
console.log("Trigger fired!", message);
|
|
console.log("Variables:", message.variables);
|
|
},
|
|
{
|
|
type: "subscribe_trigger",
|
|
trigger: {
|
|
platform: "state",
|
|
entity_id: "binary_sensor.motion_sensor",
|
|
to: "on"
|
|
},
|
|
variables: {
|
|
custom_var: "value"
|
|
}
|
|
}
|
|
);
|
|
|
|
// Unsubscribe when done
|
|
// unsubscribe();
|
|
```
|
|
|
|
**More trigger examples**:
|
|
|
|
```javascript
|
|
// Time pattern trigger
|
|
await connection.subscribeMessage(
|
|
(message) => console.log("Every 5 minutes!", message),
|
|
{
|
|
type: "subscribe_trigger",
|
|
trigger: {
|
|
platform: "time_pattern",
|
|
minutes: "/5"
|
|
}
|
|
}
|
|
);
|
|
|
|
// Numeric state trigger
|
|
await connection.subscribeMessage(
|
|
(message) => console.log("Temperature above 25°C!", message),
|
|
{
|
|
type: "subscribe_trigger",
|
|
trigger: {
|
|
platform: "numeric_state",
|
|
entity_id: "sensor.temperature",
|
|
above: 25
|
|
}
|
|
}
|
|
);
|
|
|
|
// Template trigger
|
|
await connection.subscribeMessage(
|
|
(message) => console.log("Sun is up!", message),
|
|
{
|
|
type: "subscribe_trigger",
|
|
trigger: {
|
|
platform: "template",
|
|
value_template: "{{ states('sun.sun') == 'above_horizon' }}"
|
|
}
|
|
}
|
|
);
|
|
```
|
|
|
|
### test_condition - Test Conditions Server-Side
|
|
|
|
Test conditions without implementing logic in your code:
|
|
|
|
```javascript
|
|
// Test a numeric state condition
|
|
const result = await connection.sendMessagePromise({
|
|
type: "test_condition",
|
|
condition: {
|
|
condition: "numeric_state",
|
|
entity_id: "sensor.temperature",
|
|
above: 20
|
|
}
|
|
});
|
|
|
|
if (result.result) {
|
|
console.log("Temperature is above 20°C");
|
|
}
|
|
```
|
|
|
|
**More condition examples**:
|
|
|
|
```javascript
|
|
// State condition
|
|
const result = await connection.sendMessagePromise({
|
|
type: "test_condition",
|
|
condition: {
|
|
condition: "state",
|
|
entity_id: "light.living_room",
|
|
state: "on"
|
|
}
|
|
});
|
|
|
|
// Time condition
|
|
const result = await connection.sendMessagePromise({
|
|
type: "test_condition",
|
|
condition: {
|
|
condition: "time",
|
|
after: "18:00:00",
|
|
before: "23:00:00"
|
|
}
|
|
});
|
|
|
|
// Template condition
|
|
const result = await connection.sendMessagePromise({
|
|
type: "test_condition",
|
|
condition: {
|
|
condition: "template",
|
|
value_template: "{{ is_state('sun.sun', 'above_horizon') }}"
|
|
}
|
|
});
|
|
|
|
// And/Or conditions
|
|
const result = await connection.sendMessagePromise({
|
|
type: "test_condition",
|
|
condition: {
|
|
condition: "and",
|
|
conditions: [
|
|
{
|
|
condition: "state",
|
|
entity_id: "binary_sensor.motion",
|
|
state: "on"
|
|
},
|
|
{
|
|
condition: "numeric_state",
|
|
entity_id: "sensor.light_level",
|
|
below: 100
|
|
}
|
|
]
|
|
}
|
|
});
|
|
```
|
|
|
|
### execute_script - Execute Multiple Actions
|
|
|
|
**MOST POWERFUL METHOD**: Execute sequences of actions using Home Assistant's native syntax.
|
|
|
|
```javascript
|
|
// Simple sequence
|
|
const result = await connection.sendMessagePromise({
|
|
type: "execute_script",
|
|
sequence: [
|
|
{
|
|
service: "light.turn_on",
|
|
target: { entity_id: "light.living_room" },
|
|
data: { brightness: 255 }
|
|
},
|
|
{
|
|
delay: { seconds: 5 }
|
|
},
|
|
{
|
|
service: "light.turn_off",
|
|
target: { entity_id: "light.living_room" }
|
|
}
|
|
]
|
|
});
|
|
```
|
|
|
|
**Advanced: Using wait_for_trigger**
|
|
|
|
```javascript
|
|
// Turn on light and wait for motion to stop
|
|
const result = await connection.sendMessagePromise({
|
|
type: "execute_script",
|
|
sequence: [
|
|
{
|
|
service: "light.turn_on",
|
|
target: { entity_id: "light.living_room" }
|
|
},
|
|
{
|
|
wait_for_trigger: [
|
|
{
|
|
platform: "state",
|
|
entity_id: "binary_sensor.motion",
|
|
to: "off",
|
|
for: { minutes: 5 }
|
|
}
|
|
],
|
|
timeout: { hours: 2 }
|
|
},
|
|
{
|
|
service: "light.turn_off",
|
|
target: { entity_id: "light.living_room" }
|
|
}
|
|
]
|
|
});
|
|
```
|
|
|
|
**Getting response data from service calls**:
|
|
|
|
```javascript
|
|
// Call a service and get the response
|
|
const result = await connection.sendMessagePromise({
|
|
type: "execute_script",
|
|
sequence: [
|
|
{
|
|
service: "weather.get_forecasts",
|
|
target: { entity_id: "weather.home" },
|
|
data: { type: "daily" },
|
|
response_variable: "weather_data"
|
|
},
|
|
{
|
|
stop: "Done",
|
|
response_variable: "weather_data"
|
|
}
|
|
]
|
|
});
|
|
|
|
console.log("Weather forecast:", result.response_variable);
|
|
```
|
|
|
|
**Complex automation example**:
|
|
|
|
```javascript
|
|
// Full automation logic in execute_script
|
|
const result = await connection.sendMessagePromise({
|
|
type: "execute_script",
|
|
sequence: [
|
|
// Check if it's dark
|
|
{
|
|
condition: "numeric_state",
|
|
entity_id: "sensor.light_level",
|
|
below: 100
|
|
},
|
|
// Turn on lights
|
|
{
|
|
service: "light.turn_on",
|
|
target: { area_id: "living_room" },
|
|
data: { brightness_pct: 50 }
|
|
},
|
|
// Wait for motion to stop for 10 minutes
|
|
{
|
|
wait_for_trigger: [
|
|
{
|
|
platform: "state",
|
|
entity_id: "binary_sensor.motion",
|
|
to: "off",
|
|
for: { minutes: 10 }
|
|
}
|
|
],
|
|
timeout: { hours: 4 }
|
|
},
|
|
// Turn off lights
|
|
{
|
|
service: "light.turn_off",
|
|
target: { area_id: "living_room" }
|
|
}
|
|
]
|
|
});
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
```javascript
|
|
import {
|
|
createConnection,
|
|
createLongLivedTokenAuth,
|
|
ERR_INVALID_AUTH,
|
|
ERR_CONNECTION_LOST,
|
|
} from "home-assistant-js-websocket";
|
|
|
|
try {
|
|
const auth = createLongLivedTokenAuth(url, token);
|
|
const connection = await createConnection({ auth });
|
|
|
|
console.log("Connected successfully!");
|
|
|
|
} catch (err) {
|
|
if (err === ERR_INVALID_AUTH) {
|
|
console.error("Invalid authentication - check your token");
|
|
} else if (err === ERR_CONNECTION_LOST) {
|
|
console.error("Connection lost - check your URL and network");
|
|
} else {
|
|
console.error("Connection failed:", err);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Complete Example
|
|
|
|
```javascript
|
|
import {
|
|
createConnection,
|
|
createLongLivedTokenAuth,
|
|
getConfig,
|
|
subscribeEntities,
|
|
callService,
|
|
} from "home-assistant-js-websocket";
|
|
|
|
async function main() {
|
|
// Connect
|
|
const auth = createLongLivedTokenAuth(
|
|
"http://homeassistant.local:8123",
|
|
"YOUR_TOKEN"
|
|
);
|
|
|
|
const connection = await createConnection({ auth });
|
|
console.log("✓ Connected to Home Assistant");
|
|
|
|
// Get configuration and version
|
|
const config = await getConfig(connection);
|
|
console.log(`✓ Home Assistant ${config.version}`);
|
|
console.log(` Location: ${config.location_name}`);
|
|
|
|
// Subscribe to entity changes
|
|
subscribeEntities(connection, (entities) => {
|
|
const temp = entities["sensor.living_room_temperature"];
|
|
if (temp) {
|
|
console.log(`Temperature: ${temp.state}°C`);
|
|
|
|
// Auto-control based on temperature
|
|
if (parseFloat(temp.state) > 25) {
|
|
callService(connection, "switch", "turn_on", {
|
|
entity_id: "switch.fan",
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
// Call a service
|
|
await callService(connection, "light", "turn_on", {
|
|
entity_id: "light.living_room",
|
|
});
|
|
|
|
console.log("✓ Light turned on");
|
|
}
|
|
|
|
main().catch(console.error);
|
|
```
|
|
|
|
## Using with Node.js < 22
|
|
|
|
For older Node.js versions, configure the WebSocket implementation:
|
|
|
|
```javascript
|
|
import ws from "ws";
|
|
|
|
const connection = await createConnection({
|
|
auth,
|
|
createSocket: (auth) => {
|
|
return new ws(auth.wsUrl, {
|
|
rejectUnauthorized: false, // Only for self-signed certs
|
|
});
|
|
},
|
|
});
|
|
```
|
|
|
|
## Official Documentation
|
|
|
|
For complete library documentation, see:
|
|
- https://github.com/home-assistant/home-assistant-js-websocket
|
|
- https://developers.home-assistant.io/docs/api/websocket/
|