Skip to content

Improve Boot preset handling#5573

Draft
smitty078 wants to merge 4 commits intowled:mainfrom
smitty078:boot-preset-handling
Draft

Improve Boot preset handling#5573
smitty078 wants to merge 4 commits intowled:mainfrom
smitty078:boot-preset-handling

Conversation

@smitty078
Copy link
Copy Markdown

@smitty078 smitty078 commented May 7, 2026

TODO:

  • apply the same fix / logic to address boot playlists
  • Evaluate if workaround that allows a single chained preset to be applied still makes sense and/or is involved in boot playlist behavior once fixed
  • Update documentation wherever relevant
  • Maybe more?

The primary goal for this PR is to improve reliability of boot preset handling when realtime input (DDP/E1.31/etc.) becomes active very early during startup (see discussion from #5569).

Problem

Previously, boot preset application depended on the normal loop path. handlePresets() in the main loop is gated behind realtime mode checks:

if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly))

If realtime mode became active before the first normal preset processing pass, the boot preset could be skipped or only partially applied. This was especially problematic for boot presets intended to set LiveDataOverride (lor).

Solution

This PR adds an additional handlePresets() call during setup(). The new startup-time call is intentionally placed:

  • after UsermodManager::setup()
  • after any required config migration save (serializeConfigToFS())
  • before normal network/realtime activity begins

This placement avoids calling usermod JSON handlers before setup while ensuring the boot preset is processed deterministically during startup.

Additional Improvements

- the boot preset is allowed a single chained "ps" preset load during CALL_MODE_INIT.

Normally "ps" is stripped from preset JSON to prevent recursive preset execution. This PR preserves that protection for all normal runtime preset loads. During boot initialization only, one "ps" handoff is allowed. The chained preset executes under CALL_MODE_NO_NOTIFY, so further chaining remains blocked by the existing recursion protection.

Example:

{
  "lor": 2,
  "ps": 250
}

One practical use case is the autosave usermod:

  • autosaved presets typically do not contain lor
  • users may want to force lor:2 at boot before restoring the autosaved state

Other usermods may benefit from similar boot-time state injection before loading a normal runtime preset.

- Measurably faster light activation after power on.

As a side effect of the improved boot preset behavior, for boot presets that set "on": true, there is a measurably faster light activation after power-on. This is especially useful for users who power their controllers from a physical wall switch and expect lights to turn on immediately when power is restored.

In local testing:

  • previous behavior: lights became active roughly 1–1.5 seconds after power-on
  • new behavior: lights become active nearly instantly

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5fe5912b-0b25-44fd-bf55-0e09e845726d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

The PR refines preset initialization logic by exempting CALL_MODE_INIT from preset state change notifications in handlePresets(), and adds boot-time preset handling in WLED::setup() that calls handlePresets() twice to support one-time numeric preset chaining during device startup.

Changes

Preset Initialization and Boot Handling

Layer / File(s) Summary
Preset State Change Detection
wled00/presets.cpp
handlePresets() condition is updated to add CALL_MODE_INIT exception alongside existing CALL_MODE_BUTTON_PRESET handling for cycling marker (~) logic, preventing preset state changes from being marked during initialization.
Boot Preset Initialization
wled00/wled.cpp
WLED::setup() adds conditional block that calls handlePresets() twice with yield() between when bootPreset > 0, enabling one-time preset chaining for numeric boot presets.
Code Formatting
wled00/presets.cpp
Trivial whitespace change at end of deletePreset().

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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
Title check ✅ Passed The title 'Improve Boot preset handling' accurately reflects the main change—enhancing how boot presets are applied during WLED startup to ensure deterministic processing.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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.

@smitty078
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@wled00/presets.cpp`:
- Around line 197-200: The guard currently lets any fdo["ps"] value through for
CALL_MODE_INIT (so string cycling specs like "1~5~" reach deserializeState);
restrict the CALL_MODE_INIT exception to numeric preset values by adding a type
check (e.g. fdo["ps"].is<int>() or appropriate numeric type) to the existing
condition (mirror the pattern used for CALL_MODE_BUTTON_PRESET which checks
fdo["ps"].is<const char *>()), so only numeric "ps" values are preserved for
deserializeState() and non-numeric strings are removed with fdo.remove("ps") to
avoid unintended playlist/cycling behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8125ad27-4651-45d0-adf2-70d57c1158bb

📥 Commits

Reviewing files that changed from the base of the PR and between c050973 and 88994e9.

📒 Files selected for processing (2)
  • wled00/presets.cpp
  • wled00/wled.cpp

Comment thread wled00/presets.cpp
@DedeHai
Copy link
Copy Markdown
Collaborator

DedeHai commented May 7, 2026

would a playlist not be able to do the same?
I do not see a significant speed increase, maybe 100ms.

@smitty078
Copy link
Copy Markdown
Author

would a playlist not be able to do the same? I do not see a significant speed increase, maybe 100ms.

Thanks for the feedback! Hence why I’m still in draft mode - I’m not ready to say this is review or merge ready just yet.

  1. In my initial test I thought the same thing but was unable to get a playlist to do the same thing. There may very well be a similar improvement needed for a boot playlist - I have not yet investigated that codepath but will.

  2. I wonder if the speed increase I see is specific to my hardware and/or environment. My controller is at the edge of WiFi range, so maybe that impacts startup time for WiFi fairly significantly for me. I’ll update my statement on performance before submitting for review.

smitty078 added 2 commits May 7, 2026 10:18
TODO:

set up logic handling inside handlePresets() and anywhere else required to implement behavior that is described in comments here.
Add TODO comments for handling boot presets and playlists.
Copy link
Copy Markdown
Member

@willmmiles willmmiles left a comment

Choose a reason for hiding this comment

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

Re performance changes: I do expect that the latency of how long it takes the main loop to get around to applying the boot preset will be highly dependent on environment and wifi setup. In fact, one down side of the proposed new approach is that it may lead to more devices showing visible stutters and hiccups during the connection sequence, as the light "starts up" earlier. There's undoubtedly room for improvement there -- I suspect some of the execution order details in the current code are workarounds for issues with older platforms that may not still apply. I don't think we should spend too much time worrying about this (either way) at the moment.

Re ps vs playlists: there's a very real sense in which the functionality of "ps" and "playlist" overlap: "ps" could (almost) be implemented as a single-entry "playlist" with 0 delay. In this context, both approaches require extra work at setup() time -- but realistically we probably should handle both, if only so that we don't have to document a bunch of special cases.

In practice I think this works out to something like:

if (bootPreset > 0) {
   handlePreset();   // execute boot preset
   handlePlaylist();  // if boot preset set a playlist, process it
   handlePreset();   // if either of the above set a preset for deferred execution, execute it now
}

Comment thread wled00/wled.cpp
if (needsCfgSave) serializeConfigToFS(); // usermods required new parameters; need to wait for strip to be initialised #4752

if (bootPreset > 0) {
if (!presetNeedsSaving()) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How could this be true during setup()?

Comment thread wled00/wled.cpp
if (bootPreset > 0) {
if (!presetNeedsSaving()) {
handlePlaylist(); // handle boot playlist at setup
yield();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we need to yield() from setup()? That seems quite strange to me -- we haven't even turned the network on yet at this point.

Comment thread wled00/wled.cpp

if (bootPreset > 0) {
if (!presetNeedsSaving()) {
handlePlaylist(); // handle boot playlist at setup
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This will never execute because the boot preset hasn't been parsed yet, and thus no playlist can be in flight.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants