v2/crates/homecore-api/README.md
Home Assistant-compatible REST + WebSocket API for HOMECORE state and events.
Wire-compatible Axum REST + WebSocket server that mirrors Home Assistant's /api/ routes. Ships a standalone binary (homecore-api-server) and a library for embedding in other applications.
homecore-api provides the HTTP boundary layer for HOMECORE. It wires Axum routes to the homecore state machine, exposing:
/api/states — list all entity states/api/states/:entity_id — fetch a single entity's state + attributes/api/states/:entity_id — update an entity's state and attributes/api/services — list registered services/api/services/:domain/:service — call a service with arguments/api/websocket — upgrade to WebSocket for real-time state + event streamingAll routes return HA-compatible JSON and validate Authorization: Bearer <token> headers (except the WS upgrade, which validates the token as a query param for browser compatibility).
/api/states returns [{"entity_id": "...", "state": "...", "attributes": {...}}] matching HA exactlylast_updated and last_changed timestampstype:state_changed, etc.)HOMECORE_CORS_ORIGINS env var (audit fix HC-05); defaults to localhost:5173 (frontend dev), localhost:8123 (HA port){"error": "...", "message": "..."} envelopesRUST_LOG)| Capability | Method | Endpoint | Returns |
|---|---|---|---|
| List all entities | GET | /api/states | [{entity_id, state, attributes, last_changed, ...}] |
| Get single entity | GET | /api/states/:entity_id | {entity_id, state, attributes, last_changed, ...} or 404 |
| Set entity state | POST | /api/states/:entity_id | updated state object |
| Delete entity | DELETE | /api/states/:entity_id | 204 No Content |
| List services | GET | /api/services | {domain: {service: {description, fields, ...}}} |
| Call service | POST | /api/services/:domain/:service | service result (P2) |
| Stream state changes | WebSocket | /api/websocket | {type, event} JSON messages |
| Validate token | Bearer auth | all routes | 401 Unauthorized if token invalid |
| Aspect | Home Assistant | homecore-api |
|---|---|---|
| Framework | aiohttp | Axum |
| Server type | Single-threaded async (Python asyncio) | Multi-threaded async (Tokio) |
| JSON schema | HA's /api/states format | Wire-compatible (identical) |
| CORS | Permissive (all origins allowed) | Explicit allowlist (audit fix HC-05) |
| Authentication | long_lived_access_tokens (SQLite) | LongLivedTokenStore (in-memory P1) |
| WebSocket codec | HA's message format + types dict | JSON messages with type/event fields (P2) |
| Service calling | async handler dispatch | ServiceRegistry stub (P2) |
| Error handling | Python exception → JSON 500 | Rust Result + thiserror → JSON with details |
use homecore_api::{router, SharedState};
use homecore::HomeCore;
use axum::Server;
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
// Create the shared HOMECORE runtime
let homecore = HomeCore::new();
let state = SharedState::new(homecore);
// Build the Axum router
let app = router(state);
// Bind to 8123
let addr = SocketAddr::from(([127, 0, 0, 1], 8123));
Server::bind(&addr)
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
.await
.expect("server error");
}
Or run the standalone binary:
cargo run -p homecore-api --bin homecore-api-server
# Listens on http://localhost:8123
Test it:
# List states
curl -H "Authorization: Bearer longlivedtoken" \
http://localhost:8123/api/states
# Set a light to "on"
curl -X POST \
-H "Authorization: Bearer longlivedtoken" \
-H "Content-Type: application/json" \
-d '{"state":"on","attributes":{"brightness":200}}' \
http://localhost:8123/api/states/light.kitchen
homecore-api (REST + WebSocket server)
├─ homecore (state machine + event bus)
├─ homecore-frontend (Lit web UI consuming /api endpoints)
├─ homecore-automation (services called via POST /api/services/:domain/:service)
├─ homecore-assist (intent → service call bridge)
└─ homecore-migrate (imports HA tokens + config entities)