Morning-hours lighting window (overnight-off, on again before sunrise) #52

Closed
opened 2026-07-15 15:33:14 +00:00 by claude-bot · 1 comment
Contributor

Common holiday-lights pattern the current envelope can't express: lights off at midnight, back on 05:30 → sunrise for early risers/commuters. Today's model is strictly on-at-sunset → dim → off-at-sunrise.

  • Optional overnight-off time (extends the existing late-night dim into a full off)
  • Optional morning window: MORNING_ON_TIME (fixed or sunrise-relative) → off at sunrise, reusing the day's scheme
  • Scheduler jobs + settings UI; interacts with quick-push revert semantics (revert at every envelope transition)
  • Tests around DST and the midnight boundary
Common holiday-lights pattern the current envelope can't express: lights off at midnight, back **on 05:30 → sunrise** for early risers/commuters. Today's model is strictly on-at-sunset → dim → off-at-sunrise. - [ ] Optional overnight-off time (extends the existing late-night dim into a full off) - [ ] Optional morning window: `MORNING_ON_TIME` (fixed or sunrise-relative) → off at sunrise, reusing the day's scheme - [ ] Scheduler jobs + settings UI; interacts with quick-push revert semantics (revert at every envelope transition) - [ ] Tests around DST and the midnight boundary
claude-bot added this to the v1.1.0 milestone 2026-07-15 15:33:14 +00:00
Author
Contributor

Done — #125 merged, CI green.

  • Optional overnight-off time
  • Optional morning windowMORNING_ON_TIME fixed, or sunrise-relative via MORNING_ON_OFFSET_MIN, reusing the day's scheme
  • Scheduler jobs + settings UI; quick-push revert at every envelope transition
  • Tests around DST and the midnight boundary

Shaped around this file's history

The envelope produced #106 (a pending off cancelled by recomputing it on the wrong calendar day) and #110 (the dim compounding 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. 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 does not — 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. 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 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.

A Python trap the DST tests turned up

Worth recording, because my first two duration assertions were wrong for exactly this reason:

Subtracting two aware datetimes that share a tzinfo gives the wall-clock difference, not the elapsed one. Python assumes the offsets are equal and cancels them.

same-zone subtraction : 14:00:00
via UTC (real elapsed): 15:00:00

A fall-back night and an ordinary night both report 14 hours; only converting to UTC shows the hour that actually appeared. The tests now measure via UTC, and one pins the trap directly so the conversion does not get "simplified" away later.

Nothing in the app currently does elapsed-time arithmetic on same-zone datetimes — the ramp uses datetime - timedelta, which is fine — but it is now documented in a test rather than folklore.

Default behaviour is unchanged: with both flags off the envelope is exactly the v1 on → dim → off.

Done — #125 merged, CI green. - [x] **Optional overnight-off time** - [x] **Optional morning window** — `MORNING_ON_TIME` fixed, or sunrise-relative via `MORNING_ON_OFFSET_MIN`, reusing the day's scheme - [x] **Scheduler jobs + settings UI; quick-push revert at every envelope transition** - [x] **Tests around DST and the midnight boundary** ### Shaped around this file's history The envelope produced #106 (a pending off cancelled by recomputing it on the wrong calendar day) and #110 (the dim compounding 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.** 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 does not — 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. 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 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. ### A Python trap the DST tests turned up Worth recording, because my first two duration assertions were wrong for exactly this reason: **Subtracting two aware datetimes that share a `tzinfo` gives the wall-clock difference, not the elapsed one.** Python assumes the offsets are equal and cancels them. ``` same-zone subtraction : 14:00:00 via UTC (real elapsed): 15:00:00 ``` A fall-back night and an ordinary night both report 14 hours; only converting to UTC shows the hour that actually appeared. The tests now measure via UTC, and one pins the trap directly so the conversion does not get "simplified" away later. Nothing in the app currently does elapsed-time arithmetic on same-zone datetimes — the ramp uses `datetime - timedelta`, which is fine — but it is now documented in a test rather than folklore. Default behaviour is unchanged: with both flags off the envelope is exactly the v1 on → dim → off.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Iris-WLED#52
No description provided.