un changement merci mais il y a encore autre chose à priori
il y a une erreur dans la position du curseur dans le dernier paragraphe, remplace le par ça :
cursor:
- position: absolute
- top: "[[[ return 'calc(55% - 12.5px)'; ]]]"
- left: |
[[[
const value = Number(entity.state);
const min = -25;
const max = 60;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const t = clamp(value, min, max);
const barWidth = window.innerWidth <= 600 ? 220 : 250;
const halfCursor = 8;
const barRightPercent = window.innerWidth <= 600 ? 4 : 7;
const percent = ((t - min) / (max - min)) * 100;
const offsetPx = (barWidth * percent) / 100;
return `calc(100% - ${barRightPercent}% - ${barWidth}px + ${offsetPx}px - ${halfCursor}px)`;
]]]
ton curseur sera a la bonne position, a toi de gérer la couleur, car c’est pas top top, ni même le frais a 0° :), je pense qu’il fait froid a 0° ![]()
Et à 60° C je change de planète…
Oui il faut revoir les commentaires, je voulais déjà régler la position etc. Et après je vais utiliser un number test pour adapter les zones couleurs etc
Merci en effet on est déjà mieux :

type: custom:button-card
entity: sensor.netatmo_jardin_temperature
show_state: false
show_icon: false
show_name: false
tap_action: none
double_tap_action: none
hold_action: none
custom_fields:
icon:
card:
type: custom:button-card
icon: mdi:thermometer
styles:
card:
- aspect-ratio: 1/1
- width: "[[[ return window.innerWidth <= 600 ? '20px' : '25px' ]]]"
- border: none
- border-radius: 50%
- background: none
icon:
- width: "[[[ return window.innerWidth <= 600 ? '18px' : '20px' ]]]"
- color: white
icon_border:
card:
type: custom:button-card
styles:
card:
- aspect-ratio: 1/1
- width: "[[[ return window.innerWidth <= 600 ? '23px' : '25px' ]]]"
- border: 1px solid white
- border-radius: 50%
- background: none
name:
card:
type: custom:button-card
name: Netatmo Jardin Température
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: "[[[ return window.innerWidth <= 600 ? '0.9rem' : '1.0rem' ]]]"
- align-self: start
- justify-self: start
- color: white
- font-weight: 500
bar:
card:
type: custom:button-card
show_name: false
show_icon: false
show_state: false
styles:
card:
- height: 6px
- width: |
[[[ return window.innerWidth <= 600 ? "220px" : "250px"; ]]]
- border-radius: 999px
- border: 0
- padding: 0
- background: |-
linear-gradient(to right, rgba(0,0,255,1.0) 0%,
rgba(0,165,255,1.0) 40%, rgba(0,255,0,1.0) 65%,
rgba(255,255,0,1.0) 75%, rgba(255,0,0,1.0) 100%)
cursor:
card:
type: custom:button-card
show_name: false
show_icon: false
show_state: false
styles:
card:
- width: 12px
- height: 25px
- border-radius: 999px
- border: 4px solid rgba(42,45,54,1.0)
- background-color: |
[[[
const minTemp = -20;
const maxTemp = 60;
const temp = Number(entity.state) || minTemp;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((temp - minTemp) / (maxTemp - minTemp)) * 100, 0, 100);
const stops = [
{ p: 0, c: [0, 0, 255, 1.0] },
{ p: 40,c: [0, 165, 255, 1.0] },
{ p: 65, c: [0, 255, 0, 1.0] },
{ p: 75,c: [255, 255, 0, 1.0] },
{ p: 100, c: [255, 0, 0, 1.0] }
];
let i = 1;
while (i < stops.length && percent > stops[i].p) i++;
const a = stops[i - 1], b = stops[i];
const t = (percent - a.p) / (b.p - a.p);
const lerp = (x, y, t) => Math.round(x + (y - x) * t);
const r = lerp(a.c[0], b.c[0], t);
const g = lerp(a.c[1], b.c[1], t);
const bcol = lerp(a.c[2], b.c[2], t);
return `rgba(${r},${g},${bcol},1.0)`;
]]]
value:
card:
type: custom:button-card
show_name: false
show_icon: false
show_state: true
state_display: |
[[[
const v = Number(entity.state);
return isNaN(v) ? '—' : v.toFixed(1);
]]]
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
state:
- color: white
- font-size: "[[[ return window.innerWidth <= 600 ? '1.6rem' : '1.8rem' ]]]"
- font-weight: 700
unit:
card:
type: custom:button-card
name: °C
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: "[[[ return window.innerWidth <= 600 ? '1.1rem' : '1.3rem' ]]]"
- align-self: start
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 800
min_val:
card:
type: custom:button-card
name: "-20°C"
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: "[[[ return window.innerWidth <= 600 ? '0.8rem' : '1.0rem' ]]]"
- align-self: start
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 400
max_val:
card:
type: custom:button-card
name: 60°C
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: "[[[ return window.innerWidth <= 600 ? '0.8rem' : '1.0rem' ]]]"
- align-self: start
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 400
comment:
card:
type: custom:button-card
show_name: true
show_icon: false
show_state: false
name: |
[[[
const temp = Number(entity.state);
if (temp < -5) return "Froid extrême";
if (temp >= -5 && temp < 0) return "Très froid";
if (temp >= 0 && temp < 10) return "Froid";
if (temp >= 10 && temp < 15) return "Frais";
if (temp >= 15 && temp < 25) return "Confortable";
if (temp >= 25 && temp < 30) return "Chaud";
if (temp >= 30 && temp < 40) return "Très chaud";
if (temp >= 40) return "Chaleur extrême";
]]]
styles:
card:
- height: 25px
- width: auto
- border-radius: 999px
- border: none
- padding: 0px 8px 0px 8px
- background: white
- background-color: |
[[[
const minTemp = -20;
const maxTemp = 60;
const temp = Number(entity.state);
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = isNaN(temp) ? 0 : clamp(((temp - minTemp) / (maxTemp - minTemp)) * 100, 0, 100);
const stops = [
{ p: 0, c: [0, 0, 255, 1.0] },
{ p: 40,c: [0, 165, 255, 1.0] },
{ p: 65, c: [0, 255, 0, 1.0] },
{ p: 75,c: [255, 255, 0, 1.0] },
{ p: 100, c: [255, 0, 0, 1.0] }
];
let i = 1;
while (i < stops.length && percent > stops[i].p) i++;
const a = stops[i - 1], b = stops[i];
const t = (percent - a.p) / (b.p - a.p);
const lerp = (x, y, t) => Math.round(x + (y - x) * t);
const r = lerp(a.c[0], b.c[0], t);
const g = lerp(a.c[1], b.c[1], t);
const bcol = lerp(a.c[2], b.c[2], t);
return `rgba(${r},${g},${bcol},0.5)`;
]]]
name:
- font-size: "[[[ return window.innerWidth <= 600 ? '0.8rem' : '0.9rem' ]]]"
- font-weight: 600
- line-height: |
[[[
return window.innerWidth <= 600 ? "0.9" : "normal";
]]]
- color: |
[[[
const minTemp = -20;
const maxTemp = 60;
const temp = Number(entity.state);
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = isNaN(temp) ? 0 : clamp(((temp - minTemp) / (maxTemp - minTemp)) * 100, 0, 100);
const stops = [
{ p: 0, c: [0, 0, 255, 1.0] },
{ p: 40,c: [0, 165, 255, 1.0] },
{ p: 65, c: [0, 255, 0, 1.0] },
{ p: 75,c: [255, 255, 0, 1.0] },
{ p: 100, c: [255, 0, 0, 1.0] }
];
let i = 1;
while (i < stops.length && percent > stops[i].p) i++;
const a = stops[i - 1], b = stops[i];
const t = (percent - a.p) / (b.p - a.p);
const lerp = (x, y, t) => Math.round(x + (y - x) * t);
const r = lerp(a.c[0], b.c[0], t);
const g = lerp(a.c[1], b.c[1], t);
const bcol = lerp(a.c[2], b.c[2], t);
return `rgba(${r},${g},${bcol},1.0)`;
]]]
styles:
card:
- background-color: rgba(42,45,54,1.0)
- aspect-ratio: 7/1
- cursor: default
custom_fields:
icon:
- position: absolute
- top: 55%
- left: 4%
- transform: translate(-50%, -50%)
icon_border:
- position: absolute
- top: "[[[ return window.innerWidth <= 600 ? '56%' : '55%' ]]]"
- left: 4%
- transform: translate(-50%, -50%)
name:
- position: absolute
- top: "[[[ return window.innerWidth <= 600 ? '1%' : '3%' ]]]"
- left: 8%
value:
- position: absolute
- top: 0%
- right: 6.5%
- margin-top: "-0.5%"
unit:
- position: absolute
- right: 2%
- top: 1%
- margin-left: 1%
min_val:
- position: absolute
- bottom: 2%
- left: "[[[ return window.innerWidth <= 600 ? '34%' : '38%' ]]]"
max_val:
- position: absolute
- bottom: 2%
- right: "[[[ return window.innerWidth <= 600 ? '2%' : '4%' ]]]"
comment:
- position: absolute
- top: 55%
- left: |
[[[
const iconLeftPercent = 1;
const iconDiameterPx = window.innerWidth <= 600 ? 20 : 25;
const barWidthPx = window.innerWidth <= 600 ? 220 : 250;
const barRightPercent = window.innerWidth <= 600 ? 4 : 7;
const iconRightExpr = `calc(${iconLeftPercent}% + ${iconDiameterPx}px)`;
const barLeftExpr = `calc(100% - ${barRightPercent}% - ${barWidthPx}px)`;
return `calc( ( ${iconRightExpr} + ${barLeftExpr} ) / 2 )`;
]]]
- transform: translate(-50%, -50%)
bar:
- position: absolute
- right: "[[[ return window.innerWidth <= 600 ? '4%' : '7%' ]]]"
- top: 55%
- transform: translateY(-50%)
cursor:
- position: absolute
- top: "[[[ return 'calc(55% - 12.5px)'; ]]]"
- left: |
[[[
const value = Number(entity.state);
const min = -25;
const max = 60;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const t = clamp(value, min, max);
const barWidth = window.innerWidth <= 600 ? 220 : 250;
const halfCursor = 8;
const barRightPercent = window.innerWidth <= 600 ? 4 : 7;
const percent = ((t - min) / (max - min)) * 100;
const offsetPx = (barWidth * percent) / 100;
return `calc(100% - ${barRightPercent}% - ${barWidth}px + ${offsetPx}px - ${halfCursor}px)`;
]]]
Je me demandais s’il était possible de l’adapter au capteur de pression aussi et mettre à différents endroits de la barre une icône pluie, soleil etc… Comme les baromètres
il faut que je regarde ça et réussir sur mes températures à avoir le min et le max
Bonjour à toutes et à tous,
Ma très modeste contribution, à partir de ce que M. Button Card nous a gentiment fait dans le post en lien de ma réponse.
J’ai rajouté un graphique, sur 7 jours.
A noter aussi que j’ai retiré les évolutions de couleur sur le texte dans le commentaire en dessous. Car je trouvais que ça manquait souvent de contraste, j’ai donc mis en blanc.
Voici le résultat:
Et le code associé :
type: custom:button-card
entity: sensor.lywsd02_tempc
show_state: false
show_icon: false
show_name: false
tap_action: none
double_tap_action: none
hold_action: none
custom_fields:
icon_and_name:
card:
type: horizontal-stack
cards:
- type: custom:button-card
icon: mdi:thermometer
styles:
card:
- aspect-ratio: 1/1
- width: 40px
- padding: 0
- border: 4px solid rgba(167,176,205,1.0)
- border-radius: 50%
- background: none
- margin-left: 10px
- margin-top: 5px
icon:
- width: 90%
- color: white
- type: custom:button-card
name: Température Salon
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
- margin-top: 5px
name:
- font-size: 1.2rem
- justify-self: start
- text-align: left
- color: white
- font-weight: 500
- padding-top: "[[[ return window.innerWidth <= 600 ? '2%' : '6%' ]]]"
dividing_line:
card:
type: custom:button-card
styles:
card:
- width: 600px
- height: 1px
- padding: 0
- border: 1px solid rgba(54,56,68,1.0)
- border-radius: 0
- background: none
- overflow: hidden
mini_graph:
card:
type: custom:mini-graph-card
entities:
- entity: sensor.lywsd02_tempc
name: Salon
show_fill: false
- entity: sun.sun
color: gray
name: Sun
show_line: false
show_points: false
show_legend: false
y_axis: secondary
show:
name: false
icon: false
state: false
points: false
state_map:
- value: above_horizon
label: Day
- value: below_horizon
label: Night
hours_to_show: 168
points_per_hour: 0.5
color_thresholds:
- value: 14
color: "#0000FF"
- value: 16
color: "#00A5FF"
- value: 22
color: "#00FF00"
- value: 24
color: "#FFFF00"
- value: 26
color: "#FFA500"
- value: 30
color: "#FF0000"
card_mod:
style: |
ha-card {
--ha-card-background:
}
value_and_unit:
card:
type: horizontal-stack
cards:
- type: custom:button-card
show_name: false
show_icon: false
show_state: true
state_display: |
[[[
return parseFloat(entity.state).toFixed(1);
]]]
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
- margin-right: "-10%"
state:
- color: white
- font-size: 1.4rem
- font-weight: 800
- type: custom:button-card
name: °C
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
- margin-left: "-10%"
name:
- font-size: 1.0rem
- align-self: start
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 800
- padding-top: 0%
bar:
card:
type: custom:button-card
show_name: false
show_icon: false
show_state: false
styles:
card:
- height: 12px
- width: "[[[ return window.innerWidth <= 600 ? '300px' : '360px' ]]]"
- border-radius: 999px
- border: 0
- padding: 0px
- background: >-
linear-gradient(to right, rgba(0,0,255,1.0) 0%,
rgba(0,165,255,1.0) 25%, rgba(0,255,0,1.0) 50%,
rgba(255,255,0,1.0) 62.5%, rgba(255,165,0,1.0) 75%,
rgba(255,0,0,1.0) 100%)
cursor:
card:
type: custom:button-card
show_name: false
show_icon: false
show_state: false
styles:
card:
- width: 16px
- height: 32px
- border-radius: 999px
- border: 4px solid rgba(93,83,103,1.0)
- background-color: |
[[[
const minTemp = 14; // MIN CHANGÉ
const maxTemp = 30; // MAX CHANGÉ
const temp = Number(entity.state) || minTemp;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((temp - minTemp) / (maxTemp - minTemp)) * 100, 0, 100);
const stops = [
{ p: 0, c: [0, 0, 255] },
{ p: 25, c: [0, 165, 255] },
{ p: 50, c: [0, 255, 0] },
{ p: 62.5, c: [255, 255, 0] },
{ p: 75, c: [255, 165, 0] },
{ p: 100, c: [255, 0, 0] }
];
let i = 1;
while (i < stops.length && percent > stops[i].p) i++;
const a = stops[i - 1];
const b = stops[i];
const t = (percent - a.p) / (b.p - a.p);
const lerp = (x, y, t) => Math.round(x + (y - x) * t);
const r = lerp(a.c[0], b.c[0], t);
const g = lerp(a.c[1], b.c[1], t);
const bcol = lerp(a.c[2], b.c[2], t);
return `rgba(${r},${g},${bcol},1.0)`;
]]]
min_val:
card:
type: custom:button-card
name: 14°C
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: 1.0rem
- align-self: start
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 400
max_val:
card:
type: custom:button-card
name: 30°C
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: 1.0rem
- align-self: start
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 400
comment:
card:
type: custom:button-card
show_name: true
show_icon: false
show_state: false
name: |
[[[
const temp = Number(entity.state) || 0;
if (temp < 18) { // < 18°C
return "Trop froid";
}
if (temp >= 18 && temp < 20) {
return "Frais mais acceptable";
}
if (temp >= 20 && temp < 22) {
return "Confort idéal";
}
if (temp >= 22 && temp < 24) {
return "Légèrement chaud";
}
if (temp >= 24) {
return "Trop chaud";
}
return "Indisponible";
]]]
styles:
card:
- width: auto
- height: auto
- border-radius: 999px
- border: 0
- padding: 5px 10px 5px 10px
- background: white
- background-color: |
[[[
const minTemp = 14;
const maxTemp = 30;
const temp = Number(entity.state) || minTemp;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((temp - minTemp) / (maxTemp - minTemp)) * 100, 0, 100);
const stops = [
{ p: 0, c: [0, 0, 255] }, // 14°C
{ p: 25, c: [0, 165, 255] }, // 18°C
{ p: 50, c: [0, 255, 0] }, // 22°C
{ p: 62.5, c: [255, 255, 0] }, // 24°C
{ p: 75, c: [255, 165, 0] }, // 26°C
{ p: 100, c: [255, 0, 0] } // 30°C
];
let i = 1;
while (i < stops.length && percent > stops[i].p) i++;
const a = stops[i - 1];
const b = stops[i];
const t = (percent - a.p) / (b.p - a.p);
const lerp = (x, y, t) => Math.round(x + (y - x) * t);
const r = lerp(a.c[0], b.c[0], t);
const g = lerp(a.c[1], b.c[1], t);
const bcol = lerp(a.c[2], b.c[2], t);
return `rgba(${r},${g},${bcol},0.5)`;
]]]
name:
- font-size: 0.8rem
- font-weight: 600
- color: rgba(255,255,255,1.0)
min_temp_week:
card:
type: custom:button-card
icon: mdi:arrow-collapse-down
show_name: false
show_state: true
layout: icon_state
state_display: |
[[[
return parseFloat(states['sensor.temperature_min_7j'].state).toFixed(1) + "°";
]]]
styles:
card:
- width: 80px
- padding: 0
- border: none
- border-radius: 0
- background: none
state:
- font-size: 1.2rem
- align-self: end
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 600
icon:
- width: 20px
- color: rgba(167,176,205,1.0)
max_temp_week:
card:
type: custom:button-card
icon: mdi:arrow-collapse-up
show_name: false
show_state: true
layout: state_icon
state_display: |
[[[
return parseFloat(states['sensor.temperature_max_7j'].state).toFixed(1) + "°";
]]]
styles:
card:
- width: 80px
- padding: 0
- border: none
- border-radius: 0
- background: none
state:
- font-size: 1.2rem
- align-self: end
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 600
- padding-right: 100px
icon:
- width: 20px
- left: 12px
- color: rgba(167,176,205,1.0)
styles:
card:
- background-color: rgba(93,83,103,1.0)
- aspect-ratio: "[[[ return window.innerWidth <= 600 ? '1.4/1' : '1.7/1' ]]]"
- cursor: default
custom_fields:
icon_and_name:
- position: absolute
- top: 2%
- left: 1%
dividing_line:
- position: absolute
- top: 21%
- left: 0%
mini_graph:
- position: absolute
- top: 25%
- left: 50%
- transform: translate(-50%, 0)
- z-index: 2
- width: 90%
value_and_unit:
- position: absolute
- top: 62%
- left: |
[[[
const value = Number(entity.state) || 0;
const min = 14;
const max = 30;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((value - min) / (max - min)) * 100, 0, 100);
const barWidth = 360;
const halfCursor = 8;
const offsetPx = (barWidth * percent) / 100;
return `calc(50% - ${barWidth/2}px + ${offsetPx}px - ${halfCursor}px)`;
]]]
- transform: translateX(-50%)
bar:
- position: absolute
- left: 50%
- top: 78%
- transform: translate(-50%, -50%)
cursor:
- position: absolute
- top: "[[[ return 'calc(78% - 16px)'; ]]]"
- left: |
[[[
const value = Number(entity.state) || 0;
const min = 14;
const max = 30;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((value - min) / (max - min)) * 100, 0, 100);
const barWidth = 360;
const halfCursor = 8;
const offsetPx = (barWidth * percent) / 100;
return `calc(50% - ${barWidth/2}px + ${offsetPx}px - ${halfCursor}px)`;
]]]
min_val:
- position: absolute
- top: 84%
- left: "[[[ return window.innerWidth <= 600 ? '8%' : '12%' ]]]"
max_val:
- position: absolute
- top: 84%
- right: "[[[ return window.innerWidth <= 600 ? '7%' : '10%' ]]]"
comment:
- position: absolute
- left: 50%
- top: 92%
- transform: translate(-50%, -50%)
min_temp_week:
- position: absolute
- top: 65%
- left: 5%
max_temp_week:
- position: absolute
- top: 65%
- right: 2%
Ahhhhh, un nouvel adepte
Veux-tu être le petit gourou de cette nouvelle secte ?
Un nouvel adepte, je veux bien.
Par contre, tu es notre gourou à nous tous. Nous n’arrivons pas à ta cheville ô maitre !
Juste le petit gourou (il faudra que tu changes d’avatar : petit gourou, pas petit hibou), je me garde le rôle de grand gourou (gris… pas roux). Dès que j’aurai finis la carte « heures creuses » universelle, je me pencherais à nouveau sur cette série de carte pour la rendre universelle elle aussi.
Salut, @btncrd,
J’ai zappé une formation.
Ou trouve tu t’es logo m3of ?
C’est une intégration maison ?
Ton code pour l’indice Qualité de l’air c’est les même que Pollen ?
D’avance merci.
A installer via HACS ou en manuel :
Bonjour @romain4559
C’est issu d’une intégration non « maison » : GitHub - beecho01/material-symbols: Material Symbols for Home Assistant is a collection of 15,613 Google Material Symbols for use within Home Assistant. It uses the icon-set produced and maintained by iconify.
Lol, grillé par @xTG
Bonsoir,
si cela peut intéresser quelqu’un je me suis fait un streamline_templates qui reprend pas mal de choses de ce sujet.
Il prend en entrée soit un capteur d’humidité soit un capteur de température en se basant sur le device_class.
Le rendu graphique :
Je n’ai pas de capteur d’humidité extérieure, et je n’ai pas encore créé mes capteurs de température min/max sur la journée, d’où le mix entre les deux pour la capture. ![]()
Pas de couleurs pour le mini-graph car il ne semble pas possible d’injecter un threshold variable (ou alors des trucs semi-dynamique à base de !secrets mais je ne trouvais pas cela joli…).
Donc je ne pouvais pas différentier une courbe de température d’une courbe d’humidité.
Le template utilise l’entité sun.sun, à noter aussi que j’ai inversé le jour et la nuit par rapport à ce que Tank proposait.
Je trouve plus logique de voir la nuit à « zéro » plutôt que le jour.
Code des cartes :
type: custom:streamline-card
template: jauge
variables:
entity: sensor.temperature_exterieure
name: Température extérieure
entity_variable_min: sensor.temperature_min_du_jour
entity_variable_max: sensor.temperature_max_du_jour
type: custom:streamline-card
template: jauge
variables:
entity: sensor.climacontrol_living_room_sensor_humidity
name: Humidité intérieure
entity_variable_min: null
entity_variable_max: null
Le template :
streamline_templates:
jauge:
card:
type: custom:button-card
entity: '[[entity]]'
show_state: false
show_icon: false
show_name: false
tap_action: none
double_tap_action: none
hold_action: none
custom_fields:
icon_and_name:
card:
type: horizontal-stack
cards:
- type: custom:button-card
icon: |
[[[
if( entity.attributes.device_class == "temperature" )
return "mdi:thermometer";
else if( entity.attributes.device_class == "humidity" )
return "mdi:water"
else
return "mdi:alert-circle";
]]]
styles:
card:
- aspect-ratio: 1/1
- width: 40px
- padding: 0
- border: 4px solid rgba(167,176,205,1.0)
- border-radius: 50%
- background: none
icon:
- width: 90%
- color: white
- type: custom:button-card
name: |
[[[
return "[[name]]";
]]]
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: 1.4rem
- justify-self: start
- text-align: left
- color: white
- font-weight: 500
- padding-top: '[[[ return window.innerWidth <= 600 ? ''2%'' : ''6%'' ]]]'
dividing_line:
card:
type: custom:button-card
styles:
card:
- width: 600px
- height: 1px
- padding: 0
- border: 1px solid rgba(54,56,68,1.0)
- border-radius: 0
- background: none
- overflow: hidden
mini_graph:
card:
type: custom:mini-graph-card
entities:
- entity: '[[entity]]'
show_fill: false
color: darkgrey
- entity: sun.sun
color: grey
show_line: false
show_points: false
show_legend: false
y_axis: secondary
show:
name: false
icon: false
state: false
points: false
labels: false
labels_secondary: false
state_map:
- value: below_horizon
label: Night
- value: above_horizon
label: Day
hours_to_show: 168
points_per_hour: 0.5
card_mod:
style: |
ha-card {
--ha-card-background:
}
value_and_unit:
card:
type: horizontal-stack
cards:
- type: custom:button-card
show_name: false
show_icon: false
show_state: true
state_display: |
[[[
const status = entity.state;
return parseFloat(status).toFixed(1);
]]]
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
- margin-right: '-10%'
state:
- color: white
- font-size: 1.8rem
- font-weight: 800
- type: custom:button-card
name: |
[[[
if( entity.attributes.device_class == "temperature" )
return "°C";
else if( entity.attributes.device_class == "humidity" )
return '%';
else
return '';
]]]
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
- margin-left: '-10%'
name:
- font-size: 1.2rem
- align-self: start
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 800
- padding-top: 40%
bar:
card:
type: custom:button-card
show_name: false
show_icon: false
show_state: false
styles:
card:
- height: 12px
- width: '[[[ return window.innerWidth <= 600 ? ''300px'' : ''360px'' ]]]'
- border-radius: 999px
- border: 0
- padding: 0px
- background: |
[[[
if( entity.attributes.device_class == "temperature" )
return 'linear-gradient(to right,rgba(0,0,255,1.0) 0%,rgba(0,165,255,1.0) 25%,rgba(0,255,0,1.0) 50%,rgba(255,255,0,1.0) 75%,rgba(255,0,0,1.0) 100%)';
else if( entity.attributes.device_class == "humidity" )
return 'linear-gradient(to right, rgba(255,0,0,1.0) 0%,rgba(255,165,0,1.0) 10%, rgba(255,255,0,1.0) 30%,rgba(0,255,0,1.0) 50%, rgba(255,255,0,1.0) 70%,rgba(255,165,0,1.0) 90%, rgba(255,0,0,1.0) 100%)';
else
return 'none';
]]]
cursor:
card:
type: custom:button-card
show_name: false
show_icon: false
show_state: false
styles:
card:
- width: 16px
- height: 32px
- border-radius: 999px
- border: 4px solid rgba(42,45,54,1.0)
- background-color: |
[[[
var stops = [];
var minVal = 0;
var maxVal = 1;
if( entity.attributes.device_class == "temperature" )
{
minVal = -15;
maxVal = 45;
stops = [
{ p: 0, c: [0, 0, 255] },
{ p: 25, c: [0, 165, 255] },
{ p: 50, c: [0, 255, 0] },
{ p: 75, c: [255, 255, 0] },
{ p: 100, c: [255, 0, 0] }
];
}
else if( entity.attributes.device_class == "humidity" )
{
minVal = 0;
maxVal = 100;
stops = [
{ p: 0, c: [255, 0, 0] },
{ p: 30, c: [255, 165, 0] },
{ p: 40, c: [255, 255, 0] },
{ p: 50, c: [0, 255, 0] },
{ p: 60, c: [255, 255, 0] },
{ p: 70, c: [255, 165, 0] },
{ p: 100, c: [255, 0, 0] }
];
}
else
{
stops = [
{ p: 0, c: [255, 0, 0] },
{ p: 100, c: [255, 0, 0] }
];
}
const s = entity.state;
const temp = Number(s);
const value = isNaN(temp) ? minVal : temp;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((value - minVal) / (maxVal - minVal)) * 100, 0, 100);
let i = 1;
while (i < stops.length && percent > stops[i].p) i++;
const a = stops[i - 1];
const b = stops[i];
const t = (percent - a.p) / (b.p - a.p);
const lerp = (x, y, t) => Math.round(x + (y - x) * t);
const r = lerp(a.c[0], b.c[0], t);
const g = lerp(a.c[1], b.c[1], t);
const bcol = lerp(a.c[2], b.c[2], t);
return `rgba(${r},${g},${bcol},1.0)`;
]]]
min_val:
card:
type: custom:button-card
name: |
[[[
if( entity.attributes.device_class == "temperature" )
return "-15°C";
else if( entity.attributes.device_class == "humidity" )
return "0%";
else
return '';
]]]
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: 1.0rem
- align-self: start
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 400
max_val:
card:
type: custom:button-card
name: |
[[[
if( entity.attributes.device_class == "temperature" )
return "45°C";
else if( entity.attributes.device_class == "humidity" )
return "100%";
else
return '';
]]]
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: 1.0rem
- align-self: start
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 400
comment:
card:
type: custom:button-card
show_name: true
show_icon: false
show_state: false
name: |
[[[
const s = entity.state;
const value = Number(s);
if( entity.attributes.device_class == "temperature" )
{
if (value < -5) return "Froid extrême";
if (value >= -5 && value < 0) return "Très froid";
if (value >= 0 && value < 10) return "Frais";
if (value >= 10 && value < 20) return "Confortable";
if (value >= 20 && value < 30) return "Chaud";
if (value >= 30 && value < 40) return "Très chaud";
if (value >= 40) return "Chaleur extrême";
}
else if( entity.attributes.device_class == "humidity" )
{
if (value < 30) return "Trop sec";
if (value >= 30 && value < 40) return "Sec mais acceptable";
if (value >= 40 && value < 60) return "Zone idéale de confort";
if (value >= 60 && value < 70) return "Humide mais tolérable";
if (value >= 70) return "Trop humide";
}
else
return '';
]]]
styles:
card:
- width: auto
- height: auto
- border-radius: 999px
- border: 0
- padding: 5px 10px 5px 10px
- background-color: |
[[[
var stops = [];
var minVal = 0;
var maxVal = 1;
if( entity.attributes.device_class == "temperature" )
{
minVal = -15;
maxVal = 45;
stops = [
{ p: 0, c: [0, 0, 255] },
{ p: 25, c: [0, 165, 255] },
{ p: 50, c: [0, 255, 0] },
{ p: 75, c: [255, 255, 0] },
{ p: 100, c: [255, 0, 0] }
];
}
else if( entity.attributes.device_class == "humidity" )
{
minVal = 0;
maxVal = 100;
stops = [
{ p: 0, c: [255, 0, 0] },
{ p: 30, c: [255, 165, 0] },
{ p: 40, c: [255, 255, 0] },
{ p: 50, c: [0, 255, 0] },
{ p: 60, c: [255, 255, 0] },
{ p: 70, c: [255, 165, 0] },
{ p: 100, c: [255, 0, 0] }
];
}
else
{
stops = [
{ p: 0, c: [255, 0, 0] },
{ p: 100, c: [255, 0, 0] }
];
}
const s = entity.state;
const temp = Number(s);
const value = isNaN(temp) ? minVal : temp;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((value - minVal) / (maxVal - minVal)) * 100, 0, 100);
let i = 1;
while (i < stops.length && percent > stops[i].p) i++;
const a = stops[i - 1];
const b = stops[i];
const t = (percent - a.p) / (b.p - a.p);
const lerp = (x, y, t) => Math.round(x + (y - x) * t);
const r = lerp(a.c[0], b.c[0], t);
const g = lerp(a.c[1], b.c[1], t);
const bcol = lerp(a.c[2], b.c[2], t);
return `rgba(${r},${g},${bcol},0.5)`;
]]]
name:
- font-size: 1.0rem
- font-weight: 600
- color: |
[[[
var stops = [];
var minVal = 0;
var maxVal = 1;
if( entity.attributes.device_class == "temperature" )
{
minVal = -15;
maxVal = 45;
stops = [
{ p: 0, c: [0, 0, 255] },
{ p: 25, c: [0, 165, 255] },
{ p: 50, c: [0, 255, 0] },
{ p: 75, c: [255, 255, 0] },
{ p: 100, c: [255, 0, 0] }
];
}
else if( entity.attributes.device_class == "humidity" )
{
minVal = 0;
maxVal = 100;
stops = [
{ p: 0, c: [255, 0, 0] },
{ p: 30, c: [255, 165, 0] },
{ p: 40, c: [255, 255, 0] },
{ p: 50, c: [0, 255, 0] },
{ p: 60, c: [255, 255, 0] },
{ p: 70, c: [255, 165, 0] },
{ p: 100, c: [255, 0, 0] }
];
}
else
{
stops = [
{ p: 0, c: [255, 0, 0] },
{ p: 100, c: [255, 0, 0] }
];
}
const s = entity.state;
const temp = Number(s);
const value = isNaN(temp) ? minVal : temp;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((value - minVal) / (maxVal - minVal)) * 100, 0, 100);
let i = 1;
while (i < stops.length && percent > stops[i].p) i++;
const a = stops[i - 1];
const b = stops[i];
const t = (percent - a.p) / (b.p - a.p);
const lerp = (x, y, t) => Math.round(x + (y - x) * t);
const r = lerp(a.c[0], b.c[0], t);
const g = lerp(a.c[1], b.c[1], t);
const bcol = lerp(a.c[2], b.c[2], t);
return `rgba(${r},${g},${bcol},1.0)`;
]]]
min_hist_value:
card:
type: custom:button-card
icon: |
[[[
const s = states['[[entity_variable_min]]'];
if( s === undefined )
return '';
return 'mdi:arrow-collapse-down';
]]]
show_name: false
show_state: true
layout: icon_state
state_display: |
[[[
const s = states['[[entity_variable_min]]'];
if( s === undefined )
return '';
if( entity.attributes.device_class == "temperature" )
return parseFloat(s.state).toFixed(1) + "°C";
else if( entity.attributes.device_class == "humidity" )
return parseFloat(s.state).toFixed(1) + "%";
else
return parseFloat(s.state).toFixed(1);
]]]
styles:
card:
- width: 80px
- padding: 0
- border: none
- border-radius: 0
- background: none
state:
- font-size: 1.2rem
- align-self: end
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 600
icon:
- width: 20px
- color: rgba(167,176,205,1.0)
max_hist_value:
card:
type: custom:button-card
icon: |
[[[
const s = states['[[entity_variable_max]]'];
if( s === undefined )
return '';
return 'mdi:arrow-collapse-up';
]]]
show_name: false
show_state: true
layout: state_icon
state_display: |
[[[
const s = states['[[entity_variable_max]]'];
if( s === undefined )
return '';
if( entity.attributes.device_class == "temperature" )
return parseFloat(s.state).toFixed(1) + "°C";
else if( entity.attributes.device_class == "humidity" )
return parseFloat(s.state).toFixed(1) + "%";
else
return parseFloat(s.state).toFixed(1);
]]]
styles:
card:
- width: 80px
- padding: 0
- border: none
- border-radius: 0
- background: none
state:
- font-size: 1.2rem
- align-self: end
- justify-self: start
- color: rgba(167,176,205,1.0)
- font-weight: 600
- padding-right: 130px
icon:
- width: 20px
- color: rgba(167,176,205,1.0)
styles:
card:
- background-color: rgba(42,45,54,1.0)
- margin-bottom: 5px
- aspect-ratio: '[[[ return window.innerWidth <= 600 ? ''1.6/1'' : ''1.9/1'' ]]]'
- cursor: default
custom_fields:
icon_and_name:
- position: absolute
- top: 2%
- left: 1%
dividing_line:
- position: absolute
- top: 20%
- left: 0%
mini_graph:
- position: absolute
- top: 23%
- left: 50%
- transform: translate(-50%, 0)
- z-index: 2
- width: 90%
value_and_unit:
- position: absolute
- top: 65%
- left: |
[[[
var min = 0
var max = 1
if( entity.attributes.device_class == "temperature" )
{
min = -15
max = 45
}
else if( entity.attributes.device_class == "humidity" )
{
min = 0
max = 100
}
const s = entity.state;
const value = isNaN(Number(s)) ? min : Number(s);
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((value - min) / (max - min)) * 100, 0, 100);
const barWidth = (window.innerWidth <= 600) ? 300 : 360;
const halfCursor = 8;
const offsetPx = (barWidth * percent) / 100;
return `calc(50% - ${barWidth/2}px + ${offsetPx}px - ${halfCursor}px)`;
]]]
- transform: translateX(-50%)
bar:
- position: absolute
- left: 50%
- top: 82%
- transform: translate(-50%, -50%)
cursor:
- position: absolute
- top: '[[[ return ''calc(82% - 16px)'' ]]]'
- left: |
[[[
var min = 0
var max = 1
if( entity.attributes.device_class == "temperature" )
{
min = -15
max = 45
}
else if( entity.attributes.device_class == "humidity" )
{
min = 0
max = 100
}
const s = entity.state;
const value = isNaN(Number(s)) ? min : Number(s);
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((value - min) / (max - min)) * 100, 0, 100);
const barWidth = (window.innerWidth <= 600) ? 300 : 360;
const halfCursor = 8;
const offsetPx = (barWidth * percent) / 100;
return `calc(50% - ${barWidth/2}px + ${offsetPx}px - ${halfCursor}px)`;
]]]
min_val:
- position: absolute
- bottom: 8%
- left: '[[[ return window.innerWidth <= 600 ? ''8%'' : ''12%'' ]]]'
max_val:
- position: absolute
- bottom: 8%
- right: '[[[ return window.innerWidth <= 600 ? ''7%'' : ''10%'' ]]]'
comment:
- position: absolute
- left: 50%
- bottom: '-3%'
- transform: translate(-50%, -50%)
min_hist_value:
- position: absolute
- top: 67%
- left: 5%
max_hist_value:
- position: absolute
- top: 67%
- right: 2%
Merci @xTG !
Adoptée ^^ en conservant la partie color threshold en ce qui me concerne
Whooooo, je vais finir par demander des royalties ![]()


