LumaFlow automatically adjusts your smart lights to follow natural circadian rhythms, and can switch them on before sunset so the house is lit by the time it's dark. It takes sunrise and sunset from your Home Assistant location, keeps lights bright and cool through the day, and dims and warms them through the evening. That supports your natural sleep-wake cycle without you touching a thing.
1.0.0 is a rewrite. Earlier versions didn't set up on current Home Assistant releases, and put lights on full daylight after midnight. If you tried LumaFlow before and it didn't work, this version is worth another go. Existing setups carry over. See the changelog.
| Mode | What it does |
|---|---|
| Follow the circadian rhythm | Lights that are on stay bright and cool through the day, then dim and warm through the evening. You can also have them turn on before sunset and fade up to the curve. |
| Fade on to a set level before sunset | From an hour (adjustable) before sunset, lights that are off switch on at 1% and brighten to a set level, 50% white by default, by sunset. After that LumaFlow leaves them alone: no colour changes, no dimming. |
- Real-time calculations: sunrise and sunset come from your Home Assistant location, recalculated every day
- Seasonal adaptation: follows the changing length of the day through the year
- Works worldwide: when the sun doesn't rise or set at all (polar day or night), LumaFlow holds the day or night values
- Configurable timing: a sunset offset from -120 to +120 minutes
- Selective control: only lights that are already on are adjusted, and LumaFlow never turns a light off
- Fade on before sunset (optional): lights that are off switch on at 1% and brighten so they're fully on by sunset. This happens once per evening per light, and one you turn off stays off
- Immediate adaptation: when a light is turned on, it adopts the current values within about a second
- Universal compatibility: colour temperature lights, RGB lights (warm white is mixed from RGB) and brightness-only lights
- Brand agnostic: anything Home Assistant can dim works, including Hue, LIFX, Shelly, WLED, ESPHome, Tuya and Zigbee bulbs
- Light groups: each light in a group is adjusted on its own, within that light's colour temperature range
- Manual override detection: change a light yourself, from a switch, an app or a scene, and LumaFlow leaves that light alone
- Easy restore:
lumaflow.restore_lightsputs it straight back on the curve - Fresh start: switching a light off and on again also puts it back on the curve
- Daily reset: any overrides left over clear at midnight
- Per-light: an override only affects the light that was changed
- Three-step setup in the Home Assistant UI: lights, timing, advanced
- Customisable ranges: brightness (1β100%) and colour temperature (2000β6500 K)
- Adjust both, or only one: let LumaFlow handle just the colour temperature, or just the brightness
- Transition control: slow (5 min), moderate (3 min) or fast (1 min) fades for each adjustment
- Change anything later in the integration's options
- Native entities: a switch and two sensors, for dashboards and automations
- Services for automations: enable, disable, restore and override
- Status: the current phase, today's sunrise and sunset, the target values and which lights are overridden
LumaFlow works out a target every minute. Rather than jumping between steps, the values move continuously, so changes are smooth.
sunrise β1 hββΆ day ββββββββββββΆ sunset+offset β1 hββΆ evening β3 hββΆ night βββββββΆ next sunrise
ramp up brightest, coolest winding down begins dimming dimmest, warmest
-
Sunrise (sunrise to +1 hour)
- Brightness and colour temperature rise smoothly from the night values to the day values
- Purpose: a gentle start instead of a sudden jump to full daylight
-
Day (+1 hour after sunrise to sunset + offset)
- Brightness: the maximum configured level (default 100%)
- Colour temperature: the coolest setting (default 6500 K)
- Purpose: supports alertness and productivity during daylight hours
-
Sunset (sunset + offset to +1 hour)
- Brightness: the gradual decrease begins
- Colour temperature: starts warming from cool towards neutral
- Purpose: signals the beginning of the evening wind-down
-
Evening (+1 to +4 hours after sunset + offset)
- Brightness: decreases linearly to the minimum level
- Colour temperature: progressively warms to amber tones
- Purpose: prepares the body for sleep by reducing blue light
-
Night (+4 hours after sunset + offset until sunrise)
- Brightness: the minimum configured level (default 1%)
- Colour temperature: the warmest setting (default 2700 K)
- Purpose: minimal lighting that doesn't disrupt sleep
From sunset + offset, brightness and colour temperature fall in a straight line over the four hours to the night values. With a late summer sunset, that wind-down carries on past midnight.
Switch it on with the Fade on to a set level mode, or with Also turn lights on before sunset in the circadian mode.
- The fade starts at sunset + offset β fade-in length (60 minutes by default) and ends at sunset + offset.
- Lights that are off when it starts switch on at 1%, then brighten a little every minute, so they're
fully on at sunset:
- in set-level mode, up to the brightness you choose (default 50%), in white or in each light's own colour
- in circadian mode, up to the curve's value, and from then on they follow the curve as usual
- Lights that are already on aren't touched by the fade.
- Once per evening: turn a light off during or after the fade and it stays off. Adjust one mid-fade and LumaFlow stops fading it, and leaves it alone like any other light changed by hand.
- With "What to adjust" set to one half, the fade still brightens the lights it switches on, because switching a light on gradually needs a brightness. With colour temperature only, their brightness is yours again once the fade ends; with brightness only, the fade doesn't set a colour.
- Switching LumaFlow off during a fade ends that evening's fade.
- In set-level mode, LumaFlow does nothing else: lights you switch on at other times are left as they are.
- Selective control: only lights that are on are adjusted (apart from the optional fade-in)
- Instant adaptation: a light that's turned on takes the current values straight away
- Respects manual control: lights you turn off stay off, and lights you adjust stay as you set them
- Daily override reset: manual adjustments clear at midnight
- Small steps are skipped: a light only gets a command when its target moves by at least 1% or 25 K, so it isn't flooded
LumaFlow follows the principles used in circadian lighting research:
- Blue light regulation: gradually reduces blue light in the evening, to support natural melatonin production
- Colour temperature progression: mimics the natural change from daylight (6500 K) to firelight (2700 K)
- Brightness dimming: follows the natural fall in light intensity after sunset
- Timing precision: uses astronomical calculations rather than fixed schedules
- Better sleep: less blue light in the evening supports natural sleep-wake cycles
- Better alertness: bright, cool light during the productive hours of the day
- Less eye strain: gentle transitions instead of harsh changes
- Mood support: consistent light patterns can help regulate mood and energy
- Customisable intensity: set the brightness and colour ranges to suit your eyes
- Gradual transitions: no sudden changes
- Manual override: adjust a light whenever you need to, and LumaFlow respects it
- Automatic reset: back to healthy patterns every day without you doing anything
- Add this repository to HACS as a custom repository (category: Integration)
- Install "LumaFlow" through HACS
- Restart Home Assistant
- Go to Settings β Devices & services
- Click Add integration and search for "LumaFlow"
- Download the latest release
- Copy the
custom_components/lumaflowfolder into your Home Assistantcustom_componentsfolder - Restart Home Assistant
- Add the integration through the UI
-
Choose lights and a mode: the lights (or light groups) LumaFlow should handle, and whether they follow the circadian rhythm or fade on to a set level before sunset. Any dimmable light can be chosen. Lights that can only switch on and off are ignored.
-
Timing and ranges:
- Sunset offset: start winding down before (negative) or after sunset, from -120 to +120 minutes
- Transition speed: how long each adjustment fades over (slow 5 min, moderate 3 min, fast 1 min)
- Night and day brightness: from 1 to 100%
- Night and day colour temperature: from 2000 to 6500 K. Each light is kept within its own range.
- What to adjust (circadian mode): brightness and colour temperature, colour temperature only, or brightness only. With colour temperature only, the curve leaves a light's brightness alone, and dimming one by hand doesn't count as an override. With brightness only, the colour is left to you. The curve skips lights that can't do the chosen half. A fade-in still brightens the lights it switches on (see below).
- Fade-in length: how long before sunset the lights start coming on (5β180 minutes, default 60)
- Also turn lights on before sunset (circadian mode), or Brightness at sunset and Colour (set-level mode: default 50%, white)
-
Advanced:
- Leave a light alone after it's changed by hand (override detection)
- Put lights back on the curve when Home Assistant starts (restore on startup). If this is off, lights that are on at startup are treated as overridden until they're switched off and on, or midnight.
Everything can be changed later under Settings β Devices & services β LumaFlow β Configure. The integration reloads to apply the changes.
- Enable/disable: use the
switch.lumaflowentity. Turning it off leaves the lights exactly as they are. The setting survives restarts. - Status:
sensor.lumaflow_current_phaseandsensor.lumaflow_next_transition - Manual override: adjust a light yourself and LumaFlow leaves it alone
Turn circadian lighting on, and put every light that's on straight onto the curve.
Stop adjusting lights. The settings are kept.
Clear manual overrides and put lights straight back on the curve.
action: lumaflow.restore_lights
data:
lights: # Optional - leave out to restore every LumaFlow light
- light.living_room
- light.kitchenSet lights to your own values. LumaFlow leaves them alone until they're restored, switched off and on again, or midnight passes.
action: lumaflow.override_lights
data:
lights:
- light.living_room
brightness: 50 # Optional, percent
color_temp: 3000 # Optional, Kelvin
rgb_color: [255, 200, 100] # OptionalThe names below are for an entry called "LumaFlow". A second entry gets its own name.
- Switch
switch.lumaflow: enable or disable. Its attributes give the mode, what the curve adjusts (adjusts:both,color_temporbrightness), the controlled lights, which are on, which are overridden, which are fading on (fading_lights), and today's fade-in window. - Sensor
sensor.lumaflow_current_phase:sunrise,day,sunset,eveningornight. Its attributes include the target brightness and colour temperature, today's sunrise and sunset, and the overridden lights. - Sensor
sensor.lumaflow_next_transition: when the next phase starts, with thenext_phaseattribute.
Some setups have an automation that puts bulbs back to a default (say 50% white) whenever they're
switched on. During the fade-in, that would jump the light straight to its default. The switch's
fading_lights attribute lists the lights LumaFlow is fading on, so the automation can skip them:
conditions:
- condition: template
value_template: "{{ trigger.entity_id not in (state_attr('switch.lumaflow', 'fading_lights') or []) }}"LumaFlow updates fading_lights before it sends the command that switches the light on.
automation:
- alias: "Enable LumaFlow with Motion"
triggers:
- trigger: state
entity_id: binary_sensor.living_room_motion
to: "on"
conditions:
- condition: state
entity_id: switch.lumaflow
state: "off"
actions:
- action: lumaflow.enableautomation:
- alias: "Disable LumaFlow for Movies"
triggers:
- trigger: state
entity_id: media_player.tv
to: "playing"
actions:
- action: lumaflow.disable
- alias: "Re-enable LumaFlow After Movies"
triggers:
- trigger: state
entity_id: media_player.tv
from: "playing"
actions:
- action: lumaflow.enable| Light Type | Support Level | What LumaFlow sets |
|---|---|---|
| Colour temperature | Full | Colour temperature (within the bulb's range) and brightness |
| RGB / HS / XY | Full | A warm-to-cool white mixed from RGB, and brightness |
| Brightness only | Brightness | Brightness only |
| On/off only | None | Ignored |
| Light groups | Full | Each member individually |
- Home Assistant: 2025.1 or newer
- Dependencies: none to install. Sun times come from Home Assistant's own sun calculations.
- Update frequency: every minute, plus immediately when a light turns on
- Transitions: only sent to lights that support them
- Restart Home Assistant after installing or updating
- Check that the folder is
custom_components/lumaflow, withmanifest.jsondirectly inside it - Check Settings β System β Logs for lines mentioning
lumaflow
- Check
switch.lumaflowis on - Check the light is on. LumaFlow never turns lights on.
- Look at
overridden_lightson the switch. If the light is listed, restore it or switch it off and on. - Check the light can be dimmed. On/off-only lights are ignored.
- Look in the logs for "LumaFlow couldn't adjust"
Something other than LumaFlow is changing it: another automation, an adaptive lighting integration, or a "turn on at this colour" rule. Remove that light from the other automation, or turn off override detection in the options.
- Check the location and time zone under Settings β System β General
- Look at the
sunriseandsunsetattributes onsensor.lumaflow_current_phase
switch.lumaflow: controlled lights, lights on, overridden lightssensor.lumaflow_current_phase: phase, target values, sunrise, sunset, offsetsensor.lumaflow_next_transition: the next phase and when it starts
- Check the logs: Settings β System β Logs
- Check the entities: Developer tools β States, search for "lumaflow"
- Open an issue: GitHub Issues
Contributions are welcome. See CONTRIBUTING.md.
python3 -m venv .venv && .venv/bin/pip install -r requirements_test.txt
.venv/bin/pytest -q- Lovelace card for at-a-glance control
- Per-light ranges, so a bedside lamp can be dimmer than the kitchen
- Scene integration
- Presence and activity triggers
- Time-limited overrides
- Choose what to adjust. In circadian mode LumaFlow can now set brightness and colour temperature, colour temperature only, or brightness only. The half it isn't adjusting is yours: changing it by hand isn't treated as an override. (#3)
- Switching LumaFlow off during a fade-in ends that evening's fade, and the lights no longer stay listed
in
fading_lights. - Fixed
lumaflow.restore_lightsre-sending to every light when none of the named lights belonged to that entry. - A colour bulb that reports its colour dimmed along with its brightness is no longer mistaken for one changed by hand.
- Fixed loading while Home Assistant starts. The "Home Assistant has started" hook ran outside the event loop, so the startup refresh was skipped with a warning, and reloading the entry afterwards logged "Unable to remove unknown job listener".
- Fade on before sunset. Lights that are off come on at 1% before sunset and brighten so they're fully on by sunset. The start is set by the fade-in length, 60 minutes by default.
- A new mode: Fade on to a set level. The fade-in ends at a set brightness (50% by default) in white or each light's own colour, and LumaFlow otherwise leaves the lights alone. The circadian mode can use the fade-in too, ending on the curve's values.
- The switch publishes
fading_lights, so "reset the bulb on power-on" automations can skip a light while it's fading on. - The options flow asks for the mode first, then shows that mode's settings.
- Rewritten from the ground up to do what this README describes. Existing entries keep their settings.
- Fixed: setup failed on Home Assistant 2026.1 and newer (
ATTR_COLOR_TEMPwas removed), and the light platform crashed during setup on older versions. (#1, #2) - Fixed: lights went to full brightness and daylight after midnight. The night now stays at the night values until sunrise.
- Fixed: the wrong day's sun times were used for most of the morning outside UTC.
- New: a one-hour sunrise ramp from the night values to the day values.
- The services work. Enable, disable, restore and override really control the lights now.
- Override detection compares each light with what LumaFlow last set it to, so the bulb reporting back and rounding don't count as manual changes.
- The options cover every setting, including the lights, brightness and colour ranges.
- Light groups are expanded to their members, and each light is kept within its own colour temperature range.
- The wrapper light entity and per-light switches from 0.2β0.3 are removed automatically.
- No extra Python packages to install.
- Automated tests, plus hassfest and HACS validation in CI.
- The first releases. They didn't work reliably and are replaced by 1.0.0.
This project is licensed under the MIT License.
- The Home Assistant community, for the platform and its developer tools
- Everyone who reported issues, especially the two tracebacks that pinned down why setup failed