homeos/CLAUDE.md

3.1 KiB
Raw Permalink Blame History

CLAUDE.md — HomeOS

Self-built smart-home control plane. Single household, self-hosted, LAN-first. Not Home Assistant / not a wrapper: own data model, own integrations, own UI.

Authoritative build spec: handoff.md. Non-negotiable conventions: rules.md. Approved design direction: design-mockup.html (open in a browser). Read all three before non-trivial work.

Locked decisions (see handoff §12)

  • Framework: Laravel 13 · Livewire v3 class-based (NOT Volt) · Tailwind v4 (CSS-first @theme, no tailwind.config.js) · Vite
  • DB: PostgreSQL 17 + TimescaleDB (telemetry hypertables + compression/retention)
  • Realtime: Laravel Reverb + Echo — every channel PRIVATE
  • Queue/Cache: Redis (+ Horizon)
  • MQTT: Eclipse Mosquitto 2 · client php-mqtt/laravel-client · Python discovery sidecar
  • Git: local repo, no remote yet · feature branches, never commit to main
  • Name: "HomeOS" is a working title

Everything runs in containers (R8 — host has only Docker)

The host has no PHP / Composer / Node for project work. Always exec into the stack:

docker compose up -d                       # start stack
docker compose exec app php artisan …      # artisan
docker compose exec app composer …         # composer
docker compose exec app npm …              # node/vite (or: docker compose exec app npm run build)
docker compose logs -f app                 # tail

The app, horizon, scheduler, reverb (and later mqtt-listener) services share one image (docker/app/Dockerfile). Ports are env-driven in .env (APP_PORT, VITE_PORT, REVERB_HOST_PORT, DB_HOST_PORT). HOST_UID/HOST_GID come from id nexxo — never hardcode.

Architecture (handoff §3)

MQTT-first hybrid. Mosquitto is the bus. mqtt-listener (artisan daemon) parses + dispatches only (H2) → Horizon jobs upsert device_states → Reverb private channels → Livewire (Echo). UI action → DeviceCommandService → driver (ShellyMqttDriver first) → device, every command audited in commands (H1). Vendor specifics only in app/Support/Mqtt + app/Support/Drivers (H3).

Design system (handoff §8)

"Enterprise Ops, dark OLED" — UniFi/Grafana-class console, not a consumer app. Tokens live in resources/css/app.css @theme (ported verbatim from the mockup). Status via colored dot/pill + text (never color alone, never emoji, R9). Every numeric value / MAC / IP / timestamp in font-mono with tabular-nums. Hairline borders, no shadows. Accent (cyan) sparingly.

Non-negotiable rules

See rules.md (R1R17 + H1H5). Highlights: class-based Livewire pages (R1/R2), tokens-only styling (R3/R10), wire-elements/modal for confirms (R5), UUID route keys (R11), browser-verify every page (R12), English routes / German labels (R13), self-hosted fonts (R14), Codex review gate (R15), full localization DE+EN (R16). On any conflict: STOP and ask.

Build order (handoff §13)

  1. Bootstrap → 2. Domain core → 3. MQTT ingest → 4. Discovery → 5. Presence → 6. Automations →
  2. Polish + PWA. Each phase ends browser-verified (R12) + Codex-clean (R15) + committed on a feature branch.