Alternatives pour les scènes interrompues par les mises à jour

EDIT : discussions déplacées de Gladys Assistant 4.85.0 : widget Photo, navigation dans les graphiques, chauffe-eau & HomeKit - #21 par Will_71 pour dédier un sujet précis.


Bravo à tous pour toutes ces nouveautés et améliorations :heart_eyes:

J’ai une remarque qui me chiffonne presque à chaque mise à jour : les scènes qui ont un Attendre en cours.
Pour faire simple, je mets en route la pompe de la piscine pour une durée que je définis et donc j’ai une action Attendre de X heures. Malheureusement quand survient une mise à jour, et bien mon Attendre saute tout simplement et si je ne fais pas gaffe, la piscine peut tourner 8h au lieu de 3h :frowning:
A part mettre des variables MQTT pour le suivi d’une scène en cours (qu’il faudrait que je fasse), me créer des timestamps que je stocke aussi, et un script au (re)démarrage de Gladys (d’ailleurs ça existe un déclencheur au boot ?), il n’existerait pas un (nouveau ?) quelque chose pour reprendre ces scènes où elles se sont arrêtées ?

Merci pour le retour, et tu mets le doigt sur une vraie limite de l’implémentation actuelle :slight_smile:

Techniquement, le bloc « Attendre » c’est juste un timer en mémoire dans le process Gladys. Rien n’est stocké en base : ni le fait qu’une scène soit en cours, ni où elle en est dans sa liste d’actions. Du coup au moindre redémarrage (mise à jour, reboot, coupure de courant), tout ce qui vient après le Attendre part à la poubelle. C’est pas un bug de ton côté, c’est comme ça aujourd’hui.

Et oui, le déclencheur « Gladys démarre » existe bien, tu le trouveras dans la liste des déclencheurs.

Mais je te conseillerais plutôt de t’organiser pour ne pas en avoir besoin.

Le truc le plus malin dans ton cas, c’est de ne pas faire tenir le chrono par Gladys du tout, mais par la prise elle-même. La plupart du matériel sait faire « allume-toi et coupe-toi tout seul dans X secondes ». Ça dépend de ce que tu as :

  • Shelly, avec l’action Requête HTTP : http://<ip>/relay/0?turn=on&timer=10800 (Gen1) ou http://<ip>/rpc/Switch.Set?id=0&on=true&toggle_after=10800 (Gen2+)
  • Zigbee2MQTT, avec l’action Zigbee2MQTT : sur zigbee2mqtt/<ta_pompe>/set, tu envoies {"state":"ON","on_time":10800,"off_wait_time":0}. Ça marche sur pas mal de prises et modules Tuya/Moes/Aqara.
  • Tasmota : PulseTime 10900; Power ON (au-delà de 111, PulseTime vaut valeur − 100 en secondes)

Ta scène devient une seule action, plus aucun Attendre. Et l’avantage énorme pour une pompe de piscine : même si ton Gladys est coupé ou que le mini-PC ne redémarre pas, la pompe s’arrête quand même.

Si ton matériel ne sait pas faire ça, la deuxième approche c’est un décompte plutôt qu’une attente.

Tu crées une feature nombre via MQTT, genre minutes_restantes_pompe. Ta scène de démarrage allume la pompe et met la valeur à 180. À côté, tu fais une scène de surveillance avec un déclencheur planifié en mode intervalle toutes les 5 minutes, qui récupère la valeur, continue seulement si elle est > 0, la réécrit avec la formule {{0.last_value}} - 5, et si elle tombe à 0 elle coupe la pompe. Le mode formule marche aussi bien sur « Définir une valeur » que dans les conditions, donc tout se fait à la souris.

L’intérêt c’est que ça se répare tout seul : pas besoin de script au boot ni du déclencheur au démarrage, le prochain passage du cron rattrape la situation. Au pire tu perds les 5 minutes du tick en cours.

Et si tu peux t’en contenter, le plus simple reste deux scènes planifiées, départ à 10h et arrêt à 13h. Pour une pompe de piscine c’est souvent suffisant et c’est increvable. Si tu adaptes la durée à la température de l’eau, tu peux même faire 3 scènes d’arrêt (12h, 13h, 14h) avec chacune une condition sur la température, c’est un peu bourrin mais ça marche toujours.

Le principe à retenir : le Attendre est fait pour des délais courts à l’intérieur d’une séquence, genre 2 secondes entre deux commandes ou 30 secondes avant de vérifier un état. Dès que tu dépasses quelques minutes, et surtout quand c’est une action de coupure qui est en jeu, il faut soit déporter le minuteur sur le matériel, soit passer par un état stocké et relu régulièrement.

Ceci dit, ta demande de base reste légitime : persister les Attendre longs pour les replanifier au démarrage, c’est faisable. Si tu veux ouvrir une demande de fonctionnalité dessus, n’hésite pas.

Excellente méthode que je n’avais pas imaginé du tout.
J’ai un NodOn SIN-4-1-21 et il semble que ce soit possible :

On with timed off

When setting the state to ON, it might be possible to specify an automatic shutoff after a certain amount of time. To do this add an additional property on_time to the payload which is the time in seconds the state should remain on.

Additionally an off_wait_time property can be added to the payload to specify the cooldown time in seconds when the switch will not answer to other on with timed off commands.

Support depends on the switch firmware. Some devices might require both on_time and off_wait_time to work

Examples : {"state" : "ON", "on_time": 300}, {"state" : "ON", "on_time": 300, "off_wait_time": 120}.
Et comme je m’envoie un message à la fin du Attendre, il va falloir que je me fasse une scène de supervision qui checke toutes les minutes si le module est allumé ou éteint.

Merci pour l’astuce !

J’ai tenté avec le calcul de variable {"state":"ON","on_time": 1.1. Piscine (filtration_duree_cycle) *3600 ,"off_wait_time":0} et apparemment ça n’a pas fonctionné :frowning:

Il faut que je passe par une variable MQTT temporaire ?
Et le truc c’est que je n’ai aucun moyen de voir la commande envoyée (rien dans les logs Gladys, rien dans Z2M).

EDIT : finalement {"state":"ON","on_time":10800,"off_wait_time":0} ne fonctionne pas non plus :frowning:

Après quelques tests, il ne faut pas prendre l’action Envoyer un message zigbee2mqttmais Envoyer un message MQTT sinon ça ne marche pas.
@pierre-gilles il y a une différence spécifique entre les 2 actions ?

J’ai testé {"state":"ON","on_time":60,"off_wait_time":10} en MQTT (pas zigbee2mqtt) et ça fonctionne, 1 minute après le pompe s’arrête bien.

J’ai testé {"state":"ON","on_time":10800,"off_wait_time":10} et là il y a un soucis, soit côté Gladys soit z2m, car tout est multiplié par 10 et donc j’ai un out of range :

z2m: Publish 'set' 'state' to 'relais_03' failed: '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}) failed (The value of "value" is out of range. It must be >= 0 and <= 65535. Received 108000)'

Une idée ?

Je continue mes tests et les variables ne fonctionnent pas :


et dans les logs z2m :

z2m: Invalid message 'undefined', skipping...

pas super explicite …

Merci pour tous ces tests, tu as mis le doigt sur plusieurs choses, et il y en a au moins une qui est un bug de notre côté. Je reprends dans l’ordre.

1. La différence entre les deux actions

Dans le code, Envoyer un message MQTT et Envoyer un message Zigbee2mqtt font exactement la même chose : ton texte passe dans le moteur de variables, puis est publié tel quel. La seule différence, c’est le client MQTT utilisé : l’action MQTT publie sur le broker configuré dans le service MQTT, l’action Zigbee2mqtt publie sur la connexion propre du service Zigbee2mqtt (par défaut le Mosquitto dédié à Z2M, mqtt://localhost:1884).

Si ton Z2M tourne en dehors de Gladys avec ton propre broker, l’action Zigbee2mqtt part donc potentiellement dans le vide : personne n’écoute, et comme le log de publication est en niveau debug, tu ne vois effectivement rien nulle part. Ce n’est pas toi qui as raté quelque chose. À noter aussi : cette action n’ajoute aucun préfixe zigbee2mqtt/, il faut écrire le topic complet zigbee2mqtt/relais_03/set — le placeholder gladys/my-topic est trompeur, on va le corriger.

Bref : dans ta config, reste sur Envoyer un message MQTT, c’est le bon choix.

2. Le ×10 et le RangeError

Là ce n’est ni Gladys ni un bug Z2M : c’est le protocole Zigbee. La commande ZCL genOnOff.onWithTimedOff exprime ontime et offwaittime en dixièmes de seconde, sur un entier 16 bits. Zigbee2MQTT fait donc on_time × 10, et le plafond est 65535.

Autrement dit, le maximum absolu est 6553 secondes, soit 1 h 49 min. Tes 3 h ne passeront jamais en une seule commande, quel que soit le chemin utilisé. C’est pour ça que ton 10800 échouait aussi avec l’action Zigbee2mqtt, en plus du problème de broker.

3. Les variables dans le payload

Le champ message accepte les variables, mais avec deux limites importantes :

  • la syntaxe est {{ }} (tape {{ dans le champ, l’autocomplétion apparaît), et la variable doit venir d’une action Récupérer le dernier état placée avant, dans un groupe précédent ;
  • aucun calcul n’est possible dans ce champ. Contrairement à Définir une valeur ou aux conditions, il n’y a pas de moteur de formule ici : {{0.0.last_value}}*3600 sera envoyé littéralement comme 3*3600, pas comme 10800.

Et quand la variable n’est pas résolue, elle est remplacée par une chaîne vide, ce qui produit un JSON cassé du type {"state":"ON","on_time":,"off_wait_time":0} — d’où le rejet côté Z2M. On devrait clairement logger le payload réellement publié, c’est une vraie lacune de debug. En attendant, pour voir ce qui part vraiment :

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

Pour faire ta multiplication, il faut la sortir du payload : une action Définir une valeur en mode formule sur une feature nombre MQTT (pompe_on_time), avec la formule {{...}}*3600, puis un Récupérer le dernier état sur cette feature, et enfin {{x.y.last_value}} dans le JSON.

4. Attention, un bug de notre côté sur les formules

En vérifiant ce point j’ai découvert que notre moteur de formules est instancié sans la fonction subtract : la soustraction ne fonctionne pas. La formule {{0.0.last_value}} - 5 que je t’avais donnée plus haut lève une exception, qui est avalée silencieusement (l’action échoue, la scène continue, rien ne se passe). Mes excuses, c’était faux.

En attendant le correctif, le contournement est d’écrire {{0.0.last_value}} + -5, qui lui fonctionne. +, *, /, round() et mod sont OK.

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

5. Ce que je te conseille au final : le décompte + chien de garde

Vu le plafond de 1 h 49, la bonne approche combine les deux idées, et c’est même mieux que ce que je proposais au départ, parce que ça reste increvable en cas de coupure de Gladys.

Crée une feature nombre MQTT pompe_minutes_restantes.

Scène « Démarrer la pompe » :

  • Définir une valeur : pompe_minutes_restantes = 180
  • Envoyer un message MQTT sur zigbee2mqtt/relais_03/set : {"state":"ON","on_time":900,"off_wait_time":0}

Scène « Surveillance pompe », déclencheur planifié en intervalle toutes les 10 minutes :

  • Récupérer le dernier état de pompe_minutes_restantes
  • Si / Sinon sur > 0 :
    • alors : Définir une valeur = {{0.0.last_value}} + -10, puis republier {"state":"ON","on_time":900,"off_wait_time":0}
    • sinon : publier {"state":"OFF"}

Le principe : le relais ne reçoit jamais qu’une autorisation de 15 minutes, que la scène de surveillance renouvelle tant que le décompte est positif. Résultat, si Gladys est coupé pour une mise à jour, un reboot ou un crash, la pompe s’arrête toute seule au bout de 15 minutes maximum, et au redémarrage le décompte reprend là où il en était puisqu’il est stocké en base. Plus aucun Attendre, et plus rien à réparer au boot.

Garde bien off_wait_time à 0 : c’est un temps de garde pendant lequel le module refuse les nouvelles commandes on with timed off, il empêcherait le réarmement.

6. Ta scène de supervision toutes les minutes

Pas besoin. Le NodOn remonte son changement d’état à Z2M quand il se coupe tout seul, donc une scène avec le déclencheur Un appareil change d'état sur la feature du relais, condition « est éteint », suffit pour t’envoyer ta notification de fin.

Je crée les tickets pour la soustraction manquante, le log du payload publié et le placeholder de topic Z2M.

Merci @pierre-gilles pour les explications, je comprends la différence entre les actions MQTT et Z2M.
Dans mon cas j’ai l’intégration Z2M en externe avec un MQTT en externe, et l’intégration MQTT de Gladys pointe sur le MQTT externe, donc les 2 actions vont dans piocher dans le même panier final.
Ce qui veut dire que si j’utilise l’une ou l’autre des commandes je dois avoir le même résultat mais ce n’est pas le cas car ça fonctionne avec l’action MQTT et pas Z2M.

Je vois que Claude a répondu car dans certaines affirmations je vois des loups :face_with_raised_eyebrow:

Et ce que je ne comprends du tout c’est que j’ai testé avec des 60 (extinction après 1mn), 1000 (extinction après 16mn environ) et 3600 (extinction après 1h), et aucun de ces nombres n’ont été multipliés par 10 vu que les extinctions étaient bonnes..

Oui mais.
J’ai bien une étape intermédiaire pour éviter le calcul :




Et le message envoyé m’indique bien le temps recalculé en secondes mais ça ne fonctionne pas dans le payload.
Et je teste avec 1h pour être sûr de ne pas avoir d’out of range.

Je vais regarder le point 5 plus en détail, ça me semble une bonne idée, et je vais gaffe à la soustraction pour l’instant.

Je suis désolé je fais juste passe plat avec Claude mais avec tout le travail sur les intégrations externes je ne peux pas te proposer mieux :smiley:

Tu as raison sur les deux points, et sur l’un des deux c’est moi qui me suis trompé. Je reprends.

Le ×10 : tes tests confirment l’explication, ils ne la contredisent pas.

Ma formulation était mauvaise. Le ×10 n’est pas quelque chose qui « s’ajoute » à ta durée, c’est une conversion d’unité : le champ ZCL est exprimé en dixièmes de seconde, donc Zigbee2MQTT convertit tes secondes en dixièmes avant d’envoyer, et le module reconvertit à l’arrivée. Résultat, ta durée est bien respectée — c’est exactement ce que tu as mesuré :

on_time valeur envoyée extinction
60 600 1 min :check_mark:
1000 10000 16 min 40 s :check_mark:
3600 36000 1 h :check_mark:
10800 108000 > 65535 → RangeError

Le code est ici, dans les convertisseurs Z2M :

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

Le seul moment où ça se voit, c’est quand le résultat dépasse 65535, la limite d’un entier 16 bits — et c’est précisément le message d’erreur que tu as eu. Le plafond est donc 6553 secondes, soit 1 h 49 min. Tes 3600 passent, tes 10800 ne passeront jamais. (Au passage, Z2M borne cette valeur pour d’autres paramètres du même type mais pas pour on_time, d’où l’erreur brutale plutôt qu’un plafonnement silencieux ; ça mériterait un ticket chez eux.)

Le log Invalid message 'undefined' ne veut pas dire ce qu’on croit.

J’ai regardé le code de Z2M :

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

if (!message) {

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

Il affiche le résultat du parsing, pas le message reçu. Comme le parsing a échoué, ce résultat vaut toujours undefined. Autrement dit ce log dira undefined quel que soit le payload : ce n’est pas Gladys qui t’a envoyé le mot undefined. La seule information utile est « le JSON reçu n’était pas valide ». C’est pour ça que je te disais de passer par mosquitto_sub : c’est aujourd’hui le seul moyen de voir le payload réel.

L’éditeur, lui, est hors de cause.

J’ai vérifié la piste d’un texte abîmé par le champ à variables (caractères invisibles, guillemets réencodés) : j’ai rejoué le composant dans un navigateur avec ta configuration, le texte ressort identique au caractère près, accolades et guillemets compris. Ce n’est pas là que ça casse.

En revanche j’ai trouvé un vrai piège, et il pourrait être le tien.

Si la variable est le dernier élément de ton JSON, tu te retrouves avec trois accolades collées :

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

Ce }}} est interprété comme la fermeture d’une autre syntaxe de variable, le moteur de template plante, et l’erreur est avalée silencieusement : rien n’est publié du tout. Deux contournements immédiats, les deux testés :

  • mettre un espace avant l’accolade finale : ..."on_time":{{variable}} }
  • ou simplement ne pas finir par la variable : {"on_time":{{variable}},"state":"ON","off_wait_time":0}

Ce qu’il me faudrait pour trancher ton cas

Les captures ne me permettent pas de voir les caractères exacts. Peux-tu me copier-coller, en texte :

  1. le contenu exact du champ message de ton action MQTT ;
  2. le topic exact ;
  3. et si possible la sortie de mosquitto_sub -h <ton_broker> -t 'zigbee2mqtt/relais_03/set' -v pendant que tu lances la scène.

Avec ça on saura en trente secondes si c’est le }}}, une variable vide, ou autre chose.

Sur MQTT vs Z2M avec le même broker : tu as raison, mon explication ne tient pas.

Si les deux pointent sur le même broker externe, les deux actions doivent effectivement produire le même résultat. Restent deux pistes :

  • le topic. L’action Zigbee2mqtt n’ajoute aucun préfixe, contrairement à ce que son nom suggère. As-tu bien écrit zigbee2mqtt/relais_03/set dans les deux actions ? Si dans l’action Z2M tu avais mis relais_03/set, ça expliquerait tout.
  • la connexion. Le service Zigbee2mqtt ouvre sa propre session MQTT, avec les identifiants saisis dans son intégration — distincts de ceux du service MQTT. Si ces identifiants sont refusés par ton broker, la bibliothèque MQTT met les messages en file d’attente sans jamais lever d’erreur visible. Est-ce que ton intégration Zigbee2mqtt s’affiche bien comme connectée dans Gladys ?

Pas de soucis @pierre-gilles , je sais que tu as un gros paquet de sujets en ce moment et il te faut un assistant :wink: ! … Gladys ? non Claude :sweat_smile:

C’est bon, je viens enfin de comprendre le fonctionnement et donc avant de reconvertir dans Z2M, il y a la barrière fatidique des 65535 à ne pas dépasser. C’est tout bon !

  1. copier/coller tel quel :
{"state":"ON","on_time":

2.1.A.2.1. Piscine (filtration_duree_cycle_secondes)

 ,"off_wait_time":0}

sauf qu’il n’est pas possible de donner le contenu exact car on n’a plus les accolades une fois mais j’ai un espace après la variable :
image

  1. zigbee2mqtt/relais_03/set
  2. mosquitto_sub -h <ton_broker> -t 'zigbee2mqtt/relais_03/set' -v
    Alors je ne sais absolument pas où taper cette commande car dans le docker Gladys ça me met commande inconnue :frowning:

pas de raccourci, j’ai le topic total avec zigbee2mqtt/relais_03/set.
Et j’avais testé les 2 au cas :wink:

Tout est absolument pareil au niveau des configurations login/pwd dans MQTT et Z2M, et tout est opérationnel sinon j’aurais des soucis pour piloter toutes les autres appareils :


Ok, tout est bon de ton côté du coup ? :slight_smile: Tu peux mettre le sujet en « Résolu » si oui :wink:

non pas encore résolu :frowning:

Je ne sais pas si tu as un compte Claude, mais ça serait peut-être plus rapide si tu lui parlais directement :joy:

Voilà la réponse de Claude :

Trouvé. Et ce n’est pas toi, c’est nous — ton copier-coller contenait la réponse, il fallait juste regarder les caractères un par un.

Ton payload contient un espace qui n’en est pas un.

Quand tu insères une variable depuis la liste, l’éditeur ajoute automatiquement un espace derrière — c’est celui que tu avais repéré. Sauf que le navigateur ne met pas un espace ordinaire : il met une espace insécable, le caractère U+00A0. Visuellement c’est rigoureusement identique, y compris dans un copier-coller, mais pour un analyseur JSON c’est un caractère interdit à cet endroit.

Ton payload part donc comme ceci, avec un insécable là où j’ai mis le symbole :

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

JSON.parse refuse, parseMessage renvoie undefined, et Z2M logge son fameux Invalid message 'undefined'. La boucle est bouclée.

J’ai rejoué ta saisie exacte dans un navigateur, avec ta variable et ton payload : je reproduis le caractère à l’identique. Et ça explique enfin tout le reste :

  • ta notification avec la même variable s’affiche correctement — une espace insécable dans une phrase, ça ne se voit pas et ça ne gêne personne ;
  • ton test du post #5 avec on_time:60 en dur fonctionnait — pas de variable, donc pas d’espace ajouté, donc pas d’insécable ;
  • et rien ne pouvait apparaître dans les logs, ni chez nous ni chez Z2M.

Le contournement, tout de suite

Dans ton champ message : place ton curseur juste après la variable et appuie une fois sur Retour arrière pour supprimer l’espace. Tu dois obtenir la variable directement collée à la virgule. J’ai vérifié, le payload redevient valide et la commande passe.

Garde aussi en tête le second piège : ne termine pas ton JSON par la variable, sinon les trois accolades collées }}} font planter le moteur de template. Mets toujours quelque chose après, par exemple ,"off_wait_time":0}. Dans ton cas c’est déjà bon.

On corrige

C’est un bug franc de notre côté : l’éditeur ne devrait jamais laisser passer ce caractère. Le correctif est trivial, on normalise l’espace insécable au moment où le texte est enregistré. Ce sera dans une prochaine version, et d’ici là ton Retour arrière suffit.

Pour mosquitto_sub

Normal que ça ne marche pas dans le conteneur Gladys, l’outil n’y est pas installé. Trois options selon ce qui t’arrange :

  • depuis le conteneur de ton broker : docker exec -it <nom_du_conteneur_mosquitto> mosquitto_sub -t 'zigbee2mqtt/relais_03/set' -v
  • sur la machine hôte, après un sudo apt install mosquitto-clients
  • ou, plus simple si tu préfères le graphique, MQTT Explorer, qui te montre tout le trafic du broker en direct

Cela dit, tu n’en as plus besoin pour ce problème-ci.

Il reste un point non élucidé

Ton observation du post #5 — l’action MQTT fonctionne, l’action Zigbee2mqtt non, avec le même topic complet et le même broker — n’est toujours pas expliquée, et l’espace insécable n’y change rien puisque ton test était en valeurs fixes. Maintenant qu’on sait que ce champ peut contenir des caractères invisibles, est-ce que tu peux refaire un essai propre : deux actions, le payload {"state":"ON","on_time":60,"off_wait_time":10} retapé entièrement à la main dans chacune, même topic zigbee2mqtt/relais_03/set ? Si l’action Zigbee2mqtt échoue encore, on a un second bug à creuser et j’ouvrirai un ticket dessus.

Corrigé dans Gladys Assistant 4.86 :

Je viens enfin de faire de nouveaux tests et ça marche beaucoup, même en z2m, top !

Par contre il y a tout de même un espace qui vient se mettre à la fin de la variable que l’on insère :


Et cet espace vient pourrir le json envoyé à z2m qui récupère un mauvais message → z2m: Invalid message 'undefined', skipping...

Il faut donc obligatoirement supprimer cet espace entre la variable et la virgule et là tout fonctionne correctement.
Je vais créer une demande d’amélioration.

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