Put meteor showers, full moons and aurora on the calendar (#20) #127
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!127
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/20-astronomical"
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 #20. Eclipses are split to #126.
Seven meteor showers and eleven named full moons become resolvable events, each with its own built-in scheme, plus an optional aurora layer.
Full moons are computed, not tabulated
They come from
astral, which is already a dependency for sunset. So unlike the solstices they add nothing to the lookup-table horizon that #61 warns about — they keep working indefinitely rather than falling off the end of a table in 2035.The tests assert astronomy rather than dates: that each returned day really is full (
astral.moon.phase()within tolerance of 14), that consecutive gaps are whole synodic months, and that the answer moves year to year. A function that returned a fixed date would pass a weaker test, and this code is exactly the kind that looks right while being wrong.One test premise of mine was wrong and is worth recording. I first asserted consecutive named moons are 28–31 days apart. It failed: May 2026 has two full moons (the 1st and the 31st), and picking one per calendar month necessarily skips one, so that gap is two synodic months. The test now asserts whole synodic multiples and documents the blue-moon case.
Meteor showers are fixed spans, not a table
Earth crosses the same debris stream at the same point in its orbit each year, so the peak moves by at most a day — a table would be maintenance for no accuracy. And the peak is a few hours inside several active nights, so a single-day event would usually miss it. Each shower is a multi-day span.
Schemes are explicit per event
A pre-existing test (
test_bundled_events_have_explicit_theme_proposals) enforces that convention, and my initial tag-inherited entries defeated it. That was the right catch: the colours now differ by the character of each shower and moon rather than being eleven copies. The Harvest Moon is orange because the low autumn moon actually looks that way; the Geminids are colder and faster than the Perseids.Aurora
AURORA_ENABLED, off by default, following the Calendarific pattern — a self-hosted install with no interest in this makes no outbound requests at all. Polls NOAA's planetary K-index and shows an aurora scheme aboveAURORA_MIN_KP, restoring the scheduled scheme when it subsides.Edge-triggered: re-pushing an identical state every fifteen minutes would restart the effect each time and read as a stutter on the strip rather than a continuous aurora. The falling edge hands the night back to
_push_today_scheme()rather than to a remembered state that may since have gone stale.No visibility model, deliberately. Visibility depends on geomagnetic latitude as well as Kp, and that mapping is a rule of thumb rather than a formula. Rather than ship a bad model, the threshold is the knob — low in Alaska, high enough in Texas that it never fires — and it's documented in those terms.
Why eclipses are split out
They need an ephemeris and a per-observer visibility calculation. A lunar eclipse over the Indian Ocean means nothing to a roofline in Texas, and solar totality varies over a few hundred kilometres — so a worldwide eclipse event would be wrong most of the time. That's a heavier dependency than the image should carry after #60's hardening, and it deserved its own decision rather than a guess inside this one. Tracked in #126.
Verification
ruff checkclean,mypy appclean across 69 source files,pytest610 passed.🤖 Generated with Claude Code