Issue 2: Ring sat on "verbinde…" forever because nothing ever connected — the
ring-mqtt bridge (opt-in container) wasn't running, and HomeOS was collecting
Ring credentials it never used. Per the handoff the BRIDGE owns the Ring login
(email/pw/2FA) in its own web UI; HomeOS must not handle Ring credentials.
- Replaced the credential modal with a RingSetup guide: start the bridge
(`docker compose --profile addons up -d ring-mqtt`), open its web UI (linked
at the auto-detected host:port), log in there; devices then appear.
- Status is now honest — it reflects the bridge's REAL MQTT presence
(IngestRingMessage.markStatus), showing "Nicht verbunden" with next-steps
until the bridge actually reports, instead of a stuck "connecting".
- Removed the RingConnect cred modal + fields; reset any stuck status.
Suite green, 12/12 tabs clean; setup modal browser-verified.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
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>
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>
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>