- 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>
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>
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>
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>
- 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>
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>