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 :
- Calculer la consommation électrique journalière nette en soustrayant la production solaire de la consommation brute — utile si vous avez un dashboard énergie avec Linky et Shelly EM .
- Afficher "Maison occupée" ou "Maison vide" en combinant la présence de plusieurs personnes et l'état d'un capteur de présence LD2410 .
- Construire un message vocal contextualisé pour votre assistant vocal local qui dit "Il fait 22 degrés dans le salon" plutôt qu'une valeur brute.
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 :
-
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. -
Ajouter les transformations une par une :
d'abord
| float, puis| round(1), etc. Identifier précisément l'étape qui produit une erreur. - 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é.