Home-assistant : une carte de configuration personnalisée pour les prises de courant

Cherchons-nous à mieux comprendre et gérer notre consommation électrique ? Grâce à la puissance de Home Assistant et à une carte de configuration personnalisée (config-template-card), il est possible de créer un tableau de bord dynamique et détaillé de nos prises électriques.

Dans cet article, nous allons décortiquer ensemble un exemple de template pour montrer comment nous pouvons regrouper, analyser et afficher les données de nos prises connectées de manière claire et intuitive.

Avant de commencer, assurons-nous d’avoir :

  • Home Assistant installé et fonctionnel.
  • Des prises connectées (Zigbee, Wi-Fi, etc.) intégrées à Home Assistant et qui remontent la consommation d’énergie (capteurs power et energy).
  • La carte config-template-card installée depuis HACS (Home Assistant Community Store).
  • Une connaissance de base du langage de templating Jinja2, utilisé par Home Assistant, est un plus !

Comprendre le Template : Notre tableau de bord décortiqué

Avant de nous plonger dans le code, il est essentiel de comprendre comment ce template fonctionne. Il ne s’agit pas d’une simple suite d’instructions, mais d’un petit programme dynamique qui génère une interface riche et interactive. Il utilise le langage de templating Jinja2, intégré à Home Assistant, pour transformer les données brutes de nos capteurs en informations claires et utiles.

Voici une analyse détaillée, section par section, de tout ce que nous pouvons faire et personnaliser.


La Structure de Base : custom:config-template-card

Le cœur de notre installation est cette carte personnalisée. Elle agit comme un “moteur” qui va lire toutes nos entités et régénérer le contenu d’une autre carte (ici, une carte markdown qui interprète du HTML) à chaque changement d’état.

  • entities : C’est la liste de surveillance. Nous devons y placer toutes les entités (interrupteurs, capteurs de puissance, etc.) que nous souhaitons utiliser dans notre template. Dès que l’état de l’une d’elles change, toute la carte se met à jour en temps réel.
  • card : Définit la carte qui sera affichée. Nous utilisons type: markdown car elle nous offre une liberté totale sur la mise en forme grâce à l’interprétation du HTML.

Section 1 : Définitions & Variables Centrales

Au début du code, nous définissons toutes les variables qui serviront de base à notre tableau de bord. C’est la “centrale de configuration” de notre template.

  • {%- set days = ... -%} : Récupère la valeur d’un sélecteur (input_select). Cela nous permet de changer à la volée la période d’analyse de l’historique sans toucher au code.
  • {%- set outlets_data = [...] -%} : La variable la plus importante. C’est une liste où l’on associe l’identifiant technique de chaque prise à un nom convivial (ex: 'Salon TV'). Pour ajouter ou modifier une prise, il suffit de le faire dans cette liste !
  • {%- set kwh_cost = ... -%} : Ici, nous définissons le coût de notre kilowattheure pour obtenir des estimations financières précises.
  • {%- set outlets_total = 12 -%} : Le nombre total de prises listées. Pensez à mettre ce chiffre à jour si vous ajoutez ou supprimez des appareils de la liste outlets_data.
  • {%- set unavailable_states = [...] -%} : Une sécurité pour éviter les erreurs de calcul si une prise devient indisponible (unavailable) ou si son état est inconnu (unknown).

Section 2 : Calculs des Totaux

Avant d’afficher quoi que ce soit, le template effectue une première boucle pour calculer toutes les valeurs globales. Cela évite de répéter les mêmes calculs plusieurs fois.

  • {%- set total_power = namespace(sum=0) -%} : Nous utilisons un namespace. C’est une astuce Jinja2 pour créer une variable qui peut être modifiée à l’intérieur d’une boucle. Ici, nous créons des “compteurs” pour la puissance totale, l’énergie totale et le nombre de prises allumées.
  • {%- for ... -%} ... {%- endfor -%} : La boucle parcourt chaque prise de notre liste outlets_data et additionne les puissances et énergies pour alimenter nos compteurs.

Section 3 : Tableau détaillé des prises

C’est ici que la magie opère pour afficher la liste de tous nos appareils. Le code génère dynamiquement un tableau HTML pour un affichage propre et aligné.

  • <thead> <tr> <th>...</th> </tr> </thead> : Ces balises définissent la section d’en-tête de notre tableau.
  • {%- for outlet_info in outlets_data -%} : Une seconde boucle parcourt nos prises, mais cette fois pour générer une ligne de tableau (<tr>) pour chaque appareil.
  • Icônes dynamiques :
    • switch_icon : Affiche un point vert 🟢 si la prise est allumée, rouge 🔴 si elle est éteinte, et blanc ⚪ si l’état est inconnu.
    • power_icon : Donne un indice visuel de la consommation : un feu 🔥 pour une forte consommation, un éclair ⚡ pour une consommation modérée, et une ampoule 💡 pour une faible consommation.
  • {{ power | round(1) }} : Le filtre round(1) permet d’arrondir les chiffres à une décimale pour un affichage plus lisible.

Section 4 : Tableau de Bord Global & Analyse Financière

Cette section reprend les totaux calculés précédemment pour les afficher dans un tableau de synthèse clair, incluant des conversions et estimations.

  • Pourcentages et Conversions : Le code calcule le pourcentage de prises actives et convertit la puissance totale de Watts (W) en kilowatts (kW).
  • Estimations de Coût : En utilisant notre variable kwh_cost, le template estime le coût financier basé sur la consommation enregistrée (total_energy) et sur la puissance instantanée (total_power).

Section 5 : Analyse Intelligente de la Consommation

Pour aller plus loin qu’un simple affichage, cette section analyse la consommation en temps réel pour identifier les appareils les plus gourmands.

  • Catégorisation : Le code utilise une condition if/elif/else pour classer chaque appareil dans l’une des trois listes : “Gros”, “Modérés” ou “Faibles” consommateurs.
  • Affichage Conditionnel : La section entière, ainsi que chaque catégorie, ne s’affiche que si elle contient au moins un appareil. Notre tableau de bord reste ainsi propre et ne montre que les informations pertinentes.

Section 6 : Recommandations Dynamiques

La touche finale : un tableau de bord qui nous donne des conseils !

  • {%- if total_power.sum > 300 -%} : Cette section n’apparaît que si notre consommation totale dépasse un seuil que nous définissons (ici, 300W).
  • Message d’alerte : Si le seuil est dépassé, un message formaté en liste à puces (<ul>) apparaît pour nous alerter et nous donner des pistes d’action. C’est l’exemple parfait d’une interface qui s’adapte au contexte.

Nous avons maintenant toutes les clés pour comprendre, adapter et faire évoluer ce template selon nos besoins !

type: custom:config-template-card
entities:
  - input_select.garden_history_days
  - switch.0x70b3d52b601433db
  - sensor.0x70b3d52b601433db_power
  - sensor.0x70b3d52b601433db_energy
  - sensor.0x70b3d52b601433db_linkquality
  - switch.0x70b3d52b601a7af7
  - sensor.0x70b3d52b601a7af7_power
  - sensor.0x70b3d52b601a7af7_energy
  - sensor.0x70b3d52b601a7af7_linkquality
  - switch.0x70b3d52b6017f0cd
  - sensor.0x70b3d52b6017f0cd_power
  - sensor.0x70b3d52b6017f0cd_energy
  - sensor.0x70b3d52b6017f0cd_linkquality
  - switch.0x70b3d52b601a79fc
  - sensor.0x70b3d52b601a79fc_power
  - sensor.0x70b3d52b601a79fc_energy
  - sensor.0x70b3d52b601a79fc_linkquality
  - switch.0x70b3d52b601a7a26
  - sensor.0x70b3d52b601a7a26_power
  - sensor.0x70b3d52b601a7a26_energy
  - sensor.0x70b3d52b601a7a26_linkquality
  - switch.cuisine_prise_table
  - sensor.cuisine_prise_table_power
  - sensor.cuisine_prise_table_energy
  - sensor.cuisine_prise_table_linkquality
  - switch.cuisine_prise_the
  - sensor.cuisine_prise_the_power
  - sensor.cuisine_prise_the_energy
  - sensor.cuisine_prise_the_linkquality
  - switch.cuisine_prise_cafe
  - sensor.cuisine_prise_cafe_power
  - sensor.cuisine_prise_cafe_energy
  - sensor.cuisine_prise_cafe_linkquality
  - switch.veranda_prise_piscine
  - sensor.veranda_prise_piscine_power
  - sensor.veranda_prise_piscine_energy
  - sensor.veranda_prise_piscine_linkquality
  - switch.veranda_prise_armoire
  - sensor.veranda_prise_armoire_power
  - sensor.veranda_prise_armoire_energy
  - sensor.veranda_prise_armoire_linkquality
  - switch.jardin_prise_ikea
  - sensor.jardin_prise_ikea_power
  - sensor.jardin_prise_ikea_energy
  - sensor.jardin_prise_ikea_linkquality
  - switch.0x70b3d52b601440c9
  - sensor.0x70b3d52b601440c9_power
  - sensor.0x70b3d52b601440c9_energy
card:
  type: markdown
  title: 🔌 Prises Électriques
  content: >
    {%- set days = states('input_select.garden_history_days') | int(3) -%}
    {%- set now_time = now() -%}
    {%- set start_date = (now_time - timedelta(days=days)).strftime('%Y-%m-%dT%H:%M:%S.001Z') -%}
    {%- set end_date = (now_time - timedelta(minutes=10)).strftime('%Y-%m-%dT%H:%M:%S.001Z') -%}
    {%- set date_params = '&start_date=' ~ start_date ~ '&end_date=' ~ end_date -%}
    {%- set outlets_data = [
      ['0x70b3d52b601433db', 'Salon TV'],
      ['0x70b3d52b601a7af7', 'M. à laver'],  
      ['0x70b3d52b6017f0cd', 'Grille-Pain'],
      ['0x70b3d52b601a79fc', 'Congélateur'],
      ['0x70b3d52b601a7a26', 'Frigo'],
      ['cuisine_prise_table', 'Cuisine Table'],
      ['cuisine_prise_the', 'Cuisine Thé'],
      ['cuisine_prise_cafe', 'Cuisine Café'],
      ['veranda_prise_piscine', 'Piscine'],
      ['veranda_prise_armoire', 'Veranda bas'],
      ['jardin_prise_ikea', 'Jardin IKEA'],
      ['0x70b3d52b601440c9', 'Prise ?']
    ] -%}
    {%- set kwh_cost = 0.15 -%}
    {%- set outlets_total = 12 -%}
    {%- set unavailable_states = ['unavailable', 'unknown', 'none'] -%}

    <div>
      <div> 📅 <strong>Historique : {{ days }} jour{{ 's' if days > 1 else '' }}</strong> </div>
      <table>
        <thead>
          <tr>
            <th>Prise</th>
            <th>Temps Réel</th>
            <th>Total</th>
            <th>LQI</th>
          </tr>
        </thead>
        <tbody>
          {%- for outlet_info in outlets_data %}
            {%- set outlet_id = outlet_info[0] -%}
            {%- set outlet_name = outlet_info[1] -%}
            {%- set power_state = states('sensor.' ~ outlet_id ~ '_power') -%}
            {%- set energy_state = states('sensor.' ~ outlet_id ~ '_energy') -%}
            {%- set lqi_state = states('sensor.' ~ outlet_id ~ '_linkquality') -%}
            {%- set switch_state = states('switch.' ~ outlet_id) -%}
            {%- set power = power_state | float(0) if power_state not in unavailable_states else 0 -%}
            {%- set energy = energy_state | float(0) if energy_state not in unavailable_states else 0 -%}
            {%- set lqi = lqi_state | int(0) if lqi_state not in unavailable_states else 0 -%}
            {%- set switch_icon = '🟢' if switch_state == 'on' else '🔴' if switch_state == 'off' else '⚪' -%}
            {%- set power_icon = '🔥' if power > 100 else '⚡' if power > 50 else '💡' if power > 0 else '⚪' -%}
            {%- set history_url = '/history?entity_id=sensor.' ~ outlet_id ~ '_power,sensor.' ~ outlet_id ~ '_energy' ~ date_params -%}
            <tr>
              <td>{{ switch_icon }} {{ outlet_name }}</td>
              <td>{{ power_icon }} <a href="{{ history_url }}"> {{ power }}W</a></td>
              <td><a href="{{ history_url }}">{{ energy | round(2) }} kWh</a></td>
              <td>📊 <a href="{{ history_url }}">{{ lqi }}</a></td>
            </tr>
          {%- endfor %}
        </tbody>
      </table>

      {%- set total_power = namespace(sum=0) -%}
      {%- set total_energy = namespace(sum=0) -%}
      {%- set outlets_on = namespace(count=0) -%}
      {%- for outlet_info in outlets_data -%}
        {%- set outlet_id = outlet_info[0] -%}
        {%- set switch_state = states('switch.' ~ outlet_id) -%}
        {%- set power_state = states('sensor.' ~ outlet_id ~ '_power') -%}
        {%- set energy_state = states('sensor.' ~ outlet_id ~ '_energy') -%}
        {%- set is_on = switch_state == 'on' -%}
        {%- set power = power_state | float(0) if power_state not in unavailable_states else 0 -%}
        {%- set energy = energy_state | float(0) if energy_state not in unavailable_states else 0 -%}
        {%- if is_on -%}
          {%- set outlets_on.count = outlets_on.count + 1 -%}
        {%- endif -%}
        {%- set total_power.sum = total_power.sum + power -%}
        {%- set total_energy.sum = total_energy.sum + energy -%}
      {%- endfor %}

      <h3>⚡ Tableau de Bord Électrique</h3>
      <table>
        <thead>
          <tr>
            <th>📊 Métriques</th>
            <th>Valeur</th>
            <th>Détail</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>🔌 Prises Actives</td>
            <td>{{ outlets_on.count }} / {{ outlets_total }}</td>
            <td>{{ ((outlets_on.count / outlets_total) * 100) | round(1) }}%</td>
          </tr>
          <tr>
            <td>⚡ Puissance Totale</td>
            <td>{{ total_power.sum | round(1) }}W</td>
            <td>{{ (total_power.sum / 1000) | round(2) }} kW</td>
          </tr>
          <tr>
            <td>📈 Consommation Totale</td>
            <td>{{ total_energy.sum | round(1) }} kWh</td>
            <td>≈ {{ (total_energy.sum * kwh_cost) | round(2) }}€</td>
          </tr>
          <tr>
            <td>⏱️  Coût Journalier Estimé</td>
            <td>{{ ((total_power.sum * 24 / 1000) * kwh_cost) | round(2) }}€</td>
            <td>{{ (total_power.sum * 24 / 1000) | round(2) }} kWh/j</td>
          </tr>
        </tbody>
      </table>
      {%- set high_consumers = [] -%}
      {%- set medium_consumers = [] -%}
      {%- set low_consumers = [] -%}
      {%- for outlet_info in outlets_data -%}
        {%- set outlet_id = outlet_info[0] -%}
        {%- set outlet_name = outlet_info[1] -%}
        {%- set power = states('sensor.' ~ outlet_id ~ '_power') | float(0) -%}
        {%- if power > 50 -%}
          {%- set high_consumers = high_consumers + [outlet_name ~ ' (' ~ power ~ 'W)'] -%}
        {%- elif power > 5 -%}
          {%- set medium_consumers = medium_consumers + [outlet_name ~ ' (' ~ power ~ 'W)'] -%}
        {%- elif power > 0 -%}
          {%- set low_consumers = low_consumers + [outlet_name ~ ' (' ~ power ~ 'W)'] -%}
        {%- endif -%}
      {%- endfor -%}

      {%- if high_consumers | length > 0 or medium_consumers | length > 0 -%}
        <h3>🔥 Analyse de Consommation</h3>
        {%- if high_consumers | length > 0 -%}
          <div><strong>🔴 Gros Consommateurs (>50W) :</strong><br>
            {%- for consumer in high_consumers %}
              <span>• {{ consumer }}</span>
            {%- endfor %}
          </div>
        {%- endif -%}
        {%- if medium_consumers | length > 0 -%}
          <div><strong>🟡 Consommateurs Modérés (5-50W) :</strong><br>
            {%- for consumer in medium_consumers %}
              <span>• {{ consumer }}</span>
            {%- endfor %}
          </div>
        {%- endif -%}
        {%- if low_consumers | length > 0 -%}
          <div><strong>🟢 Faible Consommation (<5W) :</strong><br>
            {%- for consumer in low_consumers %}
              <span>• {{ consumer }}</span>
            {%- endfor %}
          </div>
        {%- endif -%}
      {%- endif -%}

      {%- if total_power.sum > 300 -%}
        <h3>💡 Recommandations</h3>
        <div>
          <ul>
            <li>⚠️ Consommation élevée détectée ({{ total_power.sum | round(1) }}W)</li>
            <li>🔍 Vérifiez les appareils en veille non nécessaires</li>
            <li>⏰ Programmez les gros consommateurs aux heures creuses</li>
            <li>📊 Surveillez l'évolution sur {{ days }} jours</li>
          </ul>
        </div>
      {%- endif -%}
    </div>