Commit Graph

30 Commits (60d52758193ec05d6589d6b60efadc27bbde2427)

Author SHA1 Message Date
HomeOS Bootstrap 60d5275819 feat(presence): who's-home overview, person edit/delete, avatars, faster away
Issue 1 — presence was half-built:
- Away detection: the debounce was 8 min (you disconnect, still "home"). Now
  3 min and configurable (homeos.presence.away_debounce_minutes /
  PRESENCE_AWAY_DEBOUNCE).
- Person management: ManagePerson modal handles BOTH add and edit (name, UniFi
  client, avatar). Delete from the person card (confirm). Replaces AddPerson.
- Avatar upload: image stored on the public disk (storage:link), shown on cards
  + the who's-home strip; initials fallback (green when home).
- Who's home: a summary strip ("N Zuhause · M abwesend" with avatars) + clear
  per-person home/away, so you can see at a glance who is in.

5 tests (debounce, create+avatar, edit, delete+avatar removal). Suite 78 green,
12/12 tabs clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 10:20:49 +02:00
HomeOS Bootstrap f3ae29d65a feat(rooms): create + delete rooms (delete unassigns devices)
Issue 5: rooms couldn't be created or deleted.
- "Raum anlegen" button + CreateRoom modal (name + icon picker) on the rooms
  index.
- Delete on the room page behind a confirm that states how many devices are in
  it; on delete the devices are KEPT but moved to "no room" (room_id null).

3 tests. Suite green, 12/12 tabs clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 10:12:49 +02:00
HomeOS Bootstrap f028528f1f feat(ui): standard modal shell — fixed header/footer, scrollable body, 2-col
Issue 3: the New-Automation modal was taller than the screen and the whole
dialog scrolled. New <x-modal> component: header + footer are fixed (shrink-0),
only the body scrolls, capped to max-h-85dvh so the page never scrolls. Refactored
create-automation (now a responsive 2-column grid, widened to 3xl), add-device,
ring-connect and add-person onto it — the standard for every modal going forward.
Verified: modal fits a 720px viewport with no page scroll; create flow still works.

12/12 tabs clean, suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 10:09:24 +02:00
HomeOS Bootstrap 2fa15decb4 feat(devices): instant toggle, icon picker, real update check, transport switch
- Issue 6 (2-3s toggle lag): the command service now writes the expected state
  immediately for ALL devices (not just demo). The card flips within the
  Livewire round-trip; the real MQTT echo / post-command HTTP poll reconciles it
  (monotonic — newer observed_at wins). Toggling now feels instant.
- Issue 7 (wrong icon): Device::displayIcon() with a per-device override
  (config.icon) + a picker on the device page (lamp/led/plug/window/door/
  doorbell/sensor/…); cards use it. New icons added.
- Issue 8 (dead "Update prüfen"): real Shelly.CheckForUpdate over HTTP now
  reports available version / up-to-date / unsupported instead of a demo string.
- Issue 9 (no MQTT option): device page can switch a Shelly between Local (HTTP,
  probed) and MQTT transport; driverFor already routes by protocol.

Suite 71 green, 12/12 tabs clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 10:04:19 +02:00
HomeOS Bootstrap e3ad653582 feat(shelly): local HTTP/RPC transport — add by IP like Home Assistant
You were right that HA uses the Shelly LOCAL API, not MQTT. Adds that path
(user chose "both") — control + status over http://<ip>/rpc, no MQTT setup on
the device:
- ShellyRpc (POST /rpc), ShellyHttpDriver (Switch/Light.Set, Reboot).
- ShellyStatusApplier: one shared apply path (normalize + input roles +
  monotonic upsert + broadcast) reused by BOTH the MQTT ingest and the HTTP
  poll, so transports can't drift. IngestShellyMessage refactored onto it.
- ShellyLocalOnboarder: probe an IP → GetDeviceInfo/GetStatus → create an
  http-protocol device with its entities (reuses an MQTT-onboarded row by id,
  no duplicate). AssignDevice uses it when a discovered Shelly is reachable;
  falls back to MQTT-style if not.
- Manual "Gerät hinzufügen" modal (add by IP). shelly:poll scheduled every 10s
  + a re-poll after each command (PollShellyDevice) for near-live status.
  driverFor picks http vs mqtt by protocol.
- Normalizer now drops housekeeping components (sys/wifi/cloud/mqtt/ws/…) so
  GetStatus doesn't create junk entities.

6 ShellyHttpTest cases (Http::fake). Suite 68 green, 12/12 clean.
LIVE-VERIFIED against the real Shelly 1 Mini Gen3 at 10.10.30.78: onboarded
over local API (protocol http), entities switch:0 + input:0, kept online by the
10s poll — no MQTT configured on the device.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 09:48:35 +02:00
HomeOS Bootstrap ecfc49e665 feat(devices): auto-detect host IP, discovery dedup, delete + rescan
Addresses the device-management feedback:
- The MQTT server address is no longer hardcoded (was 10.10.90.110). It's
  auto-detected from the address you open HomeOS at (App\Support\HostAddress —
  request host + port), so it follows a DHCP/changed IP. MQTT_DEVICE_HOST is now
  an optional override only. Settings + device page use it.
- Discovery dedup: a finding whose prefix already belongs to a device (assigned
  OR auto-onboarded) no longer shows under "Neue Geräte" — fixes the device
  appearing both in discovery and in Geräte.
- Delete device: confirm-guarded button on the device page; deleting frees the
  discovery finding so the device can be re-added.
- "Neu scannen" button on the network page → publishes homeos/sidecar/rescan;
  the sidecar (now a subscriber) re-queries mDNS. ACL grants it read on that
  topic. paho-mqtt pinned to the v2 callback API.

+4 tests (host override/fallback, dedup, delete frees finding). Suite 62 green,
12/12 clean. Live-verified: rescan reaches sidecar; duplicate Shelly gone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 09:34:07 +02:00
HomeOS Bootstrap 449533b60d fix: preserve raw input value on role/invert change; strict isOnline (R15)
Codex R15 on the previous fix:
- [P2] reclassifyInput derived the raw input level from the already-displayed
  (inverted) contact state, so toggling inversion twice didn't restore the
  original. The ingest + device page now carry the raw `on` inside the contact
  state, so flipping role/inversion is fully reversible. +1 round-trip test.
- Latent: Device::isOnline() could return null (null demo, no last_seen) and
  break the type hint / views. Now returns a strict bool.

Suite 58 green, 12/12 tabs clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 09:03:00 +02:00
HomeOS Bootstrap 71b3028a8e fix: harden MQTT sharing + input roles + dedup (adversarial review)
Multi-agent review (5 confirmed) + Codex found real issues in the rework:

- [P1 security] Shared `shelly` ACL had readwrite +/rpc, so a compromised
  device could inject Switch.Set into ANY other device's <prefix>/rpc. Now
  read-only on +/rpc (receive own commands) + write only homeos/rpc (reply).
  Broker-verified: a shelly publish to victim/rpc is denied; laravel's is not.
- [P2 security] `+` wildcard reaches reserved homeos/ring namespaces → bogus
  device. Ingest now rejects RESERVED_PREFIXES (homeos/ring/$SYS).
- [P2 security] Unbounded auto-onboarding = DB-exhaustion DoS. Added a device
  cap (homeos.mqtt.max_devices, default 250).
- [P2 correctness] Every Shelly `input` became a phantom window contact (wall
  switches shown as windows, possibly inverted). `input` is now a generic
  sensor; the user PROMOTES specific inputs to window/door contacts on the
  device page (invert-aware), stored in config->input_roles and applied in the
  ingest — this is the "assign contacts" flow the user asked for.
- [P3 ux] Motion pill read `active`; producer writes `on`. Now reads both.
- [P1 migrations] Dedup computed survivor keys once; 3+ duplicates could
  collide on unique(device_id,key). Re-query per duplicate (ring + mqtt_prefix).

+9 tests (reserved prefix, cap, input generic/promoted/inverted, demo echo).
Live-verified: input published → onboarded as input → assigned window contact
via UI → appears on Fenster page, persists across messages. Suite 57 green,
12/12 tabs clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 08:59:12 +02:00
HomeOS Bootstrap 2edffadbfd feat(panel): make tiles actually control + entity naming + reorder feedback
Addresses "I arrange tiles but nothing changes":
- Demo devices have no hardware to echo a new state, so toggling looked dead.
  DeviceCommandService now simulates that echo for demo devices (writes the new
  state + broadcasts DeviceStateChanged) — the mock home is fully interactive
  (handoff §13.2). Real devices are untouched; their own status message applies.
- Panel tiles show the entity name (device · room as sub) and drive the shared
  TogglesEntities toggle; wire:loading guards double-taps.
- Drag-reorder now confirms with a "Layout saved" cue so it doesn't feel inert.

+1 test (demo toggle simulates echo; real device does not). Browser-verified:
tapping an "Aus" tile flips it to "An". Suite 52 green, 12/12 clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 08:37:17 +02:00
HomeOS Bootstrap d7d8544e52 feat(ui): card layout replaces flat lists across the app
Addresses "the list on every page isn't good": adopt the approved mockup's
card design (icon well, name, sub, live state chips, footer toggle).
- New <x-device-card>: per-device card with online dot, cloud badge, entity
  chips and an inline switch/light toggle (active cards highlighted accent).
- TogglesEntities trait → dashboard, devices index, rooms show all drive the
  same toggle (H1, audited); card grids replace the divide-y rows.
- Dashboard + devices grouped into responsive card grids by room.
- Windows page: sensor cards showing closed/open/tilted prominently, low-battery
  chip, open sensors highlighted (screenshot-verified).
- Rooms index already card-based; left as-is.

Suite 51 green, 12/12 tabs clean, zero console errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 08:32:01 +02:00
HomeOS Bootstrap fef393954c feat(mqtt): Home-Assistant-style onboarding — shared account + auto-create
Addresses "I have to configure MQTT per device; HA just works":
- Shared device account `shelly` (one credential every Shelly uses) with a
  SCOPED ACL (status/events/online/rpc for any prefix; cannot touch homeos/,
  ring/ or $SYS). gen-passwd seeds it from MQTT_SHELLY_PASSWORD.
- Auto-onboarding: IngestShellyMessage creates the device on the first
  recognizable component (sys/wifi/cloud noise ignored), so pointing a Shelly
  at the broker is all it takes — no manual "Zuweisen". Partial unique index on
  config->>'mqtt_prefix' + race-safe create (merge-dedup migration).
- Settings → Geräte-MQTT card: server, username, reveal/copy password + steps
  (config/homeos.php, MQTT_DEVICE_HOST). Discovery "Zuweisen" now upserts by
  prefix (names/rooms an already-onboarded device, no duplicate) and no longer
  forces per-device creds.
- Per-device credentials kept as opt-in hardening: a "generate" button on the
  device page (pattern %u ACL retained).

Live-verified: publishing as `shelly` to a new prefix auto-creates the device
with switch+power state; a sys topic creates nothing. Suite 46 green, 12/12
tabs clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 08:20:57 +02:00
HomeOS Bootstrap 52c1c5d3ac feat(addons): installable integrations framework + Ring connect UI
First half of the Ring integration (handoff §12: ring-mqtt, cloud token auth):
- Addons registry (static catalogue) + Addon model with encrypted config, so
  the Ring refresh token / credentials are never stored in plaintext.
- AddonService: install / uninstall (wipes secrets) / saveConnection (blank
  password keeps the stored one) / markStatus (from the bridge over MQTT).
- Addons page (new sidebar tab) with per-addon install, Cloud badge, live
  status pill, and a Ring connect modal capturing email/password/2FA — we
  never call Ring ourselves; creds are handed to the ring-mqtt sidecar.
- Uninstall behind a wire-elements confirm (R5). Fixed the confirm target:
  Livewire names App\Livewire\Addons\Index as "addons" (Index suffix dropped),
  and only $wire.$dispatch (not Alpine $dispatch) reaches the modal listener.
- DE/EN localization (R16); doorbell/package/cloud icons.

7 AddonTest cases (registry, install, encryption-at-rest, blank-password
keep, uninstall wipe, status guard). Suite 33 green; 12/12 tabs clean; full
install→connect→connecting→uninstall UI flow browser-verified, 0 console errors.

Backend bridge (ring-mqtt container) + Ring MQTT normalizer land next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 01:47:02 +02:00
HomeOS Bootstrap d10172ac26 feat(automations): trigger→condition→action engine with cooldown + dry-run
- AutomationEngine: state_change + time triggers, cooldown debounce, dry-run
  logs instead of switching (H5), actions switch via DeviceCommandService
  (source=automation, audited H1) or notify via log.
- EvaluateAutomations: queued listener on DeviceStateChanged (keeps ingest
  fast, H2). automations:tick command scheduled every minute for time rules.
- CreateAutomation modal (wire-elements) wired to Automations index; DE/EN
  keys (R16). dry_run column migration + model cast.
- AutomationTest: 6 cases (trigger match, mismatch, cooldown, dry-run,
  time tick, disabled). Full suite 23 green; 11/11 tabs clean (R12).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 01:11:02 +02:00
HomeOS Bootstrap 124e667a0a Tablet control panel: touch tiles, drag-reorder, light colour modal
New "Steuerung" page for tablet control:
- Large touch tiles for every light/switch — tap toggles (through
  DeviceCommandService), highlighted when on, live via Echo.
- Drag-to-reorder via SortableJS (handle per tile); order persisted to
  entities.panel_sort.
- Lights get a colour/brightness modal → Light.Set (brightness + rgb) on the
  Shelly, with preset swatches + a custom colour picker. Driver contract gains
  setLight(); command service audits it (H1).

Nav check 11/11 tabs clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:58:24 +02:00
HomeOS Bootstrap a4edbbf801 Fix R15: provision a per-device MQTT credential when assigning a device
Assigning a Shelly now issues a real broker credential so the physical device can
connect (onboarding was incomplete before):
- MqttCredentialProvisioner writes a mosquitto-compatible PBKDF2-SHA512 ($7$)
  password line (in PHP) for username = the device's topic prefix, and touches a
  reload trigger. A small wrapper in the mosquitto container (docker/mosquitto/
  config/entrypoint.sh) SIGHUPs mosquitto so it re-reads the passwd live — no
  restart. Verified: a provisioned device authenticates and publishes to its own
  prefix (bound by the pattern %u ACL).
- The credential is shown once on the device page after assignment (enter it into
  the Shelly). passwd is app-owned + world-readable so the web request can write it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:50:53 +02:00
HomeOS Bootstrap af951d9f91 Charts: Chart.js MQTT + host traffic on the Host page
- metrics table + Metric model; metrics:sample command (scheduled every minute,
  pruned to 24h) records MQTT throughput (a Redis counter the ingest jobs bump)
  and host CPU load + memory% from /proc.
- Chart.js line-chart Alpine island (themed from CSS tokens, dual axis) on the
  Host page shows MQTT/min, CPU load and memory %. wire:ignore so the 10s health
  poll doesn't re-init it. Nav check 10/10 clean (0 console errors).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:40:32 +02:00
HomeOS Bootstrap 7d4c9c024e Fix R15: Shelly prefix from mDNS instance, discovery broadcast once, targeted confirm
- AssignDevice derives the Shelly MQTT prefix from the mDNS service instance name
  (the device id) instead of the slugified topic identifier (which carries the
  _shelly._tcp suffix); non-Shelly devices get no prefix.
- IngestDiscoveryMessage broadcasts DeviceDiscovered only on first creation, not on
  every retained/periodic re-publish.
- The generic Confirm modal can target its event at the opening component (->to),
  so a shared event name can't be caught by an unrelated component.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:34:56 +02:00
HomeOS Bootstrap b85d2f60fe Phase 5: UniFi presence — poll, person↔client mapping
- UnifiClient service (art-of-wifi/unifi-api-client) talks to the local UDM with a
  read-only account (self-signed cert, verify_ssl off). Verified live: logged in
  and read 32 active clients.
- presence:poll command (scheduled every minute, withoutOverlapping): "home"
  immediately on association, "away" only after an 8-min debounce so iPhone WLAN
  sleep can't cause false-aways. Broadcasts PresenceChanged on the presence channel.
- Persons page: "Person hinzufügen" modal picks the representing device straight
  from the live UniFi client list (falls back to manual MAC entry if UniFi is down);
  the sweep then tracks that person's presence. mac + last_seen_home_at on persons.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:31:32 +02:00
HomeOS Bootstrap f50a665735 Phase 4: network discovery — sidecar, ingest, "Neue Geräte" + assign
- Python discovery sidecar (zeroconf mDNS over host network) publishes findings
  to homeos/discovery/<source>/<id> as the sidecar user. Compose service with
  network_mode: host + NET_RAW. Verified live: it found real devices on the LAN
  (a printer and Shellys) via mDNS.
- Listener also subscribes homeos/discovery/#; IngestDiscoveryMessage upserts
  discovery_findings (preserving assigned/ignored) and broadcasts DeviceDiscovered
  on the private discovery channel.
- "Neue Geräte" page lists findings live with Assign (modal → creates a Device
  and links the finding) and Ignore/Restore. Per-device broker credentials are
  provisioned at onboarding (noted in the assign hint).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:25:17 +02:00
HomeOS Bootstrap 5cde2786aa All sidebar tabs are real pages (zero console errors)
Persons & Anwesenheit, Netzwerk & Discovery, Automationen, Einstellungen and
Zugang & Face-ID are now working Livewire pages (were dead "In Kürze" buttons).
Adds persons / discovery_findings / automations tables + models; authorizes the
presence + discovery broadcast channels; silences MQTT debug-log spam. Nav check
10/10 tabs clean (200, 0 console errors, 0 failed requests).

Deeper behaviour (presence polling, discovery sidecar, automation engine) lands
in the respective phase; the pages + schema are in place and render live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:16:16 +02:00
HomeOS Bootstrap cc81e309b3 Fenster & Türen page: contact/motion sensor overview
Activates the windows nav tab. Summary banner (how many open) + per-room sensor
list with open/closed state and battery, live via Echo. Nav check 5/5 clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:07:44 +02:00
HomeOS Bootstrap f4d8f27db4 Rooms pages: index (per-room status) + show (devices with live control)
Activates the Räume nav tab. Index lists rooms with online/lights/open counts;
show lists a room's devices with entity chips and switch/light toggles (through
DeviceCommandService), live via Echo. Zero console errors (nav check: 4/4 clean).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:05:30 +02:00
HomeOS Bootstrap aceb140aac Fix mobile sidebar readability; rework device list for scale
- Sidebar drawer is now opaque (.sidebar-tint solid), so on mobile the bright
  content no longer bleeds through and it stays readable.
- Devices index reworked for ~50 devices: live search (name/model/vendor),
  room + online/offline filters (URL-persisted), grouped by room with counts —
  scannable instead of one long list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 23:30:15 +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 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 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