A notification about a leak is only as fast as the person reading it. A washing machine hose that lets go while the house is empty can put through gallons a minute for hours. This recipe does not wait for you. The moment any leak sensor goes wet it drives your shutoff valve closed, so the water stops within seconds of the first drop reaching a sensor.
This is the companion to the notify-only Water Leak Alert recipe. That one tells you; this one acts, and tells you as well. It needs a motorised shutoff valve on a relay, so it is a step up in hardware, and worth it on the mains feed or the line to a washing machine or water heater.
Closing the valve the right way
Motorised valves are not consistent about which way a relay closes them. Some close when the relay switches off, others when it switches on. The recipe asks you which, defaulting to closing on off, so it matches your valve rather than assuming. Once it closes the valve it leaves it closed: reopening is deliberate, on you, after you have found and dealt with the leak, so a sensor drying out does not quietly turn the water back on.
Cutting the appliance and raising the alarm
Add the appliance’s smart plug as the optional role and the recipe switches it off with the valve, so a washing machine pump is not left running against a closed valve. Add a siren and it sounds, so a leak in a room no one is in is not silent while the water is cut. Add speakers and the shutoff is announced out loud, over a speech engine your Home Assistant already has and with no paid subscription needed.
Files Selora AI's pipeline reads to install this recipe. The manifest declares roles, inputs, and integrations; the package files are Jinja-templated YAML applied to Home Assistant.
# Leak Auto Shutoff — close a motorised shutoff valve the moment any leak
# sensor goes wet, and optionally cut the appliance, sound a siren, and
# announce it.
#
# Distinct from the notify-only water-leak-alert recipe: this one acts. The
# relay role drives the valve; a select input covers valves that close on off
# and valves that close on on, rendered straight into the service name
# (switch.turn_off or switch.turn_on). The valve is left closed after a leak,
# never reopened automatically, so drying out does not restore the water.
slug: leak-auto-shutoff
version: 1.0.0
title: Leak Auto Shutoff
tagline: A wet leak sensor closes the shutoff valve within seconds, and can cut the appliance and raise the alarm.
description: >-
Closes a motorised shutoff valve the instant any leak sensor goes wet, so a
burst hose stops flooding within seconds rather than waiting on a
notification. Optionally switches off the appliance's smart plug, sounds a
siren, and announces over TTS. Leaves the valve closed until you reopen it.
author: Selora AI
released: "2026-08-28"
min_integration_version: "0.12.0"
tags: [safety, water-leak, shutoff]
roles:
# Leak sensors — multi-select. binary_sensor + device_class moisture.
# min_count 1.
- id: leak_sensors
title: Water leak sensors
kind: binary_sensor
device_class: moisture
min_count: 1
selection: required
description: >-
The leak sensors that trigger the shutoff. Any one going wet closes the
valve.
# Shutoff valve relay — the valve control. kind switch. Single: one main
# valve. min_count 1.
- id: shutoff_relay
title: Shutoff valve relay
kind: switch
min_count: 1
max_count: 1
selection: required
description: >-
The relay wired to your motorised shutoff valve. The recipe drives it to
close the valve.
# Optional appliance smart plugs cut with the valve. kind switch. min_count 0.
- id: shed_plugs
title: Appliance smart plugs
kind: switch
min_count: 0
selection: required
description: >-
Optional. Smart plugs carrying the appliance behind the leak, switched off
with the valve. Leave empty to close the valve only.
# Optional sirens. kind siren. min_count 0.
- id: sirens
title: Sirens
kind: siren
min_count: 0
selection: required
description: >-
Optional. Sirens that sound when the valve closes. Leave empty for a
silent shutoff.
# Optional announcement speakers over TTS. media_player. min_count 0.
- id: announce_speakers
title: Announcement speakers
kind: media_player
min_count: 0
selection: required
description: >-
Optional. Speakers that announce the shutoff over TTS in addition to the
push notification. Leave empty to be notified on your phone only.
inputs:
# How the relay closes the valve. Some valves close when the relay switches
# off, others when it switches on. Rendered into the service name.
# How long the optional siren sounds. A leak siren with no stop path runs
# until somebody comes home to it, so the recipe owns switching it off the
# same way the away-motion recipe does. Longer than that recipe's 30 seconds
# because a leak should keep sounding long enough to be noticed from another
# floor, but still bounded.
- id: siren_seconds
type: number
label: Siren duration (seconds)
description: >-
How long the optional siren sounds after the valve closes.
default: 120
min: 1
max: 600
- id: shutoff_command
type: select
label: Closing the valve
description: >-
Whether the relay closes the valve by switching off or switching on. Match
it to your valve.
default: "turn_off"
choices: ["turn_off", "turn_on"]
# TTS engine entity for the optional spoken announcement. Auto-resolved by the
# tts_engine resolver from the home's configured tts.* entities, so the wizard
# never shows this field. tts.speak works without a paid Home Assistant Cloud
# subscription. The resolver returns an empty string when the home has no TTS
# engine, and the template then skips the announcement. The default below is
# only used by the offline recipe-validation gate.
- id: tts_engine
type: string
label: Text-to-Speech engine
description: >-
Speech engine entity used for the spoken announcement. Resolved
automatically from the Text-to-Speech engines configured in Home
Assistant.
resolver: tts_engine
default: "tts.piper"
package_files:
- package/automations/shutoff.yaml.j2
{# Leak Auto Shutoff: on any leak sensor going wet, close the valve, optionally
cut the appliance plugs, notify, and optionally sound the siren and announce
over TTS.
The valve is closed by rendering the chosen command straight into the
service name (switch.turn_off or switch.turn_on), so the recipe matches how
the installer's valve is wired. The recipe then waits, briefly, for the
relay to report shut, and the notification and announcement say plainly
whether it did, because a leak alert that claims a closed valve it never
closed is worse than no alert at all. What a relay reports is the relay, not
the water, so the wording asks the reader to confirm the flow has stopped
rather than telling them it has. It never reopens the valve: after a leak the
valve stays closed until a person reopens it, so a sensor drying out does not
silently restore the water.
mode: single, and the leak sensors are watched for the wet state only, so a
second sensor tripping while the first is still handled does not re-run.
A second automation switches the siren off once it has sounded for its set
time. siren.turn_on does not oblige an entity to stop by itself, so without
it a leak at the start of a week away would leave the alarm sounding until
somebody came home to it. #}
{# The relay state that means "shut", derived from the wiring choice at install
time so the alert can check whether the valve actually got there. #}
{% set shut_state = 'off' if inputs.shutoff_command == 'turn_off' else 'on' %}
automation:
- id: selora_recipe_{{ slug | replace('-', '_') }}_shutoff
alias: "Leak Auto Shutoff: close valve on leak"
description: >-
When any leak sensor goes wet, close the shutoff valve and raise the
alarm.
mode: single
trigger:
{% for sensor in roles.leak_sensors %}
- platform: state
entity_id: {{ sensor }}
to: "on"
{% endfor %}
action:
- variables:
source: "{% raw %}{{ trigger.to_state.name | default(trigger.entity_id, true) }}{% endraw %}"
# continue_on_error on both device calls. An unavailable relay or plug
# raises, and without this the sequence aborts before the notification,
# the siren and the announcement: the one moment the household most needs
# telling is the moment it would hear nothing at all. Failing to close the
# valve must make the alert louder, not silence it.
- service: switch.{{ inputs.shutoff_command }}
continue_on_error: true
target:
entity_id: {{ roles.shutoff_relay[0] }}
{% if roles.shed_plugs %}
- service: switch.turn_off
continue_on_error: true
target:
entity_id:
{% for plug in roles.shed_plugs %}
- {{ plug }}
{% endfor %}
{% endif %}
# Wait for the relay to report shut rather than reading it the instant
# after the command. Many relays report optimistically or a second or two
# late, and an immediate read turns that lag into a false alarm. Ten
# seconds is long enough for a relay to answer and short enough that a
# genuine failure still raises the alarm promptly. continue_on_timeout so
# a silent relay falls through to the warning instead of ending the run.
- wait_template: "{% raw %}{{ is_state('{% endraw %}{{ roles.shutoff_relay[0] }}{% raw %}', '{% endraw %}{{ shut_state }}{% raw %}') }}{% endraw %}"
timeout: "00:00:10"
continue_on_timeout: true
- variables:
valve_shut: "{% raw %}{{ wait.completed }}{% endraw %}"
- service: notify.notify
data:
title: "{% raw %}{{ 'Water leak: valve reports closed' if valve_shut else 'WATER LEAK: valve has NOT reported closed' }}{% endraw %}"
message: "{% raw %}{{ 'Water was detected by ' ~ source ~ '. The shutoff valve was told to close and reports closed. Check the water has actually stopped, then find and clear the leak before reopening it.' if valve_shut else 'Water was detected by ' ~ source ~ ' and the shutoff valve has not reported closed. Go and shut the water off by hand now.' }}{% endraw %}"
{% if roles.sirens %}
- service: siren.turn_on
target:
entity_id:
{% for siren in roles.sirens %}
- {{ siren }}
{% endfor %}
{% endif %}
{% if roles.announce_speakers and inputs.tts_engine %}
- service: tts.speak
continue_on_error: true
target:
entity_id: {{ inputs.tts_engine }}
data:
media_player_entity_id:
{% for player in roles.announce_speakers %}
- {{ player }}
{% endfor %}
message: "{% raw %}{{ 'A water leak was detected. The shutoff valve reports closed. Check the water has stopped.' if valve_shut else 'A water leak was detected and the shutoff valve has not reported closed. Shut the water off by hand now.' }}{% endraw %}"
{% endif %}
{% if roles.sirens %}
- id: selora_recipe_{{ slug | replace('-', '_') }}_siren_stop
alias: "Leak Auto Shutoff: stop the siren"
description: >-
Switch the siren off once it has been sounding for its set time.
mode: single
trigger:
{% for siren in roles.sirens %}
- platform: state
entity_id: {{ siren }}
to: "on"
for:
seconds: {{ inputs.siren_seconds }}
{% endfor %}
# A state trigger's for: timer is discarded on restart, and a siren that
# is already on never transitions to on again, so the timed stop would
# never arrive and the alarm would sound until someone silenced it by
# hand. At startup any siren still sounding has already been going since
# before the restart, so the right move is to stop it.
- platform: homeassistant
event: start
condition:
- condition: or
conditions:
{% for siren in roles.sirens %}
- condition: state
entity_id: {{ siren }}
state: "on"
{% endfor %}
action:
- service: siren.turn_off
target:
entity_id:
{% for siren in roles.sirens %}
- {{ siren }}
{% endfor %}
{% endif %}