Template Help to find the last time state changed to a value

Proposed solution and question

(I think) I need a template which finds the last time that a WiFi switch was forced off, i.e. state “unavailable” and then triggers an hour before that, on the next day.

How do I write this?

Additional detail below, if you’re interested.


Context
I have an “Economy 7” system, which can provide cheap electricity overnight for some appliances. This means:

  • I have two dials on my (old) electricity meter: “low” and “normal”.
  • There is a switch which is supposedly meant to turn on the “low” power circuit between 23:30 and 06:30
  • When this Economy 7 switch is active, the WiFi switch I installed is on, and therefore the immersion heater is on

(The WiFi switch does have a “turn on state” (options: on | off | remember_last_state).)

Outcome

I want to have my immersion heater circuit (i.e. the WiFi switch) turn on an hour before the (dumb) Economy 7 switch is due to turn off. This is so I can limit the electricity my immersion heater uses each night, especially when it’s just me in the house (or ensure it doesn’t come on when I’m away). I want the heater to have been on just before I use the shower in the morning.

Problem

The Economy 7 switch is not always set to turn on between 23:30 and 06:30 (I think it’s slightly broken and tends to drift a bit).

See proposed solution above.

I believe you need to make an automation.
Trigger on unavailable and set a time helper to {{ now() - timedelta(hours = 1) }}

Then the helper is the trigger for next day

Or a triggered template sensor using that value template.

Thanks both, I’m a bit further along now, so I have this in my configuration.yaml:

template:
  - trigger:
      - platform: state
        entity_id: switch.fstwifitu_switch_1
        to: "unavailable"
    sensor:
      - name: "Economy7"
        state: "{{ now() + timedelta(hours = 23) }}"
        # Alternative: track time it ended, but put offset in the automation
        # state: '{{ now() }}'
        unit_of_measurement: "Hours"

So I noticed that the template given was for today, but if I’m using it as a trigger for the next day I need it to be + 23h (i.e. 1h duration). So I changed the timedelta. I’ll then use that as a trigger for the WiFi switch.

Two questions:

  1. Not sure what to do with the unit of measurement here - any suggestions?
  2. I would like to have automation set up to change the duration of the immersion heater coming on, depending on who’s in the house. If the number of people in the house is 1, then 1 hour, 2= 1.5 hours, etc. I want an input selector for number of people, then a lookup table somewhere so I can tweak the number of people vs durations, and then for the template sensor to just look up the right duration depending on the number of people. How could I do that? :pray:t2:

Replace this:

unit_of_measurement: "Hours"

With:

device_class: timestamp

You don’t give enough data to actually calculate how many hours. What is the value of hours for 3 people?

Assuming a linear relation (2 hours for 3 people) then do this:

"{{ now() + timedelta(hours = 23 - (0.5 * states(input_number.people_in_house)|int(0) + 0.5) }}"

Thank you for your help thus far!

I’m trying to check the output via the templating engine in Home Assistant. I get the following error:

P.S. I added this as template trigger to the Immersion Heater On automation:

{{ as_timestamp(states('sensor.economy7')) }}

As it says it’s missing a ).

"{{ now() + timedelta(hours = 23 - (0.5 * states(input_number.people_in_house)|int(0) + 0.5)) }}"

Brilliant - thank you.

I’m now getting UndefinedError: 'input_number' is undefined

which is strange because I’ve checked that it exists, and there is a value in there.

Wrap the input_number’s entity_id in quotes. Without quotes, it’s handled like the name of a variable (an undefined variable).

"{{ now() + timedelta(hours = 23 - (0.5 * states('input_number.people_in_house')|int(0) + 0.5)) }}"
1 Like

Jeez that was a stupid mistake. Thanks Taras.

1 Like

No worries; I’ve made many myself.

Frustrating because the real work is in composing the template’s logic, only to have it fail due to not “dotting an i or crossing a t”. :man_facepalming:

1 Like

Thank you both!

I’ll test it tonight.

I find the templating really tricky to master, I’m not sure if there’s a good resource to learn it from the basic up (currently bouncing around documentation is not super helpful, as I’m missing context).

Just to check - will this trigger the automation?
image

Given the config here:

template:
  - trigger:
      - platform: state
        entity_id: switch.fstwifitu_switch_1
        to: "unavailable"
    sensor:
      - name: "Economy7"
        state: "{{ now() + timedelta(hours = 23 - (0.5 * states('input_number.people_in_house')|int(0) + 0.5)) }}"

        # simple 1 hour duration
        # state: "{{ now() + timedelta(hours = 23) }}"

        # Alternative: track time it ended, but put offset in the automation
        # state: '{{ now() }}'
        device_class: timestamp

No, your trigger needs to resolve to a true or false value, not a number.

Use a time trigger.

trigger:
  platform: time
  at: sensor.economy

Thank you! Set up now.

image

So nearly there! It triggered, but I realised the maths doesn’t work for me - so I set out trying to change the expression @tom_l gave me, and realised I could try to simply include some if statements. But now I am chasing an error in the below (currently TemplateSyntaxError: unexpected '}' and when I remove a { I’m told another error… been going for about 20 mins so I thought I’d rubber duck you all here :slight_smile:

{{ now() + timedelta(hours = 24 - 

{ if (states('input_number.people_in_house'), 1) %}
0.5
{% elif (states('input_number.people_in_house'), 2) %}
1
{% elif (states('input_number.people_in_house'), 3) %}
3
{% elif (states('input_number.people_in_house'), 4) %}
6
{% endif %}

Did you make a mistake in the copy paste?

You are missing }} and one %, the first one (one people in house)

{{ now() + timedelta(hours = 24 - 

{% if (states('input_number.people_in_house') | int ==  1) %}
0.5
{% elif (states('input_number.people_in_house') | int ==  2) %}
1
{% elif (states('input_number.people_in_house') | int ==  3) %}
3
{% elif (states('input_number.people_in_house') | int ==  4) %}
6
{% endif %}
)
}}
state: >
  {% set hrs = [0.5,1,3,6][states('input_number.people_in_house')|float(1) - 1 ] %}
  {{ now() + timedelta(hours = 24 -  hrs }}
1 Like

Thanks once again!

I’m testing it in the templating devtools page.

I added a ) to the below (after “hrs” as it said I was missing one).

 {% set hrs = [0.5,1,3,6][states('input_number.people_in_house')|float(1) - 1 ] %}
  {{ now() + timedelta(hours = 24 -  hrs ) }}


I’m now getting an error UndefinedError: list object has no element 0.0. I’m guessing I need to reference something in the templating page so it gets the value of sensor.economy7?

But also, that reminds me that in the list I also need a “0” value so that when 0 people are in the house, the immersion heater doesn’t turn on at all

I added the 0 in the list - does this work?

 {% set hrs = [0,0.5,1,3,6][states('input_number.people_in_house')|float(1) - 1 ] %}
  {{ now() + timedelta(hours = 24 -  hrs ) }}

Here’s the properties of the inputnumber, if it helps

P.S. I don’t like having to ask you good folks for help each time, I hope I can learn to fish - any tips on how to learn this Jinja templating?