Bonjour,
je vous partage une carte que j'ai réalisé pour gérer mes volets avec un peu d'animation.
Elle est faite entièrement avec custom:button-card et du CSS (= aucune image).
Voici le rendu :

Note : la fermeture dure 17 secondes car mon module de volet ne remonte pas l'état en temps réel.
Il faut donc attendre la fin de la temporisation de calibration pour obtenir l'état du volet et sa position, à moins de refaire une action sur le volet comme quand je clique sur stop.
La carte :
type: custom:button-card
entity: cover.0xa4c1388bc992fafd
show_name: true
show_state: true
show_icon: false
name: Volet
layout: vertical
tap_action: none
styles:
grid:
- grid-template-areas: |
"n"
"volet"
"s"
"buttons"
- grid-template-rows: min-content 110px min-content min-content
card:
- padding: 16px
custom_fields:
volet:
- justify-self: center
- align-self: center
buttons:
- justify-self: center
- align-self: center
custom_fields:
volet: |
[[[
const position = entity.attributes.current_position ?? 0;
const closed = 100 - position;
return `
<div class="fenetre">
<div class="verre"></div>
<div class="volet ${entity.state}" style="height:${closed}%;"></div>
<div class="fleche ${entity.state}"></div>
</div>
<style>
.fenetre{
position:relative;
width:90px;
height:90px;
border:4px solid #666;
border-radius:6px;
overflow:hidden;
background:#222;
}
.verre{
position:absolute;
inset:0;
background:linear-gradient(#7fc8ff,#dff4ff);
}
.volet{
position:absolute;
left:0;
top:0;
width:100%;
background:
repeating-linear-gradient(
to bottom,
#d6d6d6 0px,
#d6d6d6 4px,
#bdbdbd 4px,
#bdbdbd 8px
);
transition:height .4s;
}
.fleche{
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
font-size:50px;
font-weight: bold;
opacity:0;
transition:opacity .2s, transform .2s;
pointer-events:none;
}
/* ouverture => flèche vers le haut */
.fleche.opening{
opacity:1;
color:#4CAF50;
animation:ouverture 0.8s infinite;
}
.fleche.opening::before{
content:"⬆";
}
/* fermeture => flèche vers le bas */
.fleche.closing{
opacity:1;
color:#F44336;
animation:fermeture 0.8s infinite;
}
.fleche.closing::before{
content:"⬇";
}
/* idle = stop => invisible */
.fleche.idle{
opacity:0;
}
/* animations */
@keyframes ouverture{
0% { transform:translate(-50%,-50%) translateY(6px); }
50% { transform:translate(-50%,-50%) translateY(-6px); }
100% { transform:translate(-50%,-50%) translateY(6px); }
}
@keyframes fermeture{
0% { transform:translate(-50%,-50%) translateY(-6px); }
50% { transform:translate(-50%,-50%) translateY(6px); }
100% { transform:translate(-50%,-50%) translateY(-6px); }
}
</style>
`;
]]]
buttons:
card:
type: horizontal-stack
cards:
- type: custom:button-card
entity: cover.0xa4c1388bc992fafd
show_name: false
show_icon: false
custom_fields:
icon: |
[[[
return `
<ha-icon id="up-btn" icon="mdi:arrow-up-bold" onclick="this.classList.remove('pulse');this.classList.add('pulse');"></ha-icon>
<style>
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.35); filter: brightness(1.4); }
100% { transform: scale(1); }
}
.pulse {
animation: pulse 0.4s ease;
}
</style>
`;
]]]
tap_action:
action: call-service
service: cover.open_cover
target:
entity_id: cover.0xa4c1388bc992fafd
styles:
card:
- width: 32px
state:
- value: opening
styles:
card:
- background-color: rgba(76, 175, 80, 0.25)
icon:
- color: "#4CAF50"
- type: custom:button-card
entity: cover.0xa4c1388bc992fafd
show_name: false
show_icon: false
custom_fields:
icon: |
[[[
return `
<ha-icon icon="mdi:stop" onclick="this.classList.remove('pulse');this.classList.add('pulse');"></ha-icon>
<style>
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.35); }
100% { transform: scale(1); }
}
.pulse { animation: pulse 0.4s ease; }
</style>
`;
]]]
tap_action:
action: call-service
service: cover.stop_cover
target:
entity_id: cover.0xa4c1388bc992fafd
styles:
card:
- width: 32px
state:
- value: opening
styles:
card:
- background-color: rgba(255, 193, 7, 0.25)
- value: closing
styles:
card:
- background-color: rgba(255, 193, 7, 0.25)
- type: custom:button-card
entity: cover.0xa4c1388bc992fafd
show_name: false
show_icon: false
custom_fields:
icon: |
[[[
return `
<ha-icon icon="mdi:arrow-down-bold" onclick="this.classList.remove('pulse');this.classList.add('pulse');"></ha-icon>
<style>
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.35); filter: brightness(1.4); }
100% { transform: scale(1); }
}
.pulse { animation: pulse 0.4s ease; }
</style>
`;
]]]
tap_action:
action: call-service
service: cover.close_cover
target:
entity_id: cover.0xa4c1388bc992fafd
styles:
card:
- width: 32px
state:
- value: closing
styles:
card:
- background-color: rgba(244, 67, 54, 0.25)
icon:
- color: "#F44336"
```