Une petite évolution des cartes : la valeur et l’unité se déplacent avec le curseur, ajout de la valeur min. et de la valeur max. relevées sur la semaine pour la carte température.
Code de la carte « Température intérieure » :
type: custom:button-card
entity: sensor.salle_a_manger_climatisation_ble_temp
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: 50px
- 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: Température intérieure - Salle à manger
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: 1.4rem
- justify-self: start
- color: white
- font-weight: 500
- padding-top: 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
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.8rem
- 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.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: 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(42,45,54,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: 1.0rem
- font-weight: 600
- 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] },
{ 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_temp_week:
card:
type: custom:button-card
icon: m3of:vertical-align-bottom
show_name: false
show_state: true
layout: icon_state
state_display: |
[[[
return parseFloat(states['sensor.salle_a_manger_temperature_min_semaine'].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: m3of:vertical-align-top
show_name: false
show_state: true
layout: state_icon
state_display: |
[[[
return parseFloat(states['sensor.salle_a_manger_temperature_max_semaine'].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
- color: rgba(167,176,205,1.0)
styles:
card:
- background-color: rgba(42,45,54,1.0)
- aspect-ratio: 2.8/1
- cursor: default
custom_fields:
icon_and_name:
- position: absolute
- top: 2%
- left: 1%
dividing_line:
- position: absolute
- top: 35%
- left: 0%
value_and_unit:
- position: absolute
- top: 38%
- 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: 65%
- transform: translate(-50%, -50%)
cursor:
- position: absolute
- top: "[[[ return 'calc(65% - 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
- bottom: 17%
- left: 12%
max_val:
- position: absolute
- bottom: 17%
- right: 10%
comment:
- position: absolute
- left: 50%
- bottom: "-3%"
- transform: translate(-50%, -50%)
min_temp_week:
- position: absolute
- top: 40%
- left: 5%
max_temp_week:
- position: absolute
- top: 40%
- right: 2%
Code de la carte « Niveau de CO2 » :
type: custom:button-card
entity: input_number.ppm
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:molecule-co2
styles:
card:
- aspect-ratio: 1/1
- width: 50px
- 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: Niveau de CO<sub style="font-size:1.0rem;">2</sub> intérieur
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: 1.4rem
- justify-self: start
- color: white
- font-weight: 500
- padding-top: 10%
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
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(0);
]]]
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: ppm
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: 20%
bar:
card:
type: custom:button-card
show_name: false
show_icon: false
show_state: false
styles:
card:
- height: 12px
- width: 360px
- border-radius: 999px
- border: 0
- padding: 0px
- background: >-
linear-gradient(to right, rgba(0,255,0,1.0) 0%,
rgba(165,255,0,1.0) 35%, rgba(255,255,0,1.0) 55%,
rgba(255,165,0,1.0) 85%, 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(42,45,54,1.0)
- background-color: |
[[[
const ppm = Number(entity.state) || 0;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((ppm - 400) / 1600) * 100, 0, 100);
const stops = [
{ p: 0, c: [0, 255, 0] },
{ p: 40, c: [165, 255, 0] },
{ p: 55, 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: "400"
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: "2000"
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 ppm = Number(entity.state) || 0;
if (ppm < 900) {
return "Excellent";
}
if (ppm >= 900 && ppm <= 1150) {
return "Bon";
}
if (ppm >= 1151 && ppm <= 1400) {
return "Acceptable";
}
if (ppm >= 1401 && ppm <= 1600) {
return "Mauvais";
}
if (ppm > 1600) {
return "Malsain";
}
]]]
styles:
card:
- width: auto
- height: auto
- border-radius: 999px
- border: 0
- padding: 5px 10px 5px 10px
- background: white
- background-color: |
[[[
const ppm = Number(entity.state) || 0;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((ppm - 400) / 1600) * 100, 0, 100);
const stops = [
{ p: 0, c: [0, 255, 0] },
{ p: 40, c: [165, 255, 0] },
{ p: 55, 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},0.5)`;
]]]
name:
- font-size: 1.0rem
- font-weight: 600
- color: |
[[[
const ppm = Number(entity.state) || 0;
if (ppm < 900) {
return "rgba(0,255,0,1.0)";
}
if (ppm >= 900 && ppm <= 1150) {
return "rgba(165,255,0,1.0)";
}
if (ppm >= 1151 && ppm <= 1400) {
return "rgba(255,255,0,1.0)";
}
if (ppm >= 1401 && ppm <= 1600) {
return "rgba(255,165,0,1.0)";
}
if (ppm > 1600) {
return "rgba(255,0,0,1.0)";
}
]]]
styles:
card:
- background-color: rgba(42,45,54,1.0)
- aspect-ratio: 2.8/1
- cursor: default
custom_fields:
icon_and_name:
- position: absolute
- top: 2%
- left: 1%
dividing_line:
- position: absolute
- top: 35%
- left: 0%
value_and_unit:
- position: absolute
- top: 38%
- left: |
[[[
const ppm = Number(entity.state) || 0;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((ppm - 400) / 1600) * 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: 65%
- transform: translate(-50%, -50%)
cursor:
- position: absolute
- top: "[[[ return 'calc(65% - 16px)'; ]]]"
- left: |
[[[
const ppm = Number(entity.state) || 0;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((ppm - 400) / 1600) * 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
- bottom: 17%
- left: 10%
max_val:
- position: absolute
- bottom: 17%
- right: 9%
comment:
- position: absolute
- left: 50%
- bottom: "-3%"
- transform: translate(-50%, -50%)
Code de la carte « Humidité intérieure » :
type: custom:button-card
entity: sensor.salle_a_manger_climatisation_ble_humidity
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:water
styles:
card:
- aspect-ratio: 1/1
- width: 50px
- 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: Humidité intérieure - Salle à manger
styles:
card:
- width: auto
- padding: 0
- border: none
- border-radius: 0
- background: none
name:
- font-size: 1.4rem
- justify-self: start
- color: white
- font-weight: 500
- padding-top: 7%
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
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.8rem
- font-weight: 800
- type: custom:button-card
name: "%"
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: 50%
bar:
card:
type: custom:button-card
show_name: false
show_icon: false
show_state: false
styles:
card:
- height: 12px
- width: 360px
- border-radius: 999px
- border: 0
- padding: 0px
- background: >-
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%)
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: |
[[[
const humidity = Number(entity.state) || 0;
const minHumidity = 0;
const maxHumidity = 100;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((humidity - minHumidity) / (maxHumidity - minHumidity)) * 100, 0, 100);
const 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] }
];
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: "0"
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: "100"
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 humidity = Number(entity.state) || 0;
if (humidity < 30) {
return "Trop sec";
}
if (humidity >= 30 && humidity < 40) {
return "Sec mais acceptable";
}
if (humidity >= 40 && humidity < 60) {
return "Zone idéale de confort";
}
if (humidity >= 60 && humidity < 70) {
return "Humide mais tolérable";
}
if (humidity >= 70) {
return "Trop humide";
}
]]]
styles:
card:
- width: auto
- height: auto
- border-radius: 999px
- border: 0
- padding: 5px 10px 5px 10px
- background: white
- background-color: |
[[[
const humidity = Number(entity.state) || 0;
const minHumidity = 0;
const maxHumidity = 100;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((humidity - minHumidity) / (maxHumidity - minHumidity)) * 100, 0, 100);
const 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] }
];
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: |
[[[
const humidity = Number(entity.state) || 0;
const minHumidity = 0;
const maxHumidity = 100;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
const percent = clamp(((humidity - minHumidity) / (maxHumidity - minHumidity)) * 100, 0, 100);
const 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] }
];
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)`;
]]]
styles:
card:
- background-color: rgba(42,45,54,1.0)
- aspect-ratio: 2.8/1
- cursor: default
custom_fields:
icon_and_name:
- position: absolute
- top: 2%
- left: 1%
dividing_line:
- position: absolute
- top: 35%
- left: 0%
value_and_unit:
- position: absolute
- top: 38%
- left: |
[[[
const value = Number(entity.state) || 0;
const min = 0;
const max = 100;
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: 65%
- transform: translate(-50%, -50%)
cursor:
- position: absolute
- top: "[[[ return 'calc(65% - 16px)'; ]]]"
- left: |
[[[
const value = Number(entity.state) || 0;
const min = 0;
const max = 100;
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
- bottom: 17%
- left: 12%
max_val:
- position: absolute
- bottom: 17%
- right: 10%
comment:
- position: absolute
- left: 50%
- bottom: "-3%"
- transform: translate(-50%, -50%)