Skip to content
The Wash Guide

Smart home

Laundry, meet your smart home.

The Wash Guide connects laundry to the smart home in two complementary ways, both free. Local publishing sends events from your phone directly to your own webhook or MQTT broker on your home network; those events never touch The Wash Guide’s servers. The Home Assistant integration connects through The Wash Guide cloud with a revocable connect key, sees the whole household, and keeps working when everyone is out. You can run either, or both. Reading is free on both paths. Acting from the smart home, posting a task, asking the house for a maintenance wash, or recording a clean, needs a PRO control key.

Path one

The Home Assistant integration

Three steps, and the household appears on your dashboard.

  1. Generate a connect key. In The Wash Guide app: Settings → Smart home → Generate connect key. Copy it; it’s shown only once, and you can revoke it from the same place at any time. PRO users can generate a control key here too, and paste both into Home Assistant at once.
  2. Install the integration. The Wash Guide is in the HACS default store, so this is a plain search: open HACS, search for The Wash Guide, download, and restart. Then Settings → Devices & services → Add integration → The Wash Guide, and paste the key. The control key box on that same screen is optional, and can be filled in later with Configure.Open The Wash Guide repository in HACS on your own Home Assistant

    That badge opens your own Home Assistant instance straight onto the repository page in HACS. The integration is open source and lives at github.com/romuluz/thewashguide-homeassistant. For a setup without HACS, copying custom_components/thewashguide into config/custom_components/ does the same job by hand.

  3. That’s it. Entities appear immediately, as one device.

What you get

  • sensor.pending_tasks

    Washes waiting on the household board. The pending list rides along as attributes, each task with its id, so a template sensor can slice it per person and the control actions below can name a task precisely. Each task carries a kind, either a wash (a load of laundry) or a machine job (the machine’s own maintenance wash).

  • sensor.detergent_shield

    The weakest item in the shared cupboard, as a percentage. The full cupboard (each product’s name, remaining doses and percentage) comes as attributes, which makes this the natural feed for shopping-list automations. Long-term statistics are recorded, so you get detergent burn-rate graphs for free.

  • sensor.last_wash

    A timestamp for the household’s most recent wash, with the item, shade, temperature and who ran it as attributes.

  • sensor.next_scheduled_wash

    A timestamp for the next standing wash, with what, who and cadence as attributes, plus the full schedule list. Standing schedules are a PRO feature in the app, so in a household without them this sensor simply has no value.

  • sensor.loads_since_machine_clean

    How many loads the drum has turned since its last maintenance wash, with clean_due, days_since_clean and the interval the app measures against as attributes. Statistics are recorded, so the sawtooth of a well-kept machine draws itself.

  • sensor.last_machine_clean

    A timestamp for when the machine was last cleaned, with who did it, the last filter clean, and the household’s recent care record as attributes.

  • binary_sensor.wash_overdue

    On when a pending wash has waited more than a day, or when the app’s nudge ladder is already active. The overdue tasks and their nudge levels come as attributes. The laundry-room lamp that slowly turns amber starts here.

  • binary_sensor.maintenance_wash_due

    On when the drum has earned its empty hot cycle: thirty loads or two months, whichever arrives first. The attributes include whether the job is already on the household board, so a panel can offer to run it rather than ask for it twice.

  • binary_sensor.filter_due

    On when the pump filter is due, a quarterly job that is time-based only, because a filter fills with lint and coins rather than loads.

  • sensor.<name>_personality

    One per household member: their wash personality title, with tier, 30-day wash count and 30-day rewards (in the household’s own reward currency) as attributes.

Events for automations

Five events land on the Home Assistant bus: four as fresh activity arrives from the household, and a fifth that never touches the cloud at all.

  • thewashguide_wash_logged (fields: ts, by, item, shade, temp_c)
  • thewashguide_task_created (fields: summary, for, kind, tokens, created_at)
  • thewashguide_task_completed (fields: summary, for, kind, tokens, completed_at)
  • thewashguide_machine_cleaned (fields: kind (MAINTENANCE_WASH or FILTER_CLEAN), ts, by)
  • thewashguide_cycle_finished (fields: entity_id, started_at, ended_at, duration_seconds, energy_kwh, peak_watts, average_watts)

    Local-only and immediate: it fires the moment a cycle is detected as finished, needs no PRO and no working internet, and only exists once a power sensor has been chosen. The measured machine, below, is its story.

Example automation
automation:
  - alias: "Celebrate a finished wash task"
    trigger:
      - platform: event
        event_type: thewashguide_task_completed
    action:
      - service: notify.family
        data:
          message: >
            {{ trigger.event.data.for }} finished
            "{{ trigger.event.data.summary }}"
            {% if trigger.event.data.tokens %}
            and earned {{ trigger.event.data.tokens }} rewards
            {% endif %}.

Data updates every 15 minutes, or every minute for a PRO household. The cloud tells the integration which cadence the plan allows and it paces itself, so an upgrade takes effect on the next poll with nothing to reconfigure. Anyone who would rather poll less often than their plan allows can set an interval in the integration’s options; it can never poll more often.

The connect key grants read-only access to the household’s laundry status: tasks, cupboard level, wash personalities, recent washes and the machine’s care record. It cannot log washes, change anything, or read anything outside the household. Revoking the key in the app cuts access on the next refresh.

The measured machine

Optional, and it asks for one thing: the washing machine on a plug that meters power, or a machine that reports power through its own integration. Pick the power sensor while adding The Wash Guide, at setup or any time later with Configure. Skip it, and everything above works exactly as before.

From there, the integration reads the machine’s own power curve. Watts rise, and a cycle is running. Ten straight minutes of quiet, and it finished. A soak, a mid-cycle pause and anti-crease tumbling are forgiven rather than mistaken for the end, and a blip too short to have washed anything is discarded.

One cycle, as the power curve tells it
A washing machine cycle, read from its power curveA stepped line of power against time. The cycle starts and power rises; heating the water uses the most, just over two kilowatts. Power falls to a low tumble, then to a silent soak, which is not the end. A spin follows. After ten quiet minutes the cycle is recorded as finished, and the end is recorded where the quiet began, so waiting to be sure never stretches the time.0 W1,000 W2,000 W0 min510152025 minThe cycle startsHeating the water uses the most powerA quiet soak.The cycle has not finished.SpinTen quiet minutes: the cycle is finished.The end is recorded where the quiet began,so waiting to be sure never stretches the time.

13 minutes running · 0.18 kWh · peak 2,100 W Only this summary leaves your home.

When a cycle finishes, two things happen. Locally, thewashguide_cycle_finishedfires with the cycle’s start, end, duration, energy and power figures, ready for a machine’s-done notification that knows what the cycle actually drew. And a small summary of the same facts joins the household’s record in The Wash Guide, which is how the app comes to know what a wash on this household’s own machine really takes.

Detection happens entirely in your home. Only the summary is uploaded: start, end, kilowatt-hours and two watt figures. The raw power readings never leave the house.

It watches, and it never switches. If the plug can switch, The Wash Guide never touches the switch, and it never puts itself in the machine’s power path.

Use a plug rated for the job. A washing machine heats its own water at 2 to 3 kW, so the plug should be one properly rated for a heating appliance, not a lamp-grade one.

What the app makes of it

The app learns your machine’s own timings, and speaks only when it can be honest. On the dosing calculator, under what the load needs, a PRO line reads:

On your machine, a wash like this one usually takes about 1 hour 50 minutes and costs about 12p.

Every rung of the ladder behind that sentence is the app choosing to say less. It speaks for a temperature band only once three cycles in that band agree. With too few there but five anywhere, it drops to the humbler subject, “a wash”, rather than pretending the number is about this particular load. Below that it says nothing at all. If the cycles disagree widely, the single figure becomes a range, because one number would lie. Figures round to five minutes. And it never names a temperature, saying “a wash like this one” instead, because your dial may say Quick 40 while the drum does something else.

The cost clause needs two priced cycles, on the principle that one electricity bill is an anecdote and two start an average. It is a median, never a mean, and it comes in the household’s own currency: 12p in Britain, €0.32 in Dublin, ¥30 in Tokyo. A cycle priced in another currency never pollutes the average.

The price itself is one plain setting, in the app under Settings then Setup, beneath the drum size: Electricity price, explained in the app’s own words as “Used to work out what a measured wash cycle costs. Your electricity bill shows this as the unit rate, the price for each kilowatt-hour (kWh).” It is the number every bill prints. There is no tariff machinery to configure and no account needed; the setting lives on the phone, survives signing out, and never joins the cloud profile.

And a cycle is priced once, never re-priced. That is a promise about honesty rather than a feature: the cost of a wash is stamped with the rate that was true the night it ran, and a later change to your electricity price never rewrites history. A cycle priced long after it ran is stamped with today’s rate, and records that it was.

The free half is a question. When you log a wash and the drum finished something recently that nothing has claimed, the confirm sheet carries a toggle, on by default:

The machine finished a cycle at 9:55 PM. It ran for 1 hour 55 minutes and used 0.4 kWh. Was that this wash?

The line between free and PRO sits exactly there, and it is worth drawing plainly: the events are free, and the learned reading is PRO. What the plug measured is a fact the drum produced, the same fact the local bus event carries, so kilowatt-hours can appear on a free household’s confirm sheet. What the app concludes from many cycles is the paid part.

Most of the time nobody is asked anything: if a wash is logged near the cycle, the two are paired automatically, and the question only appears when the pairing is genuinely uncertain. Undoing a wash releases its cycle again.

Four recipes

The honest machine-is-done, first, because it’s the one everybody wants and it works free, offline and without PRO:

Washing machine is done
automation:
  - alias: "Washing machine is done"
    trigger:
      - platform: event
        event_type: thewashguide_cycle_finished
    action:
      - service: notify.family
        data:
          message: >
            The wash is done. It ran
            {{ (trigger.event.data.duration_seconds / 60) | round }}
            minutes and used {{ trigger.event.data.energy_kwh }} kWh.
            Hang it now and it will not crease.

What makes it different from the usual power-plug blueprint: the event is told the duration and the energy rather than guessing from a threshold, and it has already survived soaks and anti-crease tumbling.

The forgotten log is the pairing only The Wash Guide can make: a cycle ran, but nobody logged a wash. Ten minutes after a cycle finishes, if sensor.last_washis still old, nudge the house: “The machine just ran a 45-minute cycle, but no wash was logged in The Wash Guide. Was that you? Log it in the app so your detergent stock stays correct.”

The maintenance wash that logs itself closes the loop with the PRO control key, and its YAML is already on this page, just below: when binary_sensor.maintenance_wash_due is on and a helper marks that the empty hot cycle is running, a finished cycle calls thewashguide.log_machine_clean, which also completes the board’s task.

The energy anomaly line watches for a cycle whose energy_kwhlands well above the machine’s own average, from a statistics sensor over past cycles: “The last wash used 0.9 kWh. That is much more than this machine usually uses. If it keeps happening, limescale may be building up on the heating element. A maintenance wash helps remove it; you can set one up on The Wash Guide’s Machine care page.”

Two quieter ones deserve a line each: a lamp that turns pale sage instead of a phone notification after 21:30, and a pair of trigger-based template sensors that turn the event into last-wash energy and last-wash duration entities for a dashboard.

Taking action (PRO)

With PRO, the smart home stops being read-only. Alongside the free connect key, the app can generate a control key (Settings → Smart home → Generate control key; the key list labels each key’s scope plainly). Paste it into the integration, at setup or later with Configure, and four services appear:

  • thewashguide.create_task: post an open wash task to the household board, which anyone in the house can claim. The summary, note, load facts and a reward are all optional; a bare call posts a mixed, medium load. Attaching a reward requires an operator.
  • thewashguide.cancel_task: cancel a task still waiting on the board. Operators may cancel any pending task; everyone else, their own.
  • thewashguide.request_maintenance_wash: put the machine’s maintenance wash on the board. Open to the house by default, because the machine is nobody’s in particular; name a member to hand it to them.
  • thewashguide.log_machine_clean: record that the machine was looked after, either the maintenance wash or the filter. If the maintenance wash is already waiting on the board, this completes that task rather than logging a second, parallel truth.

The key’s owner is the actor: your automations act as you, under the same household rules as the app itself. Rewards need an admin or a manager, juniors cannot create tasks at all, and a connect key presented where a control key is required is refused.

One line is worth explaining, because it’s a design position rather than a limitation: a wash task cannot be completed from the smart home, and a maintenance wash can. Completing a wash means a person ran a load of laundry, spent detergent from the cupboard and added to their wash history, and an automation cannot honestly claim any of that. A maintenance wash is the opposite case. An empty drum at 90 degrees produces no laundry, spends no detergent and enters no wash log; the only thing it produces is the fact that it ran, and a washing machine that has just finished its cycle knows that fact better than the person who will remember to mention it on Thursday.

The machine asks, and answers, for itself
automation:
  - alias: "The machine asks for its own maintenance wash"
    trigger:
      - platform: state
        entity_id: binary_sensor.maintenance_wash_due
        to: "on"
    condition: >
      {{ not state_attr('binary_sensor.maintenance_wash_due',
                        'on_the_board') }}
    action:
      - service: thewashguide.request_maintenance_wash
        data:
          note: "The drum has done thirty loads since the last one."

  - alias: "And logs it when the cycle finishes"
    trigger:
      - platform: state
        entity_id: sensor.washing_machine_power
        below: 5
        for: "00:05:00"
    condition: "{{ is_state('input_boolean.running_maintenance_wash', 'on') }}"
    action:
      - service: thewashguide.log_machine_clean
        data:
          kind: maintenance_wash
      - service: input_boolean.turn_off
        target:
          entity_id: input_boolean.running_maintenance_wash

For anyone who would rather not install the integration, the control endpoint also answers a plain rest_command. The app shows its address beside the key.

Control via rest_command
rest_command:
  washguide_task:
    url: !secret washguide_control_url
    method: post
    headers: { x-api-key: !secret washguide_control_key }
    content_type: application/json
    payload: '{"action":"create_open_task","summary":"{{ summary }}"}'

Path two

Local webhook and MQTT

In the app: Settings → Smart home → switch on Publish to your smart home, then fill either or both of:

  • Webhook URL, e.g. http://homeassistant.local:8123/api/webhook/washguide
  • MQTT broker, e.g. mqtt://192.168.1.10:1883 (or mqtts:// for TLS), with optional username and password, and a topic prefix (default thewashguide).

A Send a test event button confirms the wiring. Events are sent by the phone at the moment things happen, entirely on the local network: they never touch The Wash Guide’s servers, so this is the private-by-construction option, with the natural trade-off that the phone must be home and the app in use for events to flow.

Topics

<prefix>/state carries the retained household snapshot. Fresh activity arrives on <prefix>/event/wash, <prefix>/event/task, <prefix>/event/care and <prefix>/event/test. Every payload carries a type and an ISO ts. The webhook receives all of them at its single URL, and fans out on type.

The retained state carries the household (name, label, pending task count, detergent shield percentage), the machineblock, the cupboard item by item (weakest first), the device’s own active profile and every member.

The retained state
{
  "type": "state",
  "ts": "2026-07-14T20:10:00Z",
  "household": {"name": "Townsend", "label": "Villa",
                "pending_tasks": 2, "shield_pct": 20},
  "machine": {"loads_since_clean": 31, "clean_due": true,
              "filter_due": false,
              "last_clean": "2026-05-30T09:00:00Z",
              "last_filter": "2026-06-30T09:00:00Z"},
  "cupboard": [{"id": "persil_bio_liquid", "name": "Persil Bio Liquid",
                "remaining": 200, "pack_size": 1000, "pct": 20}],
  "active_profile": {"name": "Nick", "profile_id": "WASH_WIZARD",
                     "profile_title": "Titan of the Rinse", "tier": 3},
  "members": [...]
}
A wash event payload
{
  "type": "wash_logged",
  "ts": "2026-07-06T20:44:57Z",
  "profile": {"name": "Hermes", "profile_title": "The Clean Slate", "tier": 0},
  "wash": {"item": "TSHIRTS", "shade": "WHITE", "fabric": "COTTON",
           "load": "MEDIUM", "temp_c": 40, "detergent": "Ariel Platinum Pods",
           "dose": 1, "unit": "pods", "soiled": false, "sensitive": false}
}

Task events (task_created, task_claimed, task_completed, task_cancelled) carry a kind of wash or machine, with the summary, who it’s for, who acted, and any reward attached. The care event fires the moment the machine is looked after; its loads_since_clean is the count the clean has just reset, which is the number the drum actually ran on.

A care event payload
{
  "type": "care_logged",
  "ts": "2026-07-14T20:10:00Z",
  "kind": "MAINTENANCE_WASH",
  "by": "Nick",
  "loads_since_clean": 34
}
Ready-paste MQTT sensors
mqtt:
  sensor:
    - name: "Wash Guide pending tasks"
      state_topic: "thewashguide/state"
      value_template: "{{ value_json.household.pending_tasks }}"
    - name: "Wash Guide loads since machine clean"
      state_topic: "thewashguide/state"
      unit_of_measurement: "loads"
      value_template: "{{ value_json.machine.loads_since_clean }}"
  binary_sensor:
    - name: "Wash Guide maintenance wash due"
      state_topic: "thewashguide/state"
      device_class: problem
      value_template: "{{ 'ON' if value_json.machine.clean_due else 'OFF' }}"
Webhook trigger
automation:
  trigger:
    - platform: webhook
      webhook_id: washguide
      local_only: true
  condition: "{{ trigger.json.type == 'wash_logged' }}"
  action: ...

Zero YAML, if you’d rather

Send a test event also publishes MQTT auto-discovery configs, retained on the broker, so six local sensors appear with no YAML at all: pending tasks, the detergent shield (with the cupboard as attributes), loads since the machine clean, the last maintenance wash, and the two due flags. The manual YAML above becomes optional. MQTT only; a webhook has nothing to discover.

One honest note, because MQTT people will ask: there’s no availability topic. The phone connects, publishes and disconnects, so a last will would announce the app offline seconds after every message, and an expiry would retire a perfectly true reading from a household that simply has not washed this week. The retained snapshot is the last thing we knew, and it stays true until we know better.

Questions

The short answers

Which path should I choose?

The integration, unless you specifically want nothing to leave your network; then local MQTT. You can also run both side by side.

What does the cloud path share?

The household’s laundry status only: the task board, cupboard level, wash personalities, recent wash summaries and the machine’s care record. No account credentials, no location, no contacts. A connect key is read-only, and access dies with the key. A PRO control key can also act on the board, under the same household rules as its owner.

Can Home Assistant control my washing machine?

No, and nothing here pretends to. The Wash Guide knows what your household is washing and how the machine is being kept; your machine’s own integration, if it has one, is what starts a cycle. The two work well together: your machine reports that a cycle finished, and The Wash Guide records what it meant.

What does the power sensor add?

With a metering plug chosen at setup or later with Configure, The Wash Guide notices cycles from the power curve: a local event the moment one finishes, and a small summary (start, end, energy) added to the household’s record. It watches; it never switches. Skip it and everything else works exactly as before.

Does it drain my phone?

No. Local publishing sends tiny messages at the moment you act; the integration polls from Home Assistant, not the phone.

Do I need a subscription or port forwarding?

No. The integration connects outward from Home Assistant, so no remote access, Nabu Casa subscription or router changes are needed. PRO is only needed to act from Home Assistant, never to read.

Is it free?

Reading is, on both paths, for every user, and so is MQTT auto-discovery. Acting needs PRO: posting a task, asking the house for a maintenance wash, or recording that the machine was cleaned.

Can my automations change things, not just watch?

With a PRO control key, yes: they can post tasks to the household board, cancel pending ones, ask for the machine’s maintenance wash and record that it was done, as the key’s owner and under the household’s own rules. A wash task is the one thing they cannot complete; completing means a person ran a load and spent detergent from the cupboard, and an automation cannot honestly claim that. A maintenance wash is the opposite case, and it can.

Over to you

Show us what you build.

The best automations will come from you: the amber lamp, the shopping list that fills itself, the speaker that congratulates whoever emptied the basket. Send your recipes to hello@thewashguide.app and we’ll share the best of them here.