Cheapest Energy Hours - Jinja macro for dynamic energy prices

so if i push button at 00:15 it will take time btw 00:15 - 06:00 right?

Well, kinda, if you push the button at 00:15 it will take the times from then until 6:00 the next day, so that’s 23:45 hours on that day, and 6 hours of the next day. As the prices for the next day are not known at 00:15 it will use what it has, which is 00:15 - 06:00.

You probably want something like this (you didn’t provide the attributes with the prices for tomorrow, I assumed it’s prices_tomorrow):

{% from ‘cheapest_energy_hours.jinja’ import cheapest_energy_hours %}
{% set end = today_at('06:00') %}
{% set end = end + timedelta(days=1) if now() > end else end %}
{{ cheapest_energy_hours('sensor.energi_data_service_incl_vat_tarif', attr_today='prices_today'', attr_tomorrow='prices_tomorrow', hours=4, start=now(), end=end) }}

Hi there,

My goal is to create an average cheapest energy hours of 3 hours.

While reading the docs I can’t find out how and where to implement the tibber source sensor. Should it be in configuration.yaml or as my other sensors in the sensor directory? I have succesfully installed cheapest energy hours through hacs.

If you have them in a sensor folder, it sounds like you’re still using the legacy format.
This Tibber sensor uses the modern template sensor format. You can paste it directly in your configuration.yaml if you don’t use the template: integration in there already

My washing machine lets me manually set a start delay. As a result, I created this table to offer an overview of the upcoming peak solar energy times (which is even less expensive than the lowest electricity rates) and the cheapest electricity times for the next 12 and 24 hours. Using this table, you can manually set the delay on the machine.

It would be helpful to create a macro that performs a similar function but is specifically designed to focus on “highest solar hours.” For instance, it could calculate the time when we can expect 2000 W of solar power to run the washing machine entirely on solar energy. I have a sensor with data from forecast.solar, which includes attributes that provide the expected solar power for each hour. I created a sensor that calculates the info, but this is not as flexible as a macro like this one.

Screenshot 2025-01-26 14.06.56 - Display 2

<table border="1">
  <tr>
    <th>Source</th>
    <th>Period</th>
    <th>Start Time</th>
    <th>Time Until Start</th>
  </tr>
  <tr>
    <td>Sun</td>
    <td>Today</td>
    <td>
      {%- set start = states('sensor.power_highest_peak_time_today') -%}
      {%- set start_datetime = start | as_datetime -%}
      {%- if start_datetime -%}
        {%- set start_local = start_datetime.astimezone() -%}
        {%- set start_readable = start_local.strftime('%d-%m %H:%M') -%}
        {{ start_readable }}
      {%- else -%}
        Invalid time
      {%- endif -%}
    </td>
    <td>
      {%- if start_local -%}
        {%- if start_local > now() -%}
          {%- set time_until_seconds = (start_local - now()).total_seconds() -%}
          {%- set time_until_hours = (time_until_seconds / 3600) | round(2) -%}
          {{ time_until_hours }} hours
        {%- else -%}
          Time has already passed
        {%- endif -%}
      {%- else -%}
        Invalid time
      {%- endif -%}
    </td>
  </tr>
  <tr>
    <td>Sun</td>
    <td>Tomorrow</td>
    <td>
      {%- set start = states('sensor.power_highest_peak_time_tomorrow') -%}
      {%- set start_datetime = start | as_datetime -%}
      {%- if start_datetime -%}
        {%- set start_local = start_datetime.astimezone() -%}
        {%- set start_readable = start_local.strftime('%d-%m %H:%M') -%}
        {{ start_readable }}
      {%- else -%}
        Invalid time
      {%- endif -%}
    </td>
    <td>
      {%- if start_local -%}
        {%- if start_local > now() -%}
          {%- set time_until_seconds = (start_local - now()).total_seconds() -%}
          {%- set time_until_hours = (time_until_seconds / 3600) | round(2) -%}
          {{ time_until_hours }} hours
        {%- else -%}
          Time has already passed
        {%- endif -%}
      {%- else -%}
        Invalid time
      {%- endif -%}
    </td>
  </tr>
  <tr>
    <td>Cheapest electricity</td>
    <td>Next 12 hours</td>
    <td>
      {%- set sensor = 'sensor.average_electricity_price_today' -%}
      {%- set start = now() -%}
      {%- set end = start + timedelta(hours=12) -%}
      {%- from "cheapest_energy_hours.jinja" import cheapest_energy_hours -%}
      {%- set start_time = cheapest_energy_hours(sensor=sensor, hours=2, start=start, end=end) | as_datetime -%}
      {%- if start_time -%}
        {%- set start_local = start_time.astimezone() -%}
        {{ start_local.strftime('%d-%m %H:%M') }}
      {%- else -%}
        Invalid time
      {%- endif -%}
    </td>
    <td>
      {%- if start_local -%}
        {%- if start_local > now() -%}
          {%- set time_until_seconds = (start_local - now()).total_seconds() -%}
          {%- set time_until_hours = (time_until_seconds / 3600) | round(2) -%}
          {{ time_until_hours }} hours
        {%- else -%}
          Time has already passed
        {%- endif -%}
      {%- else -%}
        Invalid time
      {%- endif -%}
    </td>
  </tr>
  <tr>
    <td>Cheapest electricity</td>
    <td>Next 24 hours</td>
    <td>
      {%- set sensor = 'sensor.average_electricity_price_today' -%}
      {%- set start = now() -%}
      {%- set end = start + timedelta(hours=24) -%}
      {%- from "cheapest_energy_hours.jinja" import cheapest_energy_hours -%}
      {%- set start_time = cheapest_energy_hours(sensor=sensor, hours=2, start=start, end=end) | as_datetime -%}
      {%- if start_time -%}
        {%- set start_local = start_time.astimezone() -%}
        {{ start_local.strftime('%d-%m %H:%M') }}
      {%- else -%}
        Invalid time
      {%- endif -%}
    </td>
    <td>
      {%- if start_local -%}
        {%- if start_local > now() -%}
          {%- set time_until_seconds = (start_local - now()).total_seconds() -%}
          {%- set time_until_hours = (time_until_seconds / 3600) | round(2) -%}
          {{ time_until_hours }} hours
        {%- else -%}
          Time has already passed
        {%- endif -%}
      {%- else -%}
        Invalid time
      {%- endif -%}
    </td>
  </tr>
</table>

Is possible to get when energy prices procentage drop from the average is at the most (below a given threshold) ?

I dont know the maximum sequental hours, but the minimum of sequental hours that I need.(eg 4 hours)-

For example
If the lowest prices are between 22-05 => 7 hours
If the the lowest prices are between 01-05 => 4 hours

I want to charge the car at least 4 hours every night, but if the cheap period is longer I want to charge it more.

For the life of me i cannot figure this one out. And i dont know if this is the right place to ask.

I have dynamic energy prices that come from the energyzero integration.
what is try to achieve is as follows:

Every day at 00:00 so midnight i would like a sensor that tells me the following things, for the current day what are a the cheapest, moderate and expensive hours of the day.

So lets say when the energy prices are:

00:00 - 08:00 Cheapest
08:00 - 15:00 moderate
15:00 - 20:00 Expensive
20:00 - 00:00 moderate

the levels of cheapest moderate and expensive are predefined percentage of the range lowest and highest of that particular day.

what I would love to do is when the current time falls between set times e.g. 00:00 - 08:00 to activate my EV charger and that the charging stop (if fully charged or not) to stop at 08:00 and than continue again when another “Cheap” block comes along.

and to provide me with a notification on my phone to let me know a cheap hour block is starting in x amount of minutes.

I have attempted to build something like this even with the help of chatgpt, but no luck.

does anbody have an idea on how to something like this, or is willing to help me with this.

I know what I roughly need to do:

  • First get the prices including timestamp which are in the sensor.energy_zero_prices under attribute “prices”
  • then go trough the items in that list one by one saving values from first to the last on where the price is equal or below the threshold for cheapest.
  • then formating the first timestamp as xx:xx and same for the last timestamp
[
  {
    "timestamp": "2025-01-27 23:00:00+00:00",
    "price": 0.0616
  },
  {
    "timestamp": "2025-01-28 00:00:00+00:00",
    "price": 0.05573
  },
  {
    "timestamp": "2025-01-28 01:00:00+00:00",
    "price": 0.04295
  },
  {
    "timestamp": "2025-01-28 02:00:00+00:00",
    "price": 0.05138
  },
  {
    "timestamp": "2025-01-28 03:00:00+00:00",
    "price": 0.05777
  },
  {
    "timestamp": "2025-01-28 04:00:00+00:00",
    "price": 0.05707
  },
  {
    "timestamp": "2025-01-28 05:00:00+00:00",
    "price": 0.07782
  },
  {
    "timestamp": "2025-01-28 06:00:00+00:00",
    "price": 0.10355
  },
  {
    "timestamp": "2025-01-28 07:00:00+00:00",
    "price": 0.1276
  },
  {
    "timestamp": "2025-01-28 08:00:00+00:00",
    "price": 0.11747
  },
  {
    "timestamp": "2025-01-28 09:00:00+00:00",
    "price": 0.1002
  },
  {
    "timestamp": "2025-01-28 10:00:00+00:00",
    "price": 0.09804
  },
  {
    "timestamp": "2025-01-28 11:00:00+00:00",
    "price": 0.09962
  },
  {
    "timestamp": "2025-01-28 12:00:00+00:00",
    "price": 0.09831
  },
  {
    "timestamp": "2025-01-28 13:00:00+00:00",
    "price": 0.10122
  },
  {
    "timestamp": "2025-01-28 14:00:00+00:00",
    "price": 0.11393
  },
  {
    "timestamp": "2025-01-28 15:00:00+00:00",
    "price": 0.12246
  },
  {
    "timestamp": "2025-01-28 16:00:00+00:00",
    "price": 0.125
  },
  {
    "timestamp": "2025-01-28 17:00:00+00:00",
    "price": 0.1205
  },
  {
    "timestamp": "2025-01-28 18:00:00+00:00",
    "price": 0.11734
  },
  {
    "timestamp": "2025-01-28 19:00:00+00:00",
    "price": 0.08784
  },
  {
    "timestamp": "2025-01-28 20:00:00+00:00",
    "price": 0.07831
  },
  {
    "timestamp": "2025-01-28 21:00:00+00:00",
    "price": 0.069
  },
  {
    "timestamp": "2025-01-28 22:00:00+00:00",
    "price": 0.0555
  },
  {
    "timestamp": "2025-01-28 23:00:00+00:00",
    "price": 0.07579
  },
  {
    "timestamp": "2025-01-29 00:00:00+00:00",
    "price": 0.07191
  },
  {
    "timestamp": "2025-01-29 01:00:00+00:00",
    "price": 0.06574
  },
  {
    "timestamp": "2025-01-29 02:00:00+00:00",
    "price": 0.05994
  },
  {
    "timestamp": "2025-01-29 03:00:00+00:00",
    "price": 0.06401
  },
  {
    "timestamp": "2025-01-29 04:00:00+00:00",
    "price": 0.07023
  },
  {
    "timestamp": "2025-01-29 05:00:00+00:00",
    "price": 0.08116
  },
  {
    "timestamp": "2025-01-29 06:00:00+00:00",
    "price": 0.12695
  },
  {
    "timestamp": "2025-01-29 07:00:00+00:00",
    "price": 0.1362
  },
  {
    "timestamp": "2025-01-29 08:00:00+00:00",
    "price": 0.13045
  },
  {
    "timestamp": "2025-01-29 09:00:00+00:00",
    "price": 0.10505
  },
  {
    "timestamp": "2025-01-29 10:00:00+00:00",
    "price": 0.08925
  },
  {
    "timestamp": "2025-01-29 11:00:00+00:00",
    "price": 0.085
  },
  {
    "timestamp": "2025-01-29 12:00:00+00:00",
    "price": 0.08553
  },
  {
    "timestamp": "2025-01-29 13:00:00+00:00",
    "price": 0.08767
  },
  {
    "timestamp": "2025-01-29 14:00:00+00:00",
    "price": 0.10275
  },
  {
    "timestamp": "2025-01-29 15:00:00+00:00",
    "price": 0.13272
  },
  {
    "timestamp": "2025-01-29 16:00:00+00:00",
    "price": 0.14957
  },
  {
    "timestamp": "2025-01-29 17:00:00+00:00",
    "price": 0.165
  },
  {
    "timestamp": "2025-01-29 18:00:00+00:00",
    "price": 0.1486
  },
  {
    "timestamp": "2025-01-29 19:00:00+00:00",
    "price": 0.1353
  },
  {
    "timestamp": "2025-01-29 20:00:00+00:00",
    "price": 0.1221
  },
  {
    "timestamp": "2025-01-29 21:00:00+00:00",
    "price": 0.12235
  },
  {
    "timestamp": "2025-01-29 22:00:00+00:00",
    "price": 0.1001
  },
  {
    "timestamp": "2025-01-29 23:00:00+00:00",
    "price": 0.13134
  },
  {
    "timestamp": "2025-01-30 00:00:00+00:00",
    "price": 0.12579
  },
  {
    "timestamp": "2025-01-30 01:00:00+00:00",
    "price": 0.1119
  },
  {
    "timestamp": "2025-01-30 02:00:00+00:00",
    "price": 0.10448
  },
  {
    "timestamp": "2025-01-30 03:00:00+00:00",
    "price": 0.1
  },
  {
    "timestamp": "2025-01-30 04:00:00+00:00",
    "price": 0.10109
  },
  {
    "timestamp": "2025-01-30 05:00:00+00:00",
    "price": 0.13136
  },
  {
    "timestamp": "2025-01-30 06:00:00+00:00",
    "price": 0.14965
  },
  {
    "timestamp": "2025-01-30 07:00:00+00:00",
    "price": 0.1603
  },
  {
    "timestamp": "2025-01-30 08:00:00+00:00",
    "price": 0.15149
  },
  {
    "timestamp": "2025-01-30 09:00:00+00:00",
    "price": 0.1349
  },
  {
    "timestamp": "2025-01-30 10:00:00+00:00",
    "price": 0.12325
  },
  {
    "timestamp": "2025-01-30 11:00:00+00:00",
    "price": 0.11574
  },
  {
    "timestamp": "2025-01-30 12:00:00+00:00",
    "price": 0.1133
  },
  {
    "timestamp": "2025-01-30 13:00:00+00:00",
    "price": 0.12574
  },
  {
    "timestamp": "2025-01-30 14:00:00+00:00",
    "price": 0.14267
  },
  {
    "timestamp": "2025-01-30 15:00:00+00:00",
    "price": 0.15105
  },
  {
    "timestamp": "2025-01-30 16:00:00+00:00",
    "price": 0.17809
  },
  {
    "timestamp": "2025-01-30 17:00:00+00:00",
    "price": 0.17993
  },
  {
    "timestamp": "2025-01-30 18:00:00+00:00",
    "price": 0.16124
  },
  {
    "timestamp": "2025-01-30 19:00:00+00:00",
    "price": 0.14849
  },
  {
    "timestamp": "2025-01-30 20:00:00+00:00",
    "price": 0.13259
  },
  {
    "timestamp": "2025-01-30 21:00:00+00:00",
    "price": 0.12573
  },
  {
    "timestamp": "2025-01-30 22:00:00+00:00",
    "price": 0.1202
  }
]

for example when threshold is 0.06 and below then the values that need to processed are

 {
    "timestamp": "2025-01-28 00:00:00+00:00",
    "price": 0.05573
  },
  {
    "timestamp": "2025-01-28 01:00:00+00:00",
    "price": 0.04295
  },
  {
    "timestamp": "2025-01-28 02:00:00+00:00",
    "price": 0.05138
  },
  {
    "timestamp": "2025-01-28 03:00:00+00:00",
    "price": 0.05777
  },
  {
    "timestamp": "2025-01-28 04:00:00+00:00",
    "price": 0.05707
  },

which would eventually result in sensor output: 00:00 - 04:00

which would be the first block/window of hours to considered to be cheapest

Hope this gives some idea with what i would like to achieve.

Kinds regards,
Jordy

You can get most of this data using my macro.

Use the macro without start and end time to get the average price of the day.
Then use it to get the average prices of your time blocks. You can compare those to the day average to get your price inications.