Time of Day with dual input

Hi all

I’m trying to get together a state machine that will update based on time of day AND sunset/sunrise.
The idea being this will account for changes in when I actually want lights etc to come on in the evenings/mornings, but also allow for dimmer lights in the middle of the night.

00:00:00 → Sunrise -01:00:00 == Night
Sunrise -01:00:00 → Sunrise +02:00:00 == Morning
Sunrise +02:00:00 → Sunset -01:00:00 == Daytime
Sunset -01:00:00 → 22:00:00 == Evening
22:00:00 → 00:00:00 == Bedtime

Have tried a few templates, and some of my own coding and not had anything that works.
Seems it should be straightforward but I can’t figure out how to do it!

The hope is a single channel that can then be used in automations to mask light behaviour throughout the day, as well as controlling speaker volumes etc.

Any ideas? Cheers!

Copy-paste this into the Template Editor and confirm it reports what you want.

{% set sunrise = state_attr('sun.sun', 'next_rising')|as_datetime|as_local %}
{% set sunset = state_attr('sun.sun', 'next_setting')|as_datetime|as_local %}

{% set pre_sunrise = (sunrise - timedelta(hours=1)).time() %}
{% set post_sunrise = (sunrise + timedelta(hours=2)).time() %}
{% set pre_sunset = (sunset - timedelta(hours=1)).time() %}
{% set post_sunset = (sunset + timedelta(hours=2)).time() %}

{% set bedtime = today_at('22:00').time() %}
{% set midnight = today_at().time() %}
{% set t = now().time() %}

{% if midnight <= t < pre_sunrise %} Night
{% elif pre_sunrise <= t < post_sunrise %} Morning
{% elif post_sunrise <= t < pre_sunset %} Daytime
{% elif pre_sunset <= t < bedtime %} Evening
{% else %} Bedtime
{% endif %}

The template can be used in a Template Sensor.

template:
  - sensor:
      - name: Time of Day
        state: >
          {% set sunrise = state_attr('sun.sun', 'next_rising')|as_datetime|as_local %}
          {% set sunset = state_attr('sun.sun', 'next_setting')|as_datetime|as_local %}
          {% set pre_sunrise = (sunrise - timedelta(hours=1)).time() %}
          {% set post_sunrise = (sunrise + timedelta(hours=2)).time() %}
          {% set pre_sunset = (sunset - timedelta(hours=1)).time() %}
          {% set post_sunset = (sunset + timedelta(hours=2)).time() %}
          {% set t = now().time() %}
          {% if today_at().time() <= t < pre_sunrise %} Night
          {% elif pre_sunrise <= t < post_sunrise %} Morning
          {% elif post_sunrise <= t < pre_sunset %} Daytime
          {% elif pre_sunset <= t < today_at('22:00').time() %} Evening
          {% else %} Bedtime
          {% endif %}
2 Likes

Have you looked at the built-in Times of Day sensor? If you set up the TOD binary sensors, you could then use a triggered template sensor to give your ordinal value based on which one is on.

binary_sensor:
  - platform: tod
    name: Night
    before: sunrise
    before_offset: "-01:00"
    after: "00:00"
  
  - platform: tod
    name: Morning
    before: sunrise
    before_offset: "+02:00"
    after: sunrise
    after_offset: "-1:00"

  - platform: tod
    name: Daytime
    before: sunset
    before_offset: "-1:00"
    after: sunrise
    after_offset: "+02:00"

  - platform: tod
    name: Evening
    after: sunset
    after_offset: "-1:00"
    before: "22:00"

  - platform: tod
    name: Bedtime
    after: "22:00"
    before: "00:00"

group:
  times_of_day:
    name: Times of Day
    entities:
      - binary_sensor.night
      - binary_sensor.morning
      - binary_sensor.daytime
      - binary_sensor.evening
      - binary_sensor.bedtime

template:
  - trigger:
      - platform: state
        entity_id:
          - binary_sensor.night
          - binary_sensor.morning
          - binary_sensor.daytime
          - binary_sensor.evening
          - binary_sensor.bedtime
        to: 'on'
        from: 'off'
      - platform: homeassistant
        event: start
    sensor:
      - unique_id: timeofday
        name: "Time of Day"
        state: >
          {{ expand('group.times_of_day')|selectattr('state', 'eq', 'on')|map(attribute='name')|list|first}}
        availability: "{{ is_state('group.times_of_day', 'on') }}"
1 Like