Times of the Day - Dynamic configuration

In Home Assistant, I created a process that reads the water temperature and, based on that, sets a number of hours for the pump to run. It starts a timer with the right duration. Now I want the start time to be set so that the pump finishes 30 minutes after sunrise or no later than 7:00.
I was looking for a way of doing it and I found the Times of the Day that could be nice but I cannot make it dynamic
Times of the Day - Documentation

I’ve checkd the documentation but if I create the helper and set the state as:

It is always off.
While if I create it in the Yaml

binary_sensor:
  - platform: tod
    name: Test1
    after: sunrise
    after_offset: "-10:30"
    before: sunrise

It does work but is not dynamic

how can I do it?

Generally speaking, you cannot overwrite YAML configuration from the frontend… once the TOD sensor is configured, there’s no way to make it any more dynamic that what is built in.

The description of what you are trying to do isn’t very clear to me. If you can post the current automation and describe the goal more clearly, we can probably help you reach it.

I have:

  1. Automation trigger 30 min after sunrise → it reads the temperature
  2. based on temperature it identifies the required duration (from 1h to 10h 30m)

today it switches on the pump and starts a second timer with the defined duration in point 2.

This means that the pump is working during th day

What I want to achieve is:
3) evaluate next sunrise
4) calculate back the correct starting point based on the defined duration in 2)
5) at the correct point in time start the pump and the timer for the correct duration.

In this way the pump will be working during night time.

In addition to that I should check that the point at 4) is calculated so that is not before sunset and after 19:00 in any case

Example:
a) duration 8 hours
sunrise 06:00
sunset 21:00
start point 22:00

b) duration 16 hours
sunrise 06:00
sunset 21:00
start point should be 14:00 but for the 19:00 rule → 19:00

c) duration 10 hours
sunrise 06:00
sunset 21:00
start point should be 20:00 but for the sunset rule → 21:00

Hoping it makes sense :slight_smile:

M

I would use a calendar, just add an action to the automation where you are calculating the duration like:

triggers:
  - trigger: sun.sunrise
    options:
      offset:
        minutes: 30
      offset_type: after
actions:
  - # Whatever you are doing to calculate the time. 
    # You haven't shared how that value is output, 
    # so I'm going to assume it's a positive float indicating hours, stored in a variable named "duration"
  - variables:
      end: "{{ states('sensor.sun_next_rising') }}"
      start: |
        {% set max_start = today_at("22:00") %}
        {% set t_start = end|as_datetime|as_local - timedelta(hours=duration) %}
        {{ (max_start if t_start <= max_start else t_start).isoformat() }}
  - action: calendar.create_event
    target:
      entity_id: calendar.pompa
    data:
      summary: "Pump On"
      start_date_time: "{{ start }}"
      end_date_time: "{{ end }}"

Then you have an automation that turns the pump on when the Calendar event starts and off when it ends.

triggers:
  - trigger: calendar.event_started
    target:
      entity_id: calendar.pompa
    id: "on"
  - trigger: calendar.event_ended
    target:
      entity_id: calendar.pompa
    id: "off"
conditions: []
actions:
  - action: switch.turn_{{trigger.id}}
    target:
      entity_id: switch.pompa_example

Amazing,
I will work on it :slight_smile:

This is how I determine the duration:

     - choose:
          - conditions:
              - condition: numeric_state
                entity_id: sensor.sonoff_10001c5cd2_temperature
                below: 20
            sequence:
              - data:
                  duration: '02:45:00'
                target:
                  entity_id: timer.piscina
                action: timer.start
              - metadata: {}
                target:
                  entity_id: notify.piscinalog
                data:
                  message: >-
                    The temperature is {{
                    states('sensor.sonoff_10001c5cd2_temperature') }} -
                    02:45:00.
                  title: Piscina Pump Schedule - On
                action: notify.send_message
          - conditions:
              - condition: numeric_state
                entity_id: sensor.sonoff_10001c5cd2_temperature
                above: 19.9
                below: '25'
            sequence:
              - data:
                  duration: '06:45:00'
                target:
                  entity_id: timer.piscina
                action: timer.start
              - metadata: {}
                target:
                  entity_id: notify.piscinalog
                data:
                  message: >-
                    The temperature is {{
                    states('sensor.sonoff_10001c5cd2_temperature') }} - 06:45:00
                  title: Piscina Pump Schedule - On
                action: notify.send_message
          - conditions:
              - condition: numeric_state
                entity_id: sensor.sonoff_10001c5cd2_temperature
                above: 24.9
                below: '28'
            sequence:
              - data:
                  duration: '08:45:00'
                target:
                  entity_id: timer.piscina
                action: timer.start
              - metadata: {}
                target:
                  entity_id: notify.piscinalog
                data:
                  message: >-
                    The temperature is {{
                    states('sensor.sonoff_10001c5cd2_temperature') }} - 08:45:00
                  title: Piscina Pump Schedule - On
                action: notify.send_message
          - conditions:
              - condition: numeric_state
                entity_id: sensor.sonoff_10001c5cd2_temperature
                above: 27.9
            sequence:
              - data:
                  duration: '10:45:00'
                target:
                  entity_id: timer.piscina
                action: timer.start
              - metadata: {}
                target:
                  entity_id: notify.piscinalog
                data:
                  message: >-
                    The temperature is {{
                    states('sensor.sonoff_10001c5cd2_temperature') }} - 10:45:00
                  title: Piscina Pump Schedule - On
                action: notify.send_message
        default:
          - metadata: {}
            target:
              entity_id: notify.piscinalog
            data:
              message: >-
                The temperature is {{
                states('sensor.sonoff_10001c5cd2_temperature') }} - Default to
                00:45:00
              title: Piscina Pump Schedule - On
            action: notify.send_message
          - data:
              duration: '00:45:00'
            target:
              entity_id: timer.piscina
            action: timer.start

Are the durations you’ve chosen special, or is it just an attempt at proportionality?

I ask because there is a template function that can map a value proportionally, so if that is the goal the automation could be greatly simplified.

I’ve got a table from some websites on how much to run the pump based on water temperature to avoid algae and issues.
I think I can use a proporitional :slight_smile:

Okay, so the automation to set the calendar value and notification would be something like the following (I’ve guessed at the min and max values for the variables so you will probably need to play with those in the Template editor tool to tune them…)

triggers:
  - trigger: sun.sunrise
    options:
      offset:
        minutes: 30
      offset_type: after
conditions: []
actions:
  - variables:
      temperature: "{{ states('sensor.sonoff_10001c5cd2_temperature')|float }}"
      min_temp: 10
      max_temp: 40 
      min_duration_hours: 1
      max_duration_hours: 10.5
      duration: "{{ remap(temperature, min_temp, max_temp, min_duration_hours, max_duration_hours)|round(1) }}"
      end: "{{ states('sensor.sun_next_rising') }}"
      start: |
        {%- set sunset = states('sensor.sun_next_setting')|as_datetime %}
        {%- set price_start = today_at("19:00") %}
        {%- set calculated_start =  end | as_datetime - timedelta(hours=duration) %}
        {{ max(calculated_start, max(price_start,sunset)).isoformat() }}
  - action: calendar.create_event
    target:
      entity_id: calendar.pompa_example
    data:
      summary: "Pump On"
      start_date_time: "{{ start }}"
      end_date_time: "{{ end }}"
  - metadata: {}
    target:
      entity_id: notify.piscinalog
    data:
      message: >-
        The temperature is {{temperature}} - {{duration}}.
      title: Piscina Pump Schedule Set
    action: notify.send_message

EDIT: Modified to meet OP’s needs as discussed below

Amazing :slight_smile:
only one question:

why is this:

 {% set max_start = today_at("22:00") %}

I think I just misread or misremembered one of your examples… that should be

{% set max_start = today_at("19:00") %}

Ok, but in this case I have to re calculate end…

in the plain case is
end = sensor.sun_next_rising
start = end - duration

but if I force start = 19.00 to avoid issues
then end should be
end = force_start + duration

as it will be after sunrise…

does it makes sense?

My start time should be:

  1. sunrise - duration
  2. but 1. must be after sunset (to have cold air)
  3. and in any case 1. must be after 19.00 in any case (due to incremental energy cost before 19.00)

:slight_smile:

so it could be something:

      end: "{{ states('sensor.sun_next_rising') }}"
      start: |
        {% set max_start = today_at("19:00") %}
        {% set t_start = end|as_datetime|as_local - timedelta(hours=duration) %}
        {{ (max_start if t_start <= max_start else t_start).isoformat() }}
      end_t: "{{ start|as_datetime|as_local + timedelta(hours=duration) }}"

and

      summary: "Pump On"
      start_date_time: "{{ start }}"
      end_date_time: "{{ end_t }}"

Under what circumstances would you be forcing it to start at 19:00?

Is it something you would be doing manually?

Would it always be done at least 15 minutes before 19:00?


I’m asking because there are a few quirks with calendars in HA…

The main issue that comes to mind is that there isn’t currently an option to edit or delete a calendar event programmatically. We could alter the action that creates the calendar to include the duration in the event description… so if you decide to change it you would have the duration to use right there in the edit flow.

If surise is at 04.30 and duration is 10.30 hours the start time should be 18.00… so I must force it to be 19.00 and then terminate not at 4.30 but at 5.30

I would not do it manually, is all in the automation :slight_smile:

What if you define the start like:

      start: |
        {%- set sunset = states('sensor.sun_next_setting')|as_datetime %}
        {%- set price_start = today_at("19:00") %}
        {%- set calculated_start =  end | as_datetime - timedelta(hours=duration) %}
        {{ max(calculated_start, max(price_start,sunset)).isoformat() }}

That should guarantee that it’s always the later of 19:00, the calculated value, and sunset. I’ve updated the automation in the earlier post to show these changes.

I’ve evolved the code to print all variables to debug but the result is not what I do expect:

alias: Test Pumpa Calendar
description: ''
triggers:
  - trigger: timer.finished
    target:
      entity_id: timer.piscina_prerun
    options:
      for: '00:00:00'
conditions:
  - condition: not
    conditions:
      - condition: or
        conditions:
          - condition: state
            entity_id: sensor.sonoff_10001c5cd2_temperature
            state:
              - unavailable
          - condition: state
            entity_id: sensor.sonoff_10001c5cd2_temperature
            state:
              - unknown
actions:
  - variables:
      temperature: '{{ states(''sensor.sonoff_10001c5cd2_temperature'')|float }}'
      min_temp: 20
      max_temp: 28
      min_duration_minutes: 120
      max_duration_minutes: 600
      duration: >-
        {{ remap(temperature, min_temp, max_temp, min_duration_minutes,
        max_duration_minutes,  edges='clamp')|round(1) }}
      end_s: '{{ states(''sensor.sun_next_rising'') }}'
      max_start: '{{ today_at("19:00") }}'
      t_start: '{{ end_s|as_datetime|as_local - timedelta(minutes=duration) }}'
      start: |
        {{ (max_start if t_start <= max_start else t_start)}}
      end: '{{ start|as_datetime|as_local + timedelta(minutes=duration) }}'
  - action: calendar.create_event
    target:
      entity_id: calendar.local
    data:
      summary: Pump On
      start_date_time: '{{ start }}'
      end_date_time: '{{ end }}'
  - metadata: {}
    target:
      entity_id: notify.piscinalog
    data:
      message: >-

        Test Calendar | temperature {{temperature}}°C | duration {{duration}}
        min | start {{start|as_datetime|as_local}} | end
        {{end|as_datetime|as_local}} |DEBUG| end_s
        {{end_s|as_datetime|as_local}} | max_start
        {{max_start|as_datetime|as_local}} | t_start
        {{t_start|as_datetime|as_local}} 
      title: Piscina Pump Schedule Set
    action: notify.send_message
mode: single

This is the debug from today:

2026-08-17T03:38:25.100946+00:00 Test Calendar
temperature 24.3°C
duration 378.0 min
start 2026-08-17 19:00:00+02:00
end 2026-08-18 01:18:00+02:00 	

DEBUG	 

end_s 2026-08-17 06:23:23+02:00 	 
max_start 2026-08-17 19:00:00+02:00 	 
t_start 2026-08-17 00:05:23+02:00

This is not correct:
As the duration is 378 minutes (6 hours and 18 min) and the sunrise time is 2026-08-17 06:23:23+02:00 the correct start time should have been 2026-08-17 00:05:23+02:00 not 19:00

You changed the absolute reference from the end to the start by defining end based on start instead of the other way around…

Not sure I understood.

I look for the best start and then get the end :slight_smile:

The issue is that the start time is 19:00 and not 00:05

M

Compare the following:

If you derive end from start, then end will only be sunrise when the duration is greater than or equal to the time span between start and sunrise.

But, if you derive start from end, then end will always be sunrise and start will only be max_start when the duration is greater than or equal to the time span between max_start and sunrise.

that was not working on all scenarios :slight_smile:
this is the final that looks ok:

actions:
  - variables:
      temperature: '{{ states(''sensor.sonoff_10001c5cd2_temperature'')|float }}'
      min_temp: 20
      max_temp: 28
      min_duration_minutes: 120
      max_duration_minutes: 600
      duration: >-
        {{ remap(temperature, min_temp, max_temp, min_duration_minutes,
        max_duration_minutes,  edges='clamp')|round(1) }}
      sunrise : '{{ states(''sensor.sun_next_rising'') }}'
      price_start: '{{ today_at("19:00") }}'
      calculated_start: '{{ sunrise|as_datetime|as_local - timedelta(minutes=duration) }}'
      start: '{{ max(calculated_start, price_start)|as_datetime|as_local}}'
      end: '{{ start|as_datetime|as_local + timedelta(minutes=duration) }}'
  - action: calendar.create_event
    target:
      entity_id: calendar.local
    data:
      summary: Pump On
      start_date_time: '{{ start|as_datetime|as_local }}'
      end_date_time: '{{ end|as_datetime|as_local }}'
  - metadata: {}
    target:
      entity_id: notify.piscinalog
    data:
      message: >-

        Test Calendar | temperature {{temperature}}°C | duration {{duration}}
        min | start {{start|as_datetime|as_local}} | end
        {{end|as_datetime|as_local}} |DEBUG| sunset
        {{sunset|as_datetime|as_local}} | calculated_start
        {{calculated_start|as_datetime|as_local}} | price_start
        {{price_start|as_datetime|as_local}} | max 1  {{ max(calculated_start,
        price_start)|as_datetime|as_local }}
      title: Piscina Pump Schedule Set
    action: notify.send_message
mode: single

I’ve tested it with different values for temperature (forced) and the result is always good :slight_smile:

2026-08-17T14:47:57.157737+00:00 Test Calendar | temperature 29°C | duration 600.0 min | start 2026-08-17 20:24:37+02:00 | end 2026-08-18 06:24:37+02:00 |DEBUG| sunrise 2026-08-18 06:24:37+02:00 | calculated_start 2026-08-17 20:24:37+02:00 | price_start 2026-08-17 19:00:00+02:00 | max 1  2026-08-17 20:24:37+02:00
2026-08-17T14:53:29.616347+00:00 Test Calendar | temperature 40°C | duration 1000.0 min | start 2026-08-17 19:00:00+02:00 | end 2026-08-18 11:40:00+02:00 |DEBUG| sunrise 2026-08-18 06:24:37+02:00 | calculated_start 2026-08-17 13:44:37+02:00 | price_start 2026-08-17 19:00:00+02:00 | max 1  2026-08-17 19:00:00+02:00
2026-08-17T14:53:57.643355+00:00 Test Calendar | temperature 21°C | duration 230.0 min | start 2026-08-18 02:34:37+02:00 | end 2026-08-18 06:24:37+02:00 |DEBUG| sunrise 2026-08-18 06:24:37+02:00 | calculated_start 2026-08-18 02:34:37+02:00 | price_start 2026-08-17 19:00:00+02:00 | max 1  2026-08-18 02:34:37+02:00

Thank you for your great help!

I’m very very puzzled…

I did all manual test and they worked correctly
BUT
in the real world it does give me the wrong info.

Look at these two lines:

2026-08-19T03:40:52.078501+00:00 Test Calendar | temperature 23.6°C | duration 336.0 min | start 2026-08-19 19:00:00+02:00 | end 2026-08-20 00:36:00+02:00 |DEBUG| sunrise  2026-08-19 06:25:52+02:00 | calculated_start 2026-08-19 00:49:52+02:00 | price_start 2026-08-19 19:00:00+02:00 | max 1  2026-08-19 19:00:00+02:00
2026-08-20T03:42:06.123596+00:00 Test Calendar | temperature 25.3°C | duration 438.0 min | start 2026-08-20 19:00:00+02:00 | end 2026-08-21 02:18:00+02:00 |DEBUG| sunrise  2026-08-20 06:27:06+02:00 | calculated_start 2026-08-19 23:09:06+02:00 | price_start 2026-08-20 19:00:00+02:00 | max 1  2026-08-20 19:00:00+02:00

The result is Wrong. The Max 1 reports the wrong time.
In both case it should have NOT selected the 19:00

But I have discovered the issue is in the SUN function :slight_smile:
If I ask which i next_sun_raise BEFORE the sunrise has happened it reports TODAY sunrise, if I ask it after sunrise it reports TOMORROWS sunrise