---
title: A Holiday Color Engine with a Calendar Computed to 2099
description: I moved my front-yard lights off Hue hub scenes onto a Home Assistant engine with per-bulb fades and a holiday calendar computed to 2099, checked 27/27.
image: https://willcurran.com/hubfs/will-curran-brand/holiday-color-engine-cover-nolane.png
---

[Skip to content](https://willcurran.com/blog/holiday-color-engine#main)

[Will Curran](https://willcurran.com/)

[Home](https://willcurran.com/) [Portfolio](https://willcurran.com/portfolio) [Press](https://willcurran.com/media) [Blog](https://willcurran.com/blog) [Favorites](https://willcurran.com/favorites) [Speaking](https://willcurran.com/speaking) [Consulting](https://willcurran.com/consult) 

[Hire Will](https://willcurran.com/hire)

[Writing](https://willcurran.com/blog) Home — A Holiday Color Engine with a Calendar Computed to 2099 [Subscribe](https://willcurran.com/blog/subscribe)

Home

# A Holiday Color Engine with a Calendar Computed to 2099

![](https://willcurran.com/hs-fs/hubfs/will-curran-brand/will-curran-avatar.jpg?width=44&height=44&name=will-curran-avatar.jpg)

Will Curran Oct 16, 2026 · 7 min read

<https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwillcurran.com%2Fblog%2Fholiday-color-engine>

On this page

![An abstract dark scene: a wavy row of glowing colored dots above a strip of year ticks running from 2026 to 2099, next to the title A holiday color engine, computed to 2099.](https://willcurran.com/hs-fs/hubfs/will-curran-brand/holiday-color-engine-cover-nolane.png?width=1600&height=900&name=holiday-color-engine-cover-nolane.png)

On October 16, 2026 I moved my front-yard lights off the color scenes stored on the Philips Hue hub and onto an engine I built in Home Assistant. It has 46 looks and fades every bulb on its own. The lights took one evening. The calendar took most of the thinking: it knows when Diwali, Holi, Hanukkah and Lunar New Year fall every year through 2099, and before I trusted it, it had to match every published date I tested it against, 27 out of 27.

## What I wanted

- **Looks that move.** Holiday palettes that keep shifting, so no LED channel sits on one color for hours.
- **Nothing living on the hub.** I wanted the looks somewhere I could read, test and change.
- **Full brightness.** No lightning effects, and no sports game nights. Those were my calls.
- **Motion still wins.** When someone walks up, plain warm white, then back to the look.
- **Set and forget.** I never want to look up when Diwali is again.

## The first plan left the hub in charge

The first plan kept each look as a Hue dynamic scene, with the bridge drifting the colors and a Home Assistant calendar picking which scene ran each night. Before anything was built, I asked whether the looks could live in Home Assistant instead. They could: a script can fade each bulb through a palette, with hard chases as well as slow drifts. I picked that. My AI assistant wrote the code; the calls on what the yard should do were mine.

## The engine is one loop

A look is a palette of three to seven colors and a step time, from 1.5 seconds to 45. Most looks drift, fading for the whole step. A few chase, hopping fast and then holding, the way a chase runs on a lighting desk.

Every step, the engine sends each bulb its own color. Each bulb starts at a different point in the palette, offset by its position, so the color travels from bulb to bulb like a wave. It never sends a command to a Hue group. A group can't give each bulb a different color, and addressing bulbs one at a time is what lets the engine skip just one of them.

Three rules kept it from fighting anything else:

- It skips any bulb a motion override is holding.
- After its first frame, it never turns on a bulb that something else turned off.
- It stops by itself once every bulb is off, so the existing sunrise routine needed no changes.

## Motion still wins

Motion switches the yard to warm white at 2703 K, full brightness. I copied those bright scenes off the hub into Home Assistant too.

The detail that mattered was timing. While an override owns a bulb, a hold flag stays on, and the engine leaves that bulb alone. The flag clears about two minutes after motion stops, five seconds before the automation that restarts the engine. If they cleared at the same moment, the restart could see the bulbs still held and skip them.

## Moving holidays are astronomy

Fixed dates are easy. The US floating holidays (Thanksgiving, Mother's Day, Father's Day, Memorial Day) are weekday math, and Easter has a well-known arithmetic formula. All of those are computed forever inside the template.

The rest follow the moon and the sun, and the template can't run an astronomy library. So a Python script computes them once, for 2026 to 2099, and writes them into the template as lookup tables:

- **Lunar New Year**: the first day of the first lunar month, from `lunardate`.
- **Hanukkah**: 25 Kislev from `convertdate`'s Hebrew calendar, lit from the evening before.
- **Diwali**: the first new moon after the sun enters sidereal Libra (Lahiri ayanamsa), in Indian Standard Time. If the new moon lands at 18:05 IST or later, that day is Diwali; otherwise the day before.
- **Holi**: the full moon in the month the sun enters sidereal Pisces. Before 16:40 IST, that day; otherwise the next.
- **Equinoxes, solstices and full moons**: from `ephem`, turned into a local evening. A full moon that peaks before noon belongs to the night before.

Then I checked the output against published dates: 7 Diwalis, 7 Holis, 8 Lunar New Years and 5 Hanukkahs between 2022 and 2033. All 27 matched. Diwali and Holi follow Indian dates, which some calendars put a day apart in a given year, so a one-day disagreement doesn't mean the table is wrong.

![A year calendar for 2027 with one cell per evening and holidays only. Holiday nights are filled with bands of that night's palette, ordinary nights are dark and run the default rainbow, and orange dots mark dates that move every year. October and June are full, December runs a rotating Christmas lead-up. A legend lists the computed 2027 dates: Lunar New Year Feb 6, Holi Mar 22, Easter Mar 28, Mother's Day May 9, Memorial Day May 31, Father's Day Jun 20, Diwali Oct 29, Thanksgiving Nov 25, Hanukkah begins Dec 24, equinoxes Mar 20 and Sep 22, solstices Jun 21 and Dec 21, and 12 full moons.](https://willcurran.com/hs-fs/hubfs/will-curran-brand/holiday-color-engine-calendar-strip.png?width=1440&height=1248&name=holiday-color-engine-calendar-strip.png)

The calendar is a priority list, and the first match wins. That decides real collisions. In 2027 Hanukkah begins on Christmas Eve, and Christmas Eve ranks higher. Any night without a holiday gets a seven-color rainbow.

## The first table ran out in 2030

The first version of the calendar had moving dates only through 2030, with a note to extend it later. That broke my "set and forget" rule, so the same night it went to 2099. Every evening from 2026 to 2105 rendered with no errors and no night without a look. After 2099 the table holidays drop out, and full moons fall back to an average-month estimate.

## The parts that were not magic

A helper automation restarts the engine after a script reload. It checked "is the engine off?" the moment the reload event fired. But that event fires before the reload actually stops the script, so the check saw a running engine and did nothing, and then the reload stopped it. The fix was to wait five seconds and then check. I re-ran the failing case, and the engine came back five seconds after the reload.

I tested the failing state first. With every bulb held, the engine sent zero light commands. Then the positive case: the bulbs read back the exact first color of the look at full brightness.

Some things couldn't be tested on build night: the 10 pm swap to a late look, the New Year's midnight burst, the sunrise exit, and a single held bulb fading back into the look. I wrote them down as untested rather than call them done.

## If you are about to try this

- **Address bulbs one by one.** A hub group can't do per-bulb offsets, and you can't skip one bulb inside a group.
- **Give overrides a hold flag the engine reads.** Clear it a few seconds before whatever restarts the engine.
- **Compute astronomical dates offline and paste them in as tables.** Test them against published dates first.
- **Test the failing state.** An all-held run that writes nothing proves more than a pretty one.

## Build it yourself

What it does: runs moving color palettes on outdoor bulbs, picks the palette from a holiday calendar that is computed decades ahead, and gives way to motion lighting. The parts:

- **Color bulbs** Home Assistant can control one at a time. Mine are Hue; any color bulb with transitions works.
- **Motion sensors** for the warm-white override.
- **Home Assistant**: one script (the loop), a template sensor for tonight's look, a hold flag per sensor, and a few automations to start and restart it.
- **Python** with `ephem`, `convertdate` and `lunardate` to generate the moving-date tables.

How the pieces fit: the sensor names tonight's look, the script loops through its palette per bulb, motion sets a hold the loop respects, and your sunset and sunrise routines start and end the night. I used bulbs and sensors I already had; it took one evening.

Give this to your AIHelp me build a holiday color engine in Home Assistant for \[number\] color bulbs that can each be controlled on their own. Write a script that loops through a palette: every step, send each bulb its own color, offset by its position so the color moves along the row, with a fade or a quick chase depending on the look. The script must skip any bulb a motion override is holding, after the first frame, never turn on a bulb that is off, and stop when every bulb is off. Never target a hub group. Make the calendar a pure Jinja macro that returns tonight's look, with a priority order where the first match wins and a default look for ordinary nights. Compute fixed dates, US floating holidays and Easter in the macro. For \[list moving holidays\], write a Python script with ephem, convertdate and lunardate that generates tables through \[end year\], and test them against published dates before deploying. Before calling it done, test the failing state: with every bulb held, the script should send nothing.

If you have a moving holiday you want on your own lights and you're not sure how to compute it, I would like to hear about it.

Filed under [Home](https://willcurran.com/blog/tag/home)

![](https://willcurran.com/hs-fs/hubfs/will-curran-brand/will-curran-avatar.jpg?width=64&height=64&name=will-curran-avatar.jpg)

Will Curran

I spent sixteen years building one company, and I have been in the event industry since 2007. These days I write about the technology I actually use — trucks, radios, home infrastructure, and the tools that survive past the first invoice.

[All posts by Will →](https://willcurran.com/blog/author/will-curran)

Get the next one by email

One email per post. No digest, no sponsors.

[← Previous How I Pick the Best Live Sets for a Playlist](https://willcurran.com/blog/best-live-sets-playlist) [Next → Movie Mode Means the Robot Vacuum Waits](https://willcurran.com/blog/movie-mode-vacuum)

## Related reading

[Home The Heat Rating That Decides a Desert Garage Mini-Split 4 min read](https://willcurran.com/blog/desert-garage-mini-split-heat-rating) [Home Movie Mode Means the Robot Vacuum Waits 6 min read](https://willcurran.com/blog/movie-mode-vacuum) [Home Party Mode: 49 Ramps Blocked, Zero During a Six-Hour Party 6 min read](https://willcurran.com/blog/party-mode)

[Will Curran](https://willcurran.com/)

[LinkedIn — 29,339+ followers](https://www.linkedin.com/in/willcurran) [GitHub](https://github.com/rancur) [YouTube](https://www.youtube.com/channel/UCJgscc27l4fpkWx1SeK05xg/) [Spotify](https://open.spotify.com/user/rancur) [SoundCloud DJ mixes](https://www.soundcloud.com/thewillsee) [Twitch](https://www.twitch.tv/thewillsee) [Last.fm](https://www.last.fm/user/default) [Trakt — movies & TV](https://trakt.tv/users/willcurran) [Goodreads](https://www.goodreads.com/user/show/55302838-will-curran) [Will See Music](https://www.thewillsee.com) [Arizona Meshtastic](https://azmsh.net) [Electric Forest Meshtastic](https://efmesh.com)

© 2026 Will Curran — All rights reserved. [RSS](https://willcurran.com/blog/rss.xml) [Back to top](https://willcurran.com/blog/holiday-color-engine#main)

```json
{
  "@context" : "https://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Will Curran",
    "url" : "https://willcurran.com/blog/author/will-curran"
  },
  "dateModified" : "2026-10-22T17:40:11.691Z",
  "datePublished" : "2026-10-16T16:00:00.000Z",
  "headline" : "A Holiday Color Engine with a Calendar Computed to 2099",
  "image" : [ "https://willcurran.com/hubfs/will-curran-brand/holiday-color-engine-cover-nolane.png" ],
  "mainEntityOfPage" : {
    "@id" : "https://willcurran.com/blog/holiday-color-engine",
    "@type" : "WebPage"
  },
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "url" : "https://willcurran.com/hubfs/image-1.png"
    },
    "name" : "Will Curran LLC"
  }
}
```