Home Assistant: Advanced Nord Pool Cheapest Hours automation (non-sequential)

I guess there are people that have been waiting for this extension of cheapest hours automation for a while now and it’s finally here: non-sequential version cheapest hours package!

This article is continuation to Home Assistant: Sequential cheapest hours automation so I’d really suggest to read that one first. This article also points to it in couple of sections.

Features of the package

The automation I’m presenting here finds the cheapest hours from Nord Pool sensor and creates local calendar entries on cheapest hours. This Home Assistant package also acts upon cheapest hours calendar events and performs action when cheapest hours event is started. And this is the non-sequential version that can create multiple entries on a day!

Here’s the list this automation package implements (feature list):

  • Calculate number of cheapest hours when Nord Pool spot prices are received
  • Support for two days (today + tomorrow)
  • Rather easy to configure
  • Once prices are received from the Nord Pool, calendar events are created
  • Possible sequential hours are merged with each other
  • Devices(s) will start when ‘cheapest hours’ -calendar event begins
  • Device(s) will stop when ‘cheapest hours’ -calendar event is over
  • If prices are not fetched by 23:15, create a static cheapest hours event (sequential) from configured start time and end after number of hours specified. This will provide us a failsafe that no critical devices are being left out of electricity (e.g. water boiler).

Pre-requisites

This is where I’m going to refer previous cheapest hours articles.

If you have not used the advanced cheapest hours package earlier, you should start by creating a local calendar for Home Assistant and integrating Nord Pool into Home Assistant.

Once these steps are done, continue forward.

Setting up the package

Advanced cheapest hours package can be found from my Cheapest hours GitHub page, so download it and copy it on your Home Assistant ‘config‘ folder.

After copied, find the homeassistant: -block from your configurations.yaml file and add the package include on it. If you don’t have homeassistant: -block, just copy the whole block in any root level of configuration.yaml file:

homeassistant:
  packages:
    cheapest_hours_non_sequential: !include advanced_cheapest_hours_non_sequential.yaml

More details about Home Assistant packages can be found from here. Or if in doubt, leave a comment on the comment field and I’ll try to help you out 🙂

Configuration

As usual in my creations, I’ve added #CHANGE-ME comments to places where you need to configure your own data. In the main template sensor configurations there are following entries that needs configuration:

# Amount of cheapest hours in search
number_of_hours: 4
# Search starting hour
first_hour: 21
# Search ending hour
last_hour: 08
# Is the first_hour today (true / false). If false, first_hour needs to be before last_hour.
starting_today: true
# Nord pool sensor id. Check it ouf from your integrations page!
sensor: sensor.nordpool_kwh_fi_eur_3_10_024 
# If nordpool fetch fails, starting time to make the calendar entry
fail_safe_starting: '00:00' 

Along with the main configuration, there are few several places to make changes in the code. Most of them are ‘sensorId‘ values that should refer to the template sensor defined earlier. No need to change these unless you are actually making multiple separate automations or just want to change the names.

However, there’s one crucial thing to change, the actions what actually need to be done when the cheapest hours starts. So, do what-ever-you-like at this point:

then:
# CHANGE-ME: Actions to do when cheap hour starts
  - service: input_boolean.turn_on
    entity_id: input_boolean.test_switch
else:
# CHANGE-ME: Actions to do when cheap hour ends
  - service: input_boolean.turn_off
    entity_id: input_boolean.test_switch

Creating multiple automations

Ok, so you might want to have multiple automations for different amount of hours or different configurations? At that point, you need to do these steps:

  1. Make a copy the template sensor at the beginning and set unique_id + name to something suitable
  2. Create copy of first automation (cheapest_hours_non_sequential_calendar_trigger) and rename + make proper actions to do what-ever-you-like. Also change the sensorId to refer your newly created template sensor.
  3. Change cheapest_hours_non_sequential_set_sequence automation to contain another sensorId (name as you like) and create another action to call script.cheapest_hours_create_multi_calendar with this new data
  4. Change cheapest_hours_non_sequential_failsafe to contain another sensorId as in previous step and create another failsafe action to call script.cheapest_hours_create_multi_calendar with this new data

That’s it, you should be able to create multiple action by doing those steps. I will be adding an example to the GitHub with multiple entries some rainy day.

What’s coming up next…

One step closer to save energy. However, using configuring multiple entries and automations can be time consuming and so far my cheapest hours automations differs from each others more or less. I’m planning to implement a v2 version of all of these that contain some common code so that those scripts + helpers could be used between each other.

And for more, a web configuration utility is on my TODO list as well. Wouldn’t hold my breath for it soon though, it might take some time to implement a full web app for building these with my spare time.


Did you find this guide helpful? You can keep the blog going by bidding me a coffee!

63 Replies to “Home Assistant: Advanced Nord Pool Cheapest Hours automation (non-sequential)”

  1. Hello Toni,

    Amazing job!
    I did setup it for Sweden. Work like a charm, thank you.
    I was thinking how to add possibility to check maximum cost for hour as well. For example adding for cheapest hours, but with cost no more that 1 kr. I will try my best to add it to code, and very appreciate any advice.
    Thank you!

  2. I found that you already did cost limits in other article:-) One more time: just a great job!
    I have few questions: one probably super silly – I don’t understand how to run it immediately (for testing) after change some attributes itc. I restart HA but it not helps. Calendar’s events will appear only after some time.
    Thank you!

    1. Hi and thanks for the nice feedback!

      You need to turn off the ‘cheapest hours set’ input_boolean to be able to run the automation again at same day (it’s automatically set off every night at 01:15).
      This input boolean tells the automation if the calendar events has been made already for the specific day and prevents the creation of duplicate entries.

  3. Nicely done! Will it conflict if using the sequential in parallel with the non-sequential?
    Regards.

  4. Hi Toni,

    The script runs really good! The explanation was so good that for a HA noob it was good to implemented it! Thanks for this.

    Beside the cheapest hours I also would like to know which are the most expensive hours. That’s why I try to understand how you’re scripts works and what needs to be changed to realize this. What’s lines do I need to change so that the script point to the expensive hours instead of the cheapest hours?

    Regards,

    1. Hi DJ!

      Actually I just approved a comment written by Jarkko that explains this quite well. So please see the next comment 🙂

  5. Thanks a lot Toni. Great work!

    I have bee using this and the previous sequential version as well.

    Btw… Copied your yaml and replaced all the “cheapest” with “expensive” and added reverse data sorting “sort(reverse=true, attribute=’price’)”. So now I have also the most expensive hours in the calendar and I have been using that to prevent some appliances to run during the most expensive hours. Couldn’t have ever done this by myself.

      1. Hi, I am struggeling to get the expensive version going as well. Any additional pointers?

        1. found it, needs to be a proper boolean i.e. reverse=True (and not reverse=’true’).

    1. Thanks for this wonderful packages. Just love them! I’m now trying to implement some expensive hours.

      I get it with replace the cheap to eg expensive as per suggestion to make a unique ID / descriptions.

      But where should this “sort(reverse=true, attribute=’price’)” be added? As you write add I guess it should not replace “sort(attribute=’price’)”? If i simple replace sort the sensor becomes unavailable soo i my guess is something is wrong 🙂

      FYI I’m using the advanced_cheapest_hours_non_sequential.yaml as template.

      Any feedback would be highly appreicated. Thanks

  6. Hi Toni,

    I got this script working well so good work!

    However I would like to take step further and try more advanced approach based on your script. In my HA Configuration.yaml I have made myself sensor that calculates the required heating hours of my house (based on feels like temperature that is calculated based on next 12 hours temperature forecast). The sensor will provide me number of hours 1-24. 1 if it is so warm outside 1h to heat up my boiler hot water would be enough and 24 if it is over -20 degrees and I need the heating of the house constantly running.

    As taking first steps in HA and Jinja2 programing and programing in general is not something I consider my strenght can you recomend how to use my sensor as input to your script? My first idea was to replace number of hours attribute with the sensor value. Would that approach work?

    I can get my sensor hour number if in HA developers tools Template screen if I enter {{ states(‘sensor.kyttevajadus’) }}. I tried to replace in your code line 21 and 75 with {%- set numberOfHours = (states(‘sensor.kyttevajadus’) | int) -%} with no luck as I have not any calendar entries created in past 24h. Any ideas what to try next?

    1. Hi Tiit!

      Maybe you are thinking a bit too complex now. Most probably everything would work out just by adding the value from sensor directly to the attributes (line 13). Like this:
      number_of_hours: "{{ states.sensor.kyttevajadus.state | int(0) }}"

      Let me know if it works out once you’ve tested 🙂

      1. Hi Toni,

        this workd perfectly, thank you!

        The another issue I have noted (that I guess is more feature request than configuration issue) is that as I calculate cheapest hours next day 24h I get around 5 calender events and around 2 of them tend to be very short (1 hour long). It do not make much sense to turn my heating on eco mode and back for very short period. Especially when the price difference is quite small for more expensive hours. If I would be able to configure my calendar events in more detail – like determine the minimum lenght of calendar event (3 hourts for example) and/or define the maximum amount of calendar events I want to be created for the whole period I guess this would be one way to solve my problem. Would that be possible? Or even more advanced version of the event configuration that would also take into account some sort of price difference %.

  7. Thanks for a very nice integration, works really well. Use it for both cheapest and most expensive price (when the price is high I use my batteries as much as possible.
    Had one problem from start, when duplicating from cheapest to most expensive. The unique_id was not used, instead the integration use the name as sensor. Had to spend some time finding out what was wrong as it did not find any info about len() etc. Just info if some else have the same problem.
    Next is to calculate the average price for the electricity during calendar events and visualizing it in the event.

  8. Hello, One configuration question from a very new user of HA 🙂

    In configuration.yaml is this OK to have 3 differents “packages” ?

    Then i also have one folder include for the project, but that I don’t know to handle this folder, got error when try to add the line in different way. see the last line below 🙁

    homeassistant:

    packages:
    cheapest_hours: !include advanced_cheapest_hours.yaml
    expensive_hours: !include advanced_expensive_multiple_hours.yaml
    most_expensive: !include advanced_most_expensive_hours.yaml

    # packages: !include_dir_named integrations

    1. Hi Peter again!

      You can of course include multiple packages just like you have done above.

      There are two possible ways you can do to include all you want:
      – Just move those cheapest hours packages to the ‘integrations’ folder as well, they will be loaded from there and remove the separate includes 🙂
      – Remove the second ‘packages:’ from the commented line and just use ‘!include_dir_named integrations’ below the explicitly included cheapest hours packages.

      I’m using the upper way to include all my packages nowadays as well from a one specific folder (ps. you can also add subfolders and those will be included automatically as well)
      My folder structure is like this:

      packages\
      packages\energy
      packages\utility
      ...

      1. Hi,

        Is this oke?

        homeassistant:
        packages:
        cheapest_hours_energy_non_sequential: !include
        advanced_cheapest_hours_non_sequential.yaml
        cheapest_hours: !include advanced_cheapest_hours.yaml
        cheapest_hours_price_limit: !include cheap_hours_limit_price.yaml

        1. Hi!

          Seems ok.

          However, you could also place all the files into same directory and just include the dir.
          homeassistant:
          packages: !include_dir_named packages

  9. I don’t understand this
    Must i change something?

    # Automation that triggers creating of the calendar entries
    – id: ‘cheapest_hours_non_sequential_set_sequence’
    alias: ‘Cheap hours: Set next non-sequantial chepeast hours’
    description: ‘Checks tomorrow energy prices every hour and create calendar entry when available AND events not yet created’
    variables:
    # from which sensor we should try the tomorrow price validation
    # CHANGE-ME: sensorId to used template
    sensorId: >-
    {%- set sensorId = ‘sensor.cheapest_hours_energy_non_sequential’ -%}
    {{ sensorId }}
    trigger:
    – platform: time_pattern
    hours: /1

    1. Hi!

      For that part of the template you don’t have to change anything, unless you’ve renamed the template sensor at the top of the yaml package.
      If you’ve changed the templated sensor name, change the ‘sensor.cheapest_hours_energy_non_sequential’ to correspond the change.

  10. Hi Toni, loks like the expensive works fine,!

    just one thing I wish,

    I want to start 1 or 2 hour earlier from the start of expensive hour, how to do that ?

    1. Hi!

      You should able to do it by making a small modification to your automations ‘calendar.create_event’ action.
      So just reduce 3600 (one hour in seconds) or any other value from the start time of the event creation 🙂
      Example like this (the sequential version):
      - service: calendar.create_event
      data:
      start_date_time: >
      {%- set sensorId = 'sensor.cheapest_hours_energy_car_charging' -%}
      {{ (as_timestamp(states(sensorId), 0) - 3600) | float | timestamp_local }}

      For non-sequential, the principle is the same, but the modification needs to be done to script ‘cheapest_hours_create_multi_calendar’
      However, keep in mind that this change for the script may also affect your other automations so you might want to make a copy of the modified script + rename and modify the renamed one.

  11. Hi, One thing the before more testing 🙂

    I have seen theirs 3 decimal in the sensor , but in your script there is might only one ? because is pick wrong start time ? I think so 😉

    I have as one example for tomorrow time (17 Januari) , from time 01:00 to 06:00 below is for Swedish Kronor SE3
    0.983 , 0.971 , 0.958 , 0.954 , 0.954 , 1.002

    And the script pick 02:00 as start (none_sequential) = 0.971 ?? I the lowest price is 0.954

    And what happens with kl 00:00 , is this controlled by the script ? I don’t see any price for that hour 😉

    Thanks for your hard work !!

    1. Hi!

      I’ve tested this moslty with Finnish timezones and prices and so far everything has been running just as it should.
      I will check the Swedish configurations soon to verify is everything ok in there as well. 🙂

      1. Thanks, see if yo can use SE3 and SEK

        A New function to think about ?
        … I will use this for my Solar battery, and for cheapest hour I like to automate the “end” hour instead of setting this my a fix number of hours.

        The way I think is to set a percentage variabel, like 25% (set by user in script)
        And compare the cheapest hour with next hour, next hour …. if it’s found a price for a hour bigger than ‘cheapest + 25%, then set that hour as new “end hour” 🙂

        For this function I will have this Time frame for only charging state, so its not using any power from my batteri before price increase with the settings above when price increases 🙂

        Not sure if this will work ? but if, then we might can have 2 ways ,one fix span where user set like today, and this new way ?

  12. Feedback for the expensive script. I got a span between 07:00 to 12:00 in the calendar with below settings

    FYI: the most expensive hour is 08:00 for Sweden SE3 on 18 Jan.
    —————
    number_of_sequential_hours: 5 # Amount of sequantial expensive hours in search
    first_hour: 05 # Search starting hour
    last_hour: 20 # Search ending hour
    starting_today: false # Is the first_hour today (true / false). If false, first_hour needs to be before last_hour.
    ———————

    Thanks and have a nice day 🙂

  13. Feedback for Friday 19/1 SE3

    Cheapest start in calendar at 01:00 (0,929 SEK) but cheapest is 03:00 (0.892)
    Expensive start in calendar at 15:00 (1.236) but expensive is 17:00 (1.631)

    BR
    Peter

    1. Thanks Peter!

      Haven’t really had time to look into this, but I’ll try to have few hours during the weekend.
      If you find issue during saturday/sunday prices, please send me mail to creatingsmarthome (at) gmail.com 🙂

    2. Hi Peter!

      Just checked todays prices and compared to the events received from template sensor (using SE3 region), I did get the proper values.
      Thinking also that are you sure your Home Assistant is using the correct timezone? Might be related to that as well..

      Also can you give me your Nordpool configuration? Are you using other expenses in the prices or is it just plain hourly price?

  14. Hi, Only plain hourly price, rest is standard I think.
    And Time zone is GMT +01:00 Stockholm

  15. Hi, I am started having some errors with advanced_cheapest_hours_multiple.yaml
    Error while executing automation automation.cheapest_hours_set_next_cheapest_sequence: Error rendering data template: ValueError: Template error: float got invalid input ‘None’ when rendering template ‘{%- set sensorId = ‘sensor.cheapest_hours_energy_car_charging’ -%} {{ (as_timestamp(states(sensorId), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}’ but no default was specified
    I am looking at this one https://github.com/kotope/ha_nordpool_cheapest_hours/blob/main/advanced_cheapest_hours_multiple.yaml

    1. Hi!

      Sorry I’ve missed this GitHub issue totally!
      I haven’t noticed such issues that you are experiencing, so my guess would be that there’s something wrong in your configurations. Or do you have multiple automations that are writing the data with same name?

      Anyhow, I can try to help you forward. If you can, please send your cheapest hours configurations to creatingsmarthome (at) gmail.com and I try to have a look if I can spot anything odd.

      1. I have made rather basic configurations to the yaml file and there is only the non sequential version of the script loaded.

        My guess is that the hour selection logic is somehow broken when I try to feed it a high number of hours. It is also possible that my configuration has something wrong with it. I tried to go by the documentation, but I did switch the toggle logix for my own expensive energy binary toggle.

        I added the details to the github issue to keep everything in one place.

  16. Sorry for a somewhat disconnected question, trying to understand packages: what is the difference between using packages as you do, or just defining it as a template:

    templates:
    !include anything,yaml
    !include advanced_cheapest_hours.yaml

    1. Templates only contains sensor templates and you can’t include other types, like helpers or automations, in that block.

      Packages can have multiple types and I find it more simple when items included in a package are built to do one clear sequence.

  17. Hello, is it possible to have a sample file with 2 sensors ? I’m not following step 3 and 4 … I think 1 & 2 is correct, but not sure …

    It look like the should solve a need that I have 🙂
    Thanks and keep up the good work !

  18. What I can’t understans is how I do ‘contain another sensorId’ for these steps.

  19. when i try to delete events in the calendar i get

    Unable to parse date/time value: [ValueError(“Expected value to match DATE pattern: ””), ValueError(‘Expected value to match DATE-TIME pattern: ‘)]

    Is that HA’s error or something else?
    using core beta, maby thats why?

    Core
    2024.3.0b6
    Supervisor
    2024.02.1
    Operating System
    12.0
    Frontend
    20240304.0

    1. Hi!

      Haven’t seen or heard about an error like this with the automation. Might be related to 2024.3.0. I’ll get the beta myself as well and see if it works for me and get back to you after 🙂

      1. Hi Jimmy!

        Just tried this out with the beta version (2024.3.0b6) and had the same issue.
        Most probably just a bug with Home Assistant beta, since it’s reproducible also with normal events without the cheapest hours energy automation what-so-ever.

        1. Why didnt i test normal events . Ok Thanks. Otherwise it Works great 🙂

  20. Hi and thanx for great work!
    I am trying to set up your packages. Have installed the “advanced cheapest hours, non sequential, sequential and limit price”. Today trying to add the “multiple” package returns an error:

    Setup of packkage ‘advanced_cheapest_hours_multiple’ failed: integration ‘input_boolean’ has duplicate key ‘name’

    Are the packages in someway incompatible?

    1. Hi!

      Most probably there are entities with exact same names. Check your package contents that those don’t have exact same ids anywhere.

  21. Thanx for reply! I deleted the original “cheapest hours” package, wich seem to have fixed the problem.
    Now i have another issue that i dont understand, with the markdown card, giving strange respons “De billigaste fyra (4) timmarna startar idag kl 02:00, då snittpriset är 0.50 Öre/kWh.” That cannot be.

    The code in the markdowncard for cheapest and most expensive hours are similar but for the final lines

    “De billigaste fyra ({{num_hours}}) timmarna startar {% if (timelowest.hr < 24) %}idag{% else %}imorgon{% endif %} kl {{timemapper[timelowest.hr]}}, då snittpriset är {{\"%.2f\"|format(lowestiter.kr/num_hours)}} Öre/kWh. "

    vs

    "De dyraste fyra ({{num_hours}}) timmarna startar {% if (timehighest.hr now().hour -%}\n {%- if iter highestiter.kr | float -%}\n {%- set highestiter.kr = iter | float -%}\n {%- set timehighest.hr = loop.index -1 -%}\n {%- endif -%}\n {%- endif -%}\n {% endfor -%}\n De billigaste fyra ({{num_hours}}) timmarna startar {% if (timelowest.hr now().hour -%}\n {%- if iter highestiter.kr | float -%}\n {%- set highestiter.kr = iter | float -%}\n {%- set timehighest.hr = loop.index -1 -%}\n {%- endif -%}\n {%- endif -%}\n {% endfor -%}\n De dyraste fyra ({{num_hours}}) timmarna startar {% if (timehighest.hr < 24) %}idag{% else %}imorgon{% endif %} kl {{timemapper[timehighest.hr]}}, då snittpriset är {{\"%.2f\"|format(highestiter.kr/num_hours)}} Öre/kWh"

    Any ideas?

    1. Sorry, I realised this was not your code. Mixed up the origins. Thx again for good work with the cheapest hours!
      /Martin

  22. Hi

    Tried to install with multi sensors.
    Get this error Any ideas? Works with singel sensor
    Logger: homeassistant.components.script.cheapest_hours_create_multi_calendar
    Källa: helpers/script.py:1918
    integration: Skript (dokumentation, ärenden)
    Inträffade först: 16:24:37 (6 händelser)
    Senast loggade: 16:26:42

    cheapest_hours_create_multi_calendar: Error rendering cheapest_hours_create_multi_calendar repeat count template: TypeError: object of type ‘NoneType’ has no len()

    1. And the sensors show up under development tools, they are correctly populated but i wont get them in my calender

      1. Most probably the list containing cheapest hours from the sensor can’t be read.
        Can you paste here what does the developer tools template sensor outputs using following command:
        {{ states.sensor.your_cheapest_hours_sensor.attributes }}

        I had similar problem earlier my self and it was error in my cheapest hours configuration. I was trying to get number of cheapest hours from a variable that returned ‘unavailable’ and therefore the list in attributes could not be populated.

        1. This is what the tool returns:
          {{ states.sensor.cheapest_hours_energy_non_sequential_ess_dag.attributes }}
          ‘None’ has no attribute ‘attributes’

          1. Hi!

            Seems like something is failing with your templated sensor.
            If you want, you can send me your template sensor to creatingsmarthome(at)gmail.com and I’ll see if I can spot something odd in there ..

        2. that piece of code may have helpt me. found a different sensor name but i cant find that name in my non-sequential file? But it outputs this:
          {{ states.sensor.cheapest_hours_energy_ess_dag_non_sequential.attributes }}

          {
          “number_of_hours”: “5”,
          “first_hour”: “09”,
          “last_hour”: “18”,
          “starting_today”: “True”,
          “sensor”: “sensor.nordpool”,
          “fail_safe_starting”: “00:00”,
          “list”: [
          {
          “start”: “2024-04-20T12:00:00+02:00”,
          “end”: “2024-04-20T17:00:00+02:00”
          }
          ],
          “failsafe”: [
          {
          “start”: “2024-04-20T00:00:00+02:00”,
          “end”: “2024-04-20T05:00:00+02:00”
          }
          ],
          “friendly_name”: “Cheapest hours energy ess dag (non-sequential)”
          }

  23. Hi! Have been using your automation and still loving it. But I might have found a bug:

    When first_hour: 15
    Last_hour: 15
    Starting_today: True

    If the case is that the cheapest hour starts at 15. Then the automation should search, create the calender event and toggle the switch all at same time at 15. I have seen, that the calender event is created, but the switch doesn’t switch in this case. Is there too much happening at the same time? With different settings I have seen a similar case where the sequence ended at 00 and after new prices found the next sequence was set to 00-06. But again in this case the switch doesn’t toggle back on again at 00 after the previous sequence ended at 00, and I missed the heating time from 00-06.

    Would it fix this, if the calender events would be created to start at 01 minutes past the hour and to end at 59 minutes past the hour. But is this impossible to make because of when more than one hour in the sequnce and then there would be a 2 minute pauses?

    Hope I explained my self….

    1. Hi!

      I think I know what’s going on in here. The trigger of cheapest hours is triggered every even hour and the first possible start is at 15.00. Along with that everything else is created at the same time.. I don’t think it’s a bug, but rather a feature 🙂

      Anyhow, maybe the best/easiest way to overcome this is to trigger cheapest hours creation at the event of nord pool price fetch.. Something like this:
      platform: state
      entity_id:
      - sensor.nordpool
      attribute: tomorrow_valid
      from: "false"
      to: "true"

      1. Okay, that would be a fix when searching first_hour:15 and last_hour:15. But now that I am searching first_hour:15 and last_hour:23, I can still have have for example sequence from 21-00 and with new prices new sequence from 00-06. Then the automation fails to switch back on the 00-06 sequence.

        This has happened only maybe twice now, but might be difficult to overcome.

Leave a Reply

Your email address will not be published. Required fields are marked *