Simple light wake-up alarm with parabolic sunrise effect

UPDATE 2024-09-24

This appears to get getting some attention so I’ve made it easier to install using a script blueprint as well

Installation

  1. Create datetime helper for the time this is to be triggered (Target finish time - time for all steps)
  2. Import the script blueprint
  3. Create a script from the script blueprint (Only needs to be done once. Can be reused in many automations)
  4. Import the automation blueprint
  5. Create the automation from the automation blueprint and use the script and datetime helper created above in the automation

UPDATE 2024-09-13

New version in the post below

Original Post Below

[Removed Rant]
Basically it contains 2 components. The first component is a script that can be executed many times in series to change the rates of the 2 main settings, mireds and brightness. By running it many times in series you get get a more parabolic effect. I added this feature because I could visually detect changes in brightness more at lower values. Below is an example of this. You can see the points of which the values change.

The second component is an automation that executes the script one or more times in series with different values for each execution. It is here that you can add your own pre and post execution steps if desired.

Detailed Explaination of Script

The script expects to be kicked off with parameters defined. If the target light or light group is off, all values of the parameters will be used. If the target is already on, its current brightness and mired values will be used as the starting values. It has been designed this way so the script can create the more parabolic like curve you can see above.

If you turn off the light at any point while the script is running, the script will end. However, you have the script chained in the automation to achieve the parabolic curve, you will need to check that the light is off before executing the script again or it will just start up again with the values defined in the script instantiation.

The script also allows for defining how long to take to get from start to finish values and how many steps per minute to take to get there. The script will also turn off the light after the ‘Light Timeout’ period. Setting this value to 0 will disable the timeout, and this is how you can chain executions of the script together to get the desired parabolic curve effect.

alias: Lamp Wake Up
sequence:
  - service: light.turn_on
    data:
      brightness: "{{ min_brightness }}"
      color_temp: "{{ max_mireds }}"
    target:
      entity_id: "{{ target_light }}"
  - repeat:
      until:
        - condition: or
          conditions:
            - condition: template
              value_template: "{{ is_state(target_light, 'off') }}"
            - condition: template
              value_template: "{{ state_attr(target_light, 'brightness') >= max_brightness }}"
            - condition: template
              value_template: "{{ state_attr(target_light, 'color_temp') <= min_mireds }}"
            - condition: template
              value_template: >-
                {{ (((as_timestamp(now()) - start_time) / individual_step) |
                round(0, "ceil")) > steps }}
      sequence:
        - variables:
            steps_to_now: |-
              {{ ((as_timestamp(now()) - start_time) / individual_step) |
                round(0, "ceil") }}
            brightness: >-
              {{ min_brightness + (bright_step * steps_to_now) | round(0,
              'ceil') }}
            mireds: "{{ max_mireds - (mireds_step * steps_to_now) }}"
        - delay:
            seconds: "{{ individual_step }}"
        - if:
            - condition: template
              value_template: "{{ is_state(target_light, 'on') }}"
          then:
            - service: light.turn_on
              data:
                brightness: "{{ brightness }}"
                color_temp: "{{ mireds }}"
                transition: "{{ individual_step - 1 }}"
              target:
                entity_id: "{{ target_light }}"
  - if:
      - condition: and
        conditions:
          - condition: template
            value_template: "{{ light_timeout != 0 }}"
          - condition: template
            value_template: "{{ is_state(target_light, 'on') }}"
    then:
      - delay:
          minutes: "{{ light_timeout }}"
      - service: light.turn_off
        data: {}
        target:
          entity_id: "{{ target_light }}"
description: Turn on lamps brighter based on wake time
fields:
  min_mireds:
    description: Minimum mireds value. This is the end value - most white
    selector:
      color_temp: null
    required: true
    default: 200
    name: Min Mireds
    example: 200
  max_mireds_selector:
    description: >-
      Maximum mireds value. This is the start value. If the light is on the
      current value from the state of the light will be used and this will be
      ignored.
    example: 400
    selector:
      color_temp: null
    default: 400
    required: true
    name: Max Mireds
  max_brightness_pct:
    description: Maximum brightness in percent.
    example: 80
    selector:
      number:
        min: 1
        max: 100
    default: 80
    required: true
    name: Max brightness
  alarm_length:
    description: >-
      This is the start to finish time. Take this into account when setting up
      the automation this script is called by.
    example: 10
    selector:
      number:
        min: 1
        max: 60
    default: 10
    required: true
    name: Alarm Length
  steps_per_minute:
    description: How many steps per minute
    example: 4
    selector:
      number:
        min: 1
        max: 12
    default: 12
    name: Steps Per minute
    required: true
  target_light:
    description: A single light or group
    example: light.master_lamp
    selector:
      entity:
        filter:
          domain: light
    name: Target Light
    required: true
  light_timeout:
    description: >-
      Minutes to delay after Max Brightness has been reached to turn the light
      back off. Value of 0 disables the timeout
    example: 5
    selector:
      number:
        min: 0
        max: 60
    default: 5
    name: Light Timeout
    required: true
variables:
  steps: "{{ alarm_length * steps_per_minute }}"
  min_brightness: |-
    {% if state_attr(target_light, 'brightness') == None %}
      1
    {% else %}
      {{ state_attr(target_light, 'brightness') }}
    {% endif %}
  max_brightness: "{{ max_brightness_pct * 2.55 }}"
  max_mireds: |-
    {% if state_attr(target_light, 'color_temp') == None %}
      {{ max_mireds_selector }}
    {% else %}
      {{ state_attr(target_light, 'color_temp') }}
    {% endif %}
  mireds_step: "{{ (max_mireds - min_mireds) / steps }}"
  bright_step: "{{ (max_brightness - min_brightness) / steps }}"
  start_time: "{{ as_timestamp(now()) }}"
  individual_step: "{{ 60 / steps_per_minute }}"
mode: parallel

Detailed Explaination of Automation

This CAN be replaced with the Blueprint created below

The automation is much simpler. Execute the script as many times in series as you’d like to get the desired parabolic curve. All executions of the script (except for the last one) will need to have the ‘Light Timeout’ set to 0 so that the script will continue from current values. You will also need to check to see if the light is off (for all executions but the first one) so that if you turn the light off, the subsequent executions of the script in the currently running automation will not turn it back on.

When setting up start time for the trigger you will need to take into account when you want it to finish. In my example below I want it to finish by 7:00 am, so I do the math backwards and start it at 6:35. The total time is solved by adding up all the ‘Alarm Length’ fields of all executions of the script.

In my automation I’m using the Workday Integration and its binary sensor to only execute on days I have configured in that integration.

alias: Light Alarm
description: ""
trigger:
  - platform: time
    at: "06:35:00"
condition:
  - condition: state
    entity_id: binary_sensor.workday_sensor
    state: "on"
action:
  - service: script.1702692125821
    data:
      min_mireds: 375
      max_mireds_selector: 400
      max_brightness_pct: 10
      alarm_length: 10
      steps_per_minute: 12
      light_timeout: 0
      target_light: light.light_alarm_lights
  - if:
      - condition: state
        entity_id: light.light_alarm_lights
        state: "on"
    then:
      - service: script.1702692125821
        data:
          min_mireds: 325
          max_mireds_selector: 400
          max_brightness_pct: 50
          alarm_length: 10
          steps_per_minute: 12
          light_timeout: 0
          target_light: light.light_alarm_lights
  - if:
      - condition: state
        entity_id: light.light_alarm_lights
        state: "on"
    then:
      - service: script.1702692125821
        data:
          min_mireds: 250
          max_mireds_selector: 400
          max_brightness_pct: 100
          alarm_length: 5
          steps_per_minute: 12
          light_timeout: 25
          target_light: light.light_alarm_lights
mode: parallel
max: 10

Installation

  1. Copy and paste the script as is into a new script on your system when editing in YAML mode.
  2. Copy and paste the automation into a new automation when editing in YAML mode. You will need to update the ‘service’ value to your script which you will be able to more easily find after you save and edit in GUI mode. You can also add remove executions of the script to change the curve and customize the parameters.
11 Likes

Works perfectly, thank you very much

1 Like

Just tested here. Works like a charm! I’m using Philips Hue Lights running on zigbee2mqtt.

1 Like

Thanks for this script!
I was using the other popular one but I’m willing to give this one a try since it is supposed to be more optimized.

Yet, I’m still relatively new to Home Assistant and I really don’t understand anything yaml based.

Basically, instead of a fixed hour, I would like to use alarms set in my Google Home device I integrated in HA. That device’s next alarm is shown in entity: sensor.lenovo_smart_clock_alarms

So, as the trigger, I would like it to be : sensor.lenovo_smart_clock_alarms - alarm_length variable

I searched forums and tried what is below but it does not work.

trigger:
  - platform: template
    value_template: "{{ now() >= states('sensor.lenovo_smart_clock_alarms') | as_datetime - timedelta(minutes=alarm_length) }}"

How can I reference alarm_length in the yaml automation code so that it works?
Thank you

BTW: there is a typo in your thread title. That could hinder the possibility of finding this thread later on.

This would be great, but it errors on the check

state_attr(target_light, 'color_temp') <= min_mireds

My lights aren’t reporting the color_temp - it’s null, so it hits an error. Does anyone have a brilliant idea for converting the value to HS in-line like this? I have RGB, HS, and xy color. No kelvin or mired.

Maybe I should just buy the hue hub… I’ve spent the whole weekend trying to get a solution that works; the complicated blueprint referenced only checks for one variable for conditions and spams the logs, I can’t stand it.

They also don’t support transition, which is the easy way to do all this.

I have white only bulbs and replacing the condition

            - condition: template
              value_template: "{{ state_attr(target_light, 'color_temp') <= min_mireds }}"

with

            - condition: and
              conditions:
                - condition: template
                  value_template: "{{ state_attr(target_light, 'color_temp') != None }}"
                - condition: template
                  value_template: "{{ state_attr(target_light, 'color_temp') <= min_mireds }}"

did the trick for me.

2 Likes

Are you still folowing this thread ?

I’ve create a blueprint for this automation. The script in the first post will still need to be copied to your instance manually first, then specified when you create the automation from the blueprint below.

The blueprint is basically the same as the automation in the first post but configured via automation GUI.

It assumes you’re using the Workday Integration to determine whether it will be triggered on a particular day. You could use any binary_sensor to replace the Workday binary_sensor if you do not wish to use the Workday Integration.

You’ll also need an input_datetime helper to use to trigger the start time. Create one with time only.

Blueprint on Github for import

blueprint:
  name: Parabolic Alarm Automation
  description: Turn a light on based on detected motion
  domain: automation
  input:
    alarm_start_time:
      name: Start Time
      description: Datetime helper for alarm to start. Use time only and Workday sensor to determine what days to run.
      selector:
        entity:
          filter:
            - domain: input_datetime
    workday_sensor:
      name: Workday Sensor
      description: Binary Sensor for determining it it should run. Typically  from Workday Integratoin
      selector:
        entity:
          filter:
            - domain: binary_sensor
    alarm_script:
      name: Script to trigger
      description: Script to trigger
      selector:
        entity:
          filter:
            - domain: script
    target_light:
      name: Lights
      description: The light(s) with Mireds
      selector:
        entity:
          filter:
            - domain: light
    light_timeout:
      name: Timeout
      description: Light will turn off after this time on last run
      default: 10
      selector:
        number:
          min: 1
          max: 60
    steps_per_minute:
      name: Steps per minute for all runs
      description: Used for configuring percentage of each step for brightness and color temperature
      default: 12
      selector:
        number:
          min: 1
          max: 60

    min_mireds_1:
      description: Minimum mireds value (coldest) for 1st run
      selector:
        color_temp:
      default: 375
      name: Min Mireds 1
    max_mireds_1:
      description: >-
        Maximum mireds value (warmest) for 1st run
      selector:
        color_temp:
      default: 400
      name: Max Mireds 1
    max_brightness_1:
      name: Maximum Brightness 1
      selector:
        number:
          min: 1
          max: 100
      default: 10
    alarm_length_1:
      name: Alarm Length 1
      description: >-
        This is the start to finish time for the first run
      selector:
        number:
          min: 1
          max: 60
      default: 10

    min_mireds_2:
      description: Minimum mireds value (coldest) for 1st run
      selector:
        color_temp:
      default: 300
      name: Min Mireds 1
    max_mireds_2:
      description: >-
        Maximum mireds value (warmest) for 1st run
      selector:
        color_temp:
      default: 400
      name: Max Mireds 1
    max_brightness_2:
      name: Maximum Brightness 1
      selector:
        number:
          min: 1
          max: 100
      default: 50
    alarm_length_2:
      name: Alarm length 2
      description: >-
        This is the start to finish time for the second run
      selector:
        number:
          min: 1
          max: 60
      default: 10

    min_mireds_3:
      description: Minimum mireds value (coldest) for 1st run
      selector:
        color_temp:
      default: 160
      name: Min Mireds 1
    max_mireds_3:
      description: >-
        Maximum mireds value (warmest) for 1st run
      selector:
        color_temp:
      default: 400
      name: Max Mireds 1
    max_brightness_3:
      name: Maximum Brightness 1
      selector:
        number:
          min: 1
          max: 100
      default: 100
    alarm_length_3:
      name: Alarm Lenght 3
      description: >-
        This is the start to finish time for the third run
      selector:
        number:
          min: 1
          max: 60
      default: 5

trigger:
  - platform: time
    at: !input alarm_start_time

condition:
  - condition: state
    entity_id: !input workday_sensor
    state: "on"

action:
  - service: !input alarm_script
    data:
      min_mireds: !input min_mireds_1
      max_mireds_selector: !input max_mireds_1
      max_brightness_pct: !input max_brightness_1
      alarm_length: !input alarm_length_1
      steps_per_minute: !input steps_per_minute
      light_timeout: 0
      target_light: !input target_light
  - if:
      - condition: state
        state: "on"
        entity_id: !input target_light
    then:
      - service: !input alarm_script
        data:
          min_mireds: !input min_mireds_2
          max_mireds_selector: !input max_mireds_2
          max_brightness_pct: !input max_brightness_2
          alarm_length: !input alarm_length_2
          steps_per_minute: !input steps_per_minute
          light_timeout: 0
          target_light: !input target_light
  - if:
      - condition: state
        state: "on"
        entity_id: !input target_light
    then:
      - service: !input alarm_script
        data:
          min_mireds: !input min_mireds_3
          max_mireds_selector: !input max_mireds_3
          max_brightness_pct: !input max_brightness_3
          alarm_length: !input alarm_length_3
          steps_per_minute: !input steps_per_minute
          light_timeout: !input light_timeout
          target_light: !input target_light
mode: parallel
max: 10

First post here and I don’t really have the cred to post a video but I don’t think this is behaving as intended and whilst a video would be the best way to demonstrate this I’ll pate what I put into Claude to see if it could help but it couldn’t really.

The light flicks on and pretty quickly (i.e. within a second or 2) transitions / gets to a high brightness then resets to a low brightness and transitions / gets up to some other target brightness and then resets to dimmer yet then transitions / gets up to some other brightness that was different to before and then just loops like this continuously in this triad of low to max with different lows and different maxes.

Not sure what I did wrong but I was hoping for a gradual slope from 0% up to my target brightness over the course of several minutes not < 5 seconds. Would love to know what I’ve done wrong. For reference, I’m using an Ikea zigbee RGB light

What bulb are you using? Does it support mireds?

Any change you can modify that to gather the “next alarm” info from a sensor?

I don’t always wake up at the same time so I just set my Alexa every night. I know it shares this information with HA so it’s possible to do so, I just don’t have the technical skill to do it.

Check out the first post and link to the blueprint. That one will link to your phone alarm.

What blueprint are you referring to? The original one that inspired your work or your own blueprint?

I’m in same position than @Dumonster, as I would like that the alarm time (and thus also day) is whatever is set in Google Assistant and that is read by Home Assistant with Google Home integration.

yes, the original post

So for some reason this stopped working between when I disabled the automation created by the blueprint at the end of the school year and now. I’ve used the opportunity to refactor both the blueprint and the script so if upgrading you will need to update the script as well.

I’ve switch to using Kelvin as the color temperature and removed some redundant inputs.

Both the script and the blueprint can be found in the github repo.

1 Like

Thanks for the perfect timing, I’ve been wanting to try this for a while and finally made time for it today. I’ve followed your instructions and will hopefully wake up to the ‘sunrise’ tomorrow with my first ever HA automation.

Thanks for giving us an alternative to the well known and highly used “Wake-up light alarm with sunrise effect”. I’ve had some issues with that blueprint and decided to use yours. So far so good ! This morning it worked like a charm. :slight_smile:

Thanks !

This seems to be getting some attention so I updated it to make installation easier. Both the script and automation are now blueprints. See the original post for details.

I liked the script with the fields better :slight_smile:
And do multiple lights still work?

I get this error: TypeError: unhashable type: ‘list’

I didn’t consider multiple lights as an input. However, you can probably create a light group and use that in the script/automation to control multiple lights.