Laundry done, and emptied

A power-metering smart plug and a door sensor on the washer and dryer, so Home Assistant knows running, done and emptied, and nags until it's unloaded.

Difficulty
Easy
Parts cost
Two power-metering smart plugs and two door sensors
Build time
An evening, then a few loads to check the numbers
Skills
Home Assistant, Zigbee

The problem

I wanted the house to tell us when the washing’s done, and keep telling us until someone empties it.

Knowing a machine has finished is the easy half. A smart plug that measures power shows it straight away: the watts go up when it starts and drop when it stops. The hard half is knowing it’s been emptied. Without that, the reminder either stops too early or never stops. So each machine also gets a door sensor, and opening the door after a load counts as “emptied”.

How it works

How Home Assistant tracks a load of washing Each machine has a smart plug that measures its power and a door sensor. Home Assistant keeps each machine in one of three states. Idle goes to Running when the washer draws over 20 watts for a minute, or the dryer over 300 watts for a minute. Running goes to Done when the washer drops under 10 watts for 5 minutes, or the dryer under 200 watts for 3 minutes. Done shows on the board until the door is opened, the Emptied button is pressed, or the next load starts, which puts it back to Idle. The dryer's 200 watt line sits above its anti-crease tumbles of about 170 watts. Smart plug + door sensor on each machine: watts, door open or shut Idle nothing waiting Running washing or drying Done the board says "empty me" and keeps saying it washer over 20 W for 1 minute dryer over 300 W for 1 minute washer under 10 W for 5 minutes dryer under 200 W for 3 minutes Door opened, Emptied pressed, or the next load starts → back to Idle The dryer's 200 W line sits above its anti-crease tumbles (about 170 W).

Each machine sits in one of three states: Idle, Running or Done. Home Assistant moves it along using the power reading from the plug, and moves it back to Idle when someone opens the door. If the door sensor isn’t fitted yet, or plays up, there’s an Emptied button that does the same job. Starting the next load also clears it, because nobody starts a load on top of wet washing.

The door only counts while the machine says Done. Open the lid mid-wash to throw in a sock, or open it to load up, and nothing changes. Ours is a top-loader, so its “door” is the lid.

The washer and dryer need different numbers. The washer drops under 10 W when it finishes. The dryer keeps tumbling every so often after the cycle (anti-crease, so the clothes don’t set in creases), at about 170 W. So its “finished” line sits at 200 W, above those tumbles.

Done shows on the screen we have in the living area: a little laundromat panel with photos of our two machines, and the finished one blinks DONE ✔ COLLECT until someone deals with it. Any Home Assistant dashboard can do the same job, and a phone message is optional (code below).

What you need

  • Two Zigbee smart plugs with power metering, one per machine. They must be rated for the machine’s load (see below).
  • Two Zigbee door/window sensors, the cheap kind with a magnet.
  • Home Assistant with a working Zigbee setup. I use Zigbee2MQTT.
  • Somewhere to show it: a dashboard, a wall screen, or the Home Assistant phone app.

Check the plug rating, especially for the dryer. A dryer is one of the biggest loads in the house. Over 11 days of logging, my dryer peaked at 2245 W and the washer at 1353 W. A 10 A plug at 240 V is good for 2400 W, so the dryer runs close to the limit. Check the rating on the plug itself against the nameplate on your machine. Plug it straight into the wall, not through a power board or extension lead.

Wiring

There’s no wiring. It’s all plug-in:

Machine Smart plug Door sensor
Washer Between the wall socket and the washer’s plug Sensor on the body, magnet on the lid (or door), lined up so they sit together when it’s shut
Dryer Between the wall socket and the dryer’s plug Same again on the dryer door

Keep the plug somewhere dry and out of the way of the hoses. Stick the door sensor where you can still get at its battery.

Firmware and config

No firmware. Pair the plugs and sensors like any other Zigbee device and give them sensible names. The examples below use these entity IDs:

  • sensor.washer_power and sensor.dryer_power (the plugs’ power readings)
  • binary_sensor.washer_door and binary_sensor.dryer_door (on means open)

If yours come out with different IDs, rename them in Settings → Entities, or change the YAML.

First the helpers: a dropdown per machine to hold its state, and an Emptied button for each. You can make the dropdowns in the UI instead (Settings → Devices & services → Helpers → Dropdown) with the options Idle, Running and Done.

# configuration.yaml
input_select:
  washer_state:
    name: Washer
    icon: mdi:washing-machine
    options: [Idle, Running, Done]
  dryer_state:
    name: Dryer
    icon: mdi:tumble-dryer
    options: [Idle, Running, Done]

script:
  washer_emptied:
    alias: "Washer emptied"
    icon: mdi:basket-check
    sequence:
      - event: laundry_emptied
        event_data:
          machine: washer
  dryer_emptied:
    alias: "Dryer emptied"
    icon: mdi:basket-check
    sequence:
      - event: laundry_emptied
        event_data:
          machine: dryer

Leave out initial: on the dropdowns. Without it Home Assistant puts back the last state after a restart, so a load that was Done is still Done.

Then one automation per machine. This is the washer:

# automations.yaml
- id: laundry_washer
  alias: "Laundry: washer running → done → emptied"
  mode: queued
  triggers:
    - trigger: numeric_state
      entity_id: sensor.washer_power
      above: 20
      for: "00:01:00"
      id: start
    - trigger: numeric_state
      entity_id: sensor.washer_power
      below: 10
      for: "00:05:00"
      id: stop
    - trigger: state
      entity_id: binary_sensor.washer_door
      to: "on"
      id: emptied
    - trigger: event
      event_type: laundry_emptied
      event_data:
        machine: washer
      id: emptied
    # A restart throws away a half-finished "for 5 minutes" timer. This catches it.
    - trigger: homeassistant
      event: start
      id: boot
  actions:
    - choose:
        # Drawing power for a minute: a load has started (this also clears an old Done)
        - conditions:
            - condition: trigger
              id: start
          sequence:
            - action: input_select.select_option
              target:
                entity_id: input_select.washer_state
              data:
                option: Running
        # Quiet for 5 minutes after running: finished
        - conditions:
            - condition: trigger
              id: [stop, boot]
            - condition: state
              entity_id: input_select.washer_state
              state: Running
            - condition: numeric_state
              entity_id: sensor.washer_power
              below: 10
          sequence:
            - action: input_select.select_option
              target:
                entity_id: input_select.washer_state
              data:
                option: Done
        # Door opened or Emptied pressed, but only once it's finished
        - conditions:
            - condition: trigger
              id: emptied
            - condition: state
              entity_id: input_select.washer_state
              state: Done
          sequence:
            - action: input_select.select_option
              target:
                entity_id: input_select.washer_state
              data:
                option: Idle

For the dryer, copy it and change four things: every washer becomes dryer, the start line becomes above: 300, both “finished” lines become below: 200, and the finished wait becomes "00:03:00".

Those numbers came from one day’s power history on our machines. Yours will be different, so check yours before you copy mine (see Testing).

A dashboard card that only shows up when there’s something to do, plus the buttons:

type: vertical-stack
cards:
  - type: conditional
    conditions:
      - condition: state
        entity: input_select.washer_state
        state: Done
    card:
      type: markdown
      content: "## Washer DONE. Empty me!"
  - type: conditional
    conditions:
      - condition: state
        entity: input_select.dryer_state
        state: Done
    card:
      type: markdown
      content: "## Dryer DONE. Empty me!"
  - type: entities
    entities:
      - input_select.washer_state
      - input_select.dryer_state
      - script.washer_emptied
      - script.dryer_emptied

And if you want the phone told as well: one message when a machine finishes, which comes off the phone by itself once the machine is emptied. The tag ties the two together. Add a condition on person.example if you only want it sent while that person is home.

- id: laundry_phone
  alias: "Laundry: tell the phone"
  mode: queued
  triggers:
    - trigger: state
      entity_id:
        - input_select.washer_state
        - input_select.dryer_state
      to: Done
      id: done
    - trigger: state
      entity_id:
        - input_select.washer_state
        - input_select.dryer_state
      from: Done
      id: cleared
  actions:
    - choose:
        - conditions:
            - condition: trigger
              id: done
          sequence:
            - action: notify.mobile_app_example_phone
              data:
                title: "Laundry"
                message: "The {{ trigger.to_state.name | lower }} is done. Please empty it."
                data:
                  tag: "laundry_{{ trigger.to_state.object_id }}"
        - conditions:
            - condition: trigger
              id: cleared
          sequence:
            - action: notify.mobile_app_example_phone
              data:
                message: clear_notification
                data:
                  tag: "laundry_{{ trigger.to_state.object_id }}"

Step by step

  1. Put the plugs in and pair them. Leave them for a few loads with no automation at all.
  2. Open History for each plug’s power and look at a full load. Note what it draws while running, what it drops to when it’s finished, and any quiet patches mid-cycle (a soak, or the dryer’s anti-crease tumbles at the end).
  3. Pick your numbers: a “started” line well above standby, and a “finished” line and wait that a quiet patch mid-cycle won’t cross.
  4. Add the helpers and the two automations. You don’t need the door sensors yet: the Emptied buttons do the job until they arrive. I ran it that way for a day.
  5. Fit the door sensors. Before you stick them down, check in Developer tools → States that each one reads off with the door properly shut and on when it’s open.
  6. Add the dashboard card, and the phone message if you want it.
  7. Hide the plugs’ on/off switches from any voice assistant and keep them off your dashboards. Nobody should be able to turn the washer off mid-load by accident.

Testing it

  • Don’t switch the plug off to test anything. It cuts power to the machine. Test with real loads, or by setting states.
  • To check the card or the phone message without a load, pick Done in the Washer dropdown on the dashboard card, then open the door or press Emptied and watch it go back to Idle. If you didn’t test the door, put the dropdown back to Idle yourself.
  • Then watch real loads. Our washer runs about 41 minutes a load, and the dryer about 65 minutes at around 1.55 kW. The moment it shows Done should line up with the machine actually stopping.
  • If a load shows Done halfway through, the “finished” wait is shorter than a quiet patch in the cycle. Make it longer.
  • Open the automation’s Traces to see which branch ran on any change you didn’t expect.

Lessons learnt

The dryer keeps going after it’s finished. After the cycle it tumbles now and then, at about 170 W, so the clothes don’t crease. A line low enough for the washer would see those tumbles as the dryer still running. Look at a full load in History before you pick your numbers, especially the tail end.

A restart can lose the “finished” timer. A “below 10 W for 5 minutes” trigger only fires after the power drops and then stays down. If Home Assistant restarts while the timer’s counting, the timer is gone and nothing re-arms it, so the machine sits on Running forever. This bit me on a lighting automation once: a 45-minute timer never fired and a setting stayed stuck for about 22 hours. That’s why the automation checks again at start-up. Also, after a restart the Zigbee plugs read unknown for a minute or two. They only report when something changes. That’s not a fault.

A door sensor is only as good as the door. Another door sensor in our house reads open when the door is shut, because that door sits badly on its rollers. On a washer or dryer a door sensor that reads wrong either clears the reminder before anyone’s touched the washing, or never clears it. Check it reads shut with the door shut before you stick it down, and keep the Emptied button for when it plays up.

We turned the phone pushes off the same day. The first version sent a message to phones when a load finished. We took that off on day one and left it on the board, which stays lit until someone empties the machine. Try the board on its own first. You can always add the phone later.