feat: add unload reminder to Washing Machine
- Send a reminder N minutes after cycle completion, with optional repeats - Auto-cancel reminders when a new cycle starts or a door/lid sensor opens - Gate the completion timestamp on configured door sensors being closed - Track completion time (cct) and reminder count (urc) in persistent state
This commit is contained in:
@@ -7,6 +7,7 @@ This blueprint monitors washing machine or dryer appliances and sends notificati
|
||||
- Start notification with cycle duration, estimated end time, and mode details
|
||||
- Completion notification (reminder to unload clothes) with energy report
|
||||
- "Almost done" notification (configurable minutes before end)
|
||||
- Unload reminder (configurable delay, optional repeats) — auto-cancelled when a new cycle starts or a configured door/lid sensor opens
|
||||
- Pause/Resume notifications (detect when cycle is paused or resumed)
|
||||
- Error message notifications
|
||||
- Preparation mode notification (e.g., for dryer prep)
|
||||
@@ -39,6 +40,8 @@ The automation tracks the appliance through these states:
|
||||
| `cst` | Cycle Start Time (ISO timestamp) |
|
||||
| `esmp` | Energy Samples accumulator (Wh) |
|
||||
| `lst` | Last Sample Time (ISO timestamp) |
|
||||
| `cct` | Cycle Completion Time (ISO timestamp, drives unload reminder) |
|
||||
| `urc` | Unload Reminder Count (number of reminders already sent) |
|
||||
|
||||
## Message Template Variables
|
||||
|
||||
@@ -49,7 +52,7 @@ All message templates support these placeholder variables (use single braces):
|
||||
| `{appliance_name}` | Device name (e.g., "Washing Machine") |
|
||||
| `{remaining}` | Remaining time as string (e.g., "01:30:00") |
|
||||
| `{estimated_end}` | Estimated completion time (e.g., "14:30") |
|
||||
| `{minutes}` | Remaining minutes as number (e.g., 90) |
|
||||
| `{minutes}` | Remaining minutes (almost-done) or elapsed minutes since completion (unload reminder) |
|
||||
| `{error}` | Error message text (only in error notification) |
|
||||
| `{tub_count}` | Tub clean counter value (only in tub clean notification) |
|
||||
| `{tub_threshold}` | Tub clean threshold (only in tub clean notification) |
|
||||
@@ -64,6 +67,18 @@ All message templates support these placeholder variables (use single braces):
|
||||
- Notification service entity
|
||||
- (Optional) Power sensor for energy tracking
|
||||
|
||||
## Unload Reminder
|
||||
|
||||
If `Unload Reminder Delay` is greater than 0, the automation:
|
||||
|
||||
1. Records the completion timestamp (`cct`) when the cycle finishes — but **only if** every door sensor in `Unload Reminder Door Sensors` is currently closed (or the list is empty).
|
||||
2. Sends a reminder N minutes later (where N = delay), then optionally repeats every `Unload Reminder Repeat Interval` minutes up to `Unload Reminder Repeat Count` total.
|
||||
3. Cancels all pending reminders when:
|
||||
- A new cycle starts (start handler clears `cct` and `urc`), **or**
|
||||
- Any of the configured door / lid sensors transitions to `on` (treated as "user has unloaded").
|
||||
|
||||
The check runs once per minute via a `time_pattern` trigger. The door sensor list is optional — leave it empty to keep reminders purely time-based.
|
||||
|
||||
## Note
|
||||
|
||||
Default messages are in Russian for LG ThinQ integration. Customize messages in the "Messages" section for your language.
|
||||
|
||||
@@ -65,6 +65,67 @@ blueprint:
|
||||
selector:
|
||||
boolean:
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Unload Reminder
|
||||
# -------------------------------------------------------------------------
|
||||
# Optional reminder to unload the appliance after the cycle completes.
|
||||
# Purely time-based: no door/state tracking required.
|
||||
unload_reminder_group:
|
||||
name: "Unload Reminder"
|
||||
collapsed: true
|
||||
input:
|
||||
unload_reminder_delay:
|
||||
name: Unload Reminder Delay (minutes)
|
||||
description: >
|
||||
Send a reminder this many minutes after the cycle completes
|
||||
if the appliance has not been emptied yet.
|
||||
Set to 0 to disable unload reminders entirely.
|
||||
default: 0
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 180
|
||||
unit_of_measurement: minutes
|
||||
mode: slider
|
||||
|
||||
unload_reminder_repeat_count:
|
||||
name: Unload Reminder Repeat Count
|
||||
description: >
|
||||
Total number of unload reminders to send (including the first).
|
||||
Set to 1 for a single reminder.
|
||||
default: 1
|
||||
selector:
|
||||
number:
|
||||
min: 1
|
||||
max: 10
|
||||
mode: slider
|
||||
|
||||
unload_reminder_repeat_interval:
|
||||
name: Unload Reminder Repeat Interval (minutes)
|
||||
description: >
|
||||
Time between repeated unload reminders.
|
||||
Only used when Repeat Count > 1.
|
||||
default: 15
|
||||
selector:
|
||||
number:
|
||||
min: 5
|
||||
max: 120
|
||||
unit_of_measurement: minutes
|
||||
mode: slider
|
||||
|
||||
unload_reminder_door_sensors:
|
||||
name: Unload Reminder Door Sensors (optional)
|
||||
description: >
|
||||
Optional list of door / lid binary sensors. When any of them
|
||||
opens after the cycle completes, pending unload reminders are
|
||||
cancelled (treated as "user has unloaded").
|
||||
Leave empty to keep reminders purely time-based.
|
||||
default: []
|
||||
selector:
|
||||
entity:
|
||||
domain: binary_sensor
|
||||
multiple: true
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Persistent State Configuration
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -143,6 +204,17 @@ blueprint:
|
||||
text:
|
||||
multiline: true
|
||||
|
||||
message_unload_reminder:
|
||||
name: "Unload Reminder Message"
|
||||
description: >
|
||||
Reminder sent after the cycle completes if the appliance has not
|
||||
been unloaded within the configured delay.
|
||||
Variables: `{appliance_name}`, `{minutes}` (elapsed since completion)
|
||||
default: "🧺 {appliance_name}: прошло {minutes} мин. — пора достать вещи!"
|
||||
selector:
|
||||
text:
|
||||
multiline: true
|
||||
|
||||
message_almost_done:
|
||||
name: "Almost Done Message"
|
||||
description: >
|
||||
@@ -389,6 +461,18 @@ trigger:
|
||||
entity_id: !input power_sensor
|
||||
id: "power_update"
|
||||
|
||||
# Periodic tick for time-based checks (e.g., unload reminder)
|
||||
- platform: time_pattern
|
||||
minutes: "/1"
|
||||
id: "reminder_tick"
|
||||
|
||||
# Door / lid opened after completion (cancels unload reminder)
|
||||
# Note: Uses multiple selector, so empty list means trigger is skipped
|
||||
- platform: state
|
||||
entity_id: !input unload_reminder_door_sensors
|
||||
to: 'on'
|
||||
id: "door_opened"
|
||||
|
||||
# =============================================================================
|
||||
# CONDITIONS
|
||||
# =============================================================================
|
||||
@@ -412,6 +496,8 @@ variables:
|
||||
state_cycle_start_time: 'cst' # Cycle start timestamp
|
||||
state_energy_samples: 'esmp' # Energy sample accumulator (Wh)
|
||||
state_last_sample_time: 'lst' # Last power sample timestamp
|
||||
state_cycle_completion_time: 'cct' # Cycle completion timestamp (for unload reminder)
|
||||
state_unload_reminder_count: 'urc' # Number of unload reminders already sent
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Input Variables
|
||||
@@ -431,6 +517,10 @@ variables:
|
||||
show_estimated_end_time: !input show_estimated_end_time
|
||||
power_sensor: !input power_sensor
|
||||
energy_cost_per_kwh: !input energy_cost_per_kwh
|
||||
unload_reminder_delay: !input unload_reminder_delay
|
||||
unload_reminder_repeat_count: !input unload_reminder_repeat_count
|
||||
unload_reminder_repeat_interval: !input unload_reminder_repeat_interval
|
||||
unload_reminder_door_sensors: !input unload_reminder_door_sensors
|
||||
enable_debug_notifications: !input enable_debug_notifications
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -438,6 +528,7 @@ variables:
|
||||
# ---------------------------------------------------------------------------
|
||||
message_start_template: !input message_start
|
||||
message_completed_template: !input message_completed
|
||||
message_unload_reminder_template: !input message_unload_reminder
|
||||
message_almost_done_template: !input message_almost_done
|
||||
message_preparation_template: !input message_preparation
|
||||
message_error_template: !input message_error
|
||||
@@ -619,7 +710,9 @@ action:
|
||||
state_notification_about_start_sent: true,
|
||||
state_cycle_start_time: now().isoformat(),
|
||||
state_energy_samples: 0,
|
||||
state_last_sample_time: now().isoformat()
|
||||
state_last_sample_time: now().isoformat(),
|
||||
state_cycle_completion_time: '',
|
||||
state_unload_reminder_count: 0
|
||||
})) %}
|
||||
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
|
||||
|
||||
@@ -686,13 +779,17 @@ action:
|
||||
entity_id: "{{ automation_state_entity }}"
|
||||
data:
|
||||
value: >
|
||||
{% set any_door_open = (unload_reminder_door_sensors | select('is_state', 'on') | list | length) > 0 %}
|
||||
{% set arm_reminder = (unload_reminder_delay | int(0)) > 0 and not any_door_open %}
|
||||
{% 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,
|
||||
state_notification_about_pause_sent: false,
|
||||
state_was_paused: false,
|
||||
state_energy_samples: 0
|
||||
state_energy_samples: 0,
|
||||
state_cycle_completion_time: (now().isoformat() if arm_reminder else ''),
|
||||
state_unload_reminder_count: 0
|
||||
})) %}
|
||||
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
|
||||
|
||||
@@ -973,3 +1070,100 @@ action:
|
||||
state_last_sample_time: now().isoformat()
|
||||
})) %}
|
||||
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# CASE 10: Unload Reminder
|
||||
# -----------------------------------------------------------------------
|
||||
# Triggered when: Cycle completion timestamp is recorded and the
|
||||
# configured delay (plus any repeat intervals) has elapsed without a
|
||||
# new cycle starting. CASE 1 clears the timestamp on a new cycle.
|
||||
# Action: Remind the user to unload the appliance.
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: >
|
||||
{%- set cct_val = automation_state.get(state_cycle_completion_time, '') -%}
|
||||
{%- set count_val = automation_state.get(state_unload_reminder_count, 0) | int(0) -%}
|
||||
{{ (unload_reminder_delay | int(0)) > 0
|
||||
and cct_val not in ['', 'unknown', 'unavailable', 'none']
|
||||
and count_val < (unload_reminder_repeat_count | int(1))
|
||||
and ((now() - (cct_val | as_datetime)).total_seconds() / 60)
|
||||
>= ((unload_reminder_delay | int(0)) + count_val * (unload_reminder_repeat_interval | int(0))) }}
|
||||
sequence:
|
||||
- variables:
|
||||
cct_val: "{{ automation_state.get(state_cycle_completion_time, '') }}"
|
||||
count_val: "{{ automation_state.get(state_unload_reminder_count, 0) | int(0) }}"
|
||||
new_reminder_count: "{{ (count_val | int(0)) + 1 }}"
|
||||
elapsed_minutes: >
|
||||
{{ ((now() - (cct_val | as_datetime)).total_seconds() / 60) | round(0) | int }}
|
||||
# Render the message template with available variables
|
||||
message: >
|
||||
{% set tpl = message_unload_reminder_template %}
|
||||
{{ tpl | replace('{appliance_name}', appliance_name)
|
||||
| replace('{minutes}', elapsed_minutes | string) }}
|
||||
|
||||
# Send unload reminder notification
|
||||
- service: notify.send_message
|
||||
target:
|
||||
entity_id: !input notify_target
|
||||
data:
|
||||
message: "{{ message }}"
|
||||
|
||||
# Increment reminder count; clear completion time when last reminder is sent
|
||||
- service: input_text.set_value
|
||||
target:
|
||||
entity_id: "{{ automation_state_entity }}"
|
||||
data:
|
||||
value: >
|
||||
{% set new_automation_state = (automation_state | combine({
|
||||
state_unload_reminder_count: new_reminder_count | int(0),
|
||||
state_cycle_completion_time: ('' if (new_reminder_count | int(0)) >= (unload_reminder_repeat_count | int(1)) else cct_val)
|
||||
})) %}
|
||||
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
|
||||
|
||||
# Debug notification for unload reminder
|
||||
- choose:
|
||||
- conditions: "{{ enable_debug_notifications }}"
|
||||
sequence:
|
||||
- service: persistent_notification.create
|
||||
data:
|
||||
title: "{appliance_name} - UNLOAD REMINDER"
|
||||
message: >
|
||||
Action: UNLOAD REMINDER
|
||||
Time: {{ now().strftime('%H:%M:%S') }}
|
||||
Reminder #: {{ new_reminder_count }} of {{ unload_reminder_repeat_count }}
|
||||
Elapsed: {{ elapsed_minutes }} min
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# CASE 11: Door Opened After Completion (cancel unload reminder)
|
||||
# -----------------------------------------------------------------------
|
||||
# Triggered when: A configured door / lid sensor opens while a cycle
|
||||
# completion timestamp is pending. Treated as "user has unloaded".
|
||||
# Action: Clear cct and urc so no further reminders fire.
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: >
|
||||
{{ trigger.id == 'door_opened'
|
||||
and automation_state.get(state_cycle_completion_time, '') not in ['', 'unknown', 'unavailable', 'none'] }}
|
||||
sequence:
|
||||
- service: input_text.set_value
|
||||
target:
|
||||
entity_id: "{{ automation_state_entity }}"
|
||||
data:
|
||||
value: >
|
||||
{% set new_automation_state = (automation_state | combine({
|
||||
state_cycle_completion_time: '',
|
||||
state_unload_reminder_count: 0
|
||||
})) %}
|
||||
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
|
||||
|
||||
# Debug notification for door-cancelled reminder
|
||||
- choose:
|
||||
- conditions: "{{ enable_debug_notifications }}"
|
||||
sequence:
|
||||
- service: persistent_notification.create
|
||||
data:
|
||||
title: "{appliance_name} - UNLOAD DETECTED"
|
||||
message: >
|
||||
Action: UNLOAD REMINDER CANCELLED
|
||||
Time: {{ now().strftime('%H:%M:%S') }}
|
||||
Door: {{ trigger.entity_id }}
|
||||
|
||||
Reference in New Issue
Block a user