Cheapest Energy Hours - Jinja macro for dynamic energy prices

You can get that output by only placing the template in developer tools > template, and add from_json

So place only this on developer tools > template

{% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
{% set output = cheapest_energy_hours (
    sensor = 'sensor.energi_data_service',
    time_key = 'hour',
    value_key = 'price',
    hours = 2.25,
    include_tomorrow = true,
    look_ahead = true,
    mode = 'all'
) %}
{{ output | from_json }}

I have set up 3 template sensors. From 1 triggered sensor I get the correct time of the cheapest hour, from the other sensor I get the average electricity rate of that day, but I cannot get the third triggered binary sensor to work properly.
My intention for this sensor is that it gives the cheapest hours, per hour, between 08:00 and 20:00 and between 20:00 and 08:00 the next day.
Suppose hours 8 and 11 to 13 and 23 to 2 are cheap, then the binary_sensor should be true at these hours, but that does not happen.
What have I done wrong or is this simply not possible what I want?

This one works well.

template:
  # Determine the cheapest 2-hour block between 8:00 - 19:00 and between 20:00 - 7:00.
  - trigger:
      - platform: time
        at:
          - "07:05"
          - "19:05"
    sensor:
      - unique_id: starting_time_cheapest_hours
        name: Starttijd
        state: >
          {% set id = iif(now().strftime("%H:%M") == "07:05", 0, 1) %}
          {% set start = ["08:00", "20:00"][id] %}
          {% set end   = ["19:00", "07:00"][id] %}
          {% set sensor = "sensor.electricity_price" %}
          {% from "cheapest_energy_hours.jinja" import cheapest_energy_hours %}
          {{ cheapest_energy_hours (
               sensor = sensor, attr_all = "Prices", time_key = "readingDate",
               hours = 2, start = start, end = end, include_tomorrow = id )
          }}
        device_class: timestamp

This one works well.

template:
  sensor:
  # Gemiddelde stroomprijs incl. de opslagkosten
    - name: Gemiddelde stroomprijs
      unique_id: allin_electricity_average_price
      unit_of_measurement: "EUR/kWh"
      state: >
        {% set sensor = "sensor.electricity_price" %}
        {% from "cheapest_energy_hours.jinja" import cheapest_energy_hours %}
        {% set output = cheapest_energy_hours (sensor = sensor, attr_all = "Prices", time_key = "readingDate",
             hours = 24, include_tomorrow = false, mode = "all") | from_json %}
        {{ (float(output.average) + float(states("sensor.e_opslag"),0)) | round(2) }}
      state_class: measurement

This one doesn’t work.

template:
  # Determine the cheapest hour between 8:00 - 20:00 and between 20:00 - 8:00.
  - trigger:
      - platform: time
        at:
          - "08:00"
          - "20:00"
    binary_sensor:
      - unique_id: cheap_hour
        name: Cheap Hour
        state: >
          {% set id = iif(now().hour == 8, 0, 1) %}
          {% set start = ["08:00", "20:00"][id] %}
          {% set end   = ["20:00", "08:00"][id] %}
          {% set sensor = "sensor.electricity_price" %}
          {% from "cheapest_energy_hours.jinja" import cheapest_energy_hours %}
          {{ cheapest_energy_hours (sensor = sensor, attr_all = "Prices", time_key = "readingDate",
               hours = 12, start = start, end = end, include_tomorrow = id, mode = "is_now")
          }}

mode: "is_now" doesn’t work with a trigger based template sensor. The template is only rendered at the moment of the trigger, so at 8:00 and 20:00 and isn’t updated between the triggers.

Hi!

I spent some time last night to setup your macro. While testing this using the template test under developers tools it worked great. When i got the config right I set up a binary helper sensor to log overnight to see if everything was working as expected. When I checked this morning I saw that the binary sensor became unavailable a lot, always on exact minutes.

I’m using mode=“is_now” and start=now(). That should trigger the sensor, right?

This is a small grab from last night, the sensor was supposed to turn on at 4:00:00:

3:25:00 - Turned off
3:30:00 - Became unavailable
3:35:00 - Turned off
3:36:00 - Became unavailable
3:40:00 - Turned off
3:48:00 - Became unavailable
3:50:00 - Turned off
4:00:00 - Became unavailable
4:05:00 - Turned on
4:12:00 - Became unavailable

It also was unavailable for quite longer from 4:36:00 till 7:40:00. As source data I use the energy prices and the remaining charge of my EV (100 - remaining_charge divided by 10% per hour results in hours_left_to_charge). The sensor did not actually trigger the charger yet (as I haven’t set this up at this point) so I would expect the sensor to turn on at 4:00 as calculated by the macro and then stay on until the higher morning hour prices because at that point it would select a cheaper block later in the day. All input data was available during the whole night (didn’t became unavailable).

Do you perhaps know what is causing this behavior?

Emiel

Can you share the code you used for the binary sensor?

I have exactly the same problem.
No idea where I could look.
Also using mode=“is_now” and start=now()
Nothing work reliable, so I give up.

Of course! Should have done that in my initial reply.

{% set charge = states.sensor.leaf1_charge.state | float %}
{% set hours = (100 - charge) / 10 %}
{% from "cheapest_energy_hours.jinja import cheapest_energy_hours %}
{{ cheapest_energy_hours(sensor='sensor.zonneplan_current_electricity_tariff', attr_today="forecast", time_key="datetime", value_key="electricity_price", hours=hours, start=now(), include_tomorrow=true, mode="is_now") }}

When using this in de afternoon/evening (after new prices have been announced), the forecast attribute contains all prices from 12:00 yesterday until midnight the next day, in total 60 hours of data. There are no separate “today” and “tomorrow” attributes.

Could it have something to do with using a float for my hours? The exact times the sensor changes makes me suspect it maybe has something to do with rounding? (unavailable at 12 minute intervals and back online at 5 minute intervals).

Did you set it up as a binary sensor helper in the GUI?

Yes, that is what I did. Thinking back, during initial setup (under developers tools) I sometimes got an error while I did not change anything that should have. Maybe that was caused by the same issue.

What does the log of that charge sensor show? Does that one shows as unavailable periodically as well?

No, the charge sensor stayed available. At this moment the helper sensor is unavailable, so I tried hardcoding hours=4 but to no avail.

What does it show on developer tools > templates now then?

In the meantime it was available again, but I didn’t have to wait to long for it to go unavailable again.

ValueError: Template error: int got invalid input '[2, 5]' when rendering template '{% set charge = states.sensor.leaf1emie_charge.state | float %} {% set hours = ((100 - charge)/10) %} {% from "cheapest_energy_hours.jinja" import cheapest_energy_hours %} {{ cheapest_energy_hours(sensor='sensor.zonneplan_current_electricity_tariff', attr_today="forecast", time_key="datetime", value_key="electricity_price", hours=hours, start=now(), include_tomorrow=true, mode='is_now')}}' but no default was specified

Hope this helps.

I’ll try to find the cause for this issue

That’s awesome, thanks for you help. Please let me know if I can be of any assistance. Maybe some test templates to isolate the issue.

It would help if you could give the time this occurs, and the hours input the template uses at that time

So basically

{% set charge = states('sensor.leaf1_charge') | float %}
{{ (100 - charge) / 10 }}
{{ now() }}

Last hour my hours variable was 2.4 (EV sat on the driveway not charging).

It turns unavailable every 12 minutes. It gets back online on a 5 minute interval, but only if it’s currently unavailable. (Because if it’s already off, another change to off won’t show in my logs)

18:00:00 - Unavailable
18:05:00 - Off
18:12:00 - Unavailable
18:15:00 - Off
18:24:00 - Unavailable
18:25:00 - Off
18:36:00 - Unavailable
18:40:00 - Off
18:48:00 - Unavailable
18:50:00 - Off
19:00:00 - Unavailable

Update

v5.0.3

:bug: BUG FIXES

  • fix issue with determining datapoints per hour in case there were conflicting results from hour/start/end input

What’s Changed

Full Changelog: Comparing v5.0.2...v5.0.3 ¡ TheFes/cheapest-energy-hours ¡ GitHub

I think I found it, and fixed it in v5.0.3 which I just released

2 Likes

I’ve updated right after you posted, everything seems stable so far. (Which is the longest up untill now). I’ll report again in the morning. Thank you a lot for the help.