Repository navigation
feat: clock + server autodiscovery + AWTRIX device-control tab - #1
Merged
Merged
Conversation
Pure-Go DNS-SD (browse + advertise); keeps the distroless build CGO-free. Backs server-side clock discovery and _ember._tcp advertising.
Probes resolved _http._tcp hosts at /api/stats and keeps those returning AWTRIX-shaped stats (non-empty uid). The HTTP fingerprint/filter is unit-tested via httptest; the dnssd browse wrapper is thin and verified on-device. Reachable lets the server health-check a configured clock URL before auto-discovery.
…t wiring Effective clock URL = store override > reachable config.json baseline > mDNS auto-pick (in-memory only; the read-only config.json is never written). The menu-chosen URL persists in the writable store (deviceSource reports the origin). initDeviceDiscovery runs once at boot before the coordinator publishes; the awtrix_reachable doctor check now notes the source.
…t+validate) Thin passthrough of AWTRIX /api/settings behind bearer auth: GET filters the device's settings to the managed keys; PUT validates each value against a per-key whitelist (range/type/colour/enum) before forwarding, so the proxy can't poke arbitrary firmware settings. stats/reboot/notify-dismiss proxy the matching device endpoints.
Announces the Ember server over mDNS so the macOS app can discover it; TXT carries version + health path. Default on; non-fatal and a no-op on bridge networking. Gate + port parsing are unit-tested; the responder is verified on-device.
Typed wrappers over /v1/device/*. DeviceSettings maps the AWTRIX keys with all-optional fields (partial PUT omits nils) and a lenient per-field decode so an unexpected device JSON type for one field can't break the whole tab load.
NWBrowser finds Ember servers; a short-lived NWConnection resolves each to a concrete host:port for an http URL. Degrades silently when local-network access is denied or nothing is found.
New Tahoe sidebar Device tab mirrors the AWTRIX3 app (General / Native Apps / Time & Date / Actions) with debounced auto-apply, native colour pickers, a battery+firmware header, and a confirmed Reboot. The Connection tab lists Bonjour-discovered Ember servers (tap to fill the URL). Info.plist gains the local-network usage string + _ember._tcp Bonjour service. AppEnvironment wires DeviceService + ServerDiscovery and rebuilds device on reconnect.
…R_MDNS_ADVERTISE) Document the mDNS discovery model, the host-networking requirement, the clock-URL resolution precedence, and the new /v1/device/* endpoints across AGENTS, RUNBOOK, and ARCHITECTURE.
… label - /admin/reload now re-applies the menu-persisted clock URL (was dropped back to the file-config baseline until restart). [codex P2] - BrowseAWTRIX probes concurrently under a fresh bounded context so the /api/stats fingerprint phase honours the discovery time budget regardless of host count. [codex P3] - deviceSource() reports 'discovered' (not 'config') when boot discovery picked a URL that happens to equal an unreachable config baseline. - settings PUT decodes via the shared decodeJSON helper; go mod tidy drops the stale dnssd // indirect tag; add device-5xx→502 and colour-array reject tests.
… stop() Resolution connections were only cancelled on .ready/.failed; a .waiting connection (advertised host with a filtered/refused port) would retry forever and leak. Track in-flight connections, fail fast on .waiting, and cancel all on stop().
This was referenced Jun 10, 2026
This was referenced Sep 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds LAN autodiscovery in both directions plus an AWTRIX3-style Device-control tab, in one cohesive change:
_http._tcp+ a/api/statsfingerprint (non-emptyuid) replaces the hardcodedawtrix.http_base_url. Effective clock URL resolves as writable-store override > reachableconfig.jsonbaseline > mDNS auto-pick (auto-pick is in-memory only; the read-onlyconfig.jsonis never written). New bearer endpointsGET /v1/device/discover,GET/PUT /v1/device/config;/admin/doctornotes the source._ember._tcp(gated byEMBER_MDNS_ADVERTISE, default on); the macOS app browses viaNWBrowserand the Connection tab gains a "Discovered servers" picker. Manual entry remains the fallback.GET/PUT /v1/device/settings,GET /v1/device/stats,POST /v1/device/{reboot,notify/dismiss}— the server whitelists + range-validates every AWTRIX key before forwarding, so the server stays the only writer to the device.New dependency:
github.com/brutella/dnssd(pure-Go DNS-SD, browse + advertise; distroless build stays CGO-free).mDNS multicast doesn't cross the default Docker bridge. Run the Unraid container with
--network host(or macvlan) for either discovery direction to work. The Device tab + clock control still work with a configured/discovered URL regardless.Testing
go test ./... -racegreen;go vetclean;gofmtclean. New table tests cover the fingerprint/filter, config-resolution precedence (store/config/discovered/none), settings whitelist + range/type/colour/enum/string validation, the proxy GET-filter and PUT-validate-before-forward, device-5xx→502 mapping, and the advertise gate/port parsing.swift test(72 tests) green; EmberKit + app target build clean under Swift 6 strict concurrency. Tests cover theDeviceSettingsAWTRIX-key mapping, partial-PUT encoding, and lenient decode of unexpected device types.Reviewed
Reviewed by Codex and an independent review agent before opening; their findings were applied:
/admin/reloadnow re-applies the menu-persisted clock URL (was reverting to the file baseline until restart).BrowseAWTRIXprobes concurrently under a bounded context so the fingerprint phase honours the discovery budget regardless of host count.NWConnections are cancelled on.waiting/terminal states and onstop()(no lifetime leak).deviceSource()reportsdiscovered(notconfig) when boot discovery re-finds an unreachable baseline URL.Not covered (needs on-device pass)
mDNS browse/advertise and
NWBrowserresolution can't be unit-tested deterministically. On a host-networked container, verify: clock discovery from a fresh config, server discovery from the app, a brightness/volume/native-app round-trip, Reboot + Dismiss, and that Pomodoro still transiently ownsATRANS/BLOCKN.