From 4fe2cbc0d240851eaff045bff06f130df1295123 Mon Sep 17 00:00:00 2001 From: CokePanManage <389861534@qq.com> Date: Wed, 19 Aug 2026 23:24:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20main.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 初始化HA的仪表盘 --- main.yaml | 6310 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6310 insertions(+) create mode 100644 main.yaml diff --git a/main.yaml b/main.yaml new file mode 100644 index 0000000..6bb1f3e --- /dev/null +++ b/main.yaml @@ -0,0 +1,6310 @@ +views: + - type: sections + max_columns: 4 + title: Main + path: main-home + icon: mdi:home + sections: + - type: grid + cards: + - type: custom:stack-in-card + mode: vertical + keep: + outer_padding: false + margin: true + cards: + - type: custom:mushroom-chips-card + chips: + - type: template + entity: >- + sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1 + icon_color: '{{states(''sensor.livingroom_temp_color_no_rgb'')}}' + icon: mdi:thermometer + use_entity_picture: true + double_tap_action: + action: none + hold_action: + action: none + tap_action: + action: more-info + content: >- + {% if + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1','unavailable') + %} + - + {% elif + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1','unknown') + %} + - + {% else %} + {{states('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1')}}° + {% endif %} + - type: template + entity: >- + sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2 + icon_color: light-blue + icon: mdi:water-percent + name: '' + use_entity_picture: true + hold_action: + action: none + double_tap_action: + action: none + tap_action: + action: more-info + content: >- + {% if + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2','unavailable') + %} + - + {% else %} + {{states('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2')}}% + {% endif %} + - type: template + entity: sensor.xiaomi_cn_2005161482_va3_hcho_density_p_3_11 + icon_color: light-green + icon: mdi:air-filter + name: '' + use_entity_picture: true + hold_action: + action: none + double_tap_action: + action: none + tap_action: + action: more-info + content: >- + {% if + is_state('sensor.xiaomi_cn_2005161482_va3_hcho_density_p_3_11','unavailable') + %} + - + {% else %} + {{states('sensor.xiaomi_cn_2005161482_va3_hcho_density_p_3_11')}}% + {% endif %} + alignment: center + - type: custom:mushroom-chips-card + chips: + - type: template + double_tap_action: + action: none + icon: mdi:lightbulb-group + content: |- + {% if is_state('sensor.lights_of_count','0') %} + + {% else %} + {{states('sensor.lights_of_count')}} + {% endif %} + icon_color: |- + {% set state = states('sensor.lights_of_count')|float %} + {% if state <= 0 %} grey + {% else %} amber + {% endif %} + hold_action: + action: none + tap_action: + action: none + entity: sensor.lights_of_count + - type: template + double_tap_action: + action: none + icon: mdi:air-conditioner + content: |- + {% if is_state('sensor.aircondition_of_count','0') %} + + {% else %} + {{states('sensor.aircondition_of_count')}} + {% endif %} + icon_color: >- + {% set state = + states('sensor.aircondition_of_count')|float %} + + {% if state <= 0 %} grey + + {% else %} amber + + {% endif %} + hold_action: + action: none + tap_action: + action: none + entity: sensor.aircondition_of_count + - type: template + double_tap_action: + action: none + icon: mdi:water + content: |- + {% if is_state('sensor.water_damage_count','0') %} + + {% else %} + {{states('sensor.water_damage_count')}} + {% endif %} + icon_color: >- + {% set state = states('sensor.water_damage_count')|float + %} + + {% if state <= 0 %} grey + + {% else %} blue + + {% endif %} + hold_action: + action: none + tap_action: + action: none + entity: sensor.water_damage_count + alignment: center + - type: custom:swipe-card + mode: vertical + keep: + outer_padding: false + margin: true + box_shadow: false + background: false + cards: + - type: horizontal-stack + cards: + - type: custom:mushroom-entity-card + card_mod: + style: | + ha-card{ + box-shadow: none; + } + entity: input_boolean.on_state + double_tap_action: + action: none + icon: mdi:home + icon_color: green + layout: vertical + hold_action: + action: none + tap_action: + action: navigate + navigation_path: main-home + primary_info: none + secondary_info: name + name: 首页 + - type: custom:mushroom-entity-card + card_mod: + style: | + ha-card{ + box-shadow: none; + } + entity: switch.cuco_cn_2006579195_v3_on_p_2_1 + icon: mdi:server + icon_color: light-blue + layout: vertical + primary_info: none + secondary_info: name + tap_action: + action: navigate + navigation_path: home-server + hold_action: + action: none + double_tap_action: + action: none + name: 机房 + - type: custom:mushroom-entity-card + card_mod: + style: | + ha-card{ + box-shadow: none; + } + entity: light.quan_wu_zhao_ming + icon_color: yellow + secondary_info: name + layout: vertical + name: Lights + double_tap_action: + action: none + hold_action: + action: none + tap_action: + action: more-info + primary_info: none + icon: mdi:lamps + card_mod: + style: | + ha-card { + background-color: rgba(255, 255, 255, 0) !important; + box-shadow: none !important; + padding: 2px 0px 2px 0px; + } + column_span: 4 + - type: grid + cards: + - type: custom:mushroom-title-card + title: Light + - type: custom:button-card + entity: light.quan_wu_zhao_ming + show_icon: true + show_name: true + show_state: true + show_label: true + name: 灯光状态 + icon: mdi:lightbulb-group + state_display: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + const percentage = allLeafLights.length > 0 ? Math.round((onLights.length / allLeafLights.length) * 100) : 0; + + return `${percentage}%`; + ]]] + label: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + + if (onLights.length === 0) { + return '所有灯光已关闭'; + } else { + return `${onLights.length}/${allLeafLights.length} 盏灯亮着`; + } + ]]] + styles: + card: + - height: 80px + - background: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + const percentage = allLeafLights.length > 0 ? (onLights.length / allLeafLights.length) : 0; + + // 胶囊形状背景 + if (percentage === 0) { + // 全关状态 - 深色胶囊 + return 'linear-gradient(145deg, rgba(35,45,65,0.95), rgba(25,35,55,0.9))'; + } else { + // 进度条效果 - 亮色表示开启比例 + const progressPercent = percentage * 100; + return ` + linear-gradient(to right, + rgba(255,210,60,0.8) 0%, + rgba(255,190,40,0.9) ${progressPercent}%, + rgba(50,70,110,0.3) ${progressPercent}%, + rgba(35,55,95,0.4) 100% + ) + `; + } + ]]] + - border-radius: 45px !important + - box-shadow: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + const percentage = allLeafLights.length > 0 ? (onLights.length / allLeafLights.length) : 0; + + if (percentage === 0) { + return 'inset 3px 3px 6px rgba(0,0,0,0.4), inset -3px -3px 6px rgba(80,100,140,0.2), 0 2px 8px rgba(0,0,0,0.3)'; + } else { + return 'inset 2px 2px 4px rgba(0,0,0,0.3), inset -2px -2px 4px rgba(255,255,255,0.1), 0 6px 20px rgba(255,190,40,0.4)'; + } + ]]] + - border: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + + if (onLights.length === 0) { + return '1px solid rgba(70,90,130,0.4)'; + } else { + return '1px solid rgba(255,190,40,0.6)'; + } + ]]] + - padding: 12px 20px + - display: flex + - align-items: center + - position: relative + - overflow: hidden + grid: + - grid-template-areas: '"icon name state" "icon label label"' + - grid-template-columns: min-content 1fr min-content + - grid-template-rows: 1fr 1fr + - gap: 0px 12px + icon: + - width: 50px + - height: 50px + - grid-area: icon + - color: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + + if (onLights.length === 0) { + return 'rgba(140,160,200,0.7)'; + } else { + return 'rgba(255,220,60,1)'; + } + ]]] + - filter: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + + if (onLights.length === 0) { + return 'none'; + } else { + return 'drop-shadow(0 0 12px rgba(255,220,60,0.8))'; + } + ]]] + name: + - font-size: 15px + - font-weight: 700 + - color: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + + if (onLights.length === 0) { + return 'rgba(180,200,240,0.9)'; + } else { + return 'rgba(255,255,255,1)'; + } + ]]] + - grid-area: name + - align-self: end + - justify-self: start + - text-shadow: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + + if (onLights.length === 0) { + return 'none'; + } else { + return '0 1px 3px rgba(0,0,0,0.5)'; + } + ]]] + state: + - font-size: 22px + - font-weight: bold + - color: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + + if (onLights.length === 0) { + return 'rgba(160,180,220,0.9)'; + } else { + return 'rgba(255,240,150,1)'; + } + ]]] + - grid-area: state + - align-self: end + - justify-self: end + - text-shadow: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + + if (onLights.length === 0) { + return 'none'; + } else { + return '0 0 12px rgba(255,220,60,0.7)'; + } + ]]] + label: + - font-size: 13px + - font-weight: 600 + - color: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + + if (onLights.length === 0) { + return 'rgba(160,180,220,0.9)'; + } else { + return 'rgba(255,255,240,1)'; + } + ]]] + - grid-area: label + - align-self: start + - justify-self: start + - margin-top: 2px + - text-shadow: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + + if (onLights.length === 0) { + return 'none'; + } else { + return '0 1px 2px rgba(0,0,0,0.4)'; + } + ]]] + custom_fields: + progress_indicator: | + [[[ + const getAllLightEntities = (entityId) => { + const entityState = states[entityId]; + if (!entityState) return []; + if (entityState.attributes && entityState.attributes.entity_id) { + const childEntities = entityState.attributes.entity_id; + let allLights = []; + childEntities.forEach(childId => { + allLights = allLights.concat(getAllLightEntities(childId)); + }); + return allLights; + } else { + return [entityId]; + } + }; + const allLeafLights = getAllLightEntities('light.quan_wu_zhao_ming'); + const onLights = allLeafLights.filter(eid => states[eid] && states[eid].state === 'on'); + const percentage = allLeafLights.length > 0 ? (onLights.length / allLeafLights.length) : 0; + + if (percentage === 0) { + return '
'; + } else { + const progressWidth = percentage * 100; + return ` + + `; + } + ]]] + styles_css: | + ha-card { + position: relative; + overflow: hidden; + } + .custom-fields { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 0; + } + - type: vertical-stack + cards: + - type: custom:bubble-card + card_type: separator + icon: mdi:ceiling-light-multiple + card_layout: large + rows: '0.8' + - type: custom:mushroom-light-card + entity: light.pmfbj_cn_1187112198_xsx340_s_2 + name: 客厅主灯 + use_light_color: false + icon: mdi:ceiling-light-multiple + icon_color: accent + show_brightness_control: true + show_color_temp_control: true + show_color_control: false + - type: custom:mushroom-light-card + entity: light.shhf_cn_1129134246_sflt11_s_2_light + name: 餐桌 + icon_color: orange + fill_container: true + show_brightness_control: true + use_light_color: false + show_color_temp_control: true + show_color_control: false + icon: mdi:ceiling-fan-light + primary_info: name + grid_options: + columns: 6 + rows: 2 + - type: custom:mushroom-light-card + entity: light.pmfbj_cn_1186735998_xsx340_s_2 + name: 主卧 + use_light_color: true + show_brightness_control: true + show_color_temp_control: true + show_color_control: false + - type: custom:mushroom-light-card + entity: light.pmfbj_cn_1186772227_xsx340_s_2 + name: 次卧灯 + use_light_color: false + show_brightness_control: true + show_color_temp_control: true + show_color_control: false + collapsible_controls: false + - type: custom:mushroom-light-card + entity: light.opple_cn_2005385416_moat_s_2_light + name: 阳台灯 + icon: mdi:light-recessed + icon_color: accent + fill_container: true + show_brightness_control: true + show_color_temp_control: true + - type: custom:mushroom-light-card + entity: light.pmfbj_cn_1186744390_xsx340_s_2 + name: 书房灯 + icon: mdi:ceiling-light + icon_color: light-blue + fill_container: true + show_brightness_control: true + show_color_temp_control: true + - type: custom:mushroom-light-card + entity: light.panpan_cn_876603548_y18_s_3_light + icon: '' + show_brightness_control: false + show_color_temp_control: false + use_light_color: false + show_color_control: false + grid_options: + columns: 12 + rows: 2 + column_span: 1 + - type: grid + cards: + - type: custom:mushroom-title-card + title: 空调 + - type: custom:vertical-stack-in-card + cards: + - type: thermostat + entity: climate.qdhkl_cn_2113954983_b19c + show_current_as_primary: false + features: + - type: climate-hvac-modes + grid_options: + columns: 12 + rows: 6 + theme: Mushroom Shadow + - type: custom:mushroom-chips-card + chips: + - type: template + entity: climate.qdhkl_cn_2113954983_b19c + icon: mdi:power + alignment: end + card_mod: + style: > + ha-card { + /* ================================ */ + /* USER CONFIG HERE */ + /* ================================ */ + + /* 1. ACTIVE STATES */ + {% set active_states = ['on'] %} + + /* 2. POSITIONING */ + {% set pos_y = 'top' %} + {% set pos_y_val = '8px' %} + {% set pos_x = 'right' %} + {% set pos_x_val = '28px' %} + + /* 3. SIZING */ + {% set icon_size = '25px' %} + {% set text_size = '13px' %} + {% set badge_height = '6px' %} + {% set border_radius = '6px' %} + {% set border_thickness = '0.5px' %} + + /* 4. COLORS (HEX, RGB, Color Name) */ + /* --- Active --- */ + {% set active_bg = '#b86836' %} + {% set active_icon = '#d4f228' %} + {% set active_text = 'white' %} + {% set active_border = 'transparent' %} + {% set active_opacity = 0.3 %} + + /* --- Inactive --- */ + {% set inactive_bg = 'grey' %} + {% set inactive_icon = 'grey' %} + {% set inactive_text = 'darkgrey' %} + {% set inactive_border = 'transparent' %} + {% set inactive_opacity = 0.3 %} + + + /* 5. ANIMATIONS */ + /* Options: pulse, shake, wobble */ + /* flash, heartbeat, swing, vibrate */ + + {% set anim_type = 'pulse' %} + + /* true = 24/7, false = only when active */ + {% set always_animate = false %} + + {% set anim_intensity_sec = 0.5 %} /* 0.1 to 1 */ + {% set anim_duration_sec = 1 %} + {% set anim_delay_sec = 1 %} + + /* ================================== */ + /* USER CONFIG END (DONT TOUCH BELOW) */ + /* ================================== */ + + {% set current_state = states(config.chips[0].entity) %} + {% set is_active = current_state != 'off' %} + + {% if is_active %} + {% set current_bg = active_bg %} + {% set current_icon = active_icon %} + {% set current_text = active_text %} + {% set current_border = active_border %} + {% set current_op = active_opacity %} + {% set current_anim = anim_type %} + {% else %} + {% set current_bg = inactive_bg %} + {% set current_icon = inactive_icon %} + {% set current_text = inactive_text %} + {% set current_border = inactive_border %} + {% set current_op = inactive_opacity %} + {% set current_anim = anim_type if always_animate else 'none' %} + {% endif %} + + {% set master_cycle = anim_duration_sec + anim_delay_sec %} + {% set iterations = (anim_duration_sec / anim_intensity_sec) | round(0, 'floor') | int if anim_intensity_sec > 0 else 0 %} + {% set step_percent = (anim_intensity_sec / master_cycle) * 100 if master_cycle > 0 else 0 %} + + --chip-padding: {{ badge_height }}; + --chip-border-radius: {{ border_radius }}; + + --chip-border-width: 0px !important; + --ha-card-border-width: 0px !important; + + --chip-box-shadow: 0 0 0 {{ border_thickness }} {{ current_border }} !important; + + --text-color: {{ current_icon }} !important; + --color: {{ current_icon }} !important; + --chip-background: color-mix(in srgb, {{ current_bg }}, transparent {{ (1 - current_op) * 100 }}%) !important; + --chip-font-size: {{ text_size }} !important; + --chip-icon-size: {{ icon_size }} !important; + --mdc-icon-size: {{ icon_size }} !important; + --chip-height: auto !important; + --primary-text-color: {{ current_text }} !important; + --secondary-text-color: {{ current_text }} !important; + + position: absolute !important; + {{ pos_y }}: {{ pos_y_val }}; + {{ pos_x }}: {{ pos_x_val }}; + z-index: 1; + border: none !important; + border-radius: {{ border_radius }} !important; + box-shadow: 0 4px 6px rgba(0,0,0,0.2); + overflow: visible !important; + will-change: transform; + + {% if current_anim != 'none' and iterations > 0 and master_cycle > 0 %} + animation: anim-{{ config.chips[0].entity | replace('.', '-') }}-{{ anim_type }} {{ master_cycle }}s infinite linear; + {% endif %} + } + + + ha-state-icon, ha-icon { + display: flex; + align-items: center; + justify-content: center; + width: {{ icon_size }} !important; + height: {{ icon_size }} !important; + } + + + {% if config.chips[0].content is not defined or + config.chips[0].content | length == 0 %} + ha-card { padding: 5px !important; border-radius: 50% !important; aspect-ratio: 1/1; justify-content: center; } + .content { display: none; } + {% else %} + ha-card { padding: 0 10px !important; } + .content { padding-left: 5px; font-weight: bold; } + {% endif %} + + + {% if current_anim != 'none' and iterations > 0 and + master_cycle > 0 %} + + @keyframes anim-{{ config.chips[0].entity | replace('.', + '-') }}-{{ anim_type }} { + {% for i in range(iterations) %} + {% set t = i * step_percent %} + + {% if anim_type == 'pulse' %} + {{ t + 0 }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.5) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + step_percent }}% { transform: scale3d(1, 1, 1); } + + {% elif anim_type == 'shake' %} + {{ t + 0 }}% { transform: rotateZ(0deg); } + {{ t + (step_percent * 0.25) }}% { transform: rotateZ(-10deg); } + {{ t + (step_percent * 0.75) }}% { transform: rotateZ(10deg); } + {{ t + step_percent }}% { transform: rotateZ(0deg); } + + {% elif anim_type == 'flash' %} + {{ t + 0 }}% { opacity: 1; } + {{ t + (step_percent * 0.5) }}% { opacity: 0; } + {{ t + step_percent }}% { opacity: 1; } + + {% elif anim_type == 'wobble' %} + {{ t + 0 }}% { transform: translate3d(0%, 0, 0); } + {{ t + (step_percent * 0.3) }}% { transform: translate3d(-15%, 0, 0) rotateZ(-5deg); } + {{ t + (step_percent * 0.6) }}% { transform: translate3d(10%, 0, 0) rotateZ(3deg); } + {{ t + step_percent }}% { transform: translate3d(0%, 0, 0); } + + {% elif anim_type == 'heartbeat' %} + {{ t + 0 }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.25) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + (step_percent * 0.5) }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.75) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + step_percent }}% { transform: scale3d(1, 1, 1); } + + {% elif anim_type == 'swing' %} + {{ t + 0 }}% { transform: rotateZ(0deg); } + {{ t + (step_percent * 0.4) }}% { transform: rotateZ(15deg); } + {{ t + (step_percent * 0.8) }}% { transform: rotateZ(-10deg); } + {{ t + step_percent }}% { transform: rotateZ(0deg); } + + {% elif anim_type == 'vibrate' %} + {{ t + 0 }}% { transform: translate3d(0, 0, 0); } + {{ t + (step_percent * 0.2) }}% { transform: translate3d(-2px, 2px, 0); } + {{ t + (step_percent * 0.4) }}% { transform: translate3d(-2px, -2px, 0); } + {{ t + (step_percent * 0.6) }}% { transform: translate3d(2px, 2px, 0); } + {{ t + (step_percent * 0.8) }}% { transform: translate3d(2px, -2px, 0); } + {{ t + step_percent }}% { transform: translate3d(0, 0, 0); } + + {% endif %} + {% endfor %} + } + + {% endif %} + - type: custom:vertical-stack-in-card + cards: + - type: custom:mushroom-climate-card + entity: climate.qdhkl_cn_2113955096_b19c + show_temperature_control: true + collapsible_controls: true + hvac_modes: + - heat + - cool + - type: custom:mushroom-chips-card + chips: + - type: template + entity: climate.qdhkl_cn_2113955096_b19c + icon: mdi:power + alignment: end + card_mod: + style: > + ha-card { + /* ================================ */ + /* USER CONFIG HERE */ + /* ================================ */ + + /* 1. ACTIVE STATES */ + {% set active_states = ['on'] %} + + /* 2. POSITIONING */ + {% set pos_y = 'top' %} + {% set pos_y_val = '4px' %} + {% set pos_x = 'right' %} + {% set pos_x_val = '4px' %} + + /* 3. SIZING */ + {% set icon_size = '22px' %} + {% set text_size = '13px' %} + {% set badge_height = '2px' %} + {% set border_radius = '5px' %} + {% set border_thickness = '0.5px' %} + + /* 4. COLORS (HEX, RGB, Color Name) */ + /* --- Active --- */ + {% set active_bg = '#b86836' %} + {% set active_icon = '#d4f228' %} + {% set active_text = 'white' %} + {% set active_border = 'transparent' %} + {% set active_opacity = 0.3 %} + + /* --- Inactive --- */ + {% set inactive_bg = 'grey' %} + {% set inactive_icon = 'grey' %} + {% set inactive_text = 'darkgrey' %} + {% set inactive_border = 'transparent' %} + {% set inactive_opacity = 0.3 %} + + + /* 5. ANIMATIONS */ + /* Options: pulse, shake, wobble */ + /* flash, heartbeat, swing, vibrate */ + + {% set anim_type = 'pulse' %} + + /* true = 24/7, false = only when active */ + {% set always_animate = false %} + + {% set anim_intensity_sec = 0.5 %} /* 0.1 to 1 */ + {% set anim_duration_sec = 1 %} + {% set anim_delay_sec = 1 %} + + /* ================================== */ + /* USER CONFIG END (DONT TOUCH BELOW) */ + /* ================================== */ + + {% set current_state = states(config.chips[0].entity) %} + {% set is_active = current_state != 'off' %} + + {% if is_active %} + {% set current_bg = active_bg %} + {% set current_icon = active_icon %} + {% set current_text = active_text %} + {% set current_border = active_border %} + {% set current_op = active_opacity %} + {% set current_anim = anim_type %} + {% else %} + {% set current_bg = inactive_bg %} + {% set current_icon = inactive_icon %} + {% set current_text = inactive_text %} + {% set current_border = inactive_border %} + {% set current_op = inactive_opacity %} + {% set current_anim = anim_type if always_animate else 'none' %} + {% endif %} + + {% set master_cycle = anim_duration_sec + anim_delay_sec %} + {% set iterations = (anim_duration_sec / anim_intensity_sec) | round(0, 'floor') | int if anim_intensity_sec > 0 else 0 %} + {% set step_percent = (anim_intensity_sec / master_cycle) * 100 if master_cycle > 0 else 0 %} + + --chip-padding: {{ badge_height }}; + --chip-border-radius: {{ border_radius }}; + + --chip-border-width: 0px !important; + --ha-card-border-width: 0px !important; + + --chip-box-shadow: 0 0 0 {{ border_thickness }} {{ current_border }} !important; + + --text-color: {{ current_icon }} !important; + --color: {{ current_icon }} !important; + --chip-background: color-mix(in srgb, {{ current_bg }}, transparent {{ (1 - current_op) * 100 }}%) !important; + --chip-font-size: {{ text_size }} !important; + --chip-icon-size: {{ icon_size }} !important; + --mdc-icon-size: {{ icon_size }} !important; + --chip-height: auto !important; + --primary-text-color: {{ current_text }} !important; + --secondary-text-color: {{ current_text }} !important; + + position: absolute !important; + {{ pos_y }}: {{ pos_y_val }}; + {{ pos_x }}: {{ pos_x_val }}; + z-index: 1; + border: none !important; + border-radius: {{ border_radius }} !important; + box-shadow: 0 4px 6px rgba(0,0,0,0.2); + overflow: visible !important; + will-change: transform; + + {% if current_anim != 'none' and iterations > 0 and master_cycle > 0 %} + animation: anim-{{ config.chips[0].entity | replace('.', '-') }}-{{ anim_type }} {{ master_cycle }}s infinite linear; + {% endif %} + } + + + ha-state-icon, ha-icon { + display: flex; + align-items: center; + justify-content: center; + width: {{ icon_size }} !important; + height: {{ icon_size }} !important; + } + + + {% if config.chips[0].content is not defined or + config.chips[0].content | length == 0 %} + ha-card { padding: 5px !important; border-radius: 50% !important; aspect-ratio: 1/1; justify-content: center; } + .content { display: none; } + {% else %} + ha-card { padding: 0 10px !important; } + .content { padding-left: 5px; font-weight: bold; } + {% endif %} + + + {% if current_anim != 'none' and iterations > 0 and + master_cycle > 0 %} + + @keyframes anim-{{ config.chips[0].entity | replace('.', + '-') }}-{{ anim_type }} { + {% for i in range(iterations) %} + {% set t = i * step_percent %} + + {% if anim_type == 'pulse' %} + {{ t + 0 }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.5) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + step_percent }}% { transform: scale3d(1, 1, 1); } + + {% elif anim_type == 'shake' %} + {{ t + 0 }}% { transform: rotateZ(0deg); } + {{ t + (step_percent * 0.25) }}% { transform: rotateZ(-10deg); } + {{ t + (step_percent * 0.75) }}% { transform: rotateZ(10deg); } + {{ t + step_percent }}% { transform: rotateZ(0deg); } + + {% elif anim_type == 'flash' %} + {{ t + 0 }}% { opacity: 1; } + {{ t + (step_percent * 0.5) }}% { opacity: 0; } + {{ t + step_percent }}% { opacity: 1; } + + {% elif anim_type == 'wobble' %} + {{ t + 0 }}% { transform: translate3d(0%, 0, 0); } + {{ t + (step_percent * 0.3) }}% { transform: translate3d(-15%, 0, 0) rotateZ(-5deg); } + {{ t + (step_percent * 0.6) }}% { transform: translate3d(10%, 0, 0) rotateZ(3deg); } + {{ t + step_percent }}% { transform: translate3d(0%, 0, 0); } + + {% elif anim_type == 'heartbeat' %} + {{ t + 0 }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.25) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + (step_percent * 0.5) }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.75) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + step_percent }}% { transform: scale3d(1, 1, 1); } + + {% elif anim_type == 'swing' %} + {{ t + 0 }}% { transform: rotateZ(0deg); } + {{ t + (step_percent * 0.4) }}% { transform: rotateZ(15deg); } + {{ t + (step_percent * 0.8) }}% { transform: rotateZ(-10deg); } + {{ t + step_percent }}% { transform: rotateZ(0deg); } + + {% elif anim_type == 'vibrate' %} + {{ t + 0 }}% { transform: translate3d(0, 0, 0); } + {{ t + (step_percent * 0.2) }}% { transform: translate3d(-2px, 2px, 0); } + {{ t + (step_percent * 0.4) }}% { transform: translate3d(-2px, -2px, 0); } + {{ t + (step_percent * 0.6) }}% { transform: translate3d(2px, 2px, 0); } + {{ t + (step_percent * 0.8) }}% { transform: translate3d(2px, -2px, 0); } + {{ t + step_percent }}% { transform: translate3d(0, 0, 0); } + + {% endif %} + {% endfor %} + } + + {% endif %} + grid_options: + columns: 6 + - type: custom:vertical-stack-in-card + cards: + - type: custom:mushroom-climate-card + entity: climate.qdhkl_cn_2113966364_b19c + show_temperature_control: true + collapsible_controls: true + hvac_modes: + - heat + - cool + - type: custom:mushroom-chips-card + chips: + - type: template + entity: climate.qdhkl_cn_2113966364_b19c + icon: mdi:power + alignment: end + card_mod: + style: > + ha-card { + /* ================================ */ + /* USER CONFIG HERE */ + /* ================================ */ + + /* 1. ACTIVE STATES */ + {% set active_states = ['on'] %} + + /* 2. POSITIONING */ + {% set pos_y = 'top' %} + {% set pos_y_val = '4px' %} + {% set pos_x = 'right' %} + {% set pos_x_val = '4px' %} + + /* 3. SIZING */ + {% set icon_size = '22px' %} + {% set text_size = '13px' %} + {% set badge_height = '2px' %} + {% set border_radius = '5px' %} + {% set border_thickness = '0.5px' %} + + /* 4. COLORS (HEX, RGB, Color Name) */ + /* --- Active --- */ + {% set active_bg = '#b86836' %} + {% set active_icon = '#d4f228' %} + {% set active_text = 'white' %} + {% set active_border = 'transparent' %} + {% set active_opacity = 0.3 %} + + /* --- Inactive --- */ + {% set inactive_bg = 'grey' %} + {% set inactive_icon = 'grey' %} + {% set inactive_text = 'darkgrey' %} + {% set inactive_border = 'transparent' %} + {% set inactive_opacity = 0.3 %} + + + /* 5. ANIMATIONS */ + /* Options: pulse, shake, wobble */ + /* flash, heartbeat, swing, vibrate */ + + {% set anim_type = 'pulse' %} + + /* true = 24/7, false = only when active */ + {% set always_animate = false %} + + {% set anim_intensity_sec = 0.5 %} /* 0.1 to 1 */ + {% set anim_duration_sec = 1 %} + {% set anim_delay_sec = 1 %} + + /* ================================== */ + /* USER CONFIG END (DONT TOUCH BELOW) */ + /* ================================== */ + + {% set current_state = states(config.chips[0].entity) %} + {% set is_active = current_state != 'off' %} + + {% if is_active %} + {% set current_bg = active_bg %} + {% set current_icon = active_icon %} + {% set current_text = active_text %} + {% set current_border = active_border %} + {% set current_op = active_opacity %} + {% set current_anim = anim_type %} + {% else %} + {% set current_bg = inactive_bg %} + {% set current_icon = inactive_icon %} + {% set current_text = inactive_text %} + {% set current_border = inactive_border %} + {% set current_op = inactive_opacity %} + {% set current_anim = anim_type if always_animate else 'none' %} + {% endif %} + + {% set master_cycle = anim_duration_sec + anim_delay_sec %} + {% set iterations = (anim_duration_sec / anim_intensity_sec) | round(0, 'floor') | int if anim_intensity_sec > 0 else 0 %} + {% set step_percent = (anim_intensity_sec / master_cycle) * 100 if master_cycle > 0 else 0 %} + + --chip-padding: {{ badge_height }}; + --chip-border-radius: {{ border_radius }}; + + --chip-border-width: 0px !important; + --ha-card-border-width: 0px !important; + + --chip-box-shadow: 0 0 0 {{ border_thickness }} {{ current_border }} !important; + + --text-color: {{ current_icon }} !important; + --color: {{ current_icon }} !important; + --chip-background: color-mix(in srgb, {{ current_bg }}, transparent {{ (1 - current_op) * 100 }}%) !important; + --chip-font-size: {{ text_size }} !important; + --chip-icon-size: {{ icon_size }} !important; + --mdc-icon-size: {{ icon_size }} !important; + --chip-height: auto !important; + --primary-text-color: {{ current_text }} !important; + --secondary-text-color: {{ current_text }} !important; + + position: absolute !important; + {{ pos_y }}: {{ pos_y_val }}; + {{ pos_x }}: {{ pos_x_val }}; + z-index: 1; + border: none !important; + border-radius: {{ border_radius }} !important; + box-shadow: 0 4px 6px rgba(0,0,0,0.2); + overflow: visible !important; + will-change: transform; + + {% if current_anim != 'none' and iterations > 0 and master_cycle > 0 %} + animation: anim-{{ config.chips[0].entity | replace('.', '-') }}-{{ anim_type }} {{ master_cycle }}s infinite linear; + {% endif %} + } + + + ha-state-icon, ha-icon { + display: flex; + align-items: center; + justify-content: center; + width: {{ icon_size }} !important; + height: {{ icon_size }} !important; + } + + + {% if config.chips[0].content is not defined or + config.chips[0].content | length == 0 %} + ha-card { padding: 5px !important; border-radius: 50% !important; aspect-ratio: 1/1; justify-content: center; } + .content { display: none; } + {% else %} + ha-card { padding: 0 10px !important; } + .content { padding-left: 5px; font-weight: bold; } + {% endif %} + + + {% if current_anim != 'none' and iterations > 0 and + master_cycle > 0 %} + + @keyframes anim-{{ config.chips[0].entity | replace('.', + '-') }}-{{ anim_type }} { + {% for i in range(iterations) %} + {% set t = i * step_percent %} + + {% if anim_type == 'pulse' %} + {{ t + 0 }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.5) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + step_percent }}% { transform: scale3d(1, 1, 1); } + + {% elif anim_type == 'shake' %} + {{ t + 0 }}% { transform: rotateZ(0deg); } + {{ t + (step_percent * 0.25) }}% { transform: rotateZ(-10deg); } + {{ t + (step_percent * 0.75) }}% { transform: rotateZ(10deg); } + {{ t + step_percent }}% { transform: rotateZ(0deg); } + + {% elif anim_type == 'flash' %} + {{ t + 0 }}% { opacity: 1; } + {{ t + (step_percent * 0.5) }}% { opacity: 0; } + {{ t + step_percent }}% { opacity: 1; } + + {% elif anim_type == 'wobble' %} + {{ t + 0 }}% { transform: translate3d(0%, 0, 0); } + {{ t + (step_percent * 0.3) }}% { transform: translate3d(-15%, 0, 0) rotateZ(-5deg); } + {{ t + (step_percent * 0.6) }}% { transform: translate3d(10%, 0, 0) rotateZ(3deg); } + {{ t + step_percent }}% { transform: translate3d(0%, 0, 0); } + + {% elif anim_type == 'heartbeat' %} + {{ t + 0 }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.25) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + (step_percent * 0.5) }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.75) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + step_percent }}% { transform: scale3d(1, 1, 1); } + + {% elif anim_type == 'swing' %} + {{ t + 0 }}% { transform: rotateZ(0deg); } + {{ t + (step_percent * 0.4) }}% { transform: rotateZ(15deg); } + {{ t + (step_percent * 0.8) }}% { transform: rotateZ(-10deg); } + {{ t + step_percent }}% { transform: rotateZ(0deg); } + + {% elif anim_type == 'vibrate' %} + {{ t + 0 }}% { transform: translate3d(0, 0, 0); } + {{ t + (step_percent * 0.2) }}% { transform: translate3d(-2px, 2px, 0); } + {{ t + (step_percent * 0.4) }}% { transform: translate3d(-2px, -2px, 0); } + {{ t + (step_percent * 0.6) }}% { transform: translate3d(2px, 2px, 0); } + {{ t + (step_percent * 0.8) }}% { transform: translate3d(2px, -2px, 0); } + {{ t + step_percent }}% { transform: translate3d(0, 0, 0); } + + {% endif %} + {% endfor %} + } + + {% endif %} + grid_options: + columns: 6 + - type: custom:vertical-stack-in-card + cards: + - type: custom:mushroom-climate-card + entity: climate.qdhkl_cn_2113955084_b19c + show_temperature_control: true + collapsible_controls: true + fill_container: false + grid_options: + columns: 12 + rows: 1 + hvac_modes: + - heat + - cool + - type: custom:mushroom-chips-card + chips: + - type: template + entity: climate.qdhkl_cn_2113955084_b19c + icon: mdi:power + alignment: end + card_mod: + style: > + ha-card { + /* ================================ */ + /* USER CONFIG HERE */ + /* ================================ */ + + /* 1. ACTIVE STATES */ + {% set active_states = ['on'] %} + + /* 2. POSITIONING */ + {% set pos_y = 'top' %} + {% set pos_y_val = '4px' %} + {% set pos_x = 'right' %} + {% set pos_x_val = '4px' %} + + /* 3. SIZING */ + {% set icon_size = '22px' %} + {% set text_size = '13px' %} + {% set badge_height = '2px' %} + {% set border_radius = '5px' %} + {% set border_thickness = '0.5px' %} + + /* 4. COLORS (HEX, RGB, Color Name) */ + /* --- Active --- */ + {% set active_bg = '#b86836' %} + {% set active_icon = '#d4f228' %} + {% set active_text = 'white' %} + {% set active_border = 'transparent' %} + {% set active_opacity = 0.3 %} + + /* --- Inactive --- */ + {% set inactive_bg = 'grey' %} + {% set inactive_icon = 'grey' %} + {% set inactive_text = 'darkgrey' %} + {% set inactive_border = 'transparent' %} + {% set inactive_opacity = 0.3 %} + + + /* 5. ANIMATIONS */ + /* Options: pulse, shake, wobble */ + /* flash, heartbeat, swing, vibrate */ + + {% set anim_type = 'pulse' %} + + /* true = 24/7, false = only when active */ + {% set always_animate = false %} + + {% set anim_intensity_sec = 0.5 %} /* 0.1 to 1 */ + {% set anim_duration_sec = 1 %} + {% set anim_delay_sec = 1 %} + + /* ================================== */ + /* USER CONFIG END (DONT TOUCH BELOW) */ + /* ================================== */ + + {% set current_state = states(config.chips[0].entity) %} + {% set is_active = current_state != 'off' %} + + {% if is_active %} + {% set current_bg = active_bg %} + {% set current_icon = active_icon %} + {% set current_text = active_text %} + {% set current_border = active_border %} + {% set current_op = active_opacity %} + {% set current_anim = anim_type %} + {% else %} + {% set current_bg = inactive_bg %} + {% set current_icon = inactive_icon %} + {% set current_text = inactive_text %} + {% set current_border = inactive_border %} + {% set current_op = inactive_opacity %} + {% set current_anim = anim_type if always_animate else 'none' %} + {% endif %} + + {% set master_cycle = anim_duration_sec + anim_delay_sec %} + {% set iterations = (anim_duration_sec / anim_intensity_sec) | round(0, 'floor') | int if anim_intensity_sec > 0 else 0 %} + {% set step_percent = (anim_intensity_sec / master_cycle) * 100 if master_cycle > 0 else 0 %} + + --chip-padding: {{ badge_height }}; + --chip-border-radius: {{ border_radius }}; + + --chip-border-width: 0px !important; + --ha-card-border-width: 0px !important; + + --chip-box-shadow: 0 0 0 {{ border_thickness }} {{ current_border }} !important; + + --text-color: {{ current_icon }} !important; + --color: {{ current_icon }} !important; + --chip-background: color-mix(in srgb, {{ current_bg }}, transparent {{ (1 - current_op) * 100 }}%) !important; + --chip-font-size: {{ text_size }} !important; + --chip-icon-size: {{ icon_size }} !important; + --mdc-icon-size: {{ icon_size }} !important; + --chip-height: auto !important; + --primary-text-color: {{ current_text }} !important; + --secondary-text-color: {{ current_text }} !important; + + position: absolute !important; + {{ pos_y }}: {{ pos_y_val }}; + {{ pos_x }}: {{ pos_x_val }}; + z-index: 1; + border: none !important; + border-radius: {{ border_radius }} !important; + box-shadow: 0 4px 6px rgba(0,0,0,0.2); + overflow: visible !important; + will-change: transform; + + {% if current_anim != 'none' and iterations > 0 and master_cycle > 0 %} + animation: anim-{{ config.chips[0].entity | replace('.', '-') }}-{{ anim_type }} {{ master_cycle }}s infinite linear; + {% endif %} + } + + + ha-state-icon, ha-icon { + display: flex; + align-items: center; + justify-content: center; + width: {{ icon_size }} !important; + height: {{ icon_size }} !important; + } + + + {% if config.chips[0].content is not defined or + config.chips[0].content | length == 0 %} + ha-card { padding: 5px !important; border-radius: 50% !important; aspect-ratio: 1/1; justify-content: center; } + .content { display: none; } + {% else %} + ha-card { padding: 0 10px !important; } + .content { padding-left: 5px; font-weight: bold; } + {% endif %} + + + {% if current_anim != 'none' and iterations > 0 and + master_cycle > 0 %} + + @keyframes anim-{{ config.chips[0].entity | replace('.', + '-') }}-{{ anim_type }} { + {% for i in range(iterations) %} + {% set t = i * step_percent %} + + {% if anim_type == 'pulse' %} + {{ t + 0 }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.5) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + step_percent }}% { transform: scale3d(1, 1, 1); } + + {% elif anim_type == 'shake' %} + {{ t + 0 }}% { transform: rotateZ(0deg); } + {{ t + (step_percent * 0.25) }}% { transform: rotateZ(-10deg); } + {{ t + (step_percent * 0.75) }}% { transform: rotateZ(10deg); } + {{ t + step_percent }}% { transform: rotateZ(0deg); } + + {% elif anim_type == 'flash' %} + {{ t + 0 }}% { opacity: 1; } + {{ t + (step_percent * 0.5) }}% { opacity: 0; } + {{ t + step_percent }}% { opacity: 1; } + + {% elif anim_type == 'wobble' %} + {{ t + 0 }}% { transform: translate3d(0%, 0, 0); } + {{ t + (step_percent * 0.3) }}% { transform: translate3d(-15%, 0, 0) rotateZ(-5deg); } + {{ t + (step_percent * 0.6) }}% { transform: translate3d(10%, 0, 0) rotateZ(3deg); } + {{ t + step_percent }}% { transform: translate3d(0%, 0, 0); } + + {% elif anim_type == 'heartbeat' %} + {{ t + 0 }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.25) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + (step_percent * 0.5) }}% { transform: scale3d(1, 1, 1); } + {{ t + (step_percent * 0.75) }}% { transform: scale3d(1.15, 1.15, 1); } + {{ t + step_percent }}% { transform: scale3d(1, 1, 1); } + + {% elif anim_type == 'swing' %} + {{ t + 0 }}% { transform: rotateZ(0deg); } + {{ t + (step_percent * 0.4) }}% { transform: rotateZ(15deg); } + {{ t + (step_percent * 0.8) }}% { transform: rotateZ(-10deg); } + {{ t + step_percent }}% { transform: rotateZ(0deg); } + + {% elif anim_type == 'vibrate' %} + {{ t + 0 }}% { transform: translate3d(0, 0, 0); } + {{ t + (step_percent * 0.2) }}% { transform: translate3d(-2px, 2px, 0); } + {{ t + (step_percent * 0.4) }}% { transform: translate3d(-2px, -2px, 0); } + {{ t + (step_percent * 0.6) }}% { transform: translate3d(2px, 2px, 0); } + {{ t + (step_percent * 0.8) }}% { transform: translate3d(2px, -2px, 0); } + {{ t + step_percent }}% { transform: translate3d(0, 0, 0); } + + {% endif %} + {% endfor %} + } + + {% endif %} + - type: grid + cards: + - type: custom:mushroom-title-card + title: Devices + column_span: 4 + - type: grid + cards: + - type: custom:mushroom-fan-card + entity: fan.xiaomi_cn_2196552583_p90_s_2_fan + icon_animation: true + fill_container: false + show_percentage_control: true + show_oscillate_control: true + show_direction_control: false + collapsible_controls: false + - type: custom:mushroom-fan-card + entity: fan.shhf_cn_1129134246_sflt11_s_11_fan + name: 风扇 + icon: '' + icon_animation: true + show_percentage_control: true + show_oscillate_control: false + show_direction_control: true + - type: custom:mushroom-fan-card + entity: fan.xiaomi_cn_2005161482_va3_s_2_air_purifier + name: 空气净化器 + icon_animation: true + fill_container: false + show_percentage_control: true + show_oscillate_control: false + show_direction_control: false + collapsible_controls: false + grid_options: + columns: 12 + rows: 2 + - type: custom:mushroom-cover-card + entity: cover.panpan_cn_876603548_y18_s_2_airer + name: 晾衣架 + show_buttons_control: true + show_position_control: false + grid_options: + columns: 12 + rows: 2 + header: + layout: responsive + badges_position: bottom + badges_wrap: wrap + background: + opacity: 80 + alignment: center + size: cover + repeat: repeat + attachment: fixed + cards: [] + theme: ios-dark-mode-light-blue + visible: + - user: 7a6c93d08acc4692bfd561154bb386df + - user: 7809f2c31fda449b8e7c5a0d2eaf5fd5 + - type: sections + max_columns: 4 + title: home-server + path: home-server + icon: mdi:server + theme: Mushroom Shadow + sections: + - type: grid + cards: + - type: custom:mushroom-entity-card + entity: input_boolean.on_state + icon: mdi:home + tap_action: + action: navigate + navigation_path: /lovelace/main-home + hold_action: + action: none + double_tap_action: + action: none + name: 首页 + primary_info: none + secondary_info: name + fill_container: false + layout: horizontal + grid_options: + columns: 6 + rows: 1 + icon_color: green + card_mod: + style: | + ha-card{ + box-shadow: none; + } + - type: custom:mushroom-entity-card + entity: sensor.pan_ke_feng_de_iphone_battery_level + tap_action: + action: more-info + icon: '' + icon_color: white + primary_info: name + secondary_info: none + name: Pan's Phone + grid_options: + rows: 1 + card_mod: + style: + .: | + ha-card { + /* ====== USER SETTINGS ===== */ + {% set charging_entity = + 'binary_sensor.anasbox_is_charging' %} + /* ========================= */ + + /* ======= FONT SETTINGS ======= */ + --card-primary-font-size: 15px !important; + --card-secondary-font-size: 12px !important; + --card-primary-font-weight: bold !important; + + /* --- LOGIC --- */ + {% set level = states(config.entity) | float(0) %} + {% set is_charging = is_state(charging_entity, 'on') %} + + /* 1. Determine Color */ + {% if is_charging %} + {% set color = '0, 255, 255' %} /* Cyan (Charging) */ + {% elif level <= 20 %} + {% set color = '244, 67, 54' %} /* Red */ + {% elif level <= 60 %} + {% set color = '255, 152, 0' %} /* Orange */ + {% else %} + {% set color = '0, 255, 100' %} /* Green */ + {% endif %} + + /* --- PASS VARIABLES TO CSS --- */ + --custom-level: {{ level }}%; + --custom-color: rgba({{ color }}, 0.8); + --custom-bubble: {{ 'block' if is_charging else 'none' }}; + + /* Icon Glow */ + --custom-icon-shadow: {{ '0 0 15px rgba(' ~ color ~ ', 0.6)' if is_charging else 'none' }}; + + /* Text Color */ + --text-color: {{ 'rgba(' ~ color ~ ', 1)' if level < 100 else 'rgba(255,255,255,0.7)' }}; + + /* --- CARD STYLING --- */ + background: #1c1c1c !important; + border: none !important; + border-radius: 12px; + position: relative; + overflow: hidden; + transition: all 0.5s ease; + + /* Ambient Glow behind the liquid */ + background-image: radial-gradient(circle at 24px 24px, rgba({{ color }}, 0.15) 0%, transparent 60%) !important; + } + + /* Icon Styling */ + mushroom-shape-icon { + --icon-size: 45px; + display: flex; + padding-right: 10px; + padding-bottom: 5px; + } + + /* --- PERCENTAGE BADGE (Top Right) --- */ + ha-card::before { + content: '{{ level | round(0) }}%'; + position: absolute; + top: 12px; right: 12px; + font-size: 1rem; font-weight: 700; + color: var(--text-color); + background: rgba(0, 0, 0, 0.3); + border: 1px solid rgba(255, 255, 255, 0.1); + padding: 2px 6px; border-radius: 4px; + } + + /* --- PROGRESS BAR TRACK --- */ + ha-card::after { + content: ''; + position: absolute; + bottom: 0; left: 0; + height: 4px; + width: {{ level }}%; + background: linear-gradient(90deg, transparent, rgb({{ color }})); + box-shadow: 0 0 10px rgb({{ color }}); + transition: width 0.5s ease; + } + mushroom-shape-icon$: | + .shape { + /* --- LOGIC INJECTION --- */ + --liquid-level: var(--custom-level); + --liquid-color: var(--custom-color); + --bubble-display: var(--custom-bubble); + + /* Container Setup */ + background: rgba(255, 255, 255, 0.05) !important; + overflow: hidden !important; /* Keeps the liquid inside the circle */ + position: relative; + border: 1px solid rgba(255,255,255,0.1); + box-shadow: var(--custom-icon-shadow) !important; + } + + /* THE LIQUID (Wavy Fill) */ + .shape::before { + content: ''; + position: absolute; + left: -50%; + width: 200%; + height: 200%; + + /* The top is calculated based on battery level (100% - level) */ + top: calc(100% - var(--liquid-level)); + + background: var(--liquid-color); + border-radius: 40%; /* Creates the "Wave" shape when rotated */ + + /* The Wave Animation */ + animation: liquid-wave 6s linear infinite; + opacity: 0.8; + } + + /* CHARGING BUBBLES */ + .shape::after { + content: ''; + display: var(--bubble-display); + position: absolute; + inset: 0; + background-image: + radial-gradient(2px 2px at 20% 80%, rgba(255,255,255,0.8), transparent), + radial-gradient(2px 2px at 50% 70%, rgba(255,255,255,0.8), transparent), + radial-gradient(3px 3px at 80% 90%, rgba(255,255,255,0.8), transparent); + background-size: 100% 100%; + animation: bubbles-rise 0.7s linear infinite; + } + + /* Ensure the MDI Icon sits on top of the liquid */ + ha-icon { + position: relative; + z-index: 2; + /* Blend the icon slightly so it looks submerged */ + mix-blend-mode: overlay; + color: white !important; + } + + /* --- ANIMATIONS --- */ + @keyframes liquid-wave { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + @keyframes bubbles-rise { + 0% { transform: translateY(10px); opacity: 0; } + 50% { opacity: 1; } + 100% { transform: translateY(-20px); opacity: 0; } + } + column_span: 4 + - type: grid + cards: + - type: custom:mushroom-title-card + title: Hello Mother Fucker + alignment: center + - square: false + type: grid + cards: + - type: custom:vertical-stack-in-card + cards: + - type: custom:mushroom-entity-card + entity: >- + sensor.xiaomi_cn_blt_3_1ni9qhv584c03_mini_temperature_p_2_1001 + tap_action: + action: more-info + icon: mdi:thermometer + name: 温度 + primary_info: state + secondary_info: name + card_mod: + style: + mushroom-shape-icon$: | + .shape { + {# ========== CONFIG ========== #} + {# == Updated: entity auto generated == #} + {% set temp = states(config.entity) | float(0) %} + + {# ------------------------------------------- #} + {# TEMPERATURE → COLOR + EFFECT PRESETS #} + {# ------------------------------------------- #} + + {# DEFAULTS (will be overridden by ranges) #} + {% set rgb = '0,140,255' %} + {% set anim = 'temp-cold-breathe' %} + {% set glow_anim = 'temp-cold-glow' %} + {% set halo_anim = 'temp-cold-halo' %} + {% set duration = 4.0 %} + {% set intensity = 0.5 %} + + {# RANGES / COLORS #} + {# You can change temp numbers below if needed #} + + {% if temp < 16 %} + {# BLUE #} + {% set rgb = '0,140,255' %} + {% set anim = 'temp-cold-breathe' %} + {% set glow_anim = 'temp-cold-glow' %} + {% set halo_anim = 'temp-cold-halo' %} + {% set duration = 4.4 %} + {% set intensity = 0.4 %} + + {% elif temp < 18 %} + {# YELLOW #} + {% set rgb = '255,210,40' %} + {% set anim = 'temp-cool-wave' %} + {% set glow_anim = 'temp-cool-glow' %} + {% set halo_anim = 'temp-cool-halo' %} + {% set duration = 3.4 %} + {% set intensity = 0.55 %} + + {% elif temp < 20 %} + {# ORANGE #} + {% set rgb = '255,150,40' %} + {% set anim = 'temp-comfy-breathe' %} + {% set glow_anim = 'temp-comfy-glow' %} + {% set halo_anim = 'temp-comfy-halo' %} + {% set duration = 3.0 %} + {% set intensity = 0.6 %} + + {% elif temp < 22 %} + {# DARK ORANGE #} + {% set rgb = '255,115,20' %} + {% set anim = 'temp-warm-pulse' %} + {% set glow_anim = 'temp-warm-glow' %} + {% set halo_anim = 'temp-warm-halo' %} + {% set duration = 2.4 %} + {% set intensity = 0.8 %} + + {% else %} + {# RED #} + {% set rgb = '255,40,40' %} + {% set anim = 'temp-hot-shimmer' %} + {% set glow_anim = 'temp-hot-glow' %} + {% set halo_anim = 'temp-hot-halo' %} + {% set duration = 2.0 %} + {% set intensity = 1.0 %} + {% endif %} + + {# Apply variables #} + --temp-rgb: {{ rgb }}; + --temp-intensity: {{ intensity }}; + --shape-animation: {{ anim }} {{ duration }}s ease-in-out infinite; + --temp-glow-animation: {{ glow_anim }} {{ (duration * 0.9) | round(2) }}s ease-in-out infinite; + --temp-halo-animation: {{ halo_anim }} {{ (duration * 1.15) | round(2) }}s ease-in-out infinite; + + opacity: 1; + + /* Icon color follows the temperature */ + --icon-color: rgba({{ rgb }}, 1); + + /* KILL THE THEME BLUE & MAKE SHAPE NEUTRAL */ + background-color: rgba(77, 77, 77,0.1) !important; + box-shadow: none !important; + border: 1px solid rgba(255,255,255,0.06); + + position: relative; + transform-origin: 50% 60%; + animation: var(--shape-animation); + } + + /* Glow layers */ + .shape::before, + .shape::after { + content: ''; + position: absolute; + border-radius: inherit; + pointer-events: none; + } + + .shape::before { + inset: -8px; + animation: var(--temp-glow-animation); + } + + .shape::after { + inset: -22px; + animation: var(--temp-halo-animation); + mix-blend-mode: screen; + } + + /* ========== COLD ========== */ + @keyframes temp-cold-breathe { + 0% { transform: scale(0.96); } + 50% { transform: scale(1.03); } + 100% { transform: scale(0.96); } + } + + @keyframes temp-cold-glow { + 0% { + box-shadow: + 0 0 20px 0 rgba(var(--temp-rgb), 0.6), + 0 0 34px 6 rgba(var(--temp-rgb), 0.55); + } + 50% { + box-shadow: + 0 0 30px 4 rgba(var(--temp-rgb), 0.95), + 0 0 50px 10px rgba(var(--temp-rgb), 0.85); + } + 100% { + box-shadow: + 0 0 20px 0 rgba(var(--temp-rgb), 0.6), + 0 0 34px 6 rgba(var(--temp-rgb), 0.55); + } + } + + @keyframes temp-cold-halo { + 0% { + box-shadow: + 0 0 80px 20px rgba(var(--temp-rgb), 0.35), + 0 -20px 80px -14px rgba(220, 240, 255, 0.55); + } + 50% { + box-shadow: + 0 0 130px 36px rgba(var(--temp-rgb), 0.5), + 0 -34px 100px -8px rgba(240, 250, 255, 0.8); + } + 100% { + box-shadow: + 0 0 80px 20px rgba(var(--temp-rgb), 0.35), + 0 -20px 80px -14px rgba(220, 240, 255, 0.55); + } + } + + /* ========== COOL ========== */ + @keyframes temp-cool-wave { + 0% { transform: translateX(0); } + 25% { transform: translateX(-1px); } + 50% { transform: translateX(1px) translateY(-1px); } + 75% { transform: translateX(-1px); } + 100% { transform: translateX(0); } + } + + @keyframes temp-cool-glow { + 0% { + box-shadow: + 0 0 22px 0 rgba(var(--temp-rgb), 0.6), + 0 0 34px 4 rgba(var(--temp-rgb), 0.7); + } + 50% { + box-shadow: + 0 0 28px 2 rgba(var(--temp-rgb), 0.95), + 0 0 48px 12px rgba(var(--temp-rgb), 0.85); + } + 100% { + box-shadow: + 0 0 22px 0 rgba(var(--temp-rgb), 0.6), + 0 0 34px 4 rgba(var(--temp-rgb), 0.7); + } + } + + @keyframes temp-cool-halo { + 0% { + box-shadow: + 0 0 90px 26px rgba(var(--temp-rgb), 0.35), + 0 18px 80px -12px rgba(0, 220, 255, 0.35); + } + 50% { + box-shadow: + 0 0 140px 42px rgba(var(--temp-rgb), 0.45), + 0 30px 110px -10px rgba(0, 255, 255, 0.5); + } + 100% { + box-shadow: + 0 0 90px 26px rgba(var(--temp-rgb), 0.35), + 0 18px 80px -12px rgba(0, 220, 255, 0.35); + } + } + + /* ========== COMFY ========== */ + @keyframes temp-comfy-breathe { + 0% { transform: scale(0.98); } + 50% { transform: scale(1.05); } + 100% { transform: scale(0.98); } + } + + @keyframes temp-comfy-glow { + 50% { + box-shadow: + 0 0 26px 4 rgba(var(--temp-rgb), 0.9), + 0 0 42px 10px rgba(var(--temp-rgb), 0.85); + } + } + + @keyframes temp-comfy-halo { + 50% { + box-shadow: + 0 0 120px 40px rgba(var(--temp-rgb), 0.45), + 0 26px 80px -10px rgba(180,255,200,0.5); + } + } + + /* ========== WARM ========== */ + @keyframes temp-warm-pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.07); } + 100% { transform: scale(1); } + } + + @keyframes temp-warm-glow { + 50% { + box-shadow: + 0 0 30px 4 rgba(var(--temp-rgb), 0.95), + 0 0 54px 14px rgba(var(--temp-rgb), 0.9); + } + } + + @keyframes temp-warm-halo { + 50% { + box-shadow: + 0 0 140px 48px rgba(var(--temp-rgb), 0.55), + 0 26px 100px -10px rgba(255,210,150,0.5); + } + } + + /* ========== HOT ========== */ + @keyframes temp-hot-shimmer { + 0% { transform: scale(1); filter: blur(0); } + 50% { transform: scale(1.08); filter: blur(0.6px); } + 100% { transform: scale(1); filter: blur(0); } + } + + @keyframes temp-hot-glow { + 50% { + box-shadow: + 0 0 34px 6 rgba(var(--temp-rgb), 1), + 0 0 62px 14px rgba(var(--temp-rgb), 0.95); + } + } + + @keyframes temp-hot-halo { + 50% { + box-shadow: + 0 0 160px 60px rgba(var(--temp-rgb), 0.6), + 0 34px 120px -12px rgba(255,150,100,0.6); + } + } + .: | + mushroom-shape-icon { + --icon-size: 48px; + --icon-color: rgba(var(--hum-rgb),1) !important; + display: flex; + margin: -16px 0 10px -20px !important; + padding-right: 22px; + padding-bottom: 18px; + } + ha-card { + clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 14px)); + + /* FONT SIZE & SPACING SETTINGS */ + --card-primary-font-size: 1.4rem !important; + + /* Increases vertical space between primary and secondary */ + --card-primary-line-height: 1.3 !important; + } + - type: custom:vertical-stack-in-card + cards: + - type: custom:mini-graph-card + entities: + - >- + sensor.xiaomi_cn_blt_3_1ni9qhv584c03_mini_temperature_p_2_1001 + hours_to_show: 24 + line_width: 5 + show: + name: false + icon: false + state: false + labels: false + legend: false + color_thresholds: + - value: 0 + color: blue + - value: 16 + color: lightblue + - value: 18 + color: orange + - value: 21 + color: red + card_mod: + style: | + ha-card { + background: none; + box-shadow: none; + opacity: 50%; + border: none; + width: 250px; + mask-image: radial-gradient( + ellipse at center, + rgba(0,0,0,1) 0%, + rgba(0,0,0,0) 90% + ); + } + card_mod: + style: + .: | + ha-card { + background: none; + box-shadow: none; + border: none; + margin: 8px 12px; + position: absolute; + bottom: -10px; + right: -10px; + } + - type: custom:vertical-stack-in-card + cards: + - type: custom:mushroom-entity-card + entity: >- + sensor.xiaomi_cn_blt_3_1ni9qhv584c03_mini_relative_humidity_p_2_1002 + tap_action: + action: more-info + icon: mdi:water-percent + name: 湿度 + primary_info: state + secondary_info: name + card_mod: + style: + mushroom-shape-icon$: > + .shape { + {# ========== CONFIG ========== #} + {# == Updated: entity auto generated == #} + {% set hum = states(config.entity) | float(0) %} + + {# DEFAULTS #} + {% set rgb = '120,210,255' %} + {% set anim = 'hum-good-breathe' %} + {% set glow_anim = 'hum-good-glow' %} + {% set halo_anim = 'hum-good-halo' %} + {% set duration = 3.2 %} + + {# RANGES + < 40 -> BAD (dark blue) + 40-60 -> GOOD (light blue) + > 60 -> MIDDLE / humid (medium blue) + #} + + {% if hum < 40 %} + {# BAD - dry - dark blue #} + {% set rgb = '0,80,200' %} + {% set anim = 'hum-bad-pulse' %} + {% set glow_anim = 'hum-bad-glow' %} + {% set halo_anim = 'hum-bad-halo' %} + {% set duration = 2.8 %} + {% elif hum <= 60 %} + {# GOOD - light blue #} + {% set rgb = '120,210,255' %} + {% set anim = 'hum-good-breathe' %} + {% set glow_anim = 'hum-good-glow' %} + {% set halo_anim = 'hum-good-halo' %} + {% set duration = 3.4 %} + {% else %} + {# MIDDLE / humid - medium blue #} + {% set rgb = '40,140,255' %} + {% set anim = 'hum-mid-wave' %} + {% set glow_anim = 'hum-mid-glow' %} + {% set halo_anim = 'hum-mid-halo' %} + {% set duration = 3.0 %} + {% endif %} + + --hum-rgb: {{ rgb }}; + --shape-animation: {{ anim }} {{ duration }}s ease-in-out infinite; + --hum-glow-animation: {{ glow_anim }} {{ (duration * 0.9) | round(2) }}s ease-in-out infinite; + --hum-halo-animation: {{ halo_anim }} {{ (duration * 1.1) | round(2) }}s ease-in-out infinite; + + /* Icon color follows humidity color */ + --icon-color: rgba({{ rgb }}, 1); + + /* Neutral pill so theme blue does not leak through */ + background-color: rgba(77,77,77,0.2) !important; + box-shadow: none !important; + border: 1px solid rgba(255,255,255,0.06); + + opacity: 1; + position: relative; + transform-origin: 50% 60%; + animation: var(--shape-animation); + } + + + /* Glow layers */ + + .shape::before, + + .shape::after { + content: ''; + position: absolute; + border-radius: inherit; + pointer-events: none; + } + + + .shape::before { + inset: -8px; + animation: var(--hum-glow-animation); + } + + + .shape::after { + inset: -22px; + animation: var(--hum-halo-animation); + mix-blend-mode: screen; + } + + + /* ========== GOOD - light blue ========== */ + + @keyframes hum-good-breathe { + 0% { transform: scale(0.98); } + 50% { transform: scale(1.04); } + 100% { transform: scale(0.98); } + } + + + @keyframes hum-good-glow { + 0% { + box-shadow: + 0 0 18px 0 rgba(var(--hum-rgb), 0.55), + 0 0 30px 4 rgba(var(--hum-rgb), 0.6); + } + 50% { + box-shadow: + 0 0 24px 4 rgba(var(--hum-rgb), 0.9), + 0 0 44px 10px rgba(var(--hum-rgb), 0.85); + } + 100% { + box-shadow: + 0 0 18px 0 rgba(var(--hum-rgb), 0.55), + 0 0 30px 4 rgba(var(--hum-rgb), 0.6); + } + } + + + @keyframes hum-good-halo { + 0% { + box-shadow: + 0 0 80px 24px rgba(var(--hum-rgb), 0.35), + 0 18px 70px -12px rgba(180,230,255,0.3); + } + 50% { + box-shadow: + 0 0 120px 40px rgba(var(--hum-rgb), 0.45), + 0 26px 90px -10px rgba(200,240,255,0.45); + } + 100% { + box-shadow: + 0 0 80px 24px rgba(var(--hum-rgb), 0.35), + 0 18px 70px -12px rgba(180,230,255,0.3); + } + } + + + /* ========== MIDDLE / humid - medium blue ========== + */ + + @keyframes hum-mid-wave { + 0% { transform: translateX(0); } + 25% { transform: translateX(-1px); } + 50% { transform: translateX(1px) translateY(-1px); } + 75% { transform: translateX(-1px); } + 100% { transform: translateX(0); } + } + + + @keyframes hum-mid-glow { + 0% { + box-shadow: + 0 0 20px 0 rgba(var(--hum-rgb), 0.6), + 0 0 32px 4 rgba(var(--hum-rgb), 0.7); + } + 50% { + box-shadow: + 0 0 28px 3 rgba(var(--hum-rgb), 0.95), + 0 0 48px 10px rgba(var(--hum-rgb), 0.85); + } + 100% { + box-shadow: + 0 0 20px 0 rgba(var(--hum-rgb), 0.6), + 0 0 32px 4 rgba(var(--hum-rgb), 0.7); + } + } + + + @keyframes hum-mid-halo { + 0% { + box-shadow: + 0 0 90px 26px rgba(var(--hum-rgb), 0.4), + 0 18px 80px -12px rgba(80,190,255,0.35); + } + 50% { + box-shadow: + 0 0 135px 42px rgba(var(--hum-rgb), 0.5), + 0 28px 105px -10px rgba(100,210,255,0.5); + } + 100% { + box-shadow: + 0 0 90px 26px rgba(var(--hum-rgb), 0.4), + 0 18px 80px -12px rgba(80,190,255,0.35); + } + } + + + /* ========== BAD - dry - dark blue ========== */ + + @keyframes hum-bad-pulse { + 0% { transform: scale(0.97); } + 40% { transform: scale(1.03); } + 100% { transform: scale(0.97); } + } + + + @keyframes hum-bad-glow { + 0% { + box-shadow: + 0 0 18px 0 rgba(var(--hum-rgb), 0.6), + 0 0 28px 4 rgba(var(--hum-rgb), 0.7); + } + 50% { + box-shadow: + 0 0 26px 4 rgba(var(--hum-rgb), 0.95), + 0 0 44px 12px rgba(var(--hum-rgb), 0.9); + } + 100% { + box-shadow: + 0 0 18px 0 rgba(var(--hum-rgb), 0.6), + 0 0 28px 4 rgba(var(--hum-rgb), 0.7); + } + } + + + @keyframes hum-bad-halo { + 0% { + box-shadow: + 0 0 80px 22px rgba(var(--hum-rgb), 0.45), + 0 18px 75px -10px rgba(0,70,160,0.5); + } + 50% { + box-shadow: + 0 0 130px 40px rgba(var(--hum-rgb), 0.6), + 0 26px 100px -8px rgba(0,90,190,0.65); + } + 100% { + box-shadow: + 0 0 80px 22px rgba(var(--hum-rgb), 0.45), + 0 18px 75px -10px rgba(0,70,160,0.5); + } + } + .: | + mushroom-shape-icon { + --icon-size: 48px; + --icon-color: rgba(var(--hum-rgb),1) !important; + display: flex; + margin: -16px 0 10px -20px !important; + padding-right: 22px; + padding-bottom: 18px; + } + ha-card { + clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 14px)); + + /* FONT SIZE & SPACING SETTINGS */ + --card-primary-font-size: 1.4rem !important; + + /* Increases vertical space between primary and secondary */ + --card-primary-line-height: 1.3 !important; + } + - type: custom:vertical-stack-in-card + cards: + - type: custom:mini-graph-card + entities: + - >- + sensor.xiaomi_cn_blt_3_1ni9qhv584c03_mini_relative_humidity_p_2_1002 + line_color: lightblue + line_width: 5 + hours_to_show: 24 + show: + name: false + icon: false + state: false + labels: false + legend: false + card_mod: + style: | + ha-card { + background: none; + box-shadow: none; + opacity: 50%; + border: none; + width: 250px; + mask-image: radial-gradient( + ellipse at center, + rgba(0,0,0,1) 0%, + rgba(0,0,0,0) 90% + ); + } + card_mod: + style: + .: | + ha-card { + background: none; + box-shadow: none; + border: none; + margin: 8px 12px; + position: absolute; + bottom: -10px; + right: -10px; + } + columns: 2 + - type: vertical-stack + cards: + - type: custom:bubble-card + card_type: separator + icon: '' + card_layout: large + rows: '0.5' + - type: custom:mushroom-entity-card + entity: binary_sensor.lumi_cn_775597154_acn001_contact_state_p_2_1 + tap_action: + action: more-info + icon: mdi:door + name: 机柜门 + icon_color: red + primary_info: state + secondary_info: name + card_mod: + style: + mushroom-shape-icon$: | + .shape { + {% set is_open = is_state(config.entity, 'on') %} + + /* LOGIC: Define colors and animations based on state */ + {% if is_open %} + /* OPEN: Red, Dark Red Shape, Animated */ + --icon-color-set: rgb(244, 67, 54); + --shape-color-set: rgba(160, 0, 0, 0.15); + --shape-animation: door-soft-open 1.7s ease-in-out infinite; + /* Increased duration slightly for smoother persistent glow */ + --glow-animation: door-soft-glow 2.5s ease-in-out infinite; + --beam-animation: door-soft-beam 2.5s ease-in-out infinite; + --icon-filter: drop-shadow(0 0 6px rgba(160, 0, 0, 0.95)); + {% else %} + /* CLOSED: Green, Dark Green Shape, Static */ + --icon-color-set: rgb(76, 175, 80); + --shape-color-set: rgba(0, 80, 0, 0.4); + --shape-animation: none; + --glow-animation: none; + --beam-animation: none; + --icon-filter: none; + {% endif %} + + /* Apply the variable colors defined above */ + background-color: var(--shape-color-set) !important; + + /* Standard positioning */ + position: relative; + transform-origin: 25% 50%; + animation: var(--shape-animation); + } + + /* Color and Filter for the Icon itself */ + .shape ha-icon { + color: var(--icon-color-set) !important; + filter: var(--icon-filter); + } + + /* --- ANIMATION LAYERS --- */ + .shape::before, + .shape::after { + content: ''; + position: absolute; + border-radius: inherit; + pointer-events: none; + } + + /* Inner Glow */ + .shape::before { + inset: -10px; + animation: var(--glow-animation); + } + + /* Outer Beam */ + .shape::after { + inset: -15px; + animation: var(--beam-animation); + mix-blend-mode: screen; + } + + /* --- KEYFRAMES --- */ + @keyframes door-soft-open { + 0% { transform: rotate(0deg); } + 40% { transform: rotate(-9deg); } + 70% { transform: rotate(-6deg); } + 100% { transform: rotate(0deg); } + } + + @keyframes door-soft-glow { + 0% { + box-shadow: + 0 0 20px 0 rgba(160, 0, 0, 0.7), + 0 0 40px 6 rgba(160, 0, 0, 0.6), + 0 0 80px 14px rgba(160, 0, 0, 0.4); + } + 50% { + box-shadow: + 0 0 30px 4 rgba(200, 0, 0, 1), + 0 0 70px 16px rgba(200, 0, 0, 0.95), + 0 0 120px 28px rgba(200, 0, 0, 0.7); + } + 100% { + box-shadow: + 0 0 20px 0 rgba(160, 0, 0, 0.7), + 0 0 40px 6 rgba(160, 0, 0, 0.6), + 0 0 80px 14px rgba(160, 0, 0, 0.4); + } + } + + /* UPDATED: Huge halo now never fully disappears */ + @keyframes door-soft-beam { + 0% { + /* Small, persistent base glow */ + box-shadow: + 0 0 40px 10px rgba(160, 0, 0, 0.3), + 0 0 60px 20px rgba(100, 0, 0, 0.2); + opacity: 0.4; + } + 50% { + /* Maximum Intense Glow */ + box-shadow: + 0 0 160px 60px rgba(230, 20, 20, 0.8), + 0 0 230px 100px rgba(160, 0, 0, 0.7); + opacity: 1; + } + 100% { + /* Return to base glow */ + box-shadow: + 0 0 40px 10px rgba(160, 0, 0, 0.3), + 0 0 60px 20px rgba(100, 0, 0, 0.2); + opacity: 0.4; + } + } + .: | + mushroom-shape-icon { + --icon-size: 48px; + display: flex; + margin: -5px 0 10px -14px !important; + padding-right: 22px; + } + ha-card { + clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 14px)); + } + grid_options: + columns: 12 + rows: 1 + - type: vertical-stack + cards: + - type: custom:bubble-card + card_type: separator + icon: '' + card_layout: large + rows: '0.5' + - type: custom:mushroom-light-card + entity: light.lemesh_cn_2077009002_wy0d02_s_2_light + use_light_color: true + show_color_temp_control: true + show_brightness_control: true + collapsible_controls: false + show_color_control: true + name: 机柜灯 + icon_color: auto + grid_options: + columns: 12 + rows: 2 + card_mod: + style: + mushroom-shape-icon$: | + .shape { + {# ========== USER CONFIG ========== #} + {% set state_entity = 'light.lemesh_cn_2077009002_wy0d02_s_2_light' %} + {% set active_value = 'on' %} + + {# ======== TRIGGER ======== #} + {% set trigger_active = (states(state_entity) == active_value) %} + + {# ======== COLOR FROM ENTITY (rgb_color) ======== #} + {% set rgb = state_attr(config.entity, 'rgb_color') %} + {% if rgb is not none %} + {% set r = (rgb[0] | int) %} + {% set g = (rgb[1] | int) %} + {% set b = (rgb[2] | int) %} + {% else %} + {# fallback for CT-only lights #} + {% set r = 255 %} + {% set g = 240 %} + {% set b = 200 %} + {% endif %} + + {% if trigger_active %} + --shape-animation: lamp-sweep 1.8s ease-in-out infinite; + opacity: 1; + {% else %} + --shape-animation: none; + opacity: 0.5; + {% endif %} + + position: relative; + transform-origin: 50% 60%; + } + + @keyframes lamp-sweep { + 0% { + filter: brightness(1.1); + box-shadow: + 0 0 12px 4px rgba({{ r }}, {{ g }}, {{ b }}, 0.9), + 0 30px 40px -10px rgba({{ r }}, {{ g }}, {{ b }}, 0.0); + transform: rotate(-4deg) scale(1); + } + 25% { + filter: brightness(1.4); + box-shadow: + 0 0 18px 8px rgba({{ r }}, {{ g }}, {{ b }}, 1), + -8px 35px 45px -12px rgba({{ r }}, {{ g }}, {{ b }}, 0.5); + transform: rotate(3deg) scale(1.02); + } + 50% { + filter: brightness(1.2); + box-shadow: + 0 0 24px 10px rgba({{ r }}, {{ g }}, {{ b }}, 0.9), + 8px 35px 45px -12px rgba({{ r }}, {{ g }}, {{ b }}, 0.5); + transform: rotate(-2deg) scale(1.03); + } + 75% { + filter: brightness(1.5); + box-shadow: + 0 0 18px 8px rgba({{ r }}, {{ g }}, {{ b }}, 1), + -6px 30px 40px -12px rgba({{ r }}, {{ g }}, {{ b }}, 0.4); + transform: rotate(2deg) scale(1.01); + } + 100% { + filter: brightness(1.1); + box-shadow: + 0 0 12px 4px rgba({{ r }}, {{ g }}, {{ b }}, 0.8), + 0 30px 40px -10px rgba({{ r }}, {{ g }}, {{ b }}, 0.0); + transform: rotate(-4deg) scale(1); + } + } + .: | + mushroom-shape-icon { + --icon-size: 65px; + display: flex; + margin: -18px 0px 0px -18px !important; + } + ha-card { + clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 12px)); + } + icon: mdi:led-strip-variant + - type: vertical-stack + cards: + - type: custom:bubble-card + card_type: separator + icon: '' + card_layout: large + rows: '0.5' + - type: custom:mushroom-entity-card + entity: switch.cuco_cn_2006579195_v3_on_p_2_1 + tap_action: + action: none + icon: mdi:power-socket-au + icon_color: light-blue + name: Nas 插座 + card_mod: + style: + mushroom-shape-icon$: | + .shape { + {# ========== USER CONFIG ========== #} + {# true = number mode, false = state mode #} + {% set use_number = false %} + + {# STATE MODE SETTINGS #} + {% set state_entity = 'switch.cuco_cn_2006579195_v3_on_p_2_1' %} + {% set active_value = 'on' %} + + {# OPTIONAL: NUMBER MODE SETTINGS #} + {% set number_entity = 'sensor.plug_power' %} + + {# '>' '<' '=' '>=' '<=' #} + {% set number_operator = '>' %} + + {% set threshold = 0.0 %} + {# ========== END USER CONFIG ====== #} + + {# ======== TRIGGER DECISION LOGIC ======== #} + {% if use_number %} + {% set num = states(number_entity) | float(0) %} + + {% if number_operator == '>' %} + {% set trigger_active = (num > threshold) %} + {% elif number_operator == '<' %} + {% set trigger_active = (num < threshold) %} + {% elif number_operator == '=' %} + {% set trigger_active = (num == threshold) %} + {% elif number_operator == '>=' %} + {% set trigger_active = (num >= threshold) %} + {% elif number_operator == '<=' %} + {% set trigger_active = (num <= threshold) %} + {% else %} + {% set trigger_active = false %} + {% endif %} + {% else %} + {% set trigger_active = (states(state_entity) == active_value) %} + {% endif %} + {# =========== END TRIGGER LOGIC =========== #} + + {% if trigger_active %} + {# adjust speed based on this plug's own power attribute #} + {% set p = state_attr(config.entity, 'current_power_w') | float(0) %} + {% set dur = 1.6 - (p / 800) %} + --shape-animation: ultra-plug-on {{ [0.4, dur] | max | round(2) }}s ease-in-out infinite; + --plug-glow-animation: ultra-plug-glow 1.8s ease-in-out infinite; + --plug-arcs-animation: ultra-plug-arcs 1.2s linear infinite; + opacity: 1; + {% else %} + --shape-animation: none; + --plug-glow-animation: none; + --plug-arcs-animation: none; + opacity: 0.7; + {% endif %} + + transform-origin: 50% 50%; + position: relative; + } + + .shape::before, + .shape::after { + content: ''; + position: absolute; + inset: -4px; + border-radius: inherit; + pointer-events: none; + } + + .shape::before { + animation: var(--plug-glow-animation); + } + + .shape::after { + animation: var(--plug-arcs-animation); + } + + @keyframes ultra-plug-on { + 0% { transform: scale(1); } + 25% { transform: scale(1.05) translateY(-1px); } + 50% { transform: scale(1.08) translateY(-2px); } + 75% { transform: scale(1.04) translateY(-1px); } + 100% { transform: scale(1); } + } + + @keyframes ultra-plug-glow { + 0% { + box-shadow: + 0 0 10px 3px rgba(var(--rgb-{{ config.icon_color }}), 0.6), + 0 0 20px 8px rgba(var(--rgb-{{ config.icon_color }}), 0.3); + } + 50% { + box-shadow: + 0 0 18px 6px rgba(var(--rgb-{{ config.icon_color }}), 0.9), + 0 0 32px 12px rgba(var(--rgb-{{ config.icon_color }}), 0.4); + } + 100% { + box-shadow: + 0 0 10px 3px rgba(var(--rgb-{{ config.icon_color }}), 0.6), + 0 0 20px 8px rgba(var(--rgb-{{ config.icon_color }}), 0.3); + } + } + + @keyframes ultra-plug-arcs { + 0% { + box-shadow: + -10px -6px 0 -4px rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 12px 4px 0 -4px rgba(var(--rgb-{{ config.icon_color }}), 0.0); + } + 25% { + box-shadow: + -10px -6px 0 -4px rgba(var(--rgb-{{ config.icon_color }}), 0.7), + 12px 4px 0 -4px rgba(var(--rgb-{{ config.icon_color }}), 0.4); + } + 50% { + box-shadow: + -6px 2px 0 -4px rgba(var(--rgb-{{ config.icon_color }}), 0.3), + 10px -8px 0 -4px rgba(var(--rgb-{{ config.icon_color }}), 0.7); + } + 75% { + box-shadow: + -12px 4px 0 -4px rgba(var(--rgb-{{ config.icon_color }}), 0.5), + 8px 0 0 -4px rgba(var(--rgb-{{ config.icon_color }}), 0.2); + } + 100% { + box-shadow: + -10px -6px 0 -4px rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 12px 4px 0 -4px rgba(var(--rgb-{{ config.icon_color }}), 0.0); + } + } + .: | + mushroom-shape-icon { + --icon-size: 65px; + display: flex; + margin: -18px 0 10px -20px !important; + padding-right: 10px; + } + ha-card { + clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 12px)); + } + grid_options: + columns: 12 + rows: 1.5 + - type: custom:mushroom-entity-card + entity: sensor.cuco_cn_2006579195_v3_electric_power_p_11_2 + icon: mdi:nas + icon_color: red + hold_action: + action: more-info + double_tap_action: + action: none + name: Nas Plug + card_mod: + style: + .: | + ha-card { + /* ================= USER SETTINGS ================= */ + {% set power_entity = 'sensor.cuco_cn_2006579195_v3_electric_power_p_11_2' %} + {% set max_w = 60 %} + /* ================================================= */ + + /* --- LOGIC --- */ + {% set current_w = states(power_entity) | float(0) %} + + /* ACTIVE determines if animations/glows are ON (depends only on power) */ + {% set active = current_w > 0 %} + + /* Calculate Load Percentage */ + {% set width_pct = (current_w / max_w * 100) | round(2) %} + {% if width_pct > 100 %}{% set width_pct = 100 %}{% endif %} + + /* --- PASS VARIABLES TO CSS --- */ + /* Define the color we want the effects to use (from config) */ + --active-color: var(--rgb-{{ config.icon_color }}); + + --bar-width: {{ width_pct }}%; + --bar-opacity: {{ 1 if active else 0 }}; + + /* Text color: If power > 0, show colored text. Else dim. */ + --text-color: {{ 'rgba(var(--active-color), 1)' if active else 'rgba(255,255,255,0.5)' }}; + + /* PASS ANIMATION VARIABLES DOWN TO SHADOW DOM */ + --shape-animation: {{ 'ultra-plug-on 3s ease-in-out infinite' if active else 'none' }}; + --plug-glow-animation: {{ 'ultra-plug-glow 3s ease-in-out infinite' if active else 'none' }}; + --plug-arcs-animation: {{ 'ultra-plug-arcs 3s linear infinite' if active else 'none' }}; + --effect-opacity: {{ 1 if active else 0 }}; + + /* --- CARD STYLING --- */ + {% if active %} + background-color: #1c1c1c !important; + border: none !important; + background-image: + radial-gradient(circle at 24px 24px, rgba(var(--active-color), 0.35) 0%, rgba(var(--active-color), 0.1) 40%, transparent 80%), + linear-gradient(to right, rgba(255,255,255,0.1), rgba(255,255,255,0.1)) !important; + background-size: 100% 100%, 100% 6px !important; + background-position: top left, bottom left !important; + background-repeat: no-repeat !important; + {% else %} + /* When Power is 0: Clean slate */ + background-image: none !important; + box-shadow: none !important; + {% endif %} + + border-radius: 12px; + position: relative; + overflow: hidden; + transition: all 0.5s ease; + } + + /* --- WATTAGE BADGE (Top Right) --- */ + ha-card::before { + content: '{{ current_w | round(1) }} W'; + position: absolute; + top: 12px; + right: 12px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.5px; + color: var(--text-color); + + /* Only show badge background if active */ + background: {{ 'rgba(0, 0, 0, 0.2)' if active else 'transparent' }}; + border: {{ '1px solid rgba(255, 255, 255, 0.1)' if active else 'none' }}; + + padding: 4px 8px; + border-radius: 6px; + backdrop-filter: blur(2px); + transition: all 0.5s ease; + z-index: 2; + } + + /* --- ACTIVE PROGRESS BAR (BEAM) --- */ + ha-card::after { + content: ''; + position: absolute; + bottom: 0; left: 0; + height: 6px; + width: var(--bar-width); + + background: linear-gradient(90deg, + rgba(var(--active-color), 0.4) 0%, + rgba(var(--active-color), 1) 100% + ); + + box-shadow: 0 -2px 15px rgba(var(--active-color), 0.9); + opacity: var(--bar-opacity); + transition: width 0.5s ease-out, opacity 0.5s ease; + animation: current-flow 1.5s infinite linear; + } + + @keyframes current-flow { + 0% { filter: brightness(1); opacity: 0.8; } + 50% { filter: brightness(1.5); opacity: 1; } + 100% { filter: brightness(1); opacity: 0.8; } + } + + mushroom-shape-icon { + --icon-size: 65px; + display: flex; + margin: -18px 0 10px -20px !important; + padding-right: 15px; + padding-bottom: 15px; + } + mushroom-shape-icon$: > + .shape { + /* Receive animation variables from Main Card logic */ + animation: var(--shape-animation) !important; + overflow: visible !important; + position: relative; + } + + + /* Create the Pseudo-Elements for Glow and Arcs */ + + .shape::before, + + .shape::after { + content: ''; + position: absolute; + inset: -4px; + border-radius: inherit; + pointer-events: none; + opacity: var(--effect-opacity); + } + + + /* Glow Layer */ + + .shape::before { + animation: var(--plug-glow-animation); + } + + + /* Electric Arcs Layer */ + + .shape::after { + animation: var(--plug-arcs-animation); + } + + + /* --- ANIMATION KEYFRAMES --- */ + + /* Note: We use var(--active-color) so the glow stays green + even if icon is gray */ + + + @keyframes ultra-plug-on { + 0% { transform: scale(1); } + 25% { transform: scale(1.05) translateY(-1px); } + 50% { transform: scale(1.08) translateY(-2px); } + 75% { transform: scale(1.04) translateY(-1px); } + 100% { transform: scale(1); } + } + + + @keyframes ultra-plug-glow { + 0% { + box-shadow: + 0 0 10px 3px rgba(var(--active-color), 0.6), + 0 0 20px 8px rgba(var(--active-color), 0.3); + } + 50% { + box-shadow: + 0 0 18px 6px rgba(var(--active-color), 0.9), + 0 0 32px 12px rgba(var(--active-color), 0.4); + } + 100% { + box-shadow: + 0 0 10px 3px rgba(var(--active-color), 0.6), + 0 0 20px 8px rgba(var(--active-color), 0.3); + } + } + + + @keyframes ultra-plug-arcs { + 0% { + box-shadow: + -10px -6px 0 -4px rgba(var(--active-color), 0.0), + 12px 4px 0 -4px rgba(var(--active-color), 0.0); + } + 25% { + box-shadow: + -10px -6px 0 -4px rgba(var(--active-color), 0.7), + 12px 4px 0 -4px rgba(var(--active-color), 0.4); + } + 50% { + box-shadow: + -6px 2px 0 -4px rgba(var(--active-color), 0.3), + 10px -8px 0 -4px rgba(var(--active-color), 0.7); + } + 75% { + box-shadow: + -12px 4px 0 -4px rgba(var(--active-color), 0.5), + 8px 0 0 -4px rgba(var(--active-color), 0.2); + } + 100% { + box-shadow: + -10px -6px 0 -4px rgba(var(--active-color), 0.0), + 12px 4px 0 -4px rgba(var(--active-color), 0.0); + } + } + grid_options: + columns: 12 + rows: 1.5 + - type: custom:mushroom-entity-card + entity: sensor.cuco_cn_2006579195_v3_power_consumption_p_11_1 + tap_action: + action: more-info + name: 用电量 + icon: mdi:lightning-bolt + primary_info: state + secondary_info: name + icon_color: light-grey + card_mod: + style: | + ha-card { + /* ===== LOGIC & SETTINGS ===== */ + {% set state = states(config.entity) %} + /* 1. Get the Grass Sensor Data */ + {% set grass = states('sensor.cuco_cn_2006579195_v3_power_consumption_p_11_1') %} + + /* 2. Define Colors based on state */ + {% if state in ['high', 'very-high', 'red'] %} + {% set c1 = '255, 65, 108' %} /* Red RGB */ + {% set c2 = '255, 75, 43' %} /* Orange RGB */ + {% set speed = '4s' %} + {% elif state in ['moderate', 'medium', 'orange'] %} + {% set c1 = '247, 151, 30' %} /* Orange RGB */ + {% set c2 = '255, 210, 0' %} /* Yellow RGB */ + {% set speed = '7s' %} + {% else %} + {% set c1 = '0, 242, 96' %} /* Green RGB */ + {% set c2 = '5, 117, 230' %} /* Blue RGB */ + {% set speed = '12s' %} + {% endif %} + + /* 3. Pass variables to CSS */ + --aura-color-1: rgba({{ c1 }}, 0.6); + --aura-color-2: rgba({{ c2 }}, 0.6); + --speed: {{ speed }}; + + /* 4. Card Body Styling */ + background: #101010 !important; + border-radius: 25px; + border: 1px solid rgba(255,255,255,0.08); + position: relative; + overflow: hidden; + box-shadow: inset 0 0 20px rgba(0,0,0,0.5); + } + + ha-card::before { + content: ""; + position: absolute; + top: -50%; left: -50%; + width: 200%; height: 200%; + z-index: 0; + + /* Two radial gradients in one element */ + background-image: + radial-gradient(circle at 50% 50%, var(--aura-color-1) 0%, transparent 50%), + radial-gradient(circle at 80% 80%, var(--aura-color-2) 0%, transparent 50%); + + filter: blur(60px); + opacity: 0.8; + animation: rotate-aura var(--speed) linear infinite; + } + + ha-card::after { + /* INJECT THE DATA HERE */ + content: '{{ grass }}'; + + position: absolute; + top: 50%; + right: 16px; + transform: translateY(-50%); + z-index: 10; + + /* Badge Styling (Like the battery example) */ + color: white; + font-weight: 700; + font-size: 14px; + background: rgba(0, 0, 0, 0.4); + border: 1px solid rgba(255, 255, 255, 0.15); + backdrop-filter: blur(4px); + padding: 4px 10px; + border-radius: 12px; + text-shadow: 0 1px 2px black; + } + + /* --- GLASS SURFACE OVERLAY --- */ + mushroom-shape-icon { + --icon-size: 68px; + --icon-color: rgba(var(--voc-rgb),1) !important; + display: flex; + margin: -18px 0 10px -15px !important; + padding-right: 22px; + padding-bottom: 10px; + } + ha-card { + clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 14px)); + + --card-primary-font-size: 1.2rem !important; + --card-primary-line-height: 1.3 !important; + } + + /* Typography */ + .mushroom-state-info span.primary { + color: white !important; + font-weight: 600 !important; + font-size: 16px !important; + text-shadow: 0 2px 4px rgba(0,0,0,0.5); + } + .mushroom-state-info span.secondary { + color: rgba(255,255,255,0.8) !important; + } + + /* Icon Styling */ + mushroom-shape-icon { + --icon-size: 60px; + --icon-color: white !important; + } + ha-state-icon { + color: white !important; + } + + /* Animation */ + @keyframes rotate-aura { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + grid_options: + columns: 12 + rows: 1.5 + column_span: 1 + cards: [] + visible: + - user: 7a6c93d08acc4692bfd561154bb386df + - user: 7809f2c31fda449b8e7c5a0d2eaf5fd5 + - type: masonry + path: wash + cards: + - type: custom:mushroom-entity-card + entity: sensor.875060396221041554_bsh_common_setting_powerstate + name: 洗衣机 + 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.bshcn_cn_x_13164_37533621_875060396221041554_e0ec0342_k7d00w_status_p_2_24' %} + {% set ent_timerem = 'sensor.bshcn_cn_x_13164_37533621_875060396221041554_e0ec0342_k7d00w_left_time_p_2_1' %} + {% set max_time = 120 %} + {% set ent_power = 'sensor.smart_plug_power' %} + + /* OPTIONAL Progress Percentage Sensor (if exists) */ + {% set ent_percent = 'sensor.875060396221041554_bsh_common_option_programprogress' %} + + /* 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 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 %} + + {% 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; + } + grid_options: + columns: 12 + rows: 1 + icon: mdi:washing-machine + visible: + - user: 7a6c93d08acc4692bfd561154bb386df + - user: 7809f2c31fda449b8e7c5a0d2eaf5fd5 + - type: sections + max_columns: 4 + title: 测试 + path: unknown + sections: + - type: grid + cards: + - type: heading + heading: 新建部件 + visible: + - user: 7a6c93d08acc4692bfd561154bb386df + - user: 7809f2c31fda449b8e7c5a0d2eaf5fd5 + cards: [] + - type: sections + path: anashost + max_columns: 4 + title: Anashost + icon: mdi:test-tube + sections: + - type: grid + cards: + - type: heading + heading: 新建部件 + - type: custom:stack-in-card + mode: vertical + keep: + outer_padding: false + margin: true + cards: + - type: custom:mushroom-chips-card + chips: + - type: template + entity: >- + sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1 + icon_color: '{{states(''sensor.livingroom_temp_color_no_rgb'')}}' + icon: mdi:thermometer + use_entity_picture: true + double_tap_action: + action: none + hold_action: + action: none + tap_action: + action: more-info + content: >- + {% if + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1','unavailable') + %} + - + {% elif + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1','unknown') + %} + - + {% else %} + {{states('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1')}}° + {% endif %} + - type: template + entity: >- + sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2 + icon_color: light-blue + icon: mdi:water-percent + name: '' + use_entity_picture: true + hold_action: + action: none + double_tap_action: + action: none + tap_action: + action: more-info + content: >- + {% if + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2','unavailable') + %} + - + {% else %} + {{states('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2')}}% + {% endif %} + - type: template + double_tap_action: + action: none + icon: mdi:lightbulb-group + content: |- + {% if is_state('sensor.lights_of_count','0') %} + + {% else %} + {{states('sensor.lights_of_count')}} + {% endif %} + icon_color: |- + {% set state = states('sensor.lights_of_count')|float %} + {% if state <= 0 %} grey + {% else %} amber + {% endif %} + hold_action: + action: none + tap_action: + action: none + entity: sensor.lights_of_count + - type: template + double_tap_action: + action: none + icon: mdi:water + content: |- + {% if is_state('sensor.water_damage_count','0') %} + + {% else %} + {{states('sensor.water_damage_count')}} + {% endif %} + icon_color: >- + {% set state = states('sensor.water_damage_count')|float + %} + + {% if state <= 0 %} grey + + {% else %} blue + + {% endif %} + hold_action: + action: none + tap_action: + action: none + entity: sensor.water_damage_count + alignment: center + - type: custom:swipe-card + mode: vertical + keep: + outer_padding: false + margin: true + box_shadow: false + background: false + cards: + - type: horizontal-stack + cards: + - type: custom:mushroom-entity-card + card_mod: + style: | + ha-card{ + box-shadow: none; + } + entity: input_boolean.on_state + double_tap_action: + action: none + icon: mdi:home + icon_color: teal + layout: vertical + hold_action: + action: none + tap_action: + action: navigate + navigation_path: main-home + primary_info: none + secondary_info: name + name: 首页 + - type: custom:mushroom-entity-card + card_mod: + style: | + ha-card{ + box-shadow: none; + } + entity: input_boolean.on_state + icon: mdi:server + icon_color: white + layout: vertical + primary_info: none + secondary_info: name + tap_action: + action: navigate + navigation_path: home-server + hold_action: + action: none + double_tap_action: + action: none + name: 机房 + - type: custom:mushroom-entity-card + card_mod: + style: | + ha-card{ + box-shadow: none; + } + entity: light.quan_wu_zhao_ming + icon_color: yellow + secondary_info: name + layout: vertical + name: Lights + double_tap_action: + action: none + hold_action: + action: none + tap_action: + action: more-info + primary_info: none + icon: mdi:lamps + - type: custom:stack-in-card + mode: vertical + keep: + outer_padding: false + margin: true + cards: + - type: custom:mushroom-chips-card + chips: + - type: template + entity: >- + sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1 + icon_color: '{{states(''sensor.livingroom_temp_color_no_rgb'')}}' + icon: mdi:thermometer + use_entity_picture: true + double_tap_action: + action: none + hold_action: + action: none + tap_action: + action: more-info + content: >- + {% if + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1','unavailable') + %} + - + {% elif + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1','unknown') + %} + - + {% else %} + {{states('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1')}}° + {% endif %} + - type: template + entity: >- + sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2 + icon_color: light-blue + icon: mdi:water-percent + name: '' + use_entity_picture: true + hold_action: + action: none + double_tap_action: + action: none + tap_action: + action: more-info + content: >- + {% if + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2','unavailable') + %} + - + {% else %} + {{states('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2')}}% + {% endif %} + - type: template + double_tap_action: + action: none + icon: mdi:lightbulb-group + content: |- + {% if is_state('sensor.lights_of_count','0') %} + + {% else %} + {{states('sensor.lights_of_count')}} + {% endif %} + icon_color: |- + {% set state = states('sensor.lights_of_count')|float %} + {% if state <= 0 %} grey + {% else %} amber + {% endif %} + hold_action: + action: none + tap_action: + action: none + entity: sensor.lights_of_count + - type: template + double_tap_action: + action: none + icon: mdi:water + content: |- + {% if is_state('sensor.water_damage_count','0') %} + + {% else %} + {{states('sensor.water_damage_count')}} + {% endif %} + icon_color: >- + {% set state = states('sensor.water_damage_count')|float + %} + + {% if state <= 0 %} grey + + {% else %} blue + + {% endif %} + hold_action: + action: none + tap_action: + action: none + entity: sensor.water_damage_count + alignment: center + - type: custom:stack-in-card + mode: vertical + keep: + outer_padding: false + margin: true + cards: + - type: custom:mushroom-chips-card + chips: + - type: template + entity: >- + sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1 + icon_color: '{{states(''sensor.livingroom_temp_color_no_rgb'')}}' + icon: mdi:thermometer + use_entity_picture: true + double_tap_action: + action: none + hold_action: + action: none + tap_action: + action: more-info + content: >- + {% if + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1','unavailable') + %} + - + {% elif + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1','unknown') + %} + - + {% else %} + {{states('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1')}}° + {% endif %} + - type: template + entity: >- + sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2 + icon_color: light-blue + icon: mdi:water-percent + name: '' + use_entity_picture: true + hold_action: + action: none + double_tap_action: + action: none + tap_action: + action: more-info + content: >- + {% if + is_state('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2','unavailable') + %} + - + {% else %} + {{states('sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2')}}% + {% endif %} + - type: template + double_tap_action: + action: none + icon: mdi:lightbulb-group + content: |- + {% if is_state('sensor.lights_of_count','0') %} + + {% else %} + {{states('sensor.lights_of_count')}} + {% endif %} + icon_color: |- + {% set state = states('sensor.lights_of_count')|float %} + {% if state <= 0 %} grey + {% else %} amber + {% endif %} + hold_action: + action: none + tap_action: + action: none + entity: sensor.lights_of_count + - type: template + double_tap_action: + action: none + icon: mdi:water + content: |- + {% if is_state('sensor.water_damage_count','0') %} + + {% else %} + {{states('sensor.water_damage_count')}} + {% endif %} + icon_color: >- + {% set state = states('sensor.water_damage_count')|float + %} + + {% if state <= 0 %} grey + + {% else %} blue + + {% endif %} + hold_action: + action: none + tap_action: + action: none + entity: sensor.water_damage_count + alignment: center + card_mod: + style: | + ha-card { + background-color: rgba(255, 255, 255, 0) !important; + box-shadow: none !important; + padding: 2px 0px 2px 0px; + } + visible: + - user: 7a6c93d08acc4692bfd561154bb386df + - user: 7809f2c31fda449b8e7c5a0d2eaf5fd5 + cards: [] + - type: sections + max_columns: 4 + title: 中控 + path: control-center + icon: mdi:view-dashboard + theme: ios-dark-mode-light-blue + sections: + - type: grid + column_span: 4 + cards: + - type: picture + image: + media_content_id: /local/images/lbxx.jpg + media_content_type: '' + metadata: + navigateIds: + - {} + - media_content_type: '' + media_content_id: __MANUAL_ENTRY__ + grid_options: + rows: auto + columns: 3 + - type: custom:button-card + show_icon: false + show_name: false + show_state: false + grid_options: + columns: 12 + rows: 2 + custom_fields: + greeting: | + [[[ + const h = new Date().getHours(); + if (h < 6) return "夜深了"; + if (h < 11) return "早上好"; + if (h < 14) return "中午好"; + if (h < 18) return "下午好"; + return "晚上好"; + ]]] + clock: | + [[[ + return new Date().toLocaleTimeString( + 'zh-CN', + { hour: '2-digit', minute: '2-digit', hour12: false } + ); + ]]] + date: | + [[[ + return new Date().toLocaleDateString( + 'zh-CN', + { month: 'long', day: 'numeric', weekday: 'long' } + ); + ]]] + subtitle: | + [[[ + const leak = Number(states['sensor.water_damage_count']?.state ?? 0); + const lights = Number(states['sensor.lights_of_count']?.state ?? 0); + const ac = Number(states['sensor.aircondition_of_count']?.state ?? 0); + + if (leak > 0) return `⚠ 检测到 ${leak} 个水浸告警`; + if (lights === 0 && ac === 0) return "家里一切正常"; + return `${lights} 盏灯开启 · ${ac} 台空调运行`; + ]]] + styles: + card: + - height: 120px + - padding: 20px 24px + - border-radius: 24px + - border: none + - background: >- + linear-gradient(135deg, rgba(32,35,42,0.95), + rgba(20,22,27,0.95)) + - box-shadow: none + grid: + - grid-template-areas: | + "greeting clock" + "subtitle date" + - grid-template-columns: 1fr auto + - grid-template-rows: 1fr 1fr + custom_fields: + greeting: + - justify-self: start + - align-self: end + - font-size: 30px + - font-weight: 700 + - color: white + subtitle: + - justify-self: start + - align-self: start + - font-size: 14px + - color: rgba(255,255,255,0.55) + clock: + - justify-self: end + - align-self: end + - font-size: 32px + - font-weight: 600 + - color: white + date: + - justify-self: end + - align-self: start + - font-size: 14px + - color: rgba(255,255,255,0.55) + - type: grid + column_span: 4 + cards: + - type: custom:button-card + entity: weather.he_feng_tian_qi_heweather + show_icon: false + show_name: false + show_state: false + grid_options: + columns: 6 + rows: 3 + custom_fields: + weather_icon: | + [[[ + const s = entity?.state; + const isNight = + states['sun.sun']?.state === 'below_horizon'; + + if (s === 'sunny') + return isNight ? '🌙' : '☀️'; + + if (s === 'clear-night') + return '🌙'; + + if (s === 'cloudy') + return '☁️'; + + if (s === 'partlycloudy') + return isNight ? '☁️' : '⛅'; + + if (s === 'fog') + return '🌫️'; + + if (s === 'windy' || s === 'windy-variant') + return '🌬️'; + + if (s === 'rainy') + return '🌧️'; + + if (s === 'pouring') + return '🌧️'; + + if (s === 'lightning' || s === 'lightning-rainy') + return '⛈️'; + + if (s === 'hail') + return '🌨️'; + + if (s === 'snowy' || s === 'snowy-rainy') + return '❄️'; + + return '🌤️'; + ]]] + temp: | + [[[ + const temp = Number(entity?.attributes?.temperature); + + return Number.isFinite(temp) + ? `${Math.round(temp)}°` + : '--°'; + ]]] + condition: | + [[[ + const textEntity = + states['sensor.he_feng_tian_qi_heweather_text']; + + if ( + textEntity && + !['unknown', 'unavailable'].includes(textEntity.state) + ) { + return textEntity.state; + } + + const map = { + sunny: '晴', + 'clear-night': '晴', + cloudy: '阴', + partlycloudy: '多云', + fog: '雾', + rainy: '小雨', + pouring: '大雨', + lightning: '雷电', + 'lightning-rainy': '雷雨', + hail: '冰雹', + snowy: '雪', + 'snowy-rainy': '雨夹雪', + windy: '有风', + 'windy-variant': '大风' + }; + + return map[entity?.state] ?? '--'; + ]]] + forecast: | + [[[ + const feelsLike = + Number(entity?.attributes?.apparent_temperature); + + if (Number.isFinite(feelsLike)) { + return `今天 · 体感 ${Math.round(feelsLike)}°`; + } + + return '今天'; + ]]] + humidity: | + [[[ + const humidity = + Number(entity?.attributes?.humidity); + + return Number.isFinite(humidity) + ? `湿度 ${Math.round(humidity)}%` + : '湿度 --'; + ]]] + styles: + card: + - height: 175px + - border-radius: 24px + - border: none + - padding: 22px + - overflow: hidden + - background: | + linear-gradient( + 145deg, + #334b68 0%, + #23334a 55%, + #172131 100% + ) + - box-shadow: none + grid: + - grid-template-areas: | + "weather_icon temp" + "weather_icon condition" + "forecast humidity" + - grid-template-columns: 1.3fr 1fr + - grid-template-rows: 2fr 1fr 1fr + custom_fields: + weather_icon: + - font-size: 74px + - align-self: center + - justify-self: center + - animation: weatherFloat 5s ease-in-out infinite + temp: + - font-size: 46px + - font-weight: 700 + - justify-self: start + - align-self: end + - color: white + condition: + - font-size: 17px + - align-self: start + - justify-self: start + - color: rgba(255,255,255,0.7) + forecast: + - font-size: 13px + - align-self: end + - justify-self: center + - color: rgba(255,255,255,0.6) + humidity: + - font-size: 13px + - align-self: end + - justify-self: start + - color: rgba(255,255,255,0.6) + extra_styles: | + @keyframes weatherFloat { + 0% { + transform: translateY(0px) scale(1); + } + + 50% { + transform: translateY(-6px) scale(1.03); + } + + 100% { + transform: translateY(0px) scale(1); + } + } + - square: false + type: grid + cards: + - type: custom:vertical-stack-in-card + cards: + - type: custom:mushroom-entity-card + entity: >- + sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_temperature_p_2_1 + tap_action: + action: more-info + icon: mdi:thermometer + name: 温度 + primary_info: state + secondary_info: name + card_mod: + style: + mushroom-shape-icon$: | + .shape { + {# ========== CONFIG ========== #} + {# == Updated: entity auto generated == #} + {% set temp = states(config.entity) | float(0) %} + + {# ------------------------------------------- #} + {# TEMPERATURE → COLOR + EFFECT PRESETS #} + {# ------------------------------------------- #} + + {# DEFAULTS (will be overridden by ranges) #} + {% set rgb = '0,140,255' %} + {% set anim = 'temp-cold-breathe' %} + {% set glow_anim = 'temp-cold-glow' %} + {% set halo_anim = 'temp-cold-halo' %} + {% set duration = 4.0 %} + {% set intensity = 0.5 %} + + {# RANGES / COLORS #} + {# You can change temp numbers below if needed #} + + {% if temp < 16 %} + {# BLUE #} + {% set rgb = '0,140,255' %} + {% set anim = 'temp-cold-breathe' %} + {% set glow_anim = 'temp-cold-glow' %} + {% set halo_anim = 'temp-cold-halo' %} + {% set duration = 4.4 %} + {% set intensity = 0.4 %} + + {% elif temp < 18 %} + {# YELLOW #} + {% set rgb = '255,210,40' %} + {% set anim = 'temp-cool-wave' %} + {% set glow_anim = 'temp-cool-glow' %} + {% set halo_anim = 'temp-cool-halo' %} + {% set duration = 3.4 %} + {% set intensity = 0.55 %} + + {% elif temp < 20 %} + {# ORANGE #} + {% set rgb = '255,150,40' %} + {% set anim = 'temp-comfy-breathe' %} + {% set glow_anim = 'temp-comfy-glow' %} + {% set halo_anim = 'temp-comfy-halo' %} + {% set duration = 3.0 %} + {% set intensity = 0.6 %} + + {% elif temp < 22 %} + {# DARK ORANGE #} + {% set rgb = '255,115,20' %} + {% set anim = 'temp-warm-pulse' %} + {% set glow_anim = 'temp-warm-glow' %} + {% set halo_anim = 'temp-warm-halo' %} + {% set duration = 2.4 %} + {% set intensity = 0.8 %} + + {% else %} + {# RED #} + {% set rgb = '255,40,40' %} + {% set anim = 'temp-hot-shimmer' %} + {% set glow_anim = 'temp-hot-glow' %} + {% set halo_anim = 'temp-hot-halo' %} + {% set duration = 2.0 %} + {% set intensity = 1.0 %} + {% endif %} + + {# Apply variables #} + --temp-rgb: {{ rgb }}; + --temp-intensity: {{ intensity }}; + --shape-animation: {{ anim }} {{ duration }}s ease-in-out infinite; + --temp-glow-animation: {{ glow_anim }} {{ (duration * 0.9) | round(2) }}s ease-in-out infinite; + --temp-halo-animation: {{ halo_anim }} {{ (duration * 1.15) | round(2) }}s ease-in-out infinite; + + opacity: 1; + + /* Icon color follows the temperature */ + --icon-color: rgba({{ rgb }}, 1); + + /* KILL THE THEME BLUE & MAKE SHAPE NEUTRAL */ + background-color: rgba(77, 77, 77,0.1) !important; + box-shadow: none !important; + border: 1px solid rgba(255,255,255,0.06); + + position: relative; + transform-origin: 50% 60%; + animation: var(--shape-animation); + } + + /* Glow layers */ + .shape::before, + .shape::after { + content: ''; + position: absolute; + border-radius: inherit; + pointer-events: none; + } + + .shape::before { + inset: -8px; + animation: var(--temp-glow-animation); + } + + .shape::after { + inset: -22px; + animation: var(--temp-halo-animation); + mix-blend-mode: screen; + } + + /* ========== COLD ========== */ + @keyframes temp-cold-breathe { + 0% { transform: scale(0.96); } + 50% { transform: scale(1.03); } + 100% { transform: scale(0.96); } + } + + @keyframes temp-cold-glow { + 0% { + box-shadow: + 0 0 20px 0 rgba(var(--temp-rgb), 0.6), + 0 0 34px 6 rgba(var(--temp-rgb), 0.55); + } + 50% { + box-shadow: + 0 0 30px 4 rgba(var(--temp-rgb), 0.95), + 0 0 50px 10px rgba(var(--temp-rgb), 0.85); + } + 100% { + box-shadow: + 0 0 20px 0 rgba(var(--temp-rgb), 0.6), + 0 0 34px 6 rgba(var(--temp-rgb), 0.55); + } + } + + @keyframes temp-cold-halo { + 0% { + box-shadow: + 0 0 80px 20px rgba(var(--temp-rgb), 0.35), + 0 -20px 80px -14px rgba(220, 240, 255, 0.55); + } + 50% { + box-shadow: + 0 0 130px 36px rgba(var(--temp-rgb), 0.5), + 0 -34px 100px -8px rgba(240, 250, 255, 0.8); + } + 100% { + box-shadow: + 0 0 80px 20px rgba(var(--temp-rgb), 0.35), + 0 -20px 80px -14px rgba(220, 240, 255, 0.55); + } + } + + /* ========== COOL ========== */ + @keyframes temp-cool-wave { + 0% { transform: translateX(0); } + 25% { transform: translateX(-1px); } + 50% { transform: translateX(1px) translateY(-1px); } + 75% { transform: translateX(-1px); } + 100% { transform: translateX(0); } + } + + @keyframes temp-cool-glow { + 0% { + box-shadow: + 0 0 22px 0 rgba(var(--temp-rgb), 0.6), + 0 0 34px 4 rgba(var(--temp-rgb), 0.7); + } + 50% { + box-shadow: + 0 0 28px 2 rgba(var(--temp-rgb), 0.95), + 0 0 48px 12px rgba(var(--temp-rgb), 0.85); + } + 100% { + box-shadow: + 0 0 22px 0 rgba(var(--temp-rgb), 0.6), + 0 0 34px 4 rgba(var(--temp-rgb), 0.7); + } + } + + @keyframes temp-cool-halo { + 0% { + box-shadow: + 0 0 90px 26px rgba(var(--temp-rgb), 0.35), + 0 18px 80px -12px rgba(0, 220, 255, 0.35); + } + 50% { + box-shadow: + 0 0 140px 42px rgba(var(--temp-rgb), 0.45), + 0 30px 110px -10px rgba(0, 255, 255, 0.5); + } + 100% { + box-shadow: + 0 0 90px 26px rgba(var(--temp-rgb), 0.35), + 0 18px 80px -12px rgba(0, 220, 255, 0.35); + } + } + + /* ========== COMFY ========== */ + @keyframes temp-comfy-breathe { + 0% { transform: scale(0.98); } + 50% { transform: scale(1.05); } + 100% { transform: scale(0.98); } + } + + @keyframes temp-comfy-glow { + 50% { + box-shadow: + 0 0 26px 4 rgba(var(--temp-rgb), 0.9), + 0 0 42px 10px rgba(var(--temp-rgb), 0.85); + } + } + + @keyframes temp-comfy-halo { + 50% { + box-shadow: + 0 0 120px 40px rgba(var(--temp-rgb), 0.45), + 0 26px 80px -10px rgba(180,255,200,0.5); + } + } + + /* ========== WARM ========== */ + @keyframes temp-warm-pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.07); } + 100% { transform: scale(1); } + } + + @keyframes temp-warm-glow { + 50% { + box-shadow: + 0 0 30px 4 rgba(var(--temp-rgb), 0.95), + 0 0 54px 14px rgba(var(--temp-rgb), 0.9); + } + } + + @keyframes temp-warm-halo { + 50% { + box-shadow: + 0 0 140px 48px rgba(var(--temp-rgb), 0.55), + 0 26px 100px -10px rgba(255,210,150,0.5); + } + } + + /* ========== HOT ========== */ + @keyframes temp-hot-shimmer { + 0% { transform: scale(1); filter: blur(0); } + 50% { transform: scale(1.08); filter: blur(0.6px); } + 100% { transform: scale(1); filter: blur(0); } + } + + @keyframes temp-hot-glow { + 50% { + box-shadow: + 0 0 34px 6 rgba(var(--temp-rgb), 1), + 0 0 62px 14px rgba(var(--temp-rgb), 0.95); + } + } + + @keyframes temp-hot-halo { + 50% { + box-shadow: + 0 0 160px 60px rgba(var(--temp-rgb), 0.6), + 0 34px 120px -12px rgba(255,150,100,0.6); + } + } + .: | + mushroom-shape-icon { + --icon-size: 48px; + --icon-color: rgba(var(--hum-rgb),1) !important; + display: flex; + margin: -16px 0 10px -20px !important; + padding-right: 22px; + padding-bottom: 18px; + } + ha-card { + clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 14px)); + + /* FONT SIZE & SPACING SETTINGS */ + --card-primary-font-size: 1.4rem !important; + + /* Increases vertical space between primary and secondary */ + --card-primary-line-height: 1.3 !important; + } + - type: custom:vertical-stack-in-card + cards: + - type: custom:mini-graph-card + entities: + - >- + sensor.xiaomi_cn_blt_3_1ni9qhv584c03_mini_temperature_p_2_1001 + hours_to_show: 24 + line_width: 5 + show: + name: false + icon: false + state: false + labels: false + legend: false + color_thresholds: + - value: 0 + color: blue + - value: 16 + color: lightblue + - value: 18 + color: orange + - value: 21 + color: red + card_mod: + style: | + ha-card { + background: none; + box-shadow: none; + opacity: 50%; + border: none; + width: 250px; + mask-image: radial-gradient( + ellipse at center, + rgba(0,0,0,1) 0%, + rgba(0,0,0,0) 90% + ); + } + card_mod: + style: + .: | + ha-card { + background: none; + box-shadow: none; + border: none; + margin: 8px 12px; + position: absolute; + bottom: -10px; + right: -10px; + } + - type: custom:vertical-stack-in-card + cards: + - type: custom:mushroom-entity-card + entity: >- + sensor.miaomiaoc_cn_blt_3_1ndt1bnq04c04_t1_relative_humidity_p_2_2 + tap_action: + action: more-info + icon: mdi:water-percent + name: 湿度 + primary_info: state + secondary_info: name + card_mod: + style: + mushroom-shape-icon$: > + .shape { + {# ========== CONFIG ========== #} + {# == Updated: entity auto generated == #} + {% set hum = states(config.entity) | float(0) %} + + {# DEFAULTS #} + {% set rgb = '120,210,255' %} + {% set anim = 'hum-good-breathe' %} + {% set glow_anim = 'hum-good-glow' %} + {% set halo_anim = 'hum-good-halo' %} + {% set duration = 3.2 %} + + {# RANGES + < 40 -> BAD (dark blue) + 40-60 -> GOOD (light blue) + > 60 -> MIDDLE / humid (medium blue) + #} + + {% if hum < 40 %} + {# BAD - dry - dark blue #} + {% set rgb = '0,80,200' %} + {% set anim = 'hum-bad-pulse' %} + {% set glow_anim = 'hum-bad-glow' %} + {% set halo_anim = 'hum-bad-halo' %} + {% set duration = 2.8 %} + {% elif hum <= 60 %} + {# GOOD - light blue #} + {% set rgb = '120,210,255' %} + {% set anim = 'hum-good-breathe' %} + {% set glow_anim = 'hum-good-glow' %} + {% set halo_anim = 'hum-good-halo' %} + {% set duration = 3.4 %} + {% else %} + {# MIDDLE / humid - medium blue #} + {% set rgb = '40,140,255' %} + {% set anim = 'hum-mid-wave' %} + {% set glow_anim = 'hum-mid-glow' %} + {% set halo_anim = 'hum-mid-halo' %} + {% set duration = 3.0 %} + {% endif %} + + --hum-rgb: {{ rgb }}; + --shape-animation: {{ anim }} {{ duration }}s ease-in-out infinite; + --hum-glow-animation: {{ glow_anim }} {{ (duration * 0.9) | round(2) }}s ease-in-out infinite; + --hum-halo-animation: {{ halo_anim }} {{ (duration * 1.1) | round(2) }}s ease-in-out infinite; + + /* Icon color follows humidity color */ + --icon-color: rgba({{ rgb }}, 1); + + /* Neutral pill so theme blue does not leak through */ + background-color: rgba(77,77,77,0.2) !important; + box-shadow: none !important; + border: 1px solid rgba(255,255,255,0.06); + + opacity: 1; + position: relative; + transform-origin: 50% 60%; + animation: var(--shape-animation); + } + + + /* Glow layers */ + + .shape::before, + + .shape::after { + content: ''; + position: absolute; + border-radius: inherit; + pointer-events: none; + } + + + .shape::before { + inset: -8px; + animation: var(--hum-glow-animation); + } + + + .shape::after { + inset: -22px; + animation: var(--hum-halo-animation); + mix-blend-mode: screen; + } + + + /* ========== GOOD - light blue ========== */ + + @keyframes hum-good-breathe { + 0% { transform: scale(0.98); } + 50% { transform: scale(1.04); } + 100% { transform: scale(0.98); } + } + + + @keyframes hum-good-glow { + 0% { + box-shadow: + 0 0 18px 0 rgba(var(--hum-rgb), 0.55), + 0 0 30px 4 rgba(var(--hum-rgb), 0.6); + } + 50% { + box-shadow: + 0 0 24px 4 rgba(var(--hum-rgb), 0.9), + 0 0 44px 10px rgba(var(--hum-rgb), 0.85); + } + 100% { + box-shadow: + 0 0 18px 0 rgba(var(--hum-rgb), 0.55), + 0 0 30px 4 rgba(var(--hum-rgb), 0.6); + } + } + + + @keyframes hum-good-halo { + 0% { + box-shadow: + 0 0 80px 24px rgba(var(--hum-rgb), 0.35), + 0 18px 70px -12px rgba(180,230,255,0.3); + } + 50% { + box-shadow: + 0 0 120px 40px rgba(var(--hum-rgb), 0.45), + 0 26px 90px -10px rgba(200,240,255,0.45); + } + 100% { + box-shadow: + 0 0 80px 24px rgba(var(--hum-rgb), 0.35), + 0 18px 70px -12px rgba(180,230,255,0.3); + } + } + + + /* ========== MIDDLE / humid - medium blue ========== + */ + + @keyframes hum-mid-wave { + 0% { transform: translateX(0); } + 25% { transform: translateX(-1px); } + 50% { transform: translateX(1px) translateY(-1px); } + 75% { transform: translateX(-1px); } + 100% { transform: translateX(0); } + } + + + @keyframes hum-mid-glow { + 0% { + box-shadow: + 0 0 20px 0 rgba(var(--hum-rgb), 0.6), + 0 0 32px 4 rgba(var(--hum-rgb), 0.7); + } + 50% { + box-shadow: + 0 0 28px 3 rgba(var(--hum-rgb), 0.95), + 0 0 48px 10px rgba(var(--hum-rgb), 0.85); + } + 100% { + box-shadow: + 0 0 20px 0 rgba(var(--hum-rgb), 0.6), + 0 0 32px 4 rgba(var(--hum-rgb), 0.7); + } + } + + + @keyframes hum-mid-halo { + 0% { + box-shadow: + 0 0 90px 26px rgba(var(--hum-rgb), 0.4), + 0 18px 80px -12px rgba(80,190,255,0.35); + } + 50% { + box-shadow: + 0 0 135px 42px rgba(var(--hum-rgb), 0.5), + 0 28px 105px -10px rgba(100,210,255,0.5); + } + 100% { + box-shadow: + 0 0 90px 26px rgba(var(--hum-rgb), 0.4), + 0 18px 80px -12px rgba(80,190,255,0.35); + } + } + + + /* ========== BAD - dry - dark blue ========== */ + + @keyframes hum-bad-pulse { + 0% { transform: scale(0.97); } + 40% { transform: scale(1.03); } + 100% { transform: scale(0.97); } + } + + + @keyframes hum-bad-glow { + 0% { + box-shadow: + 0 0 18px 0 rgba(var(--hum-rgb), 0.6), + 0 0 28px 4 rgba(var(--hum-rgb), 0.7); + } + 50% { + box-shadow: + 0 0 26px 4 rgba(var(--hum-rgb), 0.95), + 0 0 44px 12px rgba(var(--hum-rgb), 0.9); + } + 100% { + box-shadow: + 0 0 18px 0 rgba(var(--hum-rgb), 0.6), + 0 0 28px 4 rgba(var(--hum-rgb), 0.7); + } + } + + + @keyframes hum-bad-halo { + 0% { + box-shadow: + 0 0 80px 22px rgba(var(--hum-rgb), 0.45), + 0 18px 75px -10px rgba(0,70,160,0.5); + } + 50% { + box-shadow: + 0 0 130px 40px rgba(var(--hum-rgb), 0.6), + 0 26px 100px -8px rgba(0,90,190,0.65); + } + 100% { + box-shadow: + 0 0 80px 22px rgba(var(--hum-rgb), 0.45), + 0 18px 75px -10px rgba(0,70,160,0.5); + } + } + .: | + mushroom-shape-icon { + --icon-size: 48px; + --icon-color: rgba(var(--hum-rgb),1) !important; + display: flex; + margin: -16px 0 10px -20px !important; + padding-right: 22px; + padding-bottom: 18px; + } + ha-card { + clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 14px)); + + /* FONT SIZE & SPACING SETTINGS */ + --card-primary-font-size: 1.4rem !important; + + /* Increases vertical space between primary and secondary */ + --card-primary-line-height: 1.3 !important; + } + - type: custom:vertical-stack-in-card + cards: + - type: custom:mini-graph-card + entities: + - >- + sensor.xiaomi_cn_blt_3_1ni9qhv584c03_mini_relative_humidity_p_2_1002 + line_color: lightblue + line_width: 5 + hours_to_show: 24 + show: + name: false + icon: false + state: false + labels: false + legend: false + card_mod: + style: | + ha-card { + background: none; + box-shadow: none; + opacity: 50%; + border: none; + width: 250px; + mask-image: radial-gradient( + ellipse at center, + rgba(0,0,0,1) 0%, + rgba(0,0,0,0) 90% + ); + } + card_mod: + style: + .: | + ha-card { + background: none; + box-shadow: none; + border: none; + margin: 8px 12px; + position: absolute; + bottom: -10px; + right: -10px; + } + - type: custom:mushroom-entity-card + entity: sensor.xiaomi_cn_2005161482_va3_air_quality_p_3_3 + tap_action: + action: more-info + icon: mdi:air-filter + name: 卧室 + primary_info: state + secondary_info: name + fill_container: false + card_mod: + style: + mushroom-shape-icon$: | + .shape { + {# ========== CONFIG ========== #} + {% set voc = states('sensor.livingroom_air_quality_voc') | float(0) %} + + {# DEFAULTS (will be overridden by ranges) #} + {% set rgb = '40,200,120' %} + {% set anim = 'voc-clean-breathe' %} + {% set glow_anim = 'voc-clean-glow' %} + {% set halo_anim = 'voc-clean-halo' %} + {% set duration = 4.0 %} + {% set intensity = 0.5 %} + + {# RANGES / COLORS #} + {# You can change numbers below if needed #} + + {% if voc < 100 %} + {# GREEN #} + {% set rgb = '40,200,120' %} + {% set anim = 'voc-clean-breathe' %} + {% set glow_anim = 'voc-clean-glow' %} + {% set halo_anim = 'voc-clean-halo' %} + {% set duration = 4.4 %} + {% set intensity = 0.45 %} + + {% elif voc < 200 %} + {# YELLOW-GREEN #} + {% set rgb = '140,220,80' %} + {% set anim = 'voc-good-wave' %} + {% set glow_anim = 'voc-good-glow' %} + {% set halo_anim = 'voc-good-halo' %} + {% set duration = 3.6 %} + {% set intensity = 0.55 %} + + {% elif voc < 300 %} + {# YELLOW #} + {% set rgb = '255,210,40' %} + {% set anim = 'voc-fair-breathe' %} + {% set glow_anim = 'voc-fair-glow' %} + {% set halo_anim = 'voc-fair-halo' %} + {% set duration = 3.0 %} + {% set intensity = 0.7 %} + + {% elif voc < 400 %} + {# ORANGE #} + {% set rgb = '255,140,40' %} + {% set anim = 'voc-poor-pulse' %} + {% set glow_anim = 'voc-poor-glow' %} + {% set halo_anim = 'voc-poor-halo' %} + {% set duration = 2.4 %} + {% set intensity = 0.9 %} + + {% else %} + {# RED #} + {% set rgb = '255,50,50' %} + {% set anim = 'voc-bad-shimmer' %} + {% set glow_anim = 'voc-bad-glow' %} + {% set halo_anim = 'voc-bad-halo' %} + {% set duration = 2.0 %} + {% set intensity = 1.0 %} + {% endif %} + + {# Apply variables #} + --voc-rgb: {{ rgb }}; + --voc-intensity: {{ intensity }}; + --shape-animation: {{ anim }} {{ duration }}s ease-in-out infinite; + --voc-glow-animation: {{ glow_anim }} {{ (duration * 0.9) | round(2) }}s ease-in-out infinite; + --voc-halo-animation: {{ halo_anim }} {{ (duration * 1.15) | round(2) }}s ease-in-out infinite; + + opacity: 1; + + /* Icon color follows VOC level */ + --icon-color: rgba({{ rgb }}, 1); + + /* Shape neutral base */ + background-color: rgba(77, 77, 77,0.1) !important; + box-shadow: none !important; + border: 1px solid rgba(255,255,255,0.06); + + position: relative; + transform-origin: 50% 60%; + animation: var(--shape-animation); + } + + /* Glow layers */ + .shape::before, + .shape::after { + content: ''; + position: absolute; + border-radius: inherit; + pointer-events: none; + } + + .shape::before { + inset: -8px; + animation: var(--voc-glow-animation); + } + + .shape::after { + inset: -22px; + animation: var(--voc-halo-animation); + mix-blend-mode: screen; + } + + /* ========== CLEAN ========== */ + @keyframes voc-clean-breathe { + 0% { transform: scale(0.96); } + 50% { transform: scale(1.03); } + 100% { transform: scale(0.96); } + } + + @keyframes voc-clean-glow { + 0% { + box-shadow: + 0 0 20px 0 rgba(var(--voc-rgb), 0.55), + 0 0 34px 6 rgba(var(--voc-rgb), 0.5); + } + 50% { + box-shadow: + 0 0 30px 4 rgba(var(--voc-rgb), 0.9), + 0 0 50px 10px rgba(var(--voc-rgb), 0.85); + } + 100% { + box-shadow: + 0 0 20px 0 rgba(var(--voc-rgb), 0.55), + 0 0 34px 6 rgba(var(--voc-rgb), 0.5); + } + } + + @keyframes voc-clean-halo { + 0% { + box-shadow: + 0 0 80px 20px rgba(var(--voc-rgb), 0.28), + 0 -20px 80px -14px rgba(200, 255, 230, 0.45); + } + 50% { + box-shadow: + 0 0 130px 36px rgba(var(--voc-rgb), 0.42), + 0 -34px 100px -8px rgba(220, 255, 240, 0.65); + } + 100% { + box-shadow: + 0 0 80px 20px rgba(var(--voc-rgb), 0.28), + 0 -20px 80px -14px rgba(200, 255, 230, 0.45); + } + } + + /* ========== GOOD ========== */ + @keyframes voc-good-wave { + 0% { transform: translateX(0); } + 25% { transform: translateX(-1px); } + 50% { transform: translateX(1px) translateY(-1px); } + 75% { transform: translateX(-1px); } + 100% { transform: translateX(0); } + } + + @keyframes voc-good-glow { + 0% { + box-shadow: + 0 0 22px 0 rgba(var(--voc-rgb), 0.55), + 0 0 34px 4 rgba(var(--voc-rgb), 0.6); + } + 50% { + box-shadow: + 0 0 28px 2 rgba(var(--voc-rgb), 0.9), + 0 0 48px 12px rgba(var(--voc-rgb), 0.8); + } + 100% { + box-shadow: + 0 0 22px 0 rgba(var(--voc-rgb), 0.55), + 0 0 34px 4 rgba(var(--voc-rgb), 0.6); + } + } + + @keyframes voc-good-halo { + 0% { + box-shadow: + 0 0 90px 26px rgba(var(--voc-rgb), 0.3), + 0 18px 80px -12px rgba(140, 255, 120, 0.25); + } + 50% { + box-shadow: + 0 0 140px 42px rgba(var(--voc-rgb), 0.4), + 0 30px 110px -10px rgba(160, 255, 140, 0.4); + } + 100% { + box-shadow: + 0 0 90px 26px rgba(var(--voc-rgb), 0.3), + 0 18px 80px -12px rgba(140, 255, 120, 0.25); + } + } + + /* ========== FAIR ========== */ + @keyframes voc-fair-breathe { + 0% { transform: scale(0.98); } + 50% { transform: scale(1.05); } + 100% { transform: scale(0.98); } + } + + @keyframes voc-fair-glow { + 50% { + box-shadow: + 0 0 26px 4 rgba(var(--voc-rgb), 0.85), + 0 0 42px 10px rgba(var(--voc-rgb), 0.8); + } + } + + @keyframes voc-fair-halo { + 50% { + box-shadow: + 0 0 120px 40px rgba(var(--voc-rgb), 0.42), + 0 26px 80px -10px rgba(255, 245, 180, 0.5); + } + } + + /* ========== POOR ========== */ + @keyframes voc-poor-pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.07); } + 100% { transform: scale(1); } + } + + @keyframes voc-poor-glow { + 50% { + box-shadow: + 0 0 30px 4 rgba(var(--voc-rgb), 0.95), + 0 0 54px 14px rgba(var(--voc-rgb), 0.9); + } + } + + @keyframes voc-poor-halo { + 50% { + box-shadow: + 0 0 140px 48px rgba(var(--voc-rgb), 0.52), + 0 26px 100px -10px rgba(255, 210, 150, 0.5); + } + } + + /* ========== BAD ========== */ + @keyframes voc-bad-shimmer { + 0% { transform: scale(1); filter: blur(0); } + 50% { transform: scale(1.08); filter: blur(0.6px); } + 100% { transform: scale(1); filter: blur(0); } + } + + @keyframes voc-bad-glow { + 50% { + box-shadow: + 0 0 34px 6 rgba(var(--voc-rgb), 1), + 0 0 62px 14px rgba(var(--voc-rgb), 0.95); + } + } + + @keyframes voc-bad-halo { + 50% { + box-shadow: + 0 0 160px 60px rgba(var(--voc-rgb), 0.6), + 0 34px 120px -12px rgba(255, 140, 120, 0.6); + } + } + .: | + mushroom-shape-icon { + --icon-size: 48px; + --icon-color: rgba(var(--voc-rgb),1) !important; + display: flex; + margin: -10px 0 10px -20px !important; + padding-right: 22px; + padding-bottom: 10px; + } + ha-card { + clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 14px)); + + --card-primary-font-size: 1.5rem !important; + --card-primary-line-height: 1.3 !important; + } + - type: custom:mushroom-entity-card + entity: sensor.water_damage_count + tap_action: + action: none + icon: mdi:water + name: 水浸设备数 + card_mod: + style: + mushroom-shape-icon$: | + .shape { + {# ========== USER CONFIG ========== #} + {# true = number mode, false = state mode #} + {% set use_number = true %} + + {# STATE MODE SETTINGS #} + {% set state_entity = 'switch.plug_6_local' %} + {% set active_value = 'on' %} + + {# OPTIONAL: NUMBER MODE SETTINGS #} + {% set number_entity = 'sensor.water_damage_count' %} + + {# '>' '<' '=' '>=' '<=' #} + {% set number_operator = '>' %} + + {% set threshold = 0.0 %} + {# ========== END USER CONFIG ====== #} + + + {# ======== TRIGGER DECISION LOGIC ======== #} + {% if use_number %} + {% set num = states(number_entity) | float(0) %} + {% if number_operator == '>' %} + {% set trigger_active = (num > threshold) %} + {% elif number_operator == '<' %} + {% set trigger_active = (num < threshold) %} + {% elif number_operator == '=' %} + {% set trigger_active = (num == threshold) %} + {% elif number_operator == '>=' %} + {% set trigger_active = (num >= threshold) %} + {% elif number_operator == '<=' %} + {% set trigger_active = (num <= threshold) %} + {% else %} + {% set trigger_active = false %} + {% endif %} + {% else %} + {# default: simple equality state trigger #} + {% set trigger_active = (states(state_entity) == active_value) %} + {% endif %} + {# =========== END TRIGGER LOGIC =========== #} + + + {% if trigger_active %} + --shape-animation: doorbell-ring 0.9s ease-out infinite; + opacity: 1; + {% else %} + --shape-animation: none; + opacity: 0.8; + {% endif %} + + transform-origin: 50% 50%; + } + + @keyframes doorbell-ring { + 0% { + transform: scale(1.05); + box-shadow: + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 1), + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.8), + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.4); + } + 30% { + transform: scale(0.95); + box-shadow: + 0 0 0 4px rgba(var(--rgb-{{ config.icon_color }}), 0.9), + 0 0 0 10px rgba(var(--rgb-{{ config.icon_color }}), 0.7), + 0 0 0 18px rgba(var(--rgb-{{ config.icon_color }}), 0.4); + } + 60% { + transform: scale(1.02); + box-shadow: + 0 0 0 10px rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 0 0 0 18px rgba(var(--rgb-{{ config.icon_color }}), 0.2), + 0 0 0 26px rgba(var(--rgb-{{ config.icon_color }}), 0.0); + } + 100% { + transform: scale(1); + box-shadow: + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.0); + } + } + + @keyframes doorbell-idle { + 0% { transform: scale(1); } + 50% { transform: scale(1.03); } + 100% { transform: scale(1); } + } + .: | + mushroom-shape-icon { + --icon-size: 65px; + display: flex; + margin: -18px 0 10px -20px !important; + padding-right: 10px; + } + ha-card { + clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 12px)); + } + columns: 2 + - type: grid + column_span: 4 + cards: + - type: heading + heading: 快捷控制 + icon: mdi:lightning-bolt + heading_style: title + - type: custom:mushroom-template-card + entity: light.quan_wu_zhao_ming + primary: 全屋灯光 + secondary: > + {% set n = states('sensor.lights_of_count') | int(0) %} {% if n > + 0 %}{{ n }} 盏开启{% else %}全部关闭{% endif %} + icon: mdi:lightbulb-group + icon_color: > + {% if states('sensor.lights_of_count') | int(0) > 0 %}amber{% else + %}grey{% endif %} + tap_action: + action: more-info + hold_action: + action: toggle + grid_options: + columns: 4 + rows: 1 + - type: custom:mushroom-template-card + entity: climate.qdhkl_cn_2113954983_b19c + primary: 空调 + secondary: | + {% if is_state(config.entity,'off') %} + 已关闭 + {% else %} + {{ state_attr(config.entity,'temperature') }}°C + {% endif %} + icon: mdi:snowflake + icon_color: > + {% if is_state(config.entity,'off') %}grey{% else %}blue{% endif + %} + tap_action: + action: more-info + grid_options: + columns: 4 + rows: 1 + - type: custom:mushroom-template-card + entity: fan.xiaomi_cn_2005161482_va3_s_2_air_purifier + primary: 空气净化器 + secondary: | + {% if is_state(config.entity,'on') %}运行中{% else %}已关闭{% endif %} + icon: mdi:air-purifier + icon_color: | + {% if is_state(config.entity,'on') %}cyan{% else %}grey{% endif %} + tap_action: + action: toggle + hold_action: + action: more-info + grid_options: + columns: 4 + rows: 1 + - type: custom:mushroom-template-card + entity: fan.xiaomi_cn_2196552583_p90_s_2_fan + primary: 风扇 + secondary: | + {% if is_state(config.entity,'on') %}运行中{% else %}已关闭{% endif %} + icon: mdi:fan + icon_color: > + {% if is_state(config.entity,'on') %}light-blue{% else %}grey{% + endif %} + tap_action: + action: toggle + hold_action: + action: more-info + grid_options: + columns: 4 + rows: 1 + - type: custom:mushroom-template-card + entity: cover.panpan_cn_876603548_y18_s_2_airer + primary: 晾衣架 + secondary: | + {{ states(config.entity) }} + icon: mdi:hanger + icon_color: teal + tap_action: + action: more-info + grid_options: + columns: 4 + rows: 1 + - type: custom:mushroom-template-card + entity: switch.cuco_cn_2006579195_v3_on_p_2_1 + primary: NAS + secondary: > + {{ states('sensor.cuco_cn_2006579195_v3_electric_power_p_11_2') }} + W + icon: mdi:nas + icon_color: | + {% if is_state(config.entity,'on') %}red{% else %}grey{% endif %} + tap_action: + action: navigate + navigation_path: home-server + grid_options: + columns: 4 + rows: 1 + - type: grid + column_span: 4 + cards: + - type: heading + heading: 房间灯光 + icon: mdi:floor-plan + heading_style: title + - type: custom:mushroom-light-card + entity: light.pmfbj_cn_1187112198_xsx340_s_2 + name: 客厅 + icon: mdi:sofa + use_light_color: true + show_brightness_control: true + show_color_temp_control: true + grid_options: + columns: 4 + rows: 2 + - type: custom:mushroom-light-card + entity: light.shhf_cn_1129134246_sflt11_s_2_light + name: 餐桌 + icon: mdi:table-chair + use_light_color: true + show_brightness_control: true + show_color_temp_control: true + grid_options: + columns: 4 + rows: 2 + - type: custom:mushroom-light-card + entity: light.pmfbj_cn_1186735998_xsx340_s_2 + name: 主卧 + icon: mdi:bed-king + use_light_color: true + show_brightness_control: true + show_color_temp_control: true + grid_options: + columns: 4 + rows: 2 + - type: custom:mushroom-light-card + entity: light.pmfbj_cn_1186772227_xsx340_s_2 + name: 次卧 + icon: mdi:bed-single + use_light_color: true + show_brightness_control: true + show_color_temp_control: true + grid_options: + columns: 4 + rows: 2 + - type: custom:mushroom-light-card + entity: light.pmfbj_cn_1186744390_xsx340_s_2 + name: 书房 + icon: mdi:desk + use_light_color: true + show_brightness_control: true + show_color_temp_control: true + grid_options: + columns: 4 + rows: 2 + - type: custom:mushroom-light-card + entity: light.opple_cn_2005385416_moat_s_2_light + name: 阳台 + icon: mdi:balcony + use_light_color: true + show_brightness_control: true + show_color_temp_control: true + grid_options: + columns: 4 + rows: 2 + header: + layout: responsive + badges_position: bottom + badges_wrap: wrap + background: + opacity: 80 + alignment: center + size: cover + repeat: repeat + attachment: fixed + cards: [] + - title: 首页 + path: home + icon: mdi:home + type: sections + max_columns: 4 + sections: + - type: grid + cards: + - type: custom:button-card + show_icon: false + show_name: false + show_state: false + grid_options: + columns: 12 + rows: 2 + custom_fields: + greeting: | + [[[ + const h = new Date().getHours(); + if (h < 6) return "夜深了"; + if (h < 11) return "早上好"; + if (h < 14) return "中午好"; + if (h < 18) return "下午好"; + return "晚上好"; + ]]] + clock: | + [[[ + return new Date().toLocaleTimeString( + 'zh-CN', + { + hour: '2-digit', + minute: '2-digit', + hour12: false + } + ); + ]]] + date: | + [[[ + return new Date().toLocaleDateString( + 'zh-CN', + { + month: 'long', + day: 'numeric', + weekday: 'long' + } + ); + ]]] + subtitle: | + 家里一切正常 + styles: + card: + - height: 130px + - padding: 20px 24px + - border-radius: 24px + - border: none + - background: | + linear-gradient( + 135deg, + rgba(32,35,42,0.95), + rgba(20,22,27,0.95) + ) + - box-shadow: none + grid: + - grid-template-areas: | + "greeting clock" + "subtitle date" + - grid-template-columns: 1fr auto + - grid-template-rows: 1fr 1fr + custom_fields: + greeting: + - justify-self: start + - align-self: end + - font-size: 30px + - font-weight: 700 + - color: white + subtitle: + - justify-self: start + - align-self: start + - font-size: 14px + - color: rgba(255,255,255,0.55) + clock: + - justify-self: end + - align-self: end + - font-size: 32px + - font-weight: 600 + - color: white + date: + - justify-self: end + - align-self: start + - font-size: 14px + - color: rgba(255,255,255,0.55) + - type: grid + cards: + - type: custom:button-card + entity: weather.home + show_icon: false + show_name: false + show_state: false + grid_options: + columns: 6 + rows: 4 + custom_fields: + weather_icon: | + [[[ + const s = entity?.state; + + if (s === "sunny") + return "☀️"; + + if (s === "clear-night") + return "🌙"; + + if (s === "cloudy") + return "☁️"; + + if (s === "partlycloudy") + return "⛅"; + + if (["rainy","pouring"].includes(s)) + return "🌧️"; + + if (s === "lightning") + return "⛈️"; + + return "🌤️"; + ]]] + temp: | + [[[ + const temp = + states['sensor.outdoor_temperature']?.state ?? '--'; + + return `${temp}°`; + ]]] + condition: | + [[[ + const map = { + sunny: "晴", + cloudy: "多云", + partlycloudy: "局部多云", + rainy: "下雨", + pouring: "大雨", + "clear-night": "晴朗" + }; + + return map[entity?.state] ?? entity?.state ?? "--"; + ]]] + forecast: | + [[[ + return "今天 · 舒适"; + ]]] + humidity: | + [[[ + const hum = + states['sensor.outdoor_humidity']?.state ?? '--'; + + return `湿度 ${hum}%`; + ]]] + styles: + card: + - height: 210px + - border-radius: 24px + - border: none + - padding: 22px + - overflow: hidden + - background: | + linear-gradient( + 145deg, + #334b68 0%, + #23334a 55%, + #172131 100% + ) + - box-shadow: none + grid: + - grid-template-areas: | + "weather_icon temp" + "weather_icon condition" + "forecast humidity" + - grid-template-columns: 1fr 1.3fr + - grid-template-rows: 1fr auto auto + custom_fields: + weather_icon: + - font-size: 74px + - align-self: center + - justify-self: center + - animation: weatherFloat 5s ease-in-out infinite + temp: + - font-size: 46px + - font-weight: 700 + - justify-self: start + - align-self: end + - color: white + condition: + - font-size: 17px + - justify-self: start + - color: rgba(255,255,255,0.7) + forecast: + - font-size: 13px + - justify-self: start + - color: rgba(255,255,255,0.6) + humidity: + - font-size: 13px + - justify-self: end + - color: rgba(255,255,255,0.6) + extra_styles: | + @keyframes weatherFloat { + 0% { + transform: translateY(0px) scale(1); + } + + 50% { + transform: translateY(-6px) scale(1.03); + } + + 100% { + transform: translateY(0px) scale(1); + } + } + - type: custom:button-card + show_icon: false + show_name: false + grid_options: + columns: 6 + rows: 4 + custom_fields: + title: 家庭状态 + temperature: | + [[[ + return ` +