Sensor for optimum energy hours based on Nordpool spot prices

I am writing a sensor template calculating the cheapest 3 consecutive hours based on Nordpool data for today and tomorrow. I will use this to decide when to start machines in my home. Once this is done I plan to calculate also the 2 consecutive hours and cheapest hour to use for machines not using 3 hours in total. Examples: dishwasher, washer etc.

I run into problem with my sensor template and I can not figure out what is wrong.

I get this error code:
Logger: homeassistant.config
Source: config.py:929
First occurred: 17:11:08 (1 occurrences)
Last logged: 17:11:08

Invalid config for [template]: invalid template (TemplateSyntaxError: expected token ‘end of statement block’, got ‘[’) for dictionary value @ data[‘sensor’][0][‘state’]. Got '{% set nordpool_price_json = { “kr”: state_attr(‘sensor.nordpool_kwh_se3_sek_3_10_025’, ‘today’) + state_attr(‘sensor.nordpool_kwh_se3_sek_3_10_025’, ‘tomorrow’), “hr”: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47] } %} {% set nordpool_cons_price_json = { “kr”: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47],… (See /config/configuration.yaml, line 112).

If I remove the for loop after json definitions it works fine, but not when I add the for loops again. I have tried a very simple for loop and then it is ok. Any help is apprecaited.

- sensor:
    - name: "Opt_pwr_hours"
      state: >
        {% set nordpool_price_json = {
          "kr": state_attr('sensor.nordpool_kwh_se3_sek_3_10_025', 'today') + state_attr('sensor.nordpool_kwh_se3_sek_3_10_025', 'tomorrow'),
          "hr": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47]
        } %}
        {% set nordpool_cons_price_json = {
          "kr": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47],
          "hr": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47]
        } %}
        {% for n in range (0,45) %}
          {% if nordpool_price_json.kr[n+2] != none %}
            {% set sum_of_three = nordpool_price_json.kr[n] + nordpool_price_json.kr[n+1] + nordpool_price_json.kr[n+2] %}
            {% set nordpool_cons_price_json.kr[n] = sum_of_three %}
          {% else %}
            {% set nordpool_cons_price_json.kr[n] = 1000 %}
          {% endif %}
        {% endfor %}
        {% set k = max(nordpool_cons_price_json.kr) %}
        {% set current_hour = now().hour %}  
        {% for n in range (current_hour,45) %}
          {% if nordpool_cons_price_json.kr[n] < k %}
            {% set best_3_cons_start_hour = n %}
          {% endif %}
        {% endfor %}
        {{best_3_cons_start_hour}}

Solved my issue by inspiration from others here in community. Thx.

- sensor:
    - name: "Opt_pwr_hours"
      state: >
        {% set nordpool = "sensor.nordpool_kwh_se3_sek_3_10_025" %}
        {% set nordpool_prices = namespace(numbers=[]) %}
        {% for n in state_attr(nordpool, 'raw_today') %}
          {% set nordpool_prices.numbers = nordpool_prices.numbers + [n.value] %}
        {% endfor %}
        {% for n in state_attr(nordpool, 'raw_tomorrow') %}
          {% set nordpool_prices.numbers = nordpool_prices.numbers + [n.value] %}
        {% endfor %}

        {% set nordpool_cons_prices = namespace(numbers=[]) %}
        {% for n in range (0,45) %}
          {% if nordpool_prices.numbers[n+2] != none %}
            {% set sum_of_three = (nordpool_prices.numbers[n] + nordpool_prices.numbers[n+1] + nordpool_prices.numbers[n+2])|round(2) %}
            {% set nordpool_cons_prices.numbers = nordpool_cons_prices.numbers + [sum_of_three] %}
          {% else %}
            {% set nordpool_cons_prices.numbers = nordpool_cons_prices.numbers + [1000] %}
          {% endif %}
        {% endfor %}

        {% set min_three_hour = min(nordpool_cons_prices.numbers) %}
        {% set best_3_cons_start_hour = nordpool_cons_prices.numbers.index(min_three_hour) %}    
        {{best_3_cons_start_hour|int}}
2 Likes

Hi Chrille

Is it supposed to be a sensor, right?
When I enter it, I get a 'Missing property “platform”:


Oh, and I changed it to energi data service from Malene.

Sorry, this code resides inside the template platform. Add “Template:” before the first sensor. Sorry for strange looking code below. Also changed some parts of the code. Now it returns the starting hour of the best or worse 3 hour period.

template:
    -  sensor:
        - name: "Worse_3_hours_today"
          icon: "mdi:home-lightning-bolt-outline"
          today. 21 means tomorrow evening at 21
          state: >
            {% set nordpoolSensor = "sensor.nordpool_kwh_se3_sek_3_10_025" %}
            {% set priceData = namespace(numbers=[]) %}
            {% for i in state_attr(nordpoolSensor,'raw_today') %}
              {% set priceData.numbers = priceData.numbers + [i.value] %}
            {% endfor %}
            {% set three_consecutive_hours = namespace(numbers=[]) %}
            {% set this_hour = now().hour %}
            {% for n in range(this_hour,22) %}
              {% set three_hour_sum = (priceData.numbers[n]+priceData.numbers[n+1]+priceData.numbers[n+2])|round(2) %}
              {% set three_consecutive_hours.numbers = three_consecutive_hours.numbers + [three_hour_sum] %}
            {% endfor %}
            {% set max_three_hour = max(three_consecutive_hours.numbers) %}
            {% set most_expensive_hours = this_hour + three_consecutive_hours.numbers.index(max_three_hour) %}
            {{ most_expensive_hours|int }} 
          
    -  sensor:
        - name: "Best_3_hours_today"
          icon: "mdi:home-lightning-bolt-outline"
          state: >
            {% set nordpoolSensor = "sensor.nordpool_kwh_se3_sek_3_10_025" %}
            {% set priceData = namespace(numbers=[]) %}
            {% for i in state_attr(nordpoolSensor,'raw_today') %}
              {% set priceData.numbers = priceData.numbers + [i.value] %}
            {% endfor %}
            {% set three_consecutive_hours = namespace(numbers=[]) %}
            {% set this_hour = now().hour %}
            {% for n in range(this_hour,22) %}
              {% set three_hour_sum = (priceData.numbers[n]+priceData.numbers[n+1]+priceData.numbers[n+2])|round(2) %}
              {% set three_consecutive_hours.numbers = three_consecutive_hours.numbers + [three_hour_sum] %}
            {% endfor %}
            {% set min_three_hour = min(three_consecutive_hours.numbers) %}
            {% set least_expensive_hours = this_hour + three_consecutive_hours.numbers.index(min_three_hour) %}    
            {{least_expensive_hours|int}}
          
    - sensor:
        - name: "Best_3_hours_tomorrow"
          icon: "mdi:home-lightning-bolt-outline"
          state: >
            {% set nordpoolSensor = "sensor.nordpool_kwh_se3_sek_3_10_025" %}
            {% set priceData = namespace(numbers=[]) %}
            {% for i in state_attr(nordpoolSensor,'raw_tomorrow') %}
              {% set priceData.numbers = priceData.numbers + [i.value] %}
            {% endfor %}
            {% set three_consecutive_hours = namespace(numbers=[]) %}
            {% for n in range(0,22) %}
              {% set three_hour_sum = (priceData.numbers[n]+priceData.numbers[n+1]+priceData.numbers[n+2])|round(2) %}
              {% set three_consecutive_hours.numbers = three_consecutive_hours.numbers + [three_hour_sum] %}
            {% endfor %}
            {% set min_three_hour = min(three_consecutive_hours.numbers) %}
            {% set least_expensive_hours = three_consecutive_hours.numbers.index(min_three_hour) %}    
            {{least_expensive_hours|int}}
          
    - sensor:
        - name: "Best_3_hours_today_and_tomorrow"
          icon: "mdi:home-lightning-bolt-outline"
          state: >
            {% set nordpool = "sensor.nordpool_kwh_se3_sek_3_10_025" %}
            {% set nordpool_prices = namespace(numbers=[]) %}
            {% for n in state_attr(nordpool, 'raw_today') %}
              {% set nordpool_prices.numbers = nordpool_prices.numbers + [n.value] %}
            {% endfor %}
            {% for n in state_attr(nordpool, 'raw_tomorrow') %}
              {% set nordpool_prices.numbers = nordpool_prices.numbers + [n.value] %}
            {% endfor %}

            {% set nordpool_cons_prices = namespace(numbers=[]) %}
            {% set this_hour = now().hour %}
            {% for n in range (this_hour,46) %}
              {% if nordpool_prices.numbers[n+2] != none %}
                {% set sum_of_three = (nordpool_prices.numbers[n] + nordpool_prices.numbers[n+1] + nordpool_prices.numbers[n+2])|round(2) %}
                {% set nordpool_cons_prices.numbers = nordpool_cons_prices.numbers + [sum_of_three] %}
              {% else %}
                {% set nordpool_cons_prices.numbers = nordpool_cons_prices.numbers + [1000] %}
              {% endif %}
            {% endfor %}

            {% set min_three_hour = min(nordpool_cons_prices.numbers) %}
            {% set best_3_cons_start_hour = this_hour + nordpool_cons_prices.numbers.index(min_three_hour) %}
            {% if best_3_cons_start_hour > 23 %}
              {% set best_3_cons_start_hour = best_3_cons_start_hour - 24 %}
            {% endif %}
            {{best_3_cons_start_hour|int}}
4 Likes

Hi @Chrille123
Ok, that is weird formatting there :smiley:
You should use the </> marking around the code, otherwise it’s really hard to read, or copy for that matter.
After beating around the indents, it seems to accept it.
But I think I figured it out, I was working with sensors and the template flatform, and you are working with template that holds sensors :smiley:

I tried changing it to my preferred, Energi Data Service, which wont have a probkem with automatic retreival.
But that didn’t work…

Nordpool does

raw_today: 
- start: '2022-10-06T00:00:00+02:00'
  end: '2022-10-06T01:00:00+02:00'
  value: 0.009
- start: '2022-10-06T01:00:00+02:00'
  end: '2022-10-06T02:00:00+02:00'
  value: 0.004
- start: '2022-10-06T02:00:00+02:00'
  end: '2022-10-06T03:00:00+02:00'
  value: 0.001

Where energi data service shows it as

raw_today: 
- hour: '2022-10-06T00:00:00+02:00'
  price: 1.35
- hour: '2022-10-06T01:00:00+02:00'
  price: 1.345
- hour: '2022-10-06T02:00:00+02:00'
  price: 1.342
- hour: '2022-10-06T03:00:00+02:00'
  price: 1.342

Any ideas?

Thanks for the </> tip. Prev message updated. I hope you manage to use the sensor in a good way.

1 Like

I guess you need to change from .value to .price using energi data service instead of nordpool.

1 Like

Ahh, that simple, and it seems to work, I was so focused on the timestamps :smiley:

This is your sensors for the Energi Data Service (which I can highly recommend)

- sensor:
    - name: "worst_3_hours_today"
      icon: "mdi:home-lightning-bolt-outline"
      state: >
        {% set energyPriceSensor = "sensor.energi_data_service" %}
        {% set priceData = namespace(numbers=[]) %}
        {% for i in state_attr(energyPriceSensor,'raw_today') %}
          {% set priceData.numbers = priceData.numbers + [i.price] %}
        {% endfor %}
        {% set three_consecutive_hours = namespace(numbers=[]) %}
        {% set this_hour = now().hour %}
        {% for n in range(this_hour,22) %}
          {% set three_hour_sum = (priceData.numbers[n]+priceData.numbers[n+1]+priceData.numbers[n+2])|round(2) %}
          {% set three_consecutive_hours.numbers = three_consecutive_hours.numbers + [three_hour_sum] %}
        {% endfor %}
        {% set max_three_hour = max(three_consecutive_hours.numbers) %}
        {% set most_expensive_hours = this_hour + three_consecutive_hours.numbers.index(max_three_hour) %}
        {{ most_expensive_hours | int }}
- sensor:
    - name: "best_3_hours_today"
      icon: "mdi:home-lightning-bolt-outline"
      state: >
        {% set energyPriceSensor = "sensor.energi_data_service" %}
        {% set priceData = namespace(numbers=[]) %}
        {% for i in state_attr(energyPriceSensor,'raw_today') %}
          {% set priceData.numbers = priceData.numbers + [i.price] %}
        {% endfor %}
        {% set three_consecutive_hours = namespace(numbers=[]) %}
        {% set this_hour = now().hour %}
        {% for n in range(this_hour,22) %}
          {% set three_hour_sum = (priceData.numbers[n]+priceData.numbers[n+1]+priceData.numbers[n+2])|round(2) %}
          {% set three_consecutive_hours.numbers = three_consecutive_hours.numbers + [three_hour_sum] %}
        {% endfor %}
        {% set min_three_hour = min(three_consecutive_hours.numbers) %}
        {% set least_expensive_hours = this_hour + three_consecutive_hours.numbers.index(min_three_hour) %}    
        {{ least_expensive_hours | int }}
- sensor:
    - name: "Best_3_hours_tomorrow"
      icon: "mdi:home-lightning-bolt-outline"
      state: >
        {% set energyPriceSensor = "sensor.energi_data_service" %}
        {% set priceData = namespace(numbers=[]) %}
        {% for i in state_attr(energyPriceSensor,'raw_tomorrow') %}
          {% set priceData.numbers = priceData.numbers + [i.price] %}
        {% endfor %}
        {% set three_consecutive_hours = namespace(numbers=[]) %}
        {% for n in range(0,22) %}
          {% set three_hour_sum = (priceData.numbers[n]+priceData.numbers[n+1]+priceData.numbers[n+2])|round(2) %}
          {% set three_consecutive_hours.numbers = three_consecutive_hours.numbers + [three_hour_sum] %}
        {% endfor %}
        {% set min_three_hour = min(three_consecutive_hours.numbers) %}
        {% set least_expensive_hours = three_consecutive_hours.numbers.index(min_three_hour) %}    
        {{ least_expensive_hours | int }}
- sensor:
    - name: "Best_3_hours_today_and_tomorrow"
      icon: "mdi:home-lightning-bolt-outline"
      state: >
        {% set energyPrice = "sensor.energi_data_service" %}
        {% set energyPrice_prices = namespace(numbers=[]) %}
        {% for n in state_attr(energyPrice, 'raw_today') %}
          {% set energyPrice_prices.numbers = energyPrice_prices.numbers + [n.price] %}
        {% endfor %}
        {% for n in state_attr(energyPrice, 'raw_tomorrow') %}
          {% set energyPrice_prices.numbers = energyPrice_prices.numbers + [n.price] %}
        {% endfor %}
        {% set energyPrice_cons_prices = namespace(numbers=[]) %}
        {% set this_hour = now().hour %}
        {% for n in range (this_hour,46) %}
          {% if energyPrice_prices.numbers[n+2] != none %}
            {% set sum_of_three = (energyPrice_prices.numbers[n] + energyPrice_prices.numbers[n+1] + energyPrice_prices.numbers[n+2])|round(2) %}
            {% set energyPrice_cons_prices.numbers = energyPrice_cons_prices.numbers + [sum_of_three] %}
          {% else %}
            {% set energyPrice_cons_prices.numbers = energyPrice_cons_prices.numbers + [1000] %}
          {% endif %}
        {% endfor %}
        {% set min_three_hour = min(energyPrice_cons_prices.numbers) %}
        {% set best_3_cons_start_hour = this_hour + energyPrice_cons_prices.numbers.index(min_three_hour) %}
        {% if best_3_cons_start_hour > 23 %}
          {% set best_3_cons_start_hour = best_3_cons_start_hour - 24 %}
        {% endif %}
        {{ best_3_cons_start_hour | int }}

Thankyou @Chrille123

2 Likes

I noticed that thease template sensor related to tomorrow does not work anymore when raw_tomorrow is empty. And it looks like it now becomes empty after midnight, and stay empty untill next update is comming for tomorrow prices.
Anyone done changes to the template to fix this? My thought is eithere to fill raw data if its empty or do different If based on raw_tomorrow is filled.
I know what i would like to do but not how to translate it to code.

Anyone could help out or share what they done

Thanks in advance

edit
Did some own steel with pride changes and looks to be working if anyone have same problem:

  - sensor:
      - name: "Best_3_hours_today_and_tomorrow_energy"
        icon: "mdi:home-lightning-bolt-outline"
        state: >
          {% set energyPrice = "sensor.energi_data_service" %}
          {% set energyPrice_prices = namespace(numbers=[]) %}
          {% for n in state_attr(energyPrice, 'raw_today') %}
          {% set energyPrice_prices.numbers = energyPrice_prices.numbers + [n.price] %}
          {% endfor %}
          {% if state_attr(energyPrice, 'raw_tomorrow') != none %}
            {% for n in state_attr(energyPrice, 'raw_tomorrow') %}
            {% set energyPrice_prices.numbers = energyPrice_prices.numbers + [n.price] %}
            {% endfor %}
          {% endif %}

          {% set energyPrice_cons_prices = namespace(numbers=[]) %}
          {% set this_hour = now().hour %}
          {% if state_attr(energyPrice, 'raw_tomorrow') != none %}
            {% for n in range (this_hour,30) %}
              {% if energyPrice_prices.numbers[n+2] != none %}
                {% set sum_of_three = (energyPrice_prices.numbers[n] + energyPrice_prices.numbers[n+1] + energyPrice_prices.numbers[n+2])|round(2) %}
                {% set energyPrice_cons_prices.numbers = energyPrice_cons_prices.numbers + [sum_of_three] %}
              {% else %}
                {% set energyPrice_cons_prices.numbers = energyPrice_cons_prices.numbers + [1000] %}
              {% endif %}
            {% endfor %}
          {% else %}
            {% for n in range (this_hour,6) %}
              {% if energyPrice_prices.numbers[n+2] != none %}
                {% set sum_of_three = (energyPrice_prices.numbers[n] + energyPrice_prices.numbers[n+1] + energyPrice_prices.numbers[n+2])|round(2) %}
                {% set energyPrice_cons_prices.numbers = energyPrice_cons_prices.numbers + [sum_of_three] %}
              {% else %}
                {% set energyPrice_cons_prices.numbers = energyPrice_cons_prices.numbers + [1000] %}
              {% endif %}
            {% endfor %}
          {% endif %}
          {% set min_three_hour = min(energyPrice_cons_prices.numbers) %}
          {% set best_3_cons_start_hour = this_hour + energyPrice_cons_prices.numbers.index(min_three_hour) %}
          {% if best_3_cons_start_hour > 23 %}
          {% set best_3_cons_start_hour = best_3_cons_start_hour - 24 %}
          {% endif %}
          {{ best_3_cons_start_hour | int }}
2 Likes

Great template! Let’s say this spits out 1 (ie. 1 am sharp), how do I convert this to a trigger that homeassistant can use? ie. in date_time format?

edit:

{{ ((((now().timestamp()) / 86400 ) | int) * 86400 + (best_3_cons_start_hour * 3600)) | timestamp_custom('%Y-%m-%dT%H:%M:%S' , local=False)}}

this converts from int to timestamp

Thanks for the update of in to timestamp

Hade some general trouble with Nordpool templates from day to day with same logic when it comes to just compare use
{{ now().hour == int(states.sensor.Best_3_hours_today_and_tomorrow.state) }}
So i decided to create a scheduler that runs when prices are updated for comming day with a helper.
Then used it to schedule the session. This ofcourse does not work for continues validation, but for some schedule session it works. Like charging batteries or somthing that only runes ones a day.

service: input_datetime.set_datetime
data:
  time: "{{states('sensor.Best_3_hours_today_and_tomorrow')}}:05"
target:
  entity_id: input_datetime.car_scheduler

This can be usfull if people have not found it before in othere threads.

could example be used for more online planing where the usage windows is more frequent then once a day

1 Like

Apricate you sharing your code. I can’t wrap my head around this code, wanting to get the average price { {lowest_avreage_price_3hours }} for the 3 cheapest hours. Please advise.