Faire des jauges de ce type

Ah, j’ai testé avec un number test ça me semblait ok mais du coup tu peux m’en dire plus, là malheureusement c’est au dessus de mes compétences actuelles

Teste ça :

type: custom:button-card
entity: input_number.test_temp
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: 25px
          - padding: 0
          - border: 1px solid white
          - border-radius: 50%
          - background: none
        icon:
          - width: 90%
          - color: white
  name:
    card:
      type: custom:button-card
      name: Netatmo Salon Température
      styles:
        card:
          - width: auto
          - padding: 0
          - border: none
          - border-radius: 0
          - background: none
        name:
          - font-size: 1rem
          - 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: 250px
          - border-radius: 999px
          - border: 0
          - padding: 0
          - background: |-
              linear-gradient(to right,
                rgba(255,0,0,1.0) 0%,
                rgba(255,255,0,1.0) 12.5%,
                rgba(0,255,0,1.0) 50%,
                rgba(255,255,0,1.0) 62.5%,
                rgba(255,120,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 = 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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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: 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: 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: 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);
          if (isNaN(temp)) return "Indisponible";
          if (temp < 14) return "En dehors de l'échelle";
          if (temp >= 14 && temp < 16) return "Froid";
          if (temp >= 16 && temp < 18.5) return "Frais";
          if (temp >= 18.5 && temp <= 22) return "Confortable";
          if (temp > 22 && temp <= 24) return "Tiède";
          if (temp > 24 && temp < 27) return "Chaud";
          if (temp >= 27 && temp <= 30) return "Canicule";
          if (temp > 30) return "En dehors de l'échelle";
          return "Indisponible";
        ]]]
      styles:
        card:
          - height: 25px
          - width: auto
          - border-radius: 999px
          - border: 0
          - padding: 8px
          - background: white
          - background-color: |
              [[[ 
                const minTemp = 14;
                const maxTemp = 30;
                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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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: 0.9rem
          - font-weight: 600
          - color: |
              [[[ 
                const minTemp = 14;
                const maxTemp = 30;
                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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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: 37%
      - left: 1%
    name:
      - position: absolute
      - top: 3%
      - left: 8%
    separation:
      - position: absolute
      - top: 17%
      - left: 0%
    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: 38%
    max_val:
      - position: absolute
      - bottom: 2%
      - right: 4%
    comment:
      - position: absolute
      - left: 17%
      - top: 55%
      - transform: translate(-50%, -50%)
    bar:
      - position: absolute
      - right: 7%
      - top: 55%
      - transform: translateY(-50%)
    cursor:
      - position: absolute
      - top: "[[[ return 'calc(55% - 12.5px)'; ]]]"
      - left: |
          [[[ 
            const value = Number(entity.state);
            const min = 14;
            const max = 30;
            const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
            const t = clamp(value, min, max);
            const percent = (t - min) * 6.25;
            const barWidth = 250;
            const halfCursor = 8;
            const barRightPercent = 7;
            const offsetPx = (barWidth * percent) / 100;
            return `calc(100% - ${barRightPercent}% - ${barWidth}px + ${offsetPx}px - ${halfCursor}px)`;
          ]]]

J’ai un décalage de barre et c’est rigolo, l’icône est devenue plus grosse, je t’ai mis celle avec mon code précédent au dessus pour voir, mais je regarde ton code et je crois comprendre, je vais essayer de me l’appliquer

J’ai travaillé sur l’image postée plus haut (probablement depuis ton PC) du coup ça ne colle plus sur téléphone portable. J’ai grossi l’icône pour qu’elle ait la même hauteur que « comment », j’ai réduit la taille de police de « value » et « unit » pour que le curseur n’en efface pas une partie quand la température est elevée.
Voilà le rendu sur PC (j’ai ajouté par rapport au code que je t’ai mis plus haut le déplacement dynamique de « comment » pour qu’il soit centré entre « icon » et « bar ») :
image

1 « J'aime »

Ah ok, moi en effet je regarde sur mon portable, ça marche, je vais adapter :slight_smile:

Je vais te faire une version responsive qui adaptera l’affichage en fonction de la largeur de l’écran sur lequel est affichée la carte

Voici ce que je viens de faire à partir de ton code que j’aime bien.

Ça ne me dérange pas que le curseur passe en dessous sur la fin, c’est pour ça que j’avais réorganisé la position des éléments dans ton code de départ en comprenant l’ordre de superposition

type: custom:button-card
entity: input_number.test
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: 20px
          - padding: 0
          - border: 1px solid white
          - border-radius: 50%
          - background: none
        icon:
          - width: 90%
          - color: white
  name:
    card:
      type: custom:button-card
      name: Netatmo Salon Température
      styles:
        card:
          - width: 300px
          - padding: 0
          - border: none
          - border-radius: 0
          - background: none
        name:
          - font-size: 1rem
          - align-self: start
          - justify-self: start
          - color: white
          - font-weight: 500
  separation:
    card:
      type: custom:button-card
      styles:
        card:
          - width: 600px
          - height: 0px
          - padding: 0
          - border: 0px solid transparent
          - border-radius: 0
          - background: none
  bar:
    card:
      type: custom:button-card
      show_name: false
      show_icon: false
      show_state: false
      styles:
        card:
          - height: 6px
          - width: 250px
          - border-radius: 999px
          - border: 0
          - padding: 0
          - background: |-
              linear-gradient(to right,
                rgba(255,0,0,1.0) 0%,           /* 14°C rouge */
                rgba(255,255,0,1.0) 12.5%,      /* 16°C jaune */
                rgba(200,255,120,1.0) 21.875%,  /* 17.5°C jaune-vert */
                rgba(0,255,0,1.0) 28.125%,      /* 18.5°C début vert */
                rgba(0,255,0,1.0) 50%,          /* 22°C vert saturé */
                rgba(180,255,100,1.0) 56.25%,   /* 23°C vert-jaune */
                rgba(255,255,0,1.0) 62.5%,      /* 24°C jaune */
                rgba(255,210,0,1.0) 68.75%,     /* 25°C jaune-orangé */
                rgba(255,120,0,1.0) 75%,        /* 26°C orange-rouge (début rouge) */
                rgba(255,60,0,1.0) 87.5%,       /* 28°C rouge vif */
                rgba(255,0,0,1.0) 100%)         /* 30°C rouge saturé */
  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 = 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: [255, 0, 0] },     // 14
                  { p: 12.5,   c: [255, 255, 0] },   // 16
                  { p: 21.875, c: [200, 255, 120] }, // 17.5
                  { p: 28.125, c: [0, 255, 0] },     // 18.5
                  { p: 50,     c: [0, 255, 0] },     // 22
                  { p: 56.25,  c: [180, 255, 100] }, // 23
                  { p: 62.5,   c: [255, 255, 0] },   // 24
                  { p: 68.75,  c: [255, 210, 0] },   // 25 (jaune-orangé)
                  { p: 75,     c: [255, 120, 0] },   // 26 (orange-rouge)
                  { p: 87.5,   c: [255, 60, 0] },    // 28 (rouge vif)
                  { p: 100,    c: [255, 0, 0] }      // 30 (rouge saturé)
                ];
                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);
          if (isNaN(v)) return '—';
          return v.toFixed(1);
        ]]]
      styles:
        card:
          - width: auto
          - padding: 0
          - border: none
          - border-radius: 0
          - background: none
        state:
          - color: white
          - font-size: 2rem
          - 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: 1.4rem
          - align-self: start
          - justify-self: start
          - color: rgba(167,176,205,1.0)
          - font-weight: 800
  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);
          if (isNaN(temp)) return "Indisponible";
          if (temp < 14) { return "En dehors de l'échelle"; }
          if (temp >= 14 && temp <= 15) { return "Froid"; }
          if (temp >= 16 && temp < 18.5) { return "Frais"; }
          if (temp >= 18.5 && temp <= 22) { return "Confortable"; }
          if (temp > 22 && temp <= 24) { return "Tiède"; }
          if (temp > 24 && temp < 27) { return "Chaud"; }
          if (temp >= 27) { return "Canicule"; }
          return "Indisponible";
        ]]]
      styles:
        card:
          - height: 8px
          - width: auto
          - border-radius: 999px
          - border: 0
          - padding: 8px
          - background: white
          - background-color: |
              [[[
                const minTemp = 14;
                const maxTemp = 30;
                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: [255, 0, 0] },
                  { p: 12.5,   c: [255, 255, 0] },
                  { p: 21.875, c: [200, 255, 120] },
                  { p: 28.125, c: [0, 255, 0] },
                  { p: 50,     c: [0, 255, 0] },
                  { p: 56.25,  c: [180, 255, 100] },
                  { p: 62.5,   c: [255, 255, 0] },
                  { p: 68.75,  c: [255, 210, 0] },
                  { p: 75,     c: [255, 120, 0] },
                  { p: 87.5,   c: [255, 60, 0] },
                  { p: 100,    c: [255, 0, 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: 0.9rem
          - font-weight: 600
          - color: |
              [[[
                const minTemp = 14;
                const maxTemp = 30;
                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: [255, 0, 0] },
                  { p: 12.5,   c: [255, 255, 0] },
                  { p: 21.875, c: [200, 255, 120] },
                  { p: 28.125, c: [0, 255, 0] },
                  { p: 50,     c: [0, 255, 0] },
                  { p: 56.25,  c: [180, 255, 100] },
                  { p: 62.5,   c: [255, 255, 0] },
                  { p: 68.75,  c: [255, 210, 0] },
                  { p: 75,     c: [255, 120, 0] },
                  { p: 87.5,   c: [255, 60, 0] },
                  { p: 100,    c: [255, 0, 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: 30%
      - left: 1%
    name:
      - position: absolute
      - top: 3%
      - left: 8%
    separation:
      - position: absolute
      - top: 17%
      - left: 0%
    value:
      - position: absolute
      - top: 26%
      - right: 6.5%
      - margin-right: 1%
      - transform: translateY(-50%)
    unit:
      - position: absolute
      - left: 92%
      - top: 20%
      - margin-left: 1%
      - transform: translateY(-50%)
    min_val:
      - position: absolute
      - top: 65%
      - left: 28%
    max_val:
      - position: absolute
      - top: 65%
      - right: 1%
    comment:
      - position: absolute
      - left: 17%
      - top: 65%
      - transform: translate(-50%, -50%)
    bar:
      - position: absolute
      - right: 3.5%
      - top: 55%
      - transform: translateY(-50%)
    cursor:
      - position: absolute
      - top: "[[[ return 'calc(55% - 12.5px)'; ]]]"
      - left: |
          [[[ 
            const value = Number(entity.state);
            const min = 14;
            const max = 30;
            const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
            const t = clamp(value, min, max);
            const percent = (t - min) * 6.25;
            const barWidth = 250;
            const halfCursor = 8;
            const barRightPercent = 3.5;
            const offsetPx = (barWidth * percent) / 100;
            return `calc(100% - ${barRightPercent}% - ${barWidth}px + ${offsetPx}px - ${halfCursor}px)`;
          ]]]

Voici tout en bas l’aperçu du code ci-dessus sur mon téléphone

J’avais aligné verticalement l’icône, le « comment » et la barre.
Voici ce que ça donne sur PC avec le « comment » centré entre l’icône et la barre :
image
image
Je te fais une version responsive (cet affichage sur PC) et l’affichage retravaillé sur téléphone portable. Je t’envoie le code dans la matinée.

1 « J'aime »

Impressionnant, merci, je regarderai dans la journée ou ce soir

Affichage PC :
image
image
image
image

Affichage téléphone portable :




Le code :

type: custom:button-card
entity: input_number.test_temp
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 Salon 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(255,0,0,1.0) 0%,
                rgba(255,255,0,1.0) 12.5%,
                rgba(0,255,0,1.0) 50%,
                rgba(255,255,0,1.0) 62.5%,
                rgba(255,120,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 = 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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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: 14°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: 30°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 (isNaN(temp)) return "Indisponible";
          if (temp < 14) {
            return window.innerWidth <= 600 ? "En dehors<br>de l'échelle" : "En dehors de l'échelle";
          }
          if (temp >= 14 && temp < 16) return "Froid";
          if (temp >= 16 && temp < 18.5) return "Frais";
          if (temp >= 18.5 && temp <= 22) return "Confortable";
          if (temp > 22 && temp <= 24) return "Tiède";
          if (temp > 24 && temp < 27) return "Chaud";
          if (temp >= 27 && temp <= 30) return "Canicule";
          if (temp > 30) {
            return window.innerWidth <= 600 ? "En dehors<br>de l'échelle" : "En dehors de l'échelle";
          }
          return "Indisponible";
        ]]]
      styles:
        card:
          - height: 25px
          - width: auto
          - border-radius: 999px
          - border: none
          - padding: 0px 8px 0px 8px
          - background: white
          - background-color: |
              [[[ 
                const minTemp = 14;
                const maxTemp = 30;
                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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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 = 14;
                const maxTemp = 30;
                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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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 = 14;
            const max = 30;
            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) * 6.25;
            const offsetPx = (barWidth * percent) / 100;
            return `calc(100% - ${barRightPercent}% - ${barWidth}px + ${offsetPx}px - ${halfCursor}px)`;
          ]]]
1 « J'aime »

Bonjour,
J’ai adapté la fourchette de température, j’ai remplacé tous les 14 dans le code par 0
image
Le curseur ne se positionne pas correctement, j’ai joué sur halfCursor sans succès.

Merci d’avance.
Bob

Salut @Bob,
Postes moi ton code. Ce n’est pas sur halfCursor qu’il faut jouer, halfCursor permet juste de retrancher la moitié de la largeur du curseur pour mieux le postionner.

Le voici @btncrd

type: custom:button-card
entity: sensor.temperature_ext
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: Température Extérieur
      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(255,0,0,1.0) 0%,
                rgba(255,255,0,1.0) 12.5%,
                rgba(0,255,0,1.0) 50%,
                rgba(255,255,0,1.0) 62.5%,
                rgba(255,120,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 = 0;
                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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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: 0°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: 30°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 (isNaN(temp)) return "Indisponible";
          if (temp < 0) {
            return window.innerWidth <= 600 ? "En dehors<br>de l'échelle" : "En dehors de l'échelle";
          }
          if (temp >= 0 && temp < 16) return "Froid";
          if (temp >= 16 && temp < 18.5) return "Frais";
          if (temp >= 18.5 && temp <= 22) return "Confortable";
          if (temp > 22 && temp <= 24) return "Tiède";
          if (temp > 24 && temp < 27) return "Chaud";
          if (temp >= 27 && temp <= 30) return "Canicule";
          if (temp > 30) {
            return window.innerWidth <= 600 ? "En dehors<br>de l'échelle" : "En dehors de l'échelle";
          }
          return "Indisponible";
        ]]]
      styles:
        card:
          - height: 25px
          - width: auto
          - border-radius: 999px
          - border: none
          - padding: 0px 8px 0px 8px
          - background: white
          - background-color: |
              [[[ 
                const minTemp = 0;
                const maxTemp = 30;
                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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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 = 0;
                const maxTemp = 30;
                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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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 = 0;
            const max = 30;
            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) * 6.25;
            const offsetPx = (barWidth * percent) / 100;
            return `calc(100% - ${barRightPercent}% - ${barWidth}px + ${offsetPx}px - ${halfCursor}px)`;
          ]]]

Bob

Teste avec ce code :

type: custom:button-card
entity: input_number.test_temp
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 Salon 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(255,0,0,1.0) 0%,
                rgba(255,255,0,1.0) 12.5%,
                rgba(0,255,0,1.0) 50%,
                rgba(255,255,0,1.0) 62.5%,
                rgba(255,120,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 = 0;
                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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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: 0°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: 30°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 (isNaN(temp)) return "Indisponible";
          if (temp < 0) {
            return window.innerWidth <= 600 ? "En dehors<br>de l'échelle" : "En dehors de l'échelle";
          }
          if (temp >= 0 && temp < 7) return "Glacial";
          if (temp >= 7 && temp < 14) return "Très froid";
          if (temp >= 14 && temp < 16) return "Froid";
          if (temp >= 16 && temp < 18.5) return "Frais";
          if (temp >= 18.5 && temp <= 22) return "Confortable";
          if (temp > 22 && temp <= 24) return "Tiède";
          if (temp > 24 && temp < 27) return "Chaud";
          if (temp >= 27 && temp <= 30) return "Canicule";
          if (temp > 30) {
            return window.innerWidth <= 600 ? "En dehors<br>de l'échelle" : "En dehors de l'échelle";
          }
          return "Indisponible";
        ]]]
      styles:
        card:
          - height: 25px
          - width: auto
          - border-radius: 999px
          - border: none
          - padding: 0px 8px 0px 8px
          - background: white
          - background-color: |
              [[[ 
                const minTemp = 0;
                const maxTemp = 30;
                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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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 = 0;
                const maxTemp = 30;
                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: [255, 0, 0] },
                  { p: 12.5,c: [255, 255, 0] },
                  { p: 50,  c: [0, 255, 0] },
                  { p: 62.5,c: [255, 255, 0] },
                  { p: 75,  c: [255, 120, 0] },
                  { p: 100, c: [255, 0, 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 = 0;
            const max = 30;
            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)`;
          ]]]

Il fallait prendre en compte que le delta de température est passé de 16°C (30° - 14°) à 30°C et donc chaque degré ne représente plus 6.25% de la longueur de la barre. Il faudra ajuster les pourcentages pour le dégradé linéaire car du coup « Confortable » est en orange.

Ok, nickel, merci @btncrd
Bob

J’ai intégré ton code, c’est vrai que c’est sympa même si j’aurais aimé la barre un poil plus longue mais franchement je chipote.

Mais du coup pour l’humidité etc… On ne peut pas reprendre directement le code, il y a des variations importantes pour chaque capteur non?

Le responsive forcément c’est top

J’ai mis en comparaison les avants après

Le boulot que tu as fait est vraiment incroyable, je t’en remercie vraiment, ça fait vraiment plaisir


1 « J'aime »

lol, tu as une sacrée base pour ta carte. Maintenant, il faut apprendre à utiliser button-card. @btncrd va pas tout te faire :wink:
C’est pas ainsi que tu vas apprendre.

La longueur de la barre est liée à la longueur des commentaires et notamment celui-là :
image
Mais il est possible de gagner quelques pixels en longueur en diminuant l’espace à droite et quelques pixels en augmentant légèrement la longueur tout en gardant suffisamment d’espace entre la barre et l’icône pour afficher tous les commentaires possibles.
Pour les autres cartes, la base reste la même (CO2 : delta de 1600 à la place du delta de 16 pour la température, Humidité : delta de 100, etc.).
Il faut à côté de cela jouer avec les pourcentages des dégradés pour être cohérent avec la logique d’affichage.
Copilot te corrigera le code sans souci avec le bon prompt maintenant que tu as la base.

1 « J'aime »

Oui, oui c’est clair c’est pour ça que je tente des choses, c’est génial d’essayer de comprendre quelle partie fait quoi

Super je vais pouvoir regarder ça, je pensais qu’il y avait beaucoup plus de paramètres dans le code à modifier. Merci c’est top.

Dès que j’aurais cumulé mes capteurs je vous montre ça :smiling_face::smiling_face::smiling_face: