I made an artificial sunrise that uses science to help wake me up in the morning

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:

  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).

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.

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:
                  entity_id: input_boolean.sunrise_active_lock
            alias: Logic for if I still didn't wake or maybe not home
        alias: Lighting event logic
      - conditions:
          - condition: trigger
            id: bathroom_trip
          - condition: state
            entity_id: input_boolean.sunrise_active_lock
            state: "on"
        sequence:
          - action: input_boolean.turn_off
            target:
              entity_id: input_boolean.sunrise_active_lock
          - action: system_log.write
            data:
              level: info
              message: "Bathroom trip: Post-Wake stabilization complete."
        alias: Event to see if I'm awake
      - conditions:
          - condition: trigger
            id: reset
        sequence:
          - action: input_boolean.turn_off
            target:
              entity_id:
                - input_boolean.sunrise_has_run
                - input_boolean.sunrise_active_lock
        alias: Logic to reset things
mode: restart
1 Like