Data template in timer duration

Hi there,

I’m trying to create an automation for my salt water pool chlorine generator. The time the chlorinator must be active and therefore generating chlorine is dependant on the temperature of the water. The hotter it is, the more chlorine is required.

So I want an automation that switches the chlorinator on at sunset and turns off some time later, where the time is determined by pool temp (deg C) * 12. IE for every degree the chlorinator runs for 12 minutes.

My config info is below.

The trouble is that I don’t seem to be able to use any sort of variable for timer duration. The config below, for example, gives the error: extra keys not allowed @ data['data_template']

Is there any other option?

I have a sensor that determines this time:

## Pool chlorinator time needed
- platform: template
  sensors:
    chlorinate_time:
      value_template: >-
        {% set time = (states('sensor.pool_temp')|float)|round * 720 %}
        {% set minutes = ((time % 3600) / 60) | int %}
        {% set hours = ((time % 86400) / 3600) | int %}
        "{{ "%02d" | format(hours) }}:{{"%02d" | format(minutes)}}"

sensor.chlorinate_time returns a value like “02:36”

I have a timer set in configuration.yaml:

## timers
timer:
  pool_pm:

And I have two automations (nb the chlorinator uses a climate integration as a hack, there is no pool chlorinator component):

- id: chlorinator-on-pm
  alias: chlorinator-on-pm
  description: 'Turn on pool chlorinator at sunset'
  trigger:
  - event: sunset
    platform: sun
  condition: []
  action:
  - service: climate.set_temperature
    data:
      entity_id: climate.chlorinator
      temperature: 100
  - service: timer.start
    data_template:
      entity_id: timer.pool_pm
      duration: '{{states('sensor.chlorinate_time')}}'

- id: chlorinator-off-pm
  alias: chlorinator-off-pm
  description: Turn off pool chlorinator after temp-calc duration
  trigger:
  - event_data:
      entity_id: timer.pool_pm
    event_type: timer.finished
    platform: event
  condition: []
  action:
  - data:
      entity_id: climate.chlorinator
      temperature: 0
    service: climate.set_temperature

sensor.chlorinate_time returns a value like “02:36”

This needs to be in the following format for the timer duration:

02:36:00

Thanks @tom_l. I’m not sure that’s it. I’ve made the change so that sensor now gives the HH:MM:SS value but I get the same error. I’m not sure that the duration: field can accept a template entry.

Looking at the timer integration page I don’t believe the duration can be templated. Either that or it is failing because you are using single quotes in single quotes. This

duration: '{{states('sensor.chlorinate_time')}}'

Should be:

duration: "{{states('sensor.chlorinate_time')}}"

However due to the error message I think it is the former reason.

An alternative method would be to forget the timer and just use a template trigger to turn off the chlorine generator after the required time.

- id: chlorinator-off-pm
  alias: chlorinator-off-pm
  description: Turn off pool chlorinator after temp-calc duration
  trigger:
    platform: template
    value_template: "{{ state_attr('climate.chlorinator', 'temperature')int = 100 }}"
    for: "{{ states('sensor.chlorinate_time') }}" # assuming this has hh:mm:ss format
  action:
    service: climate.set_temperature
    data:
      entity_id: climate.chlorinator
      temperature: 0

Due to some excellent work by one of the forum members, Phil Bruckner, you can use a template in for: for most (all?) triggers now.

1 Like

I’m pretty sure you can template the timer duration in a timer start service call.

this is one the actions I have in an automation and it works fine:

  - service: timer.start
    data_template:
      entity_id: timer.cpap_timer
      duration: >
        {% set time_now = as_timestamp(now()) %}
        {% set start_time = states('input_datetime.cpap_ind_start_time') %}
        {% set start_time = as_timestamp(now()) | timestamp_custom('%Y-%m-%d') + ' ' + start_time %}
        {% set duration = 255 * 60 %}
        {% set end_time = as_timestamp(start_time) + duration %}
        {% set seconds_left = end_time - time_now %}
        {% set hours_left = seconds_left // 3600  %}
        {% set minutes_left = (seconds_left - (hours_left * 3600)) // 60 %}
        {{ '%02i' | format(hours_left) }}:{{ '%02i' | format(minutes_left) }}:00

I think it’s likely the misuse of the quotes in your template that @tom_l pointed out that is the problem

3 Likes

As @tom_l said, the main problem is your template is not using quotes correctly.

       duration: '{{states('sensor.chlorinate_time')}}'

needs to be

      duration: "{{states('sensor.chlorinate_time')}}"

HH:MM format is acceptable.

And, yes, as @tom_l also said, you can use a templated for: option on most triggers now.

However, I think the easiest way is to just use a templated delay in a single automation:

 - id: chlorinator-on-off
  alias: chlorinator-on-off
  description: 'Turn on pool chlorinator at sunset and off later'
  trigger:
  - event: sunset
    platform: sun
  condition: []
  action:
  - service: climate.set_temperature
    data:
      entity_id: climate.chlorinator
      temperature: 100
  - delay: "{{states('sensor.chlorinate_time')}}"
  - data:
      entity_id: climate.chlorinator
      temperature: 0
    service: climate.set_temperature

True, if HA restarts during the delay the chlorinator won’t be turned off. But I’m pretty sure the same would be true with your timer-based implementation. (I.e., I don’t think active timers will survive a HA restart.)

If you want the solution to work through a HA restart, then you’d probably need to save the stop time in an input_datetime or input_number (as a timestamp.) Then use a template trigger on the off automation that compares the current time (e.g., sensor.date_time) to the saved time.

Thanks all for your input. I’ll clean up my quotes and see if that works, just for my own education. I like the single automation option from @pnbruckner, looks clean. Thanks so much.

1 Like

Finity, I understand ALL of your template, apart from the last line, can you describe the : - %02i’ | format(hours_left) bit, I can guess but I thought filters ran right to left ??? :confused:
TIA (thanks in advance)

I’ll take a stab. :slight_smile:

The last line actually contains two separate expressions, and each contains one filter. So right-to-left or left-to-right doesn’t really apply here. But, basically, filters are just another binary operator, and they have higher precedence than other binary operators. The filter takes what comes before it as its first “argument”, and then other arguments are supplied in parentheses. So:

'%02i' | format(hours_left)

just means to run the format filter, where the first argument is the string '%02i', and the second argument is the value of the variable hours_left. The format filter basically uses the first argument as the format string, and the remaining arguments as the values. So the above is effectively the same as the Python expression:

'%02i' % (hours_left)

For more details, see the docs on the format filter.

I should probably add, the filter operator is left-to-right associative. E.g.,

a | b | c

in this case a is “fed into” the b filter, and the result is “fed into” the c filter, like:

(a | b) | c

This, I suspect, is why the doc uses “pipe” terminology when describing filters.

^ yes.

But just for completeness in case anyone wonders what the “%02i” part does…

It basically says to format the integer(i) variable(hours_left) as a string with a length of two places(2) and zero padded(0).

https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting

you can simplify a bit with out using all the calcs.

  - service: timer.start
    data_template:
      entity_id: timer.cpap_timer
      duration: >
        {% set start = states('input_datetime.cpap_ind_start_time') %} 
        {% set start = as_timestamp([now().date() | string, start] | join(' ')) %}
        {% set end = start + 255 * 60 %}
        {{ (end - as_timestamp(now())) | timestamp_custom("%H:%M:00", false) }}

EDIT: I should mention that this won’t work if the timer is over 24 hours…

Thanks for the suggestions. I’ll look into switching it over to that format.

It’s not my template tho. I stole it from @klogg. :wink: It worked so I didn’t really dig into it too much but just adapted it to my use.

Phil, thanks for the explanation (I really need to continue reading that python book and then do some ‘more’ coding (word clock — rubs hands))
I took time to respond as I needed to play with the expressions
What I didn’t get (cos I did guess correctly what it was used for) was why it wasn’t : -
take this number - filter it (pipe it) into a format of this type
eg - hours_left | format(‘%02i’)
many other pipe instructions are very right to left, like : - value [pipe] function → result
eg 3.1415926 | int, 2.17186 | float etc. but having played I see that this is not the case here
format_you_want_it_in (as text string) [pipe] value → result

But once again Petro has done it most concisely, though I do like your automation and the off time in an input_datetime is a very nice touch :smiley:

Thanks Again - and also to @finity for his :crazy_face: time manipulations (or should that be to Klogg ??? :smiley: )

Hi all, this post is 2 years old but I found it as I had to solve the same problem. The hint of tom_l has actually helped me solve my problem. Just posting this as it may help others. For me, this works (in a HA script):

tv_timer_on:
  alias: Start TV child protection timer
  sequence:
    - service: timer.start
      data_template:
        entity_id: timer.tvwatch
        duration: "{{states('input_datetime.countdowntime')}}"

input_datetime.countdowntime is a helper variable in time format:
Bildschirmfoto 2021-11-21 um 12.26.42 (2)

2 Likes

Yes, this is what I did for my pool timers, this solution is describe here:
Restore active/paused timers after a restart - #7 by Kal_Man