WLED controller auto-discovery (mDNS) and segment import from the controller #50

Closed
opened 2026-07-15 15:33:12 +00:00 by claude-bot · 1 comment
Contributor

Cut onboarding friction to near zero: today users hand-type an IP and manually enter segment indexes.

Discovery: WLED advertises _wled._tcp via mDNS. A "Discover controllers" button in Settings scans and lists found devices (name, IP, LED count from /json/info) and applies the selection. Caveat: mDNS from inside Docker needs host networking or an mDNS reflector — document that, and keep manual IP entry as the fallback path.

Segment import (works regardless of discovery): read GET /json/state seg[] + /json/info and prefill the segments table — index, bounds, WLED segment names where set — defaulting mode to themed. Show a diff/confirm step instead of silently overwriting existing segment config.

  • zeroconf (or similar) discovery service + endpoint
  • "Import segments from controller" endpoint + Settings UI with confirm/diff
  • Docs: Docker networking note for mDNS
  • Note: WLED_HOST is env-only today; decide whether discovery writes a runtime setting that overrides it
Cut onboarding friction to near zero: today users hand-type an IP and manually enter segment indexes. **Discovery:** WLED advertises `_wled._tcp` via mDNS. A "Discover controllers" button in Settings scans and lists found devices (name, IP, LED count from `/json/info`) and applies the selection. Caveat: mDNS from inside Docker needs host networking or an mDNS reflector — document that, and keep manual IP entry as the fallback path. **Segment import (works regardless of discovery):** read `GET /json/state` `seg[]` + `/json/info` and prefill the segments table — index, bounds, WLED segment names where set — defaulting mode to `themed`. Show a diff/confirm step instead of silently overwriting existing segment config. - [ ] `zeroconf` (or similar) discovery service + endpoint - [ ] "Import segments from controller" endpoint + Settings UI with confirm/diff - [ ] Docs: Docker networking note for mDNS - [ ] Note: `WLED_HOST` is env-only today; decide whether discovery writes a runtime setting that overrides it
claude-bot added this to the v1.1.0 milestone 2026-07-15 15:33:12 +00:00
Author
Contributor

Landed in #129 (squash-merged to main). All four checkboxes done.

Verified against the live controller before the tests were written, not after — which mattered, because the mDNS shape in the docs and the one the device actually broadcasts are not the same thing:

wled-450664._wled._tcp.local. -> 192.168.1.140:80, TXT {mac: 78421c450664}

So the instance name is the label, the TXT record carries only the MAC, and the friendly name has to come from /json/info afterwards. Import proposed Segment 0 from its unnamed 36-LED segment; re-importing against an Iris that had renamed it to "Roofline" reported no changes and kept the user's name.

The Docker caveat turned out to be the interesting part. mDNS is multicast and doesn't cross a bridge network, so on a default compose install discovery finds nothing however healthy the controller is. Returning an empty list there is the wrong diagnosis — it reads as "no controllers on this network" and sends people to look at their lights. An empty scan now says which case applies, and docs/setup.md covers network_mode: host and the Avahi reflector. Typing the address in works identically and stays fully supported.

One thing the issue didn't ask for but the code needed: advertised addresses are checked before they're followed. mDNS is unauthenticated, anything on the segment can claim to be WLED and point anywhere, and discovery then fetches /json/info from what it's told — so an advert naming a public host would turn an admin's scan into an outbound request of the advertiser's choosing. Private, loopback and link-local only.

On your fourth checkbox"WLED_HOST is env-only today; decide whether discovery writes a runtime setting that overrides it"it already does. wled_host has been a runtime setting seeded from env since v1, and the database value wins once set. Discovery fills the existing field and the existing Save applies it: no new mechanism, same audit trail. Documented rather than reinvented.

Segment import proposes rather than applies. Segment identity is what every scheme is written against, so a silent overwrite could rename or drop segments out from under approved schemes. Matching is by wled_index not name (so your renames survive), and a segment the controller stops reporting is kept and listed rather than deleted (it may just have been reconfigured, and removing it would take its schemes' segment rows too).

zeroconf is a new runtime dependency — pure Python, only imported when someone clicks Discover.

Verified: 53 new tests, ruff/mypy clean, 722 backend tests, frontend tsc/eslint/vitest/build clean, CI green.

Landed in #129 (squash-merged to `main`). All four checkboxes done. **Verified against the live controller before the tests were written**, not after — which mattered, because the mDNS shape in the docs and the one the device actually broadcasts are not the same thing: ``` wled-450664._wled._tcp.local. -> 192.168.1.140:80, TXT {mac: 78421c450664} ``` So the instance name is the label, the TXT record carries only the MAC, and the friendly name has to come from `/json/info` afterwards. Import proposed `Segment 0` from its unnamed 36-LED segment; re-importing against an Iris that had renamed it to "Roofline" reported no changes and kept the user's name. **The Docker caveat turned out to be the interesting part.** mDNS is multicast and doesn't cross a bridge network, so on a default compose install discovery finds nothing however healthy the controller is. Returning an empty list there is the *wrong diagnosis* — it reads as "no controllers on this network" and sends people to look at their lights. An empty scan now says which case applies, and `docs/setup.md` covers `network_mode: host` and the Avahi reflector. Typing the address in works identically and stays fully supported. **One thing the issue didn't ask for but the code needed:** advertised addresses are checked before they're followed. mDNS is unauthenticated, anything on the segment can claim to be WLED and point anywhere, and discovery then fetches `/json/info` from what it's told — so an advert naming a public host would turn an admin's scan into an outbound request of the advertiser's choosing. Private, loopback and link-local only. **On your fourth checkbox** — *"`WLED_HOST` is env-only today; decide whether discovery writes a runtime setting that overrides it"* — **it already does.** `wled_host` has been a runtime setting seeded from env since v1, and the database value wins once set. Discovery fills the existing field and the existing Save applies it: no new mechanism, same audit trail. Documented rather than reinvented. Segment import proposes rather than applies. Segment identity is what every scheme is written against, so a silent overwrite could rename or drop segments out from under approved schemes. Matching is by `wled_index` not name (so your renames survive), and a segment the controller stops reporting is kept and listed rather than deleted (it may just have been reconfigured, and removing it would take its schemes' segment rows too). `zeroconf` is a new runtime dependency — pure Python, only imported when someone clicks Discover. Verified: 53 new tests, `ruff`/`mypy` clean, 722 backend tests, frontend `tsc`/`eslint`/vitest/build clean, CI green.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Iris-WLED#50
No description provided.