Lights on at Dusk - is this the best/simplest way?

Confirmed…Just tried @pnbruckner’s sun component, and it was exactly what was called for.

1 Like

Hi everyone.

Like others have said, I also wanted to have a light turn on at dusk rather than sunset.
Currently for me the difference is about 28 minutes between the two attributes of sun.sun being next_setting and next_dusk.

I did get some inspiration from the trigger that @will presented but I did not want the 180 second offset he used and I wanted it to occur at the exact moment that the next_dusk attribute of the sun.sun entity reports.

After removing the 180 second offset, i discovered the trigger never occurs because of the following statement at
https://www.home-assistant.io/docs/configuration/templating/#time

  • Using now() will cause templates to be refreshed at the start of every new minute.

I understood that to mean if I used a value lower than 60 seconds in the offset, now() will never get a chance to be greater than or equal to next_dusk.

Due to this discovery, I had to find another way to have my light turn on exactly at dusk.
Here is what I found worked for me.

trigger:
  - platform: state
    entity_id: sun.sun
    attribute: next_dusk

This simple trigger does exactly what I want.

When the value for now() catches up to the next_dusk value for today, the next_dusk attribute will immediately roll over to the next day.

This trigger just waits for that attribute to change and then my light turns on.

As I am rather new to Home Assistant, I only recently found the developer tools to be handy for troubleshooting. This might be handy for newbies.

States Tab

image

as well as the template tab

Hope this helps anyone else.


If anyone still wants to use the offset, then I would suggest using this updated code. Apparantly using states.sun.sun.attributes.next_dusk is a frowned upon even though it appears to do the job.

Avoid using states.sensor.temperature.state , instead use states('sensor.temperature') . It is strongly advised to use the states() , is_state() , state_attr() and is_state_attr() as much as possible, to avoid errors and error message when the entity isn’t ready yet (e.g., during Home Assistant startup).

Source: https://www.home-assistant.io/docs/configuration/templating/#home-assistant-template-extensions

Updated code

trigger:
  - platform: template
    value_template: >-
      {{ ((as_timestamp(state_attr('sun.sun', 'next_dusk')) | int ) - 180 ) <
      (as_timestamp(utcnow()) | int ) }}

2 Likes

This is slightly off topic but I have also added a notify action to show me when next dusk will occur when the trigger in my automation occurs

alias: Light on dusk
description: ''
trigger:
  - platform: state
    entity_id: sun.sun
    attribute: next_dusk
condition: []
action:
  - type: turn_on
    device_id: xxx
    entity_id: xxx
    domain: light
  - service: notify.notify
    data_template:
      message: >
        Dusk tomorrow will be at {{ as_timestamp(state_attr("sun.sun",
        "next_dusk")) | timestamp_custom('%I:%M:%S %p') }}
mode: single

This will turn the light on at dusk as well as show the time for dusk tomorrow when the time for dusk today is reached

Thought I’d share my take on this classic automation of turning on the porch lights. This was my first ever automation, and my version of it has evolved over the past four years. Here are my latest set of requirements:

  • Be ‘on’ between 30 minutes before sunset and 30 minutes after sunrise
  • EXCEPT if it’s after 23:30h or before 07:00h (so ‘off’ in this case)
  • EXCEPT if (so, ‘on’ in these cases):
    • An exterior door is open
    • It is within 30 minutes of an exterior door being closed
    • it is within 30 minutes of somebody arriving home

So, first, I have a binary sensor that will be ‘on’ if these conditions are met, and ‘off’ otherwise. I found it frustrating that sun.sun always shows the next sunrise and not the last sunrise. I approximated by just forcing today’s date onto the setting and rising times; I’m not sure if I’ll run into a “1-hour-off” bug during daylight savings time changes.

template:
  - binary_sensor:
    - name: outside_lights_on
      state: >
        {%- set current_time = states('sensor.date_time') | as_timestamp -%}
        {%- set today_date = states('sensor.date') -%}
        {%- set today_sunset = (today_date + 'T' + state_attr('sun.sun', 'next_setting').split('T')[1]) | as_timestamp - (60 * 30) -%}
        {%- set today_sunrise = (today_date + 'T' + state_attr('sun.sun', 'next_rising').split('T')[1]) | as_timestamp + (60 * 30) -%}
        {%- set morning_time = (today_date + "T07:00:00") | as_timestamp -%}
        {%- set evening_time = (today_date + "T23:30:00") | as_timestamp -%}
        {%- set exterior_doors = ['binary_sensor.frontdoor', 'binary_sensor.backdoor'] -%}

        {# returns a JSON array of entities that have a state in the *states_to_match* array #}
        {%- macro filter_by_state (entities, states_to_match) -%}
          {{ "[" }}
          {%- for e in entities if (states(e) in states_to_match) -%}
            "{{ e }}"{{ "," if (not loop.last) else "" }}
          {%- endfor -%}
          {{ "]" }}
        {% endmacro %}

        {# returns a JSON array of entities whose states have changed within *delta* time of *ct* (current_time) #}
        {%- macro changed_in_last (entities, ct, delta) -%}
          {{ "[" }}
          {%- for e in entities if ((ct - (states[e].last_changed | as_timestamp)) < delta.total_seconds()) -%}
            "{{ e }}"{{ "," if (not loop.last) else "" }}
          {%- endfor -%}
          {{ "]" }}
        {% endmacro %}

        {%- if (current_time > today_sunset) or (current_time < today_sunrise) -%}
          {%- if (current_time >= evening_time) or (current_time <= morning_time) -%}
            {%- set people_home = states.person | selectattr('state', 'eq', 'home') | map(attribute='entity_id') | list -%}
            {%- set people_home_in_range = changed_in_last (people_home, current_time, timedelta(minutes=30)) | from_json -%}
            {%- set doors_open = filter_by_state(exterior_doors, ['on']) | from_json -%}
            {%- set doors_closed = filter_by_state(exterior_doors, ['off']) | from_json -%}
            {%- set doors_closed_in_range = changed_in_last (doors_closed, current_time, timedelta(minutes=30)) | from_json -%}

            {%- if (people_home_in_range | count > 0) -%}     true
            {%- elif (doors_open | count > 0) -%}             true
            {%- elif (doors_closed_in_range | count > 0) -%}  true
            {%- else -%}                                      false
            {%- endif -%}
          {%- else -%}
            true
          {%- endif -%}
        {%- else -%}
          false 
        {%- endif -%}

Next, I have an automation to set the state of the outside lights to whatever the sensor says they should be. Ignore the condition for now, I’ll get back to that.

automation:
  - alias: outside_lights_on_off
    mode: queued
    max: 10
    max_exceeded: silent
    initial_state: 'on'
    trigger:
      - platform: state
        entity_id: binary_sensor.outside_lights_on
    action:
      - condition: template
        value_template: "{{ not is_state('timer.porchlights', 'active') }}"
      - service: "light.turn_{{ states('binary_sensor.outside_lights_on') }}"
        data:
          entity_id:
            - light.front_porch_lights
            - light.garage_outside_light
            - light.deck_light

After trying this out for a night, I realized that there was a problem: if somebody manually changed the state of any of those lights, it would never come back in-line with the rest until the binary sensor changed state again. This is where that timer comes in. I introduced it along with two other automations to allow people to manually turn the lights on or off outside of their scheduled time ranges. When the lights are manually switched, start a 20 minute timer, after which another automation will set the lights back to the state the binary sensor says they should be:

timer:
  porchlights:
    duration: '00:20:00'

automation:
  - alias: outside_lights_manually_switched
    mode: restart
    trigger:
      - platform: state
        entity_id:
          - light.front_porch_lights
          - light.garage_outside_light
          - light.deck_light
    condition:
      condition: template
      value_template: >
        {# Be careful not to trigger this automation when the lights are turned on or off via the actual schedule. #}
        {%- set two_seconds_ago = (now() - timedelta(seconds=2)) | as_timestamp -%}
        {%- set last_automation_run = state_attr('automation.outside_lights_on_off', 'last_triggered') | as_timestamp -%}
        {{ last_automation_run < two_seconds_ago }}
    action:
      - service: timer.cancel
        target:
          entity_id: timer.porchlights
      - service: timer.start
        target:
          entity_id: timer.porchlights
        data:
          duration: "00:20:00"

  - alias: outside_lights_timer_finished
    trigger:
      - platform: event
        event_type: timer.finished
        event_data:
          entity_id: timer.porchlights
    action:
      - service: automation.trigger
        target:
          entity_id: automation.outside_lights_on_off

I’m pretty happy with this version; it covers all the cases I could think of.

Hey Will,

Did you fix the turn on lights at dusk? I would really like to be able to automate that.

Thanks!

1 Like

I’ve fixed it.

Here’s how I did it


alias: Front Stairs on dusk
description: ''
trigger:
  - platform: state
    entity_id: sun.sun
    attribute: next_dusk
condition: []
action:
  - type: turn_on
    device_id: 9b90ebd0a6d1fe1021f792e438d913c3
    entity_id: light.front_stairs
    domain: light
mode: single

Alright,

Actually I tried that today. The lights went on just about 2 hours later than dusk. Do you have any ideas?

Have you set your house coordinates correctly in home assistant / configuration / general?

Also check the Timezone just below that setting on the same page … and possibly elevation might play a part in the calculation too

Yes,

Checked everything there. All set correctly…

This is what it said today:
next_dusk: ‘2021-09-10T18:19:56.880598+00:00’

Light automation triggered 8:22:54 PM

What do you see when you go here in dev tools?

Ok, Those times are in utc, to convert the time stamp, instead try pasting this in dev tools / template



Next Dusk will be at
{{ as_timestamp(state_attr("sun.sun","next_dusk")) | timestamp_custom('%I:%M:%S %p') }}

Wow,

That’s interesting. Got this when pasted into dev tools:

I’ve updated the code.

Try


Next setting will be at
{{ as_timestamp(state_attr("sun.sun","next_setting")) | timestamp_custom('%I:%M:%S %p') }}

Next Dusk will be at
{{ as_timestamp(state_attr("sun.sun","next_dusk")) | timestamp_custom('%I:%M:%S %p') }}

trigger:
  - platform: state
    entity_id: sun.sun
    attribute: next_dusk

The trigger you have will trigger when the ‘next_dusk’ attribute changes, not at the time the attribute refers to. You need to look at a time trigger or a template trigger.

I disagree. This trigger works perfectly for me everyday.

I guess it would change when the current ‘next_dusk’ time passes. Fair enough :slight_smile:

1 Like

Thats what I figured.

Then I should wait and see what happens tomorrow?

Well, is the time of 8:19pm what you expected for dusk or not? I don’t live where you live so I don’t know what is normal where you live.

Did you try the updated template that shows setting and dusk times?

I have double checked now.

Dusk where I live is at 20:19.

However. This is what the su.sun attributes says: next_dusk: ‘2021-09-10T18:19:56.880598+00:00’