Change Times of Day before / after values from a Dashboard?

The “Times of the Day” helper / sensor fits my use case really well but I need the ability for a non-admin user to change the before / after values from the dashboard. Is there a way to do this directly or do I have to create independent before / after helpers and write an automation to update the before / after values of the Times of Day helper when those independent ones are updated? Or is something like that even possible?

Note, I did try something direct like this but it seems the ToD integration doesn’t support templates for the after / before properties (or I have the syntax wrong).

- platform: tod
  name: Times of Day Adjustable
  after: "{{ states('input_datetime.dowtime_start') }}"
  before: "{{ states('input_datetime.downtime_end') }}"

The Times of Day sensor integration does not support templating and it can’t be modified by an automation. To have front-end control like you are describing, you can set up template binary sensors.

  - trigger:
    - platform: state
      entity_id:
        - input_datetime.morning_start
        - input_datetime.day_start
        - input_datetime.night_start
      not_to:
        - unknown
        - unavailable
    - platform: time
      at: 
        - "00:00:00" 
        - input_datetime.morning_start
        - input_datetime.day_start
        - input_datetime.night_start
    - platform: homeassistant
      event: start
    - platform: event
      event_type: event_template_reloaded
    binary_sensor:
    - name: Morning
      state: >
        {{ today_at( states('input_datetime.morning_start'))
        <= now() < today_at( states('input_datetime.day_start')) }}
    - name: Day
      state: >
        {{ today_at( states('input_datetime.day_start'))
        <= now() < today_at( states('input_datetime.night_start')) }}
    - name: Night
      state: >
        {{ today_at(states('input_datetime.night_start')) < now() < today_at("23:59:59")
        or today_at() < now() < today_at( states('input_datetime.morning_start')) }}

Other options:

Schedule Integration: This is a core integration, but AFAIK there isn’t a simple way to modify the schedule such as a dedicated card.

Daily Schedule custom integration and card

EDIT: Correct typos and template issue

Interesting. I started writing a similar template sensor but had issues with timeframes that spanned midnight. Will your example handle timeframes that span midnight? For example if I wanted to have a single sensor called “Downtime” that would be “on” if the time was between 10 pm to 7 am and the current time was 11 pm or 2 am would it be “on”?

Not at home right now thus can play around with it myself at the moment and can’t quite wrap my head around the Night logic to decide if it would work :smiley:

Yes, it should span midnight.

Yes, the included triggers insure that it should work reliably in most circumstances. There are other triggers that could be added to cover some edge cases, or the triggers could be removed altogether and the templates will be rendered once every minute.

Having trouble getting this to work with a simple implementation. Maybe I’m misunderstanding something. The template looks good in the template dev panel but the sensor gives a value of Unknown

template:
  - trigger:
      - platform: time
        at:
          - "00:00:00"
          - input_datetime.child1_downtime_weekend_start
          - input_datetime.child1_downtime_weekend_end
      - platform: homeassistant
        event: start
      - platform: event
        event_type: event_template_reloaded
    binary_sensor:
      - name: Child1 Downtime
        state: >
          {{ today_at(states('input_datetime.child1_downtime_weekend_start')) < now() < today_at("24:00")
          or today_at() <= now() < today_at( states('input_datetime.child1_downtime_weekend_end') ) }}

If you’re getting unknown as the state with a trigger-based template sensor, that usually means none of the triggers have fired. Try reloading the template integration from Developer Tools > YAML page.

If you expect to be changing the values of the inputs regularly, it will benefit you to add the input datetime states as triggers as well. I have added them in the example posted above. Also corrected above, it should be today_at("23:59:59"), not today_at("24:00"):man_facepalming:

You didn’t mention how you were defining it ,so the following has “weekend” defined as Friday and Saturday nights.

template:
  - trigger:
      - platform: state
        entity_id:
          - input_datetime.child1_downtime_weekend_start
          - input_datetime.child1_downtime_weekend_end
          - input_datetime.child1_downtime_weekday_start
          - input_datetime.child1_downtime_weekday_end
        not_to:
          - unknown
          - unavailable  
      - platform: time
        at:
          - "00:00:00"
          - input_datetime.child1_downtime_weekend_start
          - input_datetime.child1_downtime_weekend_end
          - input_datetime.child1_downtime_weekday_start
          - input_datetime.child1_downtime_weekday_end
      - platform: homeassistant
        event: start
      - platform: event
        event_type: event_template_reloaded
    binary_sensor:
      - name: Child1 Downtime
        state: >
          {% if now().isoweekday() in [5, 6] %} {% set x = 'weekend' %}
          {% else %} {% set x = 'weekday' %} {% endif %}
          {{ today_at(states('input_datetime.child1_downtime_'~x~'_start'))
          < now() < today_at("23:59:59") or today_at() <= now() 
          < today_at(states('input_datetime.child1_downtime_'~x~'_end')) }}
1 Like

Ah, I see. Didn’t notice that the list of datetime helpers in the original were for the time... at trigger and weren’t triggering on entity change. I was reloading the yaml but it still wasn’t updating.

This whole thing got me thinking though… what’s the reason for using triggers at all. Wouldn’t a normal state based template sensor just automatically update when the

Yes, that’s why I mentioned that option in my second post… because of the use of now(), the template will be rendered every 60 seconds as well as whenever any of the entities update.

A lot of people run HA on relatively low-powered systems, and the renders saved by using trigger-based template sensors when possible and appropriate can add up when you have a lot of templating.

Ah, missed that portion, my apologies. It’s been a long day and I’ve only been able to come back to this periodically between other things.

…the renders saved by using trigger-based template sensors when possible and appropriate can add up when you have a lot of templating.

This came to mind right after posting my question. Makes sense and it’s something I’ll keep an eye out for.

Really appreciate you help and fast responses on top of that. I learned multiple new things from this thread. Thank you!

@Didgeridrew this has been working really great for most cases (with some additional tweaks), thanks again for the help!

However, I’ve run into some edge cases where it doesn’t work and I’m having difficultly adapting it. The main edge case is where we want to extend the start of the downtime until after midnight (say for a sleepover) to something like 01:00:00. Makes sense that this doesn’t work because this scenario wasn’t part of the original use-case and your solution was not designed with that in mind (how could it be!)

I have attempted some tweaks but none have worked so far and I’m not great with date / time logic (especially in HA) and have a lot of brain for lately (side effect of chronic health issues) which is making it even harder.

Wondering if you have suggestions on how to adapt it to handle that use case.

{% if now().isoweekday() in [5, 6] %}
{% set x = 'weekend' %}
{% else %}
{% set x = 'weekday' %}
{% endif %}

{% set start = today_at(states('input_datetime.child1_downtime_'~x~'_start')) %}
{% set end = today_at(states('input_datetime.child1_downtime_'~x~'_end')) %}

{% if start > end %}
{{ start < now() < today_at("23:59:59") or today_at() <= now() < end }}
{% else %}
{{ start <= now() < end }}
{% endif %}

Beautiful! Thank you so much @Didgeridrew !

This is my customized version here and it seems to work as expected. Some of the adjustments I made earlier include:

  • Use school_night / school_day sensors (based on workday integration) to adjust schedule for holidays, non-instructional school days, etc.
  • Logic to choose the correct start / end time during “transition” days. When I was relying only on school_night or school_day certain days would use the incorrect start / end time. For example if relying on school_night and it was a Saturday night the start time would be correct but because the next morning (i.e. after midnight) is Sunday which is a school night it would use the weekday morning end time. Maybe there is a better / simpler way to do this but this seems to work.
  • Use a now_proxy sensor to allow me to set “now” to whatever time I want for debugging. Though it seems template sensors can only return strings thus I’m using as_timestamp on that and all other objects in the logic to get things to the same type for comparison. Maybe there’s a better way?
  • Variablize ‘downtie_category’ so I can use everything below that as a template and easily copy/paste it into multiple sensors. I have one child, guest, and household (aka shared) devices. I’ll probably put this into macro to avoid duplication and copy/paste updates
{% set downtime_category = 'guest' %}

{% if states('input_boolean.'~downtime_category~'_downtime') == "on" %}
  {% if states("binary_sensor.school_night") == "on" %}
    {% set schedule_start_type = 'weekday' %}
    {% set downtime_start = 'input_datetime.'~downtime_category~'_downtime_weekday_start' %}
  {% else %}
    {% set schedule_start_type = 'weekend' %}
    {% set downtime_start = 'input_datetime.'~downtime_category~'_downtime_weekend_start' %} 
  {% endif %}

  {% if states("binary_sensor.school_day") == "on" %}
    {% set schedule_end_type = 'weekday' %}
    {% set downtime_end = 'input_datetime.'~downtime_category~'_downtime_weekday_end' %}
  {% else %}
    {% set schedule_end_type = 'weekend' %}
    {% set downtime_end = 'input_datetime.'~downtime_category~'_downtime_weekend_end' %}
  {% endif %}
   
  {% set downtime_start = today_at(states('input_datetime.'~downtime_category~'_downtime_'~schedule_start_type~'_start')) %}
  {% set downtime_end = today_at(states('input_datetime.'~downtime_category~'_downtime_'~schedule_end_type~'_end')) %}
   
  {% if downtime_start > downtime_end %}
    {{ as_timestamp(downtime_start) < as_timestamp(states('sensor.now_proxy')) < as_timestamp(today_at("23:59:59")) or as_timestamp(states('sensor.now_proxy')) <= as_timestamp(now()) < as_timestamp(downtime_end) }}
  {% else %}
    {{ as_timestamp(downtime_start) <= as_timestamp(states('sensor.now_proxy')) < as_timestamp(downtime_end) }}
  {% endif %}
{% endif %}

Edit: Oops, this was my mid-refactor version with some redundant / unused lines. Will try to paste the final version when back home.