[ConcoursDash] Carte harmonisée

Bonjour à tous,

J’ai créé une carte relativement simple, qui permet une belle harmonie visuelle.
Peu de customisation.

Il faudra néanmoins :

Le texte est en markdown donc natif et customisable à souhait grace aux templates.

Exemples simples :

type: custom:stack-in-card
mode: vertical
keep:
  margin: true
  background: false
  box_shadow: false
cards:
  - type: markdown
    content: |
      🎛️
      <font color= '#A9A9A9'>RAM </font>
      <b>{{states('sensor.memory_use_percent' ) }} %</b>
    text_only: true
    tap_action:
      action: more-info
      entity: sensor.memory_use_percent
  - type: custom:bar-card
    positions:
      icon: "off"
      indicator: "off"
      minmax: "off"
      title: "off"
      name: "off"
      value: "off"
    height: 5px
    entity_row: true
    max: 100
    severity:
      - color: "#00C9A7"
        from: 0
        to: 50
      - color: "#1E90FF"
        from: 50.1
        to: 65
      - color: "#FFB347"
        from: 65.1
        to: 80
      - color: "#E65100"
        from: 80.1
        to: 100
    entities:
      - entity: sensor.memory_use_percent
    card_mod:
      style: |
        ha-card {
          --ha-card-border-width: 0;
          margin: -5px 5px -5px 5px;
          height: 10 !important;
        }
grid_options:
  columns: 6
  rows: auto

Exemple avec icone dynamique :

type: custom:stack-in-card
mode: vertical
keep:
  margin: true
  background: false
  box_shadow: false
cards:
  - type: markdown
    content: >
      {% set etat = states('weather.beaucouze') %} {% set mapping = {
        'clear-night': '🌙',
        'cloudy': '☁️',
        'fog': '🌫️',
        'hail': '🌨️',
        'lightning': '⚡️',
        'lightning-rainy': '⛈️',
        'partlycloudy': '⛅️',
        'pouring': '🌧️',
        'rainy': '🌦️',
        'snowy': '❄️',
        'snowy-rainy': '🌨️',
        'sunny': '☀️',
        'windy': '🌬️',
        'windy-variant': '🍃',
        'exceptional': '⚠️'
      } %}

      {{ mapping.get(etat, '☁️') }}

      <font color= '#A9A9A9'>Météo</font>

      <b>{{state_attr('weather.beaucouze', 'temperature' )  }} °C,
      {{state_translated('weather.beaucouze' ) }}</b>
    text_only: true
    tap_action:
      action: navigate
      navigation_path: /lovelace/sondes
  - type: custom:bar-card
    entity: sensor.beaucouze_temperature
    positions:
      icon: "off"
      indicator: "off"
      minmax: "off"
      title: "off"
      name: "off"
      value: "off"
    height: 5px
    entity_row: true
    min: "-20"
    max: 40
    target: 15
    severity:
      - color: "#FFFFFF"
        from: -20
        to: 3
      - color: "#F0FFFF"
        from: 3
        to: 14
      - color: "#1E90FF"
        from: 14
        to: 22
      - color: "#FFB347"
        from: 22
        to: 27
      - color: "#F76B1C"
        from: 27
        to: 32
      - color: "#B22222"
        from: 32
        to: 50
    entities:
      - entity: sensor.beaucouze_temperature
    card_mod:
      style: |
        ha-card {
          --ha-card-border-width: 0;
          margin: -5px 5px -5px 5px;
          height: 10 !important;
        }
grid_options:
  columns: full
  rows: auto

Exemples complexes :

Ici l’objectif est d’avoir l’info du réveil programmé, à coté l’activité de la journée (soit Repos, soit les horaires. La jauge sera basée sur les horaires de la journée.
Pour ma personne ca été plus complexe car il fallu créer un sensor qui va chercher les infos de mon calendrier et détermine l’heure de début et de fin de ma journée.

Ce qui donne :

    sensor:
      - name: "Amplitude AED Aujourdhui"
        unique_id: amplitude_aed_aujourdhui
        state: >
          {% set ns = namespace(start=[], end=[]) %}
          {% set jour = now().date() %}
          {% set maintenant = now() %}
          
          {# --- BLOC CONDITIONS DE SÉCURITÉ --- #}
          {% set est_vacances = is_state('sensor.vacances', 'on') %}
          {% set est_ferie = is_state('calendar.jours_feries', 'on') %}

          {% if est_vacances or est_ferie %}
            Repos
          {% else %}
            {% if agenda is defined and agenda['calendar.aed'] is defined %}
              {% for e in agenda['calendar.aed'].events %}
                {% if as_datetime(e.start).date() == jour %}
                  {% set ns.start = ns.start + [e.start] %}
                  {% set ns.end = ns.end + [e.end] %}
                {% endif %}
              {% endfor %}
            {% endif %}

            {% if ns.start | count > 0 %}
              {% set debut = ns.start | sort | first | as_datetime | as_local %}
              {% set fin = ns.end | sort | last | as_datetime | as_local %}
              
              {% if maintenant < fin %}
                {{ debut.strftime('%H:%M') }} - {{ fin.strftime('%H:%M') }}
              {% else %}
                Repos
              {% endif %}
            {% else %}
              Repos
            {% endif %}
          {% endif %}
      - name: "Amplitude AED Demain"
        unique_id: amplitude_aed_demain
        state: >
          {% set ns = namespace(start=[], end=[]) %}
          {% set demain = (now() + timedelta(days=1)).date() %}
          
          {% set est_vacances = is_state('sensor.vacances', 'on') %}
          {% set est_ferie_demain = is_state('calendar.jours_feries', 'on') %} 

          {% if est_vacances or est_ferie_demain %}
            Repos
          {% else %}
            {# --- CALCUL DE L'AMPLITUDE --- #}
            {% if agenda is defined and agenda['calendar.aed'] is defined %}
              {% for e in agenda['calendar.aed'].events %}
                {% if as_datetime(e.start).date() == demain %}
                  {% set ns.start = ns.start + [e.start] %}
                  {% set ns.end = ns.end + [e.end] %}
                {% endif %}
              {% endfor %}
            {% endif %}

            {% if ns.start | count > 0 %}
              {{ (ns.start | sort | first | as_datetime | as_local).strftime('%H:%M') }} - {{ (ns.end | sort | last | as_datetime | as_local).strftime('%H:%M') }}
            {% else %}
              Repos
            {% endif %}
          {% endif %}

Pour ma fille ce sont les journées de collège et j’utilise l’intégration Pronote, avec quelques templates :

      - name: Pronote Nina
        icon: >-
          {% set time = states('sensor.time') %}
          {% set CoursAujourdhui = state_attr('sensor.pronote_xxx_nina_today_s_timetable', 'lessons')[0] is defined %}
        
          {% if CoursAujourdhui %}
            {% if time < state_attr('sensor.pronote_xxx_nina_today_s_timetable', 'day_end_at').strftime('%H:%M')  %}
              mdi:account-school
            {% else %}
              mdi:account-school-outline
           {% endif %}
          {% else %}
            mdi:account-school-outline
          {% endif %} 
        state: >-
          {% set time = states('sensor.time') %}
          {% set CoursAujourdhui = state_attr('sensor.pronote_xxx_nina_today_s_timetable', 'lessons')[0] is defined %}
        
          {% if CoursAujourdhui %}
            {% if time < state_attr('sensor.pronote_xxx_nina_today_s_timetable', 'day_end_at').strftime('%H:%M')  %}
              on
            {% else %}
              off
           {% endif %}
          {% else %}
            off
          {% endif %}
        attributes: 
          classe: >
            {{ states.sensor.pronote_xxx_nina_class.state }}
          ce_jour: >
            {% if state_attr('sensor.pronote_xxx_nina_today_s_timetable', 'lessons')[0] is defined %}
                {{ as_datetime(state_attr('sensor.pronote_xxx_nina_today_s_timetable', 'day_start_at')).strftime('%H:%M') }} - {{ as_datetime(state_attr('sensor.pronote_xxx_nina_today_s_timetable', 'day_end_at')).strftime('%H:%M') }}
            {% else %}
                Pas cours
            {% endif %}            
          demain: >         
                {% if state_attr('sensor.pronote_xxx_nina_tomorrow_s_timetable', 'lessons')[0] is defined %}
                 {{state_attr('sensor.pronote_xxx_nina_tomorrow_s_timetable', 'day_start_at').strftime('%H:%M')}} - {{state_attr('sensor.pronote_xxx_nina_tomorrow_s_timetable', 'day_end_at').strftime('%H:%M')}}
                {% else %}
                 Pas cours
                {% endif %}  
          prochain_jour: >          
                {% if state_attr('sensor.pronote_xxx_nina_next_day_s_timetable', 'lessons')[0] is defined %}
                 {%- set start = state_attr('sensor.pronote_xxx_nina_next_day_s_timetable', 'day_start_at').strftime('%A%H:%M') 
                    | replace('Monday', 'L.')
                    | replace('Tuesday', 'M.')
                    | replace('Wednesday', 'Me.')
                    | replace('Thursday', 'J.')
                    | replace('Friday', 'V.')
                    | replace('Saturday', 'S.')
                    | replace('Sunday', 'D.') %}
                 {%- set end = state_attr('sensor.pronote_xxx_nina_next_day_s_timetable', 'day_end_at').strftime('%H:%M') %}                    
                 {{start}}-{{end}}
                {% else %}
                 Pas cours
                {% endif %}

      - name: Pronote Nina demain
        icon: >-
                {% if state_attr('sensor.pronote_xxx_nina_tomorrow_s_timetable', 'lessons')[0] is defined %}
                 mdi:account-school
                {% else %}
                  mdi:account-school-outline
                {% endif %}          
        state: >-
            {% set start = state_attr('sensor.pronote_xxx_nina_tomorrow_s_timetable', 'day_start_at') %}
            {% set end = state_attr('sensor.pronote_xxx_nina_tomorrow_s_timetable', 'day_end_at') %}
            {% set lessons = state_attr('sensor.pronote_xxx_nina_tomorrow_s_timetable', 'lessons') %}
            
            {% if lessons is not none and lessons[0] is defined and start is not none and end is not none %}
              {{ (start | as_datetime | as_local).strftime('%H:%M') }} - {{ (end | as_datetime | as_local).strftime('%H:%M') }}
            {% else %}
              Pas cours
            {% endif %}

La carte :

type: custom:stack-in-card
mode: vertical
keep:
  margin: true
  background: false
  box_shadow: false
cards:
  - type: markdown
    content: |
      ⏰
      <font color= '#A9A9A9'>Nina </font>
      <b>{{state_translated('sensor.reveil_nina') }} </b>
      {%- set pronote = states('sensor.pronote_nina') -%} 
      {%- set cejour = state_attr('sensor.pronote_nina', 'ce_jour') -%}  {%- set
      demain = states('sensor.pronote_nina_demain')  -%}
      <font color= '#A9A9A9'> {%- if cejour and demain == 'Pas cours' -%}
      (Repos)
      {%- elif cejour != 'Pas cours' -%}
      ({{ cejour }})
      {%- elif demain != 'Pas cours' -%} 
      </b><i>({{ demain }}) </i>
      {% endif %}</font>
    text_only: true
    tap_action:
      action: more-info
      entity: sensor.reveil_nina
  - type: custom:bar-card
    positions:
      icon: "off"
      indicator: "off"
      minmax: "off"
      title: "off"
      name: "off"
      value: "off"
    height: 5px
    entity_row: true
    max: 100
    severity:
      - color: "#B22222"
        from: 0
        to: 50
      - color: "#FFB347"
        from: 50
        to: 80
      - color: "#00C9A7"
        from: 80
        to: 100
    entities:
      - entity: sensor.journee_nina_progression
    card_mod:
      style: |
        ha-card {
          --ha-card-border-width: 0;
          margin: -5px 5px -5px 5px;
          height: 10 !important;
        }
grid_options:
  columns: 6
  rows: auto

J’ai aussi adopté ce design aux cartes températures :

type: custom:stack-in-card
mode: vertical
keep:
  margin: true
  background: false
  box_shadow: false
cards:
  - type: markdown
    content: |
      🛋️
      <font color= '#A9A9A9'>Séjour</font>
      <b>{{states('sensor.temp_sejour' ) | round(1) }} °C</b>
    text_only: true
    tap_action:
      action: more-info
      entity: sensor.temp_sejour
  - type: custom:bar-card
    entity: sensor.temp_sejour
    positions:
      icon: "off"
      indicator: "off"
      minmax: "off"
      title: "off"
      name: "off"
      value: "off"
    height: 5px
    target: 19
    entity_row: true
    max: 40
    severity:
      - color: "#B22222"
        from: 27
        to: 40
      - color: "#FFB347"
        from: 23
        to: 26.9
      - color: "#1E90FF"
        from: 19.9
        to: 22.9
      - color: "#F0FFFF"
        from: 0
        to: 19.8
    entities:
      - entity: sensor.temp_sejour
    card_mod:
      style: |
        ha-card {
          --ha-card-border-width: 0;
          margin: -5px 5px -5px 5px;
          height: 10 !important;
        }
grid_options:
  columns: 4
  rows: auto

On peut tout imaginer, en ajustant un peu, j’ai fait les batteries :

J’ai adapté le code avec custom:auto-entities :

type: custom:auto-entities
card:
  type: grid
  columns: 1
  square: false
card_param: cards
filter:
  exclude:
    - options: {}
      state: unknown
    - options: {}
      state: "off"
  include:
    - options:
        type: custom:stack-in-card
        mode: vertical
        keep:
          margin: true
          background: false
          box_shadow: false
        cards:
          - type: markdown
            content: >
              🪫 <font color= '#A9A9A9'>{{state_attr('this.entity_id',
              'friendly_name')}} </font> <b>{{states('this.entity_id')}} %</b>
            text_only: true
            tap_action:
              action: more-info
              entity: this.entity_id
          - type: custom:bar-card
            positions:
              icon: "off"
              indicator: "off"
              minmax: "off"
              title: "off"
              name: "off"
              value: "off"
            height: 5px
            entity_row: true
            max: 100
            severity:
              - color: "#B22222"
                from: 0
                to: 25
              - color: "#FFB347"
                from: 26
                to: 50
              - color: "#1E90FF"
                from: 51
                to: 100
            entities:
              - entity: this.entity_id
            card_mod:
              style: |
                ha-card {
                  --ha-card-border-width: 0;
                  margin: -5px 5px -5px 5px;
                  height: 10 !important;
                }
      attributes:
        device_class: battery
      entity_id: sensor.*porte*
sort:
  method: state
  ignore_case: true
  ip: true
  numeric: true
  reverse: false

« Au cas où je ne vous reverrai pas, bon après-midi, bonne soirée et bonne nuit ! »

Salut,
tu as regardé du côté de :

j’ai viré bar-card et suis passé sur lovelace-entity-progress-card.

Bonjour l’ami, je regarde cela de suite !