Alternativas para escenas interrumpidas por actualizaciones

EDIT: discusiones movidas de Gladys Assistant 4.85.0 : widget Photo, navigation dans les graphiques, chauffe-eau & HomeKit - #21 par Will_71 para dedicar un tema específico.


¡Bravo a todos por todas estas novedades y mejoras :heart_eyes:

Tengo una observación que me molesta casi en cada actualización: las escenas que tienen un Esperar en curso.
Para simplificar, pongo en marcha la bomba de la piscina durante una duración que yo defino y, por lo tanto, tengo una acción Esperar de X horas. Desafortunadamente, cuando ocurre una actualización, mi Esperar simplemente se salta y si no tengo cuidado, la piscina puede funcionar 8 horas en lugar de 3 horas :frowning:
Además de poner variables MQTT para el seguimiento de una escena en curso (que tendría que hacer), crear timestamps que también almaceno, y un script al (re)inicio de Gladys (por cierto, ¿existe un disparador al arranque?), ¿no existiría algo (nuevo) para reanudar estas escenas donde se detuvieron?

Gracias por el comentario, y has señalado un verdadero límite de la implementación actual :slight_smile:

Técnicamente, el bloque « Esperar » es solo un temporizador en la memoria del proceso Gladys. Nada se almacena en la base de datos: ni el hecho de que una escena esté en curso, ni dónde está en su lista de acciones. Por lo tanto, al menor reinicio (actualización, reinicio, corte de energía), todo lo que viene después del Esperar se va a la basura. No es un error de tu parte, es así hoy.

Y sí, el disparador « Gladys arranca » existe, lo encontrarás en la lista de disparadores.

Pero te aconsejaría más bien organizar las cosas para no necesitarlo.

Lo más inteligente en tu caso es no hacer que Gladys mantenga el cronómetro, sino que lo haga el enchufe mismo. La mayoría del hardware sabe hacer « enciéndete y apágate solo después de X segundos ». Depende de lo que tengas:

  • Shelly, con la acción HTTP Request: http://<ip>/relay/0?turn=on&timer=10800 (Gen1) o http://<ip>/rpc/Switch.Set?id=0&on=true&toggle_after=10800 (Gen2+)
  • Zigbee2MQTT, con la acción Zigbee2MQTT: en zigbee2mqtt/<tu_bomba>/set, envías {"state":"ON","on_time":10800,"off_wait_time":0}. Funciona en muchas tomas y módulos Tuya/Moes/Aqara.
  • Tasmota: PulseTime 10900; Power ON (más allá de 111, PulseTime vale valor − 100 en segundos)

Tu escena se convierte en una sola acción, sin ningún Esperar. Y la gran ventaja para una bomba de piscina: incluso si tu Gladys está desconectado o el mini-PC no se reinicia, la bomba se apaga de todos modos.

Si tu hardware no puede hacer esto, el segundo enfoque es un conteo en lugar de una espera.

Crea una característica de número a través de MQTT, como minutos_restantes_bomba. Tu escena de inicio enciende la bomba y pone el valor a 180. A un lado, haces una escena de supervisión con un disparador programado en modo intervalo cada 5 minutos, que recupera el valor, continúa solo si es > 0, lo reescribe con la fórmula {{0.last_value}} - 5, y si llega a 0, apaga la bomba. El modo fórmula funciona igual de bien en « Definir un valor » que en las condiciones, por lo que todo se hace con el ratón.

El interés es que se repara solo: no necesitas un script al arrancar ni el disparador al inicio, el próximo paso del cron corrige la situación. En el peor de los casos, pierdes los 5 minutos del tick en curso.

Y si puedes conformarte con eso, lo más simple sigue siendo dos escenas programadas, inicio a las 10h y parada a las 13h. Para una bomba de piscina, a menudo es suficiente y es indestructible. Si adaptas la duración a la temperatura del agua, incluso puedes hacer 3 escenas de parada (12h, 13h, 14h) con cada una una condición sobre la temperatura, es un poco bruto pero funciona siempre.

El principio a recordar: el Esperar está hecho para retrasos cortos dentro de una secuencia, como 2 segundos entre dos comandos o 30 segundos antes de verificar un estado. Tan pronto como superas unos pocos minutos, y especialmente cuando se trata de una acción de corte, debes o bien delegar el temporizador en el hardware, o bien pasar por un estado almacenado y releído regularmente.

Dicho esto, tu solicitud inicial sigue siendo legítima: persistir los Esperar largos para reprogramarlos al inicio, es factible. Si quieres abrir una solicitud de funcionalidad al respecto, no dudes en hacerlo.

Excelente método que no se me había ocurrido en absoluto.
Tengo un NodOn SIN-4-1-21 y parece que es posible:

Encendido con apagado programado

Al establecer el estado en ON, es posible especificar un apagado automático después de un cierto tiempo. Para ello, añade una propiedad adicional on_time a la carga útil que es el tiempo en segundos que el estado debe permanecer encendido.

Además, se puede añadir una propiedad off_wait_time a la carga útil para especificar el tiempo de espera de apagado en segundos cuando el interruptor no responderá a otros comandos de encendido con apagado programado.

El soporte depende del firmware del interruptor. Algunos dispositivos pueden requerir tanto on_time como off_wait_time para funcionar

Ejemplos: {"state" : "ON", "on_time": 300}, {"state" : "ON", "on_time": 300, "off_wait_time": 120}.
Y como me envío un mensaje al final de Esperar, tendré que hacerme una escena de supervisión que compruebe cada minuto si el módulo está encendido o apagado.

¡Gracias por el consejo!

He intentado con el cálculo de variable {"state":"ON","on_time": 1.1. Piscina (filtration_duree_cycle) *3600 ,"off_wait_time":0} y al parecer no ha funcionado :frowning:

¿Tengo que pasar por una variable MQTT temporal?
Y lo peor es que no tengo ningún medio para ver el comando enviado (nada en los logs de Gladys, nada en Z2M).

EDIT: finalmente {"state":"ON","on_time":10800,"off_wait_time":0} tampoco funciona :frowning:

Después de algunas pruebas, no hay que tomar la acción Enviar un mensaje zigbee2mqtt sino Enviar un mensaje MQTT de lo contrario no funciona.
@pierre-gilles ¿Hay una diferencia específica entre las 2 acciones?

He probado {"state":"ON","on_time":60,"off_wait_time":10} en MQTT (no zigbee2mqtt) y funciona, 1 minuto después la bomba se detiene correctamente.

He probado {"state":"ON","on_time":10800,"off_wait_time":10} y ahí hay un problema, ya sea del lado de Gladys o z2m, porque todo se multiplica por 10 y, por lo tanto, tengo un fuera de rango:

z2m: Publicar 'set' 'state' en 'relais_03' falló: 'RangeError [ERR_OUT_OF_RANGE]: ZCL command 0x84b4dbfffe08ce6e/1 genOnOff.onWithTimedOff({"ctrlbits":0,"ontime":108000,"offwaittime":100}, {"timeout":10000,"disableResponse":false,"disableRecovery":false,"disableDefaultResponse":false,"direction":0,"reservedBits":0,"writeUndiv":false}) falló (El valor de "value" está fuera de rango. Debe ser >= 0 y <= 65535. Recibido 108000)'

¿Alguna idea?

Sigo con mis pruebas y las variables no funcionan:


y en los registros de z2m:

z2m: Mensaje no válido 'undefined', se omite...

no muy explícito…

Gracias por todas estas pruebas, has encontrado varios problemas, y al menos uno es un error de nuestro lado. Lo reviso en orden.

1. La diferencia entre las dos acciones

En el código, Enviar un mensaje MQTT y Enviar un mensaje Zigbee2mqtt hacen exactamente lo mismo: tu texto pasa por el motor de variables y luego se publica tal cual. La única diferencia es el cliente MQTT utilizado: la acción MQTT publica en el broker configurado en el servicio MQTT, la acción Zigbee2mqtt publica en la conexión propia del servicio Zigbee2mqtt (por defecto el Mosquitto dedicado a Z2M, mqtt://localhost:1884).

Si tu Z2M funciona fuera de Gladys con tu propio broker, la acción Zigbee2mqtt se envía potencialmente al vacío: nadie escucha, y como el registro de publicación está en nivel debug, no ves nada en ninguna parte. No has perdido nada. También hay que señalar: esta acción no añade ningún prefijo zigbee2mqtt/, hay que escribir el tema completo zigbee2mqtt/relais_03/set — el marcador de posición gladys/my-topic es engañoso, lo vamos a corregir.

En resumen: en tu configuración, quédate con Enviar un mensaje MQTT, es la mejor opción.

2. El ×10 y el RangeError

Aquí no es Gladys ni un error de Z2M: es el protocolo Zigbee. El comando ZCL genOnOff.onWithTimedOff expresa ontime y offwaittime en décimas de segundo, en un entero de 16 bits. Zigbee2MQTT hace on_time × 10, y el límite es 65535.

En otras palabras, el máximo absoluto es 6553 segundos, es decir, 1 h 49 min. Tus 3 h nunca pasarán en un solo comando, sin importar el camino utilizado. Por eso tu 10800 también fallaba con la acción Zigbee2mqtt, además del problema del broker.

3. Las variables en el payload

El campo mensaje acepta variables, pero con dos limitaciones importantes:

  • la sintaxis es {{ }} (escribe {{ en el campo, aparece la autocompletar), y la variable debe provenir de una acción Obtener el último estado colocada antes, en un grupo anterior;
  • ningún cálculo es posible en este campo. A diferencia de Definir un valor o las condiciones, no hay un motor de fórmulas aquí: {{0.0.last_value}}*3600 se enviará literalmente como 3*3600, no como 10800.

Y cuando la variable no se resuelve, se reemplaza con una cadena vacía, lo que produce un JSON roto del tipo {"state":"ON","on_time":,"off_wait_time":0} — de ahí el rechazo en el lado de Z2M. Deberíamos registrar claramente el payload realmente publicado, es una verdadera laguna de depuración. Mientras tanto, para ver lo que realmente se envía:

mosquitto_sub -h <tu_broker> -t 'zigbee2mqtt/relais_03/set' -v

Para hacer tu multiplicación, hay que sacarla del payload: una acción Definir un valor en modo fórmula en una característica de número MQTT (pompe_on_time), con la fórmula {{...}}*3600, luego un Obtener el último estado en esta característica, y finalmente {{x.y.last_value}} en el JSON.

4. Atención, un error de nuestro lado en las fórmulas

Al verificar este punto, descubrí que nuestro motor de fórmulas se instancia sin la función subtract: la resta no funciona. La fórmula {{0.0.last_value}} - 5 que te di antes levanta una excepción, que se traga silenciosamente (la acción falla, la escena continúa, nada pasa). Mis disculpas, era falso.

Mientras tanto, el contorno es escribir {{0.0.last_value}} + -5, que sí funciona. +, *, /, round() y mod están bien.

Ticket creado: Scene formula engine: subtraction is not supported (`Function subtract missing in provided namespace "math"`) · Issue #2823 · GladysAssistant/Gladys · GitHub

5. Lo que te recomiendo al final: el conteo + perro guardián

Dado el límite de 1 h 49, el buen enfoque combina las dos ideas, y es incluso mejor de lo que propuse al principio, porque sigue siendo irrompible en caso de corte de Gladys.

Crea una característica de número MQTT pompe_minutes_restantes.

Escena « Iniciar la bomba »:

  • Definir un valor: pompe_minutes_restantes = 180
  • Enviar un mensaje MQTT en zigbee2mqtt/relais_03/set: {"state":"ON","on_time":900,"off_wait_time":0}

Escena « Supervisión de la bomba », desencadenador programado en intervalo cada 10 minutos:

  • Obtener el último estado de pompe_minutes_restantes
  • Si / De lo contrario en > 0:
    • entonces: Definir un valor = {{0.0.last_value}} + -10, luego republicar {"state":"ON","on_time":900,"off_wait_time":0}
    • de lo contrario: publicar {"state":"OFF"}

El principio: el relé nunca recibe más que una autorización de 15 minutos, que la escena de supervisión renueva mientras el conteo es positivo. Como resultado, si Gladys se corta para una actualización, un reinicio o un fallo, la bomba se detiene sola después de un máximo de 15 minutos, y al reiniciar el conteo se reanuda donde estaba ya que se almacena en la base. Más ningún Esperar, y nada que reparar al arrancar.

Mantén bien off_wait_time en 0: es un tiempo de guarda durante el cual el módulo rechaza los nuevos comandos on with timed off, impediría el rearme.

6. Tu escena de supervisión cada minuto

No es necesario. El NodOn sube su cambio de estado a Z2M cuando se corta solo, por lo que una escena con el desencadenador Un dispositivo cambia de estado en la característica del relé, condición « está apagado », es suficiente para enviarte tu notificación de fin.

Creo los tickets para la resta faltante, el registro del payload publicado y el marcador de posición del tema Z2M.

https://github.com/GladysAssistant/Gladys/issues/2825

https://github.com/GladysAssistant/Gladys/issues/2826

Gracias @pierre-gilles por las explicaciones, entiendo la diferencia entre las acciones MQTT y Z2M.
En mi caso, tengo la integración Z2M externa con un MQTT externo, y la integración MQTT de Gladys apunta al MQTT externo, por lo que las dos acciones van a buscar en el mismo cesto final.
Esto significa que si uso una u otra de las órdenes, debería obtener el mismo resultado, pero no es así, ya que funciona con la acción MQTT y no con Z2M.

Veo que Claude ha respondido, ya que en algunas afirmaciones veo lobos :face_with_raised_eyebrow:

Y lo que no entiendo en absoluto es que he probado con 60 (apagado después de 1 minuto), 1000 (apagado después de unos 16 minutos) y 3600 (apagado después de 1 hora), y ninguno de estos números se ha multiplicado por 10, ya que los apagados eran correctos..

Sí, pero.
Tengo un paso intermedio para evitar el cálculo:




Y el mensaje enviado me indica el tiempo recalculado en segundos, pero no funciona en el payload.
Y pruebo con 1 hora para asegurarme de no tener un fuera de rango.

Voy a mirar el punto 5 con más detalle, me parece una buena idea, y voy a tener cuidado con la resta por ahora.

Lo siento, solo estoy pasando por aquí con Claude, pero con todo el trabajo en las integraciones externas no puedo ofrecerte algo mejor :smiley:

Tienes razón en los dos puntos, y en uno de ellos fui yo quien se equivocó. Lo retomo.

El ×10: tus pruebas confirman la explicación, no la contradicen.

Mi formulación era incorrecta. El ×10 no es algo que se «añade» a tu duración, es una conversión de unidades: el campo ZCL se expresa en décimas de segundo, por lo que Zigbee2MQTT convierte tus segundos en décimas antes de enviar, y el módulo reconvierte al llegar. Resultado, tu duración se respeta correctamente — es exactamente lo que has medido:

on_time valor enviado apagado
60 600 1 min :check_mark:
1000 10000 16 min 40 s :check_mark:
3600 36000 1 h :check_mark:
10800 108000 > 65535 → RangeError ✘

El código está aquí, en los convertidores Z2M:

{ctrlbits: 0, ontime: Math.round(onTime * 10), offwaittime: Math.round(offWaitTime * 10)}

El único momento en que se nota es cuando el resultado supera 65535, el límite de un entero de 16 bits — y es precisamente el mensaje de error que has tenido. El techo es, por tanto, 6553 segundos, es decir, 1 h 49 min. Tus 3600 pasan, tus 10800 nunca pasarán. (Por cierto, Z2M limita este valor para otros parámetros del mismo tipo, pero no para on_time, de ahí el error brusco en lugar de un límite silencioso; merecería un ticket de ellos.)

El registro Invalid message 'undefined' no significa lo que creemos.

He mirado el código de Z2M:

const message = this.parseMessage(parsedTopic, data);

if (!message) {

logger.error(`Invalid message '${message}', skipping...`);

Muestra el resultado del análisis, no el mensaje recibido. Como el análisis ha fallado, este resultado siempre vale undefined. En otras palabras, este registro dirá undefined independientemente de la carga útil: no es Gladys quien te ha enviado la palabra undefined. La única información útil es «el JSON recibido no era válido». Es por eso que te decía que pasaras por mosquitto_sub: es hoy el único medio de ver la carga útil real.

El editor, en cambio, está fuera de causa.

He verificado la pista de un texto dañado por el campo de variables (caracteres invisibles, comillas reencodificadas): he vuelto a ejecutar el componente en un navegador con tu configuración, el texto sale idéntico al carácter cerca, llaves y comillas incluidas. No es ahí donde falla.

En cambio, he encontrado una verdadera trampa, y podría ser la tuya.

Si la variable es el último elemento de tu JSON, te encuentras con tres llaves juntas:

{"state":"ON","on_time":{{1.1. Piscine ...}}}

Este }}} se interpreta como el cierre de otra sintaxis de variable, el motor de plantillas falla, y el error se traga silenciosamente: nada se publica en absoluto. Dos soluciones inmediatas, las dos probadas:

  • poner un espacio antes de la llave final: ..."on_time":{{variable}} }
  • o simplemente no terminar con la variable: {"on_time":{{variable}},"state":"ON","off_wait_time":0}

Lo que necesitaría para resolver tu caso

Las capturas no me permiten ver los caracteres exactos. ¿Puedes copiar y pegar, en texto:

  1. el contenido exacto del campo mensaje de tu acción MQTT;
  2. el tema exacto;
  3. y, si es posible, la salida de mosquitto_sub -h <tu_broker> -t 'zigbee2mqtt/relais_03/set' -v mientras lanzas la escena.

Con eso sabremos en treinta segundos si es el }}}, una variable vacía o algo más.

Sobre MQTT vs Z2M con el mismo broker: tienes razón, mi explicación no se sostiene.

Si los dos apuntan al mismo broker externo, las dos acciones deben producir efectivamente el mismo resultado. Quedan dos pistas:

  • el tema. La acción Zigbee2mqtt no añade ningún prefijo, a diferencia de lo que su nombre sugiere. ¿Has escrito bien zigbee2mqtt/relais_03/set en las dos acciones? Si en la acción Z2M habías puesto relais_03/set, eso lo explicaría todo.
  • la conexión. El servicio Zigbee2mqtt abre su propia sesión MQTT, con las credenciales introducidas en su integración — distintas de las del servicio MQTT. Si estas credenciales son rechazadas por tu broker, la biblioteca MQTT pone los mensajes en cola sin levantar nunca un error visible. ¿Se muestra tu integración Zigbee2mqtt bien como conectada en Gladys?

No hay problema @pierre-gilles, sé que tienes un montón de temas en este momento y necesitas un asistente :wink: ! … ¿Gladys? no, Claude :sweat_smile:

¡Es bueno, acabo de entender el funcionamiento y, por lo tanto, antes de reconvertir en Z2M, hay la barrera fatal de 65535 que no se debe superar! ¡Todo está bien!

  1. copiar/pegar tal cual:
{"state":"ON","on_time":

2.1.A.2.1. Piscina (filtration_duree_cycle_secondes)

 ,"off_wait_time":0}

pero no es posible dar el contenido exacto porque ya no hay llaves una vez, pero tengo un espacio después de la variable:
image

  1. zigbee2mqtt/relais_03/set
  2. mosquitto_sub -h <tu_broker> -t 'zigbee2mqtt/relais_03/set' -v
    Entonces, no sé absolutamente dónde golpear este comando, porque en el docker Gladys me pone comando desconocido :frowning:

no hay atajo, tengo el tema completo con zigbee2mqtt/relais_03/set.
Y lo había probado los dos por si acaso :wink:

Todo es absolutamente igual en cuanto a las configuraciones de inicio de sesión/contraseña en MQTT y Z2M, y todo está operativo, de lo contrario tendría problemas para controlar todos los demás dispositivos:


Vale, ¿todo está bien en tu lado entonces? :slight_smile: Puedes marcar el tema como « Resuelto » si es así :wink:

no, aún no resuelto :frowning:

No sé si tienes una cuenta Claude, pero tal vez sería más rápido si le hablas directamente :joy:

Aquí está la respuesta de Claude :

Encontrado. Y no eres tú, somos nosotros — tu copia y pega contenía la respuesta, solo había que mirar los caracteres uno por uno.

Tu payload contiene un espacio que no es un espacio.

Cuando insertas una variable desde la lista, el editor agrega automáticamente un espacio después — ese es el que habías notado. Excepto que el navegador no pone un espacio común: pone un espacio en blanco insecable, el carácter U+00A0. Visualmente es idéntico, incluso en una copia y pega, pero para un analizador JSON es un carácter prohibido en ese lugar.

Por lo tanto, tu payload se envía así, con un espacio en blanco insecable donde he puesto el símbolo:

{"state":"ON","on_time":3600␣,"off_wait_time":0}

JSON.parse se niega, parseMessage devuelve undefined, y Z2M registra su famoso Invalid message 'undefined'. El círculo está cerrado.

Reproduje tu entrada exacta en un navegador, con tu variable y tu payload: reproduzco el carácter a la perfección. Y eso explica finalmente todo lo demás:

  • tu notificación con la misma variable se muestra correctamente — un espacio en blanco insecable en una oración no se nota y no molesta a nadie;
  • tu prueba del post #5 con on_time:60 en duro funcionaba — sin variable, por lo tanto sin espacio añadido, por lo tanto sin espacio en blanco insecable;
  • y nada podía aparecer en los registros, ni en los nuestros ni en los de Z2M.

La solución, de inmediato

En tu campo de mensaje: coloca el cursor justo después de la variable y presiona una vez la tecla de retroceso para eliminar el espacio. Debes obtener la variable directamente pegada a la coma. Lo he verificado, el payload vuelve a ser válido y el comando pasa.

Tenga en cuenta también la segunda trampa: no termine su JSON con la variable, de lo contrario las tres llaves pegadas }}} hacen que el motor de plantilla falle. Siempre ponga algo después, por ejemplo ,"off_wait_time":0}. En tu caso ya está bien.

Corrección

Es un error franco de nuestro lado: el editor nunca debería dejar pasar este carácter. La corrección es trivial, normalizamos el espacio en blanco insecable en el momento en que el texto se guarda. Estará en una próxima versión, y de aquí a entonces tu retroceso es suficiente.

Para mosquitto_sub

Es normal que no funcione en el contenedor Gladys, la herramienta no está instalada. Tres opciones según lo que te convenga:

  • desde el contenedor de tu broker: docker exec -it <nombre_del_contenedor_mosquitto> mosquitto_sub -t 'zigbee2mqtt/relais_03/set' -v
  • en la máquina anfitriona, después de un sudo apt install mosquitto-clients
  • o, más simple si prefieres el gráfico, MQTT Explorer, que te muestra todo el tráfico del broker en directo

Dicho esto, ya no lo necesitas para este problema.

Queda un punto sin resolver

Tu observación del post #5 — la acción MQTT funciona, la acción Zigbee2mqtt no, con el mismo topic completo y el mismo broker — sigue sin explicarse, y el espacio en blanco insecable no cambia nada ya que tu prueba era en valores fijos. Ahora que sabemos que este campo puede contener caracteres invisibles, ¿puedes hacer una nueva prueba limpia: dos acciones, el payload {"state":"ON","on_time":60,"off_wait_time":10} reescrito completamente a mano en cada una, mismo topic zigbee2mqtt/relais_03/set? Si la acción Zigbee2mqtt sigue fallando, tenemos un segundo error que investigar y abriré un ticket al respecto.

Corregido en Gladys Assistant 4.86:

¡Por fin he hecho nuevas pruebas y funciona mucho, incluso en z2m, ¡genial!

Sin embargo, hay un espacio que se añade al final de la variable que insertamos:


Y este espacio estropea el json enviado a z2m que recibe un mensaje incorrecto → z2m: Mensaje inválido 'undefined', omitiendo...

Por lo tanto, es obligatorio eliminar este espacio entre la variable y la coma y entonces todo funciona correctamente.
Voy a crear una solicitud de mejora.

EDIT: [Scènes] supprimer l'espace inséré automatiquement après l'appel d'une variable