diff --git a/appliances.md b/appliances.md index 3593e7d..ee550a0 100644 --- a/appliances.md +++ b/appliances.md @@ -500,10 +500,513 @@ card_mod: ``` +
+3 - Smart Dryer + +```yaml +type: custom:mushroom-entity-card +entity: sensor.dryer_status +name: Smart Dryer +icon: mdi:tumble-dryer +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.dryer_progress' %} + {% set ent_timerem = 'sensor.dryer_time_remaining' %} + {% set max_time = 160 %} + {% set ent_power = 'sensor.smart_plug_power' %} + + /* OPTIONAL Progress Percentage Sensor (if exists) */ + {% set ent_percent = 'sensor.dryer_progress_percentage' %} + + /* 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 max_time */ + {% 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_drying = s_lower in ['drying', 'tumble', 'dry', 'heat', 'drying', 'heating'] %} + {% set is_cooling = s_lower in ['cooling', 'cool down', 'anti-crease', 'air fluff'] %} + {% set is_done = s_lower in ['finished', 'complete', 'end'] %} + + /* --- ASSIGN ANIMATIONS --- */ + {% if is_drying %} + {% set color = '255, 87, 34' %} /* Deep Orange (Heat) */ + {% set anim_type = 'steam-rise 2s ease-in-out infinite' %} + {% set icon_shake = 'shake 0.8s ease-in-out infinite' %} + {% set wave_anim = 'wave 4s linear infinite' %} + {% set overlay_img = 'linear-gradient(0deg, transparent, rgba(255,255,255,0.4), transparent)' %} + {% elif is_cooling %} + {% set color = '33, 150, 243' %} /* Blue (Cooling) */ + {% set anim_type = 'breeze 3s ease-in-out infinite' %} + {% set icon_shake = 'wobble 2s ease-in-out infinite' %} + {% set wave_anim = 'wave 6s linear infinite' %} + {% set overlay_img = 'radial-gradient(circle, rgba(255,255,255,0.5) 0%, transparent 70%)' %} + {% 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%)' %} + {% 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 --- */ + --dryer-color: {{ color }}; + --dryer-level: {{ progress }}%; + --dryer-anim-overlay: {{ anim_type }}; + --dryer-anim-shake: {{ icon_shake }}; + --dryer-anim-wave: {{ wave_anim }}; + --dryer-overlay-bg: {{ overlay_img }}; + --dryer-badge-content: "{{ badge_text }}"; + + transition: all 0.5s ease; + overflow: hidden; + } + + /* BADGE */ + ha-card::before { + content: var(--dryer-badge-content); + position: absolute; + top: 10px; right: 10px; + background: rgba(var(--dryer-color), 0.15); + color: rgb(var(--dryer-color)); + border: 1px solid rgba(var(--dryer-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(--dryer-level); + background: rgb(var(--dryer-color)); + box-shadow: 0 0 10px rgb(var(--dryer-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(--dryer-anim-shake) !important; + transform-origin: 50% 60%; + } + + /* Wave Layer */ + .shape::before { + content: ''; + position: absolute; + left: -50%; + width: 200%; height: 200%; + top: calc(100% - var(--dryer-level)); + background: rgba(var(--dryer-color), 0.6); + border-radius: 40%; + animation: var(--dryer-anim-wave); + } + + /* Overlay Layer (Bubbles/Steam) */ + .shape::after { + content: ''; + position: absolute; + inset: 0; + background-image: var(--dryer-overlay-bg); + background-size: 100% 100%; + animation: var(--dryer-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(10deg) translateY(-2px); } + 50% { transform: rotate(0deg); } + 75% { transform: rotate(-10deg) translateY(2px); } + } + @keyframes wobble { + 0%, 100% { transform: rotate(0deg); } + 50% { transform: rotate(5deg); } + } + @keyframes steam-rise { + 0% { opacity: 0; transform: translateY(10px) scale(0.9); } + 50% { opacity: 0.6; } + 100% { opacity: 0; transform: translateY(-20px) scale(1.1); } + } + @keyframes breeze { + 0% { opacity: 0.2; transform: scale(0.95); } + 50% { opacity: 0.5; transform: scale(1.05); } + 100% { opacity: 0.2; transform: scale(0.95); } + } + @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; + } + +``` +
+ +
+4 - Smart Combo Washing machine & Dryer + +```yaml +type: custom:mushroom-entity-card +entity: sensor.combo_machine_status +name: Smart Combo +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 */ + {% set ent_status = 'sensor.combo_machine_progress' %} + {% set ent_timerem = 'sensor.combo_machine_time_remaining' %} + {% set max_time = 180 %} + + {% set ent_power = 'sensor.smart_plug_power' %} + + /* OPTIONAL Progress Percentage Sensor (if exists) */ + {% set ent_percent = 'sensor.combo_machine_progress_percentage' %} + + /* 2. SIZES (Px) */ + --config-icon-size: 65px; + --config-font-primary: 15px; + --config-font-secondary: 12px; + --config-font-badge: 12px; + /* ======================== */ + + /* --- SENSORS & DATA --- */ + {% 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 DISPLAY --- */ + {% 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 %} + {% set progress = raw_percent | int %} + {% else %} + {% 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 LOGIC (The Core Logic) --- */ + {% set s_lower = status | lower %} + + /* 1. Water Phases */ + {% set is_washing = s_lower in ['wash', 'washing', 'rinse', 'rinsing', 'pre-wash', 'soak'] %} + {% set is_spinning = s_lower in ['spin', 'spinning', 'drain'] %} + + /* 2. Heat Phases (Dryer Logic) */ + {% set is_drying = s_lower in ['dry', 'drying', 'tumble'] %} + {% set is_cooling = s_lower in ['cooling', 'cool down', 'anti-crease'] %} + + /* 3. Finished */ + {% set is_done = s_lower in ['finished', 'complete', 'end'] %} + + /* --- ANIMATION & COLOR ASSIGNMENT --- */ + {% if is_drying %} + /* DRYING MODE: Orange + Steam + Shake */ + {% set color = '255, 87, 34' %} + {% set anim_type = 'steam-rise 2s ease-in-out infinite' %} + {% set icon_shake = 'shake 0.8s ease-in-out infinite' %} + {% set wave_anim = 'wave 4s linear infinite' %} + {% set overlay_img = 'linear-gradient(0deg, transparent, rgba(255,255,255,0.4), transparent)' %} + + {% elif is_cooling %} + /* COOLING MODE: Light Blue + Breeze + Wobble */ + {% set color = '33, 150, 243' %} + {% set anim_type = 'breeze 3s ease-in-out infinite' %} + {% set icon_shake = 'wobble 2s ease-in-out infinite' %} + {% set wave_anim = 'wave 6s linear infinite' %} + {% set overlay_img = 'radial-gradient(circle, rgba(255,255,255,0.5) 0%, transparent 70%)' %} + + {% elif is_spinning %} + /* SPINNING MODE: Cyan + Fast Spin + Fast Wave */ + {% set color = '0, 255, 255' %} + {% set anim_type = 'none' %} + {% set icon_shake = 'washer-spin-smooth 0.8s linear infinite' %} + {% set wave_anim = 'wave 2s linear infinite' %} + {% set overlay_img = 'radial-gradient(circle, rgba(255,255,255,0.3) 10%, transparent 60%)' %} + + {% elif is_washing %} + /* WASHING MODE: Blue + Bubbles + Slow Shake */ + {% set color = '33, 150, 243' %} + {% set anim_type = 'bubbles 1s linear infinite' %} + {% set icon_shake = 'shake 1.5s 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)' %} + + {% elif is_done %} + /* DONE: Green + Sparkle */ + {% set color = '76, 175, 80' %} + {% 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%)' %} + + {% else %} + /* IDLE/OFF: Grey */ + {% set color = '158, 158, 158' %} + {% set anim_type = 'none' %} + {% set icon_shake = 'none' %} + {% set wave_anim = 'none' %} + {% set overlay_img = 'none' %} + {% endif %} + + /* --- OUTPUT VARIABLES --- */ + --combo-color: {{ color }}; + --combo-level: {{ progress }}%; + --combo-anim-overlay: {{ anim_type }}; + --combo-anim-shake: {{ icon_shake }}; + --combo-anim-wave: {{ wave_anim }}; + --combo-overlay-bg: {{ overlay_img }}; + --combo-badge-content: "{{ badge_text }}"; + + transition: all 0.5s ease; + overflow: hidden; + } + + /* BADGE */ + ha-card::before { + content: var(--combo-badge-content); + position: absolute; + top: 10px; right: 10px; + background: rgba(var(--combo-color), 0.15); + color: rgb(var(--combo-color)); + border: 1px solid rgba(var(--combo-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); + } + + /* PROGRESS BAR */ + ha-card::after { + content: ''; + position: absolute; + bottom: 0; left: 0; + height: 4px; + width: var(--combo-level); + background: rgb(var(--combo-color)); + box-shadow: 0 0 10px rgb(var(--combo-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 Animation */ + animation: var(--combo-anim-shake) !important; + transform-origin: center center; + } + + /* Wave Layer (Liquid) */ + .shape::before { + content: ''; + position: absolute; + left: -50%; + width: 200%; height: 200%; + top: calc(100% - var(--combo-level)); + background: rgba(var(--combo-color), 0.6); + border-radius: 40%; + animation: var(--combo-anim-wave); + } + + /* Overlay Layer (Bubbles/Steam/Sparkle) */ + .shape::after { + content: ''; + position: absolute; + inset: 0; + background-image: var(--combo-overlay-bg); + background-size: 100% 100%; + animation: var(--combo-anim-overlay); + z-index: 2; + } + + /* Icon */ + 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 wobble { + 0%, 100% { transform: rotate(0deg); } + 50% { transform: rotate(5deg); } + } + @keyframes washer-spin-smooth { + 0% { + transform: rotate(0deg); + box-shadow: inset 0 0 0 2px rgba(var(--combo-color), 0.7); + } + 100% { + transform: rotate(360deg); + box-shadow: inset 0 0 0 2px rgba(var(--combo-color), 0.7); + } + } + @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(10px) scale(0.9); } + 50% { opacity: 0.6; } + 100% { opacity: 0; transform: translateY(-20px) scale(1.1); } + } + @keyframes breeze { + 0% { opacity: 0.2; transform: scale(0.95); } + 50% { opacity: 0.5; transform: scale(1.05); } + 100% { opacity: 0.2; transform: scale(0.95); } + } + @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; + } + +``` +
+ ---
-3 - Dumb Dishwasher (smart plug) +5 - Dumb Dishwasher (smart plug) ```yaml type: custom:mushroom-entity-card @@ -531,7 +1034,7 @@ card_mod: /* Optional Helper (Leave as is if you don't have one) */ /* If in-use, then you can use this entity as the main card entity above */ /* and it will show useful secondary info like */ - /* (Off, Running) instead of repeating wattage */ + /* (Not Running, Running) instead of repeating wattage */ {% set ent_helper = 'binary_sensor.dishwasher_active_delay' %} @@ -769,7 +1272,7 @@ card_mod: ---
-4 - Dumb Washing Machine (smart plug) +6 - Dumb Washing Machine (smart plug) ```yaml type: custom:mushroom-entity-card @@ -797,7 +1300,7 @@ card_mod: /* Optional Helper (Leave as is if you don't have one) */ /* If in-use, then you can use this entity as the main card entity above */ /* and it will show useful secondary info like */ - /* (Off, Running) instead of repeating wattage */ + /* (Not Running, Running) instead of repeating wattage */ {% set ent_helper = 'binary_sensor.washing_machine_active_delay' %} @@ -1048,12 +1551,623 @@ card_mod: ---
-Smart and dumb Dryer cards (coming soon) +7 - Dumb Dryer (smart plug) +```yaml +type: custom:mushroom-entity-card +entity: binary_sensor.dryer_active_delay +name: Dumb Dryer +icon: mdi:tumble-dryer +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 (Leave as is if you don't have one) */ + /* If in-use, then you can use this entity as the main card entity above */ + /* and it will show useful secondary info like */ + /* (Not Running, Running) instead of repeating wattage */ + + {% set ent_helper = 'binary_sensor.dryer_active_delay' %} + + /* Thresholds */ + + {% set thresh_heat = 500 %} + {% 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) %} + + /* Check if helpers exist in HA */ + {% 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 %} + {% 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 %} + {% 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 (HEATING/DRYING) --- */ + {% if power > thresh_heat %} + {% set status_text = 'Drying' %} + {% set color = '255, 87, 34' %} /* Deep Orange */ + {% set anim_type = 'steam-rise 2s ease-in-out infinite' %} + {% set overlay_img = 'linear-gradient(0deg, transparent, rgba(255,255,255,0.4), transparent)' %} + {% set icon_shake = 'shake 0.8s ease-in-out infinite' %} + + /* --- MEDIUM POWER (TUMBLING/COOLING) --- */ + {% else %} + {% set status_text = 'Tumbling' %} + {% set color = '33, 150, 243' %} /* Blue (Air) */ + {% set anim_type = 'breeze 3s ease-in-out infinite' %} + {% set overlay_img = 'radial-gradient(circle, rgba(255,255,255,0.5) 0%, transparent 70%)' %} + {% set icon_shake = 'wobble 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 --- */ + --dryer-color: {{ color }}; + --dryer-level: {{ level }}%; + --dryer-anim-overlay: {{ anim_type }}; + --dryer-anim-shake: {{ icon_shake }}; + --dryer-anim-wave: {{ wave_anim }}; + --dryer-overlay-bg: {{ overlay_img }}; + --dryer-badge-status: "{{ badge_content }}"; + + --dryer-time-text: "{{ time_str }}"; + --dryer-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(--dryer-badge-status); + position: absolute; + top: 8px; right: 10px; + background: rgba(var(--dryer-color), 0.15); + color: rgb(var(--dryer-color)); + border: 1px solid rgba(var(--dryer-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 (Visual Only) */ + ha-card::after { + content: ''; + position: absolute; + bottom: 0; left: 0; + height: 4px; + width: var(--dryer-level); + background: rgb(var(--dryer-color)); + box-shadow: 0 0 10px rgb(var(--dryer-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(--dryer-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(--dryer-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(--dryer-anim-shake) !important; + transform-origin: 50% 50%; + } + + .shape::before { + content: ''; + position: absolute; + left: -50%; + width: 200%; height: 200%; + top: calc(100% - var(--dryer-level)); + background: rgba(var(--dryer-color), 0.6); + border-radius: 40%; + animation: var(--dryer-anim-wave); + } + + .shape::after { + content: ''; + position: absolute; + inset: 0; + background-image: var(--dryer-overlay-bg); + background-size: 100% 100%; + animation: var(--dryer-anim-overlay); + z-index: 2; + } + + ha-icon { + z-index: 3; + mix-blend-mode: overlay; + color: white !important; + } + + /* --- ANIMATIONS --- */ @keyframes wave { from { transform: rotate(0deg); + } to { transform: rotate(360deg); } } + + @keyframes shake { + 0%, 100% { transform: rotate(0deg); } + 25% { transform: rotate(10deg) translateY(-2px); } + 50% { transform: rotate(0deg); } + 75% { transform: rotate(-10deg) translateY(2px); } + } + + @keyframes wobble { + 0%, 100% { transform: rotate(0deg); } + 50% { transform: rotate(5deg); } + } + + @keyframes steam-rise { + 0% { opacity: 0; transform: translateY(10px) scale(0.9); } + 50% { opacity: 0.6; } + 100% { opacity: 0; transform: translateY(-20px) scale(1.1); } + } + + @keyframes breeze { + 0% { opacity: 0.2; transform: scale(0.95); } + 50% { opacity: 0.5; transform: scale(1.05); } + 100% { opacity: 0.2; transform: scale(0.95); } + } + +``` +
+ +
+Dumb Dryer (Helper/Template) + +```yaml + - binary_sensor: + - name: "Dryer Active delay" + unique_id: dryer_active_status + # 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:tumble-dryer +```
--- +
+8 - Combo Washing machine & Dryer (smart plug) + +```yaml +type: custom:mushroom-entity-card +entity: binary_sensor.combo_machine_active_delay +name: Dumb Combo +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 */ + /* ======================== */ + + /* 1. ENTITIES */ + {% set ent_switch = 'switch.smart_plug' %} + {% set ent_power = 'sensor.smart_plug_power' %} + + /* Optional Helpers (Leave as is if you don't have one) */ + /* If in-use, then you can use "ent_run" entity as the main card entity above */ + /* and it will show useful secondary info like */ + /* (Not Running, Running) instead of repeating wattage */ + + {% set ent_run = 'binary_sensor.combo_machine_active_delay' %} + {% set ent_dry_mode = 'binary_sensor.combo_machine_drying_detector' %} + + /* THRESHOLDS */ + {% set thresh_high = 800 %} + {% set thresh_active = 5 %} + + /* 2. 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) %} + + /* Check if helpers exist in HA */ + {% set run_state = states(ent_run) %} + {% set dry_state = states(ent_dry_mode) %} + {% set has_run_helper = run_state not in ['unknown', 'unavailable', 'none'] %} + {% set has_dry_helper = dry_state not in ['unknown', 'unavailable', 'none'] %} + + /* --- SMART LOGIC (With Fallbacks) --- */ + {% if has_run_helper %} + /* Use Helper */ + {% set is_running = run_state == 'on' %} + {% else %} + /* Fallback: Power > 5W */ + {% set is_running = power > thresh_active %} + {% endif %} + + {% if has_dry_helper %} + /* Use Helper */ + {% set is_drying_mode = dry_state == 'on' %} + {% else %} + /* Fallback: We can't safely guess drying mode without a timer/helper */ + /* So we default to False (Washing) to be safe */ + {% set is_drying_mode = false %} + {% endif %} + + /* --- TIMER CALCULATION --- */ + {% if is_running and switch_state == 'on' %} + {% if has_run_helper %} + /* Calculate precise time if helper exists */ + {% set start_time = states[ent_run].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 %} + /* Generic text if no helper */ + {% set time_str = 'Running' %} + {% endif %} + {% else %} + {% set time_str = '' %} + {% endif %} + + /* --- MAIN LOGIC --- */ + + /* PRIORITY 1: CHECK SWITCH STATE */ + {% if switch_state in ['unavailable', 'unknown'] %} + {% set status_text = 'Offline' %} + {% set color = '158, 158, 158' %} /* Dark Grey */ + {% set badge_content = status_text %} + {% set level = 0 %} + + /* PRIORITY 1: CHECK SWITCH STATE */ + {% elif switch_state == 'off' %} + {% set status_text = 'Plug Off' %} + {% set color = '244, 67, 54' %} /* Red */ + {% set badge_content = status_text %} + {% set level = 0 %} + + /* PRIORITY 2: MACHINE IDLE */ + {% elif not is_running %} + {% set status_text = 'Idle' %} + {% set color = '158, 158, 158' %} /* Grey */ + /* ADDED POWER DISPLAY HERE */ + {% set badge_content = status_text ~ " • " ~ power ~ " W" %} + + /* PRIORITY 3: MACHINE RUNNING */ + {% else %} + + {% if is_drying_mode %} + /* === DRYING === */ + {% set status_text = 'Drying' %} + {% set color = '255, 87, 34' %} /* Orange */ + {% set anim_type = 'steam-rise 2s ease-in-out infinite' %} + {% set overlay_img = 'linear-gradient(0deg, transparent, rgba(255,255,255,0.4), transparent)' %} + {% set icon_shake = 'shake 0.8s ease-in-out infinite' if power > 100 else 'wobble 2s ease-in-out infinite' %} + + {% else %} + /* === WASHING === */ + {% set status_text = 'Washing' %} + {% set color = '33, 150, 243' %} /* Blue */ + {% 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)' %} + + {% if power > thresh_high %} + {% set status_text = 'Spinning' %} + {% set color = '0, 255, 255' %} /* Cyan */ + {% set icon_shake = 'washer-spin-smooth 0.8s linear infinite' %} + {% else %} + {% set icon_shake = 'shake 2s ease-in-out infinite' %} + {% endif %} + {% endif %} + + {% set wave_anim = 'wave 4s linear infinite' %} + {% set level = 60 %} + {% set badge_content = status_text ~ " • " ~ power ~ " W" %} + + {% endif %} + + /* --- CSS VARIABLES --- */ + --combo-color: {{ color }}; + --combo-level: {{ level }}%; + --combo-anim-overlay: {{ anim_type }}; + --combo-anim-shake: {{ icon_shake }}; + --combo-anim-wave: {{ wave_anim }}; + --combo-overlay-bg: {{ overlay_img }}; + --combo-badge-status: "{{ badge_content }}"; + + --combo-time-text: "{{ time_str }}"; + --combo-time-display: {{ 'block' if time_str | length > 0 else 'none' }}; + + transition: all 0.5s ease; + overflow: hidden; + } + + /* BADGE */ + ha-card::before { + content: var(--combo-badge-status); + position: absolute; + top: 8px; right: 10px; + background: rgba(var(--combo-color), 0.15); + color: rgb(var(--combo-color)); + border: 1px solid rgba(var(--combo-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; + } + + /* BAR */ + ha-card::after { + content: ''; + position: absolute; + bottom: 0; left: 0; + height: 4px; + width: var(--combo-level); + background: rgb(var(--combo-color)); + box-shadow: 0 0 10px rgb(var(--combo-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(--combo-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(--combo-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(--combo-anim-shake) !important; + transform-origin: 50% 50%; + } + + .shape::before { + content: ''; + position: absolute; + left: -50%; + width: 200%; height: 200%; + top: calc(100% - var(--combo-level)); + background: rgba(var(--combo-color), 0.6); + border-radius: 40%; + animation: var(--combo-anim-wave); + } + + .shape::after { + content: ''; + position: absolute; + inset: 0; + background-image: var(--combo-overlay-bg); + background-size: 100% 100%; + animation: var(--combo-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(10deg) translateY(-2px); } + 50% { transform: rotate(0deg); } + 75% { transform: rotate(-10deg) translateY(2px); } + } + + @keyframes wobble { + 0%, 100% { transform: rotate(0deg); } + 50% { transform: rotate(5deg); } + } + + @keyframes steam-rise { + 0% { opacity: 0; transform: translateY(10px) scale(0.9); } + 50% { opacity: 0.6; } + 100% { opacity: 0; transform: translateY(-20px) scale(1.1); } + } + + @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(--combo-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(--combo-color), 0.7); + } + } + +``` +
+ +
+Dumb Combo Washing machine & Dryer (Helper/Template) + +```yaml + - binary_sensor: + # 1. Main "Combo Machine Running" Detector + - name: "Combo Machine Active Delay" + unique_id: combo_machine_active_delay + device_class: running + icon: mdi:washing-machine + state: > + {{ states('sensor.plug_6_local_power')|float(0) > 5 }} + delay_off: "00:05:00" # Wait 5 min before saying it's off + + # 2. "Drying Mode" Detector + # Logic If we see HIGH power for a sustained period, we assume Drying. + # Note: Washing heaters cycle on/off quickly. Dryers stay on longer. + - name: "Combo Machine Drying Detector" + unique_id: combo_machine_drying_detector + state: > + {{ states('sensor.plug_6_local_power')|float(0) > 800 }} + # MUST be high for 15 mins to count as drying + delay_on: "00:15:00" + # Keeps drying active during cool down + delay_off: "00:05:00" +``` +
+ +--- + + [paypal_me_shield]: https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white [paypal_me]: https://paypal.me/anasboxsupport