Skip to content

Fixed active preset being cleared by JSON requests during a brightness transition - #5856

Open
Uproared wants to merge 2 commits into
wled:mainfrom
Uproared:fix/json-bri-stateChanged
Open

Uproared wants to merge 2 commits into
wled:mainfrom
Uproared:fix/json-bri-stateChanged

Conversation

@Uproared

@Uproared Uproared commented Sep 18, 2026

Copy link
Copy Markdown

What's wrong

Any JSON API request that goes through deserializeState() while a brightness transition is in progress gets flagged as a state change, even if the request doesn't contain bri or change anything at all. That clears currentPreset, so the device reports ps: -1 and the preset shows as "none" in the UI and as "unknown" in Home Assistant.

The cause is this line near the top of deserializeState():

getVal(root["bri"], bri);
if (bri != briOld) stateChanged = true;

briOld isn't the previous brightness in the sense this line assumes. It's the starting point of the current fade, and it only catches up to bri when the transition finishes in applyFinalBri(). So for the entire transition duration after any brightness change, bri != briOld is true regardless of what the request contains. stateChanged then gets set, stateUpdated() runs at the end of deserializeState(), and its first act on a changed state is currentPreset = 0.

The clearest case is {"ps":N} where N is already the active preset. That request skips the reload (because presetCycCurr == currentPreset) and falls through to the end of the function, where the bogus stateChanged clears the preset. But an empty {} or {"v":true} does the same thing during a fade. Requests for a different preset aren't affected, since a real preset load sets currentPreset again afterward.

This line isn't in 0.15.x (v0.15.0 just has getVal(root["bri"], &bri);), so it's a regression in 16.x. It was introduced in ee9ac94 (Segment layering & effect blending improvements, Apr 2025) and is still present on main.

How I ran into it

I have a WLED strip controlled by Home Assistant with a WiZmote paired as a physical remote. One night someone pressed brightness-up on the remote right as an HA automation re-selected the preset that was already active. Remote brightness presses don't touch stateChanged, so on their own they leave the preset alone, and re-selecting the active preset on its own does nothing either. But the automation's request landed inside the 2 s fade from the button press, and the preset went to -1. Took a while to figure out because neither input reproduces it alone.

Reproduce

Any preset active. With the default 0.7 s transition the window is short, so set the transition to 2 s or more in LED settings first. Use the HTTP API for the brightness change because it doesn't set stateChanged (same as a remote or button press):

curl -s "http://WLED/win&A=120" > /dev/null
curl -s -X POST http://WLED/json/state -H 'Content-Type: application/json' -d '{"ps":4,"v":true}' | grep -o '"ps":[0-9-]*'

Pick an A= value different from the current brightness, otherwise no transition starts. Run the second command within the transition time and you get "ps":-1. Wait longer than the transition and you get "ps":4. Replacing the {"ps":4,...} body with {} or {"v":true} during the fade also returns -1. A long transition time makes it trivial to hit by hand.

The fix

Compare against the brightness the request started with, not against briOld:

bool onBefore = bri;
byte briBefore = bri;
getVal(root["bri"], bri);
if (bri != briBefore) stateChanged = true;

Now stateChanged is only set when the request itself changed bri. A request that actually carries a new brightness still behaves exactly as before, including clearing the preset. Only the false positive during a transition goes away.

An alternative would be to only set the flag when root["bri"] is present. I went with the value comparison because it also avoids flagging a request that sets bri to the value it already has, but I'm happy to change it if the other form is preferred.

Tested

Built from the v16.0.1 tag with the esp32dev env and flashed to an ESP32-D0WD (140 px WS2811, AudioReactive included). Before the patch, the reproduction above returned -1 every time. After, it returns the active preset every time, both with the curl commands and with the actual remote plus a Home Assistant preset select. Brightness changes via JSON, on/off, preset loads, and the remote all behave the same as before.

Since this is a 16.x regression verified on 16.0.1, it would be good to backport to 16_x.

Summary by CodeRabbit

  • Documentation
    • Clarified an internal code comment related to brightness state-change handling.
  • No User-Facing Changes
    • This update does not change observable brightness transitions or preset behavior.

…transition

deserializeState() flagged stateChanged whenever bri != briOld, before
checking whether the request actually carried "bri". briOld is the
origin of an in-flight brightness transition and differs from bri for
the whole transition, so any POST to /json/state arriving in that window
(e.g. {"ps":N} for the already active preset, which skips applyPreset()
and falls through) reached stateUpdated() and zeroed currentPreset,
reported as "ps":-1.

Compare against the value bri had at the start of the request instead.
A JSON "bri" change still marks the state as changed; only the false
positive from an unrelated transition (WiZmote/IR/button/HTTP A=
brightness steps) is removed.

Introduced by ee9ac94. Reproduced and fix verified on 16.0.1 on an
ESP32-D0WD-V3 with a WiZmote and Home Assistant selecting the active
preset.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 40829f7e-de19-49df-8677-05c9550fa4a2

📥 Commits

Reviewing files that changed from the base of the PR and between 0117b72 and 3ef4ab1.

📒 Files selected for processing (1)
  • wled00/json.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • wled00/json.cpp

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


Walkthrough

deserializeState compares requested brightness changes with the brightness value captured before the request. Unrelated requests during a brightness transition are no longer treated as state changes that clear currentPreset.

Changes

Brightness state-change detection

Layer / File(s) Summary
Capture and compare brightness state
wled00/json.cpp
deserializeState stores bri before applying the requested brightness and compares the updated value with that snapshot instead of briOld.

Priority: ➖ Normal

Estimated code review effort: 1 (Trivial) | ~5 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 3ef4a

Unrelated requests no longer clear the active preset during a brightness transition; no actionable merge risk remains.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: preventing the active preset from being cleared by JSON requests during a brightness transition.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files.
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.

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.

@DedeHai

DedeHai commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

good call, but please remove the lengthy AI comment

@Uproared

Copy link
Copy Markdown
Author

good call, but please remove the lengthy AI comment

Done in 3ef4ab1

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.

2 participants