mirror of
https://github.com/Anashost/HA-Animated-cards.git
synced 2026-01-11 16:20:05 +00:00
35 KiB
35 KiB
Home Assistant Animated Dishwasher / Washing Machine Cards
This YouTube Video explains how to do it.
Cards:
1 - Smart Dishwasher
type: custom:mushroom-entity-card
entity: sensor.dishwasher_status
name: Smart Dishwasher
icon: mdi:dishwasher
primary_info: name
secondary_info: state
tap_action:
action: more-info
icon_color: light-grey
fill_container: false
card_mod:
style:
.: |
ha-card {
/* ======================== */
/* USER CONFIGURATION */
/* ======================== */
/* 1. ENTITIES */
{% set ent_progress = 'sensor.dishwasher_progress' %}
{% set ent_timerem = 'sensor.dishwasher_time_remaining' %}
{% set max_time = 120 %}
{% set ent_power = 'sensor.smart_plug' %}
/* OPTION: Pregress Percentage Sensor (if exists)
{% set ent_percent = 'sensor.dishwasher_progress_percantage' %}
/* 2. SIZES (Px) */
--config-icon-size: 65px;
--config-font-primary: 15px;
--config-font-secondary: 12px;
--config-font-badge: 12px;
/* ======================== */
/* --- SENSORS & TIME --- */
{% set status = states(ent_progress) %}
{% set status_clean = status | replace('-', ' ') | title %}
{% set time_rem = states(ent_timerem) | int(0) %}
{% set raw_percent = states(ent_percent) | float(-1) %}
/* --- POWER CALCULATION --- */
{% set power_w = states(ent_power) | float(0) | round %}
{% set power_text = ' • ' ~ power_w ~ 'W' if power_w > 0 else '' %}
/* Format Time (min -> Xh Ym) */
{% set hours = (time_rem / 60) | int %}
{% set mins = time_rem % 60 %}
{% set time_formatted = '%dh %02dm' | format(hours, mins) %}
/* --- PROGRESS CALCULATION --- */
{% if status | lower in ['idle', 'off', 'standby', 'unknown', 'unavailable'] %}
{% set progress = 0 %}
{% set badge_text = status_clean ~ power_text %}
{% else %}
/* LOGIC: Use Sensor if available, otherwise Calculate */
{% if raw_percent >= 0 %}
/* Use the sensor provided percentage */
{% set progress = raw_percent | int %}
{% else %}
/* Fallback: Calculate based on fixed 180min (Adjust as needed) */
{% set max_cycle_time = max_time %}
{% set calc_prog = ((max_cycle_time - time_rem) / max_cycle_time * 100) | int %}
{% set progress = [5, [calc_prog, 100] | min] | max %}
{% endif %}
/* Show Status + Time + Power */
{% set badge_text = status_clean ~ ' • ' ~ time_formatted ~ power_text %}
{% endif %}
/* --- STATE DEFINITIONS --- */
{% set s_lower = status | lower %}
{% set is_running = s_lower in ['wash', 'washing', 'rinse', 'rinsing', 'pre-wash'] %}
{% set is_drying = s_lower in ['dry', 'drying'] %}
{% set is_done = s_lower in ['finished', 'complete', 'end'] %}
/* --- ASSIGN ANIMATIONS --- */
{% if is_drying %}
{% set color = '255, 152, 0' %} /* Orange */
{% set anim_type = 'steam-rise 2s ease-in-out infinite' %}
{% set icon_shake = 'none' %}
{% set wave_anim = 'wave 4s linear infinite' %}
{% set overlay_img = 'linear-gradient(0deg, transparent, rgba(255,255,255,0.5), transparent)' %}
{% elif is_done %}
{% set color = '76, 175, 80' %} /* Green */
{% set anim_type = 'sparkle 2s infinite' %}
{% set icon_shake = 'none' %}
{% set wave_anim = 'wave 4s linear infinite' %}
{% set overlay_img = 'radial-gradient(circle, rgba(255,255,255,0.8) 10%, transparent 60%)' %}
{% elif is_running %}
{% set color = '33, 150, 243' %} /* Blue */
{% set anim_type = 'bubbles 1s linear infinite' %}
{% set icon_shake = 'shake 0.8s ease-in-out infinite' %}
{% set wave_anim = 'wave 4s linear infinite' %}
{% set overlay_img = 'radial-gradient(2px 2px at 20% 80%, white, transparent), radial-gradient(2px 2px at 50% 70%, white, transparent)' %}
{% else %}
{% set color = '158, 158, 158' %} /* Grey */
{% set anim_type = 'none' %}
{% set icon_shake = 'none' %}
{% set wave_anim = 'none' %}
{% set overlay_img = 'none' %}
{% endif %}
/* --- OUTPUT VARIABLES --- */
--dw-color: {{ color }};
--dw-level: {{ progress }}%;
--dw-anim-overlay: {{ anim_type }};
--dw-anim-shake: {{ icon_shake }};
--dw-anim-wave: {{ wave_anim }};
--dw-overlay-bg: {{ overlay_img }};
--dw-badge-content: "{{ badge_text }}";
transition: all 0.5s ease;
overflow: hidden;
}
/* BADGE */
ha-card::before {
content: var(--dw-badge-content);
position: absolute;
top: 10px; right: 10px;
background: rgba(var(--dw-color), 0.15);
color: rgb(var(--dw-color));
border: 1px solid rgba(var(--dw-color), 0.3);
padding: 2px 10px;
border-radius: 12px;
text-transform: uppercase;
font-weight: 600;
letter-spacing: 0.5px;
font-size: var(--config-font-badge);
}
/* BAR */
ha-card::after {
content: '';
position: absolute;
bottom: 0; left: 0;
height: 4px;
width: var(--dw-level);
background: rgb(var(--dw-color));
box-shadow: 0 0 10px rgb(var(--dw-color));
transition: width 0.5s ease;
}
mushroom-shape-icon$: |
.shape {
--icon-size: var(--config-icon-size) !important;
background: rgba(255, 255, 255, 0.05) !important;
overflow: hidden;
position: relative;
border: 1px solid rgba(255,255,255,0.1);
/* Apply the animation determined by the Logic below */
animation: var(--dw-anim-shake) !important;
transform-origin: 50% 60%;
}
/* Wave Layer */
.shape::before {
content: '';
position: absolute;
left: -50%;
width: 200%; height: 200%;
top: calc(100% - var(--dw-level));
background: rgba(var(--dw-color), 0.6);
border-radius: 40%;
animation: var(--dw-anim-wave);
}
/* Overlay Layer (Bubbles/Steam) */
.shape::after {
content: '';
position: absolute;
inset: 0;
background-image: var(--dw-overlay-bg);
background-size: 100% 100%;
animation: var(--dw-anim-overlay);
z-index: 2;
}
/* The Icon Itself */
ha-icon {
z-index: 3;
mix-blend-mode: overlay;
color: white !important;
}
/* --- KEYFRAMES --- */
@keyframes wave {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes shake {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(5deg) translateY(-1px); }
75% { transform: rotate(-5deg) translateY(1px); }
}
@keyframes bubbles {
0% { transform: translateY(10px); opacity: 0; }
50% { opacity: 1; }
100% { transform: translateY(-20px); opacity: 0; }
}
@keyframes steam-rise {
0% { opacity: 0; transform: translateY(5px); }
50% { opacity: 0.8; }
100% { opacity: 0; transform: translateY(-10px); }
}
@keyframes sparkle {
0%, 100% { opacity: 0.3; transform: scale(0.9); }
50% { opacity: 1; transform: scale(1.1); }
}
mushroom-state-info$: |
.container .primary {
font-size: var(--config-font-primary) !important;
}
.container .secondary {
font-size: var(--config-font-secondary) !important;
}
2 - Smart Washing Machine
type: custom:mushroom-entity-card
entity: sensor.washing_machine_status
name: Smart Washing Machine
icon: mdi:washing-machine
primary_info: name
secondary_info: state
tap_action:
action: more-info
icon_color: light-grey
fill_container: false
card_mod:
style:
.: |
ha-card {
/* ======================== */
/* USER CONFIGURATION */
/* ======================== */
/* 1. ENTITIES (Update these!) */
{% set ent_status = 'sensor.washing_machine_progress' %}
{% set ent_timerem = 'sensor.washing_machine_time_remaining' %}
{% set max_time = 120 %}
{% set ent_power = 'sensor.smart_plug_power' %}
/* OPTION: Pregress Percentage Sensor (if exists)
{% set ent_percent = 'sensor.washing_machine_progress_percantage' %}
/* 2. SIZES (Px) */
--config-icon-size: 65px;
--config-font-primary: 15px;
--config-font-secondary: 12px;
--config-font-badge: 12px;
/* ======================== */
/* --- SENSORS & TIME --- */
{% set status = states(ent_status) %}
{% set status_clean = status | replace('-', ' ') | title %}
{% set time_rem = states(ent_timerem) | int(0) %}
{% set raw_percent = states(ent_percent) | float(-1) %}
/* --- POWER CALCULATION --- */
{% set power_w = states(ent_power) | float(0) | round %}
{% set power_text = ' • ' ~ power_w ~ 'W' if power_w > 0 else '' %}
/* Format Time (min -> Xh Ym) */
{% set hours = (time_rem / 60) | int %}
{% set mins = time_rem % 60 %}
{% set time_formatted = '%dh %02dm' | format(hours, mins) %}
/* --- PROGRESS CALCULATION --- */
{% if status | lower in ['idle', 'off', 'standby', 'unknown', 'unavailable'] %}
{% set progress = 0 %}
{% set badge_text = status_clean ~ power_text %}
{% else %}
/* LOGIC: Use Sensor if available, otherwise Calculate */
{% if raw_percent >= 0 %}
/* Use the sensor provided percentage */
{% set progress = raw_percent | int %}
{% else %}
/* Fallback: Calculate based on fixed avg cycle (90m) */
{% set max_cycle_time = max_time %}
{% set calc_prog = ((max_cycle_time - time_rem) / max_cycle_time * 100) | int %}
{% set progress = [5, [calc_prog, 100] | min] | max %}
{% endif %}
{% set badge_text = status_clean ~ ' • ' ~ time_formatted ~ power_text %}
{% endif %}
/* --- STATE DEFINITIONS --- */
{% set s_lower = status | lower %}
{% set is_running = s_lower in ['wash', 'washing', 'rinse', 'rinsing', 'pre-wash', 'soak'] %}
{% set is_spinning = s_lower in ['spin', 'spinning'] %}
{% set is_drying = s_lower in ['dry', 'drying'] %}
{% set is_done = s_lower in ['finished', 'complete', 'end'] %}
/* --- ASSIGN ANIMATIONS --- */
{% if is_drying %}
{% set color = '255, 152, 0' %} /* Orange */
{% set anim_type = 'steam-rise 2s ease-in-out infinite' %}
{% set icon_shake = 'none' %}
{% set wave_anim = 'wave 4s linear infinite' %}
{% set overlay_img = 'linear-gradient(0deg, transparent, rgba(255,255,255,0.5), transparent)' %}
{% elif is_spinning %}
{% set color = '0, 255, 255' %} /* cyan */
{% set anim_type = 'none' %}
{% set icon_shake = 'washer-spin-smooth 0.8s linear infinite' %}
{% set wave_anim = 'wave 2s linear infinite' %} /* Fast Wave */
{% set overlay_img = 'radial-gradient(circle, rgba(255,255,255,0.3) 10%, transparent 60%)' %}
{% elif is_done %}
{% set color = '76, 175, 80' %} /* Green */
{% set anim_type = 'sparkle 2s infinite' %}
{% set icon_shake = 'none' %}
{% set wave_anim = 'wave 4s linear infinite' %}
{% set overlay_img = 'radial-gradient(circle, rgba(255,255,255,0.8) 10%, transparent 60%)' %}
{% elif is_running %}
{% set color = '33, 150, 243' %} /* Blue */
{% set anim_type = 'bubbles 1s linear infinite' %}
{% set icon_shake = 'shake 1.5s ease-in-out infinite' %} /* Slow Shake */
{% set wave_anim = 'wave 4s linear infinite' %}
{% set overlay_img = 'radial-gradient(2px 2px at 20% 80%, white, transparent), radial-gradient(2px 2px at 50% 70%, white, transparent)' %}
{% else %}
{% set color = '158, 158, 158' %} /* Grey */
{% set anim_type = 'none' %}
{% set icon_shake = 'none' %}
{% set wave_anim = 'none' %}
{% set overlay_img = 'none' %}
{% endif %}
/* --- OUTPUT VARIABLES --- */
--wm-color: {{ color }};
--wm-level: {{ progress }}%;
--wm-anim-overlay: {{ anim_type }};
--wm-anim-shake: {{ icon_shake }};
--wm-anim-wave: {{ wave_anim }};
--wm-overlay-bg: {{ overlay_img }};
--wm-badge-content: "{{ badge_text }}";
transition: all 0.5s ease;
overflow: hidden;
}
/* BADGE */
ha-card::before {
content: var(--wm-badge-content);
position: absolute;
top: 10px; right: 10px;
background: rgba(var(--wm-color), 0.15);
color: rgb(var(--wm-color));
border: 1px solid rgba(var(--wm-color), 0.3);
padding: 2px 10px;
border-radius: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
font-size: var(--config-font-badge);
}
/* BAR */
ha-card::after {
content: '';
position: absolute;
bottom: 0; left: 0;
height: 4px;
width: var(--wm-level);
background: rgb(var(--wm-color));
box-shadow: 0 0 10px rgb(var(--wm-color));
transition: width 0.5s ease;
}
mushroom-shape-icon$: |
.shape {
--icon-size: var(--config-icon-size) !important;
background: rgba(255, 255, 255, 0.05) !important;
overflow: hidden;
position: relative;
border: 1px solid rgba(255,255,255,0.1);
/* Apply the animation determined by the Logic below */
animation: var(--wm-anim-shake) !important;
transform-origin: center center;
}
/* Wave Layer */
.shape::before {
content: '';
position: absolute;
left: -50%;
width: 200%; height: 200%;
top: calc(100% - var(--wm-level));
background: rgba(var(--wm-color), 0.6);
border-radius: 40%;
animation: var(--wm-anim-wave);
}
/* Overlay Layer (Bubbles/Steam) */
.shape::after {
content: '';
position: absolute;
inset: 0;
background-image: var(--wm-overlay-bg);
background-size: 100% 100%;
animation: var(--wm-anim-overlay);
z-index: 2;
}
/* The Icon Itself */
ha-icon {
z-index: 3;
mix-blend-mode: overlay;
color: white !important;
}
/* --- KEYFRAMES --- */
@keyframes wave {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes shake {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(5deg) translateY(-1px); }
75% { transform: rotate(-5deg) translateY(1px); }
}
@keyframes bubbles {
0% { transform: translateY(10px); opacity: 0; }
50% { opacity: 1; }
100% { transform: translateY(-20px); opacity: 0; }
}
@keyframes steam-rise {
0% { opacity: 0; transform: translateY(5px); }
50% { opacity: 0.8; }
100% { opacity: 0; transform: translateY(-10px); }
}
@keyframes sparkle {
0%, 100% { opacity: 0.3; transform: scale(0.9); }
50% { opacity: 1; transform: scale(1.1); }
}
@keyframes washer-spin-smooth {
0% {
transform: rotate(0deg) translate(0,0);
box-shadow: inset 0 0 0 2px rgba(var(--wm-color), 0.7);
}
25% { transform: rotate(90deg) translate(0.5px, 0.5px); }
50% { transform: rotate(180deg) translate(0,0); }
75% { transform: rotate(270deg) translate(-0.5px, -0.5px); }
100% {
transform: rotate(360deg) translate(0,0);
box-shadow: inset 0 0 0 2px rgba(var(--wm-color), 0.7);
}
}
mushroom-state-info$: |
.container .primary {
font-size: var(--config-font-primary) !important;
}
.container .secondary {
font-size: var(--config-font-secondary) !important;
}
3 - Dumb Dishwasher (smart plug)
type: custom:mushroom-entity-card
entity: sensor.smart_plug_power
name: Dishwasher
icon: mdi:dishwasher
primary_info: name
secondary_info: state
tap_action:
action: more-info
fill_container: false
icon_color: light-grey
card_mod:
style:
.: |
ha-card {
/* ======================== */
/* USER CONFIGURATION */
/* ======================== */
{% set ent_switch = 'switch.smart_plug' %}
{% set ent_power = 'sensor.smart_plug_power' %}
/* Optional Helper (Leave as is if you don't have one) */
{% set ent_helper = 'binary_sensor.dishwasher_active_delay' %}
/* Thresholds */
{% set thresh_heat = 1000 %}
{% set thresh_active = 5 %}
/* Sizes */
--config-icon-size: 65px;
--config-font-primary: 15px;
--config-font-secondary: 12px;
--config-font-badge: 11px;
/* ======================== */
/* --- DEFAULTS --- */
{% set icon_shake = 'none' %}
{% set anim_type = 'none' %}
{% set wave_anim = 'none' %}
{% set overlay_img = 'none' %}
{% set level = 0 %}
{% set badge_content = '' %}
{% set time_str = '' %}
{% set color = '158, 158, 158' %}
/* --- DATA FETCHING --- */
{% set power = states(ent_power) | float(0) | int %}
{% set switch_state = states(ent_switch) %}
{% set has_helper = states(ent_helper) not in ['unknown', 'unavailable', 'none'] %}
/* --- SMART LOGIC --- */
{% if has_helper %}
/* Scenario A: Helper Exists */
{% set status_bin = states(ent_helper) %}
{% else %}
/* Scenario B: Helper Missing (Fallback to Power) */
{% set status_bin = 'on' if power > thresh_active else 'off' %}
{% endif %}
/* --- DURATION TIMER --- */
{% if status_bin == 'on' and switch_state == 'on' %}
{% if has_helper %}
/* Precise Timer via Helper */
{% set start_time = states[ent_helper].last_changed %}
{% set seconds = as_timestamp(now()) - as_timestamp(start_time) %}
{% set hours = (seconds / 3600) | int %}
{% set mins = ((seconds % 3600) / 60) | int %}
{% if seconds > 60 %}
{% set time_str = '%dh %02dm' | format(hours, mins) %}
{% else %}
{% set time_str = 'Started' %}
{% endif %}
{% else %}
/* No Helper - Just say Started */
{% set time_str = 'Started' %}
{% endif %}
{% else %}
{% set time_str = '' %}
{% endif %}
/* --- VISUAL STATE LOGIC --- */
{% if switch_state == 'off' %}
{% set status_text = 'Plug Off' %}
{% set color = '244, 67, 54' %}
{% set badge_content = status_text %}
{% elif switch_state in ['unavailable', 'unknown'] %}
{% set status_text = 'Offline' %}
{% set color = '158, 158, 158' %}
{% set badge_content = status_text %}
{% elif status_bin == 'on' %}
/* Machine is Active */
{% if power > thresh_heat %}
{% set status_text = 'Heating' %}
{% set color = '255, 152, 0' %}
{% set anim_type = 'steam-rise 2s ease-in-out infinite' %}
{% set overlay_img = 'linear-gradient(0deg, transparent, rgba(255,255,255,0.5), transparent)' %}
{% else %}
{% set status_text = 'Washing' %}
{% set color = '33, 150, 243' %}
{% set anim_type = 'bubbles 1s linear infinite' %}
{% set overlay_img = 'radial-gradient(2px 2px at 20% 80%, white, transparent), radial-gradient(2px 2px at 50% 70%, white, transparent)' %}
{% set icon_shake = 'shake 0.8s ease-in-out infinite' %}
{% endif %}
{% set wave_anim = 'wave 4s linear infinite' %}
{% set level = 60 %}
{% set badge_content = status_text ~ " • " ~ power ~ " W" %}
{% else %}
/* --- IDLE STATE --- */
{% set status_text = 'Idle' %}
{% set color = '158, 158, 158' %}
{% set badge_content = status_text ~ " • " ~ power ~ " W" %}
{% endif %}
/* --- APPLY CSS VARIABLES --- */
--dw-color: {{ color }};
--dw-level: {{ level }}%;
--dw-anim-overlay: {{ anim_type }};
--dw-anim-shake: {{ icon_shake }};
--dw-anim-wave: {{ wave_anim }};
--dw-overlay-bg: {{ overlay_img }};
--dw-badge-status: "{{ badge_content }}";
/* Pass text AND display mode logic */
--dw-time-text: "{{ time_str }}";
--dw-time-display: {{ 'block' if time_str | length > 0 else 'none' }};
transition: all 0.5s ease;
overflow: hidden;
}
/* 1. STATUS BADGE (Top Right) */
ha-card::before {
content: var(--dw-badge-status);
position: absolute;
top: 8px; right: 10px;
background: rgba(var(--dw-color), 0.15);
color: rgb(var(--dw-color));
border: 1px solid rgba(var(--dw-color), 0.3);
padding: 2px 8px;
border-radius: 10px;
font-weight: 700;
text-transform: uppercase;
font-size: var(--config-font-badge);
letter-spacing: 0.5px;
z-index: 5;
}
/* 2. PROGRESS BAR */
ha-card::after {
content: '';
position: absolute;
bottom: 0; left: 0;
height: 4px;
width: var(--dw-level);
background: rgb(var(--dw-color));
box-shadow: 0 0 10px rgb(var(--dw-color));
transition: width 0.5s ease;
z-index: 1;
}
mushroom-state-info$: >
.container .primary { font-size: var(--config-font-primary) !important; }
.container .secondary { font-size: var(--config-font-secondary)
!important; }
.container::after {
content: var(--dw-time-text);
position: absolute;
top: 40px;
right: 10px;
color: var(--primary-text-color);
background: rgba(var(--rgb-primary-text-color), 0.05);
border: 1px solid rgba(var(--rgb-primary-text-color), 0.1);
padding: 2px 8px;
border-radius: 10px;
font-weight: 700;
font-size: var(--config-font-badge);
letter-spacing: 0.5px;
display: var(--dw-time-display);
}
mushroom-shape-icon$: >
.shape {
--icon-size: var(--config-icon-size) !important;
background: rgba(255, 255, 255, 0.05) !important;
overflow: hidden;
position: relative;
border: 1px solid rgba(255,255,255,0.1);
animation: var(--dw-anim-shake) !important;
transform-origin: 50% 60%;
}
.shape::before {
content: '';
position: absolute;
left: -50%;
width: 200%; height: 200%;
top: calc(100% - var(--dw-level));
background: rgba(var(--dw-color), 0.6);
border-radius: 40%;
animation: var(--dw-anim-wave);
}
.shape::after {
content: '';
position: absolute;
inset: 0;
background-image: var(--dw-overlay-bg);
background-size: 100% 100%;
animation: var(--dw-anim-overlay);
z-index: 2;
}
ha-icon {
z-index: 3;
mix-blend-mode: overlay;
color: white !important;
}
@keyframes wave { from { transform: rotate(0deg); } to { transform:
rotate(360deg); } } @keyframes shake {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(5deg) translateY(-1px); }
75% { transform: rotate(-5deg) translateY(1px); }
} @keyframes bubbles {
0% { transform: translateY(10px); opacity: 0; }
50% { opacity: 1; }
100% { transform: translateY(-20px); opacity: 0; }
} @keyframes steam-rise {
0% { opacity: 0; transform: translateY(5px); }
50% { opacity: 0.8; }
100% { opacity: 0; transform: translateY(-10px); }
}
Dumb Dishwasher (Helper/Template)
- binary_sensor:
- name: "Dishwasher Active delay"
unique_id: dishwasher_active_delay
# Change the entity_id below to match your actual smart plug
state: >
{{ states('sensor.smart_plug')|float(0) > 5 }}
delay_off: "00:05:00"
device_class: running
icon: mdi:dishwasher
4 - Dumb Washing Machine (smart plug)
type: custom:mushroom-entity-card
entity: sensor.smart_plug_power
name: Washing Machine
icon: mdi:washing-machine
primary_info: name
secondary_info: state
tap_action:
action: more-info
fill_container: false
icon_color: white
card_mod:
style:
.: |
ha-card {
/* ======================== */
/* USER CONFIGURATION */
/* ======================== */
{% set ent_switch = 'switch.smart_plug' %}
{% set ent_power = 'sensor.smart_plug_power' %}
/* Optional Helper */
{% set ent_helper = 'binary_sensor.washing_machine_active_delay' %}
/* Thresholds */
{% set thresh_spin = 300 %}
{% set thresh_active = 5 %}
/* Sizes */
--config-icon-size: 65px;
--config-font-primary: 15px;
--config-font-secondary: 12px;
--config-font-badge: 11px;
/* ======================== */
/* --- DEFAULTS --- */
{% set icon_shake = 'none' %}
{% set anim_type = 'none' %}
{% set wave_anim = 'none' %}
{% set overlay_img = 'none' %}
{% set level = 0 %}
{% set badge_content = '' %}
{% set time_str = '' %}
{% set color = '158, 158, 158' %}
/* --- DATA FETCHING --- */
{% set power = states(ent_power) | float(0) | int %}
{% set switch_state = states(ent_switch) %}
{% set has_helper = states(ent_helper) not in ['unknown', 'unavailable', 'none'] %}
/* --- SMART LOGIC --- */
{% if has_helper %}
{% set status_bin = states(ent_helper) %}
{% else %}
/* Fallback: Active if power > 5W */
{% set status_bin = 'on' if power > thresh_active else 'off' %}
{% endif %}
/* --- DURATION TIMER --- */
{% if status_bin == 'on' and switch_state == 'on' %}
{% if has_helper %}
/* Scenario A: Precise Timer via Helper */
{% set start_time = states[ent_helper].last_changed %}
{% set seconds = as_timestamp(now()) - as_timestamp(start_time) %}
{% set hours = (seconds / 3600) | int %}
{% set mins = ((seconds % 3600) / 60) | int %}
{% if seconds > 60 %}
{% set time_str = '%dh %02dm' | format(hours, mins) %}
{% else %}
{% set time_str = 'Started' %}
{% endif %}
{% else %}
/* Scenario B: No Helper - Just say Started */
{% set time_str = 'Started' %}
{% endif %}
{% else %}
{% set time_str = '' %}
{% endif %}
/* --- STATE LOGIC --- */
{% if switch_state == 'off' %}
{% set status_text = 'Plug Off' %}
{% set color = '244, 67, 54' %}
{% set badge_content = status_text %}
{% elif switch_state in ['unavailable', 'unknown'] %}
{% set status_text = 'Offline' %}
{% set color = '158, 158, 158' %}
{% set badge_content = status_text %}
{% elif status_bin == 'on' %}
/* --- HIGH POWER (SPINNING) --- */
{% if power > thresh_spin %}
{% set status_text = 'Spinning' %}
{% set color = '0, 255, 255' %}
{% set anim_type = 'none' %}
{% set overlay_img = 'none' %}
{% set icon_shake = 'washer-spin-smooth 0.8s linear infinite' %}
/* --- NORMAL POWER (WASHING) --- */
{% else %}
{% set status_text = 'Washing' %}
{% set color = '33, 150, 243' %}
{% set anim_type = 'bubbles 2s linear infinite' %}
{% set overlay_img = 'radial-gradient(2px 2px at 20% 80%, white, transparent), radial-gradient(2px 2px at 50% 70%, white, transparent)' %}
{% set icon_shake = 'shake 2s ease-in-out infinite' %}
{% endif %}
{% set wave_anim = 'wave 4s linear infinite' %}
{% set level = 60 %}
{% set badge_content = status_text ~ " • " ~ power ~ " W" %}
{% else %}
/* --- IDLE STATE --- */
{% set status_text = 'Idle' %}
{% set color = '158, 158, 158' %}
{% set badge_content = status_text ~ " • " ~ power ~ " W" %}
{% endif %}
/* --- OUTPUT VARIABLES --- */
--wm-color: {{ color }};
--wm-level: {{ level }}%;
--wm-anim-overlay: {{ anim_type }};
--wm-anim-shake: {{ icon_shake }};
--wm-anim-wave: {{ wave_anim }};
--wm-overlay-bg: {{ overlay_img }};
--wm-badge-status: "{{ badge_content }}";
--wm-time-text: "{{ time_str }}";
--wm-time-display: {{ 'block' if time_str | length > 0 else 'none' }};
transition: all 0.5s ease;
overflow: hidden;
}
/* 1. STATUS BADGE */
ha-card::before {
content: var(--wm-badge-status);
position: absolute;
top: 8px; right: 10px;
background: rgba(var(--wm-color), 0.15);
color: rgb(var(--wm-color));
border: 1px solid rgba(var(--wm-color), 0.3);
padding: 2px 8px;
border-radius: 10px;
font-weight: 700;
text-transform: uppercase;
font-size: var(--config-font-badge);
letter-spacing: 0.5px;
z-index: 5;
}
/* 2. PROGRESS BAR */
ha-card::after {
content: '';
position: absolute;
bottom: 0; left: 0;
height: 4px;
width: var(--wm-level);
background: rgb(var(--wm-color));
box-shadow: 0 0 10px rgb(var(--wm-color));
transition: width 0.5s ease;
z-index: 1;
}
mushroom-state-info$: >
.container .primary { font-size: var(--config-font-primary) !important; }
.container .secondary { font-size: var(--config-font-secondary)
!important; }
.container::after {
content: var(--wm-time-text);
position: absolute;
top: 40px;
right: 10px;
color: var(--primary-text-color);
background: rgba(var(--rgb-primary-text-color), 0.05);
border: 1px solid rgba(var(--rgb-primary-text-color), 0.1);
padding: 2px 8px;
border-radius: 10px;
font-weight: 700;
font-size: var(--config-font-badge);
letter-spacing: 0.5px;
display: var(--wm-time-display);
}
mushroom-shape-icon$: >
.shape {
--icon-size: var(--config-icon-size) !important;
background: rgba(255, 255, 255, 0.05) !important;
overflow: hidden !important;
position: relative;
border: 1px solid rgba(255,255,255,0.1);
animation: var(--wm-anim-shake) !important;
transform-origin: 50% 50%;
}
.shape::before {
content: '';
position: absolute;
left: -50%;
width: 200%; height: 200%;
top: calc(100% - var(--wm-level));
background: rgba(var(--wm-color), 0.6);
border-radius: 40%;
animation: var(--wm-anim-wave);
}
.shape::after {
content: '';
position: absolute;
inset: 0;
background-image: var(--wm-overlay-bg);
background-size: 100% 100%;
animation: var(--wm-anim-overlay);
z-index: 2;
}
ha-icon {
z-index: 3;
mix-blend-mode: overlay;
color: white !important;
}
@keyframes wave { from { transform: rotate(0deg); } to { transform:
rotate(360deg); } } @keyframes shake {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(5deg) translateY(-1px); }
75% { transform: rotate(-5deg) translateY(1px); }
}
@keyframes bubbles {
0% { transform: translateY(10px); opacity: 0; }
50% { opacity: 1; }
100% { transform: translateY(-20px); opacity: 0; }
}
@keyframes washer-spin-smooth {
0% {
transform: rotate(0deg) translate(0,0);
box-shadow: inset 0 0 0 2px rgba(var(--wm-color), 0.7);
}
25% { transform: rotate(90deg) translate(0.5px, 0.5px); }
50% { transform: rotate(180deg) translate(0,0); }
75% { transform: rotate(270deg) translate(-0.5px, -0.5px); }
100% {
transform: rotate(360deg) translate(0,0);
box-shadow: inset 0 0 0 2px rgba(var(--wm-color), 0.7);
}
}
Dumb Washing Machine (Helper/Template)
- binary_sensor:
- name: "Washing Machine Active delay"
unique_id: washing_machine_active_delay
# Change the entity_id below to match your actual smart plug
state: >
{{ states('sensor.smart_plug')|float(0) > 5 }}
delay_off: "00:05:00"
device_class: running
icon: mdi:washing-machine