Skip to content

MQTT UX: forgiving broker input + per-GPU Home Assistant sensors#7

Merged
sethpjohnson merged 2 commits into
mainfrom
claude/mqtt-ux-broker-gpu
Jul 17, 2026
Merged

MQTT UX: forgiving broker input + per-GPU Home Assistant sensors#7
sethpjohnson merged 2 commits into
mainfrom
claude/mqtt-ux-broker-gpu

Conversation

@sethpjohnson

Copy link
Copy Markdown
Owner

Summary

Stacked on #6 (the MQTT feature) — merge #6 first. Two UX improvements that came directly out of setting this up against a real Home Assistant install on an R730 with two Tesla P40s.

1. Forgiving broker input + clearer logging. You no longer need tcp://host:port:

  • Enter a bare IP (10.0.1.160), host:port, or even a pasted http://…:8123 — it normalizes to tcp://host:port, defaulting to port 1883. (mqtt://tcp://, mqtts:///tls://ssl://:8883, IPv6 handled, whitespace/trailing-slash trimmed; unparseable input passes through unchanged rather than crashing.)
  • Logs how the broker string was interpreted, warns when the port is 8123 (HA's web UI — the exact mistake that cost real debugging time), and replaces the silent publish-timeout loop with a one-time hint pointing at host/port.

2. Per-GPU sensors (dynamic). Instead of one aggregate GPU temperature, each detected card gets Temperature / Utilization / Power, named GPU N (model) …, under the same HA device. The aggregate stays, renamed "GPU Temperature (Max)" (unique_id unchanged, so HA history survives). State gains a per-card gpus[] array.

  • Discovery is published lazily — on the first tick a card is seen, not just at connect — because MQTT connects a few seconds before the first GPU read at cold start. Re-emitted on reconnect; announced once per index; self-heals on transient failure.

Review & verification

  • Principal review → APPROVED WITH SUGGESTIONS on the two features, then a cold-start discovery bug was caught by real-hardware testing (per-GPU entities never registered because discovery raced ahead of the first GPU read). Fixed (lazy discovery) → re-review APPROVED (reviewer reproduced the bug against pre-fix code to confirm the new tests are genuine, and walked the reconnect race).
  • go vet + go test -race clean across all 8 packages; new cold-start unit + integration tests (real in-process broker).
  • Verified live on Bandit (test build 1.2.0-rc): bare-host broker 10.0.1.160 → connected; all 17 entities on the broker including GPU 0 (Tesla P40) and GPU 1 (Tesla P40) temp/util/power.

Follow-ups filed (non-blocking): more robust broker parser via net/url, GPU index-vs-position hardening, HA state_class for statistics.

Tasks: dex i5zslbn2, df9afuxm

🤖 Generated with Claude Code

sethpjohnson and others added 2 commits July 17, 2026 14:54
Publishes controller status to MQTT and accepts commands, so the R730's
fan state appears in Home Assistant as a self-registered device. Off by
default; zero behavior change when disabled.

- New internal/mqtt bridge (paho) consuming the same controller methods
  the HTTP API uses (GetStatus/SetOverride/ClearOverride/AddHint/
  RemoveHint), so MQTT commands inherit all safety rules: min/max
  clamping, 24h override cap, critical-temp ramp override, hint
  validation. The controller package stays MQTT-unaware.
- MQTT Discovery: one HA device with 11 entities (6 sensors, 3 binary
  sensors, an override-speed number bounded to min/max, a clear-override
  button), retained config republished on every (re)connect.
- Per-tick retained state JSON on monitoring.interval; availability topic
  with Last Will so HA marks the device unavailable the instant the
  process dies.
- Command topics (override/clear/hint, QoS 1) validated by a shared
  internal/validate package extracted from the HTTP layer so both
  enforce identical rules. Retained command messages are dropped (logged
  once per topic) to avoid stale commands replaying on reconnect.
- A hung or unreachable broker can never block the control loop or delay
  the shutdown restore(): connect is fire-and-forget, publish/subscribe/
  stop are bounded, and bridge teardown is sequenced after the BMC
  hand-back with a 3s bound.
- Config: mqtt section (default off, broker required when enabled,
  password json:"-", env overrides); README HA section; Unraid template
  MQTT fields; config.example.yaml documents the block and validates
  verbatim.

Design + plan: docs/superpowers/specs and docs/superpowers/plans.

Task: dex e6fla2yl - MQTT / Home Assistant support

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…earer logging

Two MQTT/Home Assistant UX improvements from real-world setup friction.

Broker input normalization (internal/mqtt/broker.go):
- Accept a bare host (10.0.1.160), host:port, or a pasted http(s):// URL and
  normalize to tcp://host:port, defaulting to port 1883; map mqtt://->tcp://,
  mqtts/tls->ssl:// (8883); handle IPv6 literals; trim whitespace/trailing
  slash. Unparseable input passes through unchanged rather than crashing.
- Log how the broker string was interpreted; warn when the resolved port is
  8123 (Home Assistant's web UI, a common wrong-port mistake); on a stuck
  publish, log a one-time hint pointing at host/port instead of silent timeouts.

Per-GPU sensors (internal/mqtt/discovery.go, state.go):
- Publish one Temperature/Utilization/Power sensor set per detected GPU
  (dynamic count), named 'GPU N (model) ...', under the same HA device, plus
  the existing aggregate renamed 'GPU Temperature (Max)' (unique_id unchanged,
  preserving HA history). State gains a per-card gpus[] array.
- Per-GPU discovery is published lazily on the first tick a card is seen (not
  only at connect, which races ahead of the first GPU read at cold start) and
  re-emitted on reconnect; announced once per index per connection, self-
  healing on transient publish failure.

Controller stays MQTT-unaware; bounded publish/connect/stop, retained-command
drop, and zero-activity-when-disabled all preserved. Verified on a live R730
with two Tesla P40s: bare-host broker connects and both cards register as
individual HA entities.

Tasks: dex i5zslbn2, df9afuxm

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sethpjohnson
sethpjohnson changed the base branch from claude/mqtt-ha-support to main July 17, 2026 21:03
@sethpjohnson
sethpjohnson merged commit 769b959 into main Jul 17, 2026
@sethpjohnson
sethpjohnson deleted the claude/mqtt-ux-broker-gpu branch July 17, 2026 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant