En mai 2026, Home Assistant a intégralement réécrit sa documentation sur les templates en 14 pages pédagogiques interactives. Il n'existe pourtant toujours pas de guide long-form en français qui en fasse la synthèse pratique. Ce guide comble ce vide : vous y trouverez tout ce qu'il faut savoir pour écrire, tester et maintenir des templates Jinja dans Home Assistant, avec des blocs YAML complets et copiables.

Note importante sur le format YAML : l'ancien format sensor: platform: template a été déprécié en HA 2025.12 et sera définitivement supprimé en HA 2026.6. Ce guide utilise exclusivement le format moderne introduit avec l'intégration template:. Si vous suivez d'anciens tutoriels FR encore en circulation sur le web, méfiez-vous : ils seront cassés d'ici quelques mois.

Pourquoi les templates Jinja sont indispensables dans Home Assistant

Home Assistant expose deux façons de créer des entités virtuelles : les helpers (configurables via l'interface) et les template entities (déclarées en YAML). Les helpers couvrent les cas simples — une valeur fixe, un compteur, une liste de choix. Dès que vous avez besoin de calculer quelque chose à partir d'autres entités, de combiner des conditions ou d'appliquer une transformation, vous avez besoin d'un template.

Exemples concrets que seul un template peut résoudre :

Jinja2 est le moteur de templates Python qu'utilise Home Assistant. Sa syntaxe repose sur trois marqueurs :

  • {{ expression }} — évaluer une expression et afficher le résultat (état d'une entité, calcul, variable).
  • {% instruction %} — exécuter une instruction de contrôle (if, for, set…) sans rien afficher.
  • {# commentaire #} — insérer un commentaire ignoré à l'exécution.

L'éditeur de templates : votre bac à sable intégré

Avant de toucher à votre configuration.yaml, prenez l'habitude de tester chaque expression dans l'éditeur de templates intégré à Home Assistant. C'est la question la plus fréquente du forum HACF : "comment savoir si mon template est correct avant de recharger la config ?".

Accès : Outils développeur (icône en bas du menu latéral) → onglet Modèle. Vous disposez d'une zone de saisie à gauche et du résultat en temps réel à droite. L'éditeur se rafraîchit à chaque keystroke et affiche les erreurs en rouge immédiatement.

La méthode recommandée : commencez toujours par tester l'expression simple dans cet éditeur, puis intégrez-la dans votre YAML une fois qu'elle renvoie le résultat attendu. Cela évite les redémarrages inutiles et les erreurs silencieuses dans les logs.

{# Tester dans l'éditeur : état brut d'une entité #}
{{ states('sensor.linky_puissance_active') }}

{# Résultat attendu : "342.5" (ou "unavailable" si l'entité #}
{# est hors ligne) #}

Accéder aux entités et à leurs attributs

Deux fonctions sont à connaître absolument pour interagir avec vos entités depuis un template.

states() et is_state()

states('entity_id') renvoie l'état d'une entité sous forme de chaîne de caractères. is_state('entity_id', 'on') renvoie true ou false selon que l'entité est dans l'état demandé. Préférez toujours is_state() pour les comparaisons : cela évite les erreurs de type et gère proprement les états unavailable et unknown.

{# Mauvaise pratique — peut planter si unavailable #}
{% if states('binary_sensor.mouvement') == 'on' %}

{# Bonne pratique #}
{% if is_state('binary_sensor.mouvement', 'on') %}

state_attr()

state_attr('entity_id', 'attribute_name') renvoie la valeur d'un attribut. Par exemple, pour lire la puissance active depuis votre module ZLinky ou récupérer la luminosité d'une ampoule :

{# Lire un attribut d'entité #}
{{ state_attr('light.salon', 'brightness') }}

{# Toujours vérifier que l'attribut existe avant de l'utiliser #}
{% set lum = state_attr('light.salon', 'brightness') %}
{% if lum is not none %}
  Luminosité : {{ (lum / 255 * 100) | round(0) }}%
{% else %}
  Luminosité inconnue
{% endif %}

Gérer les entités indisponibles

Une entité peut retourner unavailable ou unknown — typiquement lors d'un redémarrage ou d'une perte de communication Zigbee. Le filtre default() et la fonction is_number() sont vos meilleurs alliés pour éviter les crashs silencieux.

{# Valeur de secours si l'entité n'est pas disponible #}
{{ states('sensor.temperature_salon') | default('N/A') }}

{# Vérifier qu'on peut faire de l'arithmétique #}
{% set temp = states('sensor.temperature_salon') %}
{% if temp | is_number %}
  {{ temp | float | round(1) }} °C
{% else %}
  Capteur indisponible
{% endif %}

Variables, conditions et boucles — les trois piliers du Jinja

Variables avec {% set %}

{% set ma_variable = valeur %} crée une variable locale valable dans le scope du template courant. C'est indispensable pour éviter de répéter plusieurs fois le même appel à states() et pour rendre le code lisible.

{% set temp_int = states('sensor.temperature_interieur')
    | default(0) | float %}
{% set temp_ext = states('sensor.temperature_exterieur')
    | default(0) | float %}
{% set ecart = temp_int - temp_ext %}
Écart intérieur/extérieur : {{ ecart | round(1) }} °C

Conditions avec {% if %} / {% elif %} / {% else %}

La structure conditionnelle Jinja est identique à Python. Utilisez {% elif %} pour chaîner plusieurs conditions.

{% set conso = states('sensor.linky_puissance_active')
    | default(0) | float %}
{% if conso > 6000 %}
  Pic de consommation — vérifiez vos appareils
{% elif conso > 3000 %}
  Consommation élevée
{% elif conso > 0 %}
  Consommation normale
{% else %}
  Données indisponibles
{% endif %}

Boucles avec {% for %}

Les boucles permettent d'itérer sur une liste d'entités — par exemple pour calculer une moyenne ou vérifier l'état d'un groupe. Ici, on calcule la température moyenne de plusieurs pièces :

{% set capteurs = [
  'sensor.temp_salon',
  'sensor.temp_chambre',
  'sensor.temp_bureau'
] %}
{% set ns = namespace(total=0, count=0) %}
{% for entite in capteurs %}
  {% set val = states(entite) %}
  {% if val | is_number %}
    {% set ns.total = ns.total + val | float %}
    {% set ns.count = ns.count + 1 %}
  {% endif %}
{% endfor %}
{% if ns.count > 0 %}
  {{ (ns.total / ns.count) | round(1) }}
{% else %}
  unavailable
{% endif %}

Astuce : l'objet namespace() est nécessaire pour modifier une variable à l'intérieur d'une boucle {% for %}. Sans lui, Jinja crée une copie locale et la variable extérieure reste inchangée.

Créer un template sensor dans configuration.yaml (format 2026)

Le format moderne utilise la clé racine template: dans configuration.yaml. Chaque bloc - sensor: ou - binary_sensor: définit une entité calculée.

Exemple 1 : sensor de température ressentie (indice de Steadman)

template:
  - sensor:
      - name: "Température ressentie salon"
        unique_id: temp_ressentie_salon
        unit_of_measurement: "°C"
        device_class: temperature
        state_class: measurement
        state: >
          {% set t = states('sensor.temperature_salon')
              | default(none) %}
          {% set h = states('sensor.humidite_salon')
              | default(none) %}
          {% if t | is_number and h | is_number %}
            {% set t = t | float %}
            {% set h = h | float %}
            {{
              (t - 0.4 * (t - 10) * (1 - h / 100))
              | round(1)
            }}
          {% else %}
            unavailable
          {% endif %}
        availability: >
          {{ states('sensor.temperature_salon') | is_number
             and states('sensor.humidite_salon') | is_number }}

Exemple 2 : binary_sensor "maison occupée"

Ce binary_sensor combine la présence GPS de deux personnes et l'état d'un capteur de présence LD2410 pour détecter si quelqu'un est à la maison.

template:
  - binary_sensor:
      - name: "Maison occupée"
        unique_id: maison_occupee
        device_class: occupancy
        state: >
          {{ is_state('person.alice', 'home')
             or is_state('person.bob', 'home')
             or is_state(
               'binary_sensor.ld2410_presence', 'on'
             ) }}
        delay_off:
          minutes: 5

Le paramètre delay_off évite les fausses alarmes : le sensor ne passe à off que si la condition reste fausse pendant 5 minutes consécutives.

Les filtres Jinja indispensables pour la domotique

Les filtres s'appliquent avec le symbole |. Home Assistant en ajoute plusieurs à ceux natifs de Jinja2, spécifiquement utiles pour la domotique.

Conversions numériques : round(), int(), float()

{# Arrondi à 1 décimale #}
{{ states('sensor.puissance_w') | float | round(1) }}

{# Conversion en entier (utile pour les % de batterie) #}
{{ state_attr('sensor.batterie', 'battery_level') | int }}

{# Conversion sécurisée avec valeur par défaut #}
{{ states('sensor.compteur') | float(default=0) }}

Valeur de secours : default()

{# Retourner 0 si l'entité est unavailable ou unknown #}
{{ states('sensor.puissance') | float(default=0) }}

{# Retourner une chaîne si None #}
{{ state_attr('media_player.salon', 'media_title')
   | default('Rien en cours') }}

Manipulation des dates et timestamps

{# Convertir un timestamp Unix en objet datetime #}
{{ states('sensor.dernier_mouvement')
   | as_datetime
   | as_local }}

{# Formater une date lisible en français #}
{{ now().strftime('%d/%m/%Y à %H:%M') }}

{# Calculer une durée depuis le dernier changement d'état #}
{% set delta = now() - states.light.salon.last_changed %}
{{ (delta.total_seconds() / 60) | round(0) }} minutes

Calculs sur les consommations : multiply() et l'opérateur /

Home Assistant fournit le filtre multiply() pour les multiplications. Pour la division, utilisez l'opérateur arithmétique standard / — il n'existe pas de filtre divide() dans HA.

{# Convertir Wh en kWh avec multiply() #}
{{ states('sensor.energie_consommee_wh')
   | float | multiply(0.001) | round(3) }} kWh

{# Division avec l'opérateur / standard #}
{% set wh = states('sensor.energie_consommee_wh')
    | float(default=0) %}
{{ (wh / 1000) | round(3) }} kWh

{# Calculer un coût en euros (tarif base EDF ~0.25€/kWh) #}
{% set kwh = states('sensor.conso_journaliere')
    | float(default=0) %}
{{ (kwh * 0.2516) | round(2) }} €

Cas d'usage pratiques copiables

Consommation électrique nette (solaire déduit)

Si vous avez un Shelly EM couplé à votre Linky , ce sensor calcule la consommation réseau réelle après déduction de la production solaire :

template:
  - sensor:
      - name: "Consommation nette"
        unique_id: consommation_nette
        unit_of_measurement: "W"
        device_class: power
        state_class: measurement
        state: >
          {% set import_w = states('sensor.linky_puissance')
              | float(default=0) %}
          {% set solaire_w = states('sensor.shelly_em_puissance')
              | float(default=0) %}
          {{ [import_w - solaire_w, 0] | max | round(0) }}

Détecter si tous les volets sont fermés

template:
  - binary_sensor:
      - name: "Tous les volets fermés"
        unique_id: volets_tous_fermes
        state: >
          {% set volets = [
            'cover.volet_salon',
            'cover.volet_chambre',
            'cover.volet_bureau'
          ] %}
          {{ volets | map('states') | list
             | reject('equalto', 'closed')
             | list | count == 0 }}

Message contextuel pour une notification

Utilisable dans les automatisations, notamment pour les réponses de votre assistant vocal local :

{# Dans le champ message d'une action notify #}
{% set temp = states('sensor.temperature_salon')
    | float(default=0) | round(1) %}
{% set heure = now().hour %}
{% if heure < 12 %}
  {% set moment = 'matin' %}
{% elif heure < 18 %}
  {% set moment = "après-midi" %}
{% else %}
  {% set moment = 'soir' %}
{% endif %}
Bon {{ moment }}. Il fait {{ temp }} degrés dans le salon.

Indice qualité d'air simplifié

Ce sensor exploite les entités disponibles via Zigbee2MQTT sur un capteur air Aqara ou IKEA VINDSTYRKA :

template:
  - sensor:
      - name: "Qualité air salon"
        unique_id: qualite_air_salon
        state: >
          {% set co2 = states('sensor.vindstyrka_co2')
              | float(default=0) %}
          {% set voc = states('sensor.vindstyrka_voc_index')
              | float(default=0) %}
          {% if co2 > 1500 or voc > 200 %}
            Mauvaise
          {% elif co2 > 1000 or voc > 100 %}
            Moyenne
          {% else %}
            Bonne
          {% endif %}
        icon: >
          {% if is_state('sensor.qualite_air_salon', 'Mauvaise') %}
            mdi:air-filter
          {% elif is_state('sensor.qualite_air_salon', 'Moyenne') %}
            mdi:smoke-detector
          {% else %}
            mdi:leaf
          {% endif %}

Macros custom_templates : factoriser son Jinja (depuis HA 2023.4)

Depuis la version 2023.4 de Home Assistant, il est possible de créer des macros Jinja réutilisables dans tout votre instance — automatisations, scripts, templates d'entités — via le dossier custom_templates/.

Créer le dossier et écrire une macro

Créez le dossier custom_templates/ à la racine de votre dossier de configuration Home Assistant (au même niveau que configuration.yaml). Créez ensuite un fichier custom_templates/utils.jinja :

{# custom_templates/utils.jinja #}

{# Macro : formater une puissance en W ou kW selon la valeur #}
{% macro format_puissance(entity_id) %}
  {% set val = states(entity_id) | float(default=0) %}
  {% if val >= 1000 %}
    {{ (val / 1000) | round(2) }} kW
  {% else %}
    {{ val | round(0) }} W
  {% endif %}
{% endmacro %}

{# Macro : état lisible d'une entité binaire #}
{% macro etat_binaire(entity_id, label_on='Actif', label_off='Inactif') %}
  {% if is_state(entity_id, 'on') %}
    {{ label_on }}
  {% else %}
    {{ label_off }}
  {% endif %}
{% endmacro %}

Importer et appeler une macro

Depuis n'importe quel template dans Home Assistant, importez et appelez la macro avec la syntaxe suivante :

{# Dans un template sensor ou une automatisation #}
{% from 'utils.jinja' import format_puissance, etat_binaire %}

Puissance actuelle :
{{ format_puissance('sensor.linky_puissance_active') }}

Chauffage :
{{ etat_binaire(
     'switch.chauffage_salon',
     label_on='En chauffe',
     label_off='Éteint'
   ) }}

Les macros sont rechargées sans redémarrage via Outils développeurs → Actions → homeassistant.reload_custom_templates. Elles sont particulièrement utiles pour centraliser les formats d'affichage que vous répétez dans plusieurs automatisations.

Déboguer un template qui ne fonctionne pas

Les trois erreurs les plus fréquentes sur le forum HACF sont toujours les mêmes. Voici comment les diagnostiquer et les corriger.

Erreur "float of None" ou TypeError

Cause : vous appliquez un filtre numérique (float, int, round) sur une valeur qui est None, unavailable ou unknown. Solution : utilisez toujours float(default=0) ou vérifiez avec is_number avant l'opération.

{# Plante si sensor indisponible #}
{{ states('sensor.temperature') | float | round(1) }}

{# Robuste #}
{{ states('sensor.temperature') | float(default=0) | round(1) }}

Template qui affiche "unavailable" ou "unknown"

Ajoutez la clé availability: à votre entité template. Si cette expression retourne false, l'entité affiche unavailable proprement plutôt qu'une valeur calculée incorrecte. C'est aussi ce que Google prend en compte pour les graphiques énergétiques.

template:
  - sensor:
      - name: "Mon sensor calculé"
        state: >
          {{ states('sensor.source') | float | round(1) }}
        availability: >
          {{ states('sensor.source') not in
             ['unavailable', 'unknown', 'none'] }}

Méthode de débogage pas à pas

La méthode efficace en trois étapes :

  1. Isoler l'expression problématique dans l'éditeur Outils développeurs → Modèle. Tester uniquement {{ states('sensor.mon_entite') }} pour confirmer que l'entité existe et renvoie une valeur utilisable.
  2. Ajouter les transformations une par une : d'abord | float, puis | round(1), etc. Identifier précisément l'étape qui produit une erreur.
  3. Consulter les logs : Paramètres → Système → Journaux. Filtrer sur "template" pour trouver les erreurs de rendu enregistrées.
{# Débogage : afficher le type et la valeur d'une variable #}
{% set val = states('sensor.ma_sonde') %}
Type : {{ val.__class__.__name__ }}
Valeur brute : "{{ val }}"
Is number : {{ val | is_number }}

Une fois votre template validé dans l'éditeur, rechargez uniquement les templates via Outils développeurs → YAML → Recharger les entités modèles — inutile de redémarrer Home Assistant entièrement pour les entités déclarées sous la clé template:.

Les templates Jinja constituent la colonne vertébrale de toute installation Home Assistant avancée. Maîtriser les bases présentées ici — accéder aux entités, filtrer les valeurs, créer des sensors calculés et factoriser avec des macros — vous ouvre la porte à des automatisations beaucoup plus intelligentes et maintenables. Le format moderne template: que vous venez de découvrir est celui qui sera supporté durablement : les tutos FR antérieurs à 2025.12 qui utilisent encore sensor: platform: template sont à mettre de côté.