Sun event related timers

I did not find this usecase before. It took me a while to figure it out, so I would like to share my project/idea.
I wanted to swith my lights on and off based on the sunset, but i wanted to be able to change the offset when this happens online via the GUI. So this is what I came up with:

And the config:

input_slider:
  auto_light_minutes:
    name: Minutes after sunset
    icon: mdi:timer
    initial: 30
    min: 0
    max: 120
    step: 10
  auto_light_duration:
    name: Duration hours
    icon: mdi:timer
    initial: 4
    min: 0
    max: 8
    step: 0.5

sensor:
  - platform: template
    sensors:
      auto_light_start:
        friendly_name: 'Lights on at'
        value_template: '{{ (as_timestamp(states.sun.sun.attributes.next_setting) + (states("input_slider.auto_light_minutes") | int)  * 60)  | timestamp_custom("%H:%M") }}'
      auto_light_stop:
        friendly_name: 'Lights out at'
        value_template: '{{ ( as_timestamp(states.sun.sun.attributes.next_setting) + (states("input_slider.auto_light_minutes") | int)  * 60 + (states("input_slider.auto_light_duration") | float) * 60 * 60  )  | timestamp_custom("%H:%M") }}'
      auto_light_minutes:
        friendly_name: 'Lights on '
        value_template: '{{ "%d minutes after sunset" | format(states("input_slider.auto_light_minutes") | int) }}'
      auto_light_duration:
        friendly_name: 'Lights on for'
        value_template: '{{ "%0.1f hours" | format(states("input_slider.auto_light_duration") | float) }}'
  - platform: time_date
    display_options:
      - 'time'

automation:
  - alias: "auto_light_on enabled"
    trigger:
      platform: state
      entity_id: input_slider.auto_light_minutes, input_slider.auto_light_duration
    action:
    - service: homeassistant.turn_on
      data:
        entity_id:
          - automation.auto_light_on
    - service: homeassistant.turn_on
      data:
        entity_id:
          - automation.auto_light_off
  - alias: 'auto_light_on'
    initial_state: True
    trigger:
      - platform: time
        minutes: '/1'
        seconds: 10
    condition:
      - condition: template
        value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%H:%M")) == states.sensor.auto_light_start.state }}'
    action:
      - service: homeassistant.turn_on
        entity_id: "group.lights"
  - alias: 'auto_light_off'
    initial_state: True
    trigger:
      - platform: time
        minutes: '/1'
        seconds: 10
    condition:
      - condition: template
        value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%H:%M")) == states.sensor.auto_light_stop.state }}'
    action:
      - service: homeassistant.turn_off
        entity_id: "group.lights"
28 Likes

This is frickin’ awesome! Thank you for sharing.

1 Like

Great,thanks for sharing your skills.

I am definitely switching over to this for my outside lights. Nice work, thanks for sharing it!

One small update to make the sensors more efficient by specifying what triggers and update:

- platform: template
  sensors:
      auto_light_start:
        friendly_name: 'Lights on at'
        value_template: '{{ (as_timestamp(states.sun.sun.attributes.next_setting) + (states("input_slider.auto_light_minutes") | int)  * 60)  | timestamp_custom("%H:%M") }}'
        entity_id:
          - sun.sun
          - input_slider.auto_light_minutes

      auto_light_stop:
        friendly_name: 'Lights out at'
        value_template: '{{ ( as_timestamp(states.sun.sun.attributes.next_setting) + (states("input_slider.auto_light_minutes") | int)  * 60 + (states("input_slider.auto_light_duration") | float) * 60 * $
        entity_id:
          - sun.sun
          - input_slider.auto_light_minutes
          - input_slider.auto_light_duration

      auto_light_minutes:
        friendly_name: 'Lights on '
        value_template: '{{ "%d minutes after sunset" | format(states("input_slider.auto_light_minutes") | int) }}'
        entity_id:
          - input_slider.auto_light_minutes

      auto_light_duration:
        friendly_name: 'Lights on for'
        value_template: '{{ "%0.1f hours" | format(states("input_slider.auto_light_duration") | float) }}'
        entity_id:
          - input_slider.auto_light_duration
4 Likes

After upgrading to latest dev (42-DEV) I have problem with “initial_state: True” for the automations.
It seems that this doesn’t have any effect anymore after reboot. Mine are always off.
Anyone else got this?

I did not have a lot of time to check the behaviour in detail, but I had some issues since the “restoring states” was introduced (I think it was in 0.40).
After restart the sliders and checkboxes do reset sometimes to random states for me as well.

Just to let you know, there is a copy/paste error on the value_template line of auto_light_stop, it ends with a $ in the post above but should be 60 ) | timestamp_custom("%H:%M") }} instead as per the original post.

Hey Guys,

I had been using this flawlessly for a while and then had to rebuild my HA instance. I copied all of the files to my computer via WinSCP and put them all back in the correct locations once it was rebuilt. Since then it has not worked at all. I can manually trigger the automations from the frontend but the automation does not work.

Here is the code that i have:

Automation

alias: auto_light_on
initial_state: True
trigger:
  - platform: time
    minutes: '/1'
    seconds: 10
condition:
  - condition: template
    value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%H:%M")) == states.sensor.auto_light_start.state }}'
action:
  - service: homeassistant.turn_on
    entity_id: group.living_room

alias: auto_light_off
initial_state: True
trigger:
  - platform: time
    minutes: '/1'
    seconds: 10
condition:
  - condition: template
    value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%H:%M")) == states.sensor.auto_light_stop.state }}'
action:
  - service: homeassistant.turn_off
    entity_id: group.living_room

alias: auto_light_on_enabled
initial_state: True
trigger:
  platform: state
  entity_id: input_slider.auto_light_minutes, input_slider.auto_light_duration
action:
  - service: homeassistant.turn_on
    data:
      entity_id:
        - automation.auto_light_on
  - service: homeassistant.turn_on
    data:
      entity_id:
        - automation.auto_light_off

Input Sliders

auto_light_minutes:
  name: Minutes before/after sunset
  icon: mdi:timer
  initial: -60
  min: -120
  max: 120
  step: 10
auto_light_duration:
  name: Duration hours
  icon: mdi:timer
  initial: 4
  min: 0
  max: 8
  step: 0.5

Sensor

- platform: template
  sensors:
    auto_light_start:
      friendly_name: 'Lights on at'
      value_template: '{{ (as_timestamp(states.sun.sun.attributes.next_setting) + (states("input_slider.auto_light_minutes") | int)  * 60) | timestamp_custom("%H:%M") }}'
      entity_id:
        - sun.sun
        - input_slider.auto_light_minutes

    auto_light_stop:
      friendly_name: 'Lights out at'
      value_template: '{{ (as_timestamp(states.sun.sun.attributes.next_setting) + (states("input_slider.auto_light_minutes") | int)  * 60 + (states("input_slider.auto_light_duration") | float) * 60 * 60) | timestamp_custom("%H:%M") }}'
      entity_id:
        - sun.sun
        - input_slider.auto_light_minutes
        - input_slider.auto_light_duration

    auto_light_minutes:
      friendly_name: 'Lights on'
      value_template: '{{ "%d minutes after sunset" | format(states("input_slider.auto_light_minutes") | int) }}'
      entity_id:
        - input_slider.auto_light_minutes

    auto_light_duration:
      friendly_name: 'Lights on for'
      value_template: '{{ "%0.1f hours" | format(states("input_slider.auto_light_duration") | float) }}'
      entity_id:
        - input_slider.auto_light_duration

I cannot for the life of me figure out why this isn’t working. Any help would be awesome.

Thanks Guys

@daenny This script is awesome thank you! I’d like to ask advice on how to organise it in my configuration. Is it possible to wrap this whole “Timer Light” group with the input_sliders, sensors & automations in one file?

Whats the general advice to keep my config tidy while adding this new group?

Thanks for your help

You can add it as a package, see the docs. :thumbsup:

Thanks! Thats what i needed, a hint. Strangely enough, I never heard about packages before. Ill give it a good read. Cheers.

Hey All,

Can someone post your working code? I still cant figure out why mine is not working (automations not triggering). I’d like to see where I may have gone wrong. Thanks for the help.

@djkmod83 I have a slightly modified version of the original script.

  1. You can set the on and off time offsets related to sunset and sunrise (the original was using duration for the off time)
  2. If the HASS misses the sunrise or sunset event (for example it was off or being rebooted), it will be triggered if its inside the right timeframe. Example: You set the lights to turn on at sunset -10min. Lets say the HASS was off at that time, and you turn it back on at sunset + 120min. It will trigger the on event for the lights.
  3. The off automation does not trigger if the lights are already off. Same thing with the on automation.
  4. As @anon43302295 suggested, I’ve packed everything into one single package file

There’s a lot of room for improvement, so im open to suggestions!

Good Luck!

*file: packages/ouside_lights.yaml

sensor:
  - platform: template
    sensors:
      auto_light_start:
        friendly_name: 'Lights on at '
        value_template: '{{ (as_timestamp(states.sun.sun.attributes.next_setting) + (states("input_slider.auto_light_offset_on") | int)  * 60)  | timestamp_custom("%H:%M") }}'
      auto_light_stop:
        friendly_name: 'Lights off at '
        value_template: '{{ (as_timestamp(states.sun.sun.attributes.next_rising) + (states("input_slider.auto_light_offset_off") | int)  * 60)  | timestamp_custom("%H:%M") }}'

  - platform: time_date
    display_options:
      - 'time'

input_slider:
  auto_light_offset_on:
    name: Minutes after sunset
    icon: mdi:timer
    initial: 0
    min: -60
    max: 60
    step: 10
  auto_light_offset_off:
    name: Minutes after sunrise
    icon: mdi:timer
    initial: 0
    min: -60
    max: 60
    step: 10


automation:
  - alias: "auto_light_on enabled"
    trigger:
      platform: state
      entity_id: input_slider.auto_light_offset_on, input_slider.auto_light_offset_off
    action:
    - service: homeassistant.turn_on
      data:
        entity_id:
          - automation.auto_light_on
    - service: homeassistant.turn_on
      data:
        entity_id:
          - automation.auto_light_off
          
  - alias: 'auto_light_on'
    initial_state: True
    trigger:
      - platform: time
        minutes: '/1'
        seconds: 10
    condition:
      condition: and
      conditions:
      - condition: template
        # value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%H:%M")) == states.sensor.auto_light_start.state }}'
        value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%H:%M")) > states.sensor.auto_light_start.state }}'
      - condition: template
        value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%H:%M")) < states.sensor.auto_light_stop.state  }}'
      - condition: state
        entity_id: group.automatic_lights_outside
        state: 'off'
    action:
      - service: homeassistant.turn_on
        entity_id: group.automatic_lights_outside

  - alias: 'auto_light_off'
    initial_state: True
    trigger:
      - platform: time
        minutes: '/1'
        seconds: 10
    condition:
      condition: and
      conditions:
      - condition: template
        # value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%H:%M")) == states.sensor.auto_light_stop.state }}'
        value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%H:%M")) > states.sensor.auto_light_stop.state }}'
      - condition: template
        value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%H:%M")) < states.sensor.auto_light_start.state }}'
      - condition: state
        entity_id: group.automatic_lights_outside
        state: 'on'
    action:
      - service: homeassistant.turn_off
        entity_id: group.automatic_lights_outside

group:

  Automatic Lights Outside:
    name: Automatic Lights Outside
    entities:
      - switch.outdoor_front_big
      - switch.outdoor_front_small

  Timer Light:
    name: Timer Light
    control: hidden
    entities:
    - automation.auto_light_off
    - automation.auto_light_on
    - input_slider.auto_light_offset_on
    - sensor.auto_light_start
    - input_slider.auto_light_offset_off
    - sensor.auto_light_stop

This is how it looks like:

Looks like a good improvement!

You may want to add entity_id’s to your sensors to specify when they should update. sun.sun and the input_slider.XXXX would do it. If you don’t specify they will update on EVERY STATE change which can become significant.

On a rPI with a lot of sensors it can become a noticeable CPU load; and being specific with the entity_id’s helps cut that down significantly.

I use lux meters for controlling lights, but while reading this thread It hit me that sun elevation might be more useful to use as trigger than time after/before sunset/dawn?

Especially up here north, there will be huge differences of darkness 10 minutes after sunset in summer compared to winter.

This!

This is what prompted me to recently switch to sun angle for automating lights. I also combine that with cloud cover. It’s not super-elegant, but it’s been working so far. In my setup, I handle it as a sensor, then base automations on whether that sensor is true or false. Still dialing in the best numbers, but it’s been good so far. It was interesting yesterday when it suddenly got dark out b/c of a passing storm and the lights came on automatically. Then they turned back off after the storm’d passed.

      turn_on_indoor_lights:
        friendly_name: 'Turn On Indoor Lights'
        value_template: >
          {% if (states.sun.sun.attributes.elevation | int < 30) %}true
          {% elif ( (states.sun.sun.attributes.elevation | int < 40) and (states.sensor.dark_sky_cloud_coverage.state | int > 50)) %}true
          {% elif (states.sensor.dark_sky_cloud_coverage.state | int > 90) %}true
          {% else %}false
          {% endif %}
4 Likes

@jwelter Thanks ill have a look and improve it! Today it failed to turn on the lights, maybe theres a mistake in the “ON” code, since the “OFF” part worked successfully this morning.
BTW, do you know easy ways to debug sensors, triggers and automations?

For example how could i simulate a sunrise to test my automation?

Cheers!

@caspercba I’m using your version of the script but i had to change part of it because it was using UTC time.

I’ve changed (now().strftime("%s") | int | timestamp_custom("%H:%M")) into as_timestamp (now()) | timestamp_custom("%H:%M"). this two scripts have a difference of one hour (at least here in Portugal).

The script runs fine except for one thing. The lights turn off but they don’t turn on! Let me explain:
my lights are configured to turn on 20 minutes before sunset (at 20:07 - sunset is at 20:27 - today) and 20 minutes after sunrise (at 07:14 - sunrise is at 6:54).

If i analize the conditions at 21:00 (when the lights should be on, but thay aren’t…) the first contition returns TRUE, the second return FALSE and the third returns TRUE. Shouldn’t all the conditions return TRUE because 21:00 > 20:07 and 21:00 < 7:14?? Or the system gets mixed up because 7:14 is on the next day?? In fact 21:00 is bigger than 7:14…

Should the script get the date too? Something like {{ as_timestamp (now()) | timestamp_custom("%d/%b/%Y %T") > states.sensor.auto_light_start.state }}

Thanks in advance for your answer. I really need it…

Check out how I handled it: Automatic Dimming Light Timer . In my latest code I convert all time used for calculations into seconds since the last midnight. I found that this is one of the best ways for me to handle time in home assistant.