Cheapest Energy Hours - Jinja macro for dynamic energy prices

Hi Martijn,

I’m sorry I’m so on and off this thread. I’ve probably got a bit to much going on :wink:

Last time you suggested to make a trigger based sensor which is indeed a good idea. So to realize this I made some sensors to do this. The bellow code works, it successfully charged the car for a week now. But I’ve got a bit of unexpected behavior. Let’s start with some data from last night.


(All ‘long bars’ are continues, I verified that there are no tiny changes by looking at the start and end times)

  • Status description is an output from the charger
  • ev_plugged_in is on when the car is plugged in
  • ev_charge_duration calculates how long we need to charge till 100% (not in screenshot)
  • ev_charge_task lets me define different scenarios/tasks to decide what to do. If there is not enough time to fully charge the car I don’t want to calculate the start time but just continue charging the car after it got plugged in. (Eventually I want to expand this with more options)
  • ev_charge_planned_start_time holds the start time. Only if the charge task has determined that I can do a planned charging session.

Next to these sensors I have two automations.

  • Pause charging if ev_plugged_in = true for 1 minute and ev_charger_task = "planned"
  • Start charging if current time matches ev_charge_planned_start_time

Below are the templates for these sensors. As you can see in the above screenshot ev_charge_planned_start_time is changing during charging. Based on my templates I would expect ev_charge_planned_start_time only to get calculated when plugging in the car.

I expect a change of ev_charger_task to get triggered only once after plugging in the car. Because I have not specified a “to” in the trigger it technically could trigger on an attribute change, but ev_plugged_in doesn’t have changing attributes (as far as I can see only “friendly_name”). If it indeed does only change once it should also only trigger a change of ev_charge_planned_start_time once.

I’ve been googling/searching for a few nights now, but I can’t figure out why ev_charge_planned_start_time changes during charging. Am I missing something in my templates or could this be something in the macro itself? If you have any idea for a direction to search in that would be greatly appreciated. (But if you don’t I obviously still appreciate your work on this macro and this thread! :grinning: )

template:
  - binary_sensor:
    - name: EV plugged in
      unique_id: ev_plugged_in
      state: >
        {{ not is_state('sensor.wallbox_pulsarplus_sn_xxxxxx_status_description', 'Ready') }}

  - sensor:
    - name: EV charge duration
      unique_id: ev_charge_duration
      state: >
        {% set charge = states('sensor.leaf1emie_battery') | float %}
        {{ ((100-charge)/13) * 60 }}

  - trigger:
    - platform: state
      entity_id: binary_sensor.ev_plugged_in
  - sensor:
      - name: EV charge task
        unique_id: ev_charger_task
        state: >
          {% set finish_time = today_at('7:00') + timedelta(days=1 if now() > today_at('7:00') else 0) %}
          {% set time_left = ((finish_time - now()).total_seconds())/60 | float %} 
          {% set time_needed = states('sensor.ev_charge_duration') | float %}
          {% if states('binary_sensor.ev_plugged_in') == "on" %}
            {% if time_left - time_needed > 0 %}
            Planned
            {% else %}
            Immediate
            {% endif %}
          {% else %}
            Waiting
          {% endif %}

  - trigger:
    - platform: state
      entity_id: sensor.ev_charge_task
      to: "Planned"
  - sensor:
      - name: EV charge planned start time
        unique_id: ev_charge_planned_start_time
        device_class: timestamp
        state: >
          {% if states('sensor.ev_charge_task') == "Planned" %}
            {% set hours = states('sensor.ev_charge_duration') | float %}
            {% set end = today_at('7:00') + timedelta(days=1 if now() > today_at('7:00') 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/60, start=now(), end=end, mode='start')}}
          {% else %}
            NA
          {% endif %}

Your sensors are not trigger based as you placed a dash (-) before the word sensor. Basically you now have a trigger without any sensor under it, and a state based template sensor.

So instead of

- trigger: 
    - platform: whatever
- sensor: 
    - name: Something 

it needs to be

- trigger: 
    - platform: whatever
  sensor: 
    - name: Something 

That really feels like a stupid mistake… Thank you for checking my code. I’ll refine it some more with various options and then do a write-up. Once I have something ready I’ll send you a link to see if it’s worth adding to the examples in GitHub.

Question from me again. I use the average sensor from entsoe. Something happened with the Entsoe integration and my cheapest energy hours template don’t work anymore. Sensor name is still the same.

For example i got this error. With this template code. Do you know what to adjust to get my template working again. I am struggling with this for days know.

1 error: 0 datapoints (0.0 hours) in selection, where 3 (1.5 hours) are expected

{% from ‘cheapest_energy_hours.jinja’ import cheapest_energy_hours %}
{{ cheapest_energy_hours(‘sensor.entsoezonneplan_average_electricity_price_today’, attr_today=‘prices_today’, hours=1.5, lowest=True, mode=‘average’, value_key=‘price’, precision=2) }}

Does the sensor actually has data in the attribute for today? Looks like the sensor didn’t update.
Does it maybe still have the data of yesterday?

Yesterday it had data for today and tomorrow. When i look right now, the data is from 4 octobre.
A little bit strange. The data yesterday was also not correct. Do you know a integration that is usable different then the entsoe integration for dynamic energy prices?

Without knowing your location I can’t tell you anything for sure about available integrations. Maybe the Nordpool integration?

I am in the Netherlands. The Nordpool integration is not supported here. I have the “Frank Energie” integration working now which does the same as Entsoe.

Nordpool is supported in the Netherlands, I’m from the Netherlands myself :slight_smile:

You are right. Last time that i tried, it wouldn’t configure, but now it would.
Now i have some problems with the new sensors. Some times the time is 21:00 - 24:00 and at 20:00 with the mode=isnow template it is saying true. That is one our to earlie. I have 3 different configured templates and 2 have sometimes problem that it’s says to early true. Do you know what could be the problem?

I would need to see the exact parameters you used for the template.

Trying to extract the most expensive hours in order to compare the difference between the cheapest energy hours and the moste expensive energy hours, in order determine whether charging the battery is profitable or not (once I include conversion loss and depreciation).

But I’m struggling a bit. I’ve created a sensor with the energy price as a negative numbers (so the moste expensive becomes the cheapest) in attributes named “today” and “tomorrow”, but realize that it’s probably not these attributes this integration is looking at - I guess it’s the “raw_today” and “raw_tomorrow”. This complicated my mission a bit.

Is there any way I can get this integration to extract the most expensive energy hours?

If you just add lowest=false it will give you the most expensive hours instead of the cheapest house.

See: cheapest-energy-hours/documentation/4-data_output.md at a7c184e011507a4a928fde1a6e15211ff0f642ff · TheFes/cheapest-energy-hours · GitHub

1 Like

Thank you. I have not done my due diligence.

OK, my lack of knowledge of yaml and json coding is becoming apparent…hope for som help although I’m pretty sure it has nothing to do with the integration itself.

I’m trying to create a sensor which as attributes puts out the start hour and the average price. Got this going on in the Developer tool.

state: >
{% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
{% set output = cheapest_energy_hours
(
sensor='sensor.energi_data_service',
attr_today='raw_today',
attr_tomorrow='raw_tomorrow',
time_key='datetime',
value_key='price',
hours=2,
include_tomorrow=true,
look_ahead=true,
lowest=false,
mode='all'
) | from_json %}
{{ output }}

attributes:
  start: {{ output.start | as_timestamp | timestamp_custom("%H") }}"
  average: {{ output.average }}

Once created in the config I can’t get the sensor to get a value, nor get the attributes. I’ve tried several combinations, with and without trigger, the “| from_json”.

Got it working with this

  - sensor:
      - name: "Expensive Hours"
        unique_id: expensive_hours
        state: >
          {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
          {% set output = cheapest_energy_hours
          (
          sensor='sensor.energi_data_service',
          attr_today='raw_today',
          attr_tomorrow='raw_tomorrow',
          time_key='datetime',
          value_key='price',
          ) %}
            {{ output }}

Again, probably my lack of programming knowledge. I’d appreciate any inputs.

What’s going wrong here is shat the output variable isn’t globablly available in your template sensor config. You need to define it every time under each YAML key. You also need to indent your templates 2 spaces under the key.
And the state will always be a string and is limited to 255 characters, so you won’t be able to output mode='all' for the template sensor state.

You could create a trigger based template sensor which updates every minute, in that case you can use the same output variable for each key.

BTW, there is a parameter to define the output for the datetime objects (time_format)
BTW2, if you only output the hour (using %H) how are you going to know if that’s today or tomorrow?

Here’s an example of a trigger based version. I also removed all paramaters where you were using the default settings.

template:
  - trigger: 
      - trigger: time_pattern
        minutes: "/1"
    action:
      - variables:
          output: >
            {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
            {{  cheapest_energy_hours(
                  sensor='sensor.energi_data_service',
                  attr_tomorrow='raw_tomorrow',
                  time_key='datetime',
                  hours=2,
                  include_tomorrow=true,
                  look_ahead=true,
                  lowest=false,
                  mode='all',
                  time_format='%H'
                  ) | from_json
            }}
    sensor:
      - unique_id: template_sensor_highest_prices
        name: "Highest 2 hours today and tomorrow"
        state: "{{ output.average }}"
        attributes:
          start: "{{ output.start }}"
          prices: "{{ output.price_list }}"
          is_now: "{{ output.is_now }}"
1 Like

@TheFes Thanks very useful. Indentation - yes, my sloppy copy/paste from the Developers tool. The sensor I created had the proper indentation.
This is work in progress, not though all the way through yet, but my plan was somehow to identify the hours with an average price above the charging price * conversion loss factor + depreciation. Then calculate the consumption in those hours to determine the kWh needed to charge and the time needed to charge. On that basis trigger the battery to charge the correct amount at the cheapest energy hour - if and only if it is profitable.
Today or tomorrow. Not a clear plan here either. But I’m considering triggering this check at 13PM (after the electricity prices have been updated) and at 00AM after end of the peak period. But at 13AM I would need to determine whether to charge “this” afternoon" or postpone it until after midnight - so this won’t be achieved with a single lookup using this integration.

Here are some examples, wit a screenshot.

It is about the middle line 3 sensors. Here you can see , it says true before 22:30. When i look in the data it changes to true at 22:00.

Here are the templates from the middle line:

‘{% from ‘cheapest_energy_hours.jinja’ import cheapest_energy_hours %}
{{ cheapest_energy_hours(‘sensor.current_electricity_price_all_in’, attr_today=‘prices_today’, hours=1.5, start=‘20:00’, end=‘23:59’, lowest=True, mode=‘average’, value_key=‘price’, precision=2) }}’

‘{% from ‘cheapest_energy_hours.jinja’ import cheapest_energy_hours %}
{% set start = cheapest_energy_hours(‘sensor.current_electricity_price_all_in’, attr_today=‘prices_today’, hours=1.5, start=‘20:00’, end=‘23:59’, lowest=‘true’, value_key=‘price’, mode=‘start’, time_format=‘time24’) %}
{% set end = cheapest_energy_hours(‘sensor.current_electricity_price_all_in’, attr_today=‘prices_today’, hours=1.5, start=‘20:00’, end=‘23:59’, lowest=‘true’, value_key=‘price’, mode=‘end’, time_format=‘time24’) %}
{{ start }} - {{ end }}’

‘{% from ‘cheapest_energy_hours.jinja’ import cheapest_energy_hours %}
{{ cheapest_energy_hours(‘sensor.current_electricity_price_all_in’, attr_today=‘prices_today’, value_key=‘price’, time_key=‘readingDate’, hours=1.5, start=‘20:00’, end=‘23:59’, lowest=‘true’, mode=‘is_now’) }}’

This script delivered without any modifications the following error message overnight: ZeroDivisionError: float division by zero
Though we changed to wintertime, I can’t imagine it has anything to do with it. Any suggestions?

Which template did you use?