Cheapest Energy Hours - Jinja macro for dynamic energy prices

I have this template sensor in my configuration.yaml, but the entity energy_all never has any value or attributes. Any idea where I might be going wrong?

  - trigger:
      - platform: time_pattern
        # This will update every night
        hours: 00
        minutes: 22
    sensor:
      - unique_id: 911f0a21-48de-0000-90a2-4b46401268b8
        name: Energy Json
        value_template: >
          {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
          {% set energy_all = cheapest_energy_hours(sensor='sensor.entsoe.average_electricity_price_today', mode='all', start='00:00', end='23:59', hours='24', include_tomorrow=false)  %}       
          {{ energy_all | from_json }}

A sensor state is limited to 255 characters, this will be more.
You can put in in an attribute instead

Thanks for the hint, and quick reply unfortunately I haven’t been able to work out how to use attributes.

Maybe if I explain what I’m trying to achieve: I would like to run your macro once a day with mode=‘all’. This returns a lot of json data which I want to access at any time, in my automations, What would be a simple way to achieve this?

2 options:

  1. create a template sensor which puts every output mode in its own attribute. This will require you to specify each attribute and apply the macro for each attribute, but you can retrieve the data more easily in your automation.
  2. put the entire output from the macro in an attribute of the template sensor. The configuration for the template sensor is a lot shorter, but the data retrieval is a bit more cumbersome.

BTW your template sensor config was incorrect. The new format uses state instead of value_template

BTW2 if you use hours=24 the start and end will always be at midnight (especially with include_tomorrow=false which is the default. (besides one time per year when there are 25 hours in the day, when DST will stop. It will give an error when DST starts, as there are only 23 hours in the day then).

Option 1

  • I removed some redundant settings from your template, as you were using the defaults
  • I added the first 3 modes, the others need to be added.
template:
  - trigger:
      - platform: time
        at: "22:00"
    sensor:
      - unique_id: 911f0a21-48de-0000-90a2-4b46401268b8
        name: Energy data
        state: >
          {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
          {{ cheapest_energy_hours(sensor='sensor.entsoe.average_electricity_price_today', hours='24') }}
        attributes:
          end: >
            {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
            {{ cheapest_energy_hours(sensor='sensor.entsoe.average_electricity_price_today', hours='24', mode='end') }}
          max: >
            {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
            {{ cheapest_energy_hours(sensor='sensor.entsoe.average_electricity_price_today', hours='24', mode='max') }}
          time_min: >
            {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
            {{ cheapest_energy_hours(sensor='sensor.entsoe.average_electricity_price_today', hours='24', mode='time_min') }}

To get out the output of a mode (eg end) you can use {{ state_attr('sensor.energy_data', 'end') }}

Option 2

template:
  - trigger:
      - platform: time
        at: "22:00"
    sensor:
      - unique_id: 911f0a21-48de-0000-90a2-4b46401268b8
        name: Energy data
        state: >
          {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
          {{ cheapest_energy_hours(sensor='sensor.entsoe.average_electricity_price_today', hours='24') }}
        attributes:
          all: >
            {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
            {{ cheapest_energy_hours(sensor='sensor.entsoe.average_electricity_price_today', hours='24', mode='all') }}

To get out the output of a mode (eg end ) you can use {{ state_attr('sensor.energy_data', 'all')['end'] }}

1 Like

Hey thanks for making this, question, I cant seem to work out what im doing wrong here, I’m using the amber energy forecast below is my template

{% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
{{ cheapest_energy_hours(sensor='sensor.XXXX_general_forecast', hours=2, attr_all='forecasts', time_key='start_time', value_key='per_kwh')}}

This is the error I get
1 error: 0 datapoints (0.0 hours) in selection, where 4 (2.0 hours) are expected

and this is the forecasts attribute (copied the first 3 as an example

forecasts:

  • duration: 30
    date: ‘2024-06-23’
    per_kwh: 0.33
    spot_per_kwh: 0.1
    start_time: ‘2024-06-23T05:30:01+00:00’
    end_time: ‘2024-06-23T06:00:00+00:00’
    renewables: 25
    spike_status: none
    descriptor: low
  • duration: 30
    date: ‘2024-06-23’
    per_kwh: 0.37
    spot_per_kwh: 0.14
    start_time: ‘2024-06-23T06:00:01+00:00’
    end_time: ‘2024-06-23T06:30:00+00:00’
    renewables: 15
    spike_status: none
    descriptor: low
  • duration: 30
    date: ‘2024-06-23’
    per_kwh: 0.54
    spot_per_kwh: 0.31
    start_time: ‘2024-06-23T06:30:01+00:00’
    end_time: ‘2024-06-23T07:00:00+00:00’
    renewables: 8
    spike_status: none
    descriptor: high

The data shown in the section you shared is of 2 days ago (23th of June).
Does it also contain data of today?

Oh it looks like it refreshed and now that template is working properly :smiley: thanks I didnt even notice the dates being yesterdays :sweat_smile:

Hi Martijn,

I’ve got some help from you earlier this year to get the macro to work. Unfortunately it took me a while to install my EV charger and find time to implement the macro.

Since last werk I’ve been experimenting but I can’t get the macro to work for me. It started with user errors I could solve, but now I’ve come across an issue I can’t get rid off.

I have two template helpers. One binary to start the charger and one sensor template to display the planned start time. The latter also comes in handy during troubleshooting.

This is my binary template. The only difference with my start time sensor is the mode (=‘Start’).

{% set hours = states('sensor.charge_ev_duration') | float / 60 %}
{% set end = today_at('7:00') + timedelta(days=1 if now() > today_at() else 0) %}
{% from "cheapest_energy_hours.jinja" import cheapest_energy_hours %}
{{ cheapest_energy_hours(sensor='sensor.zonneplan_current_electricity_tariff', attr_all="forecast", value_key="electricity_price", hours=hours, start=now(), end=end, mode='is_now')}}

My problem is that the macro keeps postponing the start time up to the point it doesn’t have enough time to fully charge the car before the required end time.

The yellow timestamp started when I plugged the car in around 19:00. I drove quite some kilometers so the required charge duration was 408 minutes (6 hours and 48 minutes). The yellow block planned a start time of 0:00.

The blue block (0:01 - 0:06) planned the start at 0:05
The orange block (0:06 - 0:11) planned the start at 0:10
The green block (0:11 - 0:12) planned the start at 0:12

After that the senor became ‘unknown’ because there was not enough time to be ready at 7:00.

During this whole period the binary sensor state didn’t change, so my ‘Start charging’ automation wasn’t triggered.

Do you have any idea how to solve this issue?

The moving start time is expected, it will always render a time in the future, every time the template is rendered (which is each minute if you use now() in your template. Otherwise you won’t be able to use the generated output in a time trigger, as it will be in the past already.

If you use mode='is_now' it should not always generate a time in the future, as that would never be true. Maybe something is wrong there, let me have a look.

I did a quick test based on your input

This is the template I used:

{% set hours = 408 / 60 %}
{% set end = today_at('7:00') + timedelta(days=1 if now() > today_at() else 0) %}
{% from "cheapest_energy_hours.jinja" import cheapest_energy_hours %}
{{ cheapest_energy_hours(sensor='sensor.zonneplan_current_electricity_tariff', attr_all="forecast", value_key="electricity_price", hours=hours, start=now(), end=end, mode='is_now')}}
{{ cheapest_energy_hours(sensor='sensor.zonneplan_current_electricity_tariff', attr_all="forecast", value_key="electricity_price", hours=hours, start=now(), end=end, mode='start')}}

image

This was at 10:08 local time, so the mode='start' version generated a time in the future, howerver, the mode='is_now' version generated true

If I try the template in a binary sensor helper, it also shows On

Are you sure you are on the last version of the custom template?

I suspected the postponing to be expected behaviour, but wasn’t sure. I understand now that the is_now mode is not always in the future. Both are quite obvious now you pointed it out. Thanks for the insight.

I still don’t really understand why the binary template didn’t turn on last night.

I’ve been experimenting a bit, when (at 19:50) I put hours=1 and end=‘21:00’ I get it to turn on. If I put 20:00 it is off. If I put 22:00 it also is off but that’s probably because 21:00 to 22:00 is cheaper. (With hours=2 it does turn on)

Do I understand correctly that end minus now should be bigger than hours.

I’m underestimating my charge rate so duration is shrinking faster then the integration expects. Could that be something? Although it doesn’t even start to charge.

Yes I’m on the latest version. V5.5.1

I don’t want to interfere, but is the rule {% set end = … %} correct?
I think it should be like this:

{% set end = today_at('07:00') + timedelta(days=1 if now() > today_at('07:00') else 0) %}

Thanks for thinking along. I appreciate it, wouldn’t call that interfering!

You are right, but it’s probably that I copied one of my experiments because in my template I have it just as you suggested.

Yes, there must be sufficient time between start and end to match the time set for hours

It will return an error otherwise, but based on the state class of the sensor that might not be accepted, causing the state to be unknown

How does that work when the cheapest hours are the last hours right up to the specified end time?

So let’s say I want to charge overnight. I plug in my EV around 19:00 and it needs to be full at 7:00. When plugging in my battery is at 50% and I can charge approximately 10% per hour. That means a duration of 5 hours. The cheapest block spans from 3:00 to 7:00.

My battery level is updated every 15 minutes but the binary template is updated every minute. So at 3:01 the macro concludes there is not enough time and it switches to unknown.

I expect the binary mode=is_now template to turn on at 3:00. And I use it as a trigger to start the charger. So what happens after the trigger doesn’t matter. But still it didn’t switch to on.

I’ll do some more testing later when a full battery in the morning isn’t required.

You could try determining the best start time as soon as you plug in the charger in a trigger based template sensor and then use that start time in an automation to start charging

Just out of curiosity; can I also use this macro to accomplish the oppositie, i.e. get the most expensive hours during a day?

I’m currently looking in to home batteries and was thinking that it might be interesting to charge and discharge based on energy prices. That would mean that I’d like to charge during cheap hours and discharge (if needed) during expensive hours.

Yes, if you set the lowest parameter to false.

https://github.com/TheFes/cheapest-energy-hours/blob/164bc199051a79725bb09557388dcfdedd922b5e/documentation/4-data_output.md#lowest-bolean-default-true

2 Likes

Ah completely overlooked that option. Thanks!

1 Like

I try to install and I get error:

Repository TheFes/cheapest-energy-hours not found

I tried to add github repo and says not compliant with 5.5.1 HACS?