RFC: Schema-driven settings UI rewrite (and finishing LED bus type self-description)
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 35/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- cpp, html, javascript
- Ambito
- backend-api-design, embedded-iot, frontend
Direzione di ricerca
Start by reading wled00/data/settings_*.htm, wled00/xml.cpp, wled00/set.cpp, and wled00/cfg.cpp to understand the current settings flow; for the LED bus proposal, inspect bus_manager.h and settings_leds.htm. The issue suggests a proof of concept with the shared engine, one simple settings page, and the HUB75 case, but leaves design questions open. Done would require an agreed scope and a validated implementation; no tests or acceptance criteria are specified.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
RFC: Schema-driven settings UI rewrite (and finishing LED bus type self-description)
Summary
The settings web UI (wled00/data/settings_*.htm) is hard to work with and hard to extend: hand-minified JS, values injected via a backend that prints raw JavaScript source (GetV()), and a save path that's a completely separate hand-parsed form handler. This proposes replacing the nine hand-authored settings pages with one generic shell, one shared rendering engine, and a small declarative JSON schema per subsystem — no JS framework, same flash/heap discipline, but far less hand-written, hand-synchronized code. It also proposes finishing the LED bus type self-description effort (#4129 and follow-ups) that's stalled one layer short of making HUB75 properly configurable.
This is a design proposal for discussion, not a submitted implementation — opening it to get alignment before any code lands.
The problem, concretely
One setting field currently exists in up to five places, three different naming conventions:
- An
<input>in a hand-authored.htmfile. - A
printSetFormValue/printSetFormCheckbox/printSetFormIndexcall inxml.cppthat prints JS source to populate it (194 call sites). - A hand-parsed
request->arg(...)line inset.cppto save it. - A key in
cfg.cpp'sdeserializeConfig/serializeConfig. - Sometimes a
wsec.jsoncounterpart for secrets.
Example of what (2) looks like today — the backend building a string of JavaScript and shipping it to the browser to execute:
// wled00/xml.cpp — getSettingsJS()
settingsScript.printf_P(PSTR("addWiFi(\"%s\",\"%s\",\"%s\",0x%X,0x%X,0x%X);"),
ssid, pass, bssid, ip, gw, sn);
printSetFormValue(settingsScript, PSTR("D0"), dnsAddress[0]);
Saving goes through an entirely separate, unrelated path (handleSettingsSet in set.cpp, hand-parsing individual form args), sharing no code with the JSON persistence layer in cfg.cpp.
The JS itself is hard to read for a structural reason, not a style choice: functions referenced from onclick="H()" attributes must stay global, so the build minifier (tools/cdata.js, html-minifier-terser) can't safely shrink their names — authors hand-shortened them instead (N(), T(), E(), S(), B(), H() in settings_wifi.htm alone).
Proposed design
Replace the nine pages with:
- One generic HTML shell, shared across all subsystems.
- One shared rendering engine (
common.js): reads a schema + the existingGET /json/cfgresponse, builds the form; on submit, reads the form back into JSON andPOSTs to/json/cfg(an endpoint that already exists and already works —deserializeConfig()already safely merges partial documents via#define CJSON(a,b) a = b | a, and already preserves unsent passwords correctly). - One small declarative schema per subsystem, authored as data, not code:
// wled00/data/schema/wifi.js (excerpt)
export default {
title: "WiFi Setup",
sections: [
{ title: "Configured networks", repeat: "nw.ins", minItems: 1, maxItems: 4,
fields: [
{ key: "ssid", label: "Network name (SSID)", type: "text", maxlength: 32, required: true },
{ key: "psk", label: "Password", type: "password", maxlength: 64 },
]
},
]
};
This turns into a form with data-cfg="nw.ins[0].ssid"-tagged inputs; the engine's populateForm/serializeForm handle read/write generically for any field shaped like this — no C++ changes needed for fields that are already cfg.json keys, which is most of them.
Not everything is a flat field. For genuinely interactive behavior (GPIO conflict highlighting, LED-type-driven pin reshaping, WiFi scanning), a schema entry can name a small custom widget instead of a field list. The widget owns behavior; it still emits data-cfg-tagged inputs, so the generic serializer reads it like anything else. Roughly 5 places per field collapses to 2: a cfg.cpp key (unavoidable — that's the C++ struct↔JSON boundary) and one schema entry.
Finishing LED bus type self-description
This part isn't a new idea — it's closing a gap in work that's already landed (#4129 "bus-config", and later PRs extending it). Bus subclasses already self-describe via a static getLEDTypes() returning {id, type-code string, name} (bus_manager.h), aggregated into the d.ledTypes array the LEDs settings page already reads. It stopped one layer short: the type-code string was meant to be the single source of truth for pin count, but that broke down for anything that isn't "N plain GPIOs" — network buses need +3 hardcoded on top, HUB75 needs +4, duplicated by hand in both Bus::getNumberOfPins() (C++) and numPins() (JS). BusNetwork's own code comment admits it: // should be "NNNN" to determine 4 "pin" fields — known, never carried through.
HUB75 is where this hurts most. Its 5 "pin" slots are actually panelWidth, panelHeight, chainLength, rows, cols, not GPIOs, and the shift-register chip driver is hardcoded: mxconfig.driver = HUB75_I2S_CFG::FM6124; // workaround until we can make the driver user-configurable, with four other supported drivers commented out right above it. Because none of this is expressed as data, settings_leds.htm compensates with ~13 separate isHub75() branches hand-coding labels/ranges/validation.
Proposed fix: extend LEDType with an optional field-descriptor list, defaulting to null (zero cost) for ordinary GPIO-only types — 26 of the current 36 entries need nothing new:
struct BusField {
const char* key; const char* label;
enum { GPIO, NUMBER, SELECT } role;
int16_t min = 0, max = 0;
const BusFieldOption* options = nullptr;
};
// LEDType gains: const BusField* fields = nullptr; uint8_t fieldCount = 0;
Only the divergent types (HUB75, Network) write one. HUB75's chip-driver choice reuses the driverType/drv key BusConfig already has (currently used for RMT/I2S selection on digital buses) rather than overloading the pin array further — so it round-trips through existing persistence with no new plumbing. Estimated cost: ~400-600 bytes for the two divergent type groups, against the ~1.2KB the 36-entry table already costs. Explicitly out of scope: HUB75's GPIO pinout is a compile-time board selection (platformio.ini build environments) — that stays a separate build-system effort, not fixed by this.
Usermods
Usermod settings currently split across two unrelated APIs: appendConfigData() (a hand-built JS-call-string DSL, reached through a non-reentrant global Print* shim for backward compat) for settings-page metadata, and addToJsonInfo() for the Info page — no shared descriptor between them. Proposal: one new, purely additive virtual method:
virtual void addSettingsField(Print& schema) {} // default no-op
emitting the same JSON schema shape core pages use. A showInInfo: true flag on a field drives an automatic Info-page row from the same descriptor — one field, one description, both surfaces. addToConfig/readFromConfig (persistence) are untouched. The legacy appendConfigData() DSL keeps working forever, unmodified, for the ~70+ existing usermods that use it — this is additive, not a migration.
Flash budget (estimate, needs validation against a prototype)
Current gzipped settings section: 40,895 B (wled00/html_settings.h: PAGE_settings_* sum 37,246 B + common.js 3,649 B). Estimated new total: ~17-23 KB — shared engine ~5-6KB, all schema data combined ~4-6KB, custom widgets ~6-9KB, shared shell+CSS ~2KB. Schema stays split per subsystem (fetched individually), so opening one page doesn't ship every other page's schema.
Backward compatibility
- Zero changes required for any existing usermod.
addToConfig/readFromConfiguntouched.- Settings URLs are free to change but don't have to.
- Ships incrementally: old and new mechanisms coexist per-page during migration; old
getSettingsJS/handleSettingsSetcode is deleted only once nothing references it.
Open questions
data-cfgpath convention: dotted paths mirroringcfg.json's real shape vs. WLED's existing 2-letter field codes.- Where usermod schema JSON is served: folded into
GET /json/cfg'sumpayload, or a dedicated endpoint. - Whether
GET /json/cfgshould eventually support a scoped/partial fetch (?s=wifi) — not required for correctness (full-document load is already proven heap-safe on ESP8266), possibly worth it for load time. - Custom Elements vs. plain factory functions for the widget layer.
Happy to expand any section into more detail, or start with a small proof-of-concept (engine + one simple page + the LED bus HUB75 case) before committing to the full scope.
- Lingua principale
- C++
- Stelle
- 18.8k
- Fork
- 4.4k
- Merge medio
- 2g 2h
- PR unite (30g)
- 13
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di wled/WLED
-
Gravimeter: bars turn black at full volume (uint8_t(segmentSampleAvg*8) overflows)Forse già presa @DedeHai l’ha presa 17 giorni fa. Apertabug confirmed
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
wled/WLED#5891 · 12 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
bug cannot reproduce
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
wled/WLED#5840 · 12 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
enhancement
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
I maintainer di solito rispondono entro 1 giorno
-
backburner enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
wled/WLED#4132 · 5 commenti · 2 reazioni ·
I maintainer di solito rispondono entro 1 giorno
-
discussion enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
wled/WLED#3478 · 13 commenti ·
I maintainer di solito rispondono entro 1 giorno
Issue simili
-
Remove source files from packaging for deb, zip, etcForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
OpenSpace/OpenSpace#4353 · 1 commento ·
I maintainer di solito rispondono entro 5 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 86/100
justcallmekoko/ESP32Marauder#1656 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
-
Decompiler: convertCharRef() in xml.y overflows its int4 accumulator on long character referencesForse già presa @kaayzouee l’ha presa 1 giorno fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
NationalSecurityAgency/ghidra#10009 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
mutationpp/Mutationpp#337 ·