Releases: SukramJ/openccu-loom
Releases · SukramJ/openccu-loom
v0.1.0
First public release of OpenCCU-Loom, a standalone Go daemon that
bridges Homematic CCUs to MQTT, a REST + WebSocket API, a web Config UI,
and a Matter bridge. A Go port of the aiohomematic family that adds the
standalone-daemon surface on top.
Core
- Multi-CCU from day one — one daemon, many CCUs; every coordinator,
adapter, and store iscentral_name-scoped (ADR 0002). - Hexagonal architecture (ports & adapters) with an internal typed,
priority-aware event bus for cross-domain communication. - Single static binary (
CGO_ENABLED=0, no CGo) + multi-arch Docker
images (linux/amd64, arm64, armv7). - Pure-Go SQLite (
modernc.org/sqlite) + filesystem persistence;
goose-managed migrations.
South-bound (CCU)
- All three transports: XML-RPC, BIN-RPC, JSON-RPC, plus HTTP and raw
TCP callback servers (shared across all centrals, dynamic-port aware). - Every MVP interface — HmIP-RF, BidCos-RF, BidCos-Wired, HmIP-Wired,
VirtualDevices, and CUxD (BIN-RPC) — supports push callbacks; no
polling-only code path. - Reliability layer per
(central, interface): circuit breaker, retry,
throttle, coalescer, ping/pong. - 139 generated device profiles with hand-written custom data-point types;
ReGa script runner. - Homegear backend support — system variables load and periodically
refresh over the XML-RPCgetAllSystemVariablesmethod (each variable's
type inferred from its value, since Homegear ships only name + value) and
write back viasetSystemVariable, bringing Homegear to system-variable
parity with the reference stack. Programs, rooms, and functions stay
empty on Homegear by design (no ReGa engine / metadata RPC).
North-bound (bridges)
- MQTT — Home Assistant Discovery and raw topic planes in parallel;
MQTT config applies without a daemon restart. Discovery topics are scoped
to each device's own CCU, so on a multi-CCU daemon every device's state,
availability, command, andjson_attributestopics route to the central
the device actually lives on. Availability tracks device reachability
(UNREACH/STICKY_UNREACHviaDevice.Available()): a reachable
device is publishedonlineat boot even before its data points report,
and every registered data point — including not-yet-observed ones —
publishes an explicit{"value":null,"available":true}slot state (HA
value templates render an unobserved point asunknownrather than
"None"). The full boot snapshot — per-device availability plus every
data point's slot state — is republished on every broker (re)connect, so
a broker restart or transient TCP reset never leaves entities stuck
unavailable. - REST + WebSocket API — ~80 REST endpoints (
assets/openapi.yaml) and
85 WebSocket commands. Value-bearing WebSocket push payloads
(datapoint.value_changed,custom_data_point.state_changed,
hub.sysvar_changed,hub.program_executed,
datapoint.optimistic_rolled_back,device.trigger) carry the canonical
loom-namespacedunique_id(loom_<routing-key>) that external clients
use as the Home Assistant entity key. - MCP server (Model Context Protocol) — a north-bound adapter
(internal/north/mcp/) exposing the daemon to LLM agents as tools over a
Streamable-HTTP transport, mounted on the REST listener behind the same
auth chain. Disabled by default (North.MCP.Enabled) and read-only even
when enabled — write tools are registered only whenNorth.MCP.AllowWrites
is also set, and a write tool that touches a device refuses to act on one
the named central does not own. Read tools:list_centrals,
list_devices,get_device,read_paramset,get_health,
list_audit; write tools:set_datapoint,write_paramset,
trigger_program. Each tool also gates on its own dependency, so a
partial wiring never exposes a half-functional tool. Themcp.v1/
mcp.write.v1capability tokens surface the posture viaGET /info.
Built on the officialmodelcontextprotocol/go-sdk(ADR 0025). - Config UI — a Svelte 5 SPA (Tailwind 4, embedded via
go:embed) as
the primary surface, with a minimal HTMX bootstrap surface for pre-auth
flows (login, first-run setup, OIDC callback) and SPA-down diagnosis. The
SPA includes an MCP tab (wired through the config schema and the
section store) to toggleenabled/allow_writesand set the mount
path— flagged restart-required — without editing YAML or env. - Matter bridge — native-Go, default off, operator opt-in; a semantic
port of matter.js HEAD.
Auth & security
- Basic / Session / OIDC / API-Token authentication with role gating
(admin-only mutations); CSRF protection for cookie-session flows. - Audit ledger for sensitive operations.
Diagnostics & observability
- Unified health tracker (per-central + daemon-global), Prometheus
metrics, tracing helpers, incident journal. - Live log viewer (
#/logs, admin-only): always-on ring buffer with an
SSE tail (tail -f, resume viaLast-Event-ID/?since=), aggregated
(≥ warn, deduplicated) vs. detail views, level dropdown, and download of
the last N records. - Diagnose & Aufzeichnen hub: RAM-buffered debug-log capture and an
RPC session recorder (XML / JSON / BIN-RPC traffic for deterministic
golden replay) with per-CCU scope, duration limit + safety cap,
optional anonymisation, restart-survival, andmap/goldenexport. - Composite diagnostics dump and runtime per-subsystem log-level overrides.
Internationalisation
- German + English catalogues across UI and server-rendered surfaces.
- A curated translation overlay supplies device-model labels the upstream
catalogue omits — e.g. HmIP-DLP ("Türschlossantrieb - pro" / "Door Lock
Drive - pro") and HmIP-UDI-SMI55 ("Universal Dimmeraufsatz -
Bewegungsmelder" / "Universal Dimming Control Element - motion
detector") — so their MQTT discovery payload carries a readable
model_idinstead of falling back to the raw wire type.
Quality & parity
- Cross-stack model-snapshot drift gate
(script/model_snapshot_drift_check.py) with an explicit env-override
table (OPENCCU_LOOM_DRIFT_GENERIC/_CHANNEL/_CALC), a printed
TOTAL line, and fail-closed behaviour on any drift bucket without a
baseline.