Optimal appliance scheduling

Hi,

I am working on some python code to support optimal appliance scheduling to decrease your energy bill. This is useful for those who have an energy contract with their utilities provider with dynamic hourly rates.

Most rates are determined 24 hours in advance, an overview for the next day may look something like this:

Let’s say you want to run a washing machine program tomorrow and minimize the cost of it’s energy consumption. I measured such a program on my washing machine and it’s power consumption over time looks like this:

Based on the above curves you would ideally start the program somewhere around 11-12. But how do I make sure that I start the program at the correct moment such that the bulk of the energy is consumed while the lowest energy rate is applicable?

It would be great if Home Assistant could calculate this for us. Using some python we can calculate this point, which turns out to be 0.15 euro if the program is started at 11:38:40:

As you can see from the curve the difference between the best and worst starting points is almost 1 euro, for a single washing machine program. What’s also interesting is that if you started at 11:00 you would pay almost 0.30 euro for the same program.

Do you have variable, hourly energy rates and would this be useful to you? Does something like this or similar already exist for HA? Please let me know!

This is exactly what I do with some template sensors. First I recorded the consumption of my washing machine and dishwasher in chunks of 5 minutes. Then I used these consumption patterns in the template sensors you see below. This gives me the best starting times (with a 5 minute ‘resolution’) each day for these devices.

template:
- trigger:
    - platform: time_pattern
      # Update every night
      hours: "0"
      minutes: "1"
    - platform: time_pattern
      # Update again in the morning
      hours: "7"
      minutes: "0"
  sensor:
    - name: "Washing machine best starting time today"
      icon: mdi:clock-start
      device_class: timestamp
      state: >
        {% set kwh_pattern = [0.0, 0.16, 0.04, 0.06, 0.05, 0.04, 0.05, 0.01, 0.04, 0.0, 0.01, 0.0, 0.01, 0.0, 0.01, 0.0, 0.01, 0.01, 0.0, 0.01, 0.0, 0.01, 0.0, 0.01, 0.0, 0.01, 0.0, 0.01, 0.02, 0.0, 0.01, 0.02, 0.0, 0.01, 0.02, 0.0, 0.01, 0.03, 0.02, 0.0] %}
        {% set resolution_in_minutes = 5 %}
        {% set start_time = now().replace(minute=0) %}
        {% set list = namespace(expected_costs=[], time=start_time) %}

        {# Calculating until 20:30 #}
        {% set amount_of_calculations = (20-start_time.hour) * (60 // resolution_in_minutes) + (30 // resolution_in_minutes) + 1 %}
        {% for _ in range(0, amount_of_calculations) %}
          {% set time_loop = list.time %}
          {% set ns = namespace(costs=0, time=time_loop) %}
          {% for kwh in kwh_pattern %}
            {% set ns.costs = ns.costs + kwh * state_attr('sensor.nordpool_kwh_nl_eur_3_09_009', 'today')[ns.time.hour] %}
            {% set ns.time = ns.time + timedelta(minutes=resolution_in_minutes) %}
          {% endfor %}
          {% set start_time = time_loop %}
          {% set add_item = {'start': start_time, 'costs': ns.costs | round(5)} %}
          {% set list.expected_costs = list.expected_costs + [add_item] %}
          {% set list.time = list.time + timedelta(minutes=resolution_in_minutes) %}
        {% endfor %}
        {% set sorted_list = list.expected_costs | sort(attribute='costs') %}
        {{ sorted_list[0].start }}
    - name: "Dishwasher best starting time today"
      icon: mdi:clock-start
      device_class: timestamp
      state: >
        {% set kwh_pattern = [0.0, 0.0, 0.0, 0.01, 0.0, 0.08, 0.16, 0.17, 0.15, 0.01, 0.0, 0.0, 0.0, 0.01, 0.0, 0.01, 0.0, 0.0, 0.01, 0.0, 0.0, 0.01, 0.0, 0.11, 0.17, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0] %}
        {% set resolution_in_minutes = 5 %}
        {% set start_time = now().replace(minute=0) %}
        {% set list = namespace(expected_costs=[], time=start_time) %}

        {# Calculating until 21:15 #}
        {% set amount_of_calculations = (21-start_time.hour) * (60 // resolution_in_minutes) + (15 // resolution_in_minutes) + 1 %}
        {% for _ in range(0, amount_of_calculations) %}
          {% set time_loop = list.time %}
          {% set ns = namespace(costs=0, time=time_loop) %}
          {% for kwh in kwh_pattern %}
            {% set ns.costs = ns.costs + kwh * state_attr('sensor.nordpool_kwh_nl_eur_3_09_009', 'today')[ns.time.hour] %}
            {% set ns.time = ns.time + timedelta(minutes=resolution_in_minutes) %}
          {% endfor %}
          {% set start_time = time_loop %}
          {% set add_item = {'start': start_time, 'costs': ns.costs | round(5)} %}
          {% set list.expected_costs = list.expected_costs + [add_item] %}
          {% set list.time = list.time + timedelta(minutes=resolution_in_minutes) %}
        {% endfor %}
        {% set sorted_list = list.expected_costs | sort(attribute='costs') %}
        {{ sorted_list[0].start }}

A few notes:

  1. A resolution of 5 minutes is more than enough for me, because we have to manually start the appliance so there’s no point in making this more accurate. Also, the power plug I use isn’t super accurate, so 5 minutes feels like a nice compromise.
  2. I rely on the nordpool custom component and their many attributes to calculate the expected costs with a certain starting time. If you use a different integration, you probably have to change the ‘inner’ for-loop.
  3. I calculate expected costs until 20:30 or 21:15, because after that the template sensor would need the prices for tomorrow and they’re not available until ~13:30. Electricity in the evening is never very cheap, so I don’t really care about those hours anyway.

Maybe this can help you! You can do much more with this list of expected costs, so maybe this can give you a nice starting point.

2 Likes

Thanks, this is also pretty cool and doesn’t require external python code so easier to integrate. What I don’t like about templates is that the code is hard to read and difficult to maintain, especially for these kind of larger templates. Another thing I notice is that you have 2 for-loops, which can become very slow if the resolution increases. Instead of a for-loop I use a matrix-vector dot product which is significantly faster. I use a 10 second resolution and so the calculation becomes quite large, for manually triggered appliances this resolution doesn’t make much sense but for appliances like smart washing machines I would like to keep this resolution.

Another problem I realized is that the washing machine program is highly dependent on the load (or weight) of the laundry, from anywhere between 1 and 3 kWh for the same program. My washing machine is dumb so I would have to use some kind of load sensor to measure the laundry weight and then calculate the correct pattern. I’m not sure yet what the best way to approach this is. I think ultimately we would have to use linear programming to take into account all the different variables.

Anyway I think this application is important and useful enough to spend some time working on an integration. There are a lot of hidden complexities still to be solved though.

Hi,

Have you made any advances in this matter?

I would like to be able to dynamicaly be able to calculate when, SPA heater, Car charger, washing machine,etc, should run for maximum saving. Depending on how much charging is needed.

Finding the cheapest hours is not so hard, but balancing it with the fact that everything can not run at the same time. For instance, the washing machine and the SPA heater uses the same phase, running them at the same time could result in a blown fuse.

Hi Jan, unfortunately I have not. I would also really like to achieve what you describe, but it requires a kind of math called ‘linear programming’ which I have studied but am not an expert at yet unfortunately. Perhaps there are more knowledgeable people here that can help.

You can use this module and add-on that I developed: https://community.home-assistant.io/t/emhass-an-energy-management-for-home-assistant/

1 Like