Faire des jauges de ce type

Test de transposition rapide via copilot depuis le dernier code de température vers celui pour le dioxyde de carbone, j’ai l’impression que c’est bon, je vais voir pour réduire un peu l’espace entre la value et unit je pense:

type: custom:button-card
entity: sensor.netatmo_salon_dioxyde_de_carbone
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:molecule-co2
      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 dioxyde de carbone
      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,255,24,1.0) 0%,
                rgba(152,255,0,1.0) 40%,
                rgba(255,252,0,1.0) 55%,
                rgba(255,174,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 min = 400;
                const max = 2000;
                const ppm = Number(entity.state);
                const safe = isNaN(ppm) ? min : ppm;
                const clamp = (v, mn, mx) => Math.max(mn, Math.min(mx, v));
                const percent = clamp(((safe - min) / (max - min)) * 100, 0, 100);
                const stops = [
                  { p: 0,   c: [0, 255, 24] },
                  { p: 40,  c: [152, 255, 0] },
                  { p: 55,  c: [255, 252, 0] },
                  { p: 75,  c: [255, 174, 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(0); 
        ]]]
      styles:
        card:
          - width: auto
          - padding: 0
          - border: none
          - border-radius: 0
          - background: none
        state:
          - color: white
          - font-size: "[[[ return window.innerWidth <= 600 ? '1.4rem' : '1.8rem' ]]]"
          - font-weight: 700
  unit:
    card:
      type: custom:button-card
      name: ppm
      styles:
        card:
          - width: auto
          - padding: 0
          - border: none
          - border-radius: 0
          - background: none
        name:
          - font-size: "[[[ return window.innerWidth <= 600 ? '1.0rem' : '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: "400"
      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: "2000"
      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 ppm = Number(entity.state);
          if (isNaN(ppm)) return "Indisponible";
          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 window.innerWidth <= 600 ? "Malsain" : "Malsain";
          return "Indisponible";
        ]]]
      styles:
        card:
          - height: 25px
          - width: auto
          - border-radius: 999px
          - border: none
          - padding: 0px 8px 0px 8px
          - background: white
          - background-color: |
              [[[ 
                const min = 400;
                const max = 2000;
                const ppm = Number(entity.state);
                const clamp = (v, mn, mx) => Math.max(mn, Math.min(mx, v));
                const percent = isNaN(ppm) ? 0 : clamp(((ppm - min) / (max - min)) * 100, 0, 100);
                const stops = [
                  { p: 0,   c: [0, 255, 24] },
                  { p: 40,  c: [152, 255, 0] },
                  { p: 55,  c: [255, 252, 0] },
                  { p: 75,  c: [255, 174, 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 ppm = Number(entity.state) || 0;
                if (ppm < 900) return "rgba(0,255,24,1.0)";
                if (ppm >= 900 && ppm <= 1150) return "rgba(152,255,0,1.0)";
                if (ppm >= 1151 && ppm <= 1400) return "rgba(255,252,0,1.0)";
                if (ppm >= 1401 && ppm <= 1600) return "rgba(255,174,0,1.0)";
                if (ppm > 1600) return "rgba(255,113,0,1.0)";
                return "rgba(167,176,205,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: "[[[ return window.innerWidth <= 600 ? '2%' : '1%' ]]]"
      - right: "[[[ return window.innerWidth <= 600 ? '12%' : '10%' ]]]"
    unit:
      - position: absolute
      - right: "[[[ return window.innerWidth <= 600 ? '3%' : '2%' ]]]"
      - top: "[[[ return window.innerWidth <= 600 ? '2%' : '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 min = 400;
            const max = 2000;
            const value = Number(entity.state);
            const clamp = (v, mn, mx) => Math.max(mn, Math.min(mx, 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)`;
          ]]]

1 « J'aime »

tu passe par où pour çà ?

1 « J'aime »

J’ai exactement la même question. J’ai l’impression d’avoir raté un truc.
Je suis impressionné par le rendu. Super Taf !

1 « J'aime »

Bonjour,
Il faut créer une entrée de type statistique (device et service, entrée, créé une entrée de type statistique) en choisissant le capteur source et la caractéristique mini ou maxi et âge maximum 1h, 24h…

1 « J'aime »

Ça commence à prendre forme, il va falloir que je fasse le capteur bruit aussi, température extérieure et pollen comme tu as fait mais grâce à toi ça avance. Merci

type: vertical-stack
cards:
  - type: custom:button-card
    entity: sensor.netatmo_salon_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 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)`;
              ]]]
  - type: custom:button-card
    entity: sensor.netatmo_salon_dioxyde_de_carbone
    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:molecule-co2
          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 dioxyde de carbone
          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,255,24,1.0) 0%,
                    rgba(152,255,0,1.0) 40%,
                    rgba(255,252,0,1.0) 55%,
                    rgba(255,174,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 min = 400;
                    const max = 2000;
                    const ppm = Number(entity.state);
                    const safe = isNaN(ppm) ? min : ppm;
                    const clamp = (v, mn, mx) => Math.max(mn, Math.min(mx, v));
                    const percent = clamp(((safe - min) / (max - min)) * 100, 0, 100);
                    const stops = [
                      { p: 0,   c: [0, 255, 24] },
                      { p: 40,  c: [152, 255, 0] },
                      { p: 55,  c: [255, 252, 0] },
                      { p: 75,  c: [255, 174, 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(0); 
            ]]]
          styles:
            card:
              - width: auto
              - padding: 0
              - border: none
              - border-radius: 0
              - background: none
            state:
              - color: white
              - font-size: "[[[ return window.innerWidth <= 600 ? '1.4rem' : '1.8rem' ]]]"
              - font-weight: 700
      unit:
        card:
          type: custom:button-card
          name: ppm
          styles:
            card:
              - width: auto
              - padding: 0
              - border: none
              - border-radius: 0
              - background: none
            name:
              - font-size: "[[[ return window.innerWidth <= 600 ? '1.0rem' : '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: "400"
          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: "2000"
          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 ppm = Number(entity.state);
              if (isNaN(ppm)) return "Indisponible";
              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 window.innerWidth <= 600 ? "Malsain" : "Malsain";
              return "Indisponible";
            ]]]
          styles:
            card:
              - height: 25px
    

Bonjour @bentou,

Je passe effectivement par un sensor de type statistique déclaré dans mon fichier de configuration :

# Capteurs
sensor:
  - platform: statistics
    name: Salle à manger - Température min. semaine
    entity_id: sensor.salle_a_manger_climatisation_ble_temp
    sampling_size: 1000
    max_age:
      days: 7
    state_characteristic: value_min

  - platform: statistics
    name: Salle à manger - Température max. semaine
    entity_id: sensor.salle_a_manger_climatisation_ble_temp
    sampling_size: 1000
    max_age:
      days: 7
    state_characteristic: value_max

  - platform: statistics
    name: Perron - Température min. mois
    entity_id: sensor.perron_thermometre_temperature
    sampling_size: 1000
    max_age:
      days: 30
    state_characteristic: value_min

  - platform: statistics
    name: Perron - Température max. mois
    entity_id: sensor.perron_thermometre_temperature
    sampling_size: 1000
    max_age:
      days: 30
    state_characteristic: value_max

Nickel, merci. :+1:
en attendant, j’ai fait une entrée statistique sur 24h, mais c’était juste pour voir les « bornes ».

c’est tres joli tes jauges, faudrait les mêmes sous esphome avec lvgl, mais ca c’est une autre histoire !!! :blush:

1 « J'aime »

Une dernière carte (pollen) pour clore le sujet :


Le code :

type: custom:button-card
entity: sensor.qualite_globale_pollen_lyon
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: m3of:allergy
          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:
              - color: white
              - width: 75%
        - type: custom:button-card
          name: |
            [[[
              return window.innerWidth <= 600
                ? 'Indice pollen<br>&nbsp;&nbsp;<span style="font-size:1.0rem;">(AtmoFrance)</span>'
                : 'Indice pollen (AtmoFrance)';
            ]]]
          styles:
            card:
              - width: auto
              - padding: 0
              - border: none
              - background: none
            name:
              - font-size: 1.4rem
              - text-align: left
              - color: white
              - font-weight: 500
              - padding-top: "[[[ return window.innerWidth <= 600 ? '2%' : '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
  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: |
              [[[
                const colors = [
                  "rgba(80,240,230,1.0)",
                  "rgba(80,204,170,1.0)",
                  "rgba(240,230,65,1.0)",
                  "rgba(255,80,80,1.0)",
                  "rgba(150,0,50,1.0)",
                  "rgba(125,33,129,1.0)"
                ];
                const barWidth = (window.innerWidth <= 600) ? 300 : 360;
                const step = barWidth / colors.length;
                const blendPercent = 0.5;
                const blendHalf = step * blendPercent;
                const stops = [];
                for (let i = 0; i < colors.length; i++) {
                  const start = i * step;
                  const end = (i + 1) * step;
                  stops.push(`${colors[i]} ${Math.max(0, start)}px`);
                  stops.push(`${colors[i]} ${Math.max(0, end - blendHalf)}px`);
                  if (i < colors.length - 1) {
                    stops.push(`${colors[i+1]} ${Math.min(barWidth, end + blendHalf)}px`);
                  }
                }
                stops.push(`${colors[colors.length - 1]} ${barWidth}px`);
                return `linear-gradient(to right, ${stops.join(', ')})`;
              ]]]
  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 val = Number(entity.state);
                const clamp = (v) => Math.max(1, Math.min(6, v));
                const colors = [
                  "rgba(80,240,230,1.0)",
                  "rgba(80,204,170,1.0)",
                  "rgba(240,230,65,1.0)",
                  "rgba(255,80,80,1.0)",
                  "rgba(150,0,50,1.0)",
                  "rgba(125,33,129,1.0)"
                ];
                return colors[clamp(val)-1];
              ]]]
  comment:
    card:
      type: custom:button-card
      name: |
        [[[
          const labels = ["Très faible","Faible","Modéré","Élevé","Très élevé","Extrêmement élevé"];
          const val = Number(entity.state);
          return labels[val-1] || "Indisponible";
        ]]]
      styles:
        card:
          - width: auto
          - height: auto
          - border-radius: 999px
          - border: 0
          - padding: 5px 10px
          - background-color: |
              [[[
                const val = Number(entity.state);
                const colors = [
                  "rgba(80,240,230,0.3)",
                  "rgba(80,204,170,0.3)",
                  "rgba(240,230,65,0.3)",
                  "rgba(255,80,80,0.3)",
                  "rgba(150,0,50,0.3)",
                  "rgba(125,33,129,0.3)"
                ];
                return colors[val-1] || "rgba(221,221,221,0.3)";
              ]]]
        name:
          - font-size: 1.0rem
          - font-weight: 600
          - color: |
              [[[
                const val = Number(entity.state);
                const colors = [
                  "rgba(80,240,230,1.0)",
                  "rgba(80,204,170,1.0)",
                  "rgba(240,230,65,1.0)",
                  "rgba(255,80,80,1.0)",
                  "rgba(150,0,50,1.0)",
                  "rgba(125,33,129,1.0)"
                ];
                return colors[val-1] || "rgba(221,221,221,1.0)";
              ]]]
styles:
  card:
    - background-color: rgba(42,45,54,1.0)
    - aspect-ratio: "[[[ return window.innerWidth <= 600 ? '2.2/1' : '2.8/1' ]]]"
    - cursor: default
  custom_fields:
    icon_and_name:
      - position: absolute
      - top: 2%
      - left: 1%
    dividing_line:
      - position: absolute
      - top: 35%
      - left: 0%
    bar:
      - position: absolute
      - left: 50%
      - top: 58%
      - transform: translate(-50%, -50%)
    cursor:
      - position: absolute
      - top: "[[[ return 'calc(58% - 16px)' ]]]"
      - left: |
          [[[
            const val = Number(entity.state);
            const clamp = (v) => Math.max(1, Math.min(6, v));
            const index = clamp(val);
            const barWidth = (window.innerWidth <= 600) ? 300 : 360;
            const step = barWidth / 6;
            const halfCursor = 8;
            const offsetPx = (index - 1) * step + step/2;
            return `calc(50% - ${barWidth/2}px + ${offsetPx}px - ${halfCursor}px)`;
          ]]]
    comment:
      - position: absolute
      - left: 50%
      - bottom: "-3%"
      - transform: translate(-50%, -50%)
1 « J'aime »

Et en plus il reprend les couleurs AtmoFrance :upside_down_face:

2 « J'aime »

C’est carrément une masterclass l’ensemble de l’aide qu’il nous a apporté

1 « J'aime »

@Xris, passe ton sujet en résolu si c’est bon pour toi :wink:

J’ai fait pour le capteur de bruit :smiling_face: :

type: custom:button-card
entity: input_number.testlong
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:volume-high
      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 Étage Niveau sonore
      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,255,24,1.0) 0%,
                rgba(152,255,0,1.0) 30%,
                rgba(255,252,0,1.0) 60%,
                rgba(255,174,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: 12px
          - height: 25px
          - border-radius: 999px
          - border: 4px solid rgba(42,45,54,1.0)
          - background-color: |
              [[[ 
                const min = 0;
                const max = 120;
                const db = Number(entity.state);
                const safe = isNaN(db) ? min : db;
                const clamp = (v, mn, mx) => Math.max(mn, Math.min(mx, v));
                const percent = clamp(((safe - min) / (max - min)) * 100, 0, 100);
                const stops = [
                  { p: 0,   c: [0, 255, 24] },
                  { p: 30,  c: [152, 255, 0] },
                  { p: 60,  c: [255, 252, 0] },
                  { p: 85,  c: [255, 174, 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 || 1);
                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(0); 
        ]]]
      styles:
        card:
          - width: auto
          - padding: 0
          - border: none
          - border-radius: 0
          - background: none
        state:
          - color: white
          - font-size: "[[[ return window.innerWidth <= 600 ? '1.7rem' : '1.8rem' ]]]"
          - font-weight: 900
  unit:
    card:
      type: custom:button-card
      name: dB
      styles:
        card:
          - width: auto
          - padding: 0
          - border: none
          - border-radius: 0
          - background: none
        name:
          - font-size: "[[[ return window.innerWidth <= 600 ? '1.0rem' : '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"
      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: "120"
      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 db = Number(entity.state);
          if (isNaN(db)) return "Indisponible";
          if (db < 30) return "Très calme";
          if (db >= 30 && db < 60) return "Calme";
          if (db >= 60 && db < 85) return "Bruyant";
          if (db >= 85 && db < 100) return "Très bruyant";
          if (db >= 100) return "Dangereux";
          return "Indisponible";
        ]]]
      styles:
        card:
          - height: 25px
          - width: auto
          - border-radius: 999px
          - border: none
          - padding: 0px 8px 0px 8px
          - background: white
          - background-color: |
              [[[ 
                const min = 0;
                const max = 120;
                const db = Number(entity.state);
                const clamp = (v, mn, mx) => Math.max(mn, Math.min(mx, v));
                const percent = isNaN(db) ? 0 : clamp(((db - min) / (max - min)) * 100, 0, 100);
                const stops = [
                  { p: 0,   c: [0, 255, 24] },
                  { p: 30,  c: [152, 255, 0] },
                  { p: 60,  c: [255, 252, 0] },
                  { p: 85,  c: [255, 174, 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 || 1);
                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 min = 0;
                const max = 120;
                const db = Number(entity.state);
                const clamp = (v, mn, mx) => Math.max(mn, Math.min(mx, v));
                const percent = isNaN(db) ? 0 : clamp(((db - min) / (max - min)) * 100, 0, 100);
                const stops = [
                  { p: 0,   c: [0, 255, 24] },
                  { p: 30,  c: [152, 255, 0] },
                  { p: 60,  c: [255, 252, 0] },
                  { p: 85,  c: [255, 174, 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 || 1);
                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: "[[[ return window.innerWidth <= 600 ? '2%' : '1%' ]]]"
      - right: "[[[ return window.innerWidth <= 600 ? '8%' : '10%' ]]]"
    unit:
      - position: absolute
      - right: "[[[ return window.innerWidth <= 600 ? '3%' : '2%' ]]]"
      - top: "[[[ return window.innerWidth <= 600 ? '2%' : '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 min = 0;
            const max = 120;
            const value = Number(entity.state);
            const clamp = (v, mn, mx) => Math.max(mn, Math.min(mx, v));
            const t = clamp(isNaN(value) ? min : 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)`;
          ]]]

Les dernières avancées avant de prévoir quelques autres capteurs :smiling_face::smiling_face::smiling_face:

type: vertical-stack
cards:
  - type: custom:button-card
    entity: sensor.netatmo_salon_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 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)`;
              ]]]
  - type: custom:button-card
    entity: sensor.netatmo_salon_dioxyde_de_carbone
    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:molecule-co2
          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 dioxyde de carbone
          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,255,24,1.0) 0%,
                    rgba(152,255,0,1.0) 40%,
                    rgba(255,252,0,1.0) 55%,
                    rgba(255,174,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 min = 400;
                    const max = 2000;
                    const ppm = Number(entity.state);
                    const safe = isNaN(ppm) ? min : ppm;
                    const clamp = (v, mn, mx) => Math.max(mn, Math.min(mx, v));
                    const percent = clamp(((safe - min) / (max - min)) * 100, 0, 100);
                    const stops = [
                      { p: 0,   c: [0, 255, 24] },
                      { p: 40,  c: [152, 255, 0] },
                      { p: 55,  c: [255, 252, 0] },
                      { p: 75,  c: [255, 174, 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(0); 
            ]]]
          styles:
            card:
              - width: auto
              - padding: 0
              - border: none
              - border-radius: 0
              - background: none
            state:
              - color: white
              - font-size: "[[[ return window.innerWidth <= 600 ? '1.4rem' : '1.8rem' ]]]"
              - font-weight: 700
      unit:
        card:
          type: custom:button-card
          name: ppm
          styles:
            card:
              - width: auto
              - padding: 0
              - border: none
              - border-radius: 0
              - background: none
            name:
              - font-size: "[[[ return window.innerWidth <= 600 ? '1.0rem' : '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: "400"
          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: "2000"
          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 ppm = Number(entity.state);
              if (isNaN(ppm)) return "Indisponible";
              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 window.innerWidth <= 600 ? "Malsain" : "Malsain";
              return "Indisponible";
            ]]]
          styles:
            card:
              - height: 25px
    
2 « J'aime »

Je suis en train de bosser sur le taux de radon…

Tu habites en Bretagne, dans les Vosges, en Corse ou le Massif central ? :grin:
Tu vas mettre quoi comme bornes haute et basse ? 100 Bq/m3 à 1000 Bq/m3 ?

Sinon, tu attends qu’il se soit transformé en plomb 206 et tu seras moins emm.. :rofl:

Plus sérieusement, tu récupères comment la concentration atmosphérique en Rn 222 ?

1 « J'aime »

D’apres pétain, non… Je suis dans le 44, mais quand même situé sur le bassin versant de la Villaine.
mais breton de coeur, c’est certain.

pour les bornes, ou plutot le pas, je dirais 50 100 150 200 300 500 1000

à partir de 300, le travail est interdit sans équippement de sécurité…
là je suis à 180, j’aèrre.

@bentou

Une carte avec échelle non linéaire de façon à avoir un meilleur rendu visuel : avec un dégradé proportionnel les plages vertes (entre 0 et 99 Bq/m3), jaune (entre 100 et 199 Bq/m3) et orange (entre 200 et 299 Bq/m3) auraient été à peine visible sur la barre. Là la plage verte représente 20% de la longueur totale de la barre et idem pour les plages jaunes et oranges ainsi que pour la zone rouge (de 300 à 999 Bq/m3) et la zone rouge foncé (de 1000 à 1600 Bq/m3)

Le code :

type: custom:button-card
entity: input_number.radon
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:radioactive
          show_name: false
          show_state: false
          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 radon (<sup style="font-size:1.0rem;">222</sup>Rn)
            intérieur
          show_icon: false
          show_state: false
          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
      show_icon: false
      show_name: false
      show_state: false
      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: |
            [[[
              const v = Number(entity.state) || 0;
              return v.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: Bq/m³
          show_icon: false
          show_state: false
          styles:
            card:
              - width: auto
              - padding: 0
              - border: none
              - border-radius: 0
              - background: none
              - margin-left: "-5%"
            name:
              - font-size: 1.2rem
              - align-self: start
              - justify-self: start
              - color: rgba(167,176,205,1.0)
              - font-weight: 800
              - padding-top: 18%
  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: 0
          - background: |-
              linear-gradient(to right,
                rgba(0,255,0,1.0) 0%,
                rgba(255,255,0,1.0) 20%,
                rgba(255,165,0,1.0) 40%,
                rgba(255,0,0,1.0) 60%,
                rgba(192,0,0,1.0) 80%,
                rgba(192,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 v = Number(entity.state) || 0;
                const mapPercent = (x) => {
                  if (x <= 99) { const t = x / 99; return 0 + t * 20; }
                  if (x <= 199) { const t = (x - 100) / 99; return 20 + t * 20; }
                  if (x <= 299) { const t = (x - 200) / 99; return 40 + t * 20; }
                  if (x <= 999) { const t = (x - 300) / 699; return 60 + t * 20; }
                  if (x <= 1600){ const t = (x - 1000) / 600; return 80 + t * 20; }
                  return 100;
                };
                const percent = mapPercent(v);
                if (percent <= 20) return 'rgba(0,255,0,1.0)';
                if (percent <= 40) return 'rgba(255,255,0,1.0)';
                if (percent <= 60) return 'rgba(255,165,0,1.0)';
                if (percent <= 80) return 'rgba(255,0,0,1.0)';
                return 'rgba(192,0,0,1.0)';
              ]]]
  min_val:
    card:
      type: custom:button-card
      name: 0 Bq/m³
      show_icon: false
      show_state: false
      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: 1600 Bq/m³
      show_icon: false
      show_state: false
      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 v = Number(entity.state) || 0;
          if (v < 100) return "Faible";
          if (v <= 199) return "Modéré";
          if (v <= 299) return "Élevé";
          if (v <= 999) return "Très élevé";
          return "Extrêmement élevé";
        ]]]
      styles:
        card:
          - width: auto
          - height: auto
          - border-radius: 999px
          - border: 0
          - padding: 5px 10px
          - background: white
          - background-color: |
              [[[
                const v = Number(entity.state) || 0;
                if (v < 100) return 'rgba(0,255,0,0.25)';
                if (v <= 199) return 'rgba(255,255,0,0.25)';
                if (v <= 299) return 'rgba(255,165,0,0.25)';
                if (v <= 999) return 'rgba(255,0,0,0.25)';
                return 'rgba(192,0,0,0.25)';
              ]]]
        name:
          - font-size: 1.0rem
          - font-weight: 600
          - color: |
              [[[
                const v = Number(entity.state) || 0;
                if (v < 100) return 'rgba(0,255,0,1.0)';
                if (v <= 199) return 'rgba(255,255,0,1.0)';
                if (v <= 299) return 'rgba(255,165,0,1.0)';
                if (v <= 999) return 'rgba(255,0,0,1.0)';
                return 'rgba(192,0,0,1.0)';
              ]]]
styles:
  card:
    - background-color: rgba(42,45,54,1.0)
    - aspect-ratio: |
        [[[
          return window.innerWidth <= 600 ? '2.2/1' : '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 v = Number(entity.state) || 0;
            const mapPercent = (x) => {
              if (x <= 99) { const t = x / 99; return 0 + t * 20; }
              if (x <= 199) { const t = (x - 100) / 99; return 20 + t * 20; }
              if (x <= 299) { const t = (x - 200) / 99; return 40 + t * 20; }
              if (x <= 999) { const t = (x - 300) / 699; return 60 + t * 20; }
              if (x <= 1600){ const t = (x - 1000) / 600; return 80 + t * 20; }
              return 100;
            };
            const percent = mapPercent(v);
            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: 65%
      - transform: translate(-50%, -50%)
    cursor:
      - position: absolute
      - top: "[[[ return 'calc(65% - 16px)'; ]]]"
      - left: |
          [[[
            const v = Number(entity.state) || 0;
            const mapPercent = (x) => {
              if (x <= 99) { const t = x / 99; return 0 + t * 20; }
              if (x <= 199) { const t = (x - 100) / 99; return 20 + t * 20; }
              if (x <= 299) { const t = (x - 200) / 99; return 40 + t * 20; }
              if (x <= 999) { const t = (x - 300) / 699; return 60 + t * 20; }
              if (x <= 1600){ const t = (x - 1000) / 600; return 80 + t * 20; }
              return 100;
            };
            const percent = mapPercent(v);
            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: 16%
      - left: "[[[ return window.innerWidth <= 600 ? '7.5%' : '9%' ]]]"
    max_val:
      - position: absolute
      - bottom: 16%
      - right: "[[[ return window.innerWidth <= 600 ? '6.5%' : '6%' ]]]"
    comment:
      - position: absolute
      - left: 50%
      - bottom: "-2%"
      - transform: translate(-50%, -50%)

2 « J'aime »

Tu as un dosimètre actif pour la mesure du Radon ?

c’est super !!!

j’ai bien fait d’aérer

j’ai un radoneye RD200.
il cause en bluetooth,
je l’attrappe via un esp32 qui fait proxyBT.
ca me donne 2 courbes:

le Radon Short Term et
le Radon Long Term qui se calcule sur 24 ou 48h, je sais plus.