diff --git a/main.yaml b/main.yaml index 187a038..57bfa67 100644 --- a/main.yaml +++ b/main.yaml @@ -3503,6 +3503,663 @@ views: cards: - type: heading heading: 新建部件 + - type: custom:mushroom-entity-card + entity: fan.xiaomi_cn_2196552583_p90_s_2_fan + tap_action: + action: toggle + icon: mdi:fan + icon_color: cyan + name: Fan + 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 = 'fan.xiaomi_cn_2196552583_p90_s_2_fan' %} + {% 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 sensor_entity = state_entity %} + {% if states(sensor_entity) == active_value %} + {% set trigger_active = true %} + {% else %} + {% set trigger_active = false %} + {% endif %} + {% endif %} + {# ------ END TRIGGER DECISION LOGIC ------ #} + + {# Fan speed still based on THIS card's entity percentage #} + {% set speed = state_attr(config.entity, 'percentage') | int(0) %} + + {% if trigger_active %} + {% if speed == 0 and is_state(config.entity, 'off') %} + --shape-animation: fan-ultra-idle 3s ease-in-out infinite; + {% else %} + {% set dur = 1.2 - (speed / 120) %} + --shape-animation: fan-ultra-spin {{ [0.25, dur] | max | round(2) }}s linear infinite; + {% endif %} + --fan-warp-animation: fan-warp 1.4s ease-in-out infinite; + --fan-stream-animation: fan-stream 1.6s ease-in-out infinite; + opacity: 1; + {% else %} + --shape-animation: none; + --fan-warp-animation: none; + --fan-stream-animation: none; + opacity: 0.7; + {% endif %} + + transform-origin: 50% 50%; + position: relative; + } + + .shape::before, + .shape::after { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + } + + .shape::before { + animation: var(--fan-warp-animation); + } + + .shape::after { + mix-blend-mode: screen; + animation: var(--fan-stream-animation); + } + + @keyframes fan-ultra-spin { + 0% { + transform: rotate(0deg) scale(1); + filter: drop-shadow(0 0 3px rgba(var(--rgb-{{ config.icon_color }}), 0.8)); + } + 20% { transform: rotate(72deg) scale(1.03); } + 40% { transform: rotate(144deg) scale(1.05); } + 60% { transform: rotate(216deg) scale(1.03); } + 80% { transform: rotate(288deg) scale(1.02); } + 100% { + transform: rotate(360deg) scale(1); + filter: drop-shadow(0 0 5px rgba(var(--rgb-{{ config.icon_color }}), 1)); + } + } + + @keyframes fan-ultra-idle { + 0% { transform: rotate(-6deg); } + 50% { transform: rotate(6deg); } + 100% { transform: rotate(-6deg); } + } + + @keyframes fan-warp { + 0% { + box-shadow: + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.7), + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.25); + } + 40% { + box-shadow: + 0 0 0 8px rgba(var(--rgb-{{ config.icon_color }}), 0.4), + 0 0 24px 6px rgba(var(--rgb-{{ config.icon_color }}), 0.25); + } + 100% { + box-shadow: + 0 0 0 18px rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 0 0 40px 12px rgba(var(--rgb-{{ config.icon_color }}), 0.0); + } + } + + @keyframes fan-stream { + 0% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0); + } + 50% { + box-shadow: + 24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.6), + -24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.4); + } + 100% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 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)); + } + - type: custom:mushroom-entity-card + entity: fan.shhf_cn_1129134246_sflt11_s_11_fan + tap_action: + action: toggle + icon: mdi:fan + icon_color: cyan + name: Fan + 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 = 'fan.shhf_cn_1129134246_sflt11_s_11_fan' %} + {% 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 sensor_entity = state_entity %} + {% if states(sensor_entity) == active_value %} + {% set trigger_active = true %} + {% else %} + {% set trigger_active = false %} + {% endif %} + {% endif %} + {# ------ END TRIGGER DECISION LOGIC ------ #} + + {# Fan speed still based on THIS card's entity percentage #} + {% set speed = state_attr(config.entity, 'percentage') | int(0) %} + + {% if trigger_active %} + {% if speed == 0 and is_state(config.entity, 'off') %} + --shape-animation: fan-ultra-idle 3s ease-in-out infinite; + {% else %} + {% set dur = 1.2 - (speed / 120) %} + --shape-animation: fan-ultra-spin {{ [0.25, dur] | max | round(2) }}s linear infinite; + {% endif %} + --fan-warp-animation: fan-warp 1.4s ease-in-out infinite; + --fan-stream-animation: fan-stream 1.6s ease-in-out infinite; + opacity: 1; + {% else %} + --shape-animation: none; + --fan-warp-animation: none; + --fan-stream-animation: none; + opacity: 0.7; + {% endif %} + + transform-origin: 50% 50%; + position: relative; + } + + .shape::before, + .shape::after { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + } + + .shape::before { + animation: var(--fan-warp-animation); + } + + .shape::after { + mix-blend-mode: screen; + animation: var(--fan-stream-animation); + } + + @keyframes fan-ultra-spin { + 0% { + transform: rotate(0deg) scale(1); + filter: drop-shadow(0 0 3px rgba(var(--rgb-{{ config.icon_color }}), 0.8)); + } + 20% { transform: rotate(72deg) scale(1.03); } + 40% { transform: rotate(144deg) scale(1.05); } + 60% { transform: rotate(216deg) scale(1.03); } + 80% { transform: rotate(288deg) scale(1.02); } + 100% { + transform: rotate(360deg) scale(1); + filter: drop-shadow(0 0 5px rgba(var(--rgb-{{ config.icon_color }}), 1)); + } + } + + @keyframes fan-ultra-idle { + 0% { transform: rotate(-6deg); } + 50% { transform: rotate(6deg); } + 100% { transform: rotate(-6deg); } + } + + @keyframes fan-warp { + 0% { + box-shadow: + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.7), + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.25); + } + 40% { + box-shadow: + 0 0 0 8px rgba(var(--rgb-{{ config.icon_color }}), 0.4), + 0 0 24px 6px rgba(var(--rgb-{{ config.icon_color }}), 0.25); + } + 100% { + box-shadow: + 0 0 0 18px rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 0 0 40px 12px rgba(var(--rgb-{{ config.icon_color }}), 0.0); + } + } + + @keyframes fan-stream { + 0% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0); + } + 50% { + box-shadow: + 24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.6), + -24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.4); + } + 100% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 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)); + } + - type: custom:vertical-stack-in-card + cards: + - type: custom:mushroom-entity-card + entity: fan.shhf_cn_1129134246_sflt11_s_11_fan + tap_action: + action: toggle + icon: mdi:fan + icon_color: cyan + name: 餐桌扇 + 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 = 'fan.shhf_cn_1129134246_sflt11_s_11_fan' %} + {% 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 sensor_entity = state_entity %} + {% if states(sensor_entity) == active_value %} + {% set trigger_active = true %} + {% else %} + {% set trigger_active = false %} + {% endif %} + {% endif %} + {# ------ END TRIGGER DECISION LOGIC ------ #} + + {# Fan speed still based on THIS card's entity percentage #} + {% set speed = state_attr(config.entity, 'percentage') | int(0) %} + + {% if trigger_active %} + {% if speed == 0 and is_state(config.entity, 'off') %} + --shape-animation: fan-ultra-idle 3s ease-in-out infinite; + {% else %} + {% set dur = 1.2 - (speed / 120) %} + --shape-animation: fan-ultra-spin {{ [0.25, dur] | max | round(2) }}s linear infinite; + {% endif %} + --fan-warp-animation: fan-warp 1.4s ease-in-out infinite; + --fan-stream-animation: fan-stream 1.6s ease-in-out infinite; + opacity: 1; + {% else %} + --shape-animation: none; + --fan-warp-animation: none; + --fan-stream-animation: none; + opacity: 0.7; + {% endif %} + + transform-origin: 50% 50%; + position: relative; + } + + .shape::before, + .shape::after { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + } + + .shape::before { + animation: var(--fan-warp-animation); + } + + .shape::after { + mix-blend-mode: screen; + animation: var(--fan-stream-animation); + } + + @keyframes fan-ultra-spin { + 0% { + transform: rotate(0deg) scale(1); + filter: drop-shadow(0 0 3px rgba(var(--rgb-{{ config.icon_color }}), 0.8)); + } + 20% { transform: rotate(72deg) scale(1.03); } + 40% { transform: rotate(144deg) scale(1.05); } + 60% { transform: rotate(216deg) scale(1.03); } + 80% { transform: rotate(288deg) scale(1.02); } + 100% { + transform: rotate(360deg) scale(1); + filter: drop-shadow(0 0 5px rgba(var(--rgb-{{ config.icon_color }}), 1)); + } + } + + @keyframes fan-ultra-idle { + 0% { transform: rotate(-6deg); } + 50% { transform: rotate(6deg); } + 100% { transform: rotate(-6deg); } + } + + @keyframes fan-warp { + 0% { + box-shadow: + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.7), + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.25); + } + 40% { + box-shadow: + 0 0 0 8px rgba(var(--rgb-{{ config.icon_color }}), 0.4), + 0 0 24px 6px rgba(var(--rgb-{{ config.icon_color }}), 0.25); + } + 100% { + box-shadow: + 0 0 0 18px rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 0 0 40px 12px rgba(var(--rgb-{{ config.icon_color }}), 0.0); + } + } + + @keyframes fan-stream { + 0% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0); + } + 50% { + box-shadow: + 24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.6), + -24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.4); + } + 100% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 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)); + } + - type: vertical-stack + cards: + - type: custom:bubble-card + card_type: separator + icon: '' + card_layout: large + rows: '0.5' + - type: custom:mushroom-entity-card + entity: fan.xiaomi_cn_2196552583_p90_s_2_fan + tap_action: + action: toggle + icon: mdi:fan + icon_color: cyan + name: 小爱风扇 + 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 = 'fan.xiaomi_cn_2196552583_p90_s_2_fan' %} + {% 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 sensor_entity = state_entity %} + {% if states(sensor_entity) == active_value %} + {% set trigger_active = true %} + {% else %} + {% set trigger_active = false %} + {% endif %} + {% endif %} + {# ------ END TRIGGER DECISION LOGIC ------ #} + + {# Fan speed still based on THIS card's entity percentage #} + {% set speed = state_attr(config.entity, 'percentage') | int(0) %} + + {% if trigger_active %} + {% if speed == 0 and is_state(config.entity, 'off') %} + --shape-animation: fan-ultra-idle 3s ease-in-out infinite; + {% else %} + {% set dur = 1.2 - (speed / 120) %} + --shape-animation: fan-ultra-spin {{ [0.25, dur] | max | round(2) }}s linear infinite; + {% endif %} + --fan-warp-animation: fan-warp 1.4s ease-in-out infinite; + --fan-stream-animation: fan-stream 1.6s ease-in-out infinite; + opacity: 1; + {% else %} + --shape-animation: none; + --fan-warp-animation: none; + --fan-stream-animation: none; + opacity: 0.7; + {% endif %} + + transform-origin: 50% 50%; + position: relative; + } + + .shape::before, + .shape::after { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + } + + .shape::before { + animation: var(--fan-warp-animation); + } + + .shape::after { + mix-blend-mode: screen; + animation: var(--fan-stream-animation); + } + + @keyframes fan-ultra-spin { + 0% { + transform: rotate(0deg) scale(1); + filter: drop-shadow(0 0 3px rgba(var(--rgb-{{ config.icon_color }}), 0.8)); + } + 20% { transform: rotate(72deg) scale(1.03); } + 40% { transform: rotate(144deg) scale(1.05); } + 60% { transform: rotate(216deg) scale(1.03); } + 80% { transform: rotate(288deg) scale(1.02); } + 100% { + transform: rotate(360deg) scale(1); + filter: drop-shadow(0 0 5px rgba(var(--rgb-{{ config.icon_color }}), 1)); + } + } + + @keyframes fan-ultra-idle { + 0% { transform: rotate(-6deg); } + 50% { transform: rotate(6deg); } + 100% { transform: rotate(-6deg); } + } + + @keyframes fan-warp { + 0% { + box-shadow: + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.7), + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.25); + } + 40% { + box-shadow: + 0 0 0 8px rgba(var(--rgb-{{ config.icon_color }}), 0.4), + 0 0 24px 6px rgba(var(--rgb-{{ config.icon_color }}), 0.25); + } + 100% { + box-shadow: + 0 0 0 18px rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 0 0 40px 12px rgba(var(--rgb-{{ config.icon_color }}), 0.0); + } + } + + @keyframes fan-stream { + 0% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0); + } + 50% { + box-shadow: + 24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.6), + -24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.4); + } + 100% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 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)); + } visible: - user: 7a6c93d08acc4692bfd561154bb386df - user: 7809f2c31fda449b8e7c5a0d2eaf5fd5 @@ -4168,469 +4825,350 @@ views: transform: translateY(0px) scale(1); } } - - type: custom:button-card - show_name: false - show_icon: false - show_state: false - grid_options: - columns: 9 - rows: auto - custom_fields: - dining_fan: - card: - type: custom:mushroom-entity-card - entity: fan.shhf_cn_1129134246_sflt11_s_11_fan - name: 餐边扇 - icon: mdi:fan - icon_color: cyan - tap_action: - action: toggle - hold_action: - action: more-info - card_mod: - style: - mushroom-shape-icon$: | - .shape { - {# ========== USER CONFIG ========== #} - {# true = number mode, false = state mode #} - {% set use_number = false %} + - type: custom:vertical-stack-in-card + cards: + - type: custom:mushroom-entity-card + entity: fan.shhf_cn_1129134246_sflt11_s_11_fan + tap_action: + action: toggle + icon: mdi:fan + icon_color: orange + name: 餐桌扇 + 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 = 'fan.shhf_cn_1129134246_sflt11_s_11_fan' %} - {% set active_value = 'on' %} + {# STATE MODE SETTINGS #} + {% set state_entity = 'fan.shhf_cn_1129134246_sflt11_s_11_fan' %} + {% set active_value = 'on' %} - {# OPTIONAL: NUMBER MODE SETTINGS #} - {% set number_entity = 'sensor.plug_power' %} + {# OPTIONAL: NUMBER MODE SETTINGS #} + {% set number_entity = 'sensor.plug_power' %} + + {# '>' '<' '=' '>=' '<=' #} + {% set number_operator = '>' %} + + {% set threshold = 0.0 %} + {# ========== END USER CONFIG ====== #} - {# '>' '<' '=' '>=' '<=' #} - {% 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 %} + {# -------- 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 sensor_entity = state_entity %} - - {% if states(sensor_entity) == active_value %} - {% set trigger_active = true %} - {% else %} - {% set trigger_active = false %} - {% endif %} + {% set trigger_active = false %} {% endif %} - {# ------ END TRIGGER DECISION LOGIC ------ #} - - {# 根据当前风扇的 percentage 属性计算转速 #} - {% set speed = state_attr(config.entity, 'percentage') | int(0) %} - - {% if trigger_active %} - {% if speed == 0 and is_state(config.entity, 'off') %} - --shape-animation: fan-ultra-idle 3s ease-in-out infinite; - {% else %} - {% set dur = 1.2 - (speed / 120) %} - --shape-animation: fan-ultra-spin {{ [0.25, dur] | max | round(2) }}s linear infinite; - {% endif %} - - --fan-warp-animation: fan-warp 1.4s ease-in-out infinite; - --fan-stream-animation: fan-stream 1.6s ease-in-out infinite; - opacity: 1; + {% else %} + {% set sensor_entity = state_entity %} + {% if states(sensor_entity) == active_value %} + {% set trigger_active = true %} {% else %} - --shape-animation: none; - --fan-warp-animation: none; - --fan-stream-animation: none; - opacity: 0.7; + {% set trigger_active = false %} {% endif %} + {% endif %} + {# ------ END TRIGGER DECISION LOGIC ------ #} - transform-origin: 50% 50%; - position: relative; - } + {# Fan speed still based on THIS card's entity percentage #} + {% set speed = state_attr(config.entity, 'percentage') | int(0) %} - .shape::before, - .shape::after { - content: ""; - position: absolute; - inset: 0; - border-radius: inherit; - pointer-events: none; - } - - .shape::before { - animation: var(--fan-warp-animation); - } - - .shape::after { - mix-blend-mode: screen; - animation: var(--fan-stream-animation); - } - - @keyframes fan-ultra-spin { - 0% { - transform: rotate(0deg) scale(1); - filter: drop-shadow( - 0 0 3px - rgba(var(--rgb-{{ config.icon_color }}), 0.8) - ); - } - - 20% { - transform: rotate(72deg) scale(1.03); - } - - 40% { - transform: rotate(144deg) scale(1.05); - } - - 60% { - transform: rotate(216deg) scale(1.03); - } - - 80% { - transform: rotate(288deg) scale(1.02); - } - - 100% { - transform: rotate(360deg) scale(1); - filter: drop-shadow( - 0 0 5px - rgba(var(--rgb-{{ config.icon_color }}), 1) - ); - } - } - - @keyframes fan-ultra-idle { - 0% { - transform: rotate(-6deg); - } - - 50% { - transform: rotate(6deg); - } - - 100% { - transform: rotate(-6deg); - } - } - - @keyframes fan-warp { - 0% { - box-shadow: - 0 0 0 0 - rgba(var(--rgb-{{ config.icon_color }}), 0.7), - 0 0 0 0 - rgba(var(--rgb-{{ config.icon_color }}), 0.25); - } - - 40% { - box-shadow: - 0 0 0 8px - rgba(var(--rgb-{{ config.icon_color }}), 0.4), - 0 0 24px 6px - rgba(var(--rgb-{{ config.icon_color }}), 0.25); - } - - 100% { - box-shadow: - 0 0 0 18px - rgba(var(--rgb-{{ config.icon_color }}), 0), - 0 0 40px 12px - rgba(var(--rgb-{{ config.icon_color }}), 0); - } - } - - @keyframes fan-stream { - 0% { - box-shadow: - 20px 0 16px -12px - rgba(var(--rgb-{{ config.icon_color }}), 0), - -20px 0 16px -12px - rgba(var(--rgb-{{ config.icon_color }}), 0); - } - - 50% { - box-shadow: - 24px 0 20px -10px - rgba(var(--rgb-{{ config.icon_color }}), 0.6), - -24px 0 20px -10px - rgba(var(--rgb-{{ config.icon_color }}), 0.4); - } - - 100% { - box-shadow: - 20px 0 16px -12px - rgba(var(--rgb-{{ config.icon_color }}), 0), - -20px 0 16px -12px - rgba(var(--rgb-{{ config.icon_color }}), 0); - } - } - .: | - mushroom-shape-icon { - --icon-size: 68px; - display: flex; - margin: -18px 0 10px -20px !important; - padding-right: 10px; - } - - ha-card { - height: 100% !important; - clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 12px)); - } - xiaoai_fan: - card: - type: custom:mushroom-entity-card - entity: fan.xiaomi_cn_2196552583_p90_s_2_fan - name: 小爱风扇 - icon: mdi:fan - icon_color: cyan - tap_action: - action: toggle - hold_action: - action: more-info - 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 = 'fan.xiaomi_cn_2196552583_p90_s_2_fan' %} - {% 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 %} + {% if trigger_active %} + {% if speed == 0 and is_state(config.entity, 'off') %} + --shape-animation: fan-ultra-idle 3s ease-in-out infinite; {% else %} - {% set sensor_entity = state_entity %} - - {% if states(sensor_entity) == active_value %} - {% set trigger_active = true %} - {% else %} - {% set trigger_active = false %} - {% endif %} + {% set dur = 1.2 - (speed / 120) %} + --shape-animation: fan-ultra-spin {{ [0.25, dur] | max | round(2) }}s linear infinite; {% endif %} - {# ------ END TRIGGER DECISION LOGIC ------ #} + --fan-warp-animation: fan-warp 1.4s ease-in-out infinite; + --fan-stream-animation: fan-stream 1.6s ease-in-out infinite; + opacity: 1; + {% else %} + --shape-animation: none; + --fan-warp-animation: none; + --fan-stream-animation: none; + opacity: 0.7; + {% endif %} - {# 根据当前风扇的 percentage 属性计算转速 #} - {% set speed = state_attr(config.entity, 'percentage') | int(0) %} + transform-origin: 50% 50%; + position: relative; + } - {% if trigger_active %} - {% if speed == 0 and is_state(config.entity, 'off') %} - --shape-animation: fan-ultra-idle 3s ease-in-out infinite; - {% else %} - {% set dur = 1.2 - (speed / 120) %} - --shape-animation: fan-ultra-spin {{ [0.25, dur] | max | round(2) }}s linear infinite; - {% endif %} + .shape::before, + .shape::after { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + } - --fan-warp-animation: fan-warp 1.4s ease-in-out infinite; - --fan-stream-animation: fan-stream 1.6s ease-in-out infinite; - opacity: 1; + .shape::before { + animation: var(--fan-warp-animation); + } + + .shape::after { + mix-blend-mode: screen; + animation: var(--fan-stream-animation); + } + + @keyframes fan-ultra-spin { + 0% { + transform: rotate(0deg) scale(1); + filter: drop-shadow(0 0 3px rgba(var(--rgb-{{ config.icon_color }}), 0.8)); + } + 20% { transform: rotate(72deg) scale(1.03); } + 40% { transform: rotate(144deg) scale(1.05); } + 60% { transform: rotate(216deg) scale(1.03); } + 80% { transform: rotate(288deg) scale(1.02); } + 100% { + transform: rotate(360deg) scale(1); + filter: drop-shadow(0 0 5px rgba(var(--rgb-{{ config.icon_color }}), 1)); + } + } + + @keyframes fan-ultra-idle { + 0% { transform: rotate(-6deg); } + 50% { transform: rotate(6deg); } + 100% { transform: rotate(-6deg); } + } + + @keyframes fan-warp { + 0% { + box-shadow: + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.7), + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.25); + } + 40% { + box-shadow: + 0 0 0 8px rgba(var(--rgb-{{ config.icon_color }}), 0.4), + 0 0 24px 6px rgba(var(--rgb-{{ config.icon_color }}), 0.25); + } + 100% { + box-shadow: + 0 0 0 18px rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 0 0 40px 12px rgba(var(--rgb-{{ config.icon_color }}), 0.0); + } + } + + @keyframes fan-stream { + 0% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0); + } + 50% { + box-shadow: + 24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.6), + -24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.4); + } + 100% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 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)); + + /* 当前子卡片与其他子卡片的间距 */ + margin: 6px 2px !important; + } + - type: custom:mushroom-entity-card + entity: fan.xiaomi_cn_2196552583_p90_s_2_fan + tap_action: + action: toggle + icon: mdi:fan + icon_color: cyan + name: 小爱风扇 + 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 = 'fan.xiaomi_cn_2196552583_p90_s_2_fan' %} + {% 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 %} - --shape-animation: none; - --fan-warp-animation: none; - --fan-stream-animation: none; - opacity: 0.7; + {% set trigger_active = false %} {% endif %} + {% else %} + {% set sensor_entity = state_entity %} + {% if states(sensor_entity) == active_value %} + {% set trigger_active = true %} + {% else %} + {% set trigger_active = false %} + {% endif %} + {% endif %} + {# ------ END TRIGGER DECISION LOGIC ------ #} - transform-origin: 50% 50%; - position: relative; + {# Fan speed still based on THIS card's entity percentage #} + {% set speed = state_attr(config.entity, 'percentage') | int(0) %} + + {% if trigger_active %} + {% if speed == 0 and is_state(config.entity, 'off') %} + --shape-animation: fan-ultra-idle 3s ease-in-out infinite; + {% else %} + {% set dur = 1.2 - (speed / 120) %} + --shape-animation: fan-ultra-spin {{ [0.25, dur] | max | round(2) }}s linear infinite; + {% endif %} + --fan-warp-animation: fan-warp 1.4s ease-in-out infinite; + --fan-stream-animation: fan-stream 1.6s ease-in-out infinite; + opacity: 1; + {% else %} + --shape-animation: none; + --fan-warp-animation: none; + --fan-stream-animation: none; + opacity: 0.7; + {% endif %} + + transform-origin: 50% 50%; + position: relative; + } + + .shape::before, + .shape::after { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + } + + .shape::before { + animation: var(--fan-warp-animation); + } + + .shape::after { + mix-blend-mode: screen; + animation: var(--fan-stream-animation); + } + + @keyframes fan-ultra-spin { + 0% { + transform: rotate(0deg) scale(1); + filter: drop-shadow(0 0 3px rgba(var(--rgb-{{ config.icon_color }}), 0.8)); } - - .shape::before, - .shape::after { - content: ""; - position: absolute; - inset: 0; - border-radius: inherit; - pointer-events: none; + 20% { transform: rotate(72deg) scale(1.03); } + 40% { transform: rotate(144deg) scale(1.05); } + 60% { transform: rotate(216deg) scale(1.03); } + 80% { transform: rotate(288deg) scale(1.02); } + 100% { + transform: rotate(360deg) scale(1); + filter: drop-shadow(0 0 5px rgba(var(--rgb-{{ config.icon_color }}), 1)); } + } - .shape::before { - animation: var(--fan-warp-animation); + @keyframes fan-ultra-idle { + 0% { transform: rotate(-6deg); } + 50% { transform: rotate(6deg); } + 100% { transform: rotate(-6deg); } + } + + @keyframes fan-warp { + 0% { + box-shadow: + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.7), + 0 0 0 0 rgba(var(--rgb-{{ config.icon_color }}), 0.25); } - - .shape::after { - mix-blend-mode: screen; - animation: var(--fan-stream-animation); + 40% { + box-shadow: + 0 0 0 8px rgba(var(--rgb-{{ config.icon_color }}), 0.4), + 0 0 24px 6px rgba(var(--rgb-{{ config.icon_color }}), 0.25); } - - @keyframes fan-ultra-spin { - 0% { - transform: rotate(0deg) scale(1); - filter: drop-shadow( - 0 0 3px - rgba(var(--rgb-{{ config.icon_color }}), 0.8) - ); - } - - 20% { - transform: rotate(72deg) scale(1.03); - } - - 40% { - transform: rotate(144deg) scale(1.05); - } - - 60% { - transform: rotate(216deg) scale(1.03); - } - - 80% { - transform: rotate(288deg) scale(1.02); - } - - 100% { - transform: rotate(360deg) scale(1); - filter: drop-shadow( - 0 0 5px - rgba(var(--rgb-{{ config.icon_color }}), 1) - ); - } + 100% { + box-shadow: + 0 0 0 18px rgba(var(--rgb-{{ config.icon_color }}), 0.0), + 0 0 40px 12px rgba(var(--rgb-{{ config.icon_color }}), 0.0); } + } - @keyframes fan-ultra-idle { - 0% { - transform: rotate(-6deg); - } - - 50% { - transform: rotate(6deg); - } - - 100% { - transform: rotate(-6deg); - } + @keyframes fan-stream { + 0% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0); } - - @keyframes fan-warp { - 0% { - box-shadow: - 0 0 0 0 - rgba(var(--rgb-{{ config.icon_color }}), 0.7), - 0 0 0 0 - rgba(var(--rgb-{{ config.icon_color }}), 0.25); - } - - 40% { - box-shadow: - 0 0 0 8px - rgba(var(--rgb-{{ config.icon_color }}), 0.4), - 0 0 24px 6px - rgba(var(--rgb-{{ config.icon_color }}), 0.25); - } - - 100% { - box-shadow: - 0 0 0 18px - rgba(var(--rgb-{{ config.icon_color }}), 0), - 0 0 40px 12px - rgba(var(--rgb-{{ config.icon_color }}), 0); - } + 50% { + box-shadow: + 24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.6), + -24px 0 20px -10px rgba(var(--rgb-{{ config.icon_color }}), 0.4); } - - @keyframes fan-stream { - 0% { - box-shadow: - 20px 0 16px -12px - rgba(var(--rgb-{{ config.icon_color }}), 0), - -20px 0 16px -12px - rgba(var(--rgb-{{ config.icon_color }}), 0); - } - - 50% { - box-shadow: - 24px 0 20px -10px - rgba(var(--rgb-{{ config.icon_color }}), 0.6), - -24px 0 20px -10px - rgba(var(--rgb-{{ config.icon_color }}), 0.4); - } - - 100% { - box-shadow: - 20px 0 16px -12px - rgba(var(--rgb-{{ config.icon_color }}), 0), - -20px 0 16px -12px - rgba(var(--rgb-{{ config.icon_color }}), 0); - } - } - .: | - mushroom-shape-icon { - --icon-size: 68px; - display: flex; - margin: -18px 0 10px -20px !important; - padding-right: 10px; + 100% { + box-shadow: + 20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 0), + -20px 0 16px -12px rgba(var(--rgb-{{ config.icon_color }}), 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)); - ha-card { - height: 100% !important; - clip-path: inset(0 0 0 0 round var(--ha-card-border-radius, 12px)); - } - styles: - card: - - height: 158px - - padding: 6px - - border-radius: 20px - - background: rgba(255, 255, 255, 0.06) - - box-shadow: none - - box-sizing: border-box - grid: - - grid-template-areas: | - "dining_fan" - "xiaoai_fan" - - grid-template-columns: 1fr - - grid-template-rows: repeat(2, minmax(0, 1fr)) - - row-gap: 15px - - height: 100% - custom_fields: - dining_fan: - - height: 100% - - min-height: 0 - - width: 100% - - justify-self: stretch - - text-align: left - xiaoai_fan: - - height: 100% - - min-height: 0 - - width: 100% - - justify-self: stretch - - text-align: left + /* 当前子卡片与其他子卡片的间距 */ + margin: 6px 2px !important; + } + card_mod: + style: | + ha-card { + /* 整张组合卡片的背景颜色与透明度 */ + background: rgba(30, 30, 30, 0.1) !important; + + /* 整张组合卡片与周围其他卡片的距离 */ + /*margin: 10px 8px 16px 8px !important;*/ + + /* 组合卡片边缘与内部卡片之间的距离 */ + padding: 2px 4px !important; + } - square: false type: grid cards: