Skip to content

save global on / off button override in cfg#5591

Draft
DedeHai wants to merge 1 commit into
wled:mainfrom
DedeHai:save_ON-OFF_override_to_cfg
Draft

save global on / off button override in cfg#5591
DedeHai wants to merge 1 commit into
wled:mainfrom
DedeHai:save_ON-OFF_override_to_cfg

Conversation

@DedeHai
Copy link
Copy Markdown
Collaborator

@DedeHai DedeHai commented May 10, 2026

as discussed in #5562 this adds saving of the UI setting in config in addition to localstorage.

@softhack007 I am not too sure this is a good idea - at least as is: it prevents users from using different on/off presets on different devices. This could be solved with a checkmark to use "local override only", ignorie the cfg setting and use localstorage only.

the other way to solve #5562 would be to not store them in config but add a mechanism to tell the browser to clear relevant local storage after a factory reset, I am not sure how to do that though as it requires a variable the persists a reboot so it would need to go into the config or use RTC memory. Clearing browser cache for a UI induced factory reset would be possible by adding some code to settings_sec.htm but doing it for a button called factory reset seems a bit more tricky.

Summary by CodeRabbit

  • New Features
    • Power button preset configuration – users can now configure custom preset actions that automatically execute when the power button is pressed, with separate settings for on and off states enabling flexible personalized automation
    • Enhanced configuration synchronization between device and web interface ensuring settings persist consistently across restarts

Review Change Stack

@DedeHai DedeHai requested a review from softhack007 May 10, 2026 11:25
@DedeHai DedeHai marked this pull request as draft May 10, 2026 11:25
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 10, 2026

Walkthrough

This PR adds power-button preset configuration to WLED. The feature stores which light preset activates when the button is pressed on versus pressed off, persists the setting in cfg.json, exposes it via the /json/info API, and synchronizes it bidirectionally between the device and web UI via form fields and localStorage.

Changes

Power Button Preset Configuration

Layer / File(s) Summary
Data Model
wled00/wled.h
Two new global variables powerButtonOnPreset and powerButtonOffPreset declare the preset overrides.
Backend Persistence
wled00/cfg.cpp
Deserialization reads pon/pof from cfg.json; serialization writes them back to maintain persistent storage.
Backend API Exposure
wled00/json.cpp, wled00/xml.cpp
JSON endpoint includes pon/pof fields; form settings generation outputs ON/OF form field values.
Frontend Data Sync
wled00/data/index.js
parseInfo() receives server values and updates localStorage, treating server state as authoritative.
Frontend Form & UI
wled00/data/settings_ui.htm
Form inputs gain name="ON"/name="OF" for submission; GetLS()/SetLS() sync server-authoritative values with localStorage; Save() always submits form; S() removes prior conditional logic for cleaner reload.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested reviewers

  • netmindz
  • softhack007
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: saving the global on/off button override setting to persistent configuration (cfg).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wled00/data/settings_ui.htm (1)

123-142: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

First-visit crash: sett.comp dereferences null when wledUiCfg is absent.

When localStorage has no wledUiCfg entry, localStorage.getItem('wledUiCfg') returns null, and JSON.parse(null) evaluates to null (no exception is thrown, so the catch branch is not entered). The newly added if (!sett.comp) sett.comp = {}; then throws TypeError: Cannot read properties of null (reading 'comp'), breaking the UI Settings page on a fresh browser/profile. Previously this path was tolerated because addRec(null, …)'s for…in is a no-op on null.

Normalize sett to an object before touching .comp:

🐛 Proposed fix
 		try {
-			sett = JSON.parse(sett);
+			sett = JSON.parse(sett) || {};
 		} catch (e) {
 			sett = {};
 			gId('lserr').style.display = "inline";
 			gId('lserr').innerHTML = "⚠ Settings JSON parsing failed. (" + e + ")";
 		}
 		if (!sett.comp) sett.comp = {};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/data/settings_ui.htm` around lines 123 - 142, GetLS currently assumes
sett is an object after JSON.parse, but JSON.parse(null) returns null and causes
a crash when accessing sett.comp; fix by normalizing sett to an object
immediately after reading/parsing localStorage (e.g., after sett =
JSON.parse(sett) or when sett is falsy) so sett = {} if sett is null/undefined
before any access to sett.comp, and keep the existing parse-error UI updates
(gId('lserr') usage) intact; update the logic surrounding
localStorage.getItem('wledUiCfg'), JSON.parse, and the subsequent if
(!sett.comp) sett.comp = {} to ensure sett is always an object in the GetLS
function.
🧹 Nitpick comments (1)
wled00/data/index.js (1)

656-662: ⚡ Quick win

Avoid persisting cfg to localStorage on every parseInfo call.

parseInfo() runs on every WebSocket info update and every requestJson() response, so this branch serializes and writes the entire cfg object on every refresh — even when i.pon/i.pof did not change. That is needless I/O on the hot path, and the storage event will fire for other open tabs on each write. Gate the write on an actual change:

♻️ Proposed fix
-	if (typeof i.pon === "number") cfg.comp.on = i.pon;
-	if (typeof i.pof === "number") cfg.comp.off = i.pof;
-	try {
-		localStorage.setItem('wledUiCfg', JSON.stringify(cfg));
-	} catch (e) {
-		// ignore localStorage failures
-	}
+	let pwrChanged = false;
+	if (typeof i.pon === "number" && cfg.comp.on  !== i.pon) { cfg.comp.on  = i.pon; pwrChanged = true; }
+	if (typeof i.pof === "number" && cfg.comp.off !== i.pof) { cfg.comp.off = i.pof; pwrChanged = true; }
+	if (pwrChanged) {
+		try { localStorage.setItem('wledUiCfg', JSON.stringify(cfg)); } catch (e) { /* ignore */ }
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/data/index.js` around lines 656 - 662, The current parseInfo handling
unconditionally writes cfg to localStorage after assigning cfg.comp.on/off,
causing unnecessary I/O and storage events; change parseInfo so it compares the
new values of i.pon and i.pof (or the resulting cfg.comp.on/cfg.comp.off)
against their previous values and only call localStorage.setItem('wledUiCfg',
JSON.stringify(cfg)) when an actual change occurred (i.e., detect delta on
i.pon/i.pof or cfg.comp.on/off before serializing), keeping the try/catch block
intact to ignore failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@wled00/data/settings_ui.htm`:
- Around line 123-142: GetLS currently assumes sett is an object after
JSON.parse, but JSON.parse(null) returns null and causes a crash when accessing
sett.comp; fix by normalizing sett to an object immediately after
reading/parsing localStorage (e.g., after sett = JSON.parse(sett) or when sett
is falsy) so sett = {} if sett is null/undefined before any access to sett.comp,
and keep the existing parse-error UI updates (gId('lserr') usage) intact; update
the logic surrounding localStorage.getItem('wledUiCfg'), JSON.parse, and the
subsequent if (!sett.comp) sett.comp = {} to ensure sett is always an object in
the GetLS function.

---

Nitpick comments:
In `@wled00/data/index.js`:
- Around line 656-662: The current parseInfo handling unconditionally writes cfg
to localStorage after assigning cfg.comp.on/off, causing unnecessary I/O and
storage events; change parseInfo so it compares the new values of i.pon and
i.pof (or the resulting cfg.comp.on/cfg.comp.off) against their previous values
and only call localStorage.setItem('wledUiCfg', JSON.stringify(cfg)) when an
actual change occurred (i.e., detect delta on i.pon/i.pof or cfg.comp.on/off
before serializing), keeping the try/catch block intact to ignore failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 155ddf11-0226-46c5-8100-a126e3b6ac69

📥 Commits

Reviewing files that changed from the base of the PR and between 37623ed and d550d1f.

📒 Files selected for processing (6)
  • wled00/cfg.cpp
  • wled00/data/index.js
  • wled00/data/settings_ui.htm
  • wled00/json.cpp
  • wled00/wled.h
  • wled00/xml.cpp

@softhack007
Copy link
Copy Markdown
Member

softhack007 commented May 10, 2026

@softhack007 I am not too sure this is a good idea - at least as is: it prevents users from using different on/off presets on different devices. This could be solved with a checkmark to use "local override only", ignorie the cfg setting and use localstorage only.

@DedeHai yes I see your concern, too. If configuring different on/off presets per UI instance (=different on your Pad, on your MacBook, on your son's PC) is a feature that people use, we would create a regression. I also agree that a "complete solution" - that also clears the browser's local storage on reset-by-button, plus clears all connected browsers - would be an overkill in terms of complexity.

Maybe let's ask users (discord) how they use the feature 🤔

  • Are our users aware that the setting is "per UI" ? is this useful? or maybe its just a PITA?
  • Maybe we should simply document that "UI settings" are not reset on factory reset? This could be added to the reset confirmation page, and written down in wled-docs.
  • Generally I think that "UI settings" is an unusual place for overriding the "on/off virtual button" behaviour. It might be more logical to have this in LEDs settings, next to "Apply preset on boot". Or maybe next to "button macros" in time&button settings.
  • Finally, why not simply assign "transition styles" to the on/off button, instead of using presets? (like these "banlanx" non-wled controllers usually do)

@netmindz @lost-hope @willmmiles @Moustachauve what do you think?

@softhack007 softhack007 added this to the 16.1 milestone May 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants