Home Assistant MQTT discovery permite que un dispositivo se describa a sí mismo para que Home Assistant cree sus entidades automáticamente, sin escribir YAML en Home Assistant. En este tutorial aprenderás el formato de los topics de discovery, escribirás payloads de configuración para sensores y un interruptor, los agruparás en un único dispositivo, gestionarás la disponibilidad y los reinicios de Home Assistant y eliminarás entidades de forma limpia, todo con un simulador de dispositivo en Python conectado a un broker de ConnectMQ.
Cómo funciona MQTT Discovery
Cuando se inicia la integración MQTT, se suscribe a los topics que cuelgan de un prefijo de discovery, que por defecto es homeassistant. Un dispositivo publica un mensaje JSON retenido por cada entidad en un topic de configuración:
<discovery_prefix>/<component>/[<node_id>/]<object_id>/config
homeassistant/sensor/hvac-node-1/temperature/config
homeassistant/switch/hvac-node-1/relay/config
componentes la plataforma de la entidad:sensor,binary_sensor,switch,light,button,number,select,climatey otras.node_ides opcional y solo agrupa topics; no crea el dispositivo.object_ididentifica la entidad dentro del nodo.
El payload de configuración indica a Home Assistant dónde leer el estado, dónde enviar comandos, cómo interpretar los valores y a qué dispositivo pertenece la entidad. Como el mensaje es retenido, el broker lo vuelve a entregar cada vez que Home Assistant se reconecta, de modo que las entidades reaparecen tras un reinicio.
Discovery es solo metadatos. Los datos reales siguen circulando por tus propios topics de estado y de comandos, que diseñas igual que en cualquier integración MQTT de Home Assistant.
Requisitos previos
- Home Assistant con la integración MQTT configurada y discovery activado (lo está por defecto).
- Un broker privado de ConnectMQ: créalo en el portal y anota el usuario, la contraseña y el client ID generados. Home Assistant y el simulador necesitan cada uno sus propias credenciales.
- Python 3.10+ con
pip install "paho-mqtt>=2.0".
También puedes experimentar con el servidor MQTT público gratuito, pero hay dos cosas que lo complican para discovery. Primero, los topics de configuración retenidos bajo el prefijo compartido homeassistant son visibles para todas las instancias de Home Assistant conectadas allí, así que define un prefijo de discovery personalizado en las opciones de la integración (por ejemplo cmqx7d2). Segundo, el broker público admite 10 suscripciones por cliente, y Home Assistant necesita varias para discovery más una por entidad. Un broker privado evita ambos problemas.
Un payload de discovery, campo a campo
Esta es la configuración de un sensor de temperatura:
{
"name": "Supply Air Temperature",
"unique_id": "hvacnode1_supply_temp",
"object_id": "hvac_node_1_supply_temp",
"state_topic": "home/hvac-node-1/state",
"value_template": "{{ value_json.supply_c }}",
"unit_of_measurement": "°C",
"device_class": "temperature",
"state_class": "measurement",
"availability_topic": "home/hvac-node-1/availability",
"device": {
"identifiers": ["hvacnode1"],
"name": "HVAC Node 1",
"manufacturer": "DIY",
"model": "ESP32 duct sensor",
"sw_version": "1.3.0"
}
}
| Campo | Por qué importa |
|---|---|
unique_id |
Imprescindible para el registro de entidades: renombrar, áreas, desactivar. Debe ser estable para siempre. |
device.identifiers |
Agrupa todas las entidades con el mismo identificador en una sola tarjeta de dispositivo. |
state_topic + value_template |
Varias entidades pueden leer campos distintos de un mismo mensaje de estado JSON. |
availability_topic |
Las entidades pasan a no disponibles cuando el dispositivo publica offline (normalmente mediante el last will). |
device_class, state_class |
Iconos, unidades y estadísticas a largo plazo correctos. |
object_id |
Influye en el ID de entidad generado. |
Los payloads largos pueden usar las abreviaturas documentadas, por ejemplo stat_t para state_topic, uniq_id para unique_id y dev para device. Son útiles en microcontroladores con buffers pequeños; en un PC, los nombres completos se leen mejor.
Un dispositivo completo en Python con discovery
El siguiente simulador se comporta como un dispositivo real: anuncia tres entidades, publica el estado en JSON cada diez segundos, obedece los comandos del interruptor, define un last will para la disponibilidad y vuelve a publicar su configuración cuando Home Assistant vuelve a estar en línea.
import json
import random
import time
import paho.mqtt.client as mqtt
BROKER, PORT = "broker.connectmq.com", 8883
USERNAME, PASSWORD = "YOUR_USERNAME", "YOUR_PASSWORD"
CLIENT_ID = "YOUR_CLIENT_ID"
DISCOVERY_PREFIX = "homeassistant"
NODE = "hvac-node-1"
BASE = f"home/{NODE}"
STATE_T = f"{BASE}/state"
AVAIL_T = f"{BASE}/availability"
RELAY_CMD_T = f"{BASE}/relay/set"
RELAY_STATE_T = f"{BASE}/relay/state"
DEVICE = {
"identifiers": ["hvacnode1"],
"name": "HVAC Node 1",
"manufacturer": "DIY",
"model": "ESP32 duct sensor",
"sw_version": "1.3.0",
}
ENTITIES = [
("sensor", "supply_temp", {
"name": "Supply Air Temperature",
"state_topic": STATE_T,
"value_template": "{{ value_json.supply_c }}",
"unit_of_measurement": "°C",
"device_class": "temperature",
"state_class": "measurement",
}),
("sensor", "filter_dp", {
"name": "Filter Pressure Drop",
"state_topic": STATE_T,
"value_template": "{{ value_json.filter_dp_pa }}",
"unit_of_measurement": "Pa",
"device_class": "pressure",
"state_class": "measurement",
}),
("switch", "relay", {
"name": "Fan Relay",
"command_topic": RELAY_CMD_T,
"state_topic": RELAY_STATE_T,
"payload_on": "ON",
"payload_off": "OFF",
}),
]
relay_on = False
def publish_discovery(client):
for component, object_id, cfg in ENTITIES:
payload = {
**cfg,
"unique_id": f"hvacnode1_{object_id}",
"availability_topic": AVAIL_T,
"device": DEVICE,
}
topic = f"{DISCOVERY_PREFIX}/{component}/{NODE}/{object_id}/config"
client.publish(topic, json.dumps(payload), qos=1, retain=True)
client.publish(AVAIL_T, "online", qos=1, retain=True)
client.publish(RELAY_STATE_T, "ON" if relay_on else "OFF", qos=1, retain=True)
def on_connect(client, userdata, flags, reason_code, properties):
print("connected:", reason_code)
client.subscribe(RELAY_CMD_T, qos=1)
client.subscribe("homeassistant/status", qos=1) # mensaje birth de Home Assistant
publish_discovery(client)
def on_message(client, userdata, msg):
global relay_on
if msg.topic == "homeassistant/status" and msg.payload == b"online":
time.sleep(random.uniform(0.5, 2.0)) # repartir la carga tras reinicios de HA
publish_discovery(client)
elif msg.topic == RELAY_CMD_T:
relay_on = msg.payload == b"ON"
client.publish(RELAY_STATE_T, "ON" if relay_on else "OFF", qos=1, retain=True)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.username_pw_set(USERNAME, PASSWORD)
client.tls_set()
client.will_set(AVAIL_T, "offline", qos=1, retain=True)
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, PORT, keepalive=30)
client.loop_start()
while True:
state = {"supply_c": round(random.uniform(12.5, 14.5), 1),
"filter_dp_pa": random.randint(150, 190)}
client.publish(STATE_T, json.dumps(state), qos=0)
time.sleep(10)
Ejecútalo y abre Ajustes > Dispositivos y servicios > MQTT. Aparece un dispositivo llamado HVAC Node 1 con dos sensores y un interruptor. Detén el script con Ctrl+C y, dentro del intervalo de keep-alive, el broker publica el last will y las entidades pasan a no disponibles.
La gestión del mensaje birth es importante. Home Assistant publica online en homeassistant/status al arrancar. Las configuraciones retenidas suelen hacer innecesario volver a publicar, pero un dispositivo que republica al recibir el birth también se recupera si se borraron los mensajes retenidos.
Discovery basado en dispositivo
Las versiones recientes de Home Assistant también aceptan un único mensaje de configuración por dispositivo en <prefix>/device/<object_id>/config, que enumera todas las entidades bajo una clave components (abreviada cmps), cada una con su platform (p). También necesita un bloque origin (o) que indique el software que lo publica:
{
"dev": {"ids": ["hvacnode1"], "name": "HVAC Node 1", "mf": "DIY", "mdl": "ESP32 duct sensor"},
"o": {"name": "hvac-node-firmware", "sw": "1.3.0"},
"avty_t": "home/hvac-node-1/availability",
"stat_t": "home/hvac-node-1/state",
"cmps": {
"supply_temp": {"p": "sensor", "dev_cla": "temperature", "unit_of_meas": "°C",
"val_tpl": "{{ value_json.supply_c }}", "uniq_id": "hvacnode1_supply_temp"},
"filter_dp": {"p": "sensor", "dev_cla": "pressure", "unit_of_meas": "Pa",
"val_tpl": "{{ value_json.filter_dp_pa }}", "uniq_id": "hvacnode1_filter_dp"}
}
}
Un solo mensaje retenido en lugar de muchos resulta atractivo para dispositivos con decenas de entidades. Usa un único estilo por dispositivo; no anuncies los mismos unique IDs por las dos vías.
Eliminar y modificar entidades
Para borrar una entidad, publica un payload vacío y retenido en su topic de configuración:
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID -r -n \
-t "homeassistant/sensor/hvac-node-1/filter_dp/config"
-n envía un mensaje de longitud cero y -r lo marca como retenido, lo que además borra la configuración almacenada en el broker. Para modificar una entidad, publica la nueva configuración en el mismo topic con el mismo unique_id. Cambiar el unique_id crea una entidad nueva y deja huérfana la anterior.
Lista de comprobación para resolver problemas
- No aparece nada. Comprueba que el prefijo de discovery de las opciones de la integración coincide con el que usa tu dispositivo y que discovery está activado.
- Las entidades aparecen pero no están disponibles. El topic de disponibilidad no tiene un mensaje
onlineretenido, o el payload no es exactamenteonline. - "Unique ID already exists" en el log. Dos configuraciones comparten un
unique_id. Hazlos únicos globalmente, idealmente derivados de una dirección MAC. - Dispositivos duplicados tras actualizar el firmware. Ha cambiado el valor de
device.identifiers. Mantenlo estable. - Entidades fantasma. Quedan configuraciones retenidas antiguas en el broker. Usa el explorador de topics de ConnectMQ para encontrarlas bajo el prefijo de discovery y bórralas con mensajes retenidos vacíos.
- Configuración rechazada sin aviso. Valida tu JSON; una coma final en un payload escrito a mano es el culpable habitual, y Home Assistant registra el error bajo la integración MQTT.
Discovery desde un ESP32
En un microcontrolador la lógica es idéntica: construye cada payload de configuración, publícalo retenido tras cada conexión correcta y suscríbete al topic de birth. Hay tres detalles que cambian respecto a Python. Primero, el buffer de paquetes por defecto de PubSubClient es pequeño, así que llama a mqtt.setBufferSize(1024) o a un valor mayor antes de conectar; de lo contrario, los payloads de discovery largos se descartan sin aviso. Segundo, deriva unique_id y device.identifiers de la dirección MAC del chip para que cada placa obtenga valores estables y únicos sin configuración manual. Tercero, publica la disponibilidad mediante el last will en connect() para que Home Assistant se entere de un corte de alimentación sin que el dispositivo tenga que hacer nada.
Las claves abreviadas ayudan en dispositivos pequeños, y construir los payloads con ArduinoJson evita errores de comillas en cadenas escritas a mano. Prueba los payloads de tu firmware suscribiéndote a homeassistant/# con cualquier cliente MQTT y comprobando que cada configuración es JSON válido antes de buscar las entidades en Home Assistant.
Dónde encaja discovery
Firmwares como Tasmota, ESPHome en modo MQTT y Zigbee2MQTT ya publican payloads de discovery, y por eso sus dispositivos aparecen al instante. Para tu propio firmware, el ejemplo en Python anterior se traslada directamente a C++ con PubSubClient o AsyncMqttClient; solo tienes que aumentar el tamaño del buffer del cliente, porque los payloads de configuración superan fácilmente el valor por defecto. Cuando los dispositivos están fuera de tu red doméstica, conéctalos todos a un único broker MQTT en la nube como se describe en cómo usar un broker MQTT externo con Home Assistant. La guía de ESPHome MQTT y nuestra guía de automatización IoT muestran más dispositivos que dependen de discovery.
Crea dispositivos detectables con ConnectMQ
Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, y emite credenciales para Home Assistant y para cada dispositivo. Con un broker MQTT gestionado de ConnectMQ, las configuraciones de discovery retenidas viven en un broker que nunca tienes que parchear, y el explorador de topics en vivo muestra exactamente lo que anuncia cada dispositivo.
Preguntas frecuentes
¿Los mensajes de discovery tienen que ser retenidos?
Deberían serlo. Las configuraciones retenidas permiten que Home Assistant reconstruya las entidades tras un reinicio sin que el dispositivo vuelva a publicar; republicar al recibir el mensaje birth es una copia de seguridad útil.
¿Qué diferencia hay entre node_id y el dispositivo?
node_id es solo un nivel del topic para organizar. La tarjeta del dispositivo se crea a partir del bloque device, y las entidades que comparten los mismos identifiers se agrupan.
¿Cómo elimino una entidad descubierta?
Publica un mensaje retenido de longitud cero en su topic de configuración. Home Assistant borra la entidad y el broker olvida la configuración retenida.
¿Puedo cambiar el prefijo de discovery?
Sí, en las opciones de la integración MQTT. Después, todos los dispositivos deben publicar sus configuraciones bajo el nuevo prefijo.



