ChatGPT m'a pondu ça. 

button_card_templates :
button_card_templates:
p70_small:
show_icon: true
show_name: true
show_state: false
show_label: true
tap_action:
haptic: light
styles:
card:
- min-height: 64px
- padding: 8px 4px
- border-radius: 14px
- box-shadow: none
- border: 1px solid rgba(127, 127, 127, 0.18)
- background: rgba(127, 127, 127, 0.07)
grid:
- grid-template-areas: "\"i\" \"n\" \"l\""
- grid-template-rows: 26px min-content min-content
icon:
- width: 23px
- color: var(--secondary-text-color)
name:
- font-size: 11px
- font-weight: 600
- color: var(--primary-text-color)
label:
- margin-top: 2px
- font-size: 9px
- color: var(--secondary-text-color)
p70_speed:
template: p70_small
variables:
preset: Level 1
tap_action:
action: call-service
service: fan.set_preset_mode
target:
entity_id: "[[[ return entity.entity_id; ]]]"
data:
preset_mode: "[[[ return variables.preset; ]]]"
haptic: light
state:
- operator: template
value: "[[[ return entity.attributes.preset_mode === variables.preset; ]]]"
styles:
card:
- background: rgba(1, 190, 158, 0.15)
- border: 1px solid '#01be9e'
icon:
- color: "#01be9e"
name:
- color: "#01be9e"
label:
- color: "#01be9e"
p70_toggle:
template: p70_small
variables:
active: false
service_on: null
service_off: null
tap_action:
action: call-service
service: >
[[[ return variables.active ? variables.service_off :
variables.service_on; ]]]
target:
entity_id: "[[[ return entity.entity_id; ]]]"
haptic: light
state:
- operator: template
value: "[[[ return Boolean(variables.active); ]]]"
styles:
card:
- background: rgba(1, 190, 158, 0.15)
- border: 1px solid '#01be9e'
icon:
- color: "#01be9e"
name:
- color: "#01be9e"
label:
- color: "#01be9e"
p70_move:
show_icon: true
show_name: false
show_state: false
variables:
direction: left
tap_action:
action: call-service
service: xiaomi_miio_fan.fan_turn
target:
entity_id: "[[[ return entity.entity_id; ]]]"
data:
direction: "[[[ return variables.direction; ]]]"
haptic: light
hold_action:
action: call-service
service: xiaomi_miio_fan.fan_turn
target:
entity_id: "[[[ return entity.entity_id; ]]]"
data:
direction: "[[[ return variables.direction; ]]]"
repeat: 350
haptic: selection
styles:
card:
- min-height: 48px
- border-radius: 14px
- box-shadow: none
- border: 1px solid rgba(127, 127, 127, 0.18)
- background: rgba(127, 127, 127, 0.07)
icon:
- width: 28px
- color: var(--primary-text-color)
button-card :
type: custom:button-card
entity: fan.xiaomi_smart_desktop_air_circulation_fan
name: Xiaomi Smart Fan P70
show_icon: false
show_state: false
show_label: false
show_ripple: false
tap_action:
action: none
hold_action:
action: more-info
extra_styles: |
@keyframes p70-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes p70-swing-h {
0%, 100% { transform: rotate(-9deg); }
50% { transform: rotate(9deg); }
}
@keyframes p70-swing-v {
0%, 100% { transform: translateY(-4px); }
50% { transform: translateY(5px); }
}
.p70-status {
display: flex;
align-items: center;
gap: 7px;
font-size: 12px;
color: var(--secondary-text-color);
}
.p70-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--disabled-text-color);
}
.p70-dot.on {
background: #01be9e;
box-shadow: 0 0 9px rgba(1, 190, 158, .9);
}
.p70-visual {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 190px;
}
.p70-h.active {
animation: p70-swing-h 4s ease-in-out infinite;
transform-origin: 50% 100%;
}
.p70-v.active {
animation: p70-swing-v 3.2s ease-in-out infinite;
}
.p70-shell {
width: 154px;
height: 154px;
box-sizing: border-box;
border: 10px solid rgba(127, 127, 127, .22);
border-radius: 50%;
display: grid;
place-items: center;
background: radial-gradient(circle, rgba(127,127,127,.08) 0 54%, rgba(127,127,127,.18) 55% 58%, transparent 59%);
box-shadow: inset 0 0 18px rgba(0,0,0,.08), 0 10px 25px rgba(0,0,0,.08);
}
.p70-shell.on {
border-color: rgba(1, 190, 158, .38);
box-shadow: inset 0 0 25px rgba(1,190,158,.13), 0 10px 28px rgba(0,0,0,.09);
}
.p70-blade {
width: 112px;
height: 112px;
color: var(--disabled-text-color);
}
.p70-shell.on .p70-blade {
color: #01be9e;
filter: drop-shadow(0 0 7px rgba(1,190,158,.35));
}
.p70-blade.spinning {
animation: p70-spin var(--p70-duration) linear infinite;
}
.p70-center {
position: absolute;
width: 30px;
height: 30px;
border-radius: 50%;
display: grid;
place-items: center;
background: var(--card-background-color);
border: 2px solid rgba(127,127,127,.28);
font-size: 9px;
font-weight: 700;
color: var(--secondary-text-color);
}
.p70-shell.on .p70-center {
border-color: #01be9e;
color: #01be9e;
}
.p70-meta {
margin-top: 12px;
display: flex;
gap: 7px;
flex-wrap: wrap;
justify-content: center;
}
.p70-chip {
padding: 4px 8px;
border-radius: 999px;
font-size: 10px;
color: var(--secondary-text-color);
background: rgba(127,127,127,.09);
border: 1px solid rgba(127,127,127,.14);
}
.p70-section-title {
margin-top: 4px;
font-size: 10px;
font-weight: 700;
letter-spacing: .12em;
color: var(--secondary-text-color);
}
styles:
card:
- padding: 18px
- border-radius: 24px
- overflow: hidden
grid:
- grid-template-areas: >-
"n power" "status power" "visual visual" "speed_title speed_title"
"speeds speeds" "mode_title mode_title" "modes modes" "direction_title
direction_title" "directions directions" "cycle cycle"
- grid-template-columns: 1fr 48px
- grid-template-rows: >-
min-content min-content 205px min-content min-content min-content
min-content min-content min-content min-content
- column-gap: 10px
- row-gap: 10px
name:
- justify-self: start
- align-self: end
- font-size: 18px
- font-weight: 700
custom_fields:
power:
- align-self: center
- justify-self: end
status:
- justify-self: start
- align-self: start
visual:
- width: 100%
speed_title:
- justify-self: start
- width: 100%
mode_title:
- justify-self: start
- width: 100%
direction_title:
- justify-self: start
- width: 100%
speeds:
- width: 100%
modes:
- width: 100%
directions:
- width: 100%
cycle:
- width: 100%
custom_fields:
power:
card:
type: custom:button-card
entity: fan.xiaomi_smart_desktop_air_circulation_fan
icon: mdi:power
show_name: false
show_state: false
tap_action:
action: toggle
haptic: medium
styles:
card:
- width: 46px
- height: 46px
- border-radius: 50%
- padding: 0
- box-shadow: none
- border: 1px solid rgba(127, 127, 127, 0.2)
- background: rgba(127, 127, 127, 0.08)
icon:
- width: 23px
- color: var(--secondary-text-color)
state:
- value: "on"
styles:
card:
- background: rgba(1, 190, 158, 0.16)
- border: 1px solid
icon:
- color: "#01be9e"
status: |
[[[
const on = entity.state === 'on';
const pct = Number(entity.attributes.percentage ?? entity.attributes.raw_speed ?? 0);
const mode = entity.attributes.mode === 'Natural' ? 'Naturel' : 'Direct';
return `<div class="p70-status">
<span class="p70-dot ${on ? 'on' : ''}"></span>
<span>${on ? `Allumé · ${pct}% · ${mode}` : 'Éteint'}</span>
</div>`;
]]]
visual: |
[[[
const on = entity.state === 'on';
const pct = Number(entity.attributes.percentage ?? entity.attributes.raw_speed ?? 0);
const horizontal = Boolean(entity.attributes.oscillating ?? entity.attributes.oscillate ?? false);
const vertical = Boolean(entity.attributes.vertical_oscillate ?? false);
const angleH = entity.attributes.angle ?? '—';
const angleV = entity.attributes.vertical_angle ?? '—';
const preset = entity.attributes.preset_mode ?? '—';
const duration = Math.max(0.55, 4.6 - pct * 0.037).toFixed(2);
return `<div class="p70-visual">
<div class="p70-h ${on && horizontal ? 'active' : ''}">
<div class="p70-v ${on && vertical ? 'active' : ''}">
<div class="p70-shell ${on ? 'on' : ''}" style="position:relative">
<ha-icon class="p70-blade ${on ? 'spinning' : ''}" style="--p70-duration:${duration}s" icon="mdi:fan"></ha-icon>
<div class="p70-center">${on ? pct : 0}%</div>
</div>
</div>
</div>
<div class="p70-meta">
<span class="p70-chip">${preset}</span>
<span class="p70-chip">H ${angleH}°</span>
<span class="p70-chip">V ${angleV}°</span>
</div>
</div>`;
]]]
speed_title: <div class="p70-section-title">VITESSE</div>
speeds:
card:
type: grid
columns: 4
square: false
cards:
- type: custom:button-card
template: p70_speed
entity: fan.xiaomi_smart_desktop_air_circulation_fan
variables:
preset: Level 1
icon: mdi:fan
name: Niveau 1
label: Doux
- type: custom:button-card
template: p70_speed
entity: fan.xiaomi_smart_desktop_air_circulation_fan
variables:
preset: Level 2
icon: mdi:fan
name: Niveau 2
label: Moyen
- type: custom:button-card
template: p70_speed
entity: fan.xiaomi_smart_desktop_air_circulation_fan
variables:
preset: Level 3
icon: mdi:fan
name: Niveau 3
label: Fort
- type: custom:button-card
template: p70_speed
entity: fan.xiaomi_smart_desktop_air_circulation_fan
variables:
preset: Level 4
icon: mdi:fan
name: Niveau 4
label: Turbo
mode_title: <div class="p70-section-title">MODES ET OPTIONS</div>
modes:
card:
type: grid
columns: 3
square: false
cards:
- type: custom:button-card
template: p70_toggle
entity: fan.xiaomi_smart_desktop_air_circulation_fan
variables:
active: "[[[ return entity.attributes.mode === \"Natural\"; ]]]"
service_on: xiaomi_miio_fan.fan_set_natural_mode_on
service_off: xiaomi_miio_fan.fan_set_natural_mode_off
icon: mdi:leaf
name: Naturel
label: >-
[[[ return entity.attributes.mode === "Natural" ? "Activé" :
"Désactivé"; ]]]
- type: custom:button-card
template: p70_small
entity: fan.xiaomi_smart_desktop_air_circulation_fan
icon: mdi:swap-horizontal-bold
name: Horizontal
label: >
[[[ return Boolean(entity.attributes.oscillating ??
entity.attributes.oscillate ?? false) ? 'Activée' : 'Arrêtée'; ]]]
tap_action:
action: call-service
service: fan.oscillate
target:
entity_id: fan.xiaomi_smart_desktop_air_circulation_fan
data:
oscillating: >
[[[ return !Boolean(entity.attributes.oscillating ??
entity.attributes.oscillate ?? false); ]]]
haptic: light
state:
- operator: template
value: >-
[[[ return Boolean(entity.attributes.oscillating ??
entity.attributes.oscillate ?? false); ]]]
styles:
card:
- background: rgba(1, 190, 158, 0.15)
icon:
- color: "#01be9e"
name:
- color: "#01be9e"
label:
- color: "#01be9e"
- type: custom:button-card
template: p70_toggle
entity: fan.xiaomi_smart_desktop_air_circulation_fan
variables:
active: "[[[ return Boolean(entity.attributes.vertical_oscillate); ]]]"
service_on: xiaomi_miio_fan.fan_set_vertical_oscillation_on
service_off: xiaomi_miio_fan.fan_set_vertical_oscillation_off
icon: mdi:swap-vertical-bold
name: Vertical
label: >-
[[[ return entity.attributes.vertical_oscillate ? "Activée" :
"Arrêtée"; ]]]
- type: custom:button-card
template: p70_small
entity: fan.xiaomi_smart_desktop_air_circulation_fan
icon: mdi:led-on
name: Voyant
label: "[[[ return entity.attributes.led ? \"Allumé\" : \"Éteint\"; ]]]"
tap_action:
action: call-service
service: xiaomi_miio_fan.fan_set_led_brightness
target:
entity_id: fan.xiaomi_smart_desktop_air_circulation_fan
data:
brightness: "[[[ return entity.attributes.led ? 2 : 0; ]]]"
haptic: light
state:
- operator: template
value: "[[[ return Boolean(entity.attributes.led); ]]]"
styles:
card:
- background: rgba(1, 190, 158, 0.15)
icon:
- color: "#01be9e"
name:
- color: "#01be9e"
label:
- color: "#01be9e"
- type: custom:button-card
template: p70_toggle
entity: fan.xiaomi_smart_desktop_air_circulation_fan
variables:
active: "[[[ return Boolean(entity.attributes.buzzer); ]]]"
service_on: xiaomi_miio_fan.fan_set_buzzer_on
service_off: xiaomi_miio_fan.fan_set_buzzer_off
icon: mdi:volume-high
name: Bip
label: "[[[ return entity.attributes.buzzer ? \"Activé\" : \"Muet\"; ]]]"
- type: custom:button-card
template: p70_toggle
entity: fan.xiaomi_smart_desktop_air_circulation_fan
variables:
active: "[[[ return Boolean(entity.attributes.child_lock); ]]]"
service_on: xiaomi_miio_fan.fan_set_child_lock_on
service_off: xiaomi_miio_fan.fan_set_child_lock_off
icon: mdi:lock-outline
name: Verrou
label: >-
[[[ return entity.attributes.child_lock ? "Verrouillé" : "Libre";
]]]
direction_title: <div class="p70-section-title">ORIENTATION — APPUI LONG POUR RÉPÉTER</div>
directions:
card:
type: grid
columns: 4
square: false
cards:
- type: custom:button-card
template: p70_move
entity: fan.xiaomi_smart_desktop_air_circulation_fan
icon: mdi:chevron-left
variables:
direction: left
- type: custom:button-card
template: p70_move
entity: fan.xiaomi_smart_desktop_air_circulation_fan
icon: mdi:chevron-up
variables:
direction: up
- type: custom:button-card
template: p70_move
entity: fan.xiaomi_smart_desktop_air_circulation_fan
icon: mdi:chevron-down
variables:
direction: down
- type: custom:button-card
template: p70_move
entity: fan.xiaomi_smart_desktop_air_circulation_fan
icon: mdi:chevron-right
variables:
direction: right
cycle:
card:
type: grid
columns: 3
square: false
cards:
- type: custom:button-card
template: p70_small
entity: fan.xiaomi_smart_desktop_air_circulation_fan
icon: mdi:angle-acute
name: Angle H
label: "[[[ return `${entity.attributes.angle ?? \"—\"}°`; ]]]"
tap_action:
action: call-service
service: xiaomi_miio_fan.fan_set_oscillation_angle
target:
entity_id: fan.xiaomi_smart_desktop_air_circulation_fan
data:
angle: |
[[[
const values = [30, 60, 90, 120];
const current = Number(entity.attributes.angle ?? 0);
return values.find(v => v > current) ?? values[0];
]]]
haptic: light
- type: custom:button-card
template: p70_small
entity: fan.xiaomi_smart_desktop_air_circulation_fan
icon: mdi:angle-acute
name: Angle V
label: "[[[ return `${entity.attributes.vertical_angle ?? \"—\"}°`; ]]]"
tap_action:
action: call-service
service: xiaomi_miio_fan.fan_set_vertical_oscillation_angle
target:
entity_id: fan.xiaomi_smart_desktop_air_circulation_fan
data:
vertical_angle: |
[[[
const values = [30, 60, 90, 100];
const current = Number(entity.attributes.vertical_angle ?? 0);
return values.find(v => v > current) ?? values[0];
]]]
haptic: light
- type: custom:button-card
template: p70_small
entity: fan.xiaomi_smart_desktop_air_circulation_fan
icon: mdi:timer-outline
name: Minuterie
label: |
[[[
const minutes = Number(entity.attributes.delay_off_countdown ?? 0);
return minutes > 0 ? `${minutes} min` : 'Arrêtée';
]]]
tap_action:
action: call-service
service: xiaomi_miio_fan.fan_set_delay_off
target:
entity_id: fan.xiaomi_smart_desktop_air_circulation_fan
data:
delay_off_countdown: |
[[[
const values = [0, 60, 120, 240, 480];
const current = Number(entity.attributes.delay_off_countdown ?? 0);
return values.find(v => v > current) ?? values[0];
]]]
haptic: light
edit:
version plus compacte avec slider pour la vitesse et image du ventilateur .