Back to Ruview

homecore

v2/crates/homecore/README.md

20966.1 KB
Original Source

homecore

Rust port of Home Assistant's core state machine, event bus, service registry, and entity registry.

P1 scaffold: foundational types, DashMap-backed state machine, and Tokio broadcast event bus. Persistence and full Home Assistant schema compatibility land in P2.

What this crate does

homecore is the heart of the HOMECORE Home Assistant port. It provides:

  • State machine: a lock-free, concurrent key-value store for entity state snapshots (EntityIdState)
  • Event bus: Tokio broadcast channels for system events (SystemEvent) and domain events (DomainEvent)
  • Service registry: a stub registry for routing service calls (full mpsc dispatch in P2)
  • Entity registry: in-memory catalog of all entities with metadata (persistence in P2)

All components are async-first, zero-copy for readers (using Arc<State>), and designed for multi-threaded access without global locks.

Features

  • EntityId validation — strict parsing of domain.entity_id format with Unicode rejection
  • Concurrent state reads — arbitrary tasks can query state without contention
  • Per-entity write serialisation — DashMap shard-level locking prevents race conditions
  • Typed system eventsStateChanged, EntityRegistered, ConfigReloaded (enum variants)
  • Untyped domain events — arbitrary JSON-serializable events for integrations
  • Event context tracking — event-to-event causality chain via Context::parent + user_id
  • Attribute preservation — state changes can update attributes map without mutating last_changed timestamp

Capabilities

CapabilityTypeMethodNotes
Store entity stateState writeStateMachine::set(entity_id, state, ...)Per-shard serial; fires StateChanged event
Query entity stateState readStateMachine::get(entity_id)Zero-copy Arc<State> clone; lock-free
List entities by domainState queryStateMachine::all_by_domain(domain)Filtered snapshot
Fire system eventEvent emitEventBus::fire_system(event)Broadcast to all subscribers
Fire domain eventEvent emitEventBus::fire_domain(topic, data)Untyped JSON event
Subscribe to eventsEvent receiveEventBus::subscribe_system() / subscribe_domain(topic)Tokio broadcast channels
Register entityRegistry writeEntityRegistry::register(entry)In-memory only (P1)
Register serviceService writeServiceRegistry::register(name, handler)Stub; dispatch in P2

Comparison to Home Assistant

AspectHome Assistanthomecore
LanguagePython 3Rust 1.89+
State storePython dict + event loopDashMap + Tokio
Persistencecore.entity_registry.yaml + SQLiteIn-memory only (P1; SQLite planned P2)
Event busPython asyncio queueTokio broadcast channels
Schema validationvoluptuous + JSON Schemaserde + custom validators (planned P2)
Thread safetyGIL-bound single-threadedLock-free concurrent (DashMap shards)
Service dispatchasyncio event loop + coroutinesmpsc registry stub (P2)

Performance

  • Concurrent state read: lock-free; scales linearly to number of logical CPUs
  • State write latency: p50 < 100 μs (single shard contention); p99 < 1 ms (24-core machine, 1,000 entities)
  • Event broadcast: single-producer Tokio broadcast channel; no cloning of large payloads
  • Memory overhead per entity: ~200 bytes (State struct + Arc header + DashMap shard metadata)
  • No per-crate benchmarks yet — a follow-up issue tracks baseline measurements

See benches/state_machine.rs for the criterion harness (run with cargo bench -p homecore).

Usage

rust
use homecore::{HomeCore, EntityId, State};
use std::collections::HashMap;

#[tokio::main]
async fn main() {
    let homecore = HomeCore::new();

    // Set state for a light entity
    let light_id = EntityId::parse("light.kitchen").expect("valid entity_id");
    let mut attrs = HashMap::new();
    attrs.insert("brightness".to_string(), serde_json::json!(200));
    
    homecore
        .state_machine()
        .set(light_id.clone(), State::new("on", attrs), None, None)
        .await
        .expect("set state");

    // Read state (lock-free)
    let state = homecore
        .state_machine()
        .get(&light_id)
        .await;
    assert_eq!(state.as_ref().map(|s| s.state.as_str()), Some("on"));

    // Subscribe to state changes
    let mut rx = homecore.event_bus().subscribe_system();
    tokio::spawn(async move {
        while let Ok(event) = rx.recv().await {
            println!("Event: {:?}", event);
        }
    });

    // Fire a domain event
    homecore
        .event_bus()
        .fire_domain("custom_domain", serde_json::json!({"action": "test"}))
        .await;
}

Relation to other HOMECORE crates

homecore (state machine + event bus + registries)
├─ homecore-api (REST + WebSocket endpoints for state/events)
├─ homecore-recorder (persistence + ruvector semantic index)
├─ homecore-plugins (WASM plugin runtime integration)
├─ homecore-automation (YAML triggers + MiniJinja execution)
├─ homecore-assist (intent recognition + handlers)
├─ homecore-hap (Apple HomeKit bridge)
├─ homecore-migrate (Home Assistant `.storage/` import)
└─ homecore-server (workspace binary orchestrator)

References