Skip to content

Add per-bus white-LED color temperature for accurate auto-white#5654

Open
NerdyGriffin wants to merge 16 commits into
wled:mainfrom
NerdyGriffin:claude/wled-cct-conversion-research-qmgU7
Open

Add per-bus white-LED color temperature for accurate auto-white#5654
NerdyGriffin wants to merge 16 commits into
wled:mainfrom
NerdyGriffin:claude/wled-cct-conversion-research-qmgU7

Conversation

@NerdyGriffin
Copy link
Copy Markdown

@NerdyGriffin NerdyGriffin commented May 29, 2026

What this does

Adds an opt-in, per-bus "white LED color temperature" setting that makes the
auto-white calculation account for the actual color of a strip's W LED.

Why

WLED's "Accurate" auto-white mode subtracts the white channel value equally
from R, G and B. That implicitly assumes the physical W LED emits neutral
white — RGB(255,255,255), i.e. the sRGB white point near D65 / ~6500 K. Real "RGBW"
strips often use 3000 K warm-white or 4000-5000 K "natural-white" or 5000 K+ cool-white LEDs, so the equal
subtraction shifts the resulting color visibly. This lets the calculation use
the W LED's true per-channel contribution instead.

How it works

  • New per-bus checkbox + Kelvin input below "Auto-calculate W channel from
    RGB" (shown for Brighter / Accurate / Dual modes). Disabled by default.
  • When enabled, the configured Kelvin is converted once (via the existing
    colorKtoRGB()) to the W LED's RGB equivalent and cached on the bus.
  • In autoWhiteCalc, instead of w = min(r,g,b) and an equal subtract, it
    picks the largest W whose per-channel RGB contribution won't overflow any
    channel, then (ACCURATE mode) subtracts that correct per-channel amount.
    Floor division composes back through the subtract so it can't underflow;
    per-channel zero guards handle channels that are 0 (blue is 0 at/below
    1900 K).
  • Opt-in via the checkbox: when off (default, wk = 0) autoWhiteCalc takes
    a fast path that is identical to current behavior, so existing configs
    render unchanged and there's no added per-pixel cost.
  • Persisted per-bus as wk in the LED config; range 1000–10000 K.

Testing

  • Built esp32dev; flashed via OTA to a QuinLED Dig-Uno (ESP32 + RGBW).
  • Verified: UI show/hide across all AW modes; value persists across a full
    power-cycle; Accurate-mode saturation with smooth color transitions and no
    regressions; low end down to the 1000 K floor with no underflow artifacts.

Notes / limitations

  • colorKtoRGB() is a blackbody-curve approximation; real phosphor LEDs
    differ from a true blackbody, so the temperature is a user-tunable value
    rather than a fixed per-LED-type table. (In my own testing a 3000 K-rated
    strip looked when set to 2700 K or 2500 K instead.)
  • Happy to take maintainer guidance on the per-channel-cap approach in the
    hot path.

AI assistance

This change was developed with AI assistance (Claude). AI-generated sections
are marked with // AI: comments per the contribution guidelines. All code
and this description were reviewed and proof-read by @NerdyGriffin, who takes
responsibility for the contribution.

Summary by CodeRabbit

  • New Features

    • Per-bus white-channel color temperature (Kelvin) control (0 = legacy neutral, or 1000–10000 K)
    • New per-bus UI controls to enable/configure W Kelvin (auto-seeds 6500 K when enabled)
  • Improvements

    • Kelvin values validated, saved, and restored in settings and applied when buses are created
    • Faster neutral-path when Kelvin=0 and improved per-channel capping for configured kelvins, yielding safer and more accurate W extraction

NerdyGriffin and others added 5 commits May 28, 2026 05:05
The Auto-Calculate White "Accurate" mode subtracts the W channel value
equally from R, G, B — which implicitly assumes the physical W LED
emits RGB(255, 255, 255), i.e. the sRGB white point near D65 / ~6500 K.
For 2700 K WW or 5000 K CW strips this shifts the resulting color
visibly.

Add a per-bus configurable white-LED color temperature (Kelvin) that
feeds into autoWhiteCalc, so the W LED's actual R/G/B contribution is
computed via colorKtoRGB and used to (a) cap the W channel without
overflowing any RGB channel and (b) subtract the correct per-channel
amount in ACCURATE mode. The feature is opt-in per bus via a UI
checkbox; when off (the default, wk = 0) autoWhiteCalc behaves as
before, so existing configs render identically.

UI lives next to the per-bus "Auto-calculate W channel from RGB"
selector and is only shown when AW mode is Brighter or Accurate. The
Kelvin number input is disabled (and not submitted) until the user
checks the enable box, at which point it defaults to 6500 K — matching
the implicit legacy reference white point.

https://claude.ai/code/session_019b31kdwp79ouA3gD5Tox9A

Co-authored-by: Claude <noreply@anthropic.com>
DUAL mode (RGBW_MODE_DUAL) falls through to the per-channel-cap branch
of autoWhiteCalc whenever the caller hasn't set the manual white value
(w == 0) — the same path used by BRIGHTER and ACCURATE. The UI gate
was only revealing the WKE checkbox and Kelvin input for modes 1 and
2, so users on DUAL had no way to configure the W-LED color temperature
even though their output was affected by _wR/_wG/_wB.

Include awv === 3 in the visibility condition and update the comment to
reflect the actual code path in bus_manager.cpp.

Co-authored-by: Claude <noreply@anthropic.com>
The per-channel-cap branch added in the prior commit ran three integer
divisions per pixel even when _whiteKelvin == 0 (the default), because
the cached _wR/_wG/_wB values were always read as locals — the compiler
could not constant-fold them. For RGBW strips this is a measurable hot-
path regression vs the original min(r,g,b) implementation, paid by
every user regardless of whether they enabled the feature.

Split the else-branch in two:
- _whiteKelvin == 0 (feature off, default): identical math to the
  pre-feature WLED code (w = min RGB, equal subtraction in ACCURATE).
- _whiteKelvin > 0 (feature on): the per-channel-cap path that uses
  the cached W-LED RGB equivalent.

Documents the underflow argument (floor division composes back through
the subtraction) and the _wB == 0 case near 1900 K explicitly in the
comments. Behavior is unchanged for both paths.

Co-authored-by: Claude <noreply@anthropic.com>
Rename the enable checkbox label to "Tune RGB to W channel color
temperature" so it mirrors the adjacent "Auto-calculate W channel from
RGB" selector and reads as a refinement of it.

Move the Kelvin number input out of the checkbox line into its own
wrapper div (dig<n>wkv) with a dedicated "W channel color temperature:"
label, matching the dominant WLED pattern of a checkbox revealing a
sub-options block on the following line. UI() now toggles the wrapper's
visibility instead of the bare input.

Co-authored-by: Claude <noreply@anthropic.com>
Very warm white LEDs can read as neutral even when fed a fairly warm
RGB mask, so users need to dial the configured temperature below the
previous 1900 K floor to compensate. colorKtoRGB() is well-defined down
to 1000 K (blue already clamps to 0 below 1900 K, green stays positive
-> 255,68,0), and the per-channel-cap path's existing zero guards
already handle the resulting zero channels.

Lower the bound in the form validator (set.cpp), the number input's
min attribute and the re-enable seed threshold (settings_leds.htm), and
update the zero-guard comment in bus_manager.cpp to note that blue is
zero across the whole sub-1900 K range now, not just near 1900 K.

Co-authored-by: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)

Walkthrough

Adds per-bus white-channel color temperature (Kelvin): Bus stores whiteKelvin and cached RGB coefficients, autoWhiteCalc uses either a neutral fast path or per-channel capped subtraction, and the value is persisted, parsed from settings, emitted to UI JSON, and exposed in the settings page UI/JS.

Changes

Per-Bus White Kelvin Feature

Layer / File(s) Summary
Bus class white Kelvin API & data structure
wled00/bus_manager.h
Adds colorKtoRGB prototype; Bus gains _whiteKelvin, _wR/_wG/_wB, constructor init, getWhiteKelvin() and setWhiteKelvin() declarations; BusConfig adds whiteKelvin and constructor param.
setWhiteKelvin implementation and autoWhiteCalc refactor
wled00/bus_manager.cpp
Implements Bus::setWhiteKelvin() caching Kelvin→RGB (0 = neutral); refactors Bus::autoWhiteCalc() to branch on _whiteKelvin (darkest-channel fast path when 0; per-channel capped W subtraction when non-zero); BusManager::add() applies configured Kelvin to new buses.
Configuration persistence (JSON read/write)
wled00/cfg.cpp
Reads per-bus "wk" during cfg.json deserialization and writes ins["wk"] during serialization via bus->getWhiteKelvin().
Settings form input handling
wled00/set.cpp
handleSettingsSet() parses per-bus WK, defaults to 0, clamps non-zero values outside 1000–10000K to 0, and passes validated whiteK into busConfigs.
JSON API output for UI
wled00/xml.cpp
getSettingsJS() emits per-bus WKE and WK fields based on getWhiteKelvin(), using 6500 fallback when disabled.
UI controls and configuration load
wled00/data/settings_leds.htm
Adds wkChk(n) helper, per-bus WKE checkbox and WK numeric input; UI shows/hides/enables WK based on LED type and AW mode; loadCfg() maps stored v.wk into WKE/WK fields.

Sequence Diagram

sequenceDiagram
  participant BusManager
  participant Bus
  participant autoWhiteCalc
  BusManager->>Bus: setWhiteKelvin(k)
  Bus->>Bus: colorKtoRGB(k) -> cache _wR/_wG/_wB
  Note over Bus: _whiteKelvin stored
  autoWhiteCalc->>Bus: read _whiteKelvin & cached _wR/_wG/_wB
  alt _whiteKelvin == 0
    autoWhiteCalc->>autoWhiteCalc: fast path (darkest channel)
  else _whiteKelvin > 0
    autoWhiteCalc->>autoWhiteCalc: compute per-channel caps
    autoWhiteCalc->>autoWhiteCalc: subtract scaled RGB contributions
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • wled/WLED#4529: Touches BusManager bus creation/initialization path related to applying settings after bus creation.
  • wled/WLED#5382: Modifies white/CCT computation in wled00/bus_manager.cpp, overlapping with autoWhiteCalc changes.

Suggested reviewers

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

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% 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 directly and clearly describes the main change: adding per-bus white-LED color temperature configuration for the auto-white calculation feature.
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.

@softhack007 softhack007 added the AI Partly generated by an AI. Make sure that the contributor fully understands the code! label May 29, 2026
@NerdyGriffin
Copy link
Copy Markdown
Author

I plan to submit a PR to WLED-Docs if/when this gets reviewed. Just holding off in case there are any requested changes.

@softhack007 softhack007 requested a review from DedeHai May 29, 2026 15:30
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: 2

🧹 Nitpick comments (2)
wled00/bus_manager.cpp (1)

101-115: ⚡ Quick win

Add attribution for the AI-generated blocks.

These sections are marked as AI-generated, but the required source/inspiration attribution is still missing.

As per coding guidelines "Document attribution of inspiration / knowledge / sources used in AI-generated code, e.g. link to GitHub repositories or other websites".

Also applies to: 139-158

🤖 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/bus_manager.cpp` around lines 101 - 115, The AI-generated blocks
(including the setWhiteKelvin function and the other block at the section
covering lines 139-158) need an attribution comment added immediately above each
AI-marked region: include a brief "Generated with assistance from [AI tool]"
line plus links or identifiers for any external sources or repositories used to
inspire the implementation (e.g., the AI model name and any GitHub/URL
references), and summarize what was taken from the source (e.g., colorKtoRGB
usage/logic). Update the comments around the unique symbols Bus::setWhiteKelvin
and the other AI-labeled block to contain that attribution text so reviewers can
trace provenance.
wled00/data/settings_leds.htm (1)

824-830: 💤 Low value

Use the standard AI block markers for consistency.

The other two new AI blocks in this file (Lines 208/223 and 388/408) use // AI: below section was generated by an AI// AI: end. This loadCfg block uses a single inline // AI: instead, deviating from the convention.

♻️ Align with the AI marker convention
-							{ // AI: derive WKE checkbox + WK seed from stored wk (0 = feature off)
+							// AI: below section was generated by an AI
+							// derive WKE checkbox + WK seed from stored wk (0 = feature off)
+							{
 								const wkChkEl = d.getElementsByName("WKE"+i)[0];
 								const wkEl = d.getElementsByName("WK"+i)[0];
 								const wkv = parseInt(v.wk) | 0;
 								if (wkChkEl) wkChkEl.checked = wkv > 0;
 								if (wkEl) wkEl.value = wkv > 0 ? wkv : 6500;
 							}
+							// AI: end

As per coding guidelines: "Mark AI-generated source code blocks with // AI: below section was generated by an AI / // AI: end comments".

🤖 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_leds.htm` around lines 824 - 830, Replace the single
inline AI comment in the loadCfg block that handles WKE/WK (the block creating
wkChkEl, wkEl and computing wkv) with the standard AI block markers: add a
starting comment "// AI: below section was generated by an AI" immediately
before the block and an ending comment "// AI: end" after it, so the block
wrapping the wkChkEl/wkEl/wkv logic matches the other AI-marked sections; keep
the existing code unchanged and follow the same spacing/comment style as the
other AI blocks.
🤖 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/bus_manager.cpp`:
- Around line 105-113: The setter Bus::setWhiteKelvin allows any non-zero k
through and thus can accept out-of-range kelvin values; clamp/normalize non-zero
k to the supported 1000–10000K range before storing and using it: if k==0 keep
legacy behavior, otherwise bound k to [1000,10000], assign the normalized value
to _whiteKelvin, then call colorKtoRGB(normalizedK, rgb) and update _wR/_wG/_wB
accordingly so runtime behavior matches the shared contract.

In `@wled00/data/settings_leds.htm`:
- Around line 213-222: The comment says wkChk should seed WK when re-enabling
from a blank or sub-min value but parseInt("") yields NaN so the current test
misses blanks; update function wkChk to parse wk.value with an explicit radix
(parseInt(wk.value, 10)) and change the condition to treat empty or non-numeric
values as needing seeding (e.g., if (!wk.value || isNaN(parsed) || parsed <
1000) wk.value = 6500), keeping the existing checks for the WKE checkbox
(d.Sf["WKE"+n]) and the UI() call.

---

Nitpick comments:
In `@wled00/bus_manager.cpp`:
- Around line 101-115: The AI-generated blocks (including the setWhiteKelvin
function and the other block at the section covering lines 139-158) need an
attribution comment added immediately above each AI-marked region: include a
brief "Generated with assistance from [AI tool]" line plus links or identifiers
for any external sources or repositories used to inspire the implementation
(e.g., the AI model name and any GitHub/URL references), and summarize what was
taken from the source (e.g., colorKtoRGB usage/logic). Update the comments
around the unique symbols Bus::setWhiteKelvin and the other AI-labeled block to
contain that attribution text so reviewers can trace provenance.

In `@wled00/data/settings_leds.htm`:
- Around line 824-830: Replace the single inline AI comment in the loadCfg block
that handles WKE/WK (the block creating wkChkEl, wkEl and computing wkv) with
the standard AI block markers: add a starting comment "// AI: below section was
generated by an AI" immediately before the block and an ending comment "// AI:
end" after it, so the block wrapping the wkChkEl/wkEl/wkv logic matches the
other AI-marked sections; keep the existing code unchanged and follow the same
spacing/comment style as the other AI blocks.
🪄 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: 46aeac26-38de-4191-b2cd-81f03787a86f

📥 Commits

Reviewing files that changed from the base of the PR and between 555d0cf and 38d19b0.

📒 Files selected for processing (6)
  • wled00/bus_manager.cpp
  • wled00/bus_manager.h
  • wled00/cfg.cpp
  • wled00/data/settings_leds.htm
  • wled00/set.cpp
  • wled00/xml.cpp

Comment thread wled00/bus_manager.cpp
Comment thread wled00/data/settings_leds.htm
NerdyGriffin and others added 3 commits May 29, 2026 12:29
wkChk() promised to reseed the Kelvin field to 6500 K "from a blank or
sub-min value", but parseInt("") is NaN and NaN < 1000 is false, so a
cleared field was never reseeded — contradicting the comment. Invert the
test to !(parsed >= 1000) so NaN (blank/non-numeric) also seeds, and add
an explicit radix per the review.

Spotted by CodeRabbit on wled#5654.

Co-authored-by: Claude <noreply@anthropic.com>
"Tune RGB to W channel color temperature" implied the setting adjusts
RGB, but it primarily changes how the W value is calculated (in
Brighter/Accurate/Dual) and only modifies RGB in Accurate mode — in
Brighter/Dual the RGB channels are left untouched. Rename to "Correct
auto-white for W channel color temperature", which is accurate across
all modes and matches the sibling "Auto-calculate W channel from RGB".

Co-authored-by: Claude <noreply@anthropic.com>
@coderabbitai coderabbitai Bot added the effect label May 29, 2026
NerdyGriffin and others added 3 commits May 29, 2026 13:32
The block that derives the WKE checkbox / WK seed from stored wk used a
single inline "// AI:" comment instead of the start/end markers the other
AI-generated sections use. Wrap it with the standard
"// AI: below section was generated by an AI" / "// AI: end" pair for
consistency. Comment-only; no behavior change.

Spotted by CodeRabbit on wled#5654.

Co-authored-by: Claude <noreply@anthropic.com>
@DedeHai
Copy link
Copy Markdown
Collaborator

DedeHai commented May 30, 2026

Looks good in general, the hot path needs speed optimization.
Adding config values needs careful consideration: adding per bus white balance/ color correction is on my todo list. Need to think about if that collides with the approach used, the config values especially.

A fixed W-LED color temperature only makes sense for single-white RGBW
buses. Dual-white CCT buses (RGBCW, RGB+CCT) have a variable white point
set via the CCT/white-balance control, and non-RGB buses have nothing to
derive the correction from — so the control was previously shown (gated
on hasW only, like "Auto-calculate W channel from RGB") for bus types
where it is meaningless or redundant.

Tighten the UI gate to hasW && hasRGB && !hasCCT so the control appears
only for true RGBW types. Also guard autoWhiteCalc: route _hasCCT and
!_hasRgb buses through the legacy fast path even when wk != 0, so a
hand-edited config can't trigger the per-channel-cap math on a bus type
it doesn't apply to. The _whiteKelvin == 0 check stays first to keep the
common feature-off path branch-cheap.

Co-authored-by: Claude <noreply@anthropic.com>
@NerdyGriffin
Copy link
Copy Markdown
Author

Looks good in general, the hot path needs speed optimization. Adding config values needs careful consideration: adding per bus white balance/ color correction is on my todo list. Need to think about if that collides with the approach used, the config values especially.

Thanks for the update. I see how this would overlap with general-purpose color correction, though depending on the implementation they might not achieve the same thing. I implemented this specifically for the use-case of an RGBW strip with Auto-calculate white channel from RGB set to Accurate, where the resulting colors are very wrong for anything orange/yellow if the hardware's white channel is not 6500K. Whether or not a general color correction can replace my implementation might depend on where the color correction goes in the pipeline, before or after autoWhiteCalc().

In the meantime, I will continue daily-driving my build with this feature on my DigUno and dig2go until something like this or a compatible color correction is implemented in a future release.

NerdyGriffin and others added 3 commits May 30, 2026 10:27
"W-channel CCT controls" was ambiguous — "CCT" overlaps with WLED's
separate dual-white white-balance system. Quote the actual checkbox
label ("Correct auto-white for W channel color temperature") so the
comment names this specific control unambiguously. Comment-only.

Co-authored-by: Claude <noreply@anthropic.com>
The backend comments described the new feature as "W-channel CCT", which
is ambiguous (collides with WLED's separate dual-white white-balance CCT
system) and doesn't match the actual identifiers (_whiteKelvin, wk, WK,
WKE). Rename to "W channel color temperature" across the comments added
in this PR, matching the UI label. Pre-existing _cct/_cctBlend/
setSegmentCCT comments (the real white-balance system) are left as-is.
Comment-only.

Co-authored-by: Claude <noreply@anthropic.com>
When the per-bus W channel color temperature feature is enabled, the
per-channel-cap path divided each RGB channel by the runtime-variable
_wR/_wG/_wB once per pixel (3 real divisions; the /255 terms use a
constant divisor the compiler already strength-reduces). Integer
division is multi-cycle on ESP32/ESP8266.

Precompute Q15 fixed-point reciprocals of _wR/_wG/_wB once in
setWhiteKelvin and replace the per-pixel divisions with a multiply +
shift. The reciprocals are floor-biased so the derived w cap is never
larger than the exact value, preserving underflow safety. Verified
across all 65280 (channel,_wX) pairs (max error 1, no over-estimate)
and 5.68M full-pipeline cases (<=1/255 output delta). S=15 keeps the
worst-case product (channel=255, _wX=1) inside uint32 with ~50% margin
at the same accuracy as S=16.

Addresses maintainer review feedback on hot-path speed.

Co-authored-by: Claude <noreply@anthropic.com>
@NerdyGriffin
Copy link
Copy Markdown
Author

Pushed the hot-path optimization. The per-pixel divisions by _wR/_wG/_wB in autoWhiteCalc's enabled path are now precomputed Q15 reciprocals (multiply + shift) cached in setWhiteKelvin. The reciprocals are floor-biased, so the white cap can only come out ≤ the exact value — the underflow guarantee is preserved — and I verified the output is equivalent to the previous code across the full input space (≤1/255 delta, no unsafe over-cap).

I also checked the generated Xtensa assembly to be sure: the per-pixel path now has zero divide instructions. The /255 in the subtraction step was already strength-reduced by the compiler to a muluh + shift, so I left it alone and only replaced the runtime-variable /_wX divisions (which the compiler can't reduce). Re-tested on ESP32 (QuinLED Dig-Uno + dig2go).

Separately — relevant to your point about where general color correction should sit in the pipeline: gamma is applied before autoWhiteCalc (FX_fcn.cpp:1742:1743), so with gamma enabled a per-bus correction subtracts perceptual-space coefficients from already-gamma-corrected channels. I prototyped compensating for that and it didn't cleanly help in practice, so it's not actionable here — just flagging the ordering in case it's useful for the color-correction design.

— Claude (Anthropic's Claude Code; this work was reviewed and hardware-tested by @NerdyGriffin)

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: 2

Caution

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

⚠️ Outside diff range comments (2)
wled00/data/settings_leds.htm (2)

831-835: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize imported wk values the same way the backend does.

loadCfg() treats any positive v.wk as enabled, but handleSettingsSet() later rejects non-zero values outside 1000..10000 and stores 0 instead (wled00/set.cpp, Lines 234-236). That makes config-template import show states that will silently flip off on save.

Suggested fix
 							{
 								const wkChkEl = d.getElementsByName("WKE"+i)[0];
 								const wkEl = d.getElementsByName("WK"+i)[0];
-								const wkv = parseInt(v.wk) | 0;
+								const rawWk = parseInt(v.wk, 10) | 0;
+								const wkv = (rawWk >= 1000 && rawWk <= 10000) ? rawWk : 0;
 								if (wkChkEl) wkChkEl.checked = wkv > 0;
 								if (wkEl) wkEl.value = wkv > 0 ? wkv : 6500;
 							}
🤖 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_leds.htm` around lines 831 - 835, Imported wk values
must be normalized the same way the backend does: parse v.wk to an integer
(fallback 0), then treat it as enabled only if it's within 1000..10000; update
the WKE checkbox (wkChkEl) to checked only when wk is in that range and set the
WK input (wkEl) to the wk value when enabled or to the UI default (6500) when
disabled so the displayed state matches what handleSettingsSet() will accept and
store.

399-410: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Honor the global auto-white override when deciding whether WK applies.

autoWhiteCalc() uses the global AW mode first (wled00/bus_manager.cpp, Lines 129-130), but this UI gate only looks at AW<n>. If the global override is None or Max, the Kelvin controls can still appear even though runtime will ignore them.

Suggested fix
 				{
 					const awEl = d.Sf["AW"+n];
-					const awv = awEl ? parseInt(awEl.value) : 0;
+					const awv = awEl ? parseInt(awEl.value, 10) : 0;
+					const gaw = parseInt(d.Sf.AW.value, 10);
+					const effectiveAw = gaw < 255 ? gaw : awv;
 					const wkBox = gId("dig"+n+"wk");
 					// only true single-white RGBW types: a fixed W-LED color temperature is
 					// meaningless for dual-white CCT buses (variable white point) and for
 					// non-RGB buses (nothing to derive the correction from)
-					if (wkBox) wkBox.style.display = (hasW(t) && hasRGB(t) && !hasCCT(t) && (awv === 1 || awv === 2 || awv === 3)) ? "inline" : "none";
+					if (wkBox) wkBox.style.display = (hasW(t) && hasRGB(t) && !hasCCT(t) && (effectiveAw === 1 || effectiveAw === 2 || effectiveAw === 3)) ? "inline" : "none";
 					const wke = d.Sf["WKE"+n], wk = d.Sf["WK"+n], wkv = gId("dig"+n+"wkv");
 					if (wke && wk) {
 						wk.disabled = !wke.checked;
 						if (wkv) wkv.style.display = wke.checked ? "inline" : "none";
 					}
 				}
🤖 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_leds.htm` around lines 399 - 410, The UI currently
checks only the per-bus AW control (AW+n) when deciding to show the Kelvin
controls (wkBox) but must also honor the global auto-white override used by
autoWhiteCalc(); read the global AW control (e.g. d.Sf["AW"] or gId("AW") as
used for the global setting), get its value, and include a guard that hides
wkBox when the global mode is "None" or "Max" (match the same string/enum names
used by autoWhiteCalc()), in addition to the existing per-bus checks (awEl/awv,
hasW/hasRGB/hasCCT) so WK is hidden whenever the runtime will ignore per-bus
Kelvin settings.
🤖 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/bus_manager.h`:
- Around line 235-237: The reciprocal cache fields _rwR, _rwG, and _rwB in class
Bus are added but not initialized; update the Bus constructor (the Bus::Bus
initializer list or body) to initialize _rwR, _rwG, and _rwB to 0 so they have
defined values before any call to setWhiteKelvin(), ensuring the autoWhiteCalc
hot path won't read uninitialized reciprocals.

In `@wled00/cfg.cpp`:
- Line 241: The code reads whiteK from JSON as uint16_t without validation
(uint16_t whiteK = elm[F("wk")] | 0), allowing invalid values into BusConfig;
update the ingress parsing for wk so that you accept 0 (disabled) or clamp
values into the supported range [1000,10000] before constructing BusConfig—i.e.,
parse elm[F("wk")] into a temporary integer/default 0, if value != 0 then if
value < 1000 set to 1000, if value > 10000 set to 10000, and only then assign to
whiteK (or reject non-numeric inputs by treating as 0). Ensure you apply this
change where whiteK is read/used (reference variable whiteK and the JSON key
elm[F("wk")]) so runtime state cannot contain out-of-range temps.

---

Outside diff comments:
In `@wled00/data/settings_leds.htm`:
- Around line 831-835: Imported wk values must be normalized the same way the
backend does: parse v.wk to an integer (fallback 0), then treat it as enabled
only if it's within 1000..10000; update the WKE checkbox (wkChkEl) to checked
only when wk is in that range and set the WK input (wkEl) to the wk value when
enabled or to the UI default (6500) when disabled so the displayed state matches
what handleSettingsSet() will accept and store.
- Around line 399-410: The UI currently checks only the per-bus AW control
(AW+n) when deciding to show the Kelvin controls (wkBox) but must also honor the
global auto-white override used by autoWhiteCalc(); read the global AW control
(e.g. d.Sf["AW"] or gId("AW") as used for the global setting), get its value,
and include a guard that hides wkBox when the global mode is "None" or "Max"
(match the same string/enum names used by autoWhiteCalc()), in addition to the
existing per-bus checks (awEl/awv, hasW/hasRGB/hasCCT) so WK is hidden whenever
the runtime will ignore per-bus Kelvin settings.
🪄 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: 557a77c7-2bef-4039-89ab-3a616de00f00

📥 Commits

Reviewing files that changed from the base of the PR and between 7f7e78a and 988d833.

📒 Files selected for processing (6)
  • wled00/bus_manager.cpp
  • wled00/bus_manager.h
  • wled00/cfg.cpp
  • wled00/data/settings_leds.htm
  • wled00/set.cpp
  • wled00/xml.cpp

Comment thread wled00/bus_manager.h
Comment on lines +235 to +237
uint32_t _rwR; // Q15 reciprocal of _wR (floor((255<<15)/_wR), 0 if _wR==0) for autoWhiteCalc hot path
uint32_t _rwG;
uint32_t _rwB;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Initialize reciprocal cache members in Bus constructor.

_rwR/_rwG/_rwB are newly added but not initialized in the constructor, which risks undefined values being consumed before setWhiteKelvin() runs.

Proposed fix
     , _whiteKelvin(0)
     , _wR(255)
     , _wG(255)
     , _wB(255)
+    , _rwR(0)
+    , _rwG(0)
+    , _rwB(0)
     {
🤖 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/bus_manager.h` around lines 235 - 237, The reciprocal cache fields
_rwR, _rwG, and _rwB in class Bus are added but not initialized; update the Bus
constructor (the Bus::Bus initializer list or body) to initialize _rwR, _rwG,
and _rwB to 0 so they have defined values before any call to setWhiteKelvin(),
ensuring the autoWhiteCalc hot path won't read uninitialized reciprocals.

Comment thread wled00/cfg.cpp
bool refresh = elm["ref"] | false;
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM
uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY;
uint16_t whiteK = elm[F("wk")] | 0; // physical W channel color temperature in K (0 = neutral/legacy)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clamp wk at config ingress to the supported domain.

wk is accepted as-is here. Please enforce 0 (disabled) or [1000, 10000] before constructing BusConfig, so invalid JSON values don’t leak into runtime state.

Proposed fix
-      uint16_t whiteK = elm[F("wk")] | 0; // physical W channel color temperature in K (0 = neutral/legacy)
+      uint16_t whiteK = elm[F("wk")] | 0; // physical W channel color temperature in K (0 = neutral/legacy)
+      if (whiteK != 0 && (whiteK < 1000 || whiteK > 10000)) whiteK = 0;

As per coding guidelines, enforce input-validation at the first untrusted ingress point (HTTP/JSON request bodies and query parameters).

🤖 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/cfg.cpp` at line 241, The code reads whiteK from JSON as uint16_t
without validation (uint16_t whiteK = elm[F("wk")] | 0), allowing invalid values
into BusConfig; update the ingress parsing for wk so that you accept 0
(disabled) or clamp values into the supported range [1000,10000] before
constructing BusConfig—i.e., parse elm[F("wk")] into a temporary integer/default
0, if value != 0 then if value < 1000 set to 1000, if value > 10000 set to
10000, and only then assign to whiteK (or reject non-numeric inputs by treating
as 0). Ensure you apply this change where whiteK is read/used (reference
variable whiteK and the JSON key elm[F("wk")]) so runtime state cannot contain
out-of-range temps.

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

Labels

AI Partly generated by an AI. Make sure that the contributor fully understands the code! effect enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants