WLED firmware compatibility statement and translator fixtures #62

Closed
opened 2026-07-15 15:38:34 +00:00 by claude-bot · 2 comments
Contributor

From the 2026-07-15 review follow-ups. WLED's JSON API drifts across releases (segment fields, playlist format, effect list changes between 0.13 / 0.14 / 0.15). Iris currently assumes one shape.

  • Declare tested WLED versions in the README (start with whatever the dev controller runs)
  • Capture real /json/info + /json/state responses from each supported firmware as test fixtures; run the scheme translator and preset sync against all of them
  • Handle/document the effect-ID drift issue (effect names→IDs vary by version; relates to the EffectPicker item in #48 — fetching /json/eff from the live controller sidesteps it)
  • Fail gracefully (clear error, not a stack trace) when the controller reports an unsupported/too-old version in /json/info
From the 2026-07-15 review follow-ups. WLED's JSON API drifts across releases (segment fields, playlist format, effect list changes between 0.13 / 0.14 / 0.15). Iris currently assumes one shape. - [ ] Declare tested WLED versions in the README (start with whatever the dev controller runs) - [ ] Capture real `/json/info` + `/json/state` responses from each supported firmware as test fixtures; run the scheme translator and preset sync against all of them - [ ] Handle/document the effect-ID drift issue (effect names→IDs vary by version; relates to the EffectPicker item in #48 — fetching `/json/eff` from the live controller sidesteps it) - [ ] Fail gracefully (clear error, not a stack trace) when the controller reports an unsupported/too-old version in `/json/info`
claude-bot added this to the v1.1.0 milestone 2026-07-15 15:38:34 +00:00
Author
Contributor

Picking this up. Probed the live roofline controller read-only first; three of the four bullets turn out to be actual bugs rather than doc work.

The dev controller runs WLED 16.0.0 (build 2605030, codename Niji, ESP32, fxcount: 220, palcount: 73). Note WLED renamed 0.16.016.0.0 for the 2026-05-03 release, so the version string is itself a drift hazard — any naive 0.x comparison breaks on it.

Findings

1. segment_count has always been wrong. wled_controller.py:53 reads info["leds"]["segs"]. That field has never existed in WLED's /json/info — confirmed against both the device and the JSON API reference. It silently falls back to the default 1. The real per-segment array is leds.seglc.

2. The /json/eff mitigation named in bullet 3 was never wired up. The frontend calls GET /api/v1/wled/effects (frontend/src/api/index.ts:144) and useWledEffects is deliberately written to fail silently — but that backend endpoint does not exist. So EffectPicker always falls back to its 112-entry static list against a 220-effect controller.

3. Effect-ID drift is real and already affects us. Diffing the static list against live /json/eff: FX 77 is Copy Segment on 16.0.0 but the static list calls it Meteor Smooth (its 0.14 name). FX 76 (Meteor) and 78 (Railway) are unchanged either side, so it is a genuinely repurposed slot, not an index offset. Also cosmetic: 68 is Bpm, not BPM.

4. Preset sync is broken against real firmware — splitting this out, see below.

Scope

This issue covers all four checkboxes: the compatibility statement, captured fixtures, the effect-drift handling (adding the missing /wled/effects + /palettes proxy), and graceful failure on unsupported firmware — plus the segment_count fix, which the fixtures are what caught.

Fixtures for 16.0.0 are verbatim device captures. I only have one controller, so the 0.14/0.15 fixtures are derived from WLED's published API reference and each carries a header saying so — they give the translator cross-version shape coverage without pretending to be hardware captures.

Picking this up. Probed the live roofline controller read-only first; three of the four bullets turn out to be actual bugs rather than doc work. **The dev controller runs WLED `16.0.0`** (build `2605030`, codename Niji, ESP32, `fxcount: 220`, `palcount: 73`). Note WLED [renamed `0.16.0` → `16.0.0`](https://github.com/wled/WLED/releases/tag/v16.0.0) for the 2026-05-03 release, so the version string is itself a drift hazard — any naive `0.x` comparison breaks on it. ### Findings **1. `segment_count` has always been wrong.** `wled_controller.py:53` reads `info["leds"]["segs"]`. That field has never existed in WLED's `/json/info` — confirmed against both the device and the [JSON API reference](https://kno.wled.ge/interfaces/json-api/). It silently falls back to the default `1`. The real per-segment array is `leds.seglc`. **2. The `/json/eff` mitigation named in bullet 3 was never wired up.** The frontend calls `GET /api/v1/wled/effects` (`frontend/src/api/index.ts:144`) and `useWledEffects` is deliberately written to fail silently — but that backend endpoint does not exist. So EffectPicker always falls back to its 112-entry static list against a 220-effect controller. **3. Effect-ID drift is real and already affects us.** Diffing the static list against live `/json/eff`: FX **77 is `Copy Segment`** on 16.0.0 but the static list calls it **`Meteor Smooth`** (its 0.14 name). FX 76 (`Meteor`) and 78 (`Railway`) are unchanged either side, so it is a genuinely repurposed slot, not an index offset. Also cosmetic: 68 is `Bpm`, not `BPM`. **4. Preset sync is broken against real firmware** — splitting this out, see below. ### Scope This issue covers all four checkboxes: the compatibility statement, captured fixtures, the effect-drift handling (adding the missing `/wled/effects` + `/palettes` proxy), and graceful failure on unsupported firmware — plus the `segment_count` fix, which the fixtures are what caught. Fixtures for 16.0.0 are verbatim device captures. I only have one controller, so the 0.14/0.15 fixtures are derived from WLED's published API reference and each carries a header saying so — they give the translator cross-version shape coverage without pretending to be hardware captures.
Author
Contributor

Done — #116 merged, CI green on all six jobs.

All four checkboxes are covered:

  • Tested versions declared in the README and docs/wled-compatibility.md: 16.0.0 verified on hardware, 0.14.x the minimum. The floor lives in MINIMUM_SUPPORTED (app/services/wled_version.py) and the fixture list in SUPPORTED_FIRMWARE (tests/test_wled_compat.py), both referenced from the doc so they change together.

  • Fixtures captured in backend/tests/fixtures/wled/. 16.0.0 is verbatim device output (device identifiers redacted); 0.14/0.15 are derived from the published API reference and labelled as such in the directory README and the test docstrings. The translator and preset payload run against all of them.

    The useful test is the drift tripwire: Iris only ever writes state, and WLED ignores keys it does not recognise, so a removed field would not raise — the lights would just quietly do the wrong thing. Every key Iris writes is asserted present in each firmware's recorded segment.

  • Effect-ID drift handled and documented. GET /api/v1/wled/effects now exists (it did not before, despite the frontend calling it), the offline fallback is checked entry-by-entry against the captured 16.0.0 table, and the FX-77 case is written up in the compat doc.

  • Graceful failure on unsupported firmware — one warning at startup, firmware_supported + firmware_message on /wled/status, and that message appended to failing pushes.

One deviation worth your eye

I did not make unsupported firmware a hard block. The bullet says "fail gracefully (clear error, not a stack trace)", and the emphasis there is the quality of the failure — but a literal reading would also mean refusing to drive a 0.13 controller. The segment fields Iris writes have been stable since 0.11, so such a device would very likely work fine, and blocking it would strand a working setup over a problem that may not exist. It warns loudly and carries on instead. Easy to tighten to a refusal if you disagree.

Split out

#115 — preset sync targets /json/presets, which no firmware implements. This issue's work only made that failure loud; the rework needs hardware to verify.

Incidental

The old test_get_info_parses_response passed only because it fabricated leds.segs alongside the code that read it — worth remembering the next time a mocked integration test looks like coverage. Also fixed the README Status section (still said "Approaching v1.0.0") and gitignored frontend/*.tsbuildinfo.

Done — #116 merged, CI green on all six jobs. All four checkboxes are covered: - [x] **Tested versions declared** in the README and [`docs/wled-compatibility.md`](https://git.rhoving.com/rbrooks/Iris-WLED/src/branch/main/docs/wled-compatibility.md): 16.0.0 verified on hardware, 0.14.x the minimum. The floor lives in `MINIMUM_SUPPORTED` (`app/services/wled_version.py`) and the fixture list in `SUPPORTED_FIRMWARE` (`tests/test_wled_compat.py`), both referenced from the doc so they change together. - [x] **Fixtures captured** in `backend/tests/fixtures/wled/`. 16.0.0 is verbatim device output (device identifiers redacted); 0.14/0.15 are derived from the published API reference and labelled as such in the directory README and the test docstrings. The translator and preset payload run against all of them. The useful test is the drift tripwire: Iris only ever *writes* state, and WLED ignores keys it does not recognise, so a removed field would not raise — the lights would just quietly do the wrong thing. Every key Iris writes is asserted present in each firmware's recorded segment. - [x] **Effect-ID drift handled and documented.** `GET /api/v1/wled/effects` now exists (it did not before, despite the frontend calling it), the offline fallback is checked entry-by-entry against the captured 16.0.0 table, and the FX-77 case is written up in the compat doc. - [x] **Graceful failure** on unsupported firmware — one warning at startup, `firmware_supported` + `firmware_message` on `/wled/status`, and that message appended to failing pushes. ### One deviation worth your eye I did **not** make unsupported firmware a hard block. The bullet says "fail gracefully (clear error, not a stack trace)", and the emphasis there is the quality of the failure — but a literal reading would also mean refusing to drive a 0.13 controller. The segment fields Iris writes have been stable since 0.11, so such a device would very likely work fine, and blocking it would strand a working setup over a problem that may not exist. It warns loudly and carries on instead. Easy to tighten to a refusal if you disagree. ### Split out #115 — preset sync targets `/json/presets`, which no firmware implements. This issue's work only made that failure loud; the rework needs hardware to verify. ### Incidental The old `test_get_info_parses_response` passed only because it fabricated `leds.segs` alongside the code that read it — worth remembering the next time a mocked integration test looks like coverage. Also fixed the README Status section (still said "Approaching v1.0.0") and gitignored `frontend/*.tsbuildinfo`.
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#62
No description provided.