Cheapest Energy Hours - Jinja macro for dynamic energy prices

To answer your last question:
Template triggers are not limited to limited triggers, so that shouldn’t be an issue.

Your first trigger should work. Did the attribute change from false to true? A trigger won’t trigger if it’s already true, it has to become true.
So in case of a template trigger, the result of the template has to change from false to true

Sorry for the late response.
I can only test in real time.

I have created a test automation and see that the automation is only started when the cheap hour starts.
I also see that the “from_state” state goes from “off” to the “to_state” state “off” and does not get the expected “on” state.
When the cheap hour is over, nothing happens.

Below is the test automation with associated Traces:

alias: 0-Test trigger
description: ""
triggers:
  - trigger: template
    value_template: "{{ state_attr('binary_sensor.cheapest_single_xhrs_17_00_17_00', '5h') }}"
conditions: []
actions:
  - variables:
      test: "{{ trigger.to_state.state }}"
trace:
  stored_traces: 24
mode: single
this:
  entity_id: automation.0_test_trigger
  state: 'on'
  attributes:
    id: '1766573149466'
    last_triggered: '2025-12-25T04:00:00.220944+00:00'
    mode: single
    current: 0
    friendly_name: 0-Test trigger
  last_changed: '2025-12-24T18:08:12.415112+00:00'
  last_reported: '2025-12-25T04:00:00.221433+00:00'
  last_updated: '2025-12-25T04:00:00.221433+00:00'
  context:
    id: 01KD9TKQPWBZ393NWM1XSY0WQC
    parent_id: 01KD9TKQPKWAAHP2CGG6WQ0N2G
    user_id: null
trigger:
  platform: template
  entity_id: binary_sensor.cheapest_single_xhrs_17_00_17_00
  from_state:
    entity_id: binary_sensor.cheapest_single_xhrs_17_00_17_00
    state: 'off'
    attributes:
      6h: false
      5h: false
      4h: false
      3h: false
      2h: false
      1h: false
      friendly_name: Cheapest single Xhrs (17:00-17:00)
    last_changed: '2025-12-23T18:22:07.815231+00:00'
    last_reported: '2025-12-25T06:00:00.211121+00:00'
    last_updated: '2025-12-25T06:00:00.211121+00:00'
    context:
      id: 01KDA1FEYKT4E863YEQGWX5G0H
      parent_id: null
      user_id: null
  to_state:
    entity_id: binary_sensor.cheapest_single_xhrs_17_00_17_00
    state: 'off'
    attributes:
      6h: true
      5h: true
      4h: true
      3h: true
      2h: true
      1h: false
      friendly_name: Cheapest single Xhrs (17:00-17:00)
    last_changed: '2025-12-23T18:22:07.815231+00:00'
    last_reported: '2025-12-25T10:00:00.211164+00:00'
    last_updated: '2025-12-25T10:00:00.211164+00:00'
    context:
      id: 01KDAF6XEK2897J0HK2MXSA7TR
      parent_id: null
      user_id: null
  id: '0'
  idx: '0'
  alias: null
  for: null
  description: binary_sensor.cheapest_single_xhrs_17_00_17_00 via template

Is there a particular reason that you want to use attributes inside a binary sensor?
I’m doing something similar but with multiple binary sensors, one for each block of hours, that seems to work fine. I’ve also created a binary sensor linked to a HA input number. If I want to use the cheapest block of 2.5 hours, I simply move the slider in HA to 2.5.

- binary_sensor:
    - unique_id: xyz
      name: Boiler Active
      state: >
        {% set sensor = 'sensor.nordpool_kwh' %}
        {% set hours = states('input_number.boiler_hours') | float(0)  %}
        {% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
        {{ cheapest_energy_hours(sensor=sensor, hours=hours, mode='is_now', split=true, use_hourly_avg=true) }}    

You fixed the state of the binary sensor to off by using state: false
So your binary sensor will never change state, only the attribute values will change.

Based on the trace your trigger worked fine

A trigger should be generated just when the attribute value changes. This is what I want. Not the state of the binary sensor.
There will be cheap hours again tonight, so hopefully the automation below will be triggered.

alias: 1-Test trigger
description: ""
triggers:
  - trigger: state
    entity_id: binary_sensor.cheapest_single_xhrs_17_00_17_00
    attribute: 5h
    from: [false, true]
    to: [true, false]
conditions: []
actions:
  - variables:
      test0: "{{ trigger.to_state.attributes }}"
      test1: "{{ trigger.to_state.state }}"
trace:
  stored_traces: 24
mode: single

I create the sensors the way you do, and it works well.
However, I don’t want six separate sensors, but just one sensor with six different hours (attributes: 1h to 6h).
One sensor, one entity_id; this keeps everything organized within HA.

the variable test1 will always return "off" simply because your binary template sensor is always off

If you want the attribute value, you need to use {{ trigger.to_state.attributes['5h'] }}

Yes… I managed to get the trigger working in the automation, simply by adding the “from” and “to” configuration keys to the trigger setup.

I’m now using the trigger below:

triggers:
  - trigger: state
    entity_id: binary_sensor.cheapest_single_xhrs_17_00_17_00
    attribute: 5h
    from: [false, true]
    to: [true, false]

Thanks for your help.

Ok, call me whatever you want, but I have no clue how to use this.

I have imported the jinja using hacs, and I have imported the blueprint.

According to the docs, I should now create a sensor using the blueprint? But where do I paste the example code at the bottom of that screen?

And then? Setup another sensor that trigger the start of the dishwasher at a specific time, as described here?

Steep leaning curve here :stuck_out_tongue:

Which integration are you using to get the prices?

Nord Pool…

Later maybe Frank…

So the Nordpool core integration? Not the custom one from HACS? In that case you need the blueprint. You can place the example code in your configuration.yaml.

Allright… got that working. I now have a helper…

So now I add the update code snippet from here?

 sensor:
      - unique_id: f8853830-bf81-46d4-85a9-379c25c37c23
        name: Dishwasher Start Time
        device_class: timestamp
        state: >
          {%- set sensor = 'sensor.nordpool_ceh_prices' -%}
          {% from "cheapest_energy_hours.jinja" import cheapest_energy_hours %}
          {{ cheapest_energy_hours(sensor=sensor, hours=2.5, start='22:00', end='08:00', look_ahead=true, include_tomorrow=true) }}
        availability: "{{ is_state('binary_sensor.dishwasher_door', 'on') and is_state('binary_sensor.dishwasher_remote_active', 'on')  }}"

# automation
# the door sensor is covered in the sensor now, no need to check on it in the automation
automation:
  - id: 3f61bd8e-81b8-4d62-8d5d-96d8596e29b1
    alias: Start Dishwasher During Night
    trigger:
      - platform: time
        at: sensor.dishwasher_start_time
    action:
      - service: swith.turn_on
        target:

Yes, but you’re missing the trigger part of the template sensor.
And the automation can be created in the GUI as well

Yeah… can you help me understand what this part is doing?

template:
  - trigger:
      -  platform: time
         at:
           - '19:30'
           - '20:30'
           - '21:30'

It fires the template to update the scheduled times (sensor.dishwasher_start_time)? But why only at these three moments? And isn’t the sensor updated anyway as soon as availability changes (the door of the dishwasher is closed)?

Anyway, the template sensor became unavailable at 00:00. I’m now seeing the following in the template editor (developer tools)

{%- set sensor = 'sensor.nordpool_ceh_prices' -%}
          {% from "cheapest_energy_hours.jinja" import cheapest_energy_hours %}
          {{ cheapest_energy_hours(sensor=sensor, hours=4.5, start='22:00', end='08:00', look_ahead=true, include_tomorrow=true) }}

Result:

1 error: 4 datapoints (2.0 hours) in selection, where 9 (4.5 hours) are expected
Result type: string

This template updates at the start of each minute.

This template listens for the following state changed events:

Entity: sensor.nordpool_ceh_prices

The idea is that you prepare the dishwasher before 22:00 and then the macro will determine the best time to start the dishwasher.

The automation will then use this to actually start it.

You are now getting errors because the template will evaluate every minute, and after midnight it will use only the evening of the new day. At that moment the prices for the next day aren’t available yet, so there isn’t enough information for the complete direction of the dishwasher run.

The trigger prevents that, as the template will only be evaluated at the trigger times.

Ah, that clarifies things. Thanks for this :slight_smile:

I adjusted the template accordingly. Let’s see what happens tonight!

I’m using a custom EnergyZero integration, and today I managed to get the energy prices using their new API, but somehow I can’t get the macro to work.

Below is the sensor data as I receive it.


base:
  - start: "2026-01-03T23:00:00Z"
    end: "2026-01-04T00:00:00Z"
    price:
      value: "0.0888225"
  - start: "2026-01-04T00:00:00Z"
    end: "2026-01-04T01:00:00Z"
    price:
      value: "0.08533"
  - start: "2026-01-04T01:00:00Z"
    end: "2026-01-04T02:00:00Z"
    price:
      value: "0.0850425"
  - start: "2026-01-04T02:00:00Z"
    end: "2026-01-04T03:00:00Z"
    price:
      value: "0.083745"
  - start: "2026-01-04T03:00:00Z"
    end: "2026-01-04T04:00:00Z"
    price:
      value: "0.0825575"
  - start: "2026-01-04T04:00:00Z"
    end: "2026-01-04T05:00:00Z"
    price:
      value: "0.0834075"
  - start: "2026-01-04T05:00:00Z"
    end: "2026-01-04T06:00:00Z"
    price:
      value: "0.0838"
  - start: "2026-01-04T06:00:00Z"
    end: "2026-01-04T07:00:00Z"
    price:
      value: "0.0864975"
  - start: "2026-01-04T07:00:00Z"
    end: "2026-01-04T08:00:00Z"
    price:
      value: "0.0881325"
  - start: "2026-01-04T08:00:00Z"
    end: "2026-01-04T09:00:00Z"
    price:
      value: "0.0943"
  - start: "2026-01-04T09:00:00Z"
    end: "2026-01-04T10:00:00Z"
    price:
      value: "0.094755"
  - start: "2026-01-04T10:00:00Z"
    end: "2026-01-04T11:00:00Z"
    price:
      value: "0.0908775"
  - start: "2026-01-04T11:00:00Z"
    end: "2026-01-04T12:00:00Z"
    price:
      value: "0.0895425"
  - start: "2026-01-04T12:00:00Z"
    end: "2026-01-04T13:00:00Z"
    price:
      value: "0.0867775"
  - start: "2026-01-04T13:00:00Z"
    end: "2026-01-04T14:00:00Z"
    price:
      value: "0.091825"
  - start: "2026-01-04T14:00:00Z"
    end: "2026-01-04T15:00:00Z"
    price:
      value: "0.09292"
  - start: "2026-01-04T15:00:00Z"
    end: "2026-01-04T16:00:00Z"
    price:
      value: "0.0992275"
  - start: "2026-01-04T16:00:00Z"
    end: "2026-01-04T17:00:00Z"
    price:
      value: "0.118425"
  - start: "2026-01-04T17:00:00Z"
    end: "2026-01-04T18:00:00Z"
    price:
      value: "0.12118"
  - start: "2026-01-04T18:00:00Z"
    end: "2026-01-04T19:00:00Z"
    price:
      value: "0.119715"
  - start: "2026-01-04T19:00:00Z"
    end: "2026-01-04T20:00:00Z"
    price:
      value: "0.11245"
  - start: "2026-01-04T20:00:00Z"
    end: "2026-01-04T21:00:00Z"
    price:
      value: "0.0996875"
  - start: "2026-01-04T21:00:00Z"
    end: "2026-01-04T22:00:00Z"
    price:
      value: "0.09561"
  - start: "2026-01-04T22:00:00Z"
    end: "2026-01-04T23:00:00Z"
    price:
      value: "0.08921"
  - start: "2026-01-04T23:00:00Z"
    end: "2026-01-05T00:00:00Z"
    price:
      value: "0.0924525"
  - start: "2026-01-05T00:00:00Z"
    end: "2026-01-05T01:00:00Z"
    price:
      value: "0.0888475"
  - start: "2026-01-05T01:00:00Z"
    end: "2026-01-05T02:00:00Z"
    price:
      value: "0.0865075"
  - start: "2026-01-05T02:00:00Z"
    end: "2026-01-05T03:00:00Z"
    price:
      value: "0.0863125"
  - start: "2026-01-05T03:00:00Z"
    end: "2026-01-05T04:00:00Z"
    price:
      value: "0.087495"
  - start: "2026-01-05T04:00:00Z"
    end: "2026-01-05T05:00:00Z"
    price:
      value: "0.0908575"
  - start: "2026-01-05T05:00:00Z"
    end: "2026-01-05T06:00:00Z"
    price:
      value: "0.1090225"
  - start: "2026-01-05T06:00:00Z"
    end: "2026-01-05T07:00:00Z"
    price:
      value: "0.1293125"
  - start: "2026-01-05T07:00:00Z"
    end: "2026-01-05T08:00:00Z"
    price:
      value: "0.151175"
  - start: "2026-01-05T08:00:00Z"
    end: "2026-01-05T09:00:00Z"
    price:
      value: "0.15215"
  - start: "2026-01-05T09:00:00Z"
    end: "2026-01-05T10:00:00Z"
    price:
      value: "0.1443"
  - start: "2026-01-05T10:00:00Z"
    end: "2026-01-05T11:00:00Z"
    price:
      value: "0.135195"
  - start: "2026-01-05T11:00:00Z"
    end: "2026-01-05T12:00:00Z"
    price:
      value: "0.132545"
  - start: "2026-01-05T12:00:00Z"
    end: "2026-01-05T13:00:00Z"
    price:
      value: "0.13166"
  - start: "2026-01-05T13:00:00Z"
    end: "2026-01-05T14:00:00Z"
    price:
      value: "0.136605"
  - start: "2026-01-05T14:00:00Z"
    end: "2026-01-05T15:00:00Z"
    price:
      value: "0.1470175"
  - start: "2026-01-05T15:00:00Z"
    end: "2026-01-05T16:00:00Z"
    price:
      value: "0.176"
  - start: "2026-01-05T16:00:00Z"
    end: "2026-01-05T17:00:00Z"
    price:
      value: "0.2210975"
  - start: "2026-01-05T17:00:00Z"
    end: "2026-01-05T18:00:00Z"
    price:
      value: "0.213585"
  - start: "2026-01-05T18:00:00Z"
    end: "2026-01-05T19:00:00Z"
    price:
      value: "0.1828"
  - start: "2026-01-05T19:00:00Z"
    end: "2026-01-05T20:00:00Z"
    price:
      value: "0.1470325"
  - start: "2026-01-05T20:00:00Z"
    end: "2026-01-05T21:00:00Z"
    price:
      value: "0.1291025"
  - start: "2026-01-05T21:00:00Z"
    end: "2026-01-05T22:00:00Z"
    price:
      value: "0.12385"
  - start: "2026-01-05T22:00:00Z"
    end: "2026-01-05T23:00:00Z"
    price:
      value: "0.1113"
  - start: "2026-01-05T23:00:00Z"
    end: "2026-01-06T00:00:00Z"
    price:
      value: "0.104695"
  - start: "2026-01-06T00:00:00Z"
    end: "2026-01-06T01:00:00Z"
    price:
      value: "0.1046"
  - start: "2026-01-06T01:00:00Z"
    end: "2026-01-06T02:00:00Z"
    price:
      value: "0.101545"
  - start: "2026-01-06T02:00:00Z"
    end: "2026-01-06T03:00:00Z"
    price:
      value: "0.1000875"
  - start: "2026-01-06T03:00:00Z"
    end: "2026-01-06T04:00:00Z"
    price:
      value: "0.1024525"
  - start: "2026-01-06T04:00:00Z"
    end: "2026-01-06T05:00:00Z"
    price:
      value: "0.107"
  - start: "2026-01-06T05:00:00Z"
    end: "2026-01-06T06:00:00Z"
    price:
      value: "0.1212875"
  - start: "2026-01-06T06:00:00Z"
    end: "2026-01-06T07:00:00Z"
    price:
      value: "0.1384975"
  - start: "2026-01-06T07:00:00Z"
    end: "2026-01-06T08:00:00Z"
    price:
      value: "0.164185"
  - start: "2026-01-06T08:00:00Z"
    end: "2026-01-06T09:00:00Z"
    price:
      value: "0.16882"
  - start: "2026-01-06T09:00:00Z"
    end: "2026-01-06T10:00:00Z"
    price:
      value: "0.1548425"
  - start: "2026-01-06T10:00:00Z"
    end: "2026-01-06T11:00:00Z"
    price:
      value: "0.147265"
  - start: "2026-01-06T11:00:00Z"
    end: "2026-01-06T12:00:00Z"
    price:
      value: "0.13384"
  - start: "2026-01-06T12:00:00Z"
    end: "2026-01-06T13:00:00Z"
    price:
      value: "0.125725"
  - start: "2026-01-06T13:00:00Z"
    end: "2026-01-06T14:00:00Z"
    price:
      value: "0.12532"
  - start: "2026-01-06T14:00:00Z"
    end: "2026-01-06T15:00:00Z"
    price:
      value: "0.1337975"
  - start: "2026-01-06T15:00:00Z"
    end: "2026-01-06T16:00:00Z"
    price:
      value: "0.1449675"
  - start: "2026-01-06T16:00:00Z"
    end: "2026-01-06T17:00:00Z"
    price:
      value: "0.1529925"
  - start: "2026-01-06T17:00:00Z"
    end: "2026-01-06T18:00:00Z"
    price:
      value: "0.1482675"
  - start: "2026-01-06T18:00:00Z"
    end: "2026-01-06T19:00:00Z"
    price:
      value: "0.1391175"
  - start: "2026-01-06T19:00:00Z"
    end: "2026-01-06T20:00:00Z"
    price:
      value: "0.1215975"
  - start: "2026-01-06T20:00:00Z"
    end: "2026-01-06T21:00:00Z"
    price:
      value: "0.08681"
  - start: "2026-01-06T21:00:00Z"
    end: "2026-01-06T22:00:00Z"
    price:
      value: "0.08719"
  - start: "2026-01-06T22:00:00Z"
    end: "2026-01-06T23:00:00Z"
    price:
      value: "0.0772875"
unit_of_measurement: EUR/kWh
friendly_name: Electricity market price (hour price)

I use this macro, but I do not how to set up the value_key= key.

{% from 'cheapest_energy_hours.jinja' import cheapest_energy_hours %}
{{ cheapest_energy_hours(sensor='sensor.electricity_market_price_hour_price', attr_today='base', time_key='start', value_key='price[value]') }}

1 error: Value key 'price[value]' not found in data

There is no support for nested values like this.
You could create a template sensor which loops over the items and creates a new list without the nested value.

Ouch. I wasn’t expecting this answer.
I was hoping the macro would support nested values.

Would you mind helping me figure out how best to create such a template sensor? Please?