Add an optional overnight-off and morning-on window (#52) #125
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!125
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/52-morning-window"
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 #52.
Two transitions the v1 envelope couldn't express: lights off in the small hours, back on before sunrise for early risers. Both off by default, in
.envand Settings, with the morning trigger either a fixed time or an offset from sunrise.Shaped around the two bugs this code has already had
The envelope is the most incident-prone part of the project — #106 cancelled a pending off by attributing it to the wrong calendar day, #110 compounded the dim across nights. Both were ordering and date-attribution bugs, so the design tries to make those classes hard rather than merely absent.
Midnight crossing has no special case. Later transitions fall on the next calendar day — exactly what #106 got wrong. Instead of branching, each transition is "the first occurrence of this clock time after the previous one", so an overnight-off at 00:30 lands on the following day and one at 23:45 doesn't, with no branch either way.
Out-of-order configurations are dropped, not scheduled. A morning-on at 05:30 against a 05:00 winter sunrise would switch the lights on after the off job had turned them off, and leave them burning all day. It's dropped instead, and the tests assert the ordering invariant directly rather than checking individual times.
Disabling unregisters the job. The jobstore is persistent, so merely not rescheduling would leave a stale morning-on sitting there waiting to fire.
The morning push resolves against the evening's date. At 05:30 on 26 December the lights are still showing Christmas night; the default would swap in Boxing Day partway through the night. Same reasoning
get_active_timesuses for the carried-forward off.Both new jobs also revert an active quick push, per the issue's "revert at every envelope transition".
A Python trap the DST tests turned up
Worth recording, because I got it wrong first:
Subtracting two aware datetimes that share a
tzinfogives the wall-clock difference, not the elapsed one. Python assumes the offsets are equal and cancels them. So a fall-back night and an ordinary night both report 14 hours:My first two duration assertions were wrong for precisely this reason and failed. They now measure via UTC, and one test pins the trap directly so nobody "simplifies" the conversion away.
The practical upshot for this codebase: any elapsed-time arithmetic on same-zone datetimes is silently wrong across a DST boundary. Nothing currently does that — the ramp uses
datetime - timedelta, which is fine — but it's now documented in a test rather than folklore.The other DST tests assert the invariant across both transitions (an hour vanishing mid-window on spring forward, an hour repeating on fall back), and that a 05:30 request still yields 05:30 on both sides rather than 04:30 or 06:30.
Verification
ruff,mypycleannode:22:tsc -b,eslint,vitest(76) cleanuvx pre-commit run --all-filespassesDefault behaviour is unchanged: with both flags off the envelope is exactly the v1 on → dim → off.
🤖 Generated with Claude Code