Gradual brightness ramp at on/off transitions #21
Labels
No labels
area/ai
area/backend
area/frontend
area/infra
area/scheduler
area/wled
good-first-issue
priority/high
priority/low
priority/medium
type/bug
type/chore
type/ci-cd
type/docs
type/feature
type/qa
v1.0.0
v1.1.0
v1.2.0
v2.0.0
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/Iris-WLED#21
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
Fade the lights in over a configurable window at the on-transition (sunset) and fade out at the off-transition (sunrise), instead of snapping to full/zero brightness.
Why it's valuable
A small change with an outsized polish payoff — a slow ramp at dusk reads as intentional and premium. It's a pure scheduler/push refinement with no schema impact.
Sketch
RAMP_ON_MINUTES/RAMP_OFF_MINUTES(0 = current snap behavior).Acceptance criteria
0preserves the current instant behaviorProposed enhancement (brainstorm follow-up).
Picking this up. The sketch offers two mechanisms — scheduler interval pushes, or "uses WLED's transition/nightlight features where possible to offload the ramp to the device" — so I measured the device before choosing. The answer is decisive, and not what I expected.
WLED's
transitioncaps at 65.5 seconds, and overflows silently past ittransitionis in 100 ms units, but the firmware stores it as a uint16 of milliseconds. Past 65535 ms it wraps modulo 65536 rather than clamping. Measured on the 16.0.0 controller:70000 − 65536 = 4464 ms → 44. Every row is exact modulo arithmetic.So offloading a multi-minute ramp to the device is impossible — and worse, asking for one silently produces a short, arbitrary fade instead. A 2-minute request becomes 54 seconds; a 65.6-second request becomes an instant snap. I had assumed from the field width that ~109 minutes was available; that was wrong, and only measuring caught it.
But the device's fade itself is smooth and linear
state.brireports the transition target immediately, so it cannot observe a ramp.info.leds.pwr— estimated draw, computed from actual rendered output — can. A 30-second fade from bri 1 to 255:Near-perfectly linear, ~95 mA per 2 s, completing in exactly the requested 30 s. No stepping at 2-second sampling, and WLED interpolates per frame so the true resolution is far finer.
Design that falls out of this
Neither of the sketch's options alone: chained scheduler steps, each carrying a WLED
transitionequal to the step interval. The scheduler sets a coarse target every N seconds; the device fades smoothly between them. The result is continuous rather than stepped, at ~2 pushes a minute instead of one per brightness level.The step interval must stay under 65 s or it hits the overflow above. I'll use 30 s.
Related latent bug
transition_mshas no backend validation (app/schemas/schemes.py:66). The scheme editor's input caps at 65535 ms, which divides to 655 and is coincidentally safe — but the API and preset import accept any integer, and anything above 65535 ms wraps on the device. Adding a bound as part of this, since it is the same failure and I am in the same code.Interaction with late-night dimming
Per the acceptance criteria. The ramp ceiling comes from the active baseline reduced by the late-night factor when the ramp runs inside that window, and the dim job cancels any in-flight ramp so the two cannot fight over brightness — the same reason
trigger_scheduler_revertexists for quick push.Timing: the on-ramp starts at on-time and reaches full after
ramp_on_minutes; the off-ramp ends at off-time, so it startsramp_off_minutesearly. Asymmetric, but it matches what each time means — on-time is when the lights start appearing, off-time is when they are gone.Lights were snapshotted before this and restored after; the controller is back to
on:false, bri:8, fx 115.Done — #119 merged, all seven CI jobs green.
Acceptance criteria
RAMP_ON_MINUTES/RAMP_OFF_MINUTES, also in Settings. The fade-in starts at the on time; the fade-out ends at the off time, so it begins early via a newoff_ramp_joband the lights are dark on schedule.0preserves the current instant behaviour — the default, and the only path when unset.The measurement that decided the mechanism
The sketch offered scheduler stepping or offloading to the device. The second is not available, and fails in a way worth recording:
transitionis documented in 100 ms units but stored as a uint16 of milliseconds. Past 65535 ms it wraps modulo 65536 rather than clamping — 656 (65.6 s) gives an instant snap, 1200 (nominally 2 min) gives 54.4 s. No error; the request succeeds and the light does something else.What the device is good at is the fade itself — sampling
info.leds.pwracross a 30 s transition gives a near-perfect line finishing exactly on time. So the implementation is neither option alone but a combination: scheduler steps each carrying a device transition equal to the step interval, so the device fades between coarse targets.Verified on hardware, which is the part that matters
Driving the real ramp code against the controller and sampling actual draw:
The claim under test is continuity across step boundaries, and draw climbs at a constant ~90 mA per 2 s straight through them — no discontinuity where the scheduler stepped. Lands exactly on 255.
Related bug fixed
transition_mshad no backend validation. The scheme editor's input caps at 65535 ms, which divides to 655 and is coincidentally safe; the API and preset import accepted any integer and could reach the overflow above. Now bounded, with a test asserting the bound's quotient still fits the uint16 afterscheme_to_wled_statemultiplies it back up.What is left, and it is not code
The mechanism is verified; the remaining question is taste — whether 20 minutes or 45 feels right at your roofline. That is a Settings change on some evening, not a code change. Both values default to 0, so nothing changes until you set them.
The overflow and the linear-fade measurement are both recorded in
docs/wled-compatibility.md.Controller snapshotted and restored throughout — back to
on:false, bri:8, fx 115, presets untouched.