[Mon Dashboard] - @Dapolux

Bonjour,

Bon, alors après ~1 mois et demi de HA, mon dashboard commence a prendre forme.

Au quotidien, nous interagissons avec la domotique soit avec la google home, soit via notre téléphone (quasi pas depuis la tablette, et peu depuis l’ordinateur, sauf moi pour paramétrer), je me suis donc penché sur mes dashboards smartphone.

Après divers essais, et tests d’utilisation, je suis arrivé à la conclusion que l’utilisation de tuiles n’était pas souvent efficace ergonomiquement parlant pour nous, même si cela reste plus joli visuellement, la solution se tourne sur un visuel des plans de la maison avec des infos de base d’un coup d’oeil, puis en un clic on arrive sur plus de détail (c’est peut être aussi que j’ai « trop » d’appareils a piloter … :wink: ) . Cela permet une navigation plus intuitive et rapide (et importante pour le WAF :slight_smile: ).

Mieux que des mots, la démo! heuu, non, des images en fait :).

Donc en gros, j’utilise 3 pages, correspondant aux 3 niveaux de la maison:

Puis en fonction des boutons, j’affiche des infos de base (assez limitées pour que ce soit lisible et clair sur smartphone), et en double cliquant (parfois 1 seul clic), un détail qui diffère d’un bouton à l’autre.

Boutons (global):
Pour mes boutons, j’utilise majoritairement l’excellente carte « custom:button-card », dont il existe nombreux tutos. un des éléments importants, c’est sa capacité à gérer des « templates » (via « button_card_templates »), j’ai donc regroupé pour la plupart de mes boutons, les paramètres communs. Pour rappel, un template peux lui même utiliser d’autres templates.

J’utilise donc un template « base » qui est commun a tous mes boutons (et qui en gros correspond à un bouton de version dashboard), puis en suite j’ai des déclinaisons, notamment pour la vue de mes plans.

Templates globaux

  ################################################################
################################################################
#                     Templates for cards
################################################################
################################################################


#####   Base for all the cards (normal by default)   ###########
# Ratio, generic colours, name, format, font 
  base:
    show_state: false
    show_icon: false
    aspect_ratio: 1/1
    state:
      ## Specific style when state is "On"
      - value: 'on'
        styles:
          card:
            - background-color: 'rgba(255, 255, 255, 0.8)'
            - border: 1px rgba(80, 80, 80) solid
          name:
            - color: 'rgba(0, 0, 0, 0.6)'
          custom_fields:
            temperature:
              - color: "#8d8e90"
    double_tap_action:
      action: more-info
    styles:
      name:
        - top: 74%
        - left: 11%
        - position: absolute
      card:
        - font-family: Passion One
        - letter-spacing: 0px
        - color: 'rgba(255, 255, 255, 0.3)'
        - font-size: 100%
        - background-color: 'rgba(80, 80, 80, 0.7)'
        - border-radius: 15%
        - box-shadow: none
        - transition: none
        - border: 1px rgba(255, 255, 255) solid   

###############   Specific for map tiles   ################        
  base_map:
    template: base
    show_name: false
    styles:
      custom_fields:
        temperature:
          - top: 73%
          - width: 100%
          - justify-self: center
          - align-self: center
          - font-size: >
              [[[
              var window_width = window.screen.width;
              if (window_width > 600) {return '17px';}
              else {return '13px';}
              ]]]
          - color: white
          - font-weight: black
          - position: absolute

Volets:
De base, le bouton affiche l’état de position du volet par l’image (arrondi a 25%):
image

Templates des volets
################################################################
#                       Covers
################################################################          
          
##################  Global Cover ################    

  cover_circle:
    custom_fields: 
      circle_cover: >
        [[[  
        const position = Math.round(entity.attributes.current_position); 
        const radius = 20.5; 
        const circumference = radius * 2 * Math.PI;  
        return `<svg viewBox="0 0 50 50"><circle cx="25" cy="25" r="${radius}" stroke="#b2b2b2" stroke-width="1.5" fill="none" style=" transform:rotate(-90deg); transform-origin: 50% 50%; stroke-dasharray: ${circumference};
        stroke-dashoffset: ${circumference - position / 100 * circumference};" /> <text x="50%" y="54%" fill="#8d8e90" font-size="14" text-anchor="middle" alignment-baseline="middle">${position}<tspan font-size="10">%</tspan></text></svg>`; 
         ]]]

  cover_icon:
    styles:
      custom_fields:
        icon_cover:
          [top: 20%, left: 10%, width: 50px, position: absolute]     
    custom_fields: 
      icon_cover: >
        [[[ 
        const position = Math.round(entity.attributes.current_position); 
        if (position < 5) {return `<svg viewBox="0 0 50 50"><path d="M3 4H21V8H19V20H17V8H7V20H5V8H3V4M8 9H16V11H8V9M8 12H16V14H8V12M8 15H16V17H8V15M8 18H16V20H8V18Z" fill="#8d8e90"/></svg>`;}
        else if ((position > 5) && (position < 35)) {return `<svg viewBox="0 0 50 50"><path d="M 3 4 L 21 4 L 21 8 L 19 8 L 19 20 L 17 20 L 17 8 L 7 8 L 7 20 L 5 20 L 5 8 L 3 8 L 3 4 M 8 9 L 16 9 L 16 11 L 8 11 L 8 9 M 8 12 L 16 12 L 16 14 L 8 14 L 8 12 M 8 15 L 16 15 L 16 17 L 8 17 L 8 15" fill="#8d8e90"/></svg>`;}
        else if ((position >= 35) && (position < 65)) {return `<svg viewBox="0 0 50 50"><path d="M 3 4 L 21 4 L 21 8 L 19 8 L 19 20 L 17 20 L 17 8 L 7 8 L 7 20 L 5 20 L 5 8 L 3 8 L 3 4 M 8 9 L 16 9 L 16 11 L 8 11 L 8 9 M 8 12 L 16 12 L 16 14 L 8 14 L 8 12" fill="#8d8e90"/></svg>`;}
        else if ((position >= 65) && (position < 95))  {return `<svg viewBox="0 0 50 50"><path d="M 3 4 L 21 4 L 21 8 L 19 8 L 19 20 L 17 20 L 17 8 L 7 8 L 7 20 L 5 20 L 5 8 L 3 8 L 3 4 M 8 9 L 16 9 L 16 11 L 8 11 L 8 9" fill="#8d8e90"/></svg>`;}
        else {return `<svg viewBox="0 0 50 50"><path d="M 3 4 L 21 4 L 21 8 L 19 8 L 19 20 L 17 20 L 17 8 L 7 8 L 7 20 L 5 20 L 5 8 L 3 8 L 3 4" fill="#8d8e90"/></svg>`;}
        ]]]    
  
  cover:
    template:
      - base
      - cover_circle
      - cover_icon
    styles: 
      custom_fields:
        cover_icon:
          [top: 18%, left: 6%, width: 40%, position: absolute]      
        circle_cover:
          [top: 10%, left: 50%, width: 45%, position: absolute, letter-spacing: 0px]  
          
            cover_map:
    template:
      - base_map
      - base_forced_on
      - cover_icon
    show_name: false        
    styles:
      custom_fields:
        icon_cover:
          [top: 20%, left: 20%, width: 120%, position: absolute]
Exemple de déclaration de volet
      - type: 'custom:button-card'
        entity: cover.salon
        template: cover_map
        style:
          top: 18%
          left: 95%
          width: 13%

puis au clic j’accède aux actions, et au positionnement exacte.
image

Pour ce faire, j’ai donc défini la popup card correspondante:

Popup
cover.salon:
    title: Salon
    card: 
      type: 'custom:shutter-card'
      entities:
        - cover.salon
      style: |
        .sc-shutter-position, .sc-shutter-label {
          display: none !important;
        }

Lumières:
De base, le bouton affiche l’icone de l’ampoule, avec une couleur différente en fonction de si l’ampoule est allumée ou pas, et la couleur le cas échéant.

image

Exemple de déclaration d'une lumière
      - type: 'custom:button-card'
        entity: light.salon
        template: 
          - light_map
        style:
          top: 7%
          left: 95%
          width: 13%   

Templates utilisés
  light_map:
    template:
      - base_map
      - light_icon        
    styles: 
      custom_fields:
        icon_hue:
          [top: 20%, left: 27%, width: 46%, position: absolute]    

  light_icon:
    custom_fields:

      icon_hue: >
        [[[ 
        const state = entity.state === 'on' ? 'animate' : null;
        return `
        <svg viewBox="0 0 50 50">
        <style>@keyframes animate{0%{transform: scale(0.85);}20%{transform: scale(1.1);}40%{transform: scale(0.95);}60%{transform: scale(1.03);}80%{transform: scale(0.97);}100%{transform: scale(1);}}.animate{animation: animate 0.8s; transform-origin: center;}</style>
        <path fill="rgba(180, 180, 180)" d="M 29.147 48.105 L 21.281 48.105 C 21.281 48.105 20.157 48.173 20.157 48.629 C 20.157 49.087 20.801 49.218 21.281 49.218 L 29.147 49.218 C 29.629 49.218 30.271 49.152 30.271 48.629 C 30.271 48.105 29.147 48.105 29.147 48.105 Z M 34.446 46.209 L 16.144 46.209 C 16.144 46.209 14.857 46.209 14.857 46.733 C 14.857 47.256 15.822 47.319 16.144 47.319 L 34.606 47.319 C 34.927 47.319 35.89 47.256 35.89 46.733 C 35.729 46.209 34.446 46.209 34.446 46.209 Z M 34.446 44.248 L 16.144 44.248 C 16.144 44.248 14.857 44.248 14.857 44.771 C 14.857 45.295 15.822 45.359 16.144 45.359 L 34.606 45.359 C 34.927 45.359 35.89 45.295 35.89 44.771 C 35.729 44.248 34.446 44.248 34.446 44.248 Z M 34.446 42.351 L 25.295 42.351 L 16.144 42.351 C 16.144 42.351 14.857 42.351 14.857 42.874 C 14.857 43.397 15.822 43.463 16.144 43.463 L 34.606 43.463 C 34.927 43.463 35.89 43.397 35.89 42.874 C 35.89 42.351 34.446 42.351 34.446 42.351 Z M 42.794 27.181 C 37.494 23.714 31.556 23.519 25.294 23.519 C 19.194 23.519 11.809 23.781 7.796 27.181 C 7.635 27.246 7.635 27.379 7.956 27.639 C 8.597 28.161 13.252 32.346 13.094 39.932 C 13.094 40.651 12.932 40.978 13.094 41.044 C 13.094 41.109 13.094 41.5 14.857 41.5 L 35.729 41.5 C 37.334 41.5 37.334 41.174 37.494 41.044 L 37.494 39.932 C 37.334 32.346 41.99 28.161 42.633 27.639 C 42.794 27.379 42.794 27.31 42.794 27.181" style=""/>
        <path fill="rgba(150, 150, 150)" d="M 6.828 35.24 C 14.261 41.681 38.81 39.96 43.204 35.24 C 43.872 34.362 44.375 33.484 44.707 32.825 C 48.372 20.309 48.215 15.922 48.215 14.824 C 47.874 5.16 34.192 3.846 28.021 3.846 L 22.347 3.846 C 16.172 3.846 2.321 4.943 2.152 14.824 C 2.152 15.922 1.983 20.309 5.66 32.825 C 5.66 33.484 6.158 34.582 6.828 35.24 Z" style=""/>
        <path class="${state}" fill="var(--button-card-light-color-no-temperature)" d="M 7.783 34.174 C 14.751 40.14 37.764 38.546 41.882 34.174 C 42.509 33.361 42.981 32.547 43.292 31.937 C 46.727 20.344 46.58 16.28 46.58 15.265 C 46.26 6.312 33.433 5.093 27.65 5.093 L 22.332 5.093 C 16.543 5.093 3.559 6.11 3.4 15.265 C 3.4 16.28 3.241 20.344 6.688 31.937 C 6.688 32.547 7.156 33.564 7.783 34.174 Z" style=""/>
        </svg>
        `; ]]]

  light_circle:
    custom_fields:
      circle_light: >
        [[[ if (entity.state === 'on' && entity.attributes.brightness) { const
        brightness = Math.round(entity.attributes.brightness / 2.54); const
        radius = 20.5; const circumference = radius * 2 * Math.PI;  return `<svg
        viewBox="0 0 50 50"><circle cx="25" cy="25" r="${radius}"
        stroke="#b2b2b2" stroke-width="1.5" fill="none" style=" transform:
        rotate(-90deg); transform-origin: 50% 50%; stroke-dasharray:
        ${circumference}; stroke-dashoffset: ${circumference - brightness / 100
        * circumference};" /> <text x="50%" y="54%" fill="#8d8e90"
        font-size="14" text-anchor="middle"
        alignment-baseline="middle">${brightness}<tspan
        font-size="10">%</tspan></text></svg>`; } ]]]


Un simple clique fait un « toggle », puis un double clic permet d’ajuster couleur et intensité si l’ampoule le permet. J’utilise pour cela la carte light-entity-card.

Exemple de déclaration de popup
  light.salon:
    title: Salon
    card:
      type: custom:light-entity-card
      entity: light.salon
      full_width_sliders: true
      smooth_color_wheel: true
      effects_list: false  

Température:
Plutot que de démultiplier les boutons, les thermomètre étant normalement dans des pièces avec une lumière (eh ouai! :slight_smile: ), j’ai juste ajouté l’info sur le bouton des lumières des pièces concernées.

image

Exemple de déclaration
      - type: 'custom:button-card'
        entity: light.wc
        template: 
          - light_map
        style:
          top: 67%
          left: 81%
          width: 13%    
        custom_fields:
          temperature: >
            [[[ return states['sensor.temp_wc'].state + '°' ]]]  

Nb: Je n’utilise pas de template particulier pour cette info car je l’ai déjà géré dans mon template de base.

Puis au double clic, en plus d’avoir les commandes « supplémentaires » des mes lumières, j’ajoute le graphe de la température concernée. Pour le graphe, j’utilise la carte mini-graph-card.

Exemple de déclaration de la popup
  light.wc:
    title: WC
    card:
      type: vertical-stack
      cards:
        - type: custom:light-entity-card
          entity: light.wc
          full_width_sliders: true
          smooth_color_wheel: true
          effects_list: false     

        - type: 'custom:mini-graph-card'
          entities:
            - entity: sensor.temp_wc
          hours_to_show: 30
          points_per_hour: 1
          hour24: true
          show:
            name: false
            icon: false 

Aspirateur:
De base, le bouton affiche l’icone du robot aspirateur (qui est animé si jamais l’aspirateur est en route), puis au clic alors j’accède aux commandes de l’aspirateur
aspirateur

Déclaration du bouton
      - type: 'custom:button-card'
        entity: vacuum.roborock
        show_name: false
        template: 
          - base_map_forced_on
        style:
          top:  56%
          left: 95%
          width: 13% 
        styles:
          card:
            - background-color: 'rgba(255, 255, 255, 0.8)'              
          custom_fields:
            my_icon:
              [top: 20%, left: 20%, width: 60%, position: absolute]                
        custom_fields:
          my_icon: >
            [[[ 
            const state = entity.state === 'cleaning' ? 'vacuum_animated' : '';
            return `<svg viewBox="0 0 24 24">
            <style>.vacuum_animated {animation: vacuum_animated 5s infinite; transform-origin: center;} @keyframes vacuum_animated {10%  {transform: scale(1.2);} 20% {transform: scale(1);} 50% {transform: rotate(180deg);} 100% {transform:rotate(360deg);}}</style>
            <path class="${state}" fill="#9da0a2" d="M12,2C14.65,2 17.19,3.06 19.07,4.93L17.65,6.35C16.15,4.85 14.12,4 12,4C9.88,4 7.84,4.84 6.35,6.35L4.93,4.93C6.81,3.06 9.35,2 12,2M3.66,6.5L5.11,7.94C4.39,9.17 4,10.57 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12C20,10.57 19.61,9.17 18.88,7.94L20.34,6.5C21.42,8.12 22,10.04 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12C2,10.04 2.58,8.12 3.66,6.5M12,6A6,6 0 0,1 18,12C18,13.59 17.37,15.12 16.24,16.24L14.83,14.83C14.08,15.58 13.06,16 12,16C10.94,16 9.92,15.58 9.17,14.83L7.76,16.24C6.63,15.12 6,13.59 6,12A6,6 0 0,1 12,6M12,8A1,1 0 0,0 11,9A1,1 0 0,0 12,10A1,1 0 0,0 13,9A1,1 0 0,0 12,8Z" />
            </svg>`;
            ]]]     

Déclaration de la popup
  vacuum.roborock:
    title: Aspirateur
    card:
      type: 'custom:xiaomi-vacuum-card'
      entity: vacuum.roborock
      vendor: xiaomi
      image: /local/community/lovelace-xiaomi-vacuum-card/vacuum.png
      name: Aspirateur
      attributes:
        main_brush:
          label: 'Brosse Principale: '
        side_brush:
          label: 'Brosse Latérale: '
        filter:
          label: 'Filtre: '
        sensor:
          label: 'Capteurs: '

Version 2: ajout d’une visu en temps réel de la map de chez moi, avec possibilité de déplacer l’aspirateur à un endroit précis, et/ou de nettoyer une zone particulière:

.

Pour ce faire, j’ai installé les 2 cartes suivantes:
GitHub - PiotrMachowski/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor: This custom integration provides a way to present a live view of a map for Xiaomi (Roborock/Viomi/Roidmi/Dreame) vacuums without a need for rooting. pour créer une caméra virtuelle qui renvoie en live le plan de la maison tel que vu dans l’application Xiaomi.
GitHub - PiotrMachowski/lovelace-xiaomi-vacuum-map-card: This card provides a user-friendly way to fully control Xiaomi, Valetudo, Neato and Roomba (+ possibly other) vacuums in Home Assistant. pour afficher cette caméra, et m’en servir de base pour les actions de positionnement ou de nettoyage de zone.

VMC Double flux:
De base le bouton affiche la vitesse de ventilation, puis au clic, le détail de ventilation / commandes.
image

Musique (basique):
J’ai plusieurs points d’écoute de musique (paramétrées en squeezebox), de base le bouton affiche l’icone de musique, avec une petite animation si il y a une lecture en cours, puis en cliquant j’accède a la lecture en cours, la bibliothèque…

music


avec notamment la possibilité de synchro des enceintes

image

Médias du salon (et la ça se complique un peu):
En fait j’ai une télécommande harmony, avec plusieurs activités (comme TV, SmartTV, musique ou encore Cinéma avec le projecteur). De base, le bouton affiche une icone en fonction de l’activité on cours, puis une animation si il y a une lecture. Puis lors d’un clic, le contenu dépend de l’activité.
Si je suis en mode musique, alors j’affiche les mêmes infos que pour les enceintes connectées, avec en plus le volume de l’ampli.

En mode TV, j’affiche le player de la TV

En mode SmartTV, j’affiche le player de la TV, mais aussi celui de ma NvidiaShield, et celui de l’ampli

En mode Ciné, j’affiche le player de ma Shield, celui de l’ampli, les commandes du vidéoprojecteur, et de l’écran du vidéoprojecteur.

Puis en cas de besoin, si je clique sur l’icone de ma télécommande, j’accède aux commandes principales/importantes des différents appareils média (notamment en cas de dysfonctionnement de quelque chose)

Bandeau global par étage:
Pour les lumières et volets, c’est plutot simple, j’ai des commandes groupées pour l’ensemble d’un niveau (j’ai d’ailleurs fait de même pour certaines « grandes » pièces, exemple pour fermer tous les volets de la pièce de vie.

Pour le chauffage, j’ai un plancher chauffant hydrolique, avec 3 zones distinctes (chaque zone pilotée par une vanne mélangeuse que je peux ouvrir/fermer a un % particulier).
Les infos/commandes sont donc dispos après un clic sur le bouton de température du niveau complet.Plus de détails sur la gestion du thermostat et de la V3V: Suggestions pour piloter un plancher chauffant hydrolique (via PAC) - #6 par lilian76

Monitoring:

Version 1
Dans ma page d’accueil, j’ai une tuile qui affiche l’ensemble des appareils que je souhaite monitorer.
Un premier bouton me donne une visu sur la dispo / indispo des appareils monitorés, puis pour les appareils de type « système » (VM, Nuc, Nas…), j’ai un bouton dédié donant des infos complémentaires (CPU / RAM/ HDD).

image

monitoring2

Si vous souhaitez plus de détails, vous pouvez consulter: Avis / Retour d’expériences pour une vue de monitoring / santé

Version 2
Mon monitoring version desktop me permet une vision globale de l’ensemble du système:

Toutes les catégories dans « devices » déroulent le détail par appareil afin d’identifier les éventuels problèmes (par exemple appareil plus accessible, ou faible niveau de batterie):

En complément, chaque « tuile » (ce sont des button cards), que ce soit une machine, une VM ou encore n’importe quel appareil permettent de naviguer vers l’url la plus adaptées (DSM, interface proxmox, page web des shelly ou encore des esp…), ce qui me permet de m’en servir un peu comme page générale d’accès.

Petite astuce:
J’ai pas mal galéré a trouver une solution pour mettre des « button card » sous les « fold-entity-row » car a la base c’est surtout pour des entity cards. J’ai bien tenté « collapsable-cards », mais le résultat était approximatif, surtout graphiquement (beaucoup de bidouillages à faire sur les style pour avoir un rendu propre), et au final la solution est bien de garder des fold-entity-row, mais qui utilise pour son contenu (en head, et en entities), des « decluttering-cards ».

Un bout de code vaut mieux qu’un long discours :):

Exemple de ligne de device
#  -------    Bluetooth
- type: custom:fold-entity-row
  style: |
    #items {
      padding-left: 0px !important;
      margin-top: 6px !important;
    }
    #head {
      height: 29px;
      font-size: 12px;
    }                      
  head:
    type: custom:decluttering-card
    template: title_tile
    variables:
      - entity: binary_sensor.devices_bluetooth
      - ok: sensor.devices_bluetooth_on
      - ko: sensor.devices_bluetooth_off
      - low_battery: sensor.devices_bluetooth_low_battery
      - name: Bluetooth
      - ok_title: 'Ok'
      - ko_title: 'Ko'
      - battery_title: 'Low Battery'
  entities:
    - type: custom:decluttering-card
      template: bluetooth_grid   
Puis les déclarations decluttering associées

decluttering_templates:

#------     title_tile
  title_tile:
    default:
      - ok_title: ' '
      - ko_title: ' '
      - battery_title: ' '
    card:
      type: custom:multiple-entity-row
      entity: '[[entity]]'
      show_state: false
      name: '[[name]]'
      entities:
        - entity: '[[low_battery]]'
          name: '[[battery_title]]'
          styles:
            width: 55px
            text-align: center
        - entity: '[[ok]]'
          name: '[[ok_title]]'
          styles:
            width: 50px
            text-align: center
        - entity: '[[ko]]'
          name: '[[ko_title]]'
          styles:
            width: 50px
            text-align: center 

#------     bluetooth_grid  
  bluetooth_grid:
    card:
      type: grid
      square: false
      columns: 4
      cards:
        - type: custom:decluttering-card
          template: device_tile
          variables:
            - entity: binary_sensor.bt_thermo_sdb2
        - type: custom:decluttering-card
          template: device_tile
          variables:
            - entity: binary_sensor.bt_thermo_cuisine
        - type: custom:decluttering-card
          template: device_tile
          variables:
            - entity: binary_sensor.bt_thermo_bureau
        - type: custom:decluttering-card
          template: device_tile
          variables:
            - entity: binary_sensor.bt_thermo_lywsd03_1
        - type: custom:decluttering-card
          template: device_tile
          variables:
            - entity: binary_sensor.bt_thermo_lywsd03_2

#------     device_tile
  device_tile:
    default:
      - entity: ''
    card:
      type: custom:button-card
      entity: '[[entity]]'
      aspect_ratio: 3/2
      name: |
        [[[
          return states['[[entity]]'].attributes.friendly_name
        ]]]
      tap_action:
        action: url
        url_path: |
          [[[ return states['[[entity]]'].attributes.url ]]]
      styles:
        card:
          - border-radius: 10px
          - padding: 10%
          - font-size: 10px
          - text-shadow: 0px 0px 5px black
        grid:
          - grid-template-areas: '"i info " "n n" "ip ip"'
          - grid-template-columns: 60% 40%
          - grid-template-rows: 70% 25% 25%
        name:
          - font-weight: bold
          - font-size: 12px
          - color: white
          - align-self: middle
          - padding-bottom: 4px
        img_cell:
          - justify-content: middle
          - align-items: start
          - margin: none
        icon:
          - color: |
              [[[
                if (states['[[entity]]'].state == 'on') 
                  {return 'deepskyblue';}
                else               
                  {return 'red';}
              ]]]
          - margin-top: '-10%'
        custom_fields:
          ip:
            - text-align: middle
            - font-size: 12px
          info:
            - self-align: start
            - padding-bottom: 20px
            - font-size: 12px
      custom_fields:
        ip: |
          [[[ 
            var ip = states['[[entity]]'].attributes.ip;
            if (typeof ip === 'string')  {
            return `<p>
            <span style="color: var(--text-color-sensor);">${states['[[entity]]'].attributes.ip}</span>
            </p>`;}
          ]]]
        info: |
          [[[ 
            var battery = states['[[entity]]'].attributes.battery;
            var rssi = states['[[entity]]'].attributes.rssi;
            if (typeof battery === 'string')  {
            var battery_lvl = states[battery].state;
            if (battery_lvl < 15) {var bat_color = 'red';} else {var bat_color = 'deepskyblue';}
            var result = 
            `<p>
            <ha-icon icon="mdi:battery" style="width:16px;  color:${bat_color};"></ha-icon>
            </br><span style="color: var(--text-color-sensor);">${states[battery].state}%</span></span></br>
            </p>`;
            } else if (typeof rssi === "string") {
            var result = 
            `<p>
            <ha-icon icon="mdi:wifi" style="width:16px;  color: grey;"></ha-icon>
            </br><span style="color: var(--text-color-sensor);">${states[rssi].state}dBm</span></span></br>
            </p>`;}
            return result;
          ]]]

N’hésitez pas si vous avez des questions, ou même des suggestions d’amélioration!

8 « J'aime »

Merci pour le partage :+1:

Peux tu en dire plus ? Tu parles de browser_mod ou d’autre chose (more-info-card par exemple) ?

Yes, je parle bien de browser_mod.
Chacun de mes boutons a une entité déclarée:

      - type: 'custom:button-card'
        entity: light.salon
        template: 
          - light_map
        style:
          top: 7%
          left: 95%
          width: 13%

puis je redéfinit le more-info via browser_mod:

popup_cards:
  light.salon:
    title: Salon
    card:
      type: custom:light-entity-card
      entity: light.salon
      full_width_sliders: true
      smooth_color_wheel: true
      effects_list: false

C’est ça qui m’a induit en erreur… Tu affiche une popup au lieu du more-info en fait :+1:

Bonjour @Dapolux je trouve ton dashboard génial crois tu que tu pourrais me montrer ton code Lovelace? Merxi

Bravo, mais ya un truc qui me choque, tes goûts musicaux!! :laughing:

image

1 « J'aime »

Hello, oui pas de soucis, je vais ajouter dans le port initial les différents codes utilisés par section pour s’y retrouver. En attendant de l’avoir fait n’hésites pas s’il y a des sujets particuliers que tu souhaites, je prioriserai en conséquences.

Merci!

C’est marrant j’ai hésité a changer avant de faire mon screenshot car je soupçonnai une potentielle remarque :), j’aurais peut être du mettre Patrick Sébastien à la place.

Pas grave, j’assume! :slight_smile:

2 « J'aime »

Bonjour @Dapolux,

Peux tu nous en dire plus comment tu gères toute ta musique ? J’ai actuellement plusieurs enceintes reliées chacune à un rapsberry avec max2play d’installer dessus. Je diffuse soit en airplay soit via Spotify dessus. J’aimerais beaucoup pourvoir voir chaque enceinte et savoir si il y a de la lecture en cours dessus et voir qui est synchro avec qui et pourvoir changer cette synchro. Plus tard j’aimerais pouvoir les utiliser pour du TTS.

Hello @Arnault,

Si tes enceintes sont déjà sous max2play, le plus dur est déjà fait :).
Comme tu le sais probablement déjà, la synchronisation des players squeezebox est une fonctionnalité native dans LMS:
image

C’est cette même fonctionnalité qui est reprise sous HA, une fois l’intégration squeezebox faite, tu as toutes tes enceinte déclarée en tant que « media_player », avec la fonctionnalité de synchronisation.
Pour avoir accès à la possibilité de le faire visuellement, j’utilise dans lovelace la carte mini-media-player, en paramétrant mes enceintes dans « speaker_group », exemple:

  - type: 'custom:mini-media-player'
    entity: media_player.rpi_salon
    speaker_group: 
      platform: squeezebox
      show_group_count: true
      entities: 
        - entity_id: media_player.rpi_salon
          name: Salon  
        - entity_id: media_player.pc_de_greg
          name: Pc de Grég   
        - entity_id: media_player.rpi_nomade
          name: Nomade 
        - entity_id: media_player.google_home
          name: Google Home  
        - entity_id: media_player.notre_chambre
          name: Notre Chambre     

Par contre, pour spotify, je n’utilise pas du tout l’application native spotify, tout se fais par LMS, du coup pour les lectures spotify, j’utilise le plugin LMS Spotty.

Pour la navigation dans lms depuis lovelace, je t’invite à regarder de plus près le skin lms « Material », tu trouveras plus d’infos sur ce post:
https://forum.hacf.fr/t/visualisation-de-lms/2529/4

N’hésite pas si tu as des questions!

Merci beaucoup pour toutes ses informations. Je vais regarder tout ça et essayer de mettre ça en place. :+1:t2:

Très jolie interface. Merci @Dapolux pour le partage.
Entre les templates / custom cards et les popup via le browser_mod, il y a pas mal de yaml spécifique. Or en mode UI, sauf erreur on ne peut ni faire des include, ni mettre des commentaires. Cela conduit vite a de gros fichiers peu lisibles.
Utilises tu Lovelace en mode UI ou en mode YAML ? Comment as tu structuré ton code?

Bonjour @Argonaute,

Je suis en mode YAML, qui justement permet entres autres les includes et les commentaires.

D’un point de vue structure, coté configuration.yaml, j’ai mes 2 dashboards YMAL (je suis en mode « storage », comme ça j’ai aussi un dashboard UI pour faire des tests).

lovelace:

  mode: storage
  # Add yaml dashboards
  dashboards:
    smartphone-yaml:
      mode: yaml
      title: Smartphone
      icon: mdi:cellphone-text
      show_in_sidebar: true
      filename: dashboards/01_Smartphone.yaml
    dashboard-yaml:
      mode: yaml
      title: Desktop
      icon: mdi:laptop
      show_in_sidebar: true
      filename: dashboards/02_Desktop.yaml

En suite, j’ai 1 fichier yaml par dashboard, qui ont eux même 1 yaml par vue (et incluent aussi des fichiers globaux, comme le fichier des templates ou celles de popup), qui elles mêmes incluent parfaoit d’autres fichiers réutilisés à plusieurs endroits (exemple: mes groupes de speakers).

En gros voici mes fichiers pour lovelace:
image

image

Ps: Pour info, pour les includes, je n’ai pas réussi à inclure « n’importe quel code », je n’arrive que a include une « sous section » dans la structure Yaml. Je ne sais pas si c’est possible sinon.
Exemple avec le source suivant:

################################################################
#                     RDC
################################################################

  - title: rdc
    path: rdc
    icon: 'mdi:home-floor-0'
    panel: true
    cards:
      - type: custom:layout-card
        max_width: 620px
        cards:
          - !include includes/05_map_rdc.yaml
    popup_cards: !include includes/99_popup_cards.yaml

En gros je sais externaliser dans un autre include tout ce qui est sous « cards: », mais je ne sais pas externaliser imaginons les 2 lignes icon et panel dans un seul include.

Tu devrais trouver ton bonheur (et plus !) :wink:

Merci @Dapolux pour le retour ! Très propre et cela permet d’avoir de ne pas copier-coller les cards pour les 2 dashboard. J’imagine que tu as des fichiers spécifiques pour les templates.
La limitation sur les include ne me choque pas.

Je vais regarder cela avec attention! Merci

Effectivement. Dans mon dashboard « Desktop » j’ai une vue qui affiche directement les 3 plans du smartphone.

Non, j’ai tout dans le même, notamment par ce que je n’ai pas tant de templates que ça (une dizaine), et comme plusieurs templates utilisent eux même d’autre templates, c’est plus simple pour moi d’avoir tout directement dans le même fichier.

Bon alors tout d’abord, un grand merci car je crois que je vais pouvoir alléger considérablement mon code, cet addon est top!.

Par contre je ne vois pas comment cela solution mon questionnement initial.

Prenons par exemple le code suivant avec 2 boutons:

      - type: vertical-stack
        title: Test 3
        cards:
        - type: button
          entity: light.eclairage_rdc
          tap_action:
            action: toggle
        - type: button
          entity: light.eclairage_rdc
          tap_action:
            action: toggle

Je peux par exemple découper et faire:

      - type: vertical-stack
        title: Test 3
        cards:
        - !include test.yaml
        - !include test.yaml

avec dans test.yaml:

# lovelace_gen                  
  type: button
  entity: light.eclairage_rdc
  tap_action:
    action: toggle

mais imaginons que je souhaite faire le découpage sur l’instruction suivante, du style:

      - type: vertical-stack
        title: Test 3
        cards:
        - type: button
          !include test.yaml
        - type: button
          !include test.yaml

avec dans tests.yaml:

# lovelace_gen                  
  entity: light.eclairage_rdc
  tap_action:
    action: toggle

Alors ça ne marche pas, ou alors je ne vois pas bien comment je dois faire.

Peut être placer l’inclusion plus haut : GitHub - thomasloven/hass-lovelace_gen: 🔹 Improve the lovelace yaml parser for Home Assistant

- type: vertical-stack
        title: Test 3
        cards:
        - !include
          - type: button
          - test.yaml
        - !include
          - type: button
          - test.yaml

Je n’arrive pas à le faire fonctionner comme cela (Unknown error).

J’avais bien vu ce passage dans le doc, cependant de ce que je comprends, c’est plutot une utilisation d’un include au quel on passerais des paramètres (en l’occurence dans les exemples de la doc, on vois bien qu’il passe le yaml inclu, puis en suite les arguments), dans mon cas, l’idée est plus d’insérer un bout de code d’un autre fichier, peu importe ce code (là pour le moment ça ce limite aux « sous items »).

Bonjour,
J’ai commencé à travailler sur mon floorplan.
Dans le template cover_map, il renvoit à un template base_force_on. pourrais tu nous le montrer? merci