Integraciones externas: actualizar a Gladys 5.3, SDK JS 0.16 y la tienda

¡Hola a todos!

Han cambiado muchas cosas en las últimas semanas en cuanto a integraciones externas: nueva versión del SDK de JavaScript (@gladysassistant/integration-sdk v0.16.0), nuevos campos en el manifiesto, nuevas reglas de validación en la tienda… Esta publicación recoge todo lo que debe hacer para actualizar su integración, y lo que puede hacer además si lo desea.

Buena noticia: no hay cambios obligatorios que rompan la compatibilidad. Una integración publicada hoy sigue indexada. Pero algunos puntos merecen una verificación, y las novedades valen la pena :slightly_smiling_face:


:white_check_mark: La lista de verificación rápida

  • Actualizar el SDK a ^0.16.0 (Node.js ≥ 20)
  • Verificar sus poll_frequency: en milisegundos, y solo entre los valores permitidos
  • Usar publishChangedStates si publica muchos estados (límite de 300 estados/minuto)
  • Añadir categories (y eventualmente keywords / countries) al manifiesto
  • Verificar que gladys_version cubre bien todos los campos que declara
  • Tener docs/en.md y docs/fr.md (300 caracteres mínimo cada uno)
  • Verificar la portada: JPEG o PNG, 800×534 px exactamente, 150 Ko máximo
  • Ejecutar el validador local: npx github:GladysAssistant/integration-store
  • Publicar: incrementar version + docker_image, empujando la imagen antes del manifiesto

El detalle de cada punto está abajo.


1. Actualizar el SDK

npm install @gladysassistant/integration-sdk@^0.16.0

El SDK requiere Node.js ≥ 20. Asegúrese de verificar la imagen base de su Dockerfile.

Lo que ha cambiado desde la v0.11:

Versión Principales novedades
0.12 Sincronización con Gladys 4.86: sensores de red eléctrica/gas, cámaras PTZ, wakeOnLan(), campo de configuración account_link
0.13 Seis nuevos fenómenos meteorológicos en el formato pivot, sirenas, carga de batería, formato de los resultados del escaneo
0.14 Disparadores y acciones de escenas, widgets de tablero, getHouses(), sensores de carbono de la red y humo
0.15 DEVICE_POLL_FREQUENCIES, Gladys falso para sus pruebas, parseMdnsTxt(), publishChangedStates(), tipo calendar, contratos de energía, formularios en los botones de widgets, selector de casa, publishEnergyConsumption()
0.16 Lectores de música, tipos ai y tts, should_poll se vuelve opcional

El registro completo de cambios está en GitHub.


2. Los puntos a verificar en su código

:stopwatch: poll_frequency: en milisegundos, y solo ciertos valores

Este es el error más frecuente. poll_frequency se expresa en milisegundos, y solo se aceptan estos valores: 1 s, 2 s, 10 s, 15 s, 30 s o 1 min. Cualquier otro valor (300 pensado en segundos, 300000 para 5 minutos…) hace que publishDiscoveredDevices falle con un error 400.

Use las constantes exportadas por el SDK:

import { DEVICE_POLL_FREQUENCIES } from '@gladysassistant/integration-sdk';

await gladys.publishDiscoveredDevices([
  {
    name: 'Estación meteorológica',
    external_id: ids.device,
    should_poll: true,
    poll_frequency: DEVICE_POLL_FREQUENCIES.EVERY_MINUTES, // 60000 ms
    features: [/* … */],
  },
]);

Algunas precisiones:

  • Un minuto es el ritmo más lento. Para ir más lento (API en la nube con cuota, ajuste del usuario en minutos), mantenga EVERY_MINUTES e ignore las consultas innecesarias en onPoll, o use su propio temporizador.
  • Desde Gladys 5.3, poll_frequency es suficiente: el núcleo activa should_poll solo. Si su gladys_version acepta versiones más antiguas, mantenga should_poll: true. Si establece should_poll, debe ser un booleano.
  • Estos dos campos se leen al crear el dispositivo: modificarlos después no actualiza un dispositivo ya creado.

:chart_decreasing: Límite de 300 estados por minuto: publishChangedStates

La API anfitriona limita POST /state a 300 estados por minuto y por integración. Si consulta muchos dispositivos, publique solo lo que ha cambiado:

// reemplaza publishStates: solo los valores modificados se envían a Gladys
await gladys.publishChangedStates(states);

// y se devuelve un valor sin cambios cada 30 minutos
await gladys.publishChangedStates(states, { heartbeat: 30 * 60 * 1000 });

El SDK divide los lotes de más de 100 estados, devuelve lo que Gladys ha rechazado y olvida los valores de un dispositivo cuando el usuario lo crea, lo modifica o lo elimina.

:label: Use las constantes, no cadenas fijas

DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES y DEVICE_FEATURE_UNITS son una copia exacta de server/utils/constants.js en Gladys, y se mantienen actualizadas en cada versión. Si ha copiado cadenas manualmente, este es el momento de pasar a las constantes: evita errores tipográficos y tiene autocompletado.

:sun_behind_rain_cloud: Integraciones meteorológicas: night está obsoleto

La condición night sigue siendo aceptada pero está obsoleta. Una noche lluviosa se escribe weather: 'rain', is_day: false. También piense en las nuevas condiciones del formato pivot y en el campo type de las alertas (WEATHER_ALERT_TYPES).

:clapper_board: Disparadores y acciones de escenas: las claves son definitivas

Si declara scene_triggers / scene_actions, una clave publicada nunca debe ser renombrada. Y añadir un campo required sin default a una acción existente rompe las escenas que la usan.


3. Actualizar el manifiesto

La regla de oro: gladys_version debe cubrir lo que declara

Las versiones antiguas de Gladys rechazan los campos que no conocen. La tienda verifica que el mínimo de su gladys_version cubra cada campo declarado, de lo contrario la integración es rechazada. Así, el usuario ve simplemente «requiere Gladys ≥ X» en el catálogo, en lugar de una instalación que falla sin explicación.

Usted declara… gladys_version mínimo
categories >=4.86.0
type: "provider", widgets, scene_triggers, scene_actions >=5.1.0
type: "calendar", account_schema, energy_contracts, un campo con source: "houses" >=5.2.0
type: "ai", type: "tts", music, keywords, countries >=5.3.0

:warning: En el otro sentido, no suba gladys_version sin razón: su integración sería ocultada para los usuarios que aún no han actualizado Gladys.

:card_index_dividers: categories: su lugar en el catálogo

Entre 1 y 3 categorías entre: climate, lighting, energy, security, multimedia, appliances, environment, protocols, network, notifications, assistants, services, transport, vehicles, servers, leisure.

Las cuatro últimas (transport, vehicles, servers, leisure) acaban de ser añadidas. Sin categories, su integración sigue visible en « Todas » y en la búsqueda, pero no aparece en ninguna sección. Para las integraciones ya publicadas, la tienda aplica de momento un mapeo de respaldo (data/category-fallback.json). Declarar sus propias categorías es la mejor manera de estar bien posicionado. Una categoría desconocida es simplemente ignorada, con una advertencia.

:magnifying_glass_tilted_right: keywords y countries (Gladys ≥ 5.3)

{
  "gladys_version": ">=5.3.0",
  "categories": ["energy"],
  "keywords": ["linky", "enedis", "téléinfo"],
  "countries": ["FR"]
}
  • keywords : 1 a 10 términos de 2 a 30 caracteres, nunca mostrados, usados únicamente por la búsqueda (marcas, modelos, sinónimos, ciudades o redes atendidas…).
  • countries : 1 a 20 códigos de país ISO en mayúsculas. No lo ponga si su integración funciona en todas partes (ese es el comportamiento por defecto). Si lo declara, las instancias ubicadas en otro país ya no verán la integración en las secciones del catálogo (sigue siendo encontrada a través de la búsqueda). Piensa en esto para una integración regional: servicio meteorológico nacional, red de transporte, proveedor de energía, ISP…

No olvides: declarar keywords o countries obliga a gladys_version ≥ 5.3.0.


4. Documentación y portada

  • Documentación obligatoria : docs/en.md y docs/fr.md en la raíz del repositorio, 300 caracteres mínimo cada uno, siguiendo las secciones de la plantilla (Presentación / Requisitos previos / Configuración / Solución de problemas). Si falta una, la integración es rechazada. Los archivos son alojados por la tienda y mostrados en las pantallas de instalación y configuración de Gladys.
  • Nuevo : docs/de.md y docs/es.md son opcionales. Si están presentes (300 caracteres mínimo), se muestran a los usuarios alemanes y españoles.
  • Portada : JPEG o PNG, exactamente 800×534 px, 150 Ko máximo, URL https directa (sin redirección). Lo más simple: una URL raw.githubusercontent.com hacia un archivo de tu repositorio. Una portada inválida no bloquea la indexación, pero tendrás una imagen por defecto en el catálogo.

5. Validar localmente antes de publicar

¡Ya no es necesario esperar la indexación horaria para descubrir un rechazo! En la raíz de tu repositorio:

npx github:GladysAssistant/integration-store

El validador hace exactamente las mismas comprobaciones que el robot de la tienda (esquema, reglas gladys_version, categorías, documentación, existencia de las imágenes Docker, portada) y muestra todos los problemas de una vez. Código de salida 0 = la integración sería indexada, 1 = sería rechazada.

En la CI de tus pull requests

En una PR que incrementa la versión, la imagen Docker generalmente no está publicada todavía: añade --skip-image-check para saltar solo esta comprobación.

- uses: actions/setup-node@v4
  with:
    node-version: 24
- run: npx github:GladysAssistant/integration-store --skip-image-check

Vuelve a ejecutarlo sin la opción una vez que las imágenes estén publicadas, por ejemplo en el último paso de tu flujo de trabajo de lanzamiento.


6. Publicar la nueva versión

  1. Incrementa version (semver estricto) y docker_image (con un tag explícito) en el manifiesto.
  2. Publica la imagen Docker antes de que el manifiesto que la referencia llegue a la rama por defecto: la tienda verifica que la imagen existe y que es descargable sin autenticación. Si falta la imagen, la integración es retirada del catálogo hasta la próxima indexación.
  3. Empuja. En la próxima indexación (cada hora), Gladys mostrará « actualización disponible » a tus usuarios.

¿Tu integración no aparece? Mira el archivo público rejected.json de la tienda: cada rechazo está listado con su razón (error = no indexada, warning = indexada con una degradación, por ejemplo la portada por defecto).


7. Prueba tu integración con el Gladys falso

El SDK ahora proporciona un Gladys en memoria para tus pruebas unitarias: es un verdadero GladysIntegration, conectado a un Gladys falso en lugar de la red.

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createFakeGladys } from '@gladysassistant/integration-sdk/testing';
import { registerHandlers } from '../src/app.js';

test('publica la temperatura en el sondeo', async () => {
  const gladys = createFakeGladys({ config: { api_key: 'key' } });
  registerHandlers(gladys);
  await gladys.connect();

  const [device] = gladys.fake.discoveredDevices;
  assert.deepEqual(await gladys.fake.poll(device), { success: true });
  assert.equal(gladys.fake.lastState(`${device.external_id}:temperature`), 21.5);
});

Aplica las mismas comprobaciones que la API host (prefijo de los external_id, categorías/tipos/unidades conocidas, poll_frequency…) y responde con los mismos errores 400. Así que ves los errores en tus pruebas en lugar de en tus usuarios.


8. Para ir más allá (opcional)

Si quieres enriquecer tu integración, aquí está todo lo que ahora es posible:

Funcionalidad Manifiesto SDK Gladys
Widgets de panel de control widgets onWidgetGet, onWidgetAction, requestWidgetRefresh ≥ 5.1
Disparadores / acciones de escenas scene_triggers, scene_actions publishSceneEvent, onSceneAction ≥ 5.1
Integración « capacidades solas » type: "provider" — ≥ 5.1
Proveedor de calendario type: "calendar", account_schema publishCalendars, publishCalendarEvents… ≥ 5.2
Contratos y calendarios de energía energy_contracts publishEnergyCalendar, onEnergyPrice… ≥ 5.2
Consumo datado (lecturas recibidas después) — publishEnergyConsumption ≥ 5.2
Elección de una casa en un formulario source: "houses" — ≥ 5.2
Proveedor de IA (compatible OpenAI) type: "ai" onAiChat ≥ 5.3
Síntesis de voz type: "tts" onTtsSynthesize ≥ 5.3
Reproductores de música (navegación, búsqueda, cola) music publishNowPlaying, onMusicBrowse, onMusicPlay… ≥ 5.3
Wake-on-LAN network_wake: true wakeOnLan ≥ 4.86
Conexión a una cuenta sin redirección (código QR…) campo account_link onOAuthAuthorizeUrl ≥ 4.86

Todo está documentado en el README del SDK, y todas las reglas del manifiesto están en el README de la tienda.


Si te bloqueas en una migración o si una regla de validación te parece rara, responde en este tema o abre un issue en integration-sdk-js / integration-store.

¡Gracias a todos los que desarrollan integraciones, el catálogo está creciendo muy rápido! :folded_hands:

2 Me gusta