Warmwater boiler run only during cheapest hours based on Nordpool

I am sharing my solution on how I heat my old, not smart, warm water boiler only when the electricity price are the lowest. I added a Shelly Plus 1PM switch. Besides switching it also measures the energy used (kWh). The boiler uses 2kW which is fine for the switch, although first it was getting too warm, so I had to make sure there was enough space around the device so it could cool a bit.

The boiler has enough capacity to supply us with warm water, even if not heated for 24 hours, so only heating during 8 hours per day, and not during the other 16 hours works fine for us. And mostly there will be 2 or 3 periods per 24 hours that the boiler will be heated. During the last few months we always had warm water. It might be different if you have a few teenage girls in your house taken long showers every day :slight_smile: But you can adapt it to your needs.

First I made a template sensor, shown below. It makes a list of today’ s prices and a list for the tomorrow prices. If the tomorrow price are not there, it will be an empty list. Next it adds the 2 list together. I only take first 6 hours of tomorrow, that in case the price drops the boiler will be heated during the coming night. The next step is sorting the prices. And assign the 8th hour (index 7 of the list) to the sensor. If an 8 hours heating is not enough, or too much, you can change the 7 on the last line to a number that works for you. (remember it is the index, and the first index is 0, so index 7 gives 8 hours of prices)

- platform: template
      unit_of_measurement: 'NOK/kWh'
      value_template: >
        {%- set today  = state_attr('sensor.nordpool_kwh_XXX_XXX_X_XX_XXX', 'today') | list %}
        {%- set tomorrow  = state_attr('sensor.nordpool_kwh_XXX_XXX_X_XX_XXX', 'tomorrow') | list %}
        {%- set sorted = (today + tomorrow[:6]) | sort %}
        {{ sorted[7] | float }}

Now we can make an automation that switches on the warm water boiler if the current price is less or equal to the value of the above template sensor. It triggers a few seconds after each hour, as the price changes each hour. I am waiting a few seconds to prevent any race conditions, maybe this is not needed.
Then it checks is price is lower than the sensor. I have a second condition that checks if I am on vacation. If you do not want that, you can leave out the second condition. The vacation sensor is a input_button helper.

alias: warm water boiler switch on-off based on price
description: warm water boiler switch on/off when electricity is low
  - platform: time_pattern
    minutes: "0"
    seconds: "10"
condition: []
  - if:
      - condition: template
        value_template: |2-
                {{ (states('sensor.nordpool_kwh_XXX_XXX_X_XX_XXX') | float)  <=
                (states('sensor.lowest8h_price_HOUSENAME') | float) }} 
      - condition: state
        entity_id: input_boolean.vakantie_mode
        state: "off"
      - service: switch.turn_on
        data: {}
          entity_id: switch.YOUR_BOILER_SWITCH
      - service: switch.turn_off
        data: {}
          entity_id: switch.YOUR_BOILER_SWITCH
mode: single

Every hour it will switch on or off, even if the state is the same. You could make it nicer by checking the state first, but there is no harm or wear and tear by switching the switch to the same state is already in, so I kept it simple. Keeping it simple, reduces the changes on errors.

Below graph shows the prices of the last 24 hours and a red line for the value of the template sensor.
The green area is when the warm water boiler switch is on.

The next graph is the energy usage of the boiler for the same time period.

The last graph shows again the prices and the template sensor value. Here you can see that the boiler will be switched on soon again and we will have enough warm water for the evening.

It would be nice to make a graph to show the cost as well, but I have not figures that one out yet.