Saltar al contenido

YAML y Jinja no son lo mismo: primeras plantillas en Home Assistant

23/10/2026
Primeras plantillas YAML y Jinja en Home Assistant

Este artículo puede contener enlaces de afiliado. Si compras desde estos enlaces, el precio para ti es el mismo y la tienda me paga una pequeña comisión que ayuda a mantener Tecnoyfoto.

← Capítulo 7 · Índice del curso

Hasta ahora todos nuestros valores eran fijos. Una acción encendía siempre la misma luz y una notificación habría mostrado siempre el mismo mensaje. Pero una casa cambia: la temperatura no es constante, puede haber alguien o no y la entidad que inicia una automatización puede ser distinta en cada ejecución.

Home Assistant utiliza plantillas para calcular valores a partir de esa situación. Su motor está basado en Jinja. La plantilla se escribe dentro de un valor YAML, pero Jinja y YAML continúan siendo capas distintas.

Esta separación es el objetivo principal del capítulo. Aprenderemos a reconocer dónde termina la estructura YAML y dónde empieza la expresión dinámica. Después leeremos estados y atributos, probaremos el resultado en el editor de plantillas y crearemos un mensaje que describa la casa virtual.

OFERTAS · TIENDA OFICIAL

Descuentos en domótica SONOFF

Interruptores WiFi, relés, sensores, tiras LED y más. Las promociones cambian con frecuencia en la tienda oficial.

Cupón: TECNOYFOTO (10% de descuento al pagar)

Ver ofertas oficiales Enlace de afiliado · Tienda Sonoff

El problema que un valor fijo no puede resolver

Esta acción crea siempre el mismo mensaje:

action: persistent_notification.create
data:
  title: "Curso YAML - Estado del salón"
  message: "La temperatura del salón es de 21 grados."

YAML organiza una acción con un mapa data. Home Assistant interpreta persistent_notification.create y recibe dos textos. No hay ninguna instrucción para consultar el sensor, así que el mensaje seguirá diciendo 21 aunque la temperatura cambie.

Podemos insertar el estado actual con una plantilla:

action: persistent_notification.create
data:
  title: "Curso YAML - Estado del salón"
  message: >-
    La temperatura del salón es de
    {{ states('sensor.course_living_room_temperature') }} °C.

message continúa siendo una clave YAML. >- continúa siendo la sintaxis YAML que abre un texto multilínea plegado. Dentro de ese texto, {{ ... }} delimita una expresión Jinja que Home Assistant evaluará antes de ejecutar la acción.

Podemos describir las responsabilidades:

YAML: message contiene un bloque de texto.
Jinja: calcula qué texto insertar entre las llaves.
Home Assistant: aporta la función states() y ejecuta la acción.

Las tres marcas que debes reconocer

Jinja utiliza varios delimitadores. Para este nivel necesitamos tres.

{{ ... }} calcula y muestra un resultado

{{ states('sensor.course_living_room_temperature') }}

Home Assistant consulta el estado y coloca el resultado donde aparece la expresión. Si el sensor vale 22.5, la salida será:

22.5

Piensa en las dobles llaves como «calcula esto e insértalo aquí».

{% ... %} controla la lógica

{% if is_state('binary_sensor.course_someone_home', 'on') %}
  Hay alguien en casa.
{% else %}
  No hay nadie en casa.
{% endif %}

Los bloques {% ... %} no imprimen por sí mismos la condición. Deciden qué parte del texto se genera. if abre la decisión, else proporciona el camino alternativo y endif la cierra.

{# ... #} añade un comentario de Jinja

{# Este comentario no aparece en el resultado #}

No debes confundirlo con un comentario YAML:

# Este comentario pertenece a YAML

Ambos desaparecen del resultado final, pero los procesa una capa diferente y se escriben con marcas distintas.

El editor de plantillas: prueba antes de automatizar

Abre Configuración → Herramientas → Plantilla. Este editor evalúa una plantilla inmediatamente y muestra el resultado o un error.

Empieza con:

La temperatura es {{ states('sensor.course_living_room_temperature') }} °C.

Cambia el control de temperatura del laboratorio y observa cómo se actualiza la salida.

Después prueba:

{{ is_state('binary_sensor.course_someone_home', 'on') }}

El resultado será True o False. Activa y desactiva el control de presencia para comprobarlo.

El editor permite separar dos preguntas:

  1. ¿La plantilla genera el valor esperado?
  2. ¿La acción utiliza correctamente ese valor?

Si falla el mensaje dentro de una automatización, primero copia la plantilla al editor. Si funciona allí, revisa después el contexto y la estructura YAML donde la has colocado.

Algunas variables solo existen dentro de una automatización o script, como trigger o los campos recibidos por un script. El editor no puede inventar automáticamente ese contexto. Aprenderemos a reconocerlo cuando utilicemos esas variables.

Leer el estado con states()

La forma recomendada para leer el estado de una entidad concreta es:

{{ states('sensor.course_living_room_temperature') }}

El entity_id se proporciona como texto dentro de los paréntesis. Las comillas simples pertenecen a la expresión Jinja. Si toda la plantilla está dentro de una cadena YAML con comillas dobles, la combinación resulta legible:

message: "Temperatura: {{ states('sensor.course_living_room_temperature') }} °C"

Podríamos invertir las comillas, pero tendríamos que proteger o reorganizar las interiores. Alternar dobles por fuera y simples dentro evita escapes innecesarios.

states() devuelve el estado como texto. Aunque veas 22.5, no debes asumir que ya es un número disponible para cálculos. La diferencia aparecerá cuando queramos comparar o sumar.

Evitar el acceso directo frágil

En ejemplos antiguos puedes encontrar:

{{ states.sensor.course_living_room_temperature.state }}

El acceso directo puede producir errores si la entidad no está disponible en el momento de evaluar la plantilla. La función states('entity_id') devuelve un resultado controlado y es la forma que utilizaremos en el curso.

Comprobar un estado con is_state()

Para saber si una entidad está en un estado concreto:

{{ is_state('binary_sensor.course_someone_home', 'on') }}

La función devuelve verdadero o falso. Se lee de forma natural:

¿El estado de binary_sensor.course_someone_home es on?

Podemos utilizarla en un if:

{% if is_state('binary_sensor.course_someone_home', 'on') %}
  Hay alguien en casa.
{% else %}
  La casa está vacía.
{% endif %}

No necesitamos escribir:

{% if is_state('binary_sensor.course_someone_home', 'on') == true %}

is_state() ya produce un booleano. Compararlo otra vez con true añade ruido.

Leer un atributo con state_attr()

En el capítulo 3 vimos que la unidad de temperatura es un atributo. Podemos consultarla con:

{{ state_attr('sensor.course_living_room_temperature', 'unit_of_measurement') }}

El primer argumento es la entidad y el segundo, el nombre del atributo.

Así evitamos escribir la unidad manualmente:

La temperatura es
{{ states('sensor.course_living_room_temperature') }}
{{ state_attr('sensor.course_living_room_temperature', 'unit_of_measurement') }}.

Si el atributo no existe, state_attr() devuelve none. Antes de utilizar un atributo encontrado en Internet, comprueba en Estados que tu entidad lo ofrece realmente.

Texto de una línea o bloque multilínea

Una plantilla corta cabe en una cadena:

message: "Temperatura: {{ states('sensor.course_living_room_temperature') }} °C"

Cuando hay decisiones, un bloque resulta más legible:

message: >-
  {% if is_state('binary_sensor.course_someone_home', 'on') %}
    Hay alguien en casa.
  {% else %}
    La casa está vacía.
  {% endif %}

YAML controla la indentación exterior. Todo el contenido debe pertenecer a message. Jinja controla la decisión interior.

Si desplazamos {% else %} fuera del bloque YAML, el error no significa que else esté mal escrito. Significa que la estructura exterior ya no considera esa línea parte del valor de message.

En plantillas largas, alinear if, else y endif ayuda a ver la lógica, aunque Jinja no utilice esos espacios exactamente como YAML.

Convertir texto a número

Para realizar un cálculo, convertimos el estado:

{{ states('sensor.course_living_room_temperature') | float(0) }}

El símbolo | aplica un filtro. float(0) intenta producir un número decimal y utiliza 0 como alternativa si la conversión falla.

Podemos sumar dos grados:

{{ states('sensor.course_living_room_temperature') | float(0) + 2 }}

Los paréntesis pueden hacer más clara una expresión compleja:

{{ (states('sensor.course_living_room_temperature') | float(0)) + 2 }}

El valor alternativo 0 evita un error técnico, pero no siempre representa una decisión doméstica correcta. Si el sensor está unavailable, ¿queremos tratarlo como cero grados o avisar de que no hay datos? En el próximo capítulo distinguiremos conversión segura de significado correcto.

Un mensaje dinámico completo

Construyamos una plantilla que combine presencia y temperatura:

{% if is_state('binary_sensor.course_someone_home', 'on') %}
  Hay alguien en casa. La temperatura del salón es de
  {{ states('sensor.course_living_room_temperature') }}
  {{ state_attr('sensor.course_living_room_temperature', 'unit_of_measurement') }}.
{% else %}
  No hay nadie en casa.
{% endif %}

Pruébala en el editor con presencia activada y desactivada. Cambia también la temperatura.

Después introdúcela en una acción:

action: persistent_notification.create
data:
  title: "Curso YAML - Estado de la casa"
  message: >-
    {% if is_state('binary_sensor.course_someone_home', 'on') %}
      Hay alguien en casa. La temperatura del salón es de
      {{ states('sensor.course_living_room_temperature') }}
      {{ state_attr('sensor.course_living_room_temperature', 'unit_of_measurement') }}.
    {% else %}
      No hay nadie en casa.
    {% endif %}

Ejecuta la acción desde Herramientas → Acciones. Primero Home Assistant renderiza la plantilla y obtiene un texto. Después entrega ese texto a la acción de notificación persistente.

Volver a los campos de un script

En el capítulo anterior dejamos preparado el concepto de campos. Ahora podemos ver cómo se utiliza un valor recibido.

course_create_message:
  alias: "Curso YAML - Crear mensaje"
  fields:
    message_text:
      name: "Mensaje"
      description: "Texto que mostrará la notificación."
      required: true
      selector:
        text:
  sequence:
    - action: persistent_notification.create
      data:
        title: "Curso YAML"
        message: "{{ message_text }}"
  mode: queued

Las capas son claras:

  • YAML define fields, sequence y data.
  • Home Assistant recibe un campo llamado message_text y lo expone como variable.
  • Jinja inserta el valor mediante {{ message_text }}.

La llamada directa proporciona el dato:

action: script.course_create_message
data:
  message_text: "La puerta se ha abierto."

El nombre del campo debe coincidir exactamente. Si escribimos message en la llamada y message_text en la plantilla, la variable esperada no existe.

Error deliberado: delimitadores fuera del valor YAML

Este bloque está mal estructurado:

message:
{{ states('sensor.course_living_room_temperature') }}

La expresión no está indentada dentro de message. YAML intenta interpretar las llaves en su propia estructura y puede producir un error antes de que Jinja llegue a evaluarse.

Una forma correcta es:

message: "{{ states('sensor.course_living_room_temperature') }}"

O, en bloque:

message: >-
  {{ states('sensor.course_living_room_temperature') }}

Cuando una plantilla falla, pregunta primero qué capa está informando:

  • ¿YAML no puede construir el documento?
  • ¿Home Assistant no admite una plantilla en ese campo?
  • ¿Jinja no puede evaluar la expresión?
  • ¿La expresión funciona pero devuelve un valor no deseado?

Ejercicio: describe la casa con una plantilla

Crea una notificación persistente que cumpla:

  • Si hay alguien en casa, mostrar la temperatura y su unidad.
  • Si no hay nadie, mostrar La casa está vacía.
  • No escribir manualmente el valor de temperatura.
  • Probar primero la plantilla en el editor.
  • Ejecutar después la acción completa.

Como ampliación, añade una primera frase que indique si la puerta está abierta o cerrada mediante otro if.

Ver la solución explicada
action: persistent_notification.create
data:
  title: "Curso YAML - Estado de la casa"
  message: >-
    {% if is_state('binary_sensor.course_front_door', 'on') %}
      La puerta está abierta.
    {% else %}
      La puerta está cerrada.
    {% endif %}

    {% if is_state('binary_sensor.course_someone_home', 'on') %}
      Hay alguien en casa. La temperatura del salón es de
      {{ states('sensor.course_living_room_temperature') }}
      {{ state_attr('sensor.course_living_room_temperature', 'unit_of_measurement') }}.
    {% else %}
      La casa está vacía.
    {% endif %}

YAML trata todo lo situado bajo message: >- como un bloque de texto. Los dos grupos Jinja toman decisiones independientes. states() obtiene el valor principal y state_attr() su unidad.

La plantilla no realiza cálculos, así que no necesitamos convertir la temperatura todavía.

Qué debes llevarte de este capítulo

YAML estructura los datos. Jinja genera valores dinámicos dentro de determinados campos. Home Assistant aporta estados, funciones y el contexto, renderiza la plantilla y ejecuta la configuración resultante.

{{ ... }} inserta un resultado, {% ... %} controla la lógica y {# ... #} crea comentarios de Jinja. states(), is_state() y state_attr() permiten leer la casa sin utilizar accesos directos frágiles.

El editor de plantillas ofrece el ciclo de prueba más rápido. Utilízalo antes de esconder una expresión dentro de una automatización grande.

En el siguiente capítulo trabajaremos con variables y datos que pueden no estar disponibles. Aprenderemos a no confundir «la plantilla no genera un error» con «la plantilla toma una decisión correcta cuando el sensor vale unknown o unavailable».

Continúa leyendo

Siguiente guía relacionada · 11 min de lectura

Scripts: reutiliza acciones sin duplicar YAML

← Capítulo 6 · Índice del curso Una automatización responde a un acontecimiento. Un script, en cambio, describe una secuencia que Home Assistant ejecuta…

Continuar con este artículo