This is an automated archive made by the Lemmit Bot.
The original was posted on /r/homeassistant by /u/crua9 on 2026-02-10 15:09:21+00:00.
I figure I might as well share this because I'm sure others will get some use out of this. Something to note is I use my bathroom door sensor to tell if I'm up because I don't have a motion in my room. This can be replaced with pretty much anything you want.
Anyways I based this on science and pretty much all my automations in the description I list the rules and other bits because I have bad memory at times, and if someone else works on it or I might need to mess with it down the road. I can easily see what is going on. I figure I might as well share the rules first so if you can see if you want it.
______
Rules:
- At 2:30 AM this grabs the next alarm that will happen on my phone. It then subtracts 60 minutes.
- If no time is set or out of bounds if it is a school day base on the calendar, then it will default to 6AM. If not then 8am. In bounds is 3:30 AM - 11 AM
- If the event is running, and I go to the bathroom. Assume I am up and end the lock/event.
- If the event has ran, don't run it again that day.
- If the event has ran, lock other events which might overwrite the lights for this room.
- If my protection toggle is on (my bedroom motion) then don't trigger this event. (sick or whatever and I need my rest)
The lighting events are as follows:
- Ignition (T+0m to T+10m): Ramps to deep Red/Brown (RGB 68, 28, 0) at 25% brightness.
- Dawn (T+10m to T+20m): Ramps to warm Salmon/Orange (RGB 160, 85, 68) at 51% brightness.
- Rise (T+20m to T+30m): Switches to Kelvin; ramps to Neutral White (4239K) at 75% brightness.
- Peak (T+30m to T+40m): Ramps to Cool Daylight (6500K) at 100% brightness.
- Soak (T+40m+): Maintains max brightness for 30 minutes (plus an optional 20m post-alarm buffer).
Scientific Basis:
- The Curve (Weber-Fechner Law): Uses a segmented ramp to counter the eye's logarithmic perception of brightness. By stepping intensity alongside color temperature, it creates a "perceptually linear" dawn rather than a sudden glare, mimicking the changing angle of solar elevation.
- Start (Melatonin Sparing via Rayleigh Scattering): Begins with long-wavelength Red/Amber (>600nm). This mimics the atmospheric scattering of early dawn. Crucially, this wavelength is invisible to Melanopsin (the protein in your eye that tracks time), allowing you to drift out of deep sleep without prematurely crushing melatonin, preventing "sleep inertia" (grogginess).
- End (Cortisol Awakening Response): Ramps to blue-enriched 6500K (simulating zenith daylight). This targets the ipRGC receptors (peak sensitivity ~480nm) to signal the Suprachiasmatic Nucleus. This hard-stop suppresses melatonin and triggers the natural Cortisol Awakening Response (CAR) required for alertness and circadian alignment.
_______
alias: My Bedroom Sunrise
description: >
60-min lead: Deep Red start, Gamma ramp to 6500K, 20-min pre-alarm soak, plus
post-alarm soak.
Rules:
1. At 2:30 AM this grabs the next alarm that will happen on my phone. It then
subtracts 60 minutes.
2. If no time is set or out of bounds if it is a school day base on the
calendar, then it will default to 6AM. If not then 8am. In bounds is 3:30 AM -
11 AM
3. If the event is running, and I go to the bathroom. Assume I am up and end
the lock/event.
4. If the event has ran, don't run it again that day.
5. If the event has ran, lock other events which might overwrite the lights
for this room.
6. If my protection toggle is on (my bedroom motion) then don't trigger this
event. (sick or whatever and I need my rest)
The lighting events are as follows:
1. Ignition (T+0m to T+10m): Ramps to deep Red/Brown (RGB 68, 28, 0) at 25%
brightness.
2. Dawn (T+10m to T+20m): Ramps to warm Salmon/Orange (RGB 160, 85, 68) at 51%
brightness.
3. Rise (T+20m to T+30m): Switches to Kelvin; ramps to Neutral White (4239K)
at 75% brightness.
4. Peak (T+30m to T+40m): Ramps to Cool Daylight (6500K) at 100% brightness.
5. Soak (T+40m+): Maintains max brightness for 30 minutes (plus an optional
20m post-alarm buffer).
6. If I haven't waked by that point or I'm not home then the lights turn off.
Scientific Basis:
1. The Curve (Weber-Fechner Law): Uses a segmented ramp to counter the eye's
logarithmic perception of brightness. By stepping intensity alongside color
temperature, it creates a "perceptually linear" dawn rather than a sudden
glare, mimicking the changing angle of solar elevation.
2. Start (Melatonin Sparing via Rayleigh Scattering): Begins with
long-wavelength Red/Amber (>600nm). This mimics the atmospheric scattering of
early dawn. Crucially, this wavelength is invisible to Melanopsin (the protein
in your eye that tracks time), allowing you to drift out of deep sleep without
prematurely crushing melatonin, preventing "sleep inertia" (grogginess).
3. End (Cortisol Awakening Response): Ramps to blue-enriched 6500K (simulating
zenith daylight). This targets the ipRGC receptors (peak sensitivity ~480nm)
to signal the Suprachiasmatic Nucleus. This hard-stop suppresses melatonin and
triggers the natural Cortisol Awakening Response (CAR) required for alertness
and circadian alignment.
triggers:
- trigger: time
at: "02:30:00"
id: calculate
- trigger: template
id: start_sunrise
value_template: >-
{{ now().strftime('%H:%M') ==
states('input_datetime.sunrise_start_time')[:5] }}
- trigger: state
entity_id:
- binary_sensor.bathroom_door_sensor
id: bathroom_trip
to:
- "off"
- trigger: time
at: "10:00:00"
id: reset
conditions:
- condition: state
entity_id: input_boolean.smoke_or_co_detected
state: "off"
- condition: time
after: "02:00:00"
before: "11:00:00"
actions:
- alias: Main logic
choose:
- conditions:
- condition: trigger
id: calculate
- condition: state
entity_id: input_boolean.my_bedroom_motion
state: "off"
sequence:
- action: input_datetime.set_datetime
target:
entity_id: input_datetime.sunrise_start_time
data:
time: >-
{% set alarm = states('sensor.pixel_10_pro_next_alarm') %}
{% set t = as_datetime(alarm) %}
{# 1. Check if alarm is valid, is set for TODAY, and is between
5am-8am #}
{% if alarm not in ['unavailable', 'unknown', 'none'] and t is
not none and t.date() == now().date() and 5 <= t.hour < 8 %}
{% set wake = t %}
{# 2. Fallback: School Days (6am) #}
{% elif is_state('calendar.school_days', 'on') %}
{% set wake = today_at("06:00") %}
{# 3. Fallback: Weekends (8am) #}
{% else %}
{% set wake = today_at("08:00") %}
{% endif %}
{# 4. Subtract 80 minutes and output #}
{{ (wake - timedelta(minutes=60)).strftime('%H:%M:%S') }}
alias: 2:30 AM get the alarm data
- conditions:
- condition: trigger
id: start_sunrise
- condition: state
entity_id: input_boolean.my_bedroom_motion
state: "off"
- condition: state
entity_id: input_boolean.sunrise_has_run
state: "off"
sequence:
- action: input_boolean.turn_on
target:
entity_id:
- input_boolean.sunrise_has_run
- input_boolean.sunrise_active_lock
- action: light.turn_off
metadata: {}
target:
entity_id: light.my_bedroom
data: {}
- delay:
hours: 0
minutes: 0
seconds: 2
milliseconds: 0
- action: light.turn_on
target:
entity_id: light.my_bedroom
data:
brightness: 65
rgb_color:
- 68
- 28
- 0
transition: 600
- delay: "00:10:00"
- action: light.turn_on
target:
entity_id: light.my_bedroom
data:
brightness: 130
rgb_color:
- 160
- 85
- 68
transition: 600
- delay: "00:10:00"
- action: light.turn_on
target:
entity_id: light.my_bedroom
data:
brightness: 190
transition: 600
color_temp_kelvin: 4239
- delay: "00:10:00"
- action: light.turn_on
target:
entity_id: light.my_bedroom
data:
brightness: 255
transition: 600
color_temp_kelvin: 6500
- delay: "00:30:00"
- if:
- condition: state
entity_id: input_boolean.sunrise_active_lock
state: "on"
then:
- delay:
hours: 0
minutes: 20
seconds: 0
milliseconds: 0
- action: light.turn_off
target:
entity_id: light.my_bedroom
- action: input_boolean.turn_off
target:
...
***
Content cut off. Read original on https://old.reddit.com/r/homeassistant/comments/1r139se/i_made_an_artificial_sunrise_that_uses_science_to/