Integraciones externas en Gladys Assistant

¡Qué chulo estas investigaciones, en serio es impresionante, ¡parece que son integraciones nativas!

Lo bueno es que una vez que te sientes listo con estas integraciones, solo tienes que subir tu repositorio al tema de GitHub que el store escanea cada hora, y ¡listo! Se publicará en las instancias de Gladys :grin:

Hay validación automática en el manifiesto y la imagen de Docker, ¡y eso es todo!

¡Puedes hacerlo con el botón «forzar la actualización»! :wink:

¡Lo voy a mirar!

¡Totalmente!

Efectivamente, la integración visual es realmente excelente, y para 1 usuario, de una simplicidad extrema.

Por otro lado, habrá que trabajar en la documentación, porque aquí, sin embargo, no hay posibilidad de mostrar información.

Y para sistemas más complejos como Tuya con su implementación local, aún faltan piezas (¡oh, oh! ¡ninguna crítica eh :sweat_smile: este nuevo sistema se ha implementado a una velocidad completamente loca y ya está dando sus frutos :joy: :clap:)

Entonces nos falta (a Fable y a mí) un conocimiento sobre este punto, porque no, no funciona en una imagen de desarrollo, y si el manifiesto cambia.


De lo que vemos, ¿se basa en el número de versión en el nombre de la imagen?

Registros:

2026-07-19T10:53:49+0200 <warn> externalIntegration.update.js:43 (ExternalIntegration.update) No se pudo extraer la imagen ghcr.io/terdious/gladys-tuya:1.0.0 Error: (código HTTP 404) inesperado - manifiesto desconocido
    at /home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/docker-modem/lib/modem.js:336:17
    at IncomingMessage.<anonymous> (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/docker-modem/lib/modem.js:363:9)
    at IncomingMessage.emit (node:events:530:35)
    at endReadableNT (node:internal/streams/readable:1698:12)
    at processTicksAndRejections (node:internal/process/task_queues:90:21) {
  reason: undefined,
  statusCode: 404,
  json: null
}
2026-07-19T10:53:49+0200 <debug> errorMiddleware.js:26 (errorMiddleware) BadParameters [Error]: UNABLE_TO_PULL_IMAGE: la imagen puede no existir o puede no estar disponible para tu arquitectura
    at ExternalIntegration.update (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/lib/external-integration/externalIntegration.update.js:44:11)
    at processTicksAndRejections (node:internal/process/task_queues:105:5)
    at update (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/api/controllers/externalIntegration.controller.js:97:25)
From previous event:
    at /home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/api/middlewares/asyncMiddleware.js:4:18
    at Layer.handle [as handle_request] (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/express/lib/router/layer.js:95:5)
    at next (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/express/lib/router/route.js:144:13)
    at adminMiddleware (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/api/middlewares/adminMiddleware.js:6:5)
    at Layer.handle [as handle_request] (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/express/lib/router/layer.js:95:5)
    at next (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/express/lib/router/route.js:144:13)
    at /home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/api/middlewares/authMiddleware.js:28:7

Pero quizás se deba al hecho de que lo hagamos funcionar para las pruebas fuera de docker.

Tendría otra pregunta, para los timbres Tuya, estamos trabajando con @GBoulvin:
Al profundizar en el SDK nos dimos cuenta de que Pulsar en sí no es bloqueante: el contenedor puede abrir la conexión en tiempo real de Tuya y enviar eventos (incluido el timbre) a través de publishState. La verdadera carencia en cuanto al timbre es la publicación de una imagen de cámara desde una integración externa — el SDK solo maneja hoy números/texto/estado pasado, no el equivalente a camera.setImage. He preparado una PR en este sentido en el SDK (espero que sea lo que realmente quieres que hagamos con las cosas que pueden faltar en la actualidad :sweat_smile:)

Y para el Tuya local (super complementario de la nube), dos piezas nos ayudarían — veo que coinciden con tus #2680 y #2677:

  1. el acceso a la red local desde el contenedor (para conectar con los dispositivos en 192.168.x);
  2. la posibilidad de exponer/editar params de dispositivo (IP, protocolo, conmutación nube/local) y de disparar una acción en un dispositivo con retorno de resultado (ej. una «lectura local de los DP» — una operación que un usuario no iniciado nunca adivinaría, de ahí el interés de un botón dedicado).

O sino:

  • por parte de Zendure, todo está ahora funcional para esta V1 :partying_face: Listo para proponerlo.
    Incluso hemos creado las issues para avanzar y darnos una hoja de ruta de implementación :heart_eyes:
  • por parte de Tuya, ¡casi lo hemos logrado! El local funciona para los dispositivos encontrados en UDP (3 dispositivos / 10). Los comandos pasan en local Y en la nube. Solo queda el estado de retorno a través de la nube que no funciona :sweat_smile:

EDITO: He lanzado la versión gladys-zendure + añadido el tema, ¿no hay nada más que hacer?

Y una observación de Claude después de que tuviéramos problemas de polling en las 2 integraciones Zendure y Tuya, no sé si tiene razón, pero parece haber resuelto el problema en el lado de Zendure:

Hola Pierre-Gilles,

Al finalizar mi primera integración externa (gladys-zendure), me encontré con
el poll_frequency de los dispositivos, y al comparar con tus otras integraciones
creo haber encontrado una inconsistencia en la plantilla que vale la pena
reportar.

El problema: el poll_frequency del OBJETO dispositivo (el del payload de
publishDiscoveredDevices) es validado estrictamente por el núcleo de las
integraciones externas — debe ser un valor de DEVICE_POLL_FREQUENCIES, EN
MILISEGUNDOS (1000/2000/10000/15000/30000/60000), y el dispositivo debe tener
should_poll: true para ser polleado. De lo contrario: 422 « invalid poll frequency »
(o ningún polling si falta should_poll).

Esto es exactamente lo que hace gladys-melcloud, y está correcto:
poll_frequency: POLL_FREQUENCY, // = 10 * 1000 ms
should_poll: true,

Pero la plantilla integration-template-js, por su parte, es incoherente con este contrato:

  • sus dispositivos de demostración hacen poll_frequency: config.poll_frequency
  • con un valor predeterminado poll_frequency: 300 en config.js (por lo tanto, 300, interpretado
    como segundos… pero pasado tal cual como frecuencia de dispositivo)
  • y no establecen should_poll.
    Por lo tanto, si instalamos la plantilla tal cual y creamos/pollamos un dispositivo de demostración,
    el núcleo lo rechaza (300 no es un valor de DEVICE_POLL_FREQUENCIES en ms)
    o nunca lo polla (falta should_poll).

La confusión proviene principalmente del hecho de que hay DOS « poll_frequency » homónimos:

  1. el campo config_schema del manifiesto = un simple valor de formulario
    (segundos, elegido por el integrador, sin vínculo automático con el polling);
  2. el campo poll_frequency del objeto dispositivo = restringido a
    DEVICE_POLL_FREQUENCIES (ms) por el núcleo.
    La plantilla los relaciona pasando (1) directamente a (2), lo cual no puede funcionar.

Tres opciones, a elegir:

  • corregir la plantilla para que sea funcional « out of the box »: ya sea
    codificar un valor ms válido + should_poll: true (como melcloud), o
    hacer el ajuste segundos→ms como tuve que hacerlo en zendure;
  • o hacer que el núcleo sea más tolerante: aceptar segundos y hacer el ajuste
    en el lado del servidor;
  • o al menos documentar que el poll_frequency del payload del dispositivo está
    restringido a DEVICE_POLL_FREQUENCIES (ms) y requiere should_poll: true.

Para información, en el lado de zendure tengo un pequeño helper que ajusta el valor config
(segundos, 10–3600) a la frecuencia ms autorizada más cercana — puedo compartirlo si te interesa para la plantilla.

Nada bloqueante para mí (zendure funciona), es solo un comentario de primera experiencia que podrá evitar la misma trampa a los próximos.

También:

Retorno sobre el framework external-integration (probando gladys-tuya en condiciones reales).

Observación: cuando una integración externa republica un dispositivo ya creado a través de
publishDiscoveredDevices (mismo external_id) con parámetros actualizados, los
parámetros del dispositivo existente NO se actualizan en Gladys, y ninguna opción « Actualizar » aparece en la pantalla de descubrimiento para las
integraciones externas (a diferencia de los servicios nativos).

Caso de uso concreto: un dispositivo Tuya se crea en modo cloud. Más tarde,
el usuario activa el modo local (la integración redescubre el dispositivo con
ip / local_key / protocol_version / local_override después de un escaneo LAN). Pero el
dispositivo ya creado conserva sus antiguos parámetros: imposible cambiar a
local sin eliminarlo y volver a crearlo.

Preguntas:

  • ¿Es intencional / previsto? ¿Debería un re-publish de un dispositivo existente descubierto hacer un upsert de los parámetros (por external_id)?
  • ¿O la pantalla de descubrimiento de las integraciones externas podría exponer una
    acción « Actualizar » (como lo hacen los servicios nativos en su página
    de dispositivo) para re-aplicar los parámetros de un nuevo descubrimiento?

Por ahora, el contorno del usuario es « eliminar + recrear »
el dispositivo, lo que no es muy ergonómico tan pronto como un parámetro local cambia
(IP, clave local rotada, paso cloud→local).

¡Gracias!

¡Acabo de implementar 4 tipos de dispositivos adicionales + la gestión en mqtt local en 35 minutos!!! ¡Es increíble!!

Ah, qué raro, voy a mirar, efectivamente debería hacer pull de « dev » en este caso

Ah, bien, ¡gracias! En general, lo hacía en este orden:

Modificación Spec → Modificación Gladys → Modificación SDK → Modificación de plantilla

Así todo queda coherente, pero voy a mirar tu PR :slight_smile:

Ok, voy a ver con Claude

¿No es así? ¿No puedes exponer params en publishDiscoveredDevices?

¿Puedes especificar esta parte? En particular, « DP » no sé qué significa :sweat_smile:

¡Excelente!!

Puedes ver el estado de tu release en esta URL:

Después, en tu caso te basas en una versión del SDK que aún no está fusionada, así que creo que el store la rechazará porque el manifiesto no es válido para él (normal, hay que esperar a que yo fusiona la PR)

Bien visto, es un error en la plantilla, ¡voy a corregirlo!!

¡Increíble!! Tengo ganas de poner todo eso en producción :grin:

Hola @pierre-gilles, y gracias por tus comentarios

  1. Botón « Actualizar » / versión de imagen
    Sí, es eso: el botón intenta hacer pull de la versión fija del manifiesto (ej. :1.0.0) en lugar de « :dev ». Para la iteración en desarrollo, hacer pull de « dev » sería perfecto. Tener en cuenta que una imagen dev de PR podría llamarse con un tag más largo como « :dev-test-mqtt-local » y escribirlo en la especificación para imponer los tags de imagen de desarrollo.

  2. « Exponer/editar parámetros de dispositivo » — lo aclaro
    Exponer los parámetros en el descubrimiento: OK, funciona, ya lo hago en publishDiscoveredDevices (ip, local_key, protocol_version, local_override…).
    La falta no está ahí: es la ACTUALIZACIÓN de los parámetros de un dispositivo YA CREADO. Cuando vuelvo a publicar un dispositivo descubierto (incluso con el mismo external_id) con parámetros que han cambiado — típicamente la IP LAN que cambia en DHCP, o un cambio de cloud→local después de un nuevo escaneo — los parámetros del dispositivo existente no se vuelven a aplicar, y el usuario no tiene ningún medio para editarlos/actualizarlos.
    Hoy el único trabajo es eliminar + recrear el dispositivo.
    → Necesidad: ya sea un upsert de los parámetros al volver a publicar (por external_id), o una acción « Actualizar » en la pantalla de descubrimiento de las integraciones externas

  3. « DP » y « acción con retorno de resultado »
    DP = Data Point (terminología Tuya). Cada dispositivo Tuya expone puntos de datos numerados (DP 1 = interruptor, DP 20 = luminosidad, etc.); el estado completo = el « mapa DPS ». La « lectura local de los DP » consiste en leer este mapa EN DIRECTO en el LAN a través del protocolo local de Tuya. Dos usos:

  • validar que la conexión local + la versión del protocolo funcionan
    ANTES de activar el modo local (si no, activamos un local que timeout);
  • descubrir/confirmar el mapeo DP→función.

Es una acción desencadenada por el usuario, CON un resultado devuelto a la UI (los DP leídos, o el error). La necesidad genérica para las integraciones externas: poder exponer una acción de usuario « ejecuta esta operación y muéstrame el resultado » (prueba de conexión local, lectura DP, reapareamiento, identificar…), ya que no tenemos una UI personalizada. El SDK solo gestiona hoy el push de estados y poll/setValue — no este tipo de acción solicitud/respuesta a demanda.
No podemos desencadenar esta búsqueda de versión de protocolo y DP a menos que conozcamos la IP, y si el UDP no la ha encontrado, deberíamos poder proporcionar la IP manualmente para iniciar esta búsqueda automática (escanea intentando lanzar polls para cada versión) - Y es una operación bastante larga - hasta 15 segundos creo - para evitar saturar el dispositivo.

Otros comentarios:

  • Por error, pude instalar un 2º contenedor de desarrollo cuando no había desinstalado el anterior. Sin errores, se instaló en « -2 ». Al instalar, deberíamos ser notificados y que sea imposible instalar 2 imágenes idénticas (sin embargo, deberíamos poder instalar una producción y una de desarrollo como actualmente)
    image

  • Instalar un contenedor con tag « :dev » (o tag « :dev-xxxxxx ») debería mostrar una alerta de prevención: si una instancia de producción está ejecutándose al lado, esto puede afectar/romper la otra instancia, aconsejar detener el contenedor de producción antes de instalar el tag « :dev » - sin embargo, es una muy buena cosa poder instalar una de desarrollo y una de producción lado a lado

  • Mostrar en la página de configuración la hora de inicio del contenedor

  • Cuando un contenedor externo se detiene, debería detenerse el polling (y cualquier otro intento de intercambio si es el caso), para evitar la contaminación de logs:

2026-07-20T07:43:19+0200 <error> device.poll.js:23 (DeviceManager.poll) There was an error while polling device ambiance-salon
2026-07-20T07:43:19+0200 <error> device.poll.js:24 (DeviceManager.poll) ExternalIntegrationUnavailableError: EXTERNAL_INTEGRATION_NOT_CONNECTED
    at ExternalIntegration.sendCommand (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/lib/external-integration/externalIntegration.sendCommand.js:23:11)
    at Object.poll (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/lib/external-integration/externalIntegration.registerProxyService.js:42:20)
    at DeviceManager.poll (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/lib/device/device.poll.js:21:26)
    at Promise.map.concurrency (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/lib/device/device.pollAll.js:13:87)
    at tryCatcher (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/util.js:16:23)
    at MappingPromiseArray._promiseFulfilled (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/map.js:68:38)
    at MappingPromiseArray.PromiseArray._iterate (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/promise_array.js:115:31)
    at MappingPromiseArray.init (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/promise_array.js:79:10)
    at MappingPromiseArray._asyncInit (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/map.js:37:10)
    at _drainQueueStep (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/async.js:97:12)
    at _drainQueue (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/async.js:86:9)
    at Async._drainQueues (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/async.js:102:5)
    at Immediate.Async.drainQueues (/home/gladys/GladysDev/gladys-external-integration-test/Gladys/server/node_modules/bluebird/js/release/async.js:15:14)
    at processImmediate (node:internal/timers:491:21)

  • Cuando una integración puede ser cloud y local con dispositivos que pueden ser locales o cloud, deberíamos imponer una especificación:
    • Integración global en local o en cloud => un interruptor « Modo local » que prioriza para todos los dispositivos el local si está marcado, fallback por dispositivo en cloud si no hay local o está roto; de lo contrario, 100% cloud
      image
    • una etiqueta global en la vista Dispositivo que especifique si la aplicación está definida en local o en cloud
    • una etiqueta por dispositivo que especifique si el dispositivo está en local o en cloud.
      Realmente lo necesitamos para Tuya, por ejemplo. Y me encuentro con el mismo caso en Zendure (las pruebas durante la implementación del mqtt local en zendure me permitieron darme cuenta de que tenía 2 dispositivos que no tenían el mqtt local activado, me costó 30 minutos, una etiqueta visible me habría hecho entender y pasar al verdadero diagnóstico rápidamente).
      Luego, la documentación de la integración externa podrá incluir una sección « FAQ » o de ayuda para búsquedas/resolución de problemas
  • De todos modos, ¿qué hay de la documentación? No he buscado en tus especificaciones, pero ¿has previsto imponerla para validar una integración?
    Sería bueno definir, por ejemplo (como HA), un formato de pie de página y los capítulos principales a imponer - con una plantilla como para el contenedor. ¿Es posible?

¡Hola @Terdious, son muy buenos comentarios :slight_smile:

He adaptado la especificación y la implementación seguirá:

Los siete comentarios están integrados, cada uno con la decisión de diseño correspondiente:

1. Parámetros después de la creación — los dos mecanismos solicitados, con una frontera clara. Los params son los datos técnicos de la integración: al volver a publicar un dispositivo ya creado (mismo external_id), el supervisor los actualiza silenciosamente (la IP DHCP que cambia, el paso de cloud a local), sin eco device-updated hacia la integración (de lo contrario, un bucle: republicar → evento → republicar). El name, la habitación y las características siguen siendo propiedad del usuario: si la estructura publicada difiere, la pantalla de Descubrimiento muestra un botón « Actualizar » — acción del usuario a través del POST /api/v1/device estándar. Fin de la eliminación/recración.

2. Botones de acciones — nuevo campo actions del manifiesto (máximo 10): key, etiqueta/descripción multilingüe, y sobre todo dos cosas dimensionadas para el caso Tuya: un fields opcional en el formato exacto del config_schema (el mini-formulario « ingresar la IP » se renderiza con el mismo motor, validado de la misma manera) y un timeout_seconds por acción (5–120, valor predeterminado 30) que reemplaza la regla de 5 s para action.run — el escaneo de versiones de protocolo a ~15 s pasa. Cadena completa: botón → POST .../action/:key → WS action.run → resultado en command-result.data.message (cadena o multilingüe) mostrado debajo del botón. SDK: onAction('detect_protocol', async (fields) => '...').

3. Doble instancia dev/prod: al instalar (todos los modos), si una integración instalada comparte la misma imagen sin la etiqueta (:dev junto a :1.2.0) o el mismo name del manifiesto → advertencia explícita (posible conflicto en la misma cuenta de cloud / los mismos dispositivos, consejo de detener la otra instancia durante las pruebas), pero la instalación sigue siendo posible — dev junto a prod es un uso deseado, los selectors ext-dev-* garantizan la ausencia de colisión técnica.

4. Hora de inicio: started_at del contenedor principal (y de cada subcontenedor) en el detalle de administración, mostrado en el bloque de supervisión (« en funcionamiento desde… »).

5. Poll de una integración detenida: el poll programado se convierte en un no-op silencioso cuando la integración no está RUNNING/DEGRADED — ni throw ni línea de registro cada N segundos para un estado ya conocido. setValue sigue throw: el usuario que acciona debe ver el error.

6. Tipos de campos: boolean ya existía (renderizado como interruptor/casilla de verificación — aclarado); se agrega multi_select (casillas de verificación, valor = array) y display: "radio" en los select.

7. Documentación obligatoria en la tienda: publicar ahora requiere docs/en.md y docs/fr.md, estructurados según la plantilla proporcionada en el repositorio de plantillas (Presentación / Requisitos previos / Configuración / Solución de problemas). El indexador verifica la presencia + tamaño mínimo (rechazo error de lo contrario), rehospeda los archivos en Pages como las portadas (docs en index.json), y la pantalla de instalación gana un enlace « Documentación » (idioma del usuario, retroceso en).

Creo que es un poco demasiado específico para lo que estás haciendo allí, prefiero algo genérico :slight_smile:

Entonces, en efecto, Zendure no está ni en https://integration-store-storage.gladysassistant.com/index.json, ni en https://integration-store-storage.gladysassistant.com/rejected.json ^^
Por otro lado, gladys-tuya (cuyas PR aún no he fusionado) sí aparece en https://integration-store-storage.gladysassistant.com/rejected.json:

[
  {
    "store_slug": "Terdious/gladys-tuya",
    "level": "error",
    "reason": "docker_image: image is not publicly pullable (registry auth denied, HTTP 403)",
    "checked_at": "2026-07-20T07:24:35.428Z"
  }
]

Pero tendrás que hacernos una página visual de verdad, ¿eh? :face_with_peeking_eye: :sweat_smile:

Fable 5 nos hará eso, no te preocupes :rofl:

@Terdious ¿hacías pruebas desde la PR #2680? ¿Me confirmas que funcionaba bien?

¡Pues sí, diría que perfectamente! :slight_smile:

Para las nuevas características que solicitaste, le pedí a GPT 5.6 Sol 1M High que las revisara, y él cree que son demasiado genéricas para Zendure, y estoy bastante de acuerdo con él :slight_smile:

La revisión: Add battery-storage device feature category by Terdious · Pull Request #2682 · GladysAssistant/Gladys · GitHub

Gracias Pierre-Gilles, en general todo está bien para mí:

Gracias por todas las correcciones / implementaciones

Solo un punto en el que insisto — el «demasiado específico / más bien genérico» sobre
el modo local/nube + macarrones:

Creo que es precisamente una necesidad GENÉRICA, no específica de Tuya. El motivo
recurrente = «una integración que cubre la nube Y local, con dispositivos cuyo
transporte efectivo puede diferir de un dispositivo a otro y evolucionar con el
tiempo». Ejemplo improvisado:

Integración Dualidad nube/local Estado
Tuya nube + LAN por dispositivo en Gladys (→ externo)
Netatmo meteorología = full nube; cámaras/timbre = nube + local (local mucho más rápido en la imagen, más fiable porque el enlace nube falla) en Gladys, en proceso de cambio externo
Zendure nube + MQTT local externo (el tuyo)
Shelly HTTP/WS local + Shelly Cloud convertible / por venir
eWeLink / Sonoff nube + modo LAN convertible / por venir
Somfy TaHoma nube + API local convertible / por venir

El bloque a poner en el framework no es la lógica Tuya,
es:

  1. un ESTADO DE TRANSPORTE POR DISPOSITIVO, estándar y agnóstico
    (local / nube / inalcanzable), que la integración sube y que Gladys
    muestra en macarrón — + un macarrón global en la vista Dispositivos;
  2. opcionalmente una PREFERENCIA DE CONEXIÓN estándar («priorizar lo local
    cuando esté disponible, nube de lo contrario») — es exactamente el texto de tu interruptor
    «Modo local (LAN)».
    La integración cumple, Gladys muestra → 100 % genérico, cero semántica Tuya
    en el núcleo.

El valor es sobre todo diagnóstico y universal: sin un macarrón visible,
el usuario no puede saber por qué un dispositivo es lento o bloqueado. Mi
propio caso Zendure (2 dispositivos sin MQTT local, 30 minutos perdidos): un macarrón
me habría puesto sobre la pista de inmediato.

Y no es solo una integración, la misma lógica es válida para todos los dispositivos de la tabla anterior. El otro día me hablabas de Shelly, es lo mismo, hay que activar el mqtt en la interfaz web local http para tener el MQTT local y/o la nube. El
motivo vuelve cada vez que un fabricante ofrece los dos canales — de ahí el interés de una primitiva «estado de transporte + preferencia» reutilizable en lugar de un tratamiento
por integración.

Además es bastante simple de implementar en el núcleo de Gladys: Un interruptor definido en la configuración (que sería el mismo para todos) y un parámetro en el dispositivo (definido de la misma manera en la especificación). ¡Luego es una cocina interna en el contenedor!

¡Vale, gracias por la explicación, está bien para mí, he adaptado la especificación!

Estoy hablando con Claude para el acceso a la red local

Para el acceso local normalmente ya es posible, ¿estás seguro de que te bloqueaba?

Mi opinión: ya es posible en el diseño — y es importante no « arreglar » lo que no está roto. El bridge NAT no bloquea el tráfico unicast saliente hacia la LAN: un contenedor en gladys-integrations puede abrir una conexión TCP/UDP hacia 192.168.1.50 (el protocolo local Tuya puerto 6668, la API HTTP local Shelly…) exactamente como se conecta a una nube — Docker enmascara la salida, nada se filtra. enable_icc=false solo aísla los contenedores entre sí en el bridge, no el tráfico hacia el exterior. La única verdadera limitación, ya documentada en B.2/B.16, es la recepción de broadcast/multicast (el escaneo UDP de descubrimiento) — y sospecho que es la fuente del malentendido: « sin broadcast » se leyó como « sin LAN ». Si realmente ha constatado un fallo unicast en su entorno de desarrollo, la causa está fuera del framework — el caso clásico es un firewall de host estricto (ufw) que DROP el FORWARD bridge→interfaz LAN: síntoma exacto « cloud OK, LAN KO ".

Vuelvo a revisar si no es él (y yo quien lo ha guiado mal…) quien lo ha hecho mal. Si es así, lo siento por las molestias.

SDK en versión 0.4.0 publicado, Claude adapta el template-js, y estoy haciendo los merge de Gladys hacia la PR base

Edición: merge realizado, todo está en esta PR ahora: External integrations (RFC phase 1): Docker supervisor, host API, integration WebSocket, decentralized store and generic frontend by Pierre-Gilles · Pull Request #2665 · GladysAssistant/Gladys · GitHub