Fade the lights in and out at the scheduled transitions (#21) #119
No reviewers
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 milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/Iris-WLED!119
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/21-brightness-ramp"
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?
Closes #21.
RAMP_ON_MINUTES/RAMP_OFF_MINUTES, also in Settings, both0(the previous instant behaviour) by default. The fade-in starts at the on time and reaches the scheme's brightness after the window; the fade-out ends at the off time, so it begins early and the lights are dark on schedule. Colours and effect still land immediately — only the level moves.The measurement that chose the design
The issue offered two mechanisms: scheduler interval pushes, or "offload the ramp to the device". I measured before choosing, and the second turned out to be impossible in a way worth knowing about.
WLED's
transitionis documented in 100 ms units but stored as a uint16 of milliseconds. Past 65535 ms it wraps modulo 65536 rather than clamping:70000 − 65536 = 4464 → 44. Every row is exact modulo arithmetic, with no error and no clamp — the request succeeds and the light does something else entirely.I had told you earlier that the field width allowed ~109 minutes. That was wrong: I'd reasoned from 100 ms units × uint16 without checking what the firmware actually stores. Only measuring caught it.
But the fade itself is excellent.
state.brireports the transition target immediately and can't observe a ramp;info.leds.pwr(estimated draw, derived from rendered output) can. A 30 s transition traced a near-perfect line, ~95 mA per 2 s, finishing exactly on time.So: neither of the two options, but a combination — chained scheduler steps, each carrying a device transition equal to the step interval. The scheduler sets a coarse target twice a minute; the device fades between them.
Verified on the roofline, not just in tests
I drove the real ramp code against your controller and sampled actual draw:
The point is the step boundaries: draw climbs at a constant ~90 mA per 2 s straight through them, with no discontinuity where the scheduler stepped. That's the whole design claim — continuous, not a staircase — and it holds on real hardware. It lands exactly on 255.
Interactions
The late-night dim cancels an in-flight ramp rather than racing it: a ramp still stepping toward a ceiling the dim just lowered would keep overwriting it, and whichever pushed last would win. Same reasoning as the existing
trigger_scheduler_revert.When an on transition itself falls inside the late-night window — a fixed on-time after 23:00, or a far-northern sunset — the ramp ceiling is the dimmed level, so a ramp can't undo a dim that already happened. That's the issue's "respect the late-night dim target as the ramp ceiling" criterion.
off_jobstill fires at the scheduled time as a backstop, in case a ramp was cancelled or the controller vanished mid-fade. A failed ramp step logs and keeps going rather than aborting — a ramp is cosmetic, and stranding the lights mid-fade is worse than a missed step.Related bug fixed
transition_mshad no backend validation. The scheme editor caps its input at 65535 ms, which divides to 655 and is coincidentally safe — but the API and preset import accepted any integer, and anything larger hits the overflow above. Now bounded, with a test asserting the bound's quotient still fits the uint16 afterscheme_to_wled_statemultiplies it back up.Verification
ruff,mypycleannode:22:tsc -b,eslint,vitest(76) all cleanuvx pre-commit run --all-filespasseson:false, bri:8, fx 115), presets untouched🤖 Generated with Claude Code