ha-api
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHome Assistant API Integration
Home Assistant API集成
Master Home Assistant's REST and WebSocket APIs for external integration, state management, and real-time communication.
掌握Home Assistant的REST和WebSocket API,实现外部集成、状态管理与实时通信。
⚠️ BEFORE YOU START
⚠️ 开始之前
This skill prevents 5 common API integration errors and saves ~30% token overhead.
| Aspect | Details |
|---|---|
| Common Errors Prevented | 5+ (auth, WebSocket lifecycle, state format, error handling) |
| Token Savings | ~30% vs. manual API discovery |
| Setup Time | 2-5 minutes vs. 15-20 minutes manual |
该技能可避免5种常见API集成错误,减少约30%的令牌开销。
| 方面 | 详情 |
|---|---|
| 可避免的常见错误 | 5种以上(认证、WebSocket生命周期、状态格式、错误处理等) |
| 令牌开销节省 | 相比手动API发现减少约30% |
| 设置时间 | 2-5分钟(手动设置需15-20分钟) |
Known Issues This Skill Prevents
该技能可避免的已知问题
- Incorrect authentication headers - Bearer tokens must be prefixed with "Bearer " in Authorization header
- WebSocket lifecycle management - Missing auth_required handling or improper state subscription
- State format mismatches - Confusing state vs. attributes; incorrect JSON payload structure
- Error response handling - Not distinguishing between 4xx client errors and 5xx server errors
- Service call domain/service mismatch - Incorrect routing to service endpoints
- 认证头错误 - Bearer令牌必须在Authorization头中添加前缀“Bearer ”
- WebSocket生命周期管理问题 - 缺失auth_required处理或状态订阅不当
- 状态格式不匹配 - 混淆状态与属性;JSON负载结构错误
- 错误响应处理不当 - 未区分4xx客户端错误与5xx服务器错误
- 服务调用的域/服务不匹配 - 服务端点路由错误
Quick Start
快速开始
Step 1: Authentication Setup
步骤1:认证设置
bash
undefinedbash
undefinedIn Home Assistant UI:
In Home Assistant UI:
Settings → My Home → Create Long-Lived Access Token
Settings → My Home → Create Long-Lived Access Token
Store token securely (never commit to git)
Store token securely (never commit to git)
export HA_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
export HA_URL="http://192.168.1.100:8123"
**Why this matters:** All API requests require Bearer token authentication. Creating a dedicated token for external apps allows you to revoke access without changing your password.export HA_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
export HA_URL="http://192.168.1.100:8123"
**重要性说明:** 所有API请求都需要Bearer令牌认证。为外部应用创建专用令牌可让您在不修改密码的情况下撤销访问权限。Step 2: Test REST API Connectivity
步骤2:测试REST API连通性
bash
undefinedbash
undefinedGet all entity states
Get all entity states
curl -X GET "${HA_URL}/api/states"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
**Why this matters:** Verifies your Home Assistant instance is accessible and your token is valid before building complex integrations.curl -X GET "${HA_URL}/api/states"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
**重要性说明:** 在构建复杂集成之前,验证您的Home Assistant实例是否可访问以及令牌是否有效。Step 3: Choose API Type
步骤3:选择API类型
- REST API: For one-time requests, state queries, service calls (HTTP polling)
- WebSocket API: For real-time events, continuous subscriptions, lower latency (~50ms vs. seconds)
Why this matters: Different use cases require different APIs. WebSocket excels at real-time apps; REST is simpler for occasional requests.
- REST API:适用于一次性请求、状态查询、服务调用(HTTP轮询)
- WebSocket API:适用于实时事件、持续订阅、低延迟场景(延迟约50ms,相比REST的数秒)
重要性说明: 不同的使用场景需要不同的API。WebSocket擅长实时应用;REST则更适合偶尔的请求。
Critical Rules
关键规则
✅ Always Do
✅ 务必遵守
- ✅ Store access tokens in environment variables or secure vaults (never hardcode)
- ✅ Include "Bearer " prefix in Authorization header (exact case and spacing required)
- ✅ Validate WebSocket auth_required messages before sending other commands
- ✅ Handle HTTP errors (401 = token invalid/expired, 404 = entity not found, 502 = HA unavailable)
- ✅ Specify full domain/service for service calls (e.g., "light/turn_on" not just "turn_on")
- ✅ 将访问令牌存储在环境变量或安全密钥管理库中(切勿硬编码)
- ✅ 在Authorization头中包含“Bearer ”前缀(大小写和空格必须完全匹配)
- ✅ 在发送其他命令之前验证WebSocket的auth_required消息
- ✅ 处理HTTP错误(401 = 令牌无效/过期,404 = 实体不存在,502 = Home Assistant不可用)
- ✅ 服务调用时指定完整的域/服务(例如“light/turn_on”而非仅“turn_on”)
❌ Never Do
❌ 切勿操作
- ❌ Commit access tokens to git or share in logs
- ❌ Skip the initial WebSocket auth handshake
- ❌ Mix Bearer token authentication with username/password
- ❌ Assume state values are always strings (can be "on", 123, or null)
- ❌ Call service endpoints with entity_id as path parameter (use JSON payload instead)
- ❌ 将访问令牌提交到git或在日志中共享
- ❌ 跳过初始WebSocket认证握手
- ❌ 将Bearer令牌认证与用户名/密码混合使用
- ❌ 假设状态值始终为字符串(可以是“on”、123或null)
- ❌ 使用entity_id作为路径参数调用服务端点(改用JSON负载)
Common Mistakes
常见错误示例
❌ Wrong - Missing Bearer prefix:
bash
curl -X GET "http://ha:8123/api/states" \
-H "Authorization: ${HA_TOKEN}" # Missing "Bearer "✅ Correct - Bearer prefix required:
bash
curl -X GET "http://ha:8123/api/states" \
-H "Authorization: Bearer ${HA_TOKEN}"Why: Home Assistant's API uses standard Bearer token authentication. The "Bearer " prefix tells the server this is a token-based auth scheme, not a username/password.
❌ 错误示例 - 缺失Bearer前缀:
bash
curl -X GET "http://ha:8123/api/states" \
-H "Authorization: ${HA_TOKEN}" # Missing "Bearer "✅ 正确示例 - 必须添加Bearer前缀:
bash
curl -X GET "http://ha:8123/api/states" \
-H "Authorization: Bearer ${HA_TOKEN}"原因: Home Assistant的API使用标准Bearer令牌认证。“Bearer ”前缀告知服务器这是基于令牌的认证方案,而非用户名/密码认证。
REST API Endpoints Reference
REST API端点参考
States Endpoint
状态端点
Get all states:
bash
GET /api/states
Authorization: Bearer {token}Response (200 OK):
json
[
{
"entity_id": "light.living_room",
"state": "on",
"attributes": {
"brightness": 255,
"color_mode": "color_temp",
"friendly_name": "Living Room Light"
},
"last_changed": "2025-12-31T18:00:00+00:00",
"last_updated": "2025-12-31T18:05:00+00:00"
}
]Get single entity state:
bash
GET /api/states/{entity_id}
Authorization: Bearer {token}Create/update entity state:
bash
POST /api/states/{entity_id}
Authorization: Bearer {token}
Content-Type: application/json
{
"state": "on",
"attributes": {
"friendly_name": "Custom Entity",
"custom_attribute": "value"
}
}Response (201 Created or 200 OK):
json
{
"entity_id": "sensor.custom_sensor",
"state": "on",
"attributes": { ... }
}获取所有状态:
bash
GET /api/states
Authorization: Bearer {token}响应(200 OK):
json
[
{
"entity_id": "light.living_room",
"state": "on",
"attributes": {
"brightness": 255,
"color_mode": "color_temp",
"friendly_name": "Living Room Light"
},
"last_changed": "2025-12-31T18:00:00+00:00",
"last_updated": "2025-12-31T18:05:00+00:00"
}
]获取单个实体状态:
bash
GET /api/states/{entity_id}
Authorization: Bearer {token}创建/更新实体状态:
bash
POST /api/states/{entity_id}
Authorization: Bearer {token}
Content-Type: application/json
{
"state": "on",
"attributes": {
"friendly_name": "Custom Entity",
"custom_attribute": "value"
}
}响应(201 Created或200 OK):
json
{
"entity_id": "sensor.custom_sensor",
"state": "on",
"attributes": { ... }
}Services Endpoint
服务端点
Get all available services:
bash
GET /api/services
Authorization: Bearer {token}Response (200 OK):
json
[
{
"domain": "light",
"services": {
"turn_on": {
"description": "Turn on light(s)",
"fields": {
"entity_id": {
"description": "The entity_id of the light(s)",
"example": ["light.living_room", "light.bedroom"]
},
"brightness": {
"description": "Brightness 0-255",
"example": 180
}
}
},
"turn_off": { ... }
}
}
]Call a service:
bash
POST /api/services/{domain}/{service}
Authorization: Bearer {token}
Content-Type: application/json
{
"entity_id": "light.living_room",
"brightness": 180,
"transition": 2
}Response (200 OK):
json
[
{
"entity_id": "light.living_room",
"state": "on",
"attributes": { ... }
}
]获取所有可用服务:
bash
GET /api/services
Authorization: Bearer {token}响应(200 OK):
json
[
{
"domain": "light",
"services": {
"turn_on": {
"description": "Turn on light(s)",
"fields": {
"entity_id": {
"description": "The entity_id of the light(s)",
"example": ["light.living_room", "light.bedroom"]
},
"brightness": {
"description": "Brightness 0-255",
"example": 180
}
}
},
"turn_off": { ... }
}
}
]调用服务:
bash
POST /api/services/{domain}/{service}
Authorization: Bearer {token}
Content-Type: application/json
{
"entity_id": "light.living_room",
"brightness": 180,
"transition": 2
}响应(200 OK):
json
[
{
"entity_id": "light.living_room",
"state": "on",
"attributes": { ... }
}
]Events Endpoint
事件端点
Get all events:
bash
GET /api/events
Authorization: Bearer {token}Fire an event:
bash
POST /api/events/{event_type}
Authorization: Bearer {token}
Content-Type: application/json
{
"custom_data": "value"
}获取所有事件:
bash
GET /api/events
Authorization: Bearer {token}触发事件:
bash
POST /api/events/{event_type}
Authorization: Bearer {token}
Content-Type: application/json
{
"custom_data": "value"
}History Endpoint
历史记录端点
Get entity history:
bash
GET /api/history/period/{timestamp}?filter_entity_id={entity_id}
Authorization: Bearer {token}Response (200 OK):
json
[
[
{
"entity_id": "sensor.temperature",
"state": "22.5",
"attributes": { ... },
"last_changed": "2025-12-31T12:00:00+00:00"
}
]
]获取实体历史记录:
bash
GET /api/history/period/{timestamp}?filter_entity_id={entity_id}
Authorization: Bearer {token}响应(200 OK):
json
[
[
{
"entity_id": "sensor.temperature",
"state": "22.5",
"attributes": { ... },
"last_changed": "2025-12-31T12:00:00+00:00"
}
]
]Config Endpoint
配置端点
Get Home Assistant configuration:
bash
GET /api/config
Authorization: Bearer {token}Response (200 OK):
json
{
"latitude": 52.3,
"longitude": 4.9,
"elevation": 0,
"unit_system": {
"length": "km",
"mass": "kg",
"temperature": "°C",
"volume": "L"
},
"time_zone": "Europe/Amsterdam",
"components": ["light", "switch", "sensor", ...]
}获取Home Assistant配置:
bash
GET /api/config
Authorization: Bearer {token}响应(200 OK):
json
{
"latitude": 52.3,
"longitude": 4.9,
"elevation": 0,
"unit_system": {
"length": "km",
"mass": "kg",
"temperature": "°C",
"volume": "L"
},
"time_zone": "Europe/Amsterdam",
"components": ["light", "switch", "sensor", ...]
}Template Endpoint
模板端点
Render a template:
bash
POST /api/template
Authorization: Bearer {token}
Content-Type: application/json
{
"template": "{{ states('sensor.temperature') }}"
}Response (200 OK):
json
{
"template": "{{ states('sensor.temperature') }}",
"result": "22.5"
}渲染模板:
bash
POST /api/template
Authorization: Bearer {token}
Content-Type: application/json
{
"template": "{{ states('sensor.temperature') }}"
}响应(200 OK):
json
{
"template": "{{ states('sensor.temperature') }}",
"result": "22.5"
}WebSocket API Reference
WebSocket API参考
Connection Flow
连接流程
- Open WebSocket connection to
/api/websocket - Receive auth_required message (must respond within 10 seconds)
- Send auth message with token
- Receive auth_ok confirmation
- Send subscriptions/commands
- Receive responses and events in real-time
- 打开到的WebSocket连接
/api/websocket - 接收auth_required消息(必须在10秒内响应)
- 发送带令牌的认证消息
- 接收auth_ok确认
- 发送订阅/命令
- 实时接收响应与事件
WebSocket Commands
WebSocket命令
Authentication:
javascript
// Message 1: Server sends (automatically)
{
"type": "auth_required",
"ha_version": "2025.1.0"
}
// Message 2: Client responds
{
"type": "auth",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
// Message 3: Server confirms
{
"type": "auth_ok",
"ha_version": "2025.1.0"
}Subscribe to events:
javascript
{
"id": 1,
"type": "subscribe_events",
"event_type": "state_changed"
}
// Responses come as:
{
"id": 1,
"type": "event",
"event": {
"type": "state_changed",
"data": {
"entity_id": "light.living_room",
"old_state": { "state": "off", ... },
"new_state": { "state": "on", ... }
}
}
}Call a service:
javascript
{
"id": 2,
"type": "call_service",
"domain": "light",
"service": "turn_on",
"service_data": {
"entity_id": "light.living_room",
"brightness": 200
}
}
// Response:
{
"id": 2,
"type": "result",
"success": true,
"result": [
{
"entity_id": "light.living_room",
"state": "on",
"attributes": { ... }
}
]
}Get current states:
javascript
{
"id": 3,
"type": "get_states"
}
// Response:
{
"id": 3,
"type": "result",
"success": true,
"result": [ ... ] // Array of all entity states
}Subscribe to specific trigger:
javascript
{
"id": 4,
"type": "subscribe_trigger",
"trigger": {
"platform": "state",
"entity_id": "light.living_room"
}
}
// Trigger fires when light state changes:
{
"id": 4,
"type": "event",
"event": { ... }
}认证:
javascript
// Message 1: Server sends (automatically)
{
"type": "auth_required",
"ha_version": "2025.1.0"
}
// Message 2: Client responds
{
"type": "auth",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
// Message 3: Server confirms
{
"type": "auth_ok",
"ha_version": "2025.1.0"
}订阅事件:
javascript
{
"id": 1,
"type": "subscribe_events",
"event_type": "state_changed"
}
// Responses come as:
{
"id": 1,
"type": "event",
"event": {
"type": "state_changed",
"data": {
"entity_id": "light.living_room",
"old_state": { "state": "off", ... },
"new_state": { "state": "on", ... }
}
}
}调用服务:
javascript
{
"id": 2,
"type": "call_service",
"domain": "light",
"service": "turn_on",
"service_data": {
"entity_id": "light.living_room",
"brightness": 200
}
}
// Response:
{
"id": 2,
"type": "result",
"success": true,
"result": [
{
"entity_id": "light.living_room",
"state": "on",
"attributes": { ... }
}
]
}获取当前状态:
javascript
{
"id": 3,
"type": "get_states"
}
// Response:
{
"id": 3,
"type": "result",
"success": true,
"result": [ ... ] // Array of all entity states
}订阅特定触发器:
javascript
{
"id": 4,
"type": "subscribe_trigger",
"trigger": {
"platform": "state",
"entity_id": "light.living_room"
}
}
// Trigger fires when light state changes:
{
"id": 4,
"type": "event",
"event": { ... }
}Code Examples
代码示例
Python - REST API
Python - REST API
python
import requests
import os
HA_URL = os.getenv("HA_URL", "http://localhost:8123")
HA_TOKEN = os.getenv("HA_TOKEN")
headers = {
"Authorization": f"Bearer {HA_TOKEN}",
"Content-Type": "application/json"
}python
import requests
import os
HA_URL = os.getenv("HA_URL", "http://localhost:8123")
HA_TOKEN = os.getenv("HA_TOKEN")
headers = {
"Authorization": f"Bearer {HA_TOKEN}",
"Content-Type": "application/json"
}Get all states
Get all states
response = requests.get(f"{HA_URL}/api/states", headers=headers)
response.raise_for_status()
states = response.json()
response = requests.get(f"{HA_URL}/api/states", headers=headers)
response.raise_for_status()
states = response.json()
Get single entity
Get single entity
response = requests.get(f"{HA_URL}/api/states/light.living_room", headers=headers)
light_state = response.json()
print(f"Light state: {light_state['state']}")
print(f"Brightness: {light_state['attributes'].get('brightness', 'N/A')}")
response = requests.get(f"{HA_URL}/api/states/light.living_room", headers=headers)
light_state = response.json()
print(f"Light state: {light_state['state']}")
print(f"Brightness: {light_state['attributes'].get('brightness', 'N/A')}")
Call service
Call service
service_data = {
"entity_id": "light.living_room",
"brightness": 180,
"transition": 2
}
response = requests.post(
f"{HA_URL}/api/services/light/turn_on",
headers=headers,
json=service_data
)
response.raise_for_status()
print(f"Service call successful: {response.json()}")
service_data = {
"entity_id": "light.living_room",
"brightness": 180,
"transition": 2
}
response = requests.post(
f"{HA_URL}/api/services/light/turn_on",
headers=headers,
json=service_data
)
response.raise_for_status()
print(f"Service call successful: {response.json()}")
Handle errors
Handle errors
try:
response = requests.get(f"{HA_URL}/api/states/invalid.entity", headers=headers)
response.raise_for_status()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
print("ERROR: Token invalid or expired")
elif e.response.status_code == 404:
print("ERROR: Entity not found")
else:
print(f"ERROR: {e}")
undefinedtry:
response = requests.get(f"{HA_URL}/api/states/invalid.entity", headers=headers)
response.raise_for_status()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
print("ERROR: Token invalid or expired")
elif e.response.status_code == 404:
print("ERROR: Entity not found")
else:
print(f"ERROR: {e}")
undefinedPython - WebSocket API
Python - WebSocket API
python
import asyncio
import aiohttp
import json
import os
HA_URL = os.getenv("HA_URL", "http://localhost:8123").replace("http", "ws")
HA_TOKEN = os.getenv("HA_TOKEN")
async def monitor_light_changes():
async with aiohttp.ClientSession() as session:
async with session.ws_connect(f"{HA_URL}/api/websocket") as ws:
# Wait for auth_required
msg = await ws.receive_json()
print(f"Server: {msg}")
# Send auth
await ws.send_json({
"type": "auth",
"access_token": HA_TOKEN
})
# Wait for auth_ok
msg = await ws.receive_json()
print(f"Server: {msg}")
# Subscribe to state changes
await ws.send_json({
"id": 1,
"type": "subscribe_events",
"event_type": "state_changed"
})
# Listen for events
async for msg in ws:
event = msg.json()
if event.get("type") == "event":
data = event["event"]["data"]
print(f"Entity: {data['entity_id']}")
print(f" Old state: {data['old_state']['state']}")
print(f" New state: {data['new_state']['state']}")python
import asyncio
import aiohttp
import json
import os
HA_URL = os.getenv("HA_URL", "http://localhost:8123").replace("http", "ws")
HA_TOKEN = os.getenv("HA_TOKEN")
async def monitor_light_changes():
async with aiohttp.ClientSession() as session:
async with session.ws_connect(f"{HA_URL}/api/websocket") as ws:
# Wait for auth_required
msg = await ws.receive_json()
print(f"Server: {msg}")
# Send auth
await ws.send_json({
"type": "auth",
"access_token": HA_TOKEN
})
# Wait for auth_ok
msg = await ws.receive_json()
print(f"Server: {msg}")
# Subscribe to state changes
await ws.send_json({
"id": 1,
"type": "subscribe_events",
"event_type": "state_changed"
})
# Listen for events
async for msg in ws:
event = msg.json()
if event.get("type") == "event":
data = event["event"]["data"]
print(f"Entity: {data['entity_id']}")
print(f" Old state: {data['old_state']['state']}")
print(f" New state: {data['new_state']['state']}")Run the monitoring loop
Run the monitoring loop
asyncio.run(monitor_light_changes())
undefinedasyncio.run(monitor_light_changes())
undefinedJavaScript - REST API
JavaScript - REST API
javascript
const HA_URL = process.env.HA_URL || "http://localhost:8123";
const HA_TOKEN = process.env.HA_TOKEN;
const headers = {
"Authorization": `Bearer ${HA_TOKEN}`,
"Content-Type": "application/json"
};
// Get all states
async function getAllStates() {
const response = await fetch(`${HA_URL}/api/states`, { headers });
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
// Get single entity
async function getEntityState(entityId) {
const response = await fetch(`${HA_URL}/api/states/${entityId}`, { headers });
if (response.status === 404) {
throw new Error(`Entity ${entityId} not found`);
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const state = await response.json();
console.log(`${entityId}: ${state.state}`);
console.log(`Attributes:`, state.attributes);
return state;
}
// Call service
async function callService(domain, service, data) {
const response = await fetch(
`${HA_URL}/api/services/${domain}/${service}`,
{
method: "POST",
headers,
body: JSON.stringify(data)
}
);
if (!response.ok) {
const error = await response.text();
throw new Error(`HTTP ${response.status}: ${error}`);
}
return response.json();
}
// Example usage
(async () => {
try {
const states = await getAllStates();
console.log(`Found ${states.length} entities`);
await getEntityState("light.living_room");
const result = await callService("light", "turn_on", {
entity_id: "light.living_room",
brightness: 180
});
console.log("Service call successful");
} catch (error) {
console.error(error);
}
})();javascript
const HA_URL = process.env.HA_URL || "http://localhost:8123";
const HA_TOKEN = process.env.HA_TOKEN;
const headers = {
"Authorization": `Bearer ${HA_TOKEN}`,
"Content-Type": "application/json"
};
// Get all states
async function getAllStates() {
const response = await fetch(`${HA_URL}/api/states`, { headers });
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
// Get single entity
async function getEntityState(entityId) {
const response = await fetch(`${HA_URL}/api/states/${entityId}`, { headers });
if (response.status === 404) {
throw new Error(`Entity ${entityId} not found`);
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const state = await response.json();
console.log(`${entityId}: ${state.state}`);
console.log(`Attributes:`, state.attributes);
return state;
}
// Call service
async function callService(domain, service, data) {
const response = await fetch(
`${HA_URL}/api/services/${domain}/${service}`,
{
method: "POST",
headers,
body: JSON.stringify(data)
}
);
if (!response.ok) {
const error = await response.text();
throw new Error(`HTTP ${response.status}: ${error}`);
}
return response.json();
}
// Example usage
(async () => {
try {
const states = await getAllStates();
console.log(`Found ${states.length} entities`);
await getEntityState("light.living_room");
const result = await callService("light", "turn_on", {
entity_id: "light.living_room",
brightness: 180
});
console.log("Service call successful");
} catch (error) {
console.error(error);
}
})();JavaScript - WebSocket API
JavaScript - WebSocket API
javascript
const HA_URL = (process.env.HA_URL || "http://localhost:8123").replace(/^http/, "ws");
const HA_TOKEN = process.env.HA_TOKEN;
async function subscribeToStateChanges() {
const ws = new WebSocket(`${HA_URL}/api/websocket`);
return new Promise((resolve, reject) => {
ws.onopen = () => console.log("WebSocket connected");
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log("Message:", msg);
if (msg.type === "auth_required") {
// Respond to auth challenge
ws.send(JSON.stringify({
type: "auth",
access_token: HA_TOKEN
}));
} else if (msg.type === "auth_ok") {
// Authentication successful, subscribe to events
ws.send(JSON.stringify({
id: 1,
type: "subscribe_events",
event_type: "state_changed"
}));
} else if (msg.type === "event" && msg.event?.type === "state_changed") {
// Handle state change
const data = msg.event.data;
console.log(`${data.entity_id} changed:`);
console.log(` ${data.old_state.state} → ${data.new_state.state}`);
}
};
ws.onerror = (error) => {
console.error("WebSocket error:", error);
reject(error);
};
ws.onclose = () => {
console.log("WebSocket closed");
resolve();
};
});
}
subscribeToStateChanges();javascript
const HA_URL = (process.env.HA_URL || "http://localhost:8123").replace(/^http/, "ws");
const HA_TOKEN = process.env.HA_TOKEN;
async function subscribeToStateChanges() {
const ws = new WebSocket(`${HA_URL}/api/websocket`);
return new Promise((resolve, reject) => {
ws.onopen = () => console.log("WebSocket connected");
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log("Message:", msg);
if (msg.type === "auth_required") {
// Respond to auth challenge
ws.send(JSON.stringify({
type: "auth",
access_token: HA_TOKEN
}));
} else if (msg.type === "auth_ok") {
// Authentication successful, subscribe to events
ws.send(JSON.stringify({
id: 1,
type: "subscribe_events",
event_type: "state_changed"
}));
} else if (msg.type === "event" && msg.event?.type === "state_changed") {
// Handle state change
const data = msg.event.data;
console.log(`${data.entity_id} changed:`);
console.log(` ${data.old_state.state} → ${data.new_state.state}`);
}
};
ws.onerror = (error) => {
console.error("WebSocket error:", error);
reject(error);
};
ws.onclose = () => {
console.log("WebSocket closed");
resolve();
};
});
}
subscribeToStateChanges();cURL - Common Operations
cURL - 常见操作
bash
undefinedbash
undefinedGet all entities
Get all entities
curl -X GET "http://localhost:8123/api/states"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Authorization: Bearer ${HA_TOKEN}"
curl -X GET "http://localhost:8123/api/states"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Authorization: Bearer ${HA_TOKEN}"
Get specific entity
Get specific entity
curl -X GET "http://localhost:8123/api/states/light.living_room"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Authorization: Bearer ${HA_TOKEN}"
curl -X GET "http://localhost:8123/api/states/light.living_room"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Authorization: Bearer ${HA_TOKEN}"
Turn on light with brightness
Turn on light with brightness
curl -X POST "http://localhost:8123/api/services/light/turn_on"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{ "entity_id": "light.living_room", "brightness": 200, "transition": 2 }'
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{ "entity_id": "light.living_room", "brightness": 200, "transition": 2 }'
curl -X POST "http://localhost:8123/api/services/light/turn_on"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{ "entity_id": "light.living_room", "brightness": 200, "transition": 2 }'
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{ "entity_id": "light.living_room", "brightness": 200, "transition": 2 }'
Turn off light
Turn off light
curl -X POST "http://localhost:8123/api/services/light/turn_off"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"entity_id": "light.living_room"}'
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"entity_id": "light.living_room"}'
curl -X POST "http://localhost:8123/api/services/light/turn_off"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"entity_id": "light.living_room"}'
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"entity_id": "light.living_room"}'
Set climate temperature
Set climate temperature
curl -X POST "http://localhost:8123/api/services/climate/set_temperature"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{ "entity_id": "climate.living_room", "temperature": 21 }'
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{ "entity_id": "climate.living_room", "temperature": 21 }'
curl -X POST "http://localhost:8123/api/services/climate/set_temperature"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{ "entity_id": "climate.living_room", "temperature": 21 }'
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{ "entity_id": "climate.living_room", "temperature": 21 }'
Get service schema
Get service schema
curl -X GET "http://localhost:8123/api/services/light"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Authorization: Bearer ${HA_TOKEN}"
curl -X GET "http://localhost:8123/api/services/light"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Authorization: Bearer ${HA_TOKEN}"
Call automation
Call automation
curl -X POST "http://localhost:8123/api/services/automation/trigger"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"entity_id": "automation.my_automation"}'
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"entity_id": "automation.my_automation"}'
curl -X POST "http://localhost:8123/api/services/automation/trigger"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"entity_id": "automation.my_automation"}'
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"entity_id": "automation.my_automation"}'
Render template
Render template
curl -X POST "http://localhost:8123/api/template"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"template": "{{ states("sensor.temperature") }}"}'
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"template": "{{ states("sensor.temperature") }}"}'
undefinedcurl -X POST "http://localhost:8123/api/template"
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"template": "{{ states("sensor.temperature") }}"}'
-H "Authorization: Bearer ${HA_TOKEN}"
-H "Content-Type: application/json"
-d '{"template": "{{ states("sensor.temperature") }}"}'
undefinedKnown Issues Prevention
已知问题预防
| Issue | Root Cause | Solution |
|---|---|---|
| 401 Unauthorized | Token invalid, expired, or malformed | Create new token in Settings → Create Long-Lived Access Token; verify Bearer prefix |
| 404 Not Found | Entity doesn't exist or wrong domain | Check entity_id with |
| WebSocket auth timeout | Didn't respond to auth_required within 10s | Send auth message immediately upon receiving auth_required |
| Attribute confusion | Mixing state string with attributes object | State is always a string; attributes contain metadata (brightness, color, etc.) |
| Service call fails silently | Wrong domain/service or missing required fields | Use |
| 问题 | 根本原因 | 解决方案 |
|---|---|---|
| 401 未授权 | 令牌无效、过期或格式错误 | 在设置中创建新的长期访问令牌;验证Bearer前缀 |
| 404 未找到 | 实体不存在或域错误 | 通过 |
| WebSocket认证超时 | 未在10秒内响应auth_required | 收到auth_required后立即发送认证消息 |
| 属性混淆 | 混淆状态字符串与属性对象 | 状态始终是字符串;属性包含元数据(亮度、颜色等) |
| 服务调用无响应失败 | 域/服务错误或缺失必填字段 | 使用 |
Error Handling Patterns
错误处理模式
HTTP Status Codes
HTTP状态码
python
import requests
try:
response = requests.get(f"{HA_URL}/api/states/{entity_id}", headers=headers)
if response.status_code == 401:
print("Token invalid/expired - create new token in HA UI")
elif response.status_code == 403:
print("Forbidden - token lacks necessary permissions")
elif response.status_code == 404:
print("Entity not found - check entity_id")
elif response.status_code == 502:
print("Home Assistant unavailable - check server status")
elif response.status_code >= 500:
print("Server error - try again later")
else:
response.raise_for_status()
return response.json()
except requests.exceptions.ConnectionError:
print("Cannot connect to Home Assistant - verify HA_URL and network")
except requests.exceptions.Timeout:
print("Request timeout - Home Assistant is slow to respond")python
import requests
try:
response = requests.get(f"{HA_URL}/api/states/{entity_id}", headers=headers)
if response.status_code == 401:
print("Token invalid/expired - create new token in HA UI")
elif response.status_code == 403:
print("Forbidden - token lacks necessary permissions")
elif response.status_code == 404:
print("Entity not found - check entity_id")
elif response.status_code == 502:
print("Home Assistant unavailable - check server status")
elif response.status_code >= 500:
print("Server error - try again later")
else:
response.raise_for_status()
return response.json()
except requests.exceptions.ConnectionError:
print("Cannot connect to Home Assistant - verify HA_URL and network")
except requests.exceptions.Timeout:
print("Request timeout - Home Assistant is slow to respond")WebSocket Error Handling
WebSocket错误处理
javascript
const ws = new WebSocket(wsUrl);
ws.onerror = (error) => {
console.error("WebSocket error:", error);
// Reconnect after delay
setTimeout(connect, 5000);
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === "result" && !msg.success) {
console.error("Command failed:", msg.error);
}
};
ws.onclose = () => {
console.log("Connection closed");
// Implement reconnection logic
setTimeout(connect, 3000);
};javascript
const ws = new WebSocket(wsUrl);
ws.onerror = (error) => {
console.error("WebSocket error:", error);
// Reconnect after delay
setTimeout(connect, 5000);
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === "result" && !msg.success) {
console.error("Command failed:", msg.error);
}
};
ws.onclose = () => {
console.log("Connection closed");
// Implement reconnection logic
setTimeout(connect, 3000);
};Bundled Resources
附带资源
References
参考文档
Located in :
references/- - Complete REST endpoint documentation
REST_API_REFERENCE.md - - WebSocket command reference
WEBSOCKET_API_REFERENCE.md - - Auth token creation and security best practices
AUTHENTICATION.md - - Common services by domain
SERVICE_CATALOG.md
Note: For deep dives on specific topics, see the reference files above.
位于目录下:
references/- - 完整的REST端点文档
REST_API_REFERENCE.md - - WebSocket命令参考
WEBSOCKET_API_REFERENCE.md - - 认证令牌创建与安全最佳实践
AUTHENTICATION.md - - 按域分类的常见服务
SERVICE_CATALOG.md
注意: 如需深入了解特定主题,请查看上述参考文件。
Dependencies
依赖项
Required
必需依赖
| Package | Language | Purpose |
|---|---|---|
| requests | Python | HTTP client for REST API calls |
| aiohttp | Python | Async HTTP/WebSocket client |
| fetch | JavaScript | Native HTTP client (browser/Node.js) |
| WebSocket | JavaScript | Native WebSocket API (browser/Node.js) |
| 包 | 语言 | 用途 |
|---|---|---|
| requests | Python | REST API调用的HTTP客户端 |
| aiohttp | Python | 异步HTTP/WebSocket客户端 |
| fetch | JavaScript | 原生HTTP客户端(浏览器/Node.js) |
| WebSocket | JavaScript | 原生WebSocket API(浏览器/Node.js) |
Optional
可选依赖
| Package | Language | Purpose |
|---|---|---|
| httpx | Python | Advanced HTTP client with streaming |
| websockets | Python | Pure Python WebSocket library |
| axios | JavaScript | Promise-based HTTP client |
| 包 | 语言 | 用途 |
|---|---|---|
| httpx | Python | 支持流的高级HTTP客户端 |
| websockets | Python | 纯Python WebSocket库 |
| axios | JavaScript | 基于Promise的HTTP客户端 |
Official Documentation
官方文档
Troubleshooting
故障排除
Cannot authenticate - 401 Unauthorized
无法认证 - 401未授权
Symptoms: All API requests return 401, even with correct token.
Solution:
- Verify token in Home Assistant (Settings → Developer Tools → States/Services)
- Check Authorization header includes "Bearer " prefix
- Create new token if old one expired
- Ensure HA_URL and HA_TOKEN environment variables are set
bash
echo $HA_TOKEN # Verify token is set
echo $HA_URL # Verify URL is correct (http not https)症状: 所有API请求均返回401,即使令牌正确。
解决方案:
- 在Home Assistant中验证令牌(设置 → 开发者工具 → 状态/服务)
- 检查Authorization头是否包含“Bearer ”前缀
- 若令牌过期,创建新令牌
- 确保已设置HA_URL和HA_TOKEN环境变量
bash
echo $HA_TOKEN # Verify token is set
echo $HA_URL # Verify URL is correct (http not https)WebSocket connection closes immediately
WebSocket连接立即关闭
Symptoms: WebSocket connects then closes without response.
Solution:
- Verify Home Assistant version supports WebSocket API (2021.1+)
- Check firewall doesn't block WebSocket upgrade
- Ensure auth response is sent within 10 seconds of auth_required
- Use correct path, not
/api/websocket/api/websocket/
javascript
// Incorrect path
ws = new WebSocket("ws://ha:8123/api/websocket/");
// Correct path
ws = new WebSocket("ws://ha:8123/api/websocket");症状: WebSocket连接后立即关闭,无响应。
解决方案:
- 验证Home Assistant版本支持WebSocket API(2021.1+)
- 检查防火墙是否阻止WebSocket升级
- 确保在收到auth_required后10秒内发送认证响应
- 使用正确的路径,而非
/api/websocket/api/websocket/
javascript
// Incorrect path
ws = new WebSocket("ws://ha:8123/api/websocket/");
// Correct path
ws = new WebSocket("ws://ha:8123/api/websocket");Entity states appear as wrong type
实体状态类型错误
Symptoms: State should be "on"/"off" but appears as number or boolean.
Solution:
- Check entity's integration configuration
- Reload integration in Developer Tools
- Verify template/custom component isn't converting type
- State is always a string in API response; parse as needed
python
state = entity["state"] # String: "on", "off", "123"
is_on = state == "on" # Convert to boolean症状: 状态应为“on”/“off”,但显示为数字或布尔值。
解决方案:
- 检查实体的集成配置
- 在开发者工具中重新加载集成
- 验证模板/自定义组件未转换类型
- API响应中的状态始终是字符串;按需解析
python
state = entity["state"] # String: "on", "off", "123"
is_on = state == "on" # Convert to booleanFor numeric states
For numeric states
temperature = float(entity["state"]) # Convert to float
undefinedtemperature = float(entity["state"]) # Convert to float
undefinedService call fails but no error returned
服务调用失败但无错误返回
Symptoms: Service call returns HTTP 200 but nothing happens.
Solution:
- Verify service exists:
GET /api/services/{domain} - Check required fields in service_data
- Verify entity_id is in entity_ids list, not as separate parameter
- Check entity supports service (light.turn_on won't work on switch)
bash
undefined症状: 服务调用返回HTTP 200,但无任何效果。
解决方案:
- 验证服务是否存在:
GET /api/services/{domain} - 检查service_data中的必填字段
- 验证entity_id是否在entity_ids列表中,而非作为单独参数
- 检查实体是否支持该服务(light.turn_on不适用于开关)
bash
undefinedCorrect - entity_id in service_data
Correct - entity_id in service_data
curl -X POST "http://ha:8123/api/services/light/turn_on"
-d '{"entity_id": "light.living_room"}'
-d '{"entity_id": "light.living_room"}'
curl -X POST "http://ha:8123/api/services/light/turn_on"
-d '{"entity_id": "light.living_room"}'
-d '{"entity_id": "light.living_room"}'
Incorrect - entity_id as path parameter
Incorrect - entity_id as path parameter
curl -X POST "http://ha:8123/api/services/light/turn_on/light.living_room"
undefinedcurl -X POST "http://ha:8123/api/services/light/turn_on/light.living_room"
undefinedSetup Checklist
设置检查清单
Before using this skill, verify:
- Home Assistant instance is running and accessible (test via browser)
- Created Long-Lived Access Token (Settings → My Home → Create Token)
- Token stored in environment variable (HA_TOKEN) or secure vault
- Home Assistant URL set correctly (HA_URL with http not https)
- Firewall allows API/WebSocket connections (ports 8123 or custom)
- Python packages installed if using Python examples (pip install requests aiohttp)
- Tested connectivity with simple curl/fetch request before building full integration
使用该技能前,请验证:
- Home Assistant实例正在运行且可访问(通过浏览器测试)
- 已创建长期访问令牌(设置 → 我的主页 → 创建令牌)
- 令牌已存储在环境变量(HA_TOKEN)或安全密钥管理库中
- Home Assistant URL设置正确(HA_URL使用http而非https)
- 防火墙允许API/WebSocket连接(端口8123或自定义端口)
- 若使用Python示例,已安装所需Python包(pip install requests aiohttp)
- 在构建完整集成之前,已通过简单的curl/fetch请求测试连通性