Skip to content

Navigation Menu

Sign in
Sign up

[Style] 洗衣机带动画效果 仪表盘卡片 #18

stsy713 started this conversation in Show and tell
Discussion options

[Style] 🔧 海尔洗衣机 仪表盘卡片 / Haier Washer Dashboard Card

一个基于 html-card-pro 打造的 Home Assistant 洗衣机控制卡片。

A Home Assistant washer control card built with html-card-pro.

⚠️ Compatibility / 兼容性说明

本项目开发与测试过程中使用的设备型号为:

  • Haier 洗衣机(通过 cuco 智能插座接入)
  • cuco CN94518475 智能插座

文档中的实体 ID 已经过脱敏处理,仅用于展示命名结构。

理论上只要设备能够通过智能插座、ESPHome、MQTT 或其它方式接入 Home Assistant,并提供对应开关与传感器实体,即可参考本项目进行适配。

The entity IDs used in this document have been anonymized and are only provided as examples.

In theory, any washer integrated into Home Assistant through smart plugs, ESPHome, MQTT or similar methods can be adapted using this project.


📸 Preview / 预览

海尔洗衣机卡片预览


✨ Features / 功能特点

🇨🇳 中文说明

  • 基于 html-card-pro 制作
  • SVG 矢量绘制洗衣机外观
  • 仿真实机面板布局
  • 一张卡片展示全部控制功能
  • 5 个独立操作按钮(电源 / 播报 / 减少 / 重置 / 增加)
  • 电源开关联动动画(门圈光晕 + 滚筒旋转 + 水位上升)
  • 旋钮指示灯闪烁动画
  • 面板高光扫描动画
  • 玻璃门微光闪烁效果
  • 按钮点击缩放反馈(anime.js)
  • 状态文字变化闪烁提示
  • 播报开关状态实时显示
  • 洗涤袋计数器显示与操作
  • 洗衣机状态实时显示(断电 / 运行中等)
  • 8 种洗涤模式标签展示(混合 / 快洗15' / 羊毛 / 内衣 / 棉麻 / 大件 / 婴童 / 除菌)
  • 深色主题适配
  • 移动端布局优化

🇺🇸 English

  • Built with html-card-pro
  • SVG vector washer illustration
  • Realistic control panel layout
  • All controls in a single card
  • 5 independent action buttons (Power / Broadcast / Decrement / Reset / Increment)
  • Power-linked animations (door glow + drum spin + water rise)
  • Dial indicator LED blink
  • Panel highlight sweep animation
  • Glass door shimmer effect
  • Button click scale feedback (anime.js)
  • Status text flash on change
  • Broadcast toggle real-time display
  • Washer bag counter display & controls
  • Washer status real-time display (Off / Running, etc.)
  • 8 wash cycle labels (Mixed / Quick 15' / Wool / Delicates / Cotton / Bulky / Baby Care / Sterilize)
  • Dark theme support
  • Mobile friendly

🔘 Button Functions / 按钮功能

按钮 / Button 图标 功能 / Function 关联实体 / Entity
电源 / Power 切换智能插座开关 switch.cuco_cn_xxxxx_v3_on_p_2_1
播报 / Broadcast 🔊 切换语音播报开关 input_boolean.xi_yi_ji_bo_bao_kai_guan
减少 / Decrement 洗涤袋计数器 -1 counter.xi_yi_dai_ji_ji_shu_qi
重置 / Reset 洗涤袋计数器归零 counter.xi_yi_dai_ji_ji_shu_qi
增加 / Increment + 洗涤袋计数器 +1 counter.xi_yi_dai_ji_ji_shu_qi

🎨 Animation Effects / 动画效果

动画 / Effect 触发条件 / Trigger 说明 / Description
🔄 滚筒旋转 / Drum Spin 电源开启 滚筒以 3s 周期匀速旋转
💧 水位上升 / Water Rise 电源开启 2s 缓出动画,模拟注水
💡 LED 闪烁 / LED Blink 常驻 1.5s 周期呼吸灯效果
✨ 面板扫描 / Panel Sweep 常驻 4s 周期高光从左到右扫过
🔮 玻璃微光 / Glass Shimmer 常驻 3s 周期玻璃反光闪烁
🔵 门圈光晕 / Door Glow 电源开启 0.8s 渐显蓝色光环
📊 状态闪烁 / Status Flash 状态变化 0.4s 闪烁提示数值更新
🖱️ 按钮缩放 / Button Scale 点击 anime.js 0.88→1 缩放回弹
🎬 入场动画 / Entry 卡片加载 800ms 透明度+缩放渐入

📊 Display Panel / 显示面板

区域 / Section 显示内容 / Content 颜色逻辑 / Color Logic
状态 / Status 洗衣机当前状态文字 🟠 断电: #ff8800 / 🟢 开启: #00ff88
播报 / Broadcast 开 / 关 🔵 开: #4fc3f7 / ⚪ 关: rgba(200,200,200,.4)
计数 / Counter 洗涤袋使用计数 🔵 #00ccff

🌀 Wash Cycles / 洗涤模式

旋钮面板展示以下 8 种洗涤模式(纯展示,实际控制通过洗衣机物理旋钮):

模式 / Cycle 中文 English
混合 混合洗 Mixed
快洗15' 快洗15分钟 Quick 15 min
羊毛 羊毛洗 Wool
内衣 内衣洗 Delicates
棉麻 棉麻洗 Cotton
大件 大件洗 Bulky
婴童 婴童洗 Baby Care
除菌 除菌洗 Sterilize

🔧 Entity IDs / 实体 ID(已脱敏 / Anonymized)

实体 / Entity 类型 / Type 用途 / Purpose
switch.cuco_cn_xxxxx_v3_on_p_2_1 switch 智能插座开关(控制洗衣机电源)
input_boolean.xi_yi_ji_bo_bao_kai_guan input_boolean 语音播报开关
counter.xi_yi_dai_ji_ji_shu_qi counter 洗涤袋使用计数器
sensor.hai_er_xi_yi_ji_zhuang_tai sensor 洗衣机状态传感器

实际使用时请替换为自己的 Home Assistant 实体。

Replace these example entities with your own Home Assistant sensors.


📦 Dependencies / 依赖


📄 Full Code / 完整代码

点击展开完整代码 / Click to expand full code
type: custom:html-pro-card
do_not_parse: true
scripts:
 - https://cdn.jsdelivr.net/npm/animejs@3.2.2/lib/anime.min.js
content: |
 <style>
 :host { display: block; width: 100%; }
 .washer-wrap {
 position: relative; width: 100%; aspect-ratio: 3 / 4; max-height: 520px;
 display: flex; align-items: center; justify-content: center; padding: 12px;
 }
 svg.washer-svg { width: 100%; height: 100%; overflow: visible; }
 @keyframes drumSpin { from{transform:rotate(0)}to{transform:rotate(360deg)} }
 .drum-spin-group { transform-origin: 150px 260px; }
 .washer-active .drum-spin-group { animation: drumSpin 3s linear infinite; }
 @keyframes waterRise { 0%{transform:translateY(30px);opacity:0}40%{opacity:.35}100%{transform:translateY(0);opacity:.25} }
 .water-fill { opacity:0; }
 .washer-active .water-fill { animation: waterRise 2s ease-out forwards; }
 @keyframes ledBlink { 0%,100%{opacity:1}50%{opacity:.6} }
 .led-dot { animation: ledBlink 1.5s ease-in-out infinite; }
 @keyframes panelSweep { 0%{opacity:.06;transform:translateX(-100%)}50%{opacity:.14}100%{opacity:.06;transform:translateX(100%)} }
 .panel-highlight { animation: panelSweep 4s ease-in-out infinite; }
 @keyframes shimmer { 0%,100%{opacity:.15}50%{opacity:.35} }
 .glass-shimmer { animation: shimmer 3s ease-in-out infinite; }
 .door-glow { opacity:0; transition: opacity .8s; }
 .washer-active .door-glow { opacity:1; }
 .btn-rect { cursor:pointer; transition: all .12s; }
 .btn-rect:hover rect.btn-bg { fill: rgba(255,255,255,.13); }
 .btn-rect:active rect.btn-bg { fill: rgba(255,255,255,.25); }
 .btn-rect.active-btn rect.btn-bg { fill: rgba(100,180,255,.25); }
 .btn-rect.active-btn text.btn-label { fill: #88ccff; }
 @keyframes statusFlash { 0%{opacity:1}50%{opacity:.3}100%{opacity:1} }
 .status-flash { animation: statusFlash .4s ease; }
 </style>

 <div class="washer-wrap">
 <svg class="washer-svg" viewBox="0 0 300 400" xmlns="http://www.w3.org/2000/svg">
 <defs>
 <pattern id="ribs" width="4" height="4" patternUnits="userSpaceOnUse">
 <rect width="1.2" height="4" fill="rgba(255,255,255,.06)"/><rect x="2" width=".8" height="4" fill="rgba(0,0,0,.12)"/>
 </pattern>
 <pattern id="drumHoles" width="10" height="10" patternUnits="userSpaceOnUse">
 <circle cx="5" cy="5" r="1.2" fill="rgba(0,0,0,.3)"/>
 </pattern>
 <linearGradient id="bodyGrad" x1="0" x2="1">
 <stop offset="0%" stop-color="#1a1a1a"/><stop offset="35%" stop-color="#2a2a2a"/>
 <stop offset="50%" stop-color="#333"/><stop offset="65%" stop-color="#2a2a2a"/>
 <stop offset="100%" stop-color="#1a1a1a"/>
 </linearGradient>
 <radialGradient id="doorFrameGrad" cx="50%" cy="45%" r="50%">
 <stop offset="0%" stop-color="#555"/><stop offset="60%" stop-color="#3a3a3a"/><stop offset="100%" stop-color="#222"/>
 </radialGradient>
 <radialGradient id="glassGrad" cx="40%" cy="35%" r="60%">
 <stop offset="0%" stop-color="#2a3a4a"/><stop offset="40%" stop-color="#1a2a3a"/><stop offset="100%" stop-color="#0a1520"/>
 </radialGradient>
 <radialGradient id="drumGrad" cx="50%" cy="50%" r="50%">
 <stop offset="0%" stop-color="#b0b0b0"/><stop offset="50%" stop-color="#888"/><stop offset="100%" stop-color="#555"/>
 </radialGradient>
 <linearGradient id="topPanelGrad" y1="0" y2="1">
 <stop offset="0%" stop-color="#2e2e2e"/><stop offset="100%" stop-color="#222"/>
 </linearGradient>
 <linearGradient id="waterGrad" y1="0" y2="1">
 <stop offset="0%" stop-color="rgba(100,180,255,.3)"/><stop offset="100%" stop-color="rgba(60,140,220,.15)"/>
 </linearGradient>
 <filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
 <feGaussianBlur stdDeviation="2" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
 </filter>
 <clipPath id="drumClip"><circle cx="150" cy="260" r="76"/></clipPath>
 </defs>

 <!-- BODY -->
 <rect x="15" y="70" width="270" height="310" rx="8" fill="url(#bodyGrad)"/>
 <rect x="15" y="70" width="270" height="310" rx="8" fill="url(#ribs)"/>
 <rect x="15" y="70" width="270" height="310" rx="8" fill="none" stroke="rgba(255,255,255,.08)" stroke-width=".5"/>

 <!-- TOP PANEL -->
 <rect x="15" y="70" width="270" height="72" rx="8" fill="url(#topPanelGrad)"/>
 <line x1="15" y1="142" x2="285" y2="142" stroke="rgba(0,0,0,.4)" stroke-width="1"/>

 <!-- Detergent Drawer -->
 <rect x="20" y="80" width="42" height="52" rx="3" fill="#2a2a2a" stroke="rgba(255,255,255,.06)" stroke-width=".5"/>
 <rect x="27" y="88" width="28" height="4" rx="2" fill="#1a1a1a"/>
 <rect x="27" y="90" width="28" height="1" rx=".5" fill="rgba(255,255,255,.1)"/>
 <text x="41" y="118" text-anchor="middle" fill="rgba(200,200,200,.4)" font-size="5.5">洗涤剂</text>

 <!-- DIAL (cx=100, r=18) -->
 <circle cx="100" cy="108" r="18" fill="#1a1a1a" stroke="#444" stroke-width="1.2"/>
 <circle cx="100" cy="108" r="14" fill="#222" stroke="#333" stroke-width=".5"/>
 <circle cx="100" cy="108" r="16" fill="none" stroke="rgba(255,255,255,.08)" stroke-width=".5" stroke-dasharray="2 2"/>
 <circle class="led-dot" cx="100" cy="92" r="2" fill="#ff6600" filter="url(#glow)"/>
 <circle cx="100" cy="108" r="2.5" fill="#333"/>

 <!-- Cycle labels -->
 <g fill="rgba(200,200,200,.4)" font-size="4.5" text-anchor="middle">
 <text x="81" y="96">混合</text>
 <text x="76" y="105">快洗15'</text>
 <text x="78" y="114">羊毛</text>
 <text x="84" y="122">内衣</text>
 <text x="119" y="96">棉麻</text>
 <text x="124" y="105">大件</text>
 <text x="122" y="114">婴童</text>
 <text x="116" y="122">除菌</text>
 </g>

 <!-- DISPLAY -->
 <rect x="148" y="84" width="119" height="44" rx="4" fill="#080808" stroke="rgba(255,255,255,.08)" stroke-width=".5"/>
 <line x1="212" y1="88" x2="212" y2="124" stroke="rgba(255,255,255,.08)" stroke-width=".5"/>

 <g id="pIco" transform="translate(165,94)" filter="url(#glow)">
 <circle r="4" fill="none" stroke="#ff8800" stroke-width="1.2"/>
 <line y1="-6" y2="-2.5" stroke="#ff8800" stroke-width="1.2" stroke-linecap="round"/>
 </g>
 <text id="sTxt" x="178" y="112" fill="#ff8800" font-size="8" font-weight="bold" text-anchor="middle">断电</text>
 <text x="178" y="120" fill="rgba(200,200,200,.3)" font-size="4" text-anchor="middle">状态</text>

 <g id="bcIco" transform="translate(198,94)">
 <polygon points="-2.5,-2.5 -2.5,2.5 0,2.5 3,5.5 3,-5.5 0,-2.5" fill="#4fc3f7" opacity=".7"/>
 <path d="M5,-2.5 Q7.5,0 5,2.5" fill="none" stroke="#4fc3f7" stroke-width=".7" stroke-linecap="round"/>
 </g>
 <text id="bcTxt" x="208" y="112" fill="#4fc3f7" font-size="8" font-weight="bold" text-anchor="middle">开</text>
 <text x="208" y="120" fill="rgba(200,200,200,.3)" font-size="4" text-anchor="middle">播报</text>

 <text id="ctTxt" x="247" y="104" fill="#00ccff" font-size="15" font-family="'Courier New',monospace" font-weight="bold" text-anchor="middle" filter="url(#glow)">1</text>
 <text x="247" y="114" fill="rgba(200,200,200,.3)" font-size="4" text-anchor="middle">待机</text>
 <text x="247" y="122" fill="rgba(200,200,200,.25)" font-size="3.5" text-anchor="middle">计数</text>

 <rect class="panel-highlight" x="15" y="70" width="270" height="72" rx="8" fill="rgba(255,255,255,.06)"/>

 <!-- 5 BUTTONS -->
 <g class="btn-rect" data-entity="switch.cuco_cn_945184375_v3_on_p_2_1" data-action="toggle" transform="translate(25,146)">
 <rect class="btn-bg" width="46" height="20" rx="4" fill="rgba(255,255,255,.06)" stroke="rgba(255,255,255,.1)" stroke-width=".5"/>
 <circle cx="11" cy="10" r="3" fill="none" stroke="var(--primary-color,#4fc3f7)" stroke-width="1"/>
 <line x1="11" y1="6" x2="11" y2="8" stroke="var(--primary-color,#4fc3f7)" stroke-width="1" stroke-linecap="round"/>
 <text class="btn-label" x="29" y="13" fill="rgba(200,200,200,.7)" font-size="6.5" text-anchor="middle">电源</text>
 </g>
 <g class="btn-rect" data-entity="input_boolean.xi_yi_ji_bo_bao_kai_guan" data-action="toggle" transform="translate(76,146)">
 <rect class="btn-bg" width="46" height="20" rx="4" fill="rgba(255,255,255,.06)" stroke="rgba(255,255,255,.1)" stroke-width=".5"/>
 <polygon points="7,7 7,12 9.5,12 13,15.5 13,4.5 9.5,8" fill="var(--primary-color,#4fc3f7)" opacity=".8"/>
 <path d="M15,7 Q17.5,10 15,13" fill="none" stroke="var(--primary-color,#4fc3f7)" stroke-width=".8" stroke-linecap="round"/>
 <text class="btn-label" x="30" y="13" fill="rgba(200,200,200,.7)" font-size="6.5" text-anchor="middle">播报</text>
 </g>
 <g class="btn-rect" data-counter-action="decrement" transform="translate(127,146)">
 <rect class="btn-bg" width="46" height="20" rx="4" fill="rgba(255,255,255,.06)" stroke="rgba(255,255,255,.1)" stroke-width=".5"/>
 <text x="11" y="14" fill="var(--primary-color,#4fc3f7)" font-size="12" font-weight="bold" text-anchor="middle">−</text>
 <text class="btn-label" x="30" y="13" fill="rgba(200,200,200,.7)" font-size="6.5" text-anchor="middle">减少</text>
 </g>
 <g class="btn-rect" data-counter-action="reset" transform="translate(178,146)">
 <rect class="btn-bg" width="46" height="20" rx="4" fill="rgba(255,255,255,.06)" stroke="rgba(255,255,255,.1)" stroke-width=".5"/>
 <path d="M7,10 A3.5,3.5 0 1,1 10.5,6.5" fill="none" stroke="var(--primary-color,#4fc3f7)" stroke-width="1" stroke-linecap="round"/>
 <polygon points="10.5,4.5 12,6.8 9,6.8" fill="var(--primary-color,#4fc3f7)"/>
 <text class="btn-label" x="30" y="13" fill="rgba(200,200,200,.7)" font-size="6.5" text-anchor="middle">重置</text>
 </g>
 <g class="btn-rect" data-counter-action="increment" transform="translate(229,146)">
 <rect class="btn-bg" width="46" height="20" rx="4" fill="rgba(255,255,255,.06)" stroke="rgba(255,255,255,.1)" stroke-width=".5"/>
 <text x="11" y="14" fill="var(--primary-color,#4fc3f7)" font-size="12" font-weight="bold" text-anchor="middle">+</text>
 <text class="btn-label" x="30" y="13" fill="rgba(200,200,200,.7)" font-size="6.5" text-anchor="middle">增加</text>
 </g>

 <!-- DOOR -->
 <circle cx="150" cy="260" r="95" fill="url(#doorFrameGrad)" stroke="rgba(255,255,255,.1)" stroke-width=".5"/>
 <circle cx="150" cy="260" r="93" fill="none" stroke="rgba(255,255,255,.08)" stroke-width="1"/>
 <circle cx="150" cy="260" r="82" fill="#111" stroke="rgba(0,0,0,.5)" stroke-width="2"/>
 <circle cx="150" cy="260" r="78" fill="url(#glassGrad)"/>
 <g clip-path="url(#drumClip)">
 <circle cx="150" cy="260" r="76" fill="url(#drumGrad)"/>
 <circle cx="150" cy="260" r="76" fill="url(#drumHoles)"/>
 <g stroke="rgba(255,255,255,.04)" stroke-width=".5">
 <line x1="150" y1="184" x2="150" y2="336"/><line x1="74" y1="260" x2="226" y2="260"/>
 <line x1="96" y1="206" x2="204" y2="314"/><line x1="204" y1="206" x2="96" y2="314"/>
 <line x1="150" y1="184" x2="96" y2="314" transform="rotate(15 150 260)"/>
 <line x1="150" y1="184" x2="204" y2="314" transform="rotate(-15 150 260)"/>
 </g>
 <g class="drum-spin-group">
 <path d="M150 200 Q165 230 150 260 Q135 230 150 200" fill="rgba(180,180,180,.6)" stroke="rgba(200,200,200,.3)" stroke-width=".5"/>
 <path d="M201 290 Q180 275 150 260 Q180 290 201 290" fill="rgba(170,170,170,.5)" stroke="rgba(200,200,200,.3)" stroke-width=".5"/>
 <path d="M99 290 Q120 275 150 260 Q120 290 99 290" fill="rgba(175,175,175,.55)" stroke="rgba(200,200,200,.3)" stroke-width=".5"/>
 </g>
 <ellipse class="water-fill" cx="150" cy="290" rx="70" ry="25" fill="url(#waterGrad)"/>
 </g>
 <g opacity=".2">
 <ellipse cx="130" cy="235" rx="40" ry="12" fill="rgba(255,255,255,.3)" transform="rotate(-30 130 235)"/>
 <ellipse cx="165" cy="275" rx="35" ry="8" fill="rgba(255,255,255,.2)" transform="rotate(20 165 275)"/>
 </g>
 <g class="glass-shimmer" opacity=".15">
 <path d="M110 220 Q130 210 150 225" stroke="rgba(255,255,255,.4)" stroke-width="1.5" fill="none" stroke-linecap="round"/>
 <path d="M155 290 Q175 280 190 295" stroke="rgba(255,255,255,.25)" stroke-width="1" fill="none" stroke-linecap="round"/>
 </g>
 <path d="M115 210 A78 78 0 0 1 180 200" stroke="rgba(255,255,255,.12)" stroke-width="2" fill="none"/>
 <circle class="door-glow" cx="150" cy="260" r="84" fill="none" stroke="rgba(100,180,255,.25)" stroke-width="3"/>
 <rect x="230" y="345" width="45" height="25" rx="2" fill="#1e1e1e"/>
 <rect x="230" y="345" width="45" height="25" rx="2" fill="url(#ribs)"/>
 <rect x="245" y="345" width="15" height="3" rx="1" fill="#151515"/>
 <g fill="#888" stroke="#999" stroke-width=".3"><ellipse cx="40" cy="382" rx="8" ry="4"/><ellipse cx="260" cy="382" rx="8" ry="4"/></g>
 <g fill="#777" stroke="#888" stroke-width=".3" opacity=".7"><ellipse cx="55" cy="384" rx="6" ry="3"/><ellipse cx="245" cy="384" rx="6" ry="3"/></g>
 <ellipse cx="150" cy="388" rx="120" ry="8" fill="rgba(0,0,0,.3)"/>
 <line x1="23" y1="378" x2="277" y2="378" stroke="rgba(255,255,255,.05)" stroke-width=".5"/>
 </svg>
 </div>

 <script>
 if (root._washerInitV8) return;
 root._washerInitV8 = true;

 var COUNTER_ENTITY = 'counter.xi_yi_dai_ji_ji_shu_qi';

 root._watchedEntities = [
 'switch.cuco_cn_945184375_v3_on_p_2_1',
 'input_boolean.xi_yi_ji_bo_bao_kai_guan',
 COUNTER_ENTITY,
 'sensor.hai_er_xi_yi_ji_zhuang_tai'
 ];

 var _lastVals = {};

 function _flash(el, val, key) {
 if (!el) return;
 if (_lastVals[key] !== undefined && _lastVals[key] !== val) {
 el.classList.remove('status-flash');
 void el.offsetWidth;
 el.classList.add('status-flash');
 }
 _lastVals[key] = val;
 }

 function _upd() {
 var sw = hass.states['switch.cuco_cn_945184375_v3_on_p_2_1'];
 var bc = hass.states['input_boolean.xi_yi_ji_bo_bao_kai_guan'];
 var ct = hass.states[COUNTER_ENTITY];
 var sr = hass.states['sensor.hai_er_xi_yi_ji_zhuang_tai'];

 var stEl = root.querySelector('#sTxt');
 var stVal = sr ? sr.state : '--';
 if (stEl) {
 _flash(stEl, stVal, 's');
 stEl.textContent = stVal;
 stEl.setAttribute('fill', (sw && sw.state === 'on') ? '#00ff88' : '#ff8800');
 }
 var pIco = root.querySelector('#pIco');
 if (pIco) {
 var isOn = sw && sw.state === 'on';
 pIco.querySelectorAll('circle,line').forEach(function(e) {
 e.setAttribute('stroke', isOn ? '#00ff88' : '#ff8800');
 });
 }

 var bcT = root.querySelector('#bcTxt');
 var bcI = root.querySelector('#bcIco');
 var bcOn = bc && bc.state === 'on';
 if (bcT) {
 _flash(bcT, bcOn ? '1' : '0', 'bc');
 bcT.textContent = bcOn ? '开' : '关';
 bcT.setAttribute('fill', bcOn ? '#4fc3f7' : 'rgba(200,200,200,.4)');
 }
 if (bcI) {
 bcI.querySelectorAll('polygon,path').forEach(function(e) {
 e.setAttribute('fill', bcOn ? '#4fc3f7' : 'rgba(200,200,200,.3)');
 e.setAttribute('stroke', bcOn ? '#4fc3f7' : 'rgba(200,200,200,.3)');
 });
 }

 var ctEl = root.querySelector('#ctTxt');
 var ctVal = ct ? ct.state : '0';
 if (ctEl) {
 _flash(ctEl, ctVal, 'ct');
 ctEl.textContent = ctVal;
 }

 var wrap = root.querySelector('.washer-wrap');
 if (wrap) wrap.classList.toggle('washer-active', sw && sw.state === 'on');

 root.querySelectorAll('.btn-rect[data-entity]').forEach(function(btn) {
 var eid = btn.getAttribute('data-entity');
 var st = hass.states[eid];
 if (st) btn.classList.toggle('active-btn', st.state === 'on');
 });
 }

 root._onHassUpdate = _upd;

 root.querySelectorAll('.btn-rect').forEach(function(btn) {
 btn.addEventListener('click', function(e) {
 e.stopPropagation();
 var ca = btn.getAttribute('data-counter-action');
 if (ca) {
 hass.callService('counter', ca, { entity_id: COUNTER_ENTITY });
 _animBtn(btn);
 return;
 }
 var eid = btn.getAttribute('data-entity');
 if (eid) {
 hass.callService('homeassistant', 'toggle', { entity_id: eid });
 _animBtn(btn);
 }
 });
 });

 function _animBtn(btn) {
 if (window.anime) {
 var bg = btn.querySelector('.btn-bg');
 if (bg) anime({ targets: bg, scale: [0.88, 1], duration: 200, easing: 'easeOutCubic' });
 }
 }

 if (window.anime) {
 anime({ targets: root.querySelector('.washer-svg'), opacity: [0,1], scale: [0.95,1], duration: 800, easing: 'easeOutCubic' });
 }

 _upd();
 </script>
You must be logged in to vote

Replies: 0 comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
1 participant

AltStyle によって変換されたページ (->オリジナル) /