WLED firmware compatibility statement and translator fixtures #62
Labels
No labels
area/ai
area/backend
area/frontend
area/infra
area/scheduler
area/wled
good-first-issue
priority/high
priority/low
priority/medium
type/bug
type/chore
type/ci-cd
type/docs
type/feature
type/qa
v1.0.0
v1.1.0
v1.2.0
v2.0.0
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/Iris-WLED#62
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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.
/json/info+/json/stateresponses from each supported firmware as test fixtures; run the scheme translator and preset sync against all of them/json/efffrom the live controller sidesteps it)/json/infoPicking 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(build2605030, codename Niji, ESP32,fxcount: 220,palcount: 73). Note WLED renamed0.16.0→16.0.0for the 2026-05-03 release, so the version string is itself a drift hazard — any naive0.xcomparison breaks on it.Findings
1.
segment_counthas always been wrong.wled_controller.py:53readsinfo["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 default1. The real per-segment array isleds.seglc.2. The
/json/effmitigation named in bullet 3 was never wired up. The frontend callsGET /api/v1/wled/effects(frontend/src/api/index.ts:144) anduseWledEffectsis 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 isCopy Segmenton 16.0.0 but the static list calls itMeteor 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 isBpm, notBPM.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+/palettesproxy), and graceful failure on unsupported firmware — plus thesegment_countfix, 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.
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 inMINIMUM_SUPPORTED(app/services/wled_version.py) and the fixture list inSUPPORTED_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/effectsnow 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_messageon/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_responsepassed only because it fabricatedleds.segsalongside 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 gitignoredfrontend/*.tsbuildinfo.