
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 8 · Índice del curso
Una plantilla puede funcionar perfectamente durante semanas y fallar justo después de un reinicio, cuando un sensor tarda unos segundos en publicar su primer valor. También puede seguir funcionando técnicamente y tomar una mala decisión: convertir unavailable en cero grados y encender la calefacción como si esa medición fuera real.
En este capítulo utilizaremos variables para guardar valores dentro de una ejecución y aprenderemos a tratar unknown y unavailable como situaciones previstas. El objetivo no es esconder todos los fallos detrás de un valor predeterminado. Es decidir qué debe hacer la automatización cuando no dispone de un dato fiable.
Nuestro caso será el ventilador del salón:
- Si la temperatura es válida y alcanza 25 °C, encender el ventilador.
- Si la temperatura es válida y está por debajo de 25 °C, apagarlo.
- Si no existe una temperatura utilizable, no cambiar el ventilador y crear un aviso.
YAML no tiene variables de ejecución por sí mismo
En artículos antiguos se habla con frecuencia de «variables YAML». La expresión mezcla varias cosas diferentes.
YAML puede almacenar una pareja como:
threshold: 25
Eso solo representa un dato con una clave y un valor. YAML no mantiene una variable que cambie mientras una automatización se ejecuta.
Home Assistant sí ofrece variables dentro de su sintaxis de scripts y automatizaciones:
- variables:
temperature_text: "{{ states('sensor.course_living_room_temperature') }}"
Y Jinja también permite variables locales dentro de una plantilla:
{% set temperature = states('sensor.course_living_room_temperature') | float(0) %}
{{ temperature }}
Las tres líneas están escritas dentro de documentos o textos YAML en algún momento, pero no pertenecen a la misma capa:
- YAML organiza claves y valores.
- Home Assistant define la acción
variablesy su alcance en la secuencia. - Jinja define
{% set %}dentro del renderizado de una plantilla.
En este capítulo nos centraremos principalmente en variables de Home Assistant, que pueden reutilizarse entre varias acciones de una misma ejecución.
Guardar un valor dentro de la secuencia
La acción variables crea valores disponibles para los pasos posteriores:
actions:
- variables:
temperature_text: >-
{{ states('sensor.course_living_room_temperature') }}
- variables:
temperature_is_valid: >-
{{ temperature_text not in ['unknown', 'unavailable'] }}
- action: persistent_notification.create
data:
title: "Curso YAML - Temperatura"
message: "Valor recibido: {{ temperature_text }}"
Las dos primeras acciones no controlan ningún dispositivo. La primera lee la entidad y guarda el resultado. La segunda valida el texto ya guardado, sin volver a consultar el sensor:
temperature_textconserva el estado leído como texto.temperature_is_validindica si la entidad tiene un valor distinto deunknownyunavailable.
El mensaje posterior utiliza esa misma lectura. De este modo toda la ejecución trabaja con una fotografía coherente del valor, aunque el sensor vuelva a actualizarse unos instantes después.
El nombre debe explicar el tipo y la intención
Llamar a la variable temperature_text nos recuerda que todavía no hemos convertido el valor. Si la llamáramos simplemente temperature, podríamos utilizarla por error en una comparación numérica.
Un buen nombre no sustituye la conversión, pero reduce ambigüedades:
temperature_text → estado original como texto
temperature_is_valid → decisión booleana
temperature → número después de convertir
has_value() y la diferencia entre consultar y guardar
La función:
{{ has_value('sensor.course_living_room_temperature') }}
devuelve falso si el estado es unknown o unavailable. También nos protege cuando la entidad indicada no existe, porque la consulta no aporta un valor utilizable.
No confirma que cualquier texto pueda convertirse en el número que esperamos. Una integración defectuosa podría publicar otro texto. En sensores numéricos bien definidos, comprobar disponibilidad y convertir con un valor predeterminado adecuado suele cubrir los casos comunes. En entradas externas no controladas necesitaríamos validaciones adicionales.
Cuando solo necesitamos saber si una entidad tiene un estado utilizable en ese momento, has_value() expresa muy bien la intención. Sin embargo, recibe un entity_id y consulta la entidad. Si antes hemos ejecutado states() para guardar una fotografía, llamar después a has_value() supone una segunda consulta que podría observar una actualización posterior.
Para validar exactamente el texto ya guardado utilizamos:
{{ temperature_text not in ['unknown', 'unavailable'] }}
No es un reemplazo universal de has_value(). Es la opción apropiada cuando queremos que lectura, validación, conversión y mensaje se basen en el mismo valor. La elección depende de la intención: consultar el estado actual de la entidad o trabajar con una lectura guardada.
Por qué float(0) puede ser seguro técnicamente y erróneo lógicamente
Esta conversión no genera un error si el sensor está ausente:
{{ states('sensor.course_living_room_temperature') | float(0) }}
Si el estado es unavailable, devuelve 0. Eso evita una excepción. Pero cero se convierte en una medición aparentemente válida.
Imagina una automatización de protección contra heladas:
{{ states('sensor.outdoor_temperature') | float(0) < 3 }}
Cuando el sensor desaparece, la condición sería verdadera porque 0 < 3. Quizá prefieras activar una protección por seguridad; quizá prefieras enviar un aviso y no actuar. Lo importante es que esa sea una decisión explícita, no un efecto accidental del valor predeterminado.
El valor alternativo debe elegirse según el dominio:
- Para mostrar un contador, cero puede tener sentido.
- Para una temperatura física desconocida, cero puede ser engañoso.
- Para decidir sobre seguridad, falta definir una política, no solo una conversión.
Una automatización con camino válido y camino sin datos
La solución completa utiliza un choose exterior:
alias: "Curso YAML - Controlar ventilador por temperatura"
description: >-
Controla el ventilador si la temperatura es válida y avisa cuando
el sensor no tiene datos utilizables.
triggers:
- trigger: state
entity_id: sensor.course_living_room_temperature
conditions: []
actions:
- variables:
temperature_text: >-
{{ states('sensor.course_living_room_temperature') }}
- variables:
temperature_is_valid: >-
{{ temperature_text not in ['unknown', 'unavailable'] }}
- choose:
- alias: "La temperatura es válida"
conditions:
- condition: template
value_template: "{{ temperature_is_valid }}"
sequence:
- variables:
temperature: "{{ temperature_text | float }}"
- choose:
- alias: "Temperatura alta"
conditions:
- condition: template
value_template: "{{ temperature >= 25 }}"
sequence:
- action: switch.turn_on
target:
entity_id: switch.course_fan
default:
- action: switch.turn_off
target:
entity_id: switch.course_fan
default:
- action: persistent_notification.create
data:
title: "Curso YAML - Temperatura no disponible"
message: >-
No se ha modificado el ventilador porque el sensor de temperatura
tiene el estado {{ temperature_text }}.
mode: restart
El trigger no especifica from ni to porque queremos reevaluar el control cuando cambie el estado de temperatura, cualquiera que sea su valor anterior.
Las dos primeras acciones separan lectura y validación: la entidad se consulta una vez y la validez se calcula a partir de esa fotografía. El choose exterior separa dos políticas:
- Dato válido: convertir y decidir sobre el ventilador.
- Dato no válido: conservar el ventilador como está y avisar.
Dentro de la rama válida, temperature_text | float ya no necesita convertir unknown o unavailable, porque la condición anterior ha filtrado esos estados. Aun así, en configuraciones con datos menos controlados se podría mantener un fallback y una validación más estricta.
El choose interior compara el número. Si alcanza 25, enciende el ventilador; en cualquier otro valor válido, lo apaga.
El alcance: dónde existe una variable
temperature_text y temperature_is_valid se crean antes de choose, por lo que ambas ramas pueden utilizarlas.
temperature se crea dentro de la secuencia de «La temperatura es válida». La utilizamos únicamente dentro de esa rama. No debemos asumir que existe después del choose, porque el camino sin datos nunca ejecuta su asignación.
Este diseño sería frágil:
actions:
- choose:
- conditions:
- condition: state
entity_id: binary_sensor.course_someone_home
state: "on"
sequence:
- variables:
greeting: "Hola desde casa"
- action: persistent_notification.create
data:
message: "{{ greeting }}"
Si no hay nadie en casa, greeting no se define y la acción posterior intenta utilizar una variable inexistente.
Cuando un valor será necesario después de varias ramas, inicialízalo antes o asegúrate de asignarlo en todos los caminos.
Variables proporcionadas por el trigger
Las automatizaciones reciben una variable especial llamada trigger con información sobre aquello que inició la ejecución.
En un trigger de estado podemos acceder a los objetos anterior y nuevo:
message: >-
La temperatura ha cambiado de
{{ trigger.from_state.state }} a {{ trigger.to_state.state }}.
Este valor solo existe cuando la automatización se inicia mediante un trigger que aporta esos datos. Si pulsas Ejecutar acciones, Home Assistant omite el trigger y la variable puede no estar disponible.
Por eso una plantilla puede funcionar durante una activación real y fallar al ejecutar acciones manualmente, o al revés si en el editor has simulado valores que el contexto real no tiene.
Los identificadores que vimos en el capítulo 5 también están disponibles como:
{{ trigger.id }}
No necesitamos utilizar Jinja para compararlos cuando existe la condición trigger, pero la variable resulta útil para construir mensajes o registrar información.
Campos de script y variables recibidas
Retomemos el script de mensajes del capítulo anterior. Un campo declara qué dato espera la interfaz:
fields:
message_text:
name: "Mensaje"
required: true
selector:
text:
La llamada proporciona el valor:
action: script.course_create_message
data:
message_text: "La puerta se ha abierto."
Y la secuencia lo utiliza como variable:
message: "{{ message_text }}"
Declarar un campo mejora la interfaz y documenta la entrada, pero la marca required: true es principalmente una ayuda de la interfaz. Una llamada YAML mal construida puede no proporcionar el dato. Si el script se utiliza desde muchos contextos, conviene definir un valor predeterminado o comprobar que la variable existe según el resultado deseado.
Variables Jinja con {% set %}
Cuando una variable solo sirve dentro de un mismo texto, puede ser más natural crearla dentro de la plantilla:
{% set temperature = states('sensor.course_living_room_temperature') | float(0) %}
La temperatura actual es {{ temperature }} °C.
{% set %} pertenece a Jinja y vive durante ese renderizado. La acción variables pertenece a Home Assistant y permite compartir datos entre pasos posteriores de la secuencia.
Elige según el alcance necesario:
- Solo dentro de una plantilla:
{% set %}puede ser suficiente. - Entre varias acciones: utiliza
variablesde Home Assistant. - Dato entregado al llamar un script: utiliza campos y datos de la llamada.
Llamar a todo «variable YAML» oculta estas diferencias y dificulta encontrar la documentación correcta.
Cómo simular unavailable en el laboratorio
Para probar el camino sin datos, puedes utilizar temporalmente Configuración → Herramientas → Estados y establecer el estado de:
sensor.course_living_room_temperature
como:
unavailable
Recuerda lo aprendido en el capítulo 3: esto cambia la representación interna temporal, no el control input_number que alimenta el sensor. La entidad template puede sobrescribir el estado cuando vuelva a actualizarse.
Después de simularlo:
- Comprueba que se crea la notificación de datos no disponibles.
- Verifica que el ventilador no cambia.
- Modifica el control de temperatura para restaurar una lectura normal.
- Prueba valores por debajo y por encima de 25.
Una plantilla robusta debe probarse tanto con datos normales como con los estados especiales que afirma manejar.
Error deliberado: una variable que solo existe a veces
Analiza este flujo:
actions:
- choose:
- conditions:
- condition: state
entity_id: binary_sensor.course_someone_home
state: "on"
sequence:
- variables:
status_message: "Hay alguien en casa"
- action: persistent_notification.create
data:
message: "{{ status_message }}"
Cuando ninguna rama coincide, no se ejecuta la asignación y status_message nunca se crea. Una solución consiste en inicializarla antes:
actions:
- variables:
status_message: "No hay nadie en casa"
- choose:
- conditions:
- condition: state
entity_id: binary_sensor.course_someone_home
state: "on"
sequence:
- variables:
status_message: "Hay alguien en casa"
- action: persistent_notification.create
data:
message: "{{ status_message }}"
Ahora existe un valor en todos los caminos y la rama verdadera lo sustituye.
Ejercicio: controla el ventilador sin inventar una temperatura
Construye la automatización con estos requisitos:
- Reevaluar cuando cambie
sensor.course_living_room_temperature. - Guardar el estado como
temperature_text. - Comprobar si la lectura guardada es
unknownounavailable, sin volver a consultar la entidad. - Convertirlo a número únicamente en la rama válida.
- Encender el ventilador con 25 °C o más.
- Apagarlo por debajo de 25 °C.
- Si no hay dato válido, no tocar el ventilador y crear una notificación.
- Probar
unavailable, 24 y 26.
Ver la solución explicada
La solución completa se ha mostrado anteriormente y está disponible como archivo del laboratorio. La decisión importante no es el número de bloques choose, sino la separación de políticas:
¿Existe un dato válido?
├── No → avisar y conservar el estado del ventilador
└── Sí → convertir
├── temperatura >= 25 → encender
└── temperatura < 25 → apagar
Convertir después de comprobar disponibilidad evita disfrazar unavailable como una temperatura real. Mantener el uso de temperature dentro de la rama donde se crea evita un error de alcance.
Qué debes llevarte de este capítulo
Las variables utilizadas por automatizaciones y scripts pertenecen a Home Assistant, no a YAML. Jinja también puede crear variables locales mediante {% set %}, y los campos de scripts entregan datos como variables de entrada. El alcance de cada mecanismo es diferente.
unknown y unavailable son estados esperables. Un fallback numérico impide un error técnico, pero puede introducir una decisión doméstica falsa. Primero define qué debe ocurrir cuando falta el dato; después escribe la conversión que representa esa política.
En el siguiente capítulo pasaremos de la lógica a la organización. Abriremos configuration.yaml, automations.yaml, scripts.yaml, las directivas !include, packages y secrets.yaml, y veremos por qué cada archivo incluido debe tener la forma que espera su punto de inserción.
Continúa leyendo
Siguiente guía relacionada · 11 min de lectura
YAML y Jinja no son lo mismo: primeras plantillas en Home Assistant
← 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…
Continuar con este artículo

