Picture element card, vaste sujet, mes pistes de recherche, voir les solutions qui me conviennent

Re,

Ajout de l’état des bornes du mesh wifi et ajout de la surveillance des GH ( de façon “conditionnelle”, je n’affiche rien sauf si un GH “tombe” ) cf Notifications dynamiques en fonction de la pièce occupée

1

cdt

Re,

Aujourd’hui exit les state-badge trop peu permissif à mon goût et migration vers button-card

tant qu’à faire on va améliorer le rendu avec l’ajout des lux à la température

carte de départ
  • type: state-badge
    entity: sensor.esp1_entree_temperature1
    style:
    top: 66%
    left: 70.7%
    font-size: 9px
    color: transparent
    card_mod:
    style: |
    :host {
    –label-badge-red:
    {% if states(‹ sensor.esp1_entree_temperature1 ›) | int <= 15 %}
    skyblue
    {% elif states(‹ sensor.esp1_entree_temperature1 ›) | int <= 20 %}
    green
    {% elif states(‹ sensor.esp1_entree_temperature1 ›) | int <= 25 %}
    orange
    {% elif states(‹ sensor.esp1_entree_temperature1 ›) | int <= 29 %}
    red
    {% else %}
    brown
    {% endif %}
    ;
    }

1

déjà on commence par créer un input boolean pour switcher au clic entre les deux infos

J’ai fixé les mêmes seuils de changement de couleur que sur mes state-badge

testé un a un que ça fonctionne

la bascule

0

la température

1

la luminosité et le tout ensemble

2

version de base
type: custom:button-cardentity: input_boolean.toggle_temp_lux_esp1_entreetap_action:action: toggleshow_icon: falseshow_name: falseshow_state: truestate_display: |[[[const temp = states[‹ sensor.esp1_entree_temperature1 ›];const lux = states[‹ sensor.esp1_entree_lumiere1 ›];const toggle = states[entity.entity_id];

if (!temp || !lux || !toggle) return '-';

const tempValue = parseFloat(temp.state);
const luxValue = parseFloat(lux.state);

if (isNaN(tempValue) || isNaN(luxValue)) return '-';

return toggle.state === 'on'
  ? Math.round(luxValue) + ' lx'
  : tempValue.toFixed(1) + '°';
]]]styles:card:- border-radius: 50%- height: 70px- width: 70px- display: flex- align-items: center- justify-content: center- background-color: var(–label-badge-background-color)- box-shadow: var(–ha-card-box-shadow)- border: 2px solid var(–primary-color)state:- font-size: 16px- font-weight: bold- text-align: center- color: var(–primary-text-color)card_mod:style: |:host {–border-color: {% set temp = states(‹ sensor.esp1_entree_temperature1 ›) | float %}{% set lux = states(‹ sensor.esp1_entree_lumiere1 ›) | int %}{% set toggle = states(‹ input_boolean.toggle_temp_lux_esp1_entree ›) %}{% if toggle == ‹ off › %}{% if temp <= 15 %} skyblue{% elif temp <= 20 %} green{% elif temp <= 25 %} orange{% elif temp <= 29 %} red{% else %} brown{% endif %}{% else %}{% if lux <= 50 %} black{% elif lux <= 250 %} orange{% else %} white{% endif %}{% endif %};}ha-card {border: 2px solid var(–border-color) !important;}
version plus complète avec more info géré sur les deux entités au double tap
type: custom:button-cardentity: input_boolean.toggle_temp_lux_esp1_entreetap_action:action: toggledouble_tap_action:action: more-infoentity: |[[[const toggle = states[‹ input_boolean.toggle_temp_lux_esp1_entree ›];return toggle && toggle.state === ‹ on ›? ‹ sensor.esp1_entree_lumiere1 ›: ‹ sensor.esp1_entree_temperature1 ›;]]]show_icon: falseshow_name: falseshow_state: truestate_display: |[[[const temp = states[‹ sensor.esp1_entree_temperature1 ›];const lux = states[‹ sensor.esp1_entree_lumiere1 ›];const toggle = states[entity.entity_id];
if (!temp || !lux || !toggle) return '-';

const tempValue = parseFloat(temp.state);
const luxValue = parseFloat(lux.state);

if (isNaN(tempValue) || isNaN(luxValue)) return '-';

return toggle.state === 'on'
  ? Math.round(luxValue) + ' lx'
  : tempValue.toFixed(1) + '°C';
]]]styles:card:- border-radius: 50%- height: 70px- width: 70px- display: flex- align-items: center- justify-content: center- background-color: var(–label-badge-background-color)- box-shadow: var(–ha-card-box-shadow)- border: 2px solid var(–primary-color)state:- font-size: 16px- font-weight: bold- text-align: center- color: var(–primary-text-color)card_mod:style: |:host {–border-color: {% set temp = states(‹ sensor.esp1_entree_temperature1 ›) | float %}{% set lux = states(‹ sensor.esp1_entree_lumiere1 ›) | int %}{% set toggle = states(‹ input_boolean.toggle_temp_lux_esp1_entree ›) %}{% if toggle == ‹ off › %}{% if temp <= 15 %} skyblue{% elif temp <= 20 %} green{% elif temp <= 25 %} orange{% elif temp <= 29 %} red{% else %} brown{% endif %}{% else %}{% if lux <= 50 %} black{% elif lux <= 250 %} orange{% else %} white{% endif %}{% endif %};}ha-card {border: 2px solid var(–border-color) !important;}

Au final ça donnera un badge d’état 2 infos + more info pour avoir le graphe

3

solution mise en prod ( ou presque à 95% )

bonus :slight_smile:

4gif

cdt

Re,

Finalement button card c’est bien aussi :slight_smile:

1

Anim1

Bubble dessus vs button card dessous

cdt

Re,

Amélioration du “state badge” custom button-card à x états avec un input select

info 1 > clic > info 2 > clic info x > clic > retour à l’info 1
le double clic déclenche le more info sur toutes les entités

info 1 > clic > info 2 > clic info x > clic > retour à l’info 1
le double clic déclenche le more info sur toutes les entités

1

Code de la carte
type: custom:button-card
entity: input_select.toggle_temp_hum_lux_esp1_entree
style:
  top: 64.5%
  left: 70.7%
tap_action:
  action: call-service
  service: input_select.select_next
  service_data:
    entity_id: input_select.toggle_temp_hum_lux_esp1_entree
double_tap_action:
  action: more-info
  entity: |
    [[[
      const toggle = states['input_select.toggle_temp_hum_lux_esp1_entree'];
      if (!toggle) return 'sensor.esp1_entree_temperature1';
      
      switch(toggle.state) {
        case 'Lumiere':
          return 'sensor.esp1_entree_lumiere1';
        case 'Humidite':
          return 'sensor.esp1_entree_humidite1';
        case 'Temperature':
        default:
          return 'sensor.esp1_entree_temperature1';
      }
    ]]]
show_icon: false
show_name: false
show_state: true
state_display: |
  [[[
    const temp = states['sensor.esp1_entree_temperature1'];
    const lux = states['sensor.esp1_entree_lumiere1'];
    const humidity = states['sensor.esp1_entree_humidite1'];
    const toggle = states['input_select.toggle_temp_hum_lux_esp1_entree'];
    
    if (!temp || !lux || !humidity || !toggle) return '-';
    
    const tempValue = parseFloat(temp.state);
    const luxValue = parseFloat(lux.state);
    const humidityValue = parseFloat(humidity.state);
    
    if (isNaN(tempValue) || isNaN(luxValue) || isNaN(humidityValue)) return '-';
    
    switch(toggle.state) {
      case 'Lumiere':
        return Math.round(luxValue) + 'lx';
      case 'Humidite':
        return humidityValue.toFixed(0) + '%';
      case 'Temperature':
      default:
        return tempValue.toFixed(1) + '°';
    }
  ]]]
styles:
  card:
    - border-radius: 50%
    - height: 38px
    - width: 38px
    - display: flex
    - align-items: center
    - justify-content: center
    - background-color: var(--label-badge-background-color)
    - box-shadow: var(--ha-card-box-shadow)
    - border: 2px solid var(--primary-color)
  state:
    - font-size: 13px
    - text-align: center
    - color: var(--primary-text-color)
card_mod:
  style: |
    :host {
      --border-color: 
        {% set temp = states('sensor.esp1_entree_temperature1') | float %}
        {% set lux = states('sensor.esp1_entree_lumiere1') | int %}
        {% set humidity = states('sensor.esp1_entree_humidite1') | float %}
        {% set toggle = states('input_select.toggle_temp_hum_lux_esp1_entree') %}
        
        {% if toggle == 'Lumiere' %}
          {% if lux <= 50 %}
            black
          {% elif lux <= 250 %}
            orange
          {% else %}
            white
          {% endif %}
        {% elif toggle == 'Humidite' %}
          {% if humidity <= 30 %}
            red
          {% elif humidity <= 40 %}
            orange
          {% elif humidity <= 60 %}
            green
          {% elif humidity <= 70 %}
            blue
          {% else %}
            purple
          {% endif %}
        {% else %}
          {% if temp <= 15 %}
            skyblue
          {% elif temp <= 20 %}
            green
          {% elif temp <= 25 %}
            orange
          {% elif temp <= 29 %}
            red
          {% else %}
            brown
          {% endif %}
        {% endif %};
    }
    ha-card {
      border: 2px solid var(--border-color) !important;
    }

cdt

Re,

Exit bubble card, pas envie de chercher ce qui ne passe pas en V3.0.x du coup migration en cours sur custom button card général ou presque, j’ai posé les bases plus qu’à copier les codes et à fixer les liens de navigation

Désolé les gif est crade, mais il a fallu compresser un max pour qu’il passe ici

1(1)

1

1

1

cdt

Re,

Vu que j’en avais assez de cacher des bouts de cartes avec les membres de la maison, j’ai mis un mode anonymat pour faire des gif et video ça sera plus facile pour moi, sur un input boolean on passe en anonyme ou pas.

4

5

Code
      - type: custom:button-card
        name: Freetronic
        show_icon: false
        show_name: false
        entity: person.freetronic
        state:
          - value: home
            styles:
              card:
                - background: |
                    [[[
                      if (states['input_boolean.masque_anonymat'].state === 'on') {
                        return 'black';
                      }
                      return 'rgba(0, 128, 0, 0.4)';
                    ]]]
          - value: not_home
            styles:
              card:
                - background: |
                    [[[
                      if (states['input_boolean.masque_anonymat'].state === 'on') {
                        return 'black';
                      }
                      return 'rgba(255, 0, 0, 0.4)';
                    ]]]
          - value: unknown
            styles:
              card:
                - background: |
                    [[[
                      if (states['input_boolean.masque_anonymat'].state === 'on') {
                        return 'black';
                      }
                      return 'rgba(255, 140, 0, 0.6)';
                    ]]]
          - value: unavailable
            styles:
              card:
                - background: |
                    [[[
                      if (states['input_boolean.masque_anonymat'].state === 'on') {
                        return 'black';
                      }
                      return 'rgba(255, 140, 0, 0.6)';
                    ]]]
        card_mod:
          style: |
            ha-card {
              margin-bottom: 0px;
              border-radius: 8px !important;
              padding: 0 !important;
              {% if is_state('input_boolean.masque_anonymat', 'on') %}
              pointer-events: none !important;
              {% endif %}
            }
        custom_fields:
          menu: |
            [[[
              if (states['input_boolean.masque_anonymat'].state === 'on') {
                return `<div class="subbtn-masked">&nbsp;</div>`;
              }
              return `<div class="subbtn">🧑‍💼 Freetronic</div>`;
            ]]]
        styles:
          grid:
            - grid-template-areas: "\"menu\""
            - grid-template-columns: 1fr
            - grid-template-rows: 38px
          custom_fields:
            menu:
              - justify-self: stretch
              - align-self: stretch
              - padding: 0
              - margin: 0
              - min-height: 38px
        extra_styles: |
          .subbtn {
            display: flex !important;
            align-items: center !important;
            justify-content: flex-start !important;
            width: 100% !important;
            height: 38px !important;
            box-sizing: border-box !important;
            padding: 0 12px !important;
            gap: 8px !important;
            border-radius: 6px !important;
            background: transparent !important;
            color: white !important;
            cursor: pointer !important;
            transition: all 0.2s ease !important;
            text-align: left !important;
          }
          .subbtn-masked {
            display: flex !important;
            align-items: center !important;
            justify-content: flex-start !important;
            width: 100% !important;
            height: 38px !important;
            min-height: 38px !important;
            box-sizing: border-box !important;
            padding: 0 12px !important;
            border-radius: 6px !important;
            background: black !important;
            color: transparent !important;
          }
          ha-card:hover:not(:has(.subbtn-masked)) {
            background: rgba(0,123,255,0.4) !important;
            transform: translateX(2px) !important;
          }
          ha-card:hover:not(:has(.subbtn-masked)) .subbtn {
            color: white !important;
          }

cdt

Re,

Les dernières évolutions, exit bubble ( il me reste une carte ) et les popups

bouton update interactif simple qui pointe vers le dashbord upgrade, le tout en streamline parce que tout le menu est en train d’y passer.

6

oui je m’étais planté de texte :sweat_smile:

Alertes météos, bouton interactif plus complexe, en fonction de l’attribut et de la couleur de l’alerte, icone, nom et couleur dynamiques ça pointe vers le dashbord meteo pour plus de précisions, le tout en streamline parce que tout le menu est en train d’y passser

18

1

cdt

Edit, un peu galère celui-là

1

Re,

Dernières modifs sans les tuyaux, j’ai scindé la page matériel en 2 pour que ça soit moins lourd à charger

en test

12

le tour “surveille” 4 entités, si une change, le tour change

        card_mod:
          style: |
            ha-card {
              {% set cpu = states('sensor.pi3_adguard_cpu_utilise') | float(0) %}
              {% set temp = states('sensor.pi3_adguard_cpu_temperature') | float(0) %}
              {% set mem = states('sensor.pi3_adguard_memoire_utilisee') | float(0) %}
              {% set status = states('binary_sensor.pi3_adguard') %}
              
              {% set cpu_free = 100 - cpu %}
              {% set mem_free = 100 - mem %}
              
              {% set has_red = (cpu_free < 25) or (temp >= 65) or (mem_free < 25) or (status in ['off', 'unavailable']) %}
              {% set has_orange = (cpu_free < 50 and cpu_free >= 25) or (temp >= 50 and temp < 65) or (mem_free < 50 and mem_free >= 25) %}
              
              {% if has_red %}
                border: 2px solid #ff0000 !important;
                box-shadow: 
                  0 0 15px #ff0000,
                  inset 0 0 15px rgba(255, 0, 0, 0.4),
                  0 2px 8px rgba(0,0,0,0.4),
                  inset 0 1px 0 rgba(255,255,255,0.1) !important;
              {% elif has_orange %}
                border: 2px solid #ff8800 !important;
                box-shadow: 
                  0 0 15px #ff8800,
                  inset 0 0 15px rgba(255, 136, 0, 0.4),
                  0 2px 8px rgba(0,0,0,0.4),
                  inset 0 1px 0 rgba(255,255,255,0.1) !important;
              {% else %}
                border: 2px solid #00ff00 !important;
                box-shadow: 
                  0 0 12px #00ff00,
                  inset 0 0 12px rgba(0, 255, 0, 0.3),
                  0 2px 8px rgba(0,0,0,0.4),
                  inset 0 1px 0 rgba(255,255,255,0.1) !important;
              {% endif %}
            }

même chose avec un effet pulsar plus ou moins rapide

1

        card_mod:
          style: |
            ha-card {
              {% set cpu = states('sensor.pi3_adguard_cpu_utilise') | float(0) %}
              {% set temp = states('sensor.pi3_adguard_cpu_temperature') | float(0) %}
              {% set mem = states('sensor.pi3_adguard_memoire_utilisee') | float(0) %}
              {% set status = states('binary_sensor.pi3_adguard') %}
              
              {% set cpu_free = 100 - cpu %}
              {% set mem_free = 100 - mem %}
              
              {% set has_red = (cpu_free < 25) or (temp >= 65) or (mem_free < 25) or (status in ['off', 'unavailable']) %}
              {% set has_orange = (cpu_free < 50 and cpu_free >= 25) or (temp >= 50 and temp < 65) or (mem_free < 50 and mem_free >= 25) %}
              
              position: relative !important;
              overflow: visible !important;
              
              {% if has_red %}
                --glow-color: #ff0000;
                --glow-intensity: 15px;
                --speed: 1s;
              {% elif has_orange %}
                --glow-color: #ff8800;
                --glow-intensity: 15px;
                --speed: 2s;
              {% else %}
                --glow-color: #00ff00;
                --glow-intensity: 12px;
                --speed: 6s;
              {% endif %}
              
              border: 2px solid var(--glow-color) !important;
              box-shadow: 
                0 0 var(--glow-intensity) var(--glow-color),
                inset 0 0 var(--glow-intensity) var(--glow-color),
                0 2px 8px rgba(0,0,0,0.4),
                inset 0 1px 0 rgba(255,255,255,0.1) !important;
              animation: glow-pulse var(--speed) ease-in-out infinite !important;
            }

            @keyframes glow-pulse {
              0%, 100% {
                filter: brightness(1) drop-shadow(0 0 5px var(--glow-color));
              }
              50% {
                filter: brightness(1.3) drop-shadow(0 0 20px var(--glow-color));
              }
            }

j’ai tenté de faire un effet “tron” sans y parvenir pour le moment, parfois on obtient des trucs inutilisables :smiley:

13

cdt

Hello,

Quelques ressources

c’est le code adapté à mon dashboard, pour les positionnements et echelles, il faudra vous débrouiller :wink:

Code
  - type: custom:button-card
    show_name: false
    show_icon: false
    style:
      height: 50%
      width: 99%
      left: 50%
      top: 108.5%
    styles:
      card:
        - background: |
            linear-gradient(90deg, 
              rgba(60,60,60,1) 0%,
              rgba(75,75,75,1) 25%,
              rgba(60,60,60,1) 50%,
              rgba(75,75,75,1) 75%,
              rgba(60,60,60,1) 100%)
        - border-radius: 15px
        - box-shadow: |
            inset 0 1px 0 rgba(255,255,255,0.1),
            inset 0 -1px 0 rgba(0,0,0,0.5),
            0 4px 8px rgba(0,0,0,0.6)
        - position: absolute
        - height: 33.5%
    custom_fields:
      vis_tl: |
        [[[
          return `<div style="width:20px;height:20px;position:absolute;top:4px;left:4px;z-index:10;">
            <div style="width:20px;height:20px;background:radial-gradient(circle at 30% 30%, #8a8a8a, #4a4a4a);border-radius:50%;box-shadow:inset -2px -2px 4px rgba(0,0,0,0.5), inset 2px 2px 4px rgba(255,255,255,0.3), 2px 2px 4px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;position:relative;">
              <div style="position:absolute;width:16px;height:4px;background:linear-gradient(to bottom, #1a1a1a, #0a0a0a);box-shadow:inset 0 2px 3px rgba(0,0,0,0.9),0 1px 0 rgba(255,255,255,0.1);"></div>
              <div style="position:absolute;width:4px;height:16px;background:linear-gradient(to right, #1a1a1a, #0a0a0a);box-shadow:inset 2px 0 3px rgba(0,0,0,0.9),1px 0 0 rgba(255,255,255,0.1);"></div>
            </div>
          </div>`;
        ]]]
      vis_tr: |
        [[[
          return `<div style="width:20px;height:20px;position:absolute;top:4px;right:4px;z-index:10;">
            <div style="width:20px;height:20px;background:radial-gradient(circle at 30% 30%, #8a8a8a, #4a4a4a);border-radius:50%;box-shadow:inset -2px -2px 4px rgba(0,0,0,0.5), inset 2px 2px 4px rgba(255,255,255,0.3), 2px 2px 4px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;position:relative;">
              <div style="position:absolute;width:16px;height:4px;background:linear-gradient(to bottom, #1a1a1a, #0a0a0a);box-shadow:inset 0 2px 3px rgba(0,0,0,0.9),0 1px 0 rgba(255,255,255,0.1);"></div>
              <div style="position:absolute;width:4px;height:16px;background:linear-gradient(to right, #1a1a1a, #0a0a0a);box-shadow:inset 2px 0 3px rgba(0,0,0,0.9),1px 0 0 rgba(255,255,255,0.1);"></div>
            </div>
          </div>`;
        ]]]
      vis_bl: |
        [[[
          return `<div style="width:20px;height:20px;position:absolute;bottom:4px;left:4px;z-index:10;">
            <div style="width:20px;height:20px;background:radial-gradient(circle at 30% 30%, #8a8a8a, #4a4a4a);border-radius:50%;box-shadow:inset -2px -2px 4px rgba(0,0,0,0.5), inset 2px 2px 4px rgba(255,255,255,0.3), 2px 2px 4px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;position:relative;">
              <div style="position:absolute;width:16px;height:4px;background:linear-gradient(to bottom, #1a1a1a, #0a0a0a);box-shadow:inset 0 2px 3px rgba(0,0,0,0.9),0 1px 0 rgba(255,255,255,0.1);"></div>
              <div style="position:absolute;width:4px;height:16px;background:linear-gradient(to right, #1a1a1a, #0a0a0a);box-shadow:inset 2px 0 3px rgba(0,0,0,0.9),1px 0 0 rgba(255,255,255,0.1);"></div>
            </div>
          </div>`;
        ]]]
      vis_br: |
        [[[
          return `<div style="width:20px;height:20px;position:absolute;bottom:4px;right:4px;z-index:10;">
            <div style="width:20px;height:20px;background:radial-gradient(circle at 30% 30%, #8a8a8a, #4a4a4a);border-radius:50%;box-shadow:inset -2px -2px 4px rgba(0,0,0,0.5), inset 2px 2px 4px rgba(255,255,255,0.3), 2px 2px 4px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;position:relative;">
              <div style="position:absolute;width:16px;height:4px;background:linear-gradient(to bottom, #1a1a1a, #0a0a0a);box-shadow:inset 0 2px 3px rgba(0,0,0,0.9),0 1px 0 rgba(255,255,255,0.1);"></div>
              <div style="position:absolute;width:4px;height:16px;background:linear-gradient(to right, #1a1a1a, #0a0a0a);box-shadow:inset 2px 0 3px rgba(0,0,0,0.9),1px 0 0 rgba(255,255,255,0.1);"></div>
            </div>
          </div>`;
        ]]]

on numérote

code
      - type: custom:button-card
        show_name: false
        show_icon: false
        style:
          height: 50%
          width: 99%
          left: 50%
          top: 108.5%
         styles:
          card:
            - background: |
                linear-gradient(90deg, 
                  rgba(60,60,60,1) 0%,
                  rgba(75,75,75,1) 25%,
                  rgba(60,60,60,1) 50%,
                  rgba(75,75,75,1) 75%,
                  rgba(60,60,60,1) 100%)
            - border-radius: 15px
            - box-shadow: |
                inset 0 1px 0 rgba(255,255,255,0.1),
                inset 0 -1px 0 rgba(0,0,0,0.5),
                0 4px 8px rgba(0,0,0,0.6)
            - position: absolute
            - height: 33.5%
        custom_fields:
          vis_tl: |
            [[[
              return `<div style="width:20px;height:20px;position:absolute;top:4px;left:4px;z-index:10;">
                <div style="width:20px;height:20px;background:radial-gradient(circle at 30% 30%, #8a8a8a, #4a4a4a);border-radius:50%;box-shadow:inset -2px -2px 4px rgba(0,0,0,0.5), inset 2px 2px 4px rgba(255,255,255,0.3), 2px 2px 4px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;position:relative;">
                  <div style="position:absolute;width:16px;height:4px;background:linear-gradient(to bottom, #1a1a1a, #0a0a0a);box-shadow:inset 0 2px 3px rgba(0,0,0,0.9),0 1px 0 rgba(255,255,255,0.1);"></div>
                  <div style="position:absolute;width:4px;height:16px;background:linear-gradient(to right, #1a1a1a, #0a0a0a);box-shadow:inset 2px 0 3px rgba(0,0,0,0.9),1px 0 0 rgba(255,255,255,0.1);"></div>
                </div>
              </div>`;
            ]]]
          vis_tr: |
            [[[
              return `<div style="width:20px;height:20px;position:absolute;top:4px;right:4px;z-index:10;">
                <div style="width:20px;height:20px;background:radial-gradient(circle at 30% 30%, #8a8a8a, #4a4a4a);border-radius:50%;box-shadow:inset -2px -2px 4px rgba(0,0,0,0.5), inset 2px 2px 4px rgba(255,255,255,0.3), 2px 2px 4px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;position:relative;">
                  <div style="position:absolute;width:16px;height:4px;background:linear-gradient(to bottom, #1a1a1a, #0a0a0a);box-shadow:inset 0 2px 3px rgba(0,0,0,0.9),0 1px 0 rgba(255,255,255,0.1);"></div>
                  <div style="position:absolute;width:4px;height:16px;background:linear-gradient(to right, #1a1a1a, #0a0a0a);box-shadow:inset 2px 0 3px rgba(0,0,0,0.9),1px 0 0 rgba(255,255,255,0.1);"></div>
                </div>
              </div>`;
            ]]]
          vis_bl: |
            [[[
              return `<div style="width:20px;height:20px;position:absolute;bottom:4px;left:4px;z-index:10;">
                <div style="width:20px;height:20px;background:radial-gradient(circle at 30% 30%, #8a8a8a, #4a4a4a);border-radius:50%;box-shadow:inset -2px -2px 4px rgba(0,0,0,0.5), inset 2px 2px 4px rgba(255,255,255,0.3), 2px 2px 4px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;position:relative;">
                  <div style="position:absolute;width:16px;height:4px;background:linear-gradient(to bottom, #1a1a1a, #0a0a0a);box-shadow:inset 0 2px 3px rgba(0,0,0,0.9),0 1px 0 rgba(255,255,255,0.1);"></div>
                  <div style="position:absolute;width:4px;height:16px;background:linear-gradient(to right, #1a1a1a, #0a0a0a);box-shadow:inset 2px 0 3px rgba(0,0,0,0.9),1px 0 0 rgba(255,255,255,0.1);"></div>
                </div>
              </div>`;
            ]]]
          vis_br: |
            [[[
              return `<div style="width:20px;height:20px;position:absolute;bottom:4px;right:4px;z-index:10;">
                <div style="width:20px;height:20px;background:radial-gradient(circle at 30% 30%, #8a8a8a, #4a4a4a);border-radius:50%;box-shadow:inset -2px -2px 4px rgba(0,0,0,0.5), inset 2px 2px 4px rgba(255,255,255,0.3), 2px 2px 4px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;position:relative;">
                  <div style="position:absolute;width:16px;height:4px;background:linear-gradient(to bottom, #1a1a1a, #0a0a0a);box-shadow:inset 0 2px 3px rgba(0,0,0,0.9),0 1px 0 rgba(255,255,255,0.1);"></div>
                  <div style="position:absolute;width:4px;height:16px;background:linear-gradient(to right, #1a1a1a, #0a0a0a);box-shadow:inset 2px 0 3px rgba(0,0,0,0.9),1px 0 0 rgba(255,255,255,0.1);"></div>
                </div>
              </div>`;
            ]]]
          text: |
            [[[
              // Positions exactes des ports (coordonnées left de vos images)
              const portPositions = [
                // Groupe 1 (ports 1-6)
                15.9, 19.1, 22.2, 25.4, 28.6, 31.8,
                // Groupe 2 (ports 7-12)
                36.4, 39.6, 42.8, 46, 49.2, 52.4,
                // Groupe 3 (ports 13-18)
                57, 60.2, 63.4, 66.6, 69.9, 73.1,
                // Groupe 4 (ports 19-24)
                77.9, 81.1, 84.4, 87.6, 90.9, 94.1
              ];

              // Ports du haut = impairs (1, 3, 5, ..., 47)
              const topHtml = portPositions.slice(0, 24).map((position, index) => {
                const num = 1 + index * 2; // 1, 3, 5, ...
                if (num > 47) return '';   // Stop à 47
                return `<span style="position:absolute;top:12px;left:${position}%;transform:translateX(-50%);font-size:10px;color:white;font-weight:bold;z-index:5;">${num}</span>`;
              }).join('');

              // Ports du bas = pairs (2, 4, 6, ..., 48)
              const bottomHtml = portPositions.slice(0, 24).map((position, index) => {
                const num = 2 + index * 2; // 2, 4, 6, ...
                if (num > 48) return '';   // Stop à 48
                return `<span style="position:absolute;bottom:4px;left:${position}%;transform:translateX(-50%);font-size:10px;color:white;font-weight:bold;z-index:5;">${num}</span>`;
              }).join('');

              return `<div style="position:absolute;width:100%;height:100%;top:0;left:0%;pointer-events:none;">${topHtml}${bottomHtml}</div>`;
            ]]]

on groupe tout

1

cdt

Re,

Pour le fun, récupération des dernières notifs en temps réel, génération de liens inter dashboard en temps réel en fonction de la notif, et navigation dans le dashboard concerné si clic

19

c’est « templaté » pour mon utilisation, on met ça ou on veut ou presque, après il y a un peu de boulot derrière. récupérer les notif toussa

  - type: custom:streamline-card
    template: notification_ticker
    view_layout:
      grid-area: ticker

cdt

Hello,

J’arrive sur la fin de mes aventures, j’ai encore quelques petites choses à affiner, mais globalement j’ai enfin fini et j’ai atteint ce que je voulais faire.
Je viens de mettre en place la détection incendie avec redirection directement dans le bandeau d’info, ça fonctionne parfaitement. D’ici un moment, le temps d’affiner encore un peu plus, je ferai une vidéo finale. En espérant que tout ceci inspire. Mais c’est un long chemin pavé d’embûches et un boulot de titan :smiley:

1

cdt

1 « J'aime »

Re,

nouvelle maj ( de card mod je pense ) et mon code couleur sur les maj est de nouveau cassé… vu que je n’ai pas l’intention d’y revenir tous les 2 mois (j’ai tenté mais je n’y suis pas parvenu rapidement, j’ai laissé tomber), changement de tactique et passage à button card

image

image

exit

                - entity: update.esphome_update
                  name: ESPHome
                  type: custom:template-entity-row
                  state: >
                    {{ state_attr('update.esphome_update', 'installed_version')
                    }} ➜ {{ state_attr('update.esphome_update',
                    'latest_version') }}
                  card_mod:
                    style: |
                      :host {
                        {% if state_attr('update.esphome_update', 'installed_version') != state_attr('update.esphome_update', 'latest_version') %}
                          color: red !important;
                        {% else %}
                          color: inherit;
                        {% endif %}
                      }

bienvenue

                - type: custom:streamline-card
                  template: update_display
                  variables:
                    - entity_update: update.esphome_update
                    - icon: mdi:chip
                    - name: ESPHome
streamline_templates:
  update_display:
    card:
      type: custom:button-card
      entity: '[[entity_update]]'
      show_icon: true
      show_name: false
      show_state: false
      icon: '[[icon]]'
      styles:
        card:
          - background: none
          - box-shadow: none
          - padding: 2px 4px
        grid:
          - grid-template-areas: '"i name_installed" "i available"'
          - grid-template-columns: min-content 1fr
          - grid-template-rows: min-content min-content
        icon:
          - width: 24px
          - animation: |
              [[[ 
                const ent = '[[entity_update]]';
                if (states[ent] &&
                    states[ent].attributes.installed_version !== 
                    states[ent].attributes.latest_version) {
                  return 'blink 1.5s ease-in-out infinite';
                }
                return '';
              ]]]
        custom_fields:
          name_installed:
            - align-self: start
            - justify-self: start
            - padding-left: 10px
            - font-size: 14px
          available:
            - align-self: start
            - justify-self: start
            - padding-left: 10px
            - font-size: 14px
      custom_fields:
        name_installed: |
          [[[
            const ent = '[[entity_update]]';
            const name = '[[name]]' || states[ent]?.attributes?.friendly_name || '';
            const inst = states[ent]?.attributes?.installed_version || '';
            return `${name} ${inst}`;
          ]]]
        available: |
          [[[
            const ent = '[[entity_update]]';
            const installed = states[ent]?.attributes?.installed_version;
            const latest = states[ent]?.attributes?.latest_version;
            if (installed && latest && installed !== latest) {
              return `└ Disponible: ${latest}`;
            }
            return '';
          ]]]
      extra_styles: |
        @keyframes blink {
          0%, 100% { opacity: 1; }
          50% { opacity: 0.3; }
        }

si pas de maj, affichage de la version, icone fixe
image

si maj, on déclenche l’affichage d’un conditionnelle avec la dernière version dispo et le clignotement de l’icone.

1

cdt

edit et pour ne pas avoir de v désagréable dans le nom, modif des custom fields

pas bien

image

bien

        name_installed: |
          [[[
            const ent = '[[entity_update]]';
            const name = '[[name]]' || states[ent]?.attributes?.friendly_name || '';
            let inst = states[ent]?.attributes?.installed_version || '';
            inst = inst.replace(/^v/i, '');  // supprime le "v" au début, insensible à la casse
            return `${name} ${inst}`;
          ]]]
        available: |
          [[[
            const ent = '[[entity_update]]';
            let installed = states[ent]?.attributes?.installed_version || '';
            let latest = states[ent]?.attributes?.latest_version || '';
            installed = installed.replace(/^v/i, '');
            latest = latest.replace(/^v/i, '');
            if (installed && latest && installed !== latest) {
              return `└ Disponible: ${latest}`;
            }
            return '';
          ]]]

Re,

J’en suis à la v3 du template, qui apporte la couleur sur l’icon et la désactivation possible de la version actuelle pour pouvoir utiliser le même template pour les update HA et les update OTA ( avec des versions à rallonge dont on a pas forcément l’utilité.

Je mettrai le template si ça interresse

1

2

cdt

Re,

Exit le gif matrix que j’aime bcp

zzz

On passe au semi live de mon écran principal pc

zzz

Devinez sur quel site je suis :smiley:

cdt

1 « J'aime »

Re,

Ajout du panneau d’automation ( c’est mon système au complet, et il y a encore de la purge à faire )

  • pas d’accès à la barre latérale
  • 0 scroll
  • activation / désactivation protégé par lock ( custom button card )
  • accès protégé par pin ( custom button card )

et parallèlement, mise en place de l’automation de surveillance des notif si passage en mode manuel ( que moi qui ait l’accès mais bon )

cdt

Re,

J’aime bien le design des automations du coup j’ai creusé un peu

et en creusant encore plus :smiley:

1

Edit, c’est un peu lourd, je ne vais pas utiliser, je met les codes non finalisés si ça intéresse

button card > card mod pour pouvoir afficher la vertical stack avec la bubble popup puis la button card …

je met les codes ou j’ai stoppé si ça intéresse

1

Version 1 colonne 1 rangée 1 entité
      - type: custom:button-card
        entity: light.ecl_allee_entree
        name: Allee
        show_icon: false
        show_name: false
        style:
          top: 81%
          left: 81%
          width: 20px
          height: 20px
        styles:
          card:
            - width: 20px !important
            - height: 20px !important
            - min-width: 20px
            - min-height: 20px
            - max-width: 20px
            - max-height: 20px
            - background: |
                [[[
                  return entity.state === 'on'
                    ? 'radial-gradient(circle at 3px 3px, Orange 10%, Red 50%)' 
                    : 'radial-gradient(circle at 3px 3px, Lime 10%, Green 50%)';
                ]]]
            - border-radius: 50%
            - box-shadow: |
                [[[
                  return entity.state === 'on'
                    ? '0 0 5px 3px rgba(255, 0, 0, 0.5)' 
                    : '0 0 5px 2px rgba(0, 255, 0, 0.5)';
                ]]]
            - border: |
                [[[
                  return entity.state === 'on'
                    ? '1px solid DarkRed' 
                    : '1px solid DarkGreen';
                ]]]
            - cursor: pointer
        tap_action:
          action: navigate
          navigation_path: "#Allee"
      - type: custom:mod-card
        style:
          top: 90.5%
          left: 75.2%
          height: 37%
          z-index: 5
          .: |
            ha-card {
              background: transparent !important; # Rendre le fond de la carte stack transparent
              box-shadow: none !important;
              border: none !important;
            }
            :host {
              background: transparent !important;
              box-shadow: none !important;
              border: none !important;
            }
        card:
          type: vertical-stack
          style:
            - background: transparent
            - box-shadow: none
          cards:
            - type: custom:bubble-card
              card_type: pop-up
              width_desktop: 100px
              hash: "#Allee"
              entity: light.ecl_allee_entree
              show_header: false
              styles: >
                :host {
                  --bubble-pop-up-border-radius: 15px !important;
                  background: transparent !important; /* On réaffirme */
                  box-shadow: none !important;
                }

                /* Cible tous les div descendants pour forcer la transparence */

                div { 
                  background: transparent !important;
                  box-shadow: none !important;
                }

                /* Cibler également le conteneur de la carte interne s'il y en a
                un */

                .card-content {
                  background: transparent !important;
                }
            - type: custom:button-card
              name: Allee
              show_icon: true
              style:
                width: 67px
                height: 56px
              styles:
                card:
                  - background: rgba(255,255,255,0.1) !important
                  - border-radius: 15px !important
                  - border: 2px solid rgba(255, 255, 255, 0.3)
                  - height: 84px
                  - padding: 0px
                grid:
                  - grid-template-areas: "\"n\" \"rangee1\""
                  - grid-template-rows: 20px 1fr
                name:
                  - justify-self: center
                  - align-self: start
                  - font-size: 16px
                  - color: white
                  - font-weight: bold
                  - margin-top: 1px
                custom_fields:
                  prises:
                    - width: 100%
                    - align-self: start
              custom_fields:
                rangee1:
                  card:
                    type: custom:button-card
                    styles:
                      card:
                        - background: none
                        - border: none
                        - padding: 0px
                      grid:
                        - grid-template-areas: "\"c1\""
                        - grid-template-columns: 1fr
                    custom_fields:
                      c1:
                        card:
                          type: custom:streamline-card
                          template: lampe1
                          variables:
                            - entity: light.ecl_allee_entree
                            - action: toggle
                            - show_name: true
                            - name: Ecl
Version 1 colonne 2 rangées 2 entités
      - type: custom:button-card
        entity: light.lampe_entree
        name: Entree
        show_icon: false
        show_name: false
        style:
          top: 71%
          left: 74%
          width: 20px
          height: 20px
        styles:
          card:
            - width: 20px !important
            - height: 20px !important
            - min-width: 20px
            - min-height: 20px
            - max-width: 20px
            - max-height: 20px
            - background: |
                [[[
                  const lampe_on = states['light.lampe_entree'].state === 'on';
                  const prise_on = states['switch.prise_entree'].state === 'on';
                  
                  // Vrai si l'une ou l'autre est sur 'on'
                  const est_allume = lampe_on || prise_on; 
                  
                  return est_allume 
                    ? 'radial-gradient(circle at 3px 3px, Orange 10%, Red 50%)' 
                    : 'radial-gradient(circle at 3px 3px, Lime 10%, Green 50%)';
                ]]]
            - border-radius: 50%
            - box-shadow: |
                [[[
                  const lampe_on = states['light.lampe_entree'].state === 'on';
                  const prise_on = states['switch.prise_entree'].state === 'on';
                  
                  // Vrai si l'une ou l'autre est sur 'on'
                  const est_allume = lampe_on || prise_on;
                  
                  return est_allume
                    ? '0 0 5px 3px rgba(255, 0, 0, 0.5)' 
                    : '0 0 5px 2px rgba(0, 255, 0, 0.5)';
                ]]]
            - border: |
                [[[
                  const lampe_on = states['light.lampe_entree'].state === 'on';
                  const prise_on = states['switch.prise_entree'].state === 'on';
                  
                  // Vrai si l'une ou l'autre est sur 'on'
                  const est_allume = lampe_on || prise_on;
                  
                  return est_allume
                    ? '1px solid DarkRed' 
                    : '1px solid DarkGreen';
                ]]]
            - cursor: pointer
        tap_action:
          action: navigate
          navigation_path: "#Entree"
      - type: custom:mod-card
        style:
          top: 71.55%
          left: 75.2%
          height: 37%
          z-index: 5
          .: |
            ha-card {
              background: transparent !important; # Rendre le fond de la carte stack transparent
              box-shadow: none !important;
              border: none !important;
            }
            :host {
              background: transparent !important;
              box-shadow: none !important;
              border: none !important;
            }
        card:
          type: vertical-stack
          style:
            - background: transparent
            - box-shadow: none
          cards:
            - type: custom:bubble-card
              card_type: pop-up
              width_desktop: 100px
              hash: "#Entree"
              entity: light.lampe_entree
              show_header: false
              styles: >
                :host {
                  --bubble-pop-up-border-radius: 15px !important;
                  background: transparent !important; /* On réaffirme */
                  box-shadow: none !important;
                }

                /* Cible tous les div descendants pour forcer la transparence */

                div { 
                  background: transparent !important;
                  box-shadow: none !important;
                }

                /* Cibler également le conteneur de la carte interne s'il y en a
                un */

                .card-content {
                  background: transparent !important;
                }
            - type: custom:button-card
              name: Entree
              show_icon: true
              style:
                width: 67px
                height: 56px
              styles:
                card:
                  - background: rgba(255,255,255,0.1) !important
                  - border-radius: 15px !important
                  - border: 2px solid rgba(255, 255, 255, 0.3)
                  - height: 150px
                  - padding: 0px
                grid:
                  - grid-template-areas: "\"n\" \"lampes\" \"prises\""
                  - grid-template-rows: 20px 1fr 1fr
                name:
                  - justify-self: center
                  - align-self: start
                  - font-size: 16px
                  - color: white
                  - font-weight: bold
                  - margin-top: 1px
              custom_fields:
                lampes:
                  card:
                    type: custom:button-card
                    styles:
                      card:
                        - background: none
                        - border: none
                        - padding: 0px
                      grid:
                        - grid-template-areas: "\"l1\""
                        - grid-template-columns: 1fr
                    custom_fields:
                      l1:
                        card:
                          type: custom:streamline-card
                          template: lampe1
                          variables:
                            - entity: light.lampe_entree
                            - action: toggle
                            - show_name: true
                            - name: Ecl
                prises:
                  card:
                    type: custom:button-card
                    styles:
                      card:
                        - background: none
                        - border: none
                        - padding: 0px
                      grid:
                        - grid-template-areas: "\"p1\""
                        - grid-template-columns: 1fr
                    custom_fields:
                      p1:
                        card:
                          type: custom:streamline-card
                          template: switch_prise
                          variables:
                            - entity: switch.prise_entree
                            - action: toggle
                            - show_name: true
                            - name: Couloir
Version 3 colonnes 2 rangées 5 entités
      - type: custom:button-card
        entity: light.lampes_salon_45
        name: Salon
        show_icon: false
        show_name: false
        style:
          top: 68%
          left: 82%
          width: 20px
          height: 20px
        styles:
          card:
            - width: 20px !important
            - height: 20px !important
            - min-width: 20px
            - min-height: 20px
            - max-width: 20px
            - max-height: 20px
            - background: |
                [[[
                  const lampe1 = states['light.lampes_salon_45'].state === 'on';
                  const lampe2 = states['light.lampes_salon_123'].state === 'on';
                  const lampe3 = states['light.lampes_salon_12345'].state === 'on';
                  const prise = states['switch.prise_tv_salon'].state === 'on';
                  const bar = states['light.lampe_bar_salon'].state === 'on';
                  
                  // Vrai si au moins une est sur 'on'
                  const est_allume = lampe1 || lampe2 || lampe3 || prise || bar; 
                  
                  return est_allume 
                    ? 'radial-gradient(circle at 3px 3px, Orange 10%, Red 50%)' 
                    : 'radial-gradient(circle at 3px 3px, Lime 10%, Green 50%)';
                ]]]
            - border-radius: 50%
            - box-shadow: |
                [[[
                  const lampe1 = states['light.lampes_salon_45'].state === 'on';
                  const lampe2 = states['light.lampes_salon_123'].state === 'on';
                  const lampe3 = states['light.lampes_salon_12345'].state === 'on';
                  const prise = states['switch.prise_tv_salon'].state === 'on';
                  const bar = states['light.lampe_bar_salon'].state === 'on';
                  
                  // Vrai si au moins une est sur 'on'
                  const est_allume = lampe1 || lampe2 || lampe3 || prise || bar;
                  
                  return est_allume
                    ? '0 0 5px 3px rgba(255, 0, 0, 0.5)' 
                    : '0 0 5px 2px rgba(0, 255, 0, 0.5)';
                ]]]
            - border: |
                [[[
                  const lampe1 = states['light.lampes_salon_45'].state === 'on';
                  const lampe2 = states['light.lampes_salon_123'].state === 'on';
                  const lampe3 = states['light.lampes_salon_12345'].state === 'on';
                  const prise = states['switch.prise_tv_salon'].state === 'on';
                  const bar = states['light.lampe_bar_salon'].state === 'on';
                  
                  // Vrai si au moins une est sur 'on'
                  const est_allume = lampe1 || lampe2 || lampe3 || prise || bar;
                  
                  return est_allume
                    ? '1px solid DarkRed' 
                    : '1px solid DarkGreen';
                ]]]
            - cursor: pointer
        tap_action:
          action: navigate
          navigation_path: "#Salon"
      - type: custom:mod-card
        style:
          top: 71.55%
          left: 85%
          height: 37%
          z-index: 5
          .: |
            ha-card {
              background: transparent !important; # Rendre le fond de la carte stack transparent
              box-shadow: none !important;
              border: none !important;
            }
            :host {
              background: transparent !important;
              box-shadow: none !important;
              border: none !important;
            }
        card:
          type: vertical-stack
          style:
            - background: transparent
            - box-shadow: none
          cards:
            - type: custom:bubble-card
              card_type: pop-up
              width_desktop: 228px
              hash: "#Salon"
              entity: light.lampes_salon_45
              show_header: false
              styles: >
                :host {
                  --bubble-pop-up-border-radius: 15px !important;
                  background: transparent !important; /* On réaffirme */
                  box-shadow: none !important;
                }

                /* Cible tous les div descendants pour forcer la transparence */

                div { 
                  background: transparent !important;
                  box-shadow: none !important;
                }

                /* Cibler également le conteneur de la carte interne s'il y en a
                un */

                .card-content {
                  background: transparent !important;
                }
            - type: custom:button-card
              name: Salon
              show_icon: true
              style:
                width: 192px
                height: 56px
              styles:
                card:
                  - background: rgba(255,255,255,0.1) !important
                  - border-radius: 15px !important
                  - border: 2px solid rgba(255, 255, 255, 0.3)
                  - height: 150px
                  - padding: 0px
                grid:
                  - grid-template-areas: "\"n\" \"rangee1\" \"rangee2\""
                  - grid-template-rows: 20px 1fr 1fr
                name:
                  - justify-self: center
                  - align-self: start
                  - font-size: 16px
                  - color: white
                  - font-weight: bold
                  - margin-top: 1px
              custom_fields:
                rangee1:
                  card:
                    type: custom:button-card
                    styles:
                      card:
                        - background: none
                        - border: none
                        - padding: 0px
                      grid:
                        - grid-template-areas: "\"c1 c2 c3\""
                        - grid-template-columns: repeat(3, 1fr)
                    custom_fields:
                      c1:
                        card:
                          type: custom:streamline-card
                          template: lampegrp
                          variables:
                            - entity: light.lampes_salon_45
                            - action: toggle
                            - show_name: true
                            - name: 2L
                      c2:
                        card:
                          type: custom:streamline-card
                          template: lampegrp
                          variables:
                            - entity: light.lampes_salon_123
                            - action: toggle
                            - show_name: true
                            - name: 3L
                      c3:
                        card:
                          type: custom:streamline-card
                          template: lampegrp
                          variables:
                            - entity: light.lampes_salon_12345
                            - action: toggle
                            - show_name: true
                            - name: 5L
                rangee2:
                  card:
                    type: custom:button-card
                    styles:
                      card:
                        - background: none
                        - border: none
                        - padding: 0px
                      grid:
                        - grid-template-areas: "\"c1 c2 .\""
                        - grid-template-columns: repeat(3, 1fr)
                    custom_fields:
                      c1:
                        card:
                          type: custom:streamline-card
                          template: switch_prise
                          variables:
                            - entity: switch.prise_tv_salon
                            - action: toggle
                            - show_name: true
                            - name: Tv
                      c2:
                        card:
                          type: custom:streamline-card
                          template: lampe1
                          variables:
                            - entity: light.lampe_bar_salon
                            - action: toggle
                            - show_name: true
                            - name: Bar

cdt

Re,

Version tel en cours d’upgrade

on reconnaitra facilement le menu de base :slight_smile: Une interface mobile pour votre domotique Home Assistant

plus facile quand on a déjà toutes les cartes ou presque :slight_smile:

bonne fin d’année :partying_face:

cdt

1 « J'aime »

Re,

Rendu final ( quasi ), je voulais « finir » en 2025 je suis dans les temps :smiley:

Je suis sur un écran 4K du coup les sur les rendus il y a une grande zone en bas qui n’est pas présente en full screen HD.

ça aura qd même été un boulot de titan vu que je suis parti sur plusieurs trucs différents au fur et à mesure :sweat_smile:

bcp de custom buton card, bcp de streamline template, bcp de métal écouté :smiley:

au final c’est pas si copieux en code ( merci streamline )

image

Bonne fin d’année à tous :clinking_glasses:
cdt

3 « J'aime »

Gros boulot quand même.
Bravo

1 « J'aime »

titanesque même, merci pour le partage!

1 « J'aime »