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:
- 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.
- 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.
- 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.