Cheapest Energy Hours - Jinja macro for dynamic energy prices

I’m not experienced (enough) with the coding, would be great to have an example of car charging based on remaining the current battery percentage.

Ie. if the car has 30% remaining, and default charging profile is set to max. 80%, would be nice to have a notification when the charge should start charging (which can be automated as well) for best price performace.

You would need the battery capacity (in my case 77kWh) and the default charging speed (11kW) in a helper I guess?

This example is based on remaining time: cheapest-energy-hours/documentation/6b-charge_car.md at 06c1ded7496b77e5ffae1a653a298a263b62ddbc · TheFes/cheapest-energy-hours · GitHub

In most situation the time only is provided when the car is actually charging, which is not the case when you just plugin the chargeconnector, then it’s narmally ‘connected - waiting to charge’.

You can plug it in, let it charge for a few minutes until you know the remaining time, then use that to determine the best time to continue charging.
You will need to know what the remaining time is, as that is a factor to determine the best time.

Yes that’s true, but not very ‘hands-off automation’.

When you know the current percentage, full capacity of the battery (kWh), and charging speed (kW) you can make a estimation of the chargetime which can be used in the code. Right? Or am I missing an element?

Somthing like this?
(ChatGPT)

sensor:
  - platform: template
    sensors:
      ev_remaining_charge_time:
        friendly_name: "EV Remaining Charge Time"
        unit_of_measurement: "hours"
        value_template: >
          {% set battery_capacity_kWh = 77 %}
          {% set charge_speed_kW = 11 %}
          {% set battery_percentage = states('sensor.your_battery_percentage_sensor') | float %}
          
          {% if battery_percentage is defined and battery_percentage > 0 %}
            {% set remaining_capacity_kWh = (100 - battery_percentage) / 100 * battery_capacity_kWh %}
            {% set remaining_time_hours = remaining_capacity_kWh / charge_speed_kW %}
            {{ remaining_time_hours }}
          {% else %}
            N/A
          {% endif %}

Well, batteries usually charge slower the fuller they are.
I don’t have an electric vehicle myself, so I don’t have any practical experience with how to use the macro for it. The example is based on the information provided by another user of the macro.

There’s a lot wrong with that, but yes, you could use something like that.
I’m currently busy, but I’ll respond with a better version later today or tomorrow.

That’s fine, thanks for looking into it. Addition would be to have it fully charged by a predefined time (helper).

With slow chargers (at home in my case) the charge is always 11kW until it’s full. Only with fast (DC) chargers there is a different chargecurve.

Screenshot 2024-03-20 at 16.39.02

This sensor provides me the estimated charge time:


    ev_estimated_charge_time:
      unique_id: f48b4b86-a6c9-4791-93a3-daf9c3a89ff2
      friendly_name: "EV estimated charge time"
      unit_of_measurement: "minutes"
      value_template: >
        {% set battery_capacity = states('input_number.ev_battery_capacity') | float %}
        {% set charge_speed = states('input_number.ev_charging_speed') | float %}
        {% set current_charge_percentage = states('sensor.ev_state_of_charge') | float %}
        {% set current_charge_kwh = (current_charge_percentage / 100) * battery_capacity %}
        {% set remaining_kwh = battery_capacity - current_charge_kwh %}
        {% set remaining_time_hours = remaining_kwh / charge_speed %}
        {{ (remaining_time_hours * 60) | round(0) }}

Next step is to use this with Jinja and your macro, with a preferred end time. Based on the example I came up with the following:

    ev_optimize_charge:
      unique_id: 8ced58d7-8919-4a0f-8cd6-4b8b55b81470
      friendly_name: "EV optimize charge"
      value_template: >
        {% set sensor = 'sensor.nordpool_kwh_nl_eur_3_10_0' %}
        {% set hours = states('sensor.ev_estimated_charge_time') | float / 60 %}
        {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
        {{ cheapest_energy_hours(sensor=sensor, hours=hours, start=now(), end=now().replace(hour=7, minute=0, second=0, microsecond=0) + timedelta(days=1), mode='is_now') }}
      availability_template: >
        {{
          is_state('device_tracker.ev_tracker', 'home')
          and is_state('binary_sensor.charger_connected', 'Locked, can connected')
          and states('sensor.ev_estimated_charge_time') | is_number
        }}

Automation is yet to be done. With the automation the actual charge start/stop will be initialized, right?

Does this makes sense? :slight_smile:

The template (binary) sensors you now have is the legacy template sensor format. I would advice to use the modern format.

There was also an issue with your end paramter. After midnight this would still add one day, and this could result in your car not being charged at 7:00 in the morning because the cheapest moment was eg at 15:00

It would look like this (note that template: is the integration, your current sensors are under sensor, the code below is how it could be placed directly in your configration.yaml):

template:
  - sensor:
      - unique_id: f48b4b86-a6c9-4791-93a3-daf9c3a89ff2
        name: "EV estimated charge time"
        unit_of_measurement: "minutes"
        state: >
          {% set battery_capacity = states('input_number.ev_battery_capacity') | float %}
          {% set charge_speed = states('input_number.ev_charging_speed') | float %}
          {% set current_charge_percentage = states('sensor.ev_state_of_charge') | float %}
          {% set current_charge_kwh = (current_charge_percentage / 100) * battery_capacity %}
          {% set remaining_kwh = battery_capacity - current_charge_kwh %}
          {% set remaining_time_hours = remaining_kwh / charge_speed %}
          {{ (remaining_time_hours * 60) | round(0) }}
        availability: "{{ states('sensor.ev_state_of_charge') | is_number }}"

  - binary_sensor:
      - unique_id: 8ced58d7-8919-4a0f-8cd6-4b8b55b81470
        name: "EV optimize charge"
        state: >
          {% set sensor = 'sensor.nordpool_kwh_nl_eur_3_10_0' %}
          {% set hours = states('sensor.ev_estimated_charge_time') | float / 60 %}
          {% set end = today_at('07:00') + timedelta(days=1 if now() > today_at('07:00') else 0) %}
          {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
          {{ cheapest_energy_hours(sensor=sensor, hours=hours, start=now(), end=end, mode='is_now') }}
        availability: >
          {{
            is_state('device_tracker.ev_tracker', 'home')
            and is_state('binary_sensor.charger_connected', 'Locked, can connected')
            and states('sensor.ev_estimated_charge_time') | is_number
          }}
1 Like

Thanks, have implemented it and will try this evening how it turns out :+1:

Works like a charm!

Possible adjustment / tweak to be made is that the code can be optimized to start at the beginning of the lowest price. Now it seems that it started charging at 02:40 when the first 40mins of the lowest price are already ‘gone’. The end of the charging session is slightly in more expensive tariffs.

Another aspect is that, when the dishwasher (example) kicks in, the power to the EV charger is limited (load balancing) this may cause a delay in charging the EV, and extending the period in more expensive tariffs at the end of the charging session.

Is that possible with a command, to start at the very beginning of the cheapest moment (02:00 this morning in the screenshot), instead of waiting?

Another option would be to manually add let’s say 15min to the estimated charge time, to incorporate any slight delays in charging.

    {% set remaining_time = ((remaining_kwh / charge_speed) * 60) + 15 %}

Sorry for my late response.
I think you’re misreading the data.

The cheapest hour in your graph is from 4:00 to 5:00, so it starts at 2:40 to fully make use of that cheapest hour.
If it would have started 40 minutes earlier, you would have missed the 40 cheap minutes from 4:20 to 5:00

Apparently the charging did take like 1 or 2 minutes longer, so that’s why it charged a few minutes in the 5:00 to 6:00 time block. You could add maybe 5 minutes to the estimated charge time to account for that, that will make it start a bit earlier.

Yes, done that (added 15min) which works fine. Thanks!

Is there a way to visualize the calculated start time?
Is there some value that is provided that can be visualized?

You can create a template sensor with the start time and show that on your Dashboard, or use a markdown card which supports templates

Update

:star2: IMPROVEMENTS

  • Add option to use 'all' as input for the hours parameter. When hours='all' is set, all hours between the start and end will be used. This can for example be practical when you want the average price of the whole day, and will avoid issues when hours=24 and there are only 23 hours in the day because of DST.

:bug: BUG FIXES

  • Fix a bug in the time_key finder when the data also includes yesterday’s prices.
  • Fix a bug in de debug output

What’s Changed

Full Changelog: Comparing v5.4.4...v5.5.0 · TheFes/cheapest-energy-hours · GitHub

1 Like

I have made a few sensors such as expensive_hours_8_13 e.g. morning’s expensive hours and I want to avoid battery charge within these hours. Typicly in my area the electricity price is higher 07-12 than 12-17 so if I have inverted in self-use mode it will charge as soon as PV excess is present. I need to change inverter working mode to feed-in during expensive hours and get it back to self-use mode during cheap hours. I have tested automation based on these sensors but got stuck at trigger step. Shoud I use a state of an attribut in this case which one? There are only unavaillable and unknown. I have also cheched examples in the documentation but didn’t find them usefull.

what is the state of the sensors you made?

is that the start time of the most expensive period?