Find time to start device (washing machine) from price and time shedule

Hi there,

I have dynamic energy prices and get the prices for the next day.
Also I already have a switch and a timer / automation setup at my washing machine, that starts it at the time I set.

Now I want this to run automatically at the best price within “reasonable” times (washing machine at night = nono). I set up a time shedule helper, but there might be better ways. I also have template sensors for “price level” and another one that gives me the future prices (and some other stuff).
The trigger for the automation will be me switching on the plug for the device to set the program.

I can see two possible ways to handle this:

  1. Check every hour (-> change of price) of the time shedule and while “triggered” whether this is the minimum price or whether this is “a minimum”.
    But this won’t tell me before when the machine will run.

  2. When triggered calculate where the “minima” of the future price lie (but since it is a step function, this might be more complicated than using the derivation helper?), order them rising by energy price, and check for them whether they’re in the allowed time shedule until one is found. If not delay till midnight (and try with the next day). When a good minima is found, enter it into the existing timer.

That way I have a good visual when the device will be running.

Multiple hurdles:

  • the whole “find minima” and “compare with time” sounds like a script? does that belong into an automation?
  • clever way to find the minima and create… dictionaries? with their datetime
  • how to check the time shedule? (The doc and attributes only give “next event”)

Or is there another way better way to handle this?

Looking further:

  • HASS doesn’t seem to be handling future dates nicely, so handing the data from the attribute to a sensor into a derivate helper doesn’t look feasable to me…
  • the time shedule doc doesn’t show any real way to interact, so I’ll have to go with an automation that is triggered from the time shedule and checks for whether the device is “triggered” and prive is at a minimum…

Edit:
So right now have:

  • a boolean helper to save whether the washing machine was “triggered”
  • automation (switch put to on → change boolean helper)
  • automation triggered on shedule that switches the device on under the conditions:
    • boolean helper is set to ON
    • price is at minimum

Problems I can already see:

  • second automation might trigger the boolean helper? (that actually works to switch it off, so it won’t would work to switch it off

  • automation might not trigger often enough (only at the beginning of the allowed times)

I can’t ask the shedule “what about time X” - but I can ask it “what about right now”. So I might change the trigger condition to price changes (hourly)…

I can tell you how I tackle a similar need, albeit not for energy use I do use this method for a number of things like when my vacuums run so I can choose to delay them.

I create a date-time helper for when that something is going to happen, let’s say it’s the vacuum. I have automation that runs at that particular time in the helper. I landed on this because my house announces when the vacuum is going to run (or other things, as mentioned) and I have voice commands like “delay vacuum” which will add 90 minutes to that time and then cancel the running automation. When the automation does fully run it then calculates when the next time will be based on a script I wrote:

##########################################
# CALCULATE NEXT VACUUM RUN DATETIME
##########################################
get_next_datetime:
  alias: Get Next Date/Time Based on Rules
  mode: single

  fields:
    days:
      description: "Day numbers to generate date/time with 0 being monday.  Dict format, full day name, proper case."
      example: "['Saturday', 'Tuesday', 'Thursday']"
    hour:
      description: "Hour of day to use if not the current time on a new day.  24 hour format"
      example: 8
    minutes:
      description: "Minutes of hour to use if not the current time on a new day."
      example: 55
    mindays:
      description: "Minimum number of days until next datetime."
      example: 0

  sequence:
    - variables:
        datetime: >-
          {% set ns = namespace(nextdate = now()) %}
          {% set isnow = false %}
          {% set istoday = false %}
          {% set result = '' %}
          {% set time = "{:02d}".format(hour) + ":" + "{:02d}".format(minutes) %}

          {% if mindays == null %}
            {% set mindays = 0 %}
          {% endif %}

          {% if mindays > 0 %}
            {# Calculate starting mindays from now #}
            {% set startdate = now() + timedelta(days=mindays) %}
            {% for l in [0, 1, 2, 3, 4, 5, 6, 7] %}
              {% set ns.nextdate = startdate + timedelta(days=l) %}
              {% if ns.nextdate.strftime('%A') in days %}
                {% break %}
              {% endif %}
            {% endfor %}
          {% elif now().strftime('%A') in days and now().strftime('%H:%M') == time %}
            {# The day and time match the pattern, do nothing #}
            {% set isnow = true %}
          {% elif ns.nextdate.hour <= hour and ns.nextdate.minute < minutes and ns.nextdate.strftime('%A') in days %}
            {# The day is today but the time is earlier, set for today, do nothing #}
            {% set istoday = true %}
          {% else %}
            {# Start calculation from now forward #}
            {% for l in [1, 2, 3, 4, 5, 6, 7] %}
              {% set ns.nextdate = now() + timedelta(days=l) %}
              {% if ns.nextdate.strftime('%A') in days %}
                {% break %}
              {% endif %}
              
            {% endfor %}
          {% endif %}

          
          {{
            {
              'datetime': ns.nextdate.strftime('%Y-%m-%d ' + time + ':00'),
              'day': ns.nextdate.strftime('%A'),
              'time': time,
              'isnow': isnow,
              'istoday': istoday
            }
          }}

    - stop:
      response_variable: datetime

Which sets the next schedule time. In addition to vacuums I use this for restarting HA periodically, when to turn on/off my drive towers on my massive Plex system and more.

The idea is that this returns a JSON payload of values that the calling function or automation can read and parse to know what to set the date-time helper for (or if to run at all). I’m sure this could be refined, it was a quick and dirty solution for me and I wasn’t paying much attention to optimizing the Jinja2 code, just getting the job done.

While this may not be precisely what you are needing, it might give you some ideas how you could tackle this. For instance you could have it set for tomorrow at 9:00am, but between now and then perhaps you get a better price, it then changes it to that time.

How dare you :wink: It can, not sure what you mean. But maybe this can give you some pointers:

That does look perfect. For now I think I have it working, but not in the way I find optimal :smiley:
Most of what I have should fit right in.

To document my solution up to now:

  • Zigbee/monitored switch on the device, switch.wama
  • Shedule Helper called shedule.wana-allowed which carries the allowed STARTING times (washing machine can take up to 3 hrs with the loudest part at the end, so starting at 10pm is not cool)
  • rest sensor and template sensor, see here HomeAssistant-Config/packages/electricity/tibber.yaml at c8ef70bfc1a9791f042c55e7124781f5a9f4e08d · edwardtfn/HomeAssistant-Config · GitHub
  • Automation Trigger Wama that is triggered by setting the switch.wama to on (when I set the program) and which switches the actual automation to on. I will also manually set the switch.wama to off (so the program doesn’t just run through immediately.
  • Automation WaMa_on which checks whether “the time is right”, “the price is minimal” and is triggered by the energy_price state (changing every hour). It will send a notification and set switch.wama to on - thereby triggering Trigger Wama again which will switch off this Automation WaMa_on to avoid it running a second time.

ToDo: add an action for a new automatisation that monitors the energy usage and tells me when the program has ran through (lots of threads/posts out there)

Code:

alias: Trigger WaMa
description:  Washing machine shall run on the next energy price minimum 
trigger: # I set this up via the UI, this could probably also be handled with the name
  - platform: device
    type: turned_on
    device_id: 1eb2144d3c1fc4740db3886593a65b0f
    entity_id: cecc7c8962a90f2d40d7154135963e52
    domain: switch
condition: []
action:
  - service: input_boolean.toggle
    target:
      entity_id: input_boolean.wama_trigger
    data: {}
  - service: automation.toggle
    metadata: {}
    data: {}
    target:
      entity_id: automation.wama_on
mode: single
alias: WaMa_on
description: >-
  Switches the washing machine on, during allowed times and if it was triggered
  and the price is at a (daily) minimum. Switching on also turns off the trigger
  helper.
trigger:
  - platform: state
    entity_id: sensor.electricity_price_street_nr
condition:
  - condition: state
    entity_id: schedule.wama_allowed
    state: "on"
  - condition: state
    entity_id: sensor.electricity_price
    attribute: is_at_min
    state: "True"
action:
  - service: switch.turn_on
    metadata: {}
    data: {}
    target:
      entity_id: switch.wama
  - service: notify.pushbullet
    data:
      message: Washing machine is runnign now!
      target:
        - device/Mi10 # My mobile phone
mode: single
2 Likes

Hi to all,

I’m struggling with a similiar problem but less complicated - still not getting it to run.

Sitaution:

What I like to have / automate:

  • Push the shelly button
  • “Helper” should look for the cheapest price → if true
  • Dishwasher should start with the specific program
  • Optional:
    ** Voice outcome → tell me when the dishwasher will start / will be finished
    ** Notification when the dishwasher will start / will be finished

But the the dishwasher won’t start.
I think the solution should be very easy and I’m just oversee an easy step, but where?

“Automation configuration”

id: '1735831353949'
alias: Shelly Button Geschirrspüler_günstigste-Stunde_bis_07:00
description: ''
triggers:
  - device_id: bfbf423cd3ee9f4f4cd88eef1ba8b505
    domain: shelly
    type: single
    subtype: button
    trigger: device
conditions:
  - condition: state
    entity_id: binary_sensor.strompreis_gunstigste_1_stunde_bis_07_00
    attribute: earliest_start_time
    state: Wahr
actions:
  - type: turn_on
    device_id: ac726b27da7e27d5ac101fa62266673b
    entity_id: 8499054c55e5a4ae9a43bb6c7aa54d71
    domain: switch
mode: single

“Changed variables”

this:
  entity_id: automation.shelly_button_geschirrspuler_test
  state: 'on'
  attributes:
    id: '1735831353949'
    last_triggered: '2025-01-02T21:35:53.009512+00:00'
    mode: single
    current: 0
    friendly_name: Shelly Button Geschirrspüler_günstigste-Stunde_bis_07:00
  last_changed: '2025-01-04T08:09:13.108253+00:00'
  last_reported: '2025-01-04T08:09:13.108253+00:00'
  last_updated: '2025-01-04T08:09:13.108253+00:00'
  context:
    id: 01JGR5RX4KZFFP5D8J6SY3DJ0A
    parent_id: null
    user_id: 2c5d39754f694e449ed481a83b5e245e
trigger:
  id: '0'
  idx: '0'
  alias: null
  platform: device
  event:
    event_type: shelly.click
    data:
      device_id: bfbf423cd3ee9f4f4cd88eef1ba8b505
      device: shellybutton1-4417932262EE
      channel: 1
      click_type: single
      generation: 1
    origin: LOCAL
    time_fired: '2025-01-04T08:10:41.206467+00:00'
    context:
      id: 01JGR5VK5PGQF5N49D5H7C75TF
      parent_id: null
      user_id: null
  description: event 'shelly.click'

Screenshot shows how I set up the automation using the GUI and how I defined the above mentioned helper

Automation-Helper-Definition

conditions:
  - condition: state
    entity_id: binary_sensor.strompreis_gunstigste_1_stunde_bis_07_00
    attribute: earliest_start_time
    state: Wahr

A binary sensor is on or off internally. It may show Wahr in the UI, but in automations you should test the actual value (on). The automation trace should tell you why it refured to continue.