Commit Graph

15 Commits (23ef993cb27930552cf8604c50c72a77159b84ce)

Author SHA1 Message Date
HomeOS Bootstrap 23ef993cb2 Fix R15: per-device MQTT ACLs, monotonic presence, real-device online semantics
- ACL: drop the shared `shelly` account + `+` wildcard (allowed cross-device
  spoofing/control). Devices now authenticate with per-device credentials
  (username = topic prefix, provisioned at onboarding) bound to their own prefix
  via `pattern %u`. gen-passwd.sh creates only laravel + sidecar.
- last_seen_at is set from the message receive time (observed_at), monotonically,
  so a delayed/retried/stale ingest job can't mark a device online incorrectly.
- Device::isOnline: a real device with no last_seen is OFFLINE (never connected);
  only demo devices are assumed reachable. Added `demo` flag + presence tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 23:08:29 +02:00
HomeOS Bootstrap 66f0d7b067 Fix R15: reject out-of-order MQTT state updates (concurrency)
With multiple Horizon workers, ingest jobs for one entity can finish out of
order and overwrite newer state with older. The listener now stamps each message
with a µs receive time (observed_at); IngestShellyMessage applies state only when
the incoming message is newer (race-safe via a conditional update + unique guard),
and broadcasts only when applied. Added a feature test for the ordering guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 23:01:32 +02:00
HomeOS Bootstrap 00fac6a910 Fix R15: control Shelly Gen2+ via RPC (Switch.Set/Light.Set), not command topic
Gen2+ devices ignore <prefix>/command/<component>; they take JSON-RPC on
<prefix>/rpc. The driver now sends Switch.Set / Light.Set (and Shelly.Reboot),
so commands actually change device state instead of being audited as a no-op.
Verified: a toggle publishes {"method":"Switch.Set","params":{"id":0,"on":true}}
to <prefix>/rpc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:56:50 +02:00
HomeOS Bootstrap 1eec714d4a Fix R15: pass MQTT passwords to the broker without shell interpolation
gen-passwd.sh embedded passwords in `sh -c "..."`, which breaks or injects for
strong passwords containing quotes/$/;. Export them and forward with bare
`-e NAME`, reading them as env vars inside a single-quoted container script — any
character is now handled safely.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:54:03 +02:00
HomeOS Bootstrap ea9e35883b Phase 3: MQTT ingest — Mosquitto bus, Shelly driver, live device state
Real bidirectional MQTT so devices are live, not mock (handoff §13.3):
- Mosquitto 2 broker (auth + per-client ACLs for laravel/shelly/sidecar from day
  one); passwd generated by docker/mosquitto/gen-passwd.sh (gitignored).
- mqtt-listener daemon: subscribes `+/status/#`, parse + dispatch only (H2),
  exponential reconnect backoff, graceful SIGTERM. php-mqtt/laravel-client.
- Ingest path (H4): IngestShellyMessage resolves device by mqtt_prefix, upserts
  device_states, refreshes last_seen, broadcasts DeviceStateChanged
  (ShouldBroadcastNow) on the private `home` channel.
- Control path (H1): DeviceDriver contract + ShellyMqttDriver (command topic +
  Shelly.Reboot RPC) behind DeviceCommandService, which audits every command to
  the new `commands` table. Device detail toggles + restart route through it;
  flash reflects the real result.
- Live UI: dashboard + device pages listen via Echo (#[On('echo-private:home,
  .DeviceStateChanged')]) and re-render instantly.
- Vendor specifics isolated in Support/Mqtt + Support/Drivers (H3).

Verified end-to-end in a real browser: publishing an MQTT status turned a light
"An" on the dashboard in 3.0s with no reload, 0 console errors. R12 30/30;
15 feature tests green (incl. ingest + command audit). README/bootstrap document
the broker passwd step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:51:14 +02:00
HomeOS Bootstrap 2a39f1cc4e Fix R15: require prod hardening in docs; drop N+1 in warnings
- Document that production must set APP_ENV=production + APP_DEBUG=false (else
  exceptions leak and the demo household seeds); note it in .env.example too.
- HomeStatus::warnings now iterates devices → entities using the already-loaded
  device, instead of $entity->device, removing an N+1 on dashboard render.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:03:20 +02:00
HomeOS Bootstrap 52af82115d Fix R15: gate demo seeder to local/testing
`migrate --seed` runs in the documented bootstrap; without a guard a production
deploy would get the mock household (stale/open/low-battery devices) in real
tables. DemoHomeSeeder now only runs in local/testing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:00:25 +02:00
HomeOS Bootstrap 1520b95f8f Fix R15: include room-less devices in home status
HomeStatus aggregated from Room, so a device with room_id = null (a supported
state, e.g. after picking "no room") vanished from dashboard totals, KPIs and
warnings. Aggregate from all devices instead; the dashboard groups them by room
with a trailing "Ohne Raum" group so nothing disappears. Added a feature test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:58:35 +02:00
HomeOS Bootstrap dc27b1686c Fix R15: no weak admin fallback in prod; validate device room update
- DatabaseSeeder throws outside local/testing when HOMEOS_ADMIN_PASSWORD is
  unset, instead of silently seeding the documented `homeos-dev` password
  (bootstrap always runs migrate --seed, so a prod misconfig must fail loudly).
- Devices\Show::saveRoom validates roomId as nullable|exists:rooms,id, so a
  crafted request can no longer trigger a foreign-key 500. Added a feature test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:55:21 +02:00
HomeOS Bootstrap f00a1ac069 Warnings → modal, device detail page, stable mock online state
- Warnings no longer clutter the dashboard: a compact "Warnungen (n)" button in
  the topbar opens a themed wire-elements/modal (R5) listing all messages. Dark
  modal wrapper overridden; HomeStatus service shared by dashboard + modal.
- Device detail page (/devices/{uuid}, UUID route key): edit name, room and
  active state; view info + live capabilities; Neustart (with confirm modal) and
  "Update prüfen" as mock commands (Phase 3 routes them through the real driver).
  Devices index (/devices) added; "Geräte" nav activated; dashboard device rows
  and index link to the detail. Generic Confirm modal + x-detail component.
- Mock devices no longer rot: online is now "active and (no last_seen or seen
  <10min)", so the demo stays healthy; the one offline device keeps a stale
  timestamp. Full DE/EN i18n for devices + modal copy.

Verified: R12 30/30 in headless Chromium (0 console errors, 0 failed requests);
10 feature tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:52:57 +02:00
HomeOS Bootstrap 1b69c39a93 Fix R15 findings: fresh-clone bootstrap + never seed a blank admin password
- Document the first-run path (deps/assets/key/migrate aren't auto-installed
  because vendor/node_modules/public-build are gitignored): add
  docker/app/bootstrap.sh and a README with the exact sequence to run before
  `docker compose up`.
- Seeder: fall back with `?:` so a present-but-empty HOMEOS_ADMIN_PASSWORD can
  never create a blank-password admin; .env.example ships a non-empty dev value.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:24:10 +02:00
HomeOS Bootstrap fcb6daa6ab Home dashboard + Host tab: device state & warnings, server status split out
Reworks the dashboard into a home-control view per user feedback (it is not a
server dashboard):
- Dashboard now shows home data — rooms with live device state (lights on/off,
  power draw, window/door contacts, battery %), a home summary (devices online,
  lights on, open contacts, low batteries), and a Warnings panel that only lists
  what needs attention (offline device, open window, low battery, degraded host
  service). No server internals on the dashboard.
- Server/service health (DB, Redis, Reverb, Horizon) + version info moved to a
  dedicated "Host & Dienste" page (new nav item under System); dashboard surfaces
  a host problem only as a warning that links there.
- New domain slice (handoff §3, mock-first §13.2): rooms/devices/entities/
  device_states migrations + models (UUID route keys, R11) + DemoHomeSeeder with
  Shelly-like devices incl. deliberate faults. Extracted SystemHealth service.
- Sidebar decluttered further; new x-entity-state component; full DE/EN i18n.
- Fixed R15 findings: .env.example now ships matching non-empty dev defaults for
  DB_PASSWORD and Reverb keys so a fresh `cp .env.example .env` boots cleanly.

Verified: R12 21/21 in headless Chromium (0 console errors, 0 failed requests,
breakpoints 375/768/1280); 10 feature tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:20:38 +02:00
HomeOS Bootstrap d4f667437c Dashboard: live service health + fault surfacing; declutter sidebar
Addresses user feedback on Phase 1:
- Dashboard reoriented from static stack facts to LIVE operational status.
  New health banner surfaces faults prominently (green when all online;
  amber/red with a pluralized problem count when a service is down).
  Service tiles probe DB, Redis, Reverb (socket) and Horizon in real time
  with latency; versions moved to a small, de-emphasized "System" block.
  wire:poll.10s + a refresh button keep it live. Verified: stopping Reverb
  flips the banner to "Ein Dienst mit Störung" and the Echtzeit tile to
  Offline/nicht erreichbar (screenshotted), healthy state restores clean.
- Sidebar decluttered: removed the wrapping "In Kürze" count badges;
  coming-soon items are now dimmed (still show lock icons where gated).

R12 re-verified in headless Chromium: 15/15 assertions, 0 console errors,
0 failed requests, breakpoints 375/768/1280. 7 feature tests still green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:06:20 +02:00
HomeOS Bootstrap beac04ec51 Phase 1: bootstrap — Docker stack, Laravel 13, design system, auth
Foundation for HomeOS (handoff §13.1). Verified in a real browser (R12):
HTTP 200, zero console errors, zero failed requests, breakpoints 1280/768/375,
no leaked template/lang literals. Feature test suite green (7 passing).

Stack (locked, handoff §12): Laravel 13, Livewire v3 (class-based, no Volt),
Tailwind v4 (@theme, no config), PostgreSQL 17 + TimescaleDB, Redis + Horizon,
Reverb (private channels, proxied same-origin via nginx), Vite. Everything runs
in containers (R8); one app image shared by app/horizon/scheduler/reverb.

- docker: app (php-fpm 8.4 + nginx + supervisor), db (timescale pg17), redis,
  reverb, horizon, scheduler; env-driven ports; HOST_UID/GID from id nexxo
- design system ported verbatim from design-mockup.html into @theme (§8);
  self-hosted Plus Jakarta Sans + IBM Plex Mono woff2 (R14)
- Blade component kit: icon, panel, kpi, status-dot/pill, badge, toggle,
  room-card, device-chip, sidebar, topbar; app + guest layouts (mobile drawer)
- auth: class-based Livewire Login (throttled) + Dashboard; English routes,
  German UI; full DE/EN localization (R16)
- rules.md + CLAUDE.md; admin user seeded from gitignored .env

Deferred by design: php-mqtt/laravel-client (Phase 3), mosquitto/mqtt-listener
(Phase 3), discovery sidecar (Phase 4).

R15 (Codex review) pending user `codex login`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:06:20 +02:00
HomeOS Bootstrap ede174e50b Initialize repository 2026-07-17 21:05:37 +02:00