Home Assistant: Advanced Nord Pool Cheapest Hours automation with local calendar support

In few weeks I finally have to move from constant electricity price to the spot price and I’m going to have to expand my automations a bit. In the most popular article how to use Nord Pool cheapest hours and automate devices I did the automation that will search for cheapest sequential hours. However, there are some flaws in that I wanted to finally handle myself and create even better automation!

One of the biggest issue (in my opinion) was the automation time of next day being set at 23:15. I want to be able to see the next days cheapest hours “immediately” when next day prices are published by the Nord Pool. So that really got me thinking, that I just don’t want to use one variable to store the time, but I could use a calendar to actually mark the sequential hours and run the automations by the calendar entries instead!

Second issue was only able to use one single day (the next day) prices and the automation was not able expand between two days (e.g. 22 – 08).

And one more thing (not a big problem for me), I think the configurations were a bit messy, so maybe its time to try to make this package a bit more configurable 🙂

If you wish to know more and see exact features that this package has to offer, continue reading next sections..

Features and what does this package has to REALLY offer

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

  • Calculate wanted number of sequential cheapest hours for the next day immediately when available
  • Support for two days (today + tomorrow)
  • Required hour range needs to be easily configurable (e.g. during night, full day, etc.. just like in the previous version)
  • Once prices are received from the Nord Pool, a calendar event is created for the cheapest hours
  • 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 from 00:00 to XX:XX depending on the selected number of sequential hours. This will provide us a failsafe that no critical devices are being left out of electricity (e.g. water boiler).
  • Support to use multiple separate cheapest hours sequences for different devices (e.g. car charging, water boiler…)

That’s about it.. with those features in place, it should be rather simple and error proof to run devices during the hours of cheapest electricity prices.

This automation is the sequential version of cheapest hours. For non-sequential, checkout this article.

Using the local calendar

Home Assistant release 2022.12 finally introduced a local calendar support that can be used exactly like these situations! No longer we need to rely on a cloud based calendar, no-one wants to run critical automations based on a cloud connection, right? And we don’t even need those pesky little time helpers to hold our information anymore.

Anyhow, there is still one missing features with the local calendar implementation that we need to handle or make a workaround:

  • We can’t check if calendar event has already been created

This can luckily be worked around though with a small boolean helper utility to keep the information about calendar mark being created.

That’s the necessary background information now.. time to get hands dirty set the package + configurations!

Setting up the local calendar

So we need to create a calendar, great! In my package, the calendar is called ‘electricity‘ so I suggest to use the same or else you need to rename the calendar events in the package as well.

Creating the calendar can be done by going to Home Assistant Settings -> Devices & Services -> + Add Integration (bottom right corner) -> Search for ‘Local Calendar’ and click it.

Next enter name ‘electricity’ and press Submit, done! Now the calendar is in place and we are ready to move on to the actual automations!

Setting up the package

Prerequisites for this package to work is to have Nord Pool integration installed. You can look for integration installation guide from the original cheapest hours article or directly from Nord Pool integration GitHub page. Once that is done, continue reading..

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: !include advanced_cheapest_hours.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 🙂

Configurations

This version of the cheapest hours package is quite much easier to configure than the previous one. So open up the file advanced_cheapest_hours.yaml for editing. All the required lines to be edited can be found with #CHANGE-ME tag!

Mandatory configuration

First there are five sensor attributes that needs to be changed:

attributes:
# CHANGE-ME: Set your personal configurations in here
     # Amount of sequantial cheapest hours in search
     number_of_sequential_hours: 3
     # Search starting hour
     first_hour: 22
     # 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_01
     # If nordpool fetch fails, starting time to make the calendar entry
     fail_safe_starting: '00:00' 

The example configuration above will search for cheapest three sequential hours between today 22:00 and tomorrow 08:00. If failing to get the hours, it will mark the time from 00:00 to 03:00.

Next thing to do is to implement the automation actions. In the package, there’s two more #CHANGE-ME tags in the next section. Those are the actual automation actions that should be done during the cheapest hours.

# CHANGE-ME: Actions to do when cheapest hours starts
      - service: light.turn_on
        entity_id: light.office_work
      else:
# CHANGE-ME: Actions to do when cheapest hours ends
      - service: light.turn_off
        entity_id: light.office_work

In above example, there are actions to turn on and off the light.office entity during the cheapest hours. Just change these to what every you want to do during the cheapest hours configure previosy!

With these changes made, the automation should be ready to go! Just reload yaml file using ‘developer tools -> restart‘ -functionality. If everything is working properly, a calendar event should be created to the local calendar called ‘electricity’ when the clock hits full hours AND tomorrow prices are available. Along with that, the configured actions should start running once the calendar event (cheapest hour) is hit.

Creating multiple sequences (optional)

If it’s not enough to have one cheapest hours sequence, with this package it’s also possible to make multiple sequence. For example: five hours sequence to run the water boiler between 22-06 and load the car between 00-23 for 8 hours every day. Let me explain how to achieve that:

  1. Copy the templated sensor “Cheapest hours energy” and rename its unique_id and name something more suitable. Remember to write this unique_id down somewhere. You are going to need it on the later steps.
  2. Make a copy of the first automationcheapest_hours_calendar_entry‘ and rename it to something more suitable (also change the id). After copied, rename the sensorId of to the templated sensor unique_id you made previously. And of course change the actions to what every you like to do during the cheapest hours sequence.
  3. On the second automation ‘cheapest_hours_set_sequence‘, make again a new copycalendar.create_eventaction. On this new action, rename all the sensorIds from this action to the templated sensor (unique_id) you made previously.
  4. Last, but not least, make the failsafe work with the new automation. Make once again a copy of another ‘calendar.create_eventaction of the third automation ‘cheapest_hours_failsafe‘ and rename the all sensorIds of this new action to the templated sensor unique_id you made previously.

That’s about it! Multiple entires should now be created once cheapest hours are received and automations should run when specific calendar mark occurs!

In my GitHub page, there’s also an example with two separate time schedules in the same package. You can look example directly from that one.

Bonus: Making the UI

To make the advanced cheapest hours visible through Home Assistant UI it’s a good idea to add a calendar card in a view.

First go to a dashboard and select a view you would like to add the card. Then press ‘Menu’ -> ‘Edit Dashboard’. In edit mode, press ‘+ add new card‘ and select ‘Calendar’.

Remove all the pre-selected calendars (if any) and select one entity ‘electricity’. I’d suggest to use ‘List (7 days)’ as a display mode and it will create you a calendar card as shown below.

Conclusion and what’s next

I’ve been running this automation for a week now and so far it has worked great!

The configuration is now much easier, but I’m not still very happy when configuring for multiple sequences. For this, I’m planning to implement a configuration utility to web that will construct the package dynamically based on given information. That way user just has to enter all the info he/she want’s and the package will be automatically generated and can be downloaded without touching the contents of the file itself at all!

However, based on the time I’ve got in my hands, it might take ‘some time’. So keep checking the blog frequently to get the utility immediately when available 🙂

Another thing what do next is to handle non-sequential hours. Some device doesn’t need the hours to be sequential and could be activated when ever the electricity is cheap. E.g. water boiler could run over night for five hours, but those hours don’t need to be sequential.

Anyhow, drop a comment below and say what do you think about this package? Is this better or worse than the previous version? And what would you improve or do you have any feature requests?


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

117 Replies to “Home Assistant: Advanced Nord Pool Cheapest Hours automation with local calendar support”

  1. Hello Toni,
    Smart work done!

    Can’t get it running though.

    YAML configuration invalid!
    extra keys not allowed @ data[‘cheapest_hours’]

    1. Got same feedback from another person too yesterday. I will look into this today in a clean Home Assistant environment, most probably just some minor issue somewhere in the guide/code.
      Thanks for your patience!

      1. Hi again!

        Checked it out straight away, my package installation guide missed one line (packages:)

        Should work now 🙂

  2. Another issue was found with using a comment block while updating next cheapest hours. So if you’ve downloaded the package before 14/04/2022 18:00 (EEST), please get the package again 🙂

  3. Hi Toni, awesome automations! Thanks for sharing and educating us!

    I also got this automation to work and love it that it works over the span of two days and the calendar overview for multiple devices.

    The non-consecutive hours would also be a great extension.

    1. Hi!

      It’s always good to hear that the stuff I do are useful. Thanks for the feedback! 🙂

  4. Thanks, Works great. I have a question tho. Im a beginner in home assistant. Is it Possible to add like at price under xx.xx always on and over xx.xx always off?

    1. Hi!

      Yes, its possible of course.

      Here’s an example how to do that (turn on only):
      https://pastebin.com/G26Y0X3D

      However, if you are using my cheapest hours package, you need to take care of situation that the devices are not being turned off while working on cheapest hours sequence.
      If you need a more advanced setup, please comment and I’ll help you more 🙂

      1. thanx Toni. im thinking more and gonna try to explain what im trying to do ^^

        When checking for the X(2 for me now) cheapes hours to add to the calendar im thinking Dont add those hours if the price is above xx.xx$, and add all hours below xx.xx$ always(not just those 2h but all the day if under that price).

        im using it for my wallbox and dont need to charge every day so im trying to max it out so cheap i can ^^

        1. Hi and thanks for clarification, now I see what you are trying to achieve 🙂

          The idea is of course doable, but needs to be thought a bit more, since managing a calendar with multiple time slots might get confusing at the end.
          Maybe a more simple approach of just enabling/disabling the charging when price goes below X would be a better choice.

          Anyhow, I can’t give you a direct example for now, but once I implement ‘non-sequential hours’ support to the current package, it could get you a bit closer to this 🙂

  5. Hi Toni,
    I’m new to home assistant, and love what you’ve done here!
    I’ve got a question:
    Would it be possible to e.g. have an if statement in the sensor (or somewhere else)? Wat I’m trying to do is to find the cheapest price from Nord pool between 18-09 on Monday, Tuesday and Thursday( and add calendar entries), the rest of the week I want to add calendar entries for the cheapest price between 00-23.
    I’ve tried to make some changes in your example, but I fail miserably, since I’m new to home assistant (and programming). Any tips/help would be greatly appreciated .

    1. Interesting idea and definately doable!
      I need to play around a bit to see what would be the best way to go, but I will absolutely get back to you on this 🙂

  6. Hi. I got some help to add this program on to my HA. Works very well – thank you! But why only pick the adjacent cheapest hours? Eg a water heater, electric car etc, can run well without adjacent heating/charging hours. Hence, will it be possible to modify your excellent program to pick the cheapest X hours the next day? And even more, when Nordpool update prices at 14:00, if next day is more expensive than some of the remaining hours of today – make an option to pick some of them, instead of picking all from next day. What do you think?

    1. For car charging (at least on winter times), I think it’s better to keep the hours sequential to avoid unecessary battery heating.

      However, for water boiler this could work in some cases.. unless all the cheapest hours are in the beginning of the day and next day in the end of the day.. that would make almost 48h without heating and at least in our household it would be too long period.

      Anyhow, non-sequential hours is something I’m planning to do as soon as I can 🙂

      1. Thanks!

        I agree that if heating comes too far apart, it might get too cold water. But I have made an “emergency hot water heat” automation, that triggers if water temp gets too low. No mater what the price is, but only raising temp to 40C . Not very often it has triggered in our house 🙂

      2. Hi. And one more. Is it much change in the code for the program to identfy eg. the 5 most expensive hours, instead of cheapest? Will use it to tell when the battery pack should deliver energy to the house. /Erik

        1. Hi!

          In theory, changing templated sensors comparison to “bigger than” should do the trick: {%- if ns.counter > ns.cheapestPrice -%}
          And then making the automations work as opposite.. of course I’d recommend to change the names “cheapestPrice” to “expensivePrice” etc.

          And note that I’ve not tested this, but in theory should work that easy 🙂

          1. HI.

            Thanks. Did not work. I have following code (nnly changed the “”, ie no name change for now,
            *************
            template:
            – sensor:
            # The actual cheapest hour sensor. If multiple entries are required, copy and set unique_id and configure attributes.
            – name: “Cheapest hours energy (full day)”
            unique_id: cheapest_hours_energy_full_day
            device_class: timestamp
            state: >
            {%- set sensor = (this.attributes.get(‘sensor’, ‘sensor.nordpool_kwh_oslo_nok_3_01_025’) | string) -%}
            {%- set numberOfSequentialHours = (this.attributes.get(‘number_of_sequential_hours’,1) | int) -%}
            {%- set lastHour = (this.attributes.get(‘last_hour’,23) | int) -%}
            {%- set firstHour = (this.attributes.get(‘first_hour’, 0) | int) -%}
            {%- set startingToday = (this.attributes.get(‘starting_today’, false) | bool) -%}
            {%- if state_attr(sensor, ‘tomorrow_valid’) == true -%}
            {%- set arr = state_attr(sensor, ‘today’) + state_attr(sensor, ‘tomorrow’) -%}
            {%- set ns = namespace(counter=0, list=[], cheapestHour=today_at(“00:00”), cheapestPrice=999.00) -%}
            {%- if startingToday == true -%}
            {%- set ns.starting = firstHour -%}
            {%- else -%}
            {%- set ns.starting = firstHour + 24 -%}
            {%- endif -%}
            {%- set ns.ending = lastHour + 24 -%}
            {%- for i in range(ns.starting + numberOfSequentialHours, ns.ending+1) -%}
            {%- set ns.counter = 0.0 -%}
            {%- for j in range(i-numberOfSequentialHours, i) -%}
            {%- set ns.counter = ns.counter + arr[j] -%}
            {%- endfor -%}
            {%- set ns.list = ns.list + [ns.counter] -%}
            {%- if ns.counter > ns.cheapestPrice -%}
            {%- set ns.cheapestPrice = ns.counter -%}
            {%- set ns.cheapestHour = today_at(“00:00”) + timedelta( hours = (i – numberOfSequentialHours)) -%}
            {%- endif -%}
            {%- endfor -%}
            {{ ns.cheapestHour }}
            {%- set ns.cheapestPrice = ns.cheapestPrice / numberOfSequentialHours -%}
            {%- endif -%}
            attributes:
            # CHANGE-ME: Set your personal configurations in here
            number_of_sequential_hours: 4 # Amount of sequantial cheapest hours in search
            first_hour: 23 # Search starting hour
            last_hour: 23 # Search ending hour
            starting_today: true # Is the first_hour today (true / false). If false, first_hour needs to be before last_hour.
            sensor: sensor.nordpool_kwh_oslo_nok_3_01_025 # Nord pool sensor id. Check it ouf from your integrations page!
            fail_safe_starting: ’01:00′ # If nordpool fetch fails, starting time to make the calendar entry

            ************

            CAn you see where the error is?
            /Erik

          2. Forgot to say, that the code made an event back in time. Made an event at 00:00 till 04:00 today, May 30., even the time now is 14:15

          3. Hi.
            As I wrote I did not make it work. But a smart colleague of mine, came up with one more needed change. Apart from what you mentioned. To make it work we also need to sett 0.00 and not 999 in:
            {%- set ns = namespace(counter=0, list=[], expensiveHour=today_at(“00:00”), expensivePrice=0.00) -%}

            Then it works perfect :-), Thanks to Thomas!

          4. Hi!

            Sorry I didn’t have time to test the change through and therefore my initial comments were rwong, but it’s great you got it working and thanks for sharing the info with others 🙂

          5. Hi Tony,

            This colleague of mine – when you get him going…

            Ref question of finding cheapest hours, not adjacent, as now. So now we are testing this code, that picks cheapest 4 each day:

            binary_sensor:
            – platform: template
            sensors:
            cheapest_4_hours:
            value_template: >-
            {% set l=state_attr(‘sensor.nordpool_kwh_oslo_nok_3_10_025’, ‘raw_today’)|sort(attribute=’value’) %}
            {{ (now() >= l[0].start and now() = l[1].start and now() = l[2].start and now() = l[3].start and now() <= l[3].end) }}

            I let you know if it works OK

            /Erik – and thanks to Thomas

  7. Nice work

    Tried this and charged my battery at cheapest hours.

    But when trying the multiple code and two devices only one automation shows??

    Any idea

    Niklas

    1. Hi!

      There should be total of five automations in the list for “Cheapest Hours” if using two devices (set sequence, reset helper, failsafe and one for each actual automation).
      Ensure all the variables and entities are correct and also check if there are any information coming in home-assistant.log

      1. found it, works now.
        removed minus – before service and did some changes for wrog indentation

  8. Hi Toni, thanks for this, it is really useful.

    I have been experimenting with the advanced package,

    I had some trouble getting the “set next cheapest sequence” automations to run.

    It turned out that the run condition is a tomorrow_valid attribute being set on the cheapest_hours_energy, but that attribute does not exist on that sensor definition.

    There is one on the Nordpool sensor, so i changed the sensor name to get it working, but I am not sure whether that was the intention or whether you would have preferred another attribute being set inside cheapest_hours_energy?

    1. Yes, tomorrow_valid should be received directly from the nordpool sensor (and also in my example it’s referring to the nordpool one in the cheapest_hours_energy templated sensor).

      1. Hei, thanks, not sure i understand though: the advanced_cheapest_hour automation cheapest_hours_set_sequence has a condition that points to the sensor.cheapest_hours_energy on line 94.

        if the tomorrow_valid is an attribute of the nordpool sensor does this line need to be changed to point to the nordpool sensor instead?

        1. I had a different issue at the same place of the code as well. Apparently home assistant had an issue finding the sensor value when being time triggered, it ran fine when triggered manually for some reason. I resolved this by replacing the condition state_attr((state_attr(sensorId, ‘sensor’) | string), ‘tomorrow_valid’) == true with the following state_attr(sensorId, ‘tomorrow_valid’) == true

          so the condition for the set_sequence automation working for me, now looks like this:
          condition:
          – condition: template
          # from which sensor we should try the tomorrow price validation
          value_template: >
          {%- set sensorId = ‘sensor.nordpool_kwh_bergen_eur_3_10_025’ -%}
          {{ state_attr((state_attr(sensorId, ‘sensor’) | string), ‘tomorrow_valid’) == true }}
          – condition: state
          entity_id: input_boolean.cheapest_hours_set
          state: ‘off’

          does this look meaningful?

  9. I got errors about as_timestamp() command has unknown value. I think latest versions of HA need extra default value described there. I got it working with these modifications:

    {{ as_timestamp(states(sensorId), 0) | timestamp_local }}

    1. Well spotted, even though I haven’t noticed anything after latest upgrade. Will investigate that a bit as well, but thanks for reporting and providing a fix!

  10. After updating HA to latest, this stopped working.

    Cheapest hours: Set next cheapest sequence – automation gives this

    Executed: 27. toukokuuta 2023 klo 00.11.51
    Error: Error rendering data template: ValueError: Template error: float got invalid input ‘None’ when rendering template ‘{%- set sensorId = ‘sensor.cheapest_hours_energy’ -%} {{ (as_timestamp(states(sensorId), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}’ but no default was specified

    1. Hi!

      In previous comments another person has also reported this issue.
      My Home Assistant instance was actually not updated properly (which I didn’t notice), but after succesfully updating I get the same error.

      As in previous comment stated, changing the as_timestamp function to contain default value will fix the issue:

      {{ as_timestamp(states(sensorId)) | timestamp_local }}
      ->
      {{ as_timestamp(states(sensorId), 0) | timestamp_local }}

      I will need to wait until next day nordpool prices are available to be able to fully test this and will commit the change to the GitHub after that.

      Thanks for reporting!

  11. Hi,
    Amazing guys! I’ve managed to set it up but I wonder a couple of things.
    What is the point of the “Reset the set helper for the next day”. Should it be on or off? Do you need to interact with that, and if so why/when?

    Same question for the helper “Cheapest hours set for the next day”. Is this simply to show that the calendar was updated X time ago?

    And last one, is there a way to retrigger/update the calendar and I have change the config file? For example if I change the time in the config file, I might want to see the effect directly.
    Best Regards,
    Javo

    1. Hi!

      The switch is automatically set on when the calendar event has been created for the next sequence.
      Currently Home Assistant can’t read entries from the calendar by id, so we have to make a workaround like that. So, you don’t need to interact with the switch manually.
      The switch will be reset every day a bit past 00:00.

      And for the next question, you can retrigger the calendar event creation by turning off the switch and running actions for the ‘Cheapest hours: Set next cheapest sequence’ through Home Assistant automations tab. However, just remember to delete the existing events from the calendar when you do this, otherwise there will be duplicates.
      ..and of course the automation will run only when the next day electric prices are available from the nordpool integration.

  12. Hi,

    I’m trying to get this working, but I’m getting this error:
    sensor.cheapest_hours_energy_boiler rendered invalid timestamp:

    Any ideas what might cause this?

    1. Hi!

      That’s a warning that is shown when tomorrow prices are not yet available.
      Try again later when next day electric prices are published by nordpool (about 15.00 CET).

      1. Thanks, it updated the prices and the error went away. Now I have another issue – I don’t have any calendar events, even the fallback ones. I double checked the calendar name and that I followed the guide step by step, but my electricity calendar is empty.

        1. Hi!

          Couple of things:
          – Check the case sensitivity, e.g. “Electricity” is not the same as “electricity”
          – Try to make a manual entry to the calendar, does it work?
          – Does the ‘next cheapest hours’ switch turn on during the evening or is it off always?

          This way you try to see in which part of the package the problem is.
          If the switch turns on, the problem is either within the calendar or calendar event creation automation.

          Also check the automation debug traces from all the automations to see if there are any issues visibile.

          1. Hi,

            I’ve narrowed it down to this error, the same as some other users previously had:’
            Error rendering data template: ValueError: Template error: float got invalid input ‘None’ when rendering template ‘{%- set sensorId = ‘sensor.cheapest_hours_energy’ -%} {{ (as_timestamp(states(sensorId), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}’ but no default was specified

            Even though the default value seems to be there, it still gives this error.

  13. Hello,

    I got this working perfectly, however I got a question.
    Would it be possible to implement some sort of limit to the script, so that if for example the price of electricity is above a certain value (even tho it’s in the cheapest hour per day span) it will not trigger the actions? Like for some reason the price rushes above 0,6kWh (for example) and I want a limit on 0,5kWh so that it will not trigger as it’s higher than set value?

    1. Definitely possible, however there’s couple of points you might want to consider as well.
      Do you want to use average price of the sequence of starting price?
      Or would you just want to skip the single expensive hour if it’s in the middle of sequence?

      If ignoring the whole sequence, you could e.g. make a new condition in the ‘set next cheapest sequence’ that checks the price before making a calendar entry 🙂
      Skipping single values in between would be a more complicated since currently this does have support non-sequential cheapest hours.

  14. Hi Toni,

    I really like this and felt inspired to go ahead and make this a way to learn about Home Assistant and automation. I wrote up most of the automation logic in a custom Python script which simplifies the yaml and jinja templating, and also adds the capability to schedule multiple sequences of the same length:

    https://github.com/eibakke/hass_scripts/blob/main/cheapest_hours_energy.py

    This lets you have the automations as pasted below. I hope you like my ideas! I’d like to keep working on it and add some more features.

    automation:
    # Automation to trigger when calendar event hits the cheapest hour mark
    # start/stop automation
    – id: ‘cheapest_hours_calendar_entry’
    alias: ‘Cheapest hours: Calendar trigger’
    description: ”
    trigger:
    – platform: calendar
    event: start
    entity_id: calendar.electricity
    – platform: calendar
    event: end
    entity_id: calendar.electricity
    condition:
    – condition: template
    value_template: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy_water_boiler’ -%}
    {{ (state_attr(sensorId, ‘friendly_name’) | string) in trigger.calendar_event.summary }}
    action:
    – if:
    – condition: template
    value_template: ‘{{ trigger.event == ”start” }}’
    then:
    # CHANGE-ME: Actions to do when cheapest hours starts
    – service: light.turn_on
    entity_id: light.office_work
    else:
    # CHANGE-ME: Actions to do when cheapest hours ends
    – service: light.turn_off
    entity_id: light.office_work
    mode: single

    # Create calendar event
    – id: ‘cheapest_hours_set_sequence’
    alias: ‘Cheapest hours: Set next cheapest sequence’
    description: ‘Checks tomorrow energy prices every hour and create calendar entry when available AND events not yet created’
    trigger:
    – platform: time_pattern
    hours: /1
    action:
    # Event creation
    – service: python_script.cheapest_hours_energy
    data:
    number_of_sequential_hours: 2
    number_of_sequences: 2
    min_hours_between_sequences: 6
    cheapest_hours_set_bool: “input_boolean.cheapest_hours_set”
    fail_safe_hour: 23
    mode: single

    # input_boolean reset
    – id: ‘cheapest_hours_clear_set_flag’
    alias: ‘Cheapest hours: Reset the set helper for the next day’
    description: ‘Clears cheapest hours helper boolean when the day changes.’
    trigger:
    – platform: time
    at: ’01:15:00′
    condition: []
    action:
    – service: input_boolean.turn_off
    data: {}
    target:
    entity_id: input_boolean.cheapest_hours_set
    mode: single

    # We need a helper to know if the calendar mark(s) has already been set!
    input_boolean:
    cheapest_hours_set:
    name: Cheapest hours set for the next day
    icon: mdi:clock

    1. Excellent! Definitely will try your python script very soon 🙂
      Thanks for sharing!

      1. Updated the script (and messed around with github a bit to get it to work) So the updated URL is at: https://github.com/eibakke/hass_scripts

        The biggest update is that I support for putting the command to call when the calendar event starts and ends in the “cheapest_hours” service. This lets you reduce the number of triggers, and makes it easier to use setup automation for multiple devices. I haven’t actually tried multiple devices yet, and I suspect there is a bug where the cheapest_hours_set flag gets flipped when the script gets called for the first device. Haven’t fixed it yet because I haven’t decided what the best way to solve it is yet…

        As the README in the GitHub repo suggests I have some ideas for more automations that make use of the energy prices too, so I’ve got some fun work ahead of me 🙂

        1. Update again! (if you couldn’t tell I find this really fun to work on)

          I added the ability to schedule a certain number of hours, with a maximum amount of time off between on-time. You can read about it here: https://github.com/eibakke/hass_scripts#variable-length-sequences-max-hours-apart

          It’s still in progress, and I’m just starting to experiment with it, but I’m quite happy with it! In particular the configuration ends up looking really nice:

          # To add scheduling for other devices, simple add another call to the python_script.cheapest_non_sequential service.
          – service: python_script.cheapest_non_sequential
          data:
          max_hours_between_sequences: 12
          number_of_hours: 8
          service_to_call: switch
          start_method: turn_on
          end_method: turn_off
          automate_entity_id: switch.waterheater

          1. Nice work!
            How do make these scripts run in HA? 🙂
            Would be nice with some hints in your README file.

  15. Very informative guide. Will try to test it with water boiler.
    Maybe any updates coming soon?

    1. Hi!

      No updates in sight for now. Might add something like non-sequential hours in a month or two.

      1. Good to hear.
        Non sequential hours sounds fun for water heater or even smart robot vacuums to charge on cheapest hours.
        Hope you doing well, take care

  16. I would need a script which uses human input. When button is pressed in UI, the script would use the timestamp of the press and would calculate the cheapest hours during next xx hours after the press. Any idea how to modify the script?

    1. Interesting approach!

      You could set the timestamp to some other entity attributes and refer that start time + end time from cheapest hours template sensor when the button is pressed:

      {%- set lastHour = (this.attributes.get('last_hour',23) | int) -%}
      {%- set firstHour = (this.attributes.get('first_hour', 0) | int) -%}

      At least that’s where I’d start to looking at.. most probably needs some more changes as well, but start point could be there 🙂

  17. Hi Tony,

    Thanks for a good solutions.
    Since I’m new to this, I hope I can get some help with my problem.
    Using your solution, I want to charge and discharge my battery bank, but would need an extension.

    1.
    I do not want to sell from the batteries if the price difference between the lowest and highest price is less than SEK 1. (losses and wear on the batteries are too expensive).

    2.
    I do not want to charge the batteries if the lowest price is over 0.5 SEK and the price difference between the lowest and highest price is less than 1 SEK. (if the price is under SEK 0.5, then it is cheap enough to have the batteries charged as a backup)

    Is there anything you can help me with?

    1. Hi!

      I think this cheapest hours sequence is not the right choice for the automation you are requesting.
      However, I’m currently implementing another automation that makes calendar entries for non-sequential hours when the price is below a certain range. That one could then most probably be modified to suit your needs.

      So please come back in a week or so to see if it can be used for your needs 🙂

      1. Hi,

        Great post, thanks!

        I’m also wondering about the same as Michael. Would be super to see this evolve into a swiss-army knife. 🙂

        Please let me know if it happens.

        Have a great one!

  18. Getting the same error now as some people earlier:

    Error rendering data template: ValueError: Template error: float got invalid input ‘None’ when rendering template ‘{%- set sensorId = ‘sensor.cheapest_hours_energy’ -%} {{ (as_timestamp(states(sensorId), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}’ but no default was specified

    Any idea how to fix this?

  19. I used to have the “simple” version running and that seemed to work fine, I have been trying to integrate the new version that sounds great, but I am not succeeding… however I try the following message keeps popping up behind the following lines: “Incorrect type. Expected “string”.”

    number_of_sequential_hours: 2
    first_hour: 22
    last_hour: 08

    And I get “Incorrect type.” behind this line:

    starting_today: true

    I have tried a thousand different ways, but I never get it to work…

    Would be very grateful if you could help, I would like to charge a battery when energy is cheap.

    1. Hi!

      Could you send me your modified package to creatingsmarthome (at) gmail.com and I’ll check if I can spot anything wrong?

  20. Hi i’m getting error sensor.cheapest_hours_energy rendered invalid timestamp. Something in the recent update broke alot of things.

    1. That’s usually a (normal) warning when nord pool has not yet published prices for the next day. What time of they day did you get this error and were there prices available already?

      1. I got the error in the evening after installing this. Nordpool works fine and i can see tomorrows price just fine but Cheapest hours energy still says unknown and keeps giving me invalid timestamp error. I had the other automation previously and it worked fine for sometime but noticed recently its cheapest hour sensor had also broken and showed unknown.

  21. Hi Toni,

    You are doing a great job helping us all to save money:)

    Could you please help me to use number helper in your code. I would like have a number helper in my Dashboard to input amount of hours for sequantial hours.

    I have tried to use it in your core below to replace the number 3, but with no success. Name of the helper is input_number.number_of_sequential_hours.

    # CHANGE-ME: Set your personal configurations in here
    number_of_sequential_hours: 3 # Amount of sequantial cheapest hours in search

    What is the format I should use to replace number 3 with the helper mentioned above?

    Many thanks,
    Rami

    1. Hi Rami!

      should work with something like this:
      number_of_sequential_hours: "{{ states.input_number.number_of_sequential_hours.state | int(0) }}"

      ..and the explanation:
      input_number value is in the state of the input_number helper. And just in case, we cast it as int and set default value to zero if it happens unavailable or something else.

  22. Hello,

    Thank You Toni for a super integration! I was just wondering if someone else also besides me has the issue that sometimes you get double the calendar entries and then on the next day you have only one / none ? It does not happen often but if i look at my calendar for september i have 5 occasions where i got the same calendar entries twice and then on the next day one or none (i took the example with 2 sequences) . I have configured both sequences to start at 00 and last hour is 23 and one is 5 sequential hours and other one just 1 hour.

    1. Interesting, never happened with me at least and haven’t heard anyone with such a problem.
      Anyhow, next time this happens, check the traces of the automation and see if cheapest_hours_set boolean is being triggered.
      In theory, if that fails once, the same calendar entries could be made twice on the next iteration run.

      1. Hello,

        So again i saw the issue that i had only one event for the next day so i tried few times manually to trigger it and the result was always that i got one event created for today and another one for tomorrow- also downloaded the trace – somehow it finds the cheapest hours for 2 hours for the next day but for 5 hours on the current day….
        will attach the json – maybe it makes some sense to You …

        {
        “trace”: {
        “last_step”: “action/2”,
        “run_id”: “6b7a3ebea91547ac2b76a50ac0ef4839”,
        “state”: “stopped”,
        “script_execution”: “finished”,
        “timestamp”: {
        “start”: “2023-10-19T14:27:32.350212+00:00”,
        “finish”: “2023-10-19T14:27:32.415787+00:00”
        },
        “domain”: “automation”,
        “item_id”: “cheapest_hours_set_sequence”,
        “trigger”: null,
        “trace”: {
        “trigger”: [
        {
        “path”: “trigger”,
        “timestamp”: “2023-10-19T14:27:32.350377+00:00”,
        “changed_variables”: {
        “this”: {
        “entity_id”: “automation.cheapest_hours_set_next_cheapest_sequence”,
        “state”: “on”,
        “attributes”: {
        “id”: “cheapest_hours_set_sequence”,
        “last_triggered”: “2023-10-19T14:26:04.086862+00:00”,
        “mode”: “single”,
        “current”: 0,
        “friendly_name”: “Cheapest hours: Set next cheapest sequence”
        },
        “last_changed”: “2023-10-17T14:13:39.076198+00:00”,
        “last_updated”: “2023-10-19T14:26:04.156410+00:00”,
        “context”: {
        “id”: “01HD458H5P69BHKSS41K322A7H”,
        “parent_id”: “01HD458H5PQ3N9966KT44T51N8”,
        “user_id”: null
        }
        },
        “trigger”: {
        “platform”: null
        }
        }
        }
        ],
        “action/0”: [
        {
        “path”: “action/0”,
        “timestamp”: “2023-10-19T14:27:32.351316+00:00”,
        “changed_variables”: {
        “context”: {
        “id”: “01HD45B7BYDJEZC0W9GMPFS88M”,
        “parent_id”: “01HD45B7BX0ESGJWJJTFYYFY27”,
        “user_id”: null
        }
        },
        “result”: {
        “params”: {
        “domain”: “input_boolean”,
        “service”: “turn_on”,
        “service_data”: {},
        “target”: {
        “entity_id”: [
        “input_boolean.cheapest_hours_set”
        ]
        }
        },
        “running_script”: false
        }
        }
        ],
        “action/1”: [
        {
        “path”: “action/1”,
        “timestamp”: “2023-10-19T14:27:32.351958+00:00”,
        “result”: {
        “params”: {
        “domain”: “calendar”,
        “service”: “create_event”,
        “service_data”: {
        “start_date_time”: “2023-10-19T01:00:00+03:00”,
        “end_date_time”: “2023-10-19T06:00:00+03:00”,
        “summary”: “Cheapest hours energy (Water Boiler)”,
        “entity_id”: [
        “calendar.electricity”
        ]
        },
        “target”: {
        “entity_id”: [
        “calendar.electricity”
        ]
        }
        },
        “running_script”: false
        }
        }
        ],
        “action/2”: [
        {
        “path”: “action/2”,
        “timestamp”: “2023-10-19T14:27:32.384436+00:00”,
        “result”: {
        “params”: {
        “domain”: “calendar”,
        “service”: “create_event”,
        “service_data”: {
        “start_date_time”: “2023-10-20T04:00:00+03:00”,
        “end_date_time”: “2023-10-20T06:00:00+03:00”,
        “summary”: “Cheapest hours energy (Car Charging)”,
        “entity_id”: [
        “calendar.electricity”
        ]
        },
        “target”: {
        “entity_id”: [
        “calendar.electricity”
        ]
        }
        },
        “running_script”: false
        }
        }
        ]
        },
        “config”: {
        “id”: “cheapest_hours_set_sequence”,
        “alias”: “Cheapest hours: Set next cheapest sequence”,
        “description”: “Checks tomorrow energy prices every hour and create calendar entry when available AND events not yet created”,
        “trigger”: [
        {
        “platform”: “time_pattern”,
        “hours”: “/1”
        }
        ],
        “condition”: [
        {
        “condition”: “template”,
        “value_template”: “{%- set sensorId = ‘sensor.cheapest_hours_energy_water_boiler’ -%} {{ state_attr((state_attr(sensorId, ‘sensor’) | string), ‘tomorrow_valid’) == true }}\n”
        },
        {
        “condition”: “state”,
        “entity_id”: “input_boolean.cheapest_hours_set”,
        “state”: “off”
        }
        ],
        “action”: [
        {
        “service”: “input_boolean.turn_on”,
        “data”: {},
        “target”: {
        “entity_id”: “input_boolean.cheapest_hours_set”
        }
        },
        {
        “service”: “calendar.create_event”,
        “data”: {
        “start_date_time”: “{%- set sensorId = ‘sensor.cheapest_hours_energy_water_boiler’ -%} {{ as_timestamp(states(sensorId), 0) | timestamp_local }}\n”,
        “end_date_time”: “{%- set sensorId = ‘sensor.cheapest_hours_energy_water_boiler’ -%} {{ (as_timestamp(states(sensorId), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}\n”,
        “summary”: “{%- set sensorId = ‘sensor.cheapest_hours_energy_water_boiler’ -%} {{ state_attr(sensorId, ‘friendly_name’) | string }}\n”
        },
        “target”: {
        “entity_id”: “calendar.electricity”
        }
        },
        {
        “service”: “calendar.create_event”,
        “data”: {
        “start_date_time”: “{%- set sensorId = ‘sensor.cheapest_hours_energy_car_charging’ -%} {{ as_timestamp(states(sensorId), 0) | timestamp_local }}\n”,
        “end_date_time”: “{%- set sensorId = ‘sensor.cheapest_hours_energy_car_charging’ -%} {{ (as_timestamp(states(sensorId), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}\n”,
        “summary”: “{%- set sensorId = ‘sensor.cheapest_hours_energy_car_charging’ -%} {{ state_attr(sensorId, ‘friendly_name’) | string }}\n”
        },
        “target”: {
        “entity_id”: “calendar.electricity”
        }
        }
        ],
        “mode”: “single”
        },
        “blueprint_inputs”: null,
        “context”: {
        “id”: “01HD45B7BYDJEZC0W9GMPFS88M”,
        “parent_id”: “01HD45B7BX0ESGJWJJTFYYFY27”,
        “user_id”: null
        }
        },
        “logbookEntries”: [
        {
        “name”: “Cheapest hours: Set next cheapest sequence”,
        “message”: “triggered”,
        “source”: null,
        “entity_id”: “automation.cheapest_hours_set_next_cheapest_sequence”,
        “context_id”: “01HD45B7BYDJEZC0W9GMPFS88M”,
        “when”: 1697725652.350441,
        “domain”: “automation”
        }
        ]
        }

        1. ok already found it- one of the sensors had starting_today:true so probably that was the issue

  23. Hi, I’m using your multiple setup but I have noticed a little problem that is systematic and I can’t figure it out.

    If searching for 4 hours between 22-06 “starting today: true” results in calendar entry 01-05 the automation works (on and off).
    If it results in 23-03 the device will not turn on at 23 but it will stop att 03.00.
    I have three setups and all behave the same if the start is before midnight (only the stop works then).

    The calendar entries looks correct so I think it’s connected to how the automation reads the calendar somehow.

    Thankful for any idéas/pointers!

      1. Thanks, yes it is the latest, seems to be the same problem. Not easy to develop when things change. Maybe I will try to go back to earlier version. This night the cheapest hours was 00-04 and it worked.

        1. The HA issue seems now to be fixed on 2013.10.1. If you still get this scheduling issue, please let me know and I’ll check more into it 🙂

  24. Hello,

    One more thing i wanted to ask if someone has an idea how to calculate the average price for the cheap hours in some helper so that one could see what is the average price during the cheapest hours and make some decisions based on that either to consume just enough to survive or pedal to the metal and use as much power as possible during the cheapest hours ?

    1. Hi!

      Currently the data is in the template (ns.cheapestPrice), but unfortunately it can’t be exported to any variable.
      The automation would need some fine-tuning, like creating a dictionary like entity under the attributes and then read the data from it to state and multiple attributes.

      Anyhow, very interesting idea! I’ll try to make this work during this week 🙂

      1. Hi Toni,
        I have now used your “advanced_cheapest_hours” for a while.
        It picks the cheapest hours good and I assume the average value from those hours are “ns.cheapestPrice”.
        So I copied your “advanced_cheapest_hours” to a “cheapest_hours_price” template and return the “ns.cheapestPrice” instead by
        {%- set ns.cheapestPrice = ns.cheapestPrice / numberOfSequentialHours -%}
        {{ ns.cheapestPrice }}
        instead of {{ ns.cheapestHour }} in “advanced_cheapest_hours”.

        Is not that the average of the cheapest price hours?
        At 20231110 03:00 to 05:00 is average 26,3 öre.

        I have also made the same for most expensive hours by doing the changes “mexpensivePrice=0.00” and
        “{%- if ns.counter > ns.mexpensivePrice -%}
        But I get faulty hours. About 3 hours wrong?
        Do you have some suggestion?

        mexpensivePrice = Most Expensive Price

        1. I am a novice to Jinja and strugling with assessing variables. The code you proposed
          {%- set ns.cheapestPrice = ns.cheapestPrice / numberOfSequentialHours -%}
          was in te the file I downloaded from Github today and I added
          {{ ns.cheapestPrice }}
          But where do I find this cheapest price? Tried to define it as an attribute somehow but it either fails, just shows a text or does’t show up at all.
          Suggestion highly appreciated!

          1. Hi!

            Actually that ‘ns.cheapestPrice’ is not visible at all in the Home Assistant.
            The code should be refactored a bit to get it visible as it’s on the non-sequential version.

          2. Thanks, I take a look. And by the way, thanks for great code, it worked fine for me almost at once!

  25. Cant get it to work ?
    Getting this error when checking config
    extra keys not allowed @ data[‘cheapest_hours’]

    Trying to use this for charging and using a sungrow battery and running my house a bit cheaper.

    /Micke

    1. Hi!

      Do you have only one copy of the automation? If not, check that there are no duplicates between packages.

      You can also send me your .yaml file to creatingsmarthome (at) gmail.com and I’ll try to see if I can spot something odd in there .

  26. Hi! Thank you for the package! I was wondering if you need to edit all instances of the nordpool-sensor in the .yaml-file? In the description it says “All the required lines to be edited can be found with #CHANGE-ME tag!” but in the file there’s also a reference to the nordpool sensor on row 12. Should I edit this in addition to the entry on row 46?

    1. Hi!

      No need to change the row 12.
      It’s just a default value provided to the sensor if the configured attribute can’t be found for some weird reason. In reality, should not ever happen, but required by the Home Assistant ‘get’ method.

  27. This topic gave me pretty big headache to get it work. I mean I tried everthing like 100 times, but my calendar didnt update. I got it work only one time and thats all.

    Now I understand what was wrong while trying to get it work for first time: (noob alert!)

    1. It ran only 1 time per hour.
    2. When it ran first time and “input_boolean.cheapest_hours_set” got state ON then it didnt update my calendar anymore if HA didnt run at night (at 01:15:00). It have reset trigger “input_boolean.turn_off”, but it is only for 1 time per night.

    Solution (specially for newbies like me) is to make it more foolproof and easier for first time testing.

    I have added condition in case user deletes calendar events or HA was turned off when reset trigger should run (at 01:15:00).

    Here is my code part: (runs every minute atm, you can change “minutes” to “hours” when you got it work)

    # Create calendar event
    – id: ‘cheapest_hours_set_sequence’
    alias: ‘Cheapest hours: Set next cheapest sequence’
    description: ‘Checks tomorrow energy prices every hour and create calendar entry when available AND events not yet created’
    trigger:
    – platform: time_pattern
    minutes: /1
    condition:
    – condition: template
    value_template: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ state_attr((state_attr(sensorId, ‘sensor’) | string), ‘tomorrow_valid’) == true }}
    – condition: or
    conditions:
    – condition: state
    entity_id: input_boolean.cheapest_hours_set
    state: ‘off’
    – condition: template
    value_template: ‘{{ state_attr(”calendar.electricity”, ”message”) != ”Cheapest hours energy” }}’
    action:
    – service: input_boolean.turn_on
    data: {}
    target:
    entity_id: input_boolean.cheapest_hours_set
    # Event creation
    – service: calendar.create_event
    data:
    start_date_time: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ as_timestamp(states(sensorId), 0) | timestamp_local }}
    end_date_time: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ (as_timestamp(states(sensorId), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}
    summary: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ state_attr(sensorId, ‘friendly_name’) | string }}
    target:
    entity_id: calendar.electricity
    # If multiple cheapest hours sequences are required, copy ‘calendar.create_event’ block from aboce and change the ‘sensorId’ variabes values to match corresponding new cheapest hours templated sensor
    mode: single

  28. Hi! And first of all thank you for your awesome work!

    Im having some problems with this one though.. Im just a beginner with this stuff and i cant find the fault in my settings. I did have your older walkthrough working with spot prices. But this one keeps resisting me.

    I think maybe the automation is not getting the data from nordpool sensor and it is triggering only the failsafe at 00.00 for the 3 hours i configured it to do. I can make it reset itself by switching the “cheapest hour set for the next day”- to “off”, and then running the automations manually. But it just makes doubles to the calendar and it sets them to trigger at 00.00-03.00.
    Im stumped.

    1. Hi!

      Probably there’s something odd with the configuration and/or entity names of the cheapest_hours_set helper in your modified package.
      Couple of thing you could do:
      1. Go to settings -> devices & services -> entities and ensure the entity ids are correct of all the entities included in the package. I’ve noticed that while developing/testing, if I change some key elements the Home Assistant creates another entity with “_2” at the end of the entity name. Rename those _2, _3, … if existing
      2. You can send me your modified package to creatingsmarthome (at) gmail.com and I’ll have a look if I can spot something odd in there 🙂

  29. hi i am new on this have problem to get conection between calender and switch (deltaco power by tuya have local tuya) the calander get signal and send it on chepest houer but the switch dosent react. name on the switc is varmepump
    entiti switch.varmepump i put the script below thanx in advance

    template:
    – sensor:
    # The actual cheapest hour sensor. If multiple entries are required, copy and set unique_id and configure attributes.
    – name: “Cheapest hours energy”
    unique_id: cheapest_hours_energy
    device_class: timestamp
    state: >
    {%- set sensor = (this.attributes.get(‘sensor’, ‘sensor.nordpool_kwh_se3_sek_5_10_025’) | string) -%}
    {%- set numberOfSequentialHours = (this.attributes.get(‘number_of_sequential_hours’,1) | int) -%}
    {%- set lastHour = (this.attributes.get(‘last_hour’,23) | int) -%}
    {%- set firstHour = (this.attributes.get(‘first_hour’, 0) | int) -%}
    {%- set startingToday = (this.attributes.get(‘starting_today’, false) | bool) -%}
    {%- if state_attr(sensor, ‘tomorrow_valid’) == true -%}
    {%- set arr = state_attr(sensor, ‘today’) + state_attr(sensor, ‘tomorrow’) -%}
    {%- set ns = namespace(counter=0, list=[], cheapestHour=today_at(“00:00”), cheapestPrice=999.00) -%}
    {%- if startingToday == true -%}
    {%- set ns.starting = firstHour -%}
    {%- else -%}
    {%- set ns.starting = firstHour + 24 -%}
    {%- endif -%}
    {%- set ns.ending = lastHour + 24 -%}
    {%- for i in range(ns.starting + numberOfSequentialHours, ns.ending+1) -%}
    {%- set ns.counter = 0.0 -%}
    {%- for j in range(i-numberOfSequentialHours, i) -%}
    {%- set ns.counter = ns.counter + arr[j] -%}
    {%- endfor -%}
    {%- set ns.list = ns.list + [ns.counter] -%}
    {%- if ns.counter
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ (state_attr(sensorId, ‘friendly_name’) | string) == trigger.calendar_event.summary }}
    action:
    – if:
    – condition: template
    value_template: “{{ trigger.event == ‘start’ }}”
    then:
    # CHANGE-ME: Actions to do when cheapest hours starts
    – service: switch.turn_on
    entity_id: switch.varmepump
    else:
    # CHANGE-ME: Actions to do when cheapest hours ends
    – service: switch.turn_off
    entity_id: switch.varmepump
    mode: single

    # — GLOBAL CONFIGURATIONS BELOW, NO NEED TO CHANGE UNLESS CREATING MULTIPLE SEQUENCES —
    # Create calendar event
    – id: “cheapest_hours_set_sequence”
    alias: “Cheapest hours: Set next cheapest sequence”
    description: “Checks tomorrow energy prices every hour and create calendar entry when available AND events not yet created”
    trigger:
    – platform: time_pattern
    hours: /1
    condition:
    – condition: template
    # from which sensor we should try the tomorrow price validation
    value_template: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ state_attr((state_attr(sensorId, ‘sensor’) | string), ‘tomorrow_valid’) == true }}
    – condition: state
    entity_id: input_boolean.cheapest_hours_set
    state: “off”
    action:
    – service: input_boolean.turn_on
    data: {}
    target:
    entity_id: input_boolean.cheapest_hours_set
    # Event creation
    – service: calendar.create_event
    data:
    start_date_time: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ as_timestamp(states(sensorId), 0) | timestamp_local }}
    end_date_time: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ (as_timestamp(states(sensorId), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}
    summary: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ state_attr(sensorId, ‘friendly_name’) | string }}
    target:
    entity_id: calendar.electricity
    # If multiple cheapest hours sequences are required, copy ‘calendar.create_event’ block from aboce and change the ‘sensorId’ variabes values to match corresponding new cheapest hours templated sensor
    mode: single

    # Failsafe
    – id: “cheapest_hours_failsafe”
    alias: “Cheapest hours: Failsafe”
    description: “Failsafe: Set cheapest hours from fail_safe value to amount of hours”
    trigger:
    – platform: time
    at: “23:15”
    condition:
    – condition: state
    entity_id: input_boolean.cheapest_hours_set
    state: “off”
    action:
    – service: input_boolean.turn_on
    data: {}
    target:
    entity_id: input_boolean.cheapest_hours_set
    # Failsafe action
    – service: calendar.create_event
    data:
    start_date_time: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ as_timestamp(today_at(state_attr(sensorId, ‘fail_safe_starting’)) + timedelta( hours = 24 ), 0) | timestamp_local }}
    end_date_time: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ (as_timestamp(today_at(state_attr(sensorId, ‘fail_safe_starting’)) + timedelta( hours = 24 ), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}
    summary: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ state_attr(sensorId, ‘friendly_name’) | string }}
    target:
    entity_id: calendar.electricity
    # If multiple cheapest hours sequences are required, make a copy of ‘calendar.create_event’ block above and set the ‘sensorId’ values to match the new one
    mode: single

    # input_boolean reset
    – id: “cheapest_hours_clear_set_flag”
    alias: “Cheapest hours: Reset the set helper for the next day”
    description: “Clears cheapes hours helper boolean when the day changes.”
    trigger:
    – platform: time
    at: “01:15:00”
    condition: []
    action:
    – service: input_boolean.turn_off
    data: {}
    target:
    entity_id: input_boolean.cheapest_hours_set
    mode: single

    # We need a helper to know if the calendar mark(s) has already been set!
    input_boolean:
    cheapest_hours_set:
    name: Cheapest hours set for the next day
    icon: mdi:clock

    1. Hi!

      Does the switch work ok when added to the Home Assistant UI without the automation?

      1. yes it work manually white tuya
        i tryed tis night to do an automation in tuya that start 3:00 due to calender but stopps 1 min later so mayby it is in tuya it need to be config.

  30. varmepump slogs av utlöst av automatisering Cheapest hours: Calendar trigger triggered
    03:00:01 – För 7 timmar sedan

    varmepump slogs på utlöst av automatisering Cheapest hours: Calendar trigger triggered
    03:00:00 – För 7 timmar sedan

  31. like this i is in tuya inter

    alias: “Cheapest hours: Calendar trigger”
    description: “”
    trigger:
    – platform: calendar
    event: start
    entity_id: calendar.electricity
    – platform: calendar
    event: end
    entity_id: calendar.electricity
    condition:
    – condition: template
    value_template: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%} {{
    (state_attr(sensorId, ‘friendly_name’) | string) ==
    trigger.calendar_event.summary }}
    action:
    – if:
    – condition: template
    value_template: “{{ trigger.event == ‘start’ }}”
    then:
    – service: switch.turn_on
    entity_id: switch.varmepump
    else:
    – service: switch.turn_off
    entity_id: switch.varmepump
    mode: single

    1. Look fine to me.

      Have you tried using the developer tools ‘service’ tab to call the ‘switch.turn_off’ for this entity to see if it works?

  32. it find it
    it is in local tuya as well but tuya makes a own config in #automation
    i try to trigger from calender but nothing happens

  33. thanx i get it to work it was somthing wrong whit local tuya unistall it and install so now it works fine

  34. Hey. Noticed a small bug in your sensor template.

    First the backstory. I took the sensor part from your package config to use in an automation. Usually I have my car charger enabled all the time, but sometimes when electricity is really expensive I want to use a timer to start the charging instead. So I copied the sensor YAML and created a template sensor out of it. Then I made an automation that uses the sensor data to update my charging timer’s start time everytime the cheapest hour changes. This way I always have the time field set to the cheapest hours, so all I need to do is flip a switch to enable timed charging without having to manually set the cheapest start time to the time field too.

    Anyway. I was testing the functionality by looking at the Nordpool values manually and comparing them to the cheapest hour calculated by this sensor, and I noticed that they did not match. I went through the sensor YAML line by line to find out what was happening and stumbled into this: {%- for i in range(ns.starting + numberOfSequentialHours, ns.ending+1) -%}. This does not actually include the lastHour itself in the calculation. Let’s say firstHour is 0, lastHour is 8 and numberOfSequentialHours is 5. Using these as hardcoded values is equal to {%- for i in range(5, 9) -%} and printing i during the loop actually results in: 5,6,7,8 which is only 4 values instead of 5. To get the expected 5 values, ns.ending+1 should be replaced with ns.ending+2.

    1. Hi!

      Thanks for letting me know. I’ll check into this in soon to see if there’s something wrong with the automation. So let’s get back to this in few days 🙂

    2. Hi Jesse! Just checked this out and you’re right in here. The last hour marked is actually the ending time of the last hour rather than start of the hour (so ending at midnight should be set as 24 rather than 23).
      I will make a change to add +1 on the lastHour variable rather than ns.ending on the template.
      Still need to do some testing that the change won’t break anything.

      99% of cases this does not affect the outcome since we are looking for sequential hours, but this issue is more clear on the non-sequential version of the automation.

      Anyhow, thanks for spotting and reporting this!

  35. Hi, This is a great job, kudos to Toni ! 🙂

    I’m a neewbe and old and actually manage this 🙂 but is there any files to download fore expensive hours ?

    I don’t know if I manage to set up that by myself 🙁

    1. Hi and thanks for the feedback Peter!

      Currently for the most expensive hours of the advanced package there’s no example template. But don’t worry, I’ll create one for you in this afternoon once the nordpool prices are received (so I can test it before pushing out to the GitHub).

      1. Thanks, looking forward to it

        My purpose is to control when to charge the solar battery and maybe de-charge it to the grid on expensive hour 🙂

        1. Hi!

          There’s an example of advanced expensive hours in the GitHub page now for you 🙂

          1. Thanks, I will try this 🙂

            Can you look at my other question in the project ?

            “ADVANCED NORD POOL CHEAPEST HOURS AUTOMATION (NON-SEQUENTIAL)”

  36. Good to see Toni your patience for various issues with people. I am also new with HA that´s why my question; I get an error message :
    Error loading /config/configuration.yaml: in “/config/configuration.yaml”, line 14, column 21: Unable to read file /config/advanced_cheapest_hours(1).yaml.
    line 14 is the packages lines….
    packages:
    cheapest_hours: !include advanced_cheapest_hours.yaml
    … I had another failure with the previous installation so I copied the file again, that´s why (1) …

    1. I’m not 100% if the syntax supports brackets (), so I would first try to rename the file something else without brackets 🙂

      If still not working, you can send me mail to creatingsmarthome (at) gmail.com and I can try to help you on Finnish as well.

  37. hi i have a question is it possibly to have more then one device to same sensor and calander`?

    1. Hi!

      Yes, both (control more devices and use the same calendar) are possible. You can just add another action to the existing automation to control another device.

      1. hi i try and try but not get it can you show where to wright

        action:
        – if:
        – condition: template
        value_template: “{{ trigger.event == ‘start’ }}”
        then:
        # CHANGE-ME: Actions to do when cheapest hours starts
        – service: switch.turn_on
        entity_id: switch.varmepump
        else:
        # CHANGE-ME: Actions to do when cheapest hours ends
        – service: switch.turn_off
        entity_id: switch.varmepump
        mode: single

        1. Hi! If you want to run another device with the same time schedule, you should write it something like below (second device is called ‘your_second_decide’ in the example)

          If you want to make another schedule, then you need to make another template sensor with different parameters and another automation (see GitHub for example with multiple entries)

          # CHANGE-ME: Actions to do when cheapest hours starts
          – service: switch.turn_on
          entity_id: switch.varmepump
          - service: switch.turn_on
          entity_id: switch.your_second_device
          else:
          # CHANGE-ME: Actions to do when cheapest hours ends
          – service: switch.turn_off
          entity_id: switch.varmepump
          - service: switch.turn_off
          entity_id: switch.your_second_device
          mode: single

  38. this is the one i have today it is working but want to have more units to run
    yours sineserley magnus

  39. Hello. Thank you for this automation template.
    Im using this so i can turn on a Radiator at the cheapest hours of the day. However i want to add a condition to it of when the temprature is below for example 10c then i want it to start the smart plug that i’ve configured in the script.

    I added a condition to your script but now it turns on at the cheapest hour but it doesnt turn off.
    Here is how i reconfigured the trigger automation:

    automation:
    # Automation to trigger when calendar event hits the cheapest hour mark
    # start/stop automation
    – id: ‘cheapest_hours_calendar_entry’
    alias: ‘Cheapest hours: Calendar trigger’
    description: ”
    trigger:
    – platform: calendar
    event: start
    entity_id: calendar.electricity
    – platform: calendar
    event: end
    entity_id: calendar.electricity
    condition:
    – condition: template
    value_template: >
    {%- set sensorId = ‘sensor.cheapest_hours_energy’ -%}
    {{ (state_attr(sensorId, ‘friendly_name’) | string) == trigger.calendar_event.summary }}
    – type: is_temperature
    condition: device
    device_id: 80fede1e11caa517d7cc3563c5e56b0e
    entity_id: e4a544f1f046585ba71a4f49b23cb660
    domain: sensor
    below: 10
    action:
    – if:
    – condition: template
    value_template: ‘{{ trigger.event == ”start” }}’
    then:
    # CHANGE-ME: Actions to do when cheapest hours starts
    – service: switch.turn_on
    entity_id: switch.shelly_shellyplusplugs_b0b21c125f38_1
    else:
    # CHANGE-ME: Actions to do when cheapest hours ends
    – service: switch.turn_off
    entity_id: switch.shelly_shellyplusplugs_b0b21c125f38_1
    mode: single

  40. Thanks again for the great work. How would I get an average from todays price and a second one from the next days price?

  41. Need help … why do i see this errors? , just DL the script with boiler and car and changed the nordpool sensor,
    And the time of day now is 20:30 so there is prices for yesterday in sensor

    Logger: homeassistant.components.automation.cheapest_hours_set_next_cheapest_sequence
    Source: components/automation/__init__.py:687
    Integration: Automation (documentation, issues)
    First occurred: 19:26:30 (2 occurrences)
    Last logged: 20:29:19

    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_water_boiler’ -%} {{ (as_timestamp(states(sensorId), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}’ but no default was specified

    Logger: homeassistant.components.automation.cheapest_hours_set_next_cheapest_sequence
    Source: helpers/script.py:1805
    Integration: Automation (documentation, issues)
    First occurred: 19:26:30 (2 occurrences)
    Last logged: 20:29:19

    Cheapest hours: Set next cheapest sequence: Error executing script. Error for call_service at pos 2: Error rendering data template: ValueError: Template error: float got invalid input ‘None’ when rendering template ‘{%- set sensorId = ‘sensor.cheapest_hours_energy_water_boiler’ -%} {{ (as_timestamp(states(sensorId), 0) + (3600 * state_attr(sensorId, ‘number_of_sequential_hours’) | float)) | timestamp_local }}’ but no default was specified

    Thanks for help

    Peter

Leave a Reply to Erik Østbye Cancel reply

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