Files
haos-blueprints/Common/Washing Machine.yaml

551 lines
22 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# Washing Machine / Dryer Notifications Blueprint for Home Assistant
# =============================================================================
# This blueprint monitors washing machine or dryer appliances and sends
# notifications for various events throughout the wash/dry cycle.
#
# Features:
# - Start notification with cycle duration and mode details
# - Completion notification (reminder to unload clothes)
# - "Almost done" notification (configurable minutes before end)
# - Error message notifications
# - Preparation mode notification (e.g., for dryer prep)
# - Tub/drum cleaning reminder based on wash counter
#
# State Machine:
# The automation tracks the appliance through these states:
# 1. IDLE: Waiting for cycle to start
# 2. RUNNING: Cycle in progress (start notification sent)
# 3. FINISHING: Near completion (time-to-end notification sent)
# 4. COMPLETED: Cycle done (completion notification sent, state reset)
#
# Persistent State Keys:
# - nass: Notification About Start Sent
# - nart: Notification About Remaining Time Sent
# - naps: Notification About Preparation Sent
#
# Requirements:
# - Sensors for: remaining time, run state, error messages
# - input_text entity for persistent state storage
# - Notification service entity
#
# Note: Default values are in Russian for LG ThinQ integration.
# Adjust run_state_completion_id and other defaults for your language.
#
# Author: Alexei Dolgolyov (dolgolyov.alexei@gmail.com)
# =============================================================================
blueprint:
name: "Custom: Washing Machine Notifications"
description: >
Sends notifications when washing starts (with mode details),
when errors occur, and when run completes.
domain: automation
# ===========================================================================
# INPUT CONFIGURATION
# ===========================================================================
input:
# -------------------------------------------------------------------------
# Time & State Sensors
# -------------------------------------------------------------------------
# Core sensors for tracking the appliance cycle state
time_group:
name: "Time"
collapsed: false
input:
remaining_time_sensor:
name: Remaining Time Sensor
description: >
Sensor that contains remaining time in format `hh:mm:ss`.
Note: `-`, 'unknown' values mean remaining time is not available.
selector:
entity:
domain: sensor
run_state_sensor:
name: Run State Sensor
description: "Sensor that reports the run state of the device"
selector:
entity:
domain: sensor
run_state_completion_id:
name: Run State Completion ID
description: "Identifier of run state that indicates that the cycle is completed"
default: 'Цикл завершен.'
selector:
text:
notify_time_to_end:
name: Notify Time-to-End (minutes)
description: "Send notification when cycle is about to finish (minutes remaining)"
default: 10
selector:
number:
min: 1
max: 60
unit_of_measurement: minutes
mode: slider
# -------------------------------------------------------------------------
# Persistent State Configuration
# -------------------------------------------------------------------------
# Stores automation state between triggers to track notification history
persistent_state:
name: "Persistent State"
collapsed: false
input:
automation_state_entity:
name: Automation state entity
description: >
`input_text` entity that stores the automation state in JSON format.
Required for all features to function properly.
Doesn't require specific initial state - values can be empty.
selector:
entity:
domain: input_text
automation_state_placeholder_key:
name: Automation state placeholder key
description: >
Overrides key for persistent storage if not empty.
By default uses the remaining time sensor entity ID.
Don't override if you don't understand the meaning.
default: ''
selector:
text:
# -------------------------------------------------------------------------
# Notification Settings
# -------------------------------------------------------------------------
notification_group:
name: "Notification"
collapsed: false
input:
input_device_name:
name: "Device Name"
description: "Device name used in notification messages"
default: "Стиральная машина"
selector:
text:
notify_target:
name: Notification Target
description: "Notification service entity to send messages to"
selector:
entity:
domain: notify
# -------------------------------------------------------------------------
# Device Information Sensors
# -------------------------------------------------------------------------
info_group:
name: "Info"
collapsed: false
input:
non_running_state_ids:
name: Non Running State ID(s)
description: >
List of run state ID(s) that indicate the device is not actively
running a cycle (e.g., 'Pause', 'Standby', 'Ready')
default: []
selector:
text:
multiple: true
preparation_state_id:
name: Preparation Mode State ID (optional)
description: >
Optional run state ID that indicates the device is preparing
(e.g., dryer heating up before starting)
default: 'Подготовка к сушке'
selector:
text:
error_message_sensor:
name: Error Message Sensor
description: "Sensor that reports error messages from the device"
selector:
entity:
domain: sensor
tub_clean_counter_sensor:
name: Tub Clean Counter Sensor (optional)
description: "Sensor that reports the number of cycles since last tub cleaning"
selector:
entity:
domain:
- sensor
- input_number
tub_clean_counter_threshold:
name: Tub Clean Counter Threshold
description: >
Number of cycles after which a tub cleaning reminder is sent.
Set to 0 to disable tub cleaning notifications.
default: 30
selector:
number:
min: 0
max: 100
mode: slider
# -------------------------------------------------------------------------
# Startup Details (optional additional info in start notification)
# -------------------------------------------------------------------------
details_group:
name: "Details"
collapsed: false
input:
startup_info_sensors:
name: Startup Info Sensors (optional)
description: >
List of sensors with additional details to include in the
startup notification (e.g., temperature, spin speed, program)
default: []
selector:
entity:
domain:
- sensor
- binary_sensor
multiple: true
startup_info_texts:
name: Startup Info Texts
description: >
Labels for each sensor in `Startup Info Sensors` list.
Must have the same number of entries as sensors.
default: []
selector:
text:
multiple: true
# =============================================================================
# AUTOMATION MODE
# =============================================================================
# Restart mode ensures new triggers interrupt any running automation
# (useful for rapid state changes)
mode: restart
# =============================================================================
# TRIGGERS
# =============================================================================
# Monitor all relevant sensors for state changes
trigger:
# Remaining time changes (tracks cycle progress)
- platform: state
entity_id: !input remaining_time_sensor
# Error message appears
- platform: state
entity_id: !input error_message_sensor
# Run state changes (start, stop, pause, etc.)
- platform: state
entity_id: !input run_state_sensor
# Tub clean counter changed
- platform: state
entity_id: !input tub_clean_counter_sensor
# =============================================================================
# CONDITIONS
# =============================================================================
# No global conditions - individual cases handle their own logic
condition: []
# =============================================================================
# VARIABLES
# =============================================================================
variables:
# ---------------------------------------------------------------------------
# Persistent State Keys
# ---------------------------------------------------------------------------
# Short keys to minimize JSON storage size in input_text entity
state_notification_about_remaining_time_sent: 'nart' # "Almost done" notification sent
state_notification_about_start_sent: 'nass' # Start notification sent
state_notification_about_preparation_sent: 'naps' # Preparation notification sent
# ---------------------------------------------------------------------------
# Input Variables
# ---------------------------------------------------------------------------
run_state_sensor: !input run_state_sensor
non_running_state_ids: !input non_running_state_ids
preparation_state_id: !input preparation_state_id
error_message_sensor: !input error_message_sensor
remaining_time_sensor: !input remaining_time_sensor
notify_target: !input notify_target
notify_time_to_end: !input notify_time_to_end
input_device_name: !input input_device_name
tub_clean_counter_sensor: !input tub_clean_counter_sensor
tub_clean_counter_threshold: !input tub_clean_counter_threshold
run_state_completion_id: !input run_state_completion_id
# ---------------------------------------------------------------------------
# Computed State Values
# ---------------------------------------------------------------------------
remaining: "{{ states(remaining_time_sensor) }}"
device_name: "{{ input_device_name }}"
run_state: "{{ states(run_state_sensor) }}"
# Determine if the appliance is actively running a cycle
# Excludes: unknown states, non-running states, preparation, and completion
is_running: >
{{ run_state not in ['unknown', 'unavailable', 'waiting', '-']
and run_state not in non_running_state_ids
and run_state != preparation_state_id
and run_state != run_state_completion_id
and remaining not in ['unknown', 'unavailable'] }}
# Parse remaining time string (hh:mm:ss) to total minutes
remaining_time_in_minutes: >
{% if remaining not in ['unknown', 'unavailable', '-'] %}
{% set parts = remaining.split(':') %}
{% if parts | length >= 2 %}
{% set total = parts[0]|int * 60 + parts[1]|int %}
{{ total }}
{% else %}
{{ 0 }}
{% endif %}
{% else %}
{{ 0 }}
{% endif %}
# ---------------------------------------------------------------------------
# Persistent State Management
# ---------------------------------------------------------------------------
automation_state_entity: !input automation_state_entity
# Parse global state JSON from input_text entity
automation_state_global: >
{% set text = states(automation_state_entity) | string %}
{% if text in ['unknown','unavailable','none',''] %}
{{ dict() }}
{% else %}
{{ text | from_json }}
{% endif %}
automation_state_placeholder_key: !input automation_state_placeholder_key
# Determine the key for this automation's state
automation_state_key: >
{% if automation_state_placeholder_key != '' %}
{{ automation_state_placeholder_key }}
{% else %}
{{ remaining_time_sensor }}
{% endif %}
# Get this automation's state from global state
automation_state: "{{ automation_state_global.get(automation_state_key, dict()) }}"
# ---------------------------------------------------------------------------
# Debug Flag
# ---------------------------------------------------------------------------
is_debug: false
# =============================================================================
# ACTIONS
# =============================================================================
action:
# ---------------------------------------------------------------------------
# DEBUG: Log current state (enable by setting is_debug: true)
# ---------------------------------------------------------------------------
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info - Washing Machine"
message: >
run_state = {{ run_state }},
non_running_state_ids = {{ non_running_state_ids }},
remaining_time = {{ states(remaining_time_sensor) }},
is_running = {{ is_running }},
start_notification_sent = {{ automation_state.get(state_notification_about_start_sent, false) }}
# ===========================================================================
# MAIN STATE MACHINE - Handle different cycle events
# ===========================================================================
- choose:
# -----------------------------------------------------------------------
# CASE 1: Cycle Started
# -----------------------------------------------------------------------
# Triggered when: Device starts running and start notification not yet sent
# Action: Send start notification with duration and optional details
- conditions:
- condition: template
value_template: >
{{ not automation_state.get(state_notification_about_start_sent, false)
and is_running }}
sequence:
- variables:
startup_info_sensors: !input startup_info_sensors
startup_info_texts: !input startup_info_texts
# Build notification message with optional sensor details
message: >
{% set ns = namespace(text = '🧺 ' ~ device_name ~ ': старт. Длительность: `' ~ remaining ~ '`.') %}
{% for i in range(startup_info_sensors | count) %}
{% set ns.text = ns.text ~ ' ' ~ startup_info_texts[i] ~ ': [' ~ states(startup_info_sensors[i]) ~ '].' %}
{% endfor %}
{{ ns.text }}
# Send start notification
- service: notify.send_message
target:
entity_id: !input notify_target
data:
message: "{{ message }}"
# Mark start notification as sent
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >
{% set new_automation_state = (automation_state | combine({ state_notification_about_start_sent: true })) %}
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
# -----------------------------------------------------------------------
# CASE 2: Cycle Completed
# -----------------------------------------------------------------------
# Triggered when: Device stops running after cycle was started
# Action: Send completion notification and reset all state flags
- conditions:
- condition: template
value_template: >
{{ not is_running
and automation_state.get(state_notification_about_start_sent, false)
and (states(run_state_sensor) in [run_state_completion_id, 'unknown', '-'] or states(remaining_time_sensor) in ['unknown', '-']) }}
sequence:
# Send completion notification
- service: notify.send_message
target:
entity_id: !input notify_target
data:
message: >
🟢 {{ device_name }}: завершено. Не забудьте достать вещи!
# Reset all notification flags for next cycle
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >
{% set new_automation_state = (automation_state | combine({
state_notification_about_remaining_time_sent: false,
state_notification_about_start_sent: false,
state_notification_about_preparation_sent: false
})) %}
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
# -----------------------------------------------------------------------
# CASE 3: Preparation Mode Started
# -----------------------------------------------------------------------
# Triggered when: Device enters preparation state (e.g., dryer heating)
# Action: Send preparation notification
- conditions:
- condition: template
value_template: >
{{ preparation_state_id != ''
and states(run_state_sensor) == preparation_state_id
and not automation_state.get(state_notification_about_preparation_sent, false) }}
sequence:
# Mark preparation notification as sent (before sending to prevent duplicates)
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >
{% set new_automation_state = (automation_state | combine({ state_notification_about_preparation_sent: true })) %}
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
# Send preparation notification
- service: notify.send_message
target:
entity_id: !input notify_target
data:
message: >
⚙️ {{ device_name }}: подготовка активирована!
# -----------------------------------------------------------------------
# CASE 4: Error Occurred
# -----------------------------------------------------------------------
# Triggered when: Error message sensor changes to non-empty value
# Action: Send error notification with details
- conditions:
- condition: template
value_template: >
{{ trigger.entity_id == error_message_sensor
and states(error_message_sensor) not in ['', 'unknown', 'unavailable', 'none', '-']
and states(error_message_sensor) | length > 0 }}
sequence:
- variables:
error: "{{ states(error_message_sensor) }}"
# Send error notification
- service: notify.send_message
target:
entity_id: !input notify_target
data:
message: >
⚠️ {{ device_name }}: ошибка. Детали: {{ error }}. Для более подробной информации обратитесь к приложению LG ThinQ.
# -----------------------------------------------------------------------
# CASE 5: Almost Done (Time-to-End Notification)
# -----------------------------------------------------------------------
# Triggered when: Remaining time drops below threshold
# Action: Send "almost done" notification
- conditions:
- condition: template
value_template: >
{{ not automation_state.get(state_notification_about_remaining_time_sent, false)
and is_running
and remaining_time_in_minutes <= notify_time_to_end
and remaining_time_in_minutes > 0 }}
sequence:
# Mark time-to-end notification as sent
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >
{% set new_automation_state = (automation_state | combine({ state_notification_about_remaining_time_sent: true })) %}
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
# Send "almost done" notification
- service: notify.send_message
target:
entity_id: !input notify_target
data:
message: >
🟢 {{ device_name }}: завершение через {{ remaining.split(':')[1] | int }} минут.
# -----------------------------------------------------------------------
# CASE 6: Tub Cleaning Reminder
# -----------------------------------------------------------------------
# Triggered when: Tub clean counter exceeds threshold
# Action: Send tub cleaning reminder
- conditions:
- condition: template
value_template: >
{{ trigger.entity_id == tub_clean_counter_sensor
and tub_clean_counter_threshold != 0
and (states(tub_clean_counter_sensor) | int(0)) > tub_clean_counter_threshold }}
sequence:
# Send tub cleaning reminder
- service: notify.send_message
target:
entity_id: !input notify_target
data:
message: >
⚠️ {{ device_name }}: внимание. Необходима чистка барабана.
Число стирок: {{ states(tub_clean_counter_sensor) | int }}.
Допустимый предел: {{ tub_clean_counter_threshold }}.