Skip to content

ADR-0024 — Sincronización de estado Music Assistant ↔ Home Assistant para el DJ de Vera

Fecha: 2026-07-25 Estado: propuesto — no implementado, sin cambios aplicados Ámbito: LXC 100 (Vera / dj MCP server), VM 208 (Music Assistant), VM 171 (Home Assistant)


Contexto

El DJ conversacional de Vera (documentación) habla directamente con Music Assistant por su propia conexión WebSocket autenticada (un JWT dedicado, vera-dj-skill), sin pasar por Home Assistant. Esto le da al DJ telemetría rica e inmediata (elapsed_time exacto, estado en tiempo real — verificado con números idénticos hasta el último decimal entre altavoces sincronizados) sin depender de que HA esté arriba ni de su ciclo de refresco.

Problema descubierto: cuando el DJ reproduce/para música por su cuenta, las entidades media_player.* de Home Assistant no reflejan el cambio. Se verificó exhaustivamente antes de escribir este ADR:

  • Altavoz sin agrupar (Cocina) reproduciendo de verdad (confirmado por dj_now_playing y por consulta directa a MA) → HA sigue mostrando idle, sin título ni artista.
  • Mismo resultado esperando 12+ segundos (descarta que sea solo latencia de sondeo).
  • Mismo resultado tras un reload completo de la integración (homeassistant.reload_config_entry) — descarta que sea una suscripción "dormida" que un refresco despierte.
  • El mismo patrón se repite en altavoces agrupados (grupo persistente sync_group) y sin agrupar — no es un problema específico de los grupos, es general a cualquier acción disparada desde fuera de la sesión propia de HA.

Hipótesis de causa raíz (no confirmada — no se inspeccionó el código fuente de la integración music_assistant de HA ni el de Music Assistant): la integración de HA mantiene su propia sesión/suscripción WebSocket con MA y depende de eventos que MA le empuja a esa sesión concreta. Una segunda sesión externa (la del DJ) puede disparar acciones reales en MA sin que necesariamente se emita el mismo evento de "player actualizado" hacia la sesión de HA — o si se emite, HA no lo está aplicando al estado de la entidad por alguna razón no determinada. Esto queda como pregunta abierta, no como diagnóstico cerrado.

Qué NO es este problema

  • No afecta a la funcionalidad del DJ en sídj_now_playing, el volumen, los grupos sincronizados, todo funciona y se verifica correctamente por su propio canal.
  • No afecta a acciones iniciadas desde la propia HA (p. ej. si alguien pulsa play en el dashboard de HA o en la app móvil) — solo a las que dispara el DJ desde fuera.

Opciones consideradas

1. No hacer nada

Dejar el DJ como está: control y telemetría fiables por su cuenta, HA simplemente no se entera de esas reproducciones concretas.

  • Pros: cero riesgo, cero esfuerzo, no toca código que ya funciona y está verificado en producción (Vera).
  • Contras: cualquier automatización futura de HA que dependa de "¿está sonando música en tal sitio?" no vería las reproducciones del DJ. El dashboard de HA/tablet mostraría "idle" aunque haya música real sonando.

2. Reenrutar el DJ para que hable SIEMPRE a través de los servicios de HA

dj_play/dj_stop/dj_set_volume dejarían de llamar a la API de Music Assistant directamente y llamarían a media_player.play_media / media_player.turn_off / media_player.volume_set de Home Assistant, que a su vez reenvía a MA por la conexión que HA sí escucha.

  • Pros: sincronía garantizada por diseño — es HA quien dispara la acción, así que su propio estado se actualiza por definición. Sin ambigüedad de causa raíz que investigar.
  • Contras:
  • Riesgo real: reescribe un componente ya probado y en uso (server.py de Vera) en su ruta más crítica (reproducir/parar), con las precauciones ya conocidas de tocar un asistente en producción diaria.
  • Nueva dependencia dura: el DJ dejaría de funcionar si HA está caído o lento, cuando hoy es independiente de HA.
  • Pérdida de telemetría rica: la comparación exacta de elapsed_time entre altavoces de un grupo (la prueba que demostró la sincronía real) se hizo consultando MA directamente — por los servicios de HA esa precisión no está garantizada de la misma forma.
  • Cambia el diseño "fino y sin estado" del servidor (documentado como principio en music-dj.md), añadiendo una dependencia de infraestructura a cada llamada.

3. Un "trigger" que sincronice HA aparte, sin tocar cómo reproduce el DJ

La idea que propone el usuario: mantener el DJ hablando directo con MA (control + telemetría como hasta ahora), y añadir un mecanismo aparte que empuje ese estado hacia HA después de cada acción. Tres variantes concretas, de más a menos robusta:

3a. Entidad propia del DJ en HA (recomendada si se decide seguir adelante)

El DJ, tras cada dj_play/dj_stop, escribe su propio estado a una entidad que él mismo posee (p. ej. un sensor/input_text vía la API REST de HA, o MQTT discovery — mismo patrón ya usado por el agente de Bazzite en este homelab). Algo como sensor.dj_now_playing con atributos player/title/ artist/state, actualizado por el propio DJ.

  • Pros: sortea la incertidumbre de causa raíz por completo — no depende de entender ni arreglar la suscripción de MA↔HA, solo añade una fuente de verdad nueva y fiable. Reutiliza un patrón que ya existe en esta casa (Bazzite → MQTT → HA). Cero riesgo para el DJ existente (es aditivo).
  • Contras: es una entidad paralela, no "arregla" que media_player.cocina muestre el estado real — un dashboard que ya use esa entidad nativa seguiría sin verlo. Duplica la fuente de información (la entidad nativa de MA + la nueva del DJ) para las mismas reproducciones.

3b. Forzar refresco puntual de la entidad tras cada acción del DJ (homeassistant.update_entity sobre la entidad concreta, no recarga completa)

  • Pros: si funcionara, sería la solución "correcta" — arregla la entidad real, no una paralela.
  • Contras: especulativo. Ya se probó la recarga completa de la integración y no sirvió; es razonable sospechar (sin certeza) que si la integración usa un modelo de actualización por push de eventos en vez de sondeo, update_entity no tendría nada que "sondear" y tampoco serviría. Requeriría probarlo para saberlo — no se ha hecho, por decisión explícita de no tocar nada en esta sesión.

3c. Webhook del DJ → automatización de HA que refresca desde dentro

El DJ llama a un webhook de HA (patrón ya existente: webhooks.routes.alert en openclaw.json) tras cada acción; una automatización de HA, ejecutándose dentro de su propio contexto, intenta refrescar la entidad.

  • Pros: mantiene la lógica de refresco dentro de HA (más fácil de depurar con sus propias herramientas).
  • Contras: si el problema de fondo es que MA no emite el evento hacia la sesión de HA en absoluto (no solo que HA no procesa un evento ya recibido), esto tampoco lo arreglaría — es, en esencia, un intento más fino de la misma idea que la opción 3b, con el mismo riesgo de no funcionar por la misma razón no confirmada.

4. Investigar la causa raíz real antes de construir nada

Mirar el código fuente de la integración music_assistant de HA (o preguntar en su GitHub/Discord) para entender exactamente cómo decide actualizar el estado de una entidad, y si hay una forma nativa/soportada de notificarle un cambio externo — en vez de adivinar con paliativos.

  • Pros: la única opción que podría dar una solución "correcta" en vez de un parche; evita construir 3a/3b/3c a ciegas para descubrir después que ninguna aborda la causa real.
  • Contras: coste de investigación desconocido de antemano; puede no haber una vía soportada y tener que llegar a la misma conclusión que las opciones 3 de todas formas.

Utilidad real — ¿esto merece la pena?

Antes de decidir cómo, vale la pena preguntar si. Hoy, ninguna automatización existente depende de que HA vea las reproducciones del DJ:

  • Las dos automatizaciones de ejemplo que se dejaron pendientes de Music Assistant (modo noche, llegada a casa) se disparan por hora y presencia, no por "¿está sonando música?" — no las bloquea este gap.
  • El propio DJ ya tiene su fuente de verdad fiable (dj_now_playing) para cualquier cosa que Vera necesite saber sobre sí misma.

El valor real de arreglar esto sería:

  1. Consistencia visual — que el dashboard de HA (o la tablet, que ya lee entidades de HA en otras vistas) no muestre "idle" mientras suena música de verdad por orden del DJ. Esto es sobre todo relevante si en el futuro se quiere un widget "sonando ahora" en HA/tablet que también refleje lo que pone Vera, no solo lo que se reproduce desde la propia HA/tablet.
  2. Automatizaciones futuras condicionadas a reproducción — algo tipo "si el DJ pone música en el dormitorio de noche, atenúa las luces". Hoy esto no funcionaría con el DJ (aunque sí funcionaría si esa misma música se pusiera desde la app de HA en vez de por Vera).

Ninguno de los dos es una necesidad hoy — son mejoras de cara a usos futuros concretos, no una funcionalidad rota que bloquee algo actual.

Recomendación

No implementar nada todavía. Si en el futuro aparece un caso de uso concreto (un dashboard, una automatización) que realmente necesite ver el estado del DJ en HA, la opción 3a (entidad propia del DJ) es la más segura y aditiva — no depende de entender la causa raíz, no toca el DJ existente, y reutiliza un patrón ya probado en este homelab (Bazzite→MQTT→HA). La opción 2 (reenrutar todo por HA) solo se justificaría si ese caso de uso exige específicamente que sea la entidad nativa la que se actualice, no una paralela — y aun así, antes de tocar el DJ en producción, valdría la pena la opción 4 (investigar la causa raíz) primero, para no arriesgar el funcionamiento actual del DJ a cambio de un apaño que quizás ni siquiera resuelva el problema real.

Próximos pasos (si se decide seguir adelante en el futuro)

  1. Definir el caso de uso concreto que lo necesita (evita construir una solución genérica para un problema hipotético).
  2. Si se sigue adelante, empezar por la opción 4 (investigar causa raíz) antes que por un paliativo — más barato entender el problema que adivinar tres veces.
  3. Si no hay tiempo/interés en investigar, ir directo a 3a (entidad propia) por ser la opción de menor riesgo y mayor certeza de que funcionará.