SONOFF NsPanel : Tuto complet sous Tasmota & écrans d'origine

Salut à tous,

Contexte :

J’ai récemment reçu mes NsPanel et j’ai voulu les utiliser sous Jeedom, cependant, je n’ai rien trouvé de très précis sur comment faire tout fonctionner. Il a fallu lire un post très très long, avec pleins de pistes différentes qui s’entremêlent et ce n’était vraiment pas évident. J’ai donc décidé de faire des « petits » tutos qui reprennent toutes les informations que j’ai pu trouver à droite et à gauche, ici et sur d’autres forums pour que les prochaines personnes qui se lancent puissent avoir une version agrégée et à jour !

Vous pouvez aussi voir mon autre tuto : SONOFF NsPanel : Tuto complet sous Tasmota & NxPanel

Qu’est-ce que nous allons faire dans ce tuto ?
En partant d’un NsPanel sorti du carton nous allons :

  1. Comprendre le fonctionnement (Firmware/Driver/Nextion) (A voir sur mon autre tuto)
  2. Les prérequis (A voir sur mon autre tuto)
  3. Choisir le walpaper (mise à jour ou pas !)
  4. Flasher le NsPanel avec le firmware Tasmota (A voir sur mon autre tuto)
  5. Modifier le driver pour la température
  6. Configurer le MQTT (A voir sur mon autre tuto)
  7. Créer l’équipement dans jeedom
  8. Créer les scénarios pour le faire fonctionner
  9. Configurer l’affichage de la page menu
  10. Configurer l’affichage de la page thermostat
  11. Installer JMQTT en plus de MQTT manager (A voir sur mon autre tuto)
  12. Sources et ressources
  13. A l’aide ! (A voir sur mon autre tuto)
  14. Conclusion

Le tout agrémenté de tips !

Comme j’ai fait beaucoup de liens vers l’autre tuto, pour une meilleure lisibilité je vous conseille de rester sur ce menu et de suivre les étapes une par une en les ouvrant dans un autre onglet :slight_smile:

Voila à quoi va ressembler votre NsPanel si vous suivez ce tuto :







3/ Choisir le walpaper (mise à jour ou pas !)

Quand Sonoff fait des mises à jour de son NsPanel il en profite pour modifier les fonds d’écran !
Nous pouvons donc jouer avec la version pour choisir le fond d’écran que nous préférons !

Quand j’ai reçu mes NsPanel, ils étaient en version 1.3.0 soit avec ce fond d’écran :

Mais si vous les connectez à eWeLink (l’appli officielle) vous pouvez les mettre à jour en version 1.4.0 pour avoir le choix entre ces deux fonds d’écran :

L’ancienne version 1.0.1 proposait ce fond d’écran, mais le panneau menu ne fonctionne pas, donc pas d’intérêt de l’utiliser…

image

Comment changer de version ?

Si vous souhaitez les fonds d’écran de la 1.4.0, c’est très simple il suffit de mettre à jour votre NsPanel via eWeLink, pour ce faire suivez cette procédure :

ATTENTION : En version 1.4.0 pensez à bien choisir le fond d’écran via eWeLink avant de flasher Tasmota !
Après vous ne pourrez plus le faire !! Il faudra reflasher le Firmware d’origine et synchroniser de nouveau avec eWeLink pour changer de fond d’écran !

Si par contre vous souhaitez un fond d’écran dune version antérieure, il va falloir vous procurer le bon Firmware Nextion et le flasher soit même !
Mais sur le web, impossible de trouver autre chose que le firmware 1.0.1…

Au cas où, je vous met tout de même la procédure ici.

Voici comment faire pour downgrader le Firmware Nextion :

  1. Commencez par installer le driver de NxPanel qui va vous permettre de flasher le Nextion, il est récupérable à cette adresse : https://github.com/peepshow-21/ns-flash/raw/master/berry/nxpanel.be et renommez le en autoexec.be
  2. Rendez vous dans l’interface web de tasmota dans Consoles > Manage File system
  3. Envoyez le fichier autoexec.be en cliquant sur le bouton Start upload
  4. Redemarrez ensuite votre NsPanel via le Main Menu et Restart
    Votre NsPanel redémarre.
  5. Retournez dans Consoles > Console puis tapez flashnextion [URLduFirmware]
    Il faut bien sûr remplacer [URLduFirmware] par le chemin de votre fichier ! Attention, il faut que l’URL soit en http ! et non en https ! Le driver ne supporte pas les URL sécurisées !
    Votre NsPanel redémarre ensuite avec le nouveau Firmware !

La version 1.0.1 est disponible ici : https://cdn.discordapp.com/attachments/538814618106331139/925351768852951050/ns-panel.tft
Vous devez trouver comment l’héberger en http !
Mais pour rappel, la page menu ne fonctionne pas avec… (Ou alors je n’ai pas réussi à la faire fonctionner…)

ATTENTION, si vous voulez un jour restaurer votre NsPanel avec le firmware d’origine pour le faire fonctionner sous eWeLink, il faut absolument que vous restauriez le backup qui vient de votre propre NsPanel. Il contient son identifiant unique, et si vous restaurez le backup d’un autre NsPanel, vous aurez une erreur 30014 au moment de la synchro wifi. Impossible alors de le faire refonctionner sous eWeLink…

5/ Modifier le driver pour la température

Avant de pousser le driver à Tasmota pour piloter l’écran, nous allons le modifier afin qu’il n’utilise pas la température de la sonde intégrée (qui n’est pas du tout fiable)
Il faudra de ce fait envoyer via Jeedom la température de la pièce.

  1. Commencez par télécharger le fichier driver : https://raw.githubusercontent.com/blakadder/nspanel/main/nspanel.be
  2. A faire uniquement si vous ne voulez pas utiliser la sonde interne : Editez le avec un bloc note et ajouter un # en début de ligne 411 pour avoir ceci :
    # tasmota.add_rule("Tele#ANALOG#Temperature1", set_temp) # rule to run set_temp on teleperiod
  3. Renommez le en autoexec.be
  4. Rendez vous dans l’interface web de tasmota dans Consoles > Manage File system
  5. Envoyez le fichier autoexec.be en cliquant sur le bouton Start upload
  6. Redemarrez ensuite votre NsPanel via le Main Menu et Restart

Voilà, Tasmota devrait être en mesure de dialoguer avec l’écran et vous devriez avoir l’heure qui se met à jour !

S’il n’est pas à la bonne heure, vous pouvez vous rendre sur l’interface Tasmota, et ensuite rendez-vous dans la console ( Consoles > Console ) pour taper la commande suivante :
timezone +2

Vous voilà à l’heure Française !

7/ Créer l’équipement dans jeedom

L’équipement doit obligatoirement être créé dans JMQTT, et pas dans MQTT manager qui ne gère pas bien le JSON.

Si vous avez bien paramétré le MQTT dans Tasmota comme je vous l’ai indiqué, vous devez renseigner le topic de la sorte :

Pour la commande de disponibilité pour le moment vous ne pourrez pas la remplir, nous verrons plus loin dans le tuto comment la créer.

Sauvegardez ensuite l’équipement pour valider.

Allez ensuite dans commandes, c’est là que les choses intéressantes arrivent !!

Nous allons avoir deux types de commandes, (comme toujours :wink: )
Les commandes info, et les commandes action.

Voici comment fonctionne une commande info

nspanel/chambreparents/INFO2 est le nœud sur lequel aller chercher l’information.
[Info2][IPAddress] est le chemin JSON où se trouve l’information.

Je vous conseille d’utiliser MQTT explorer pour fouiller par vous même et trouver toutes les informations qui vous semblerons utiles.

Astuce : Pensez à aller dans les paramètres de la commande pour activer la répétition des valeurs identiques pour les infos qui déclenches des actions. Cela permet par exemple si vous cliquez deux fois de suite sur le même bouton, que l’action associée s’active deux fois !!
Ca sera important par la suite pour la commande info qui lance les scénarios

image

Voici comment fonctionne une commande action

C’est un peu le même principe :

nspanel/salledejeux/cmnd/NSPsend est le nœud sur lequel poster la commande
{"HMI_dimOpen":#select#} est le message en JSON à déposer sur le broker (C’est ça qu’on ne peut pas faire avec MQTT Manager)

La case Pub Auto sur les commandes actions permet de publier le JSON sur le broker automatiquement sans lancer l’action dès qu’une commande info présente dans le payload a changé de valeur. Utile pour mettre à jour automatiquement les température par exemple. Pour ma part j’utilise plutôt un scenario pour tout centraliser.
image

Cette commande permet par exemple d’indiquer si le NsPanel doit s’éteindre complètement en cas d’inactivité (par défaut) ou si au contraire, il doit rester allumer en baissant la luminosité.
Vous pouvez donc via des scénarios changer cela en fonction des horaires !
Attention ! Si le NsPanel est en mode éteint, et que l’écran est déjà éteint, quand vous le repassez en mode assombri, l’écran ne s’allumera pas tout seul, il faudra le toucher pour qu’il se rallume et ensuite avec un temps d’inactivité, il repassera en assombri.

Voici les commandes qui permettent de récupérer l’état des deux boutons/relais physiques, et de les commander :slight_smile:

Vous pouvez voir que les commandes info pour l’état sont en binaire. Pourtant le JSON renvoi les valeurs ON ou OFF. Mais l’astuce réside dans les paramètres de la commande, il faut aller indiquer une valeur calculée de cette manière pour la transformer en binaire :
#value# == 'ON'

C’est comme cela que nous allons faire aussi avec la commande Online qui est à régler dans le panneau « informations »

#value# == "Online"
Et le topic MQTT de la commande info est :
nspanel/chambreparents/LWT

Régler la page d’accueil

Pour la météo, il reste à configurer la localisation et la forcer sur votre ville pour obtenir les bonnes informations.

Pour vérifier la ville que vous pensez utiliser, passer par le le site wttr.in. Pour Paris par exemple, tapez l’url https://wttr.in/paris et si vous avez bien le bon résultat, c’est que la ville est compatible !

Utilisez donc le topic nspanel/salledejeux/cmnd/NSPLocation pour pousser la ville :

S’il n’est pas à la bonne heure, vous pouvez vous rendre sur l’interface Tasmota, et ensuite rendez-vous dans la console ( Consoles > Console ) pour taper la commande suivante :
timezone +2

Vous voilà à l’heure Française !

Enfin, pour la température et l’humidité, c’est cette commande qu’il faut utiliser :

nspanel/salledejeux/cmnd/NSPsend

{"temperature":#[Salle de jeux][Capteur Température Hygrométrie 3][Température]#,"humidity":#[Salle de jeux][Capteur Température Hygrométrie 3][Humidité]#}

Voila comment paramétrer la page d’accueil

Documentation des commandes disponibles
Si vous avez besoin de vous référer à la documentation pour connaitre le détail des JSON possibles à utiliser, voici la documentation : NSPanel Protocol | nspanel

Toutes les commandes sont à envoyer sur le topic : nspanel/salledejeux/cmnd/NSPsend

Enfin, voici le template JMQTT afin que vous puissiez créer l’équipement directement avec toutes mes commandes, cela vous donnera un bon exemple :
NsPanel.json.txt (36,1 Ko)

8/ Créer les scénarios pour le faire fonctionner

Pour faire fonctionner votre NsPanel, il va vous falloir plusieurs scénarios :

  1. Un scénario d’init : A lancer lorsque le NsPanel s’allume, il va permettre de lui envoyer votre config par défaut, de paramétrer votre écran menu et votre écran de thermostat
  2. Un scénario d’update : A lancer lorsque que vous souhaitez envoyer un état ou un changement à votre NsPanel. Vous pouvez le faire aussi dans les paramètre avancé de toutes les commandes de équipements qui vous servent sur votre NsPanel, mais c’est plus propre et plus lisible je trouve de tout centraliser.
  3. Un scénario d’action : Il se lancera lorsque vous appuyez sur les boutons de l’interface de votre NsPanel, c’est lui qui lancera les commandes de vos équipements que vous pilotez avec votre NsPanel.
  4. Un scénario pour les boutons physiques : Il se lancera quand vous utilisez vos boutons physique, il permettra d’affecter une fonction dans jeedom à ces boutons.
  5. Un scénario pour gérer les thermostat : Il se lancera lorsque vous interagirez avec les commandes du NsPanel qui concernent le thermostat.
  6. Un scénario pour contourner certains bugs du NsPanel : Et oui, il n’envoi pas toujours du JSON propre, et il faut le nettoyer :wink:

Scénario d’init
Commençons par le début, Il faut détecter le démarrage du NsPanel.
Il est provoqué lorsque le NsPanel devient Online !

Comme j’ai plusieurs NsPanel, je commence par vérifier lequel a lancé le scénario, ensuite je lui envoie ma configuration (location, toujours allumé, et j’initialise la température)

Dans ce scénario, à la suite de ses actions, je configure la page menu et la page thermostat, mais nous verrons cela dans les deux prochains chapitre, je vous laisse pour le moment là dessus :wink:

Scénario d’update
A chaque fois qu’une information change sur votre système il faut prévenir votre NsPanel, voici comment je procède :

Je met en déclencheur toutes les informations qui doivent mettre à jour mes NsPanel, ensuite je vérifie qu’elle info à déclenché le scenario, et je met à jour les NsPanel :


Scénario pour les boutons physiques

Il y a deux boutons physiques sur le NsPanel, vous pouvez biensur brancher des équipements dessus qui s’allumeront avec les relais. Vous pouvez déclencher ces relais à distance via MQTT, et vous pouvez aussi tout simplement ne rien brancher dessus et vous servir de leur état pour lancer des actions sur votre Jeedom. C’est ce que j’ai fait, je les utilise pour piloter les volets de la pièce.

Voici comment cela fonctionne :


Vous voyez que je repasse ensuite les boutons en OFF, c’est pour faire un retour d’état et ne déclencher qu’au moment ou ils passent sur ON. J’aurai pu être moins maniaque et déclencher l’action au changement d’état quel qu’il soit, mais on aurait vu sur l’interface qu’ils sont parfois en état actifs et parfois non actifs… :slight_smile:

Attention, il y a une subtilité ici.
L’état des bouton power est ON et OFF, et pourtant j’utilise un type binaire.
Il faut aller configurer une valeur calculée (comme expliquée avant dans ce tuto)

Scénario pour contourner les bugs

Le NsPanel renvoi parfois du JSON erroné, il n’est dont pas compris par JMQTT et aucune action ne se déclenche, la seule parade que j’ai trouvé c’est de déclencher un scénario qui va scruter toutes les commandes qui passent et corriger celle que j’ai repéré comme non correctes.
Le scénario se déclenche tout simple comme cela :

La commande info est la suivante :

Et le bloc code exécuté est :


Le premier cas concerne le bouton pour passer OFF un bandeau LED
Le second cas se produit quand nous réglons la température du thermostat à 21°C
Je n’ai pas testé toutes les sortes de boutons. A vous de vérifier s’il y a d’autres bugs, et n’hésitez pas à me les indiquer pour mettre à jour le tuto !

Vous voyez ici les erreurs de JSON :
image

image

Le forum n’affiche pas le caractère spécial et je ne peux pas vous copier coller le code ici, voici donc un pastebin pour le récupérer.

Si ce JSON erroné est détecté j’appelle une commande qui publie sur le même topic le JSON corrigé :wink:
Si ce mécanisme vous parait trop lent à cause des échanges MQTT multiples, vous pouvez aussi dans le code appeler plutôt directement la commande d’action prévue…
Ce qui est étrange, c’est que pour la température, ça se produit qu’avec 21°C !! Avec les autres valeurs le JSON est correct !!
Ce bug se produit avec les 3 versions de firmwares nextion (1.0.1 comme 1.2.1 et 1.4.0)

Voici les commandes appelées pour publier le bon JSON :

Les scénarios d’actions et thermostat seront présentés dans les deux prochains chapitres !

9/ Configurer l’affichage de la page menu

Pour configurer la page menu, il faut le faire dans le scénario d’init, de cette manière elle sera prête lorsque vous y accéderez.

Il va falloir commencer par supprimer tous les boutons qui sont créés par défaut :

La commande deleteIndex est :

Ensuite il va falloir créer vos boutons, ils sont dans l’ordre et vous pouvez en mettre de 1 à 8
Voici les index :

L’icone du bouton définira sont comportement.
Dans la capture ci dessus, vous avec des boutons à 1, 2, 3 ou 4 slider horizontaux, ou verticaux.
Vous avez aussi ces boutons :


Ou encore celui là :
image

Tout est très bien expliqué dans cette documentation :

Voici les commandes que j’exécute pour avoir ce rendu :


Vous voyez qu’il y a des commandes ‹ HMI_resources › et d’autres ‹ relation ›
Les premières servent à définir le bouton et doivent être exécutés qu’une fois, les secondes servent à régler l’état du bouton (disponible ou pas, activé ou pas) et sont à exécuter à chaque changement d’état, c’est comme cela que vous allez pouvoir mettre à jour l’état du panneau. (Dans le scénario d’update)

10/ Configurer l’affichage de la page thermostat

Pour exécuter les actions que vous allez demander depuis votre NsPanel concernant votre thermostat, il va falloir créer un scénario qui sera déclencher par les commandes suivantes :

image

Et ensuite le scénario de paramètre comme cela :


En fonction de l’état, j’arrête ou j’active le chauffage.


En fonction du mode, je passe en automatique, ou manuel avec la température paramétrée dans le NsPanel.

La fonction min(24,#[Salle de jeux][NsPanel][TEMP_CONSIGNE]#) me permet de ne pas envoyer à mon thermostat une valeur supérieure à 24, si l’utilisateur choisi sur le NsPanel 26° par exemple, ça se mettra automatiquement à 24 !

Voici les autres commandes dans JMQTT :

ATCExpect0 est la température de consigne en manuel
ATCExpect1 est la température de consigne en automatique qui est affichée sur le NsPanel dans le cercle mais en lecture seule pour l’utilisateur (il doit passer en manuel avant de pouvoir modifier)

Je set les deux à la même valeur, comme cela quand l’utilisateur passe en manuel, il n’y a pas de changement de température avant qu’il ne le fasse volontairement.

13/ Sources et ressources

Pour mes recherches je me suis basé sur :

ATTENTION, si vous voulez un jour restaurer votre NsPanel avec le firmware d’origine pour le faire fonctionner sous eWeLink, il faut absolument que vous restauriez le backup qui vient de votre propre NsPanel. Il contient son identifiant unique, et si vous restaurez le backup d’un autre NsPanel, vous aurez une erreur 30014 au moment de la synchro wifi. Impossible alors de le faire refonctionner sous eWeLink…

Conclusion

Entre cette version, et la version avec le NxPanel, j’ai préféré celle-ci et je l’utilise au quotidien.
En effet, cela est plus réactif, car sur le NxPanel, il faut attendre que la page soit chargée avant d’interagir avec elle. Ce qui met un peu de latence.
Et puis j’aime bcp les nouveaux fond d’écrans, je les trouve bcp plus classes que celui du NxPanel.

Par contre le NxPanel offre plus de fonctionnalités et de personnalisation.
Il n’est par exemple pas possible de renommer les slider « outlet1 » « outlet2 » quand on a choisi un bouton multi slider… C’est dommage !
Ca pourrait être bien pour mettre plusieurs fonctions.

A refaire, je pense que je me tournerai vers les NsPanel Pro !

Réservé… :slight_smile:

Réservé… :slight_smile:

Excellent tuto, j’en ai profité pour réajuster mon NSPanel.
Par contre je bute sur la création des icones. Je fais bien un delete, j’arrive à créer mes nouvelles icones, par contre niveau feedback, ça ne va pas. J’ai beau lire/relire les commandes NSPANEL, y a un truc qui m’échappe…
Par défaut, la première icone (Index 1) est un switch, qui peux prendre 2 états.
Je veux re-créer cette même icone sauf que celle que je crée n’enregistre pas son état.
Quand je clic dessus, il passe à ON, puis 2sec retombe à OFF.
Il y a un moyen de maintenir la position à ON et à OFF ?

Hello @tibo.percin c’est normal, il passe à ON 2s pour dire qu’il a bien envoyé la commande.
Et ensuite, coté jeedom tu dois envoyé l’état réel de ton équipement, s’il est bien passé à ON tu dois envoyer {"relation":[{"ctype":"device","id":"1","params":{"switch":"on"}}]}

Bonjour,

En version de base en 1.4.0, j ai mis un fond d ecran, qui est ma sauvegarde aussi.

J ai flashé tasmota, mais le fond ecran n est pas celui du 1.4.0 (alors que je l avais bien mis)
j’ai remis la version 1.4.0, mais je n’ai plus le panel de sonoff, mais tjrs nxpanel bien que le soft soit bien charge en 1.4.0 car avec l’app smartphone ewelink je pilote bien les sonoff, mais le cran affiche celui de nxpanel

je suis un peu perdu qui pourrait m’expliquer les manipulations ?

merci

Salut @bouboun59

Il y a deux tutos, celui-ci et celui avec NxPanel => SONOFF NsPanel : Tuto complet sous Tasmota & NxPanel

Si tu mets NxPanel c’est normal que tu n’as pas le fond d’écran du NsPanel.

Relis ce passage avec attention pour bien comprendre ce qu’est NxPanel : SONOFF NsPanel : Tuto complet sous Tasmota & NxPanel - #2 par dcat

Merci, c’est vrai que je me suis melangé les pinceaux.
Par contre quand je restaure mon sonoff en 1.4.0, je n arrive plus a remettre l’ecran d’origine, il garde le nxpanel.
Je ne sais pas comment remettre celui d’orgine au niveau du panel.
Pourtant je communique bien avec le Ewelink de mon smartphone

Salut @bouboun59

Il faudrait que tu restaures le tft d’origine dans le Nextion.
Tu peux trouver la version 1.0.1 dans les sources : SONOFF NsPanel : Tuto complet sous Tasmota & écrans d'origine - #8 par dcat

Je ne l’ai jamais fait, je ne sais pas si ça fonctionne.
Pour le restaurer il faut que tu remettes Tasmota avec le drivers du NxPanel pour pouvoir flasher des tft, et au lieu de lui donner le tft du NxPanel, tu lui donnes celui qu’on retrouve sur le web.
Attention, tu dois l’héberger sur du http (et pas https)
Ensuite tu pourras remettre ton backup eWeLink et croiser les doigts pour que ça fonctionne !

Bon courage !

merci je vais attendre une mise à jour sup à 1.4 on verra

merci de ton aide et tes explications

Bonjour,
J’ai créé une fonction sync et refresh sur la base des nombreux posts, une sorte de Groovy Script OpenHab adapté à jeedom.

Je pense que j’ai zappé un truc au niveau des fonctionnalités BUTTON / NEXT.
En effet, si j’ajoute le status lorsqu’il y a la notion de next, l’affichage n’est pas bon. Si en revanche, j’enlève le « status », alors tout s’affiche correctement. J’ai corrigé ma fonction et maintenant, je butte sur le « NEXT », qui fait planter le NSPANEL (reboot au lieu de lier à l’écran suivant via un refresh ou sync).

Quelqu’un à une idée de là ou ça plante ? J’ai certainement zappé quelque chose dans les doc de ce tuto. J’ai placé des dumps un peu partout, mais là, je vois pas.

Merci pour votre aide.

Voici un premier exemple de script que j’ai :
/…/…/data/php/panel.user.function.class.php

global $Name_scenario ;
$Name_scenario = $scenario->getName();
$scenario->setLog("$Name_scenario - Step 01 - Variable");

/*
PANEL : Format :
"" - Power - Min - Max
Ex : Dimmer : 		{"refresh":{"pid":17,"name":"Lounge Fan", "power":ON, "min":1, "max":4, "icon":13,"dimmer":3}}
Ex : Dimmer : 		{"refresh":{"pid":18,"name":"Lounge Light", "power":ON, "dimmer":30}}
Ex : Dimmer color : {"refresh":{"pid":16,"name":"Bedroom mood light", "power":ON, "hsbcolor":"10,100,50"}} 
Ex : Thermostat : 	{"refresh":{"pid":15,"name":"Cabin","therm":{"set":14,"temp":0,"heat":1,"state":0"}}
Ex : Media : 		{"refresh":{"pid":20,"name":"Music Room","artist":"New Order","album":"Power, Corruption & Lies","track":"Blue Monday","volume":70}}
Ex : Status : 		{"refresh":{"pid":19,"name":"System Status", "status":[{"id":1,"text":"Gate":,"value":"Open","color":2},...]}}
					color=2 : vert - color=3 : red
*/

/* Definition of Panel types (page types) for available layouts * 
NOTE! This is not the same as the PanelID (id) parameter but instead one of * 15 predefined “panel designs */
$PAGE_HOME = 1 ;		$PAGE_2_BUTTON = 2 ;		$PAGE_3_BUTTON = 3 ;		$PAGE_4_BUTTON = 4 ;
$PAGE_6_BUTTON = 5 ;	$PAGE_8_BUTTON = 6 ;		$PAGE_DIMMER = 7 ;			$PAGE_DIMMER_COLOR = 8 ;
$PAGE_THERMOSTAT = 9 ;	$PAGE_ALERT_1 = 10 ;		$PAGE_ALERT_2 = 11 ;		$PAGE_ALARM = 12 ;
$PAGE_MEDIA = 13 ;		$PAGE_PLAYLIST = 14 ;		$PAGE_STATUS = 15 ;

/* Definition of button types */
$BUTTON_UNUSED = 0 ;	$BUTTON_TOGGLE = 1 ;		$BUTTON_PUSH = 2 ;		
$BUTTON_DIMMER = 3 ;	$BUTTON_DIMMER_COLOR = 4  ;	$BUTTON_PAGE = 10 ;

/* Definition of icon types */
$ICON_BLANK = 0 ;	$ICON_BULB = 1 ;	$ICON_DIMMER = 2 ;		$ICON_DIMMER_COLOR = 3; 
$ICON_VACUUM = 4 ;	$ICON_BED = 5  ;	$ICON_HOUSE = 6 ;		$ICON_SOFA = 7 ;
$ICON_BELL = 8 ;	$ICON_HEAT = 9 ;	$ICON_CURTAINS = 10 ;	$ICON_MUSIC = 11 ;
$ICON_BINARY = 12 ;	$ICON_FAN = 13 ;	$ICON_SWITCH = 14 ;		$ICON_TALK = 15 ;
$ICON_INFO = 16 ;	$ICON_NONE = 0 ;

/* Status of each Button */
$P10B1 = 0; $P10B2=0;$P10B3=0;$P10B4=0;$P10B5=0;$P10B6=0;$P10B7=0;$P10B8=0;

// Chambre Parents
$P11B1 = cmd::byString('#[Chambre Parents][Lampe parent bf26c59a890374c427d0t8][Etat]#')->execCmd(null, false, false);  // Plafond
$P11B2 = cmd::byString('#[Chambre Parents][Volet Parents][Position]#')->execCmd(null, false, false);  // Volet
$P11B3 = cmd::byString('#[Chambre Parents][Vanne MOES Parents][Consigne]#')->execCmd(null, false, false);  // Thermostat
$P11B4 = cmd::byString('#[Chambre Parents][INNR Parents][Etat]#')->execCmd(null, false, false);  // Prise INNR
$P11B5 = cmd::byString('#[Chambre Parents][Diffuseur Maman bf6f1f0cb65c373cc3zvai][Status]#')->execCmd(null, false, false);  // Diffuseur
$P11B6 = '0' ; // cmd::byString('#[Chambre Parents][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Infos

// Chambre Lysandre
$P12B1 = cmd::byString('#[Chambre Lysandre][Lampe Lysandre bfaffc4e853676bd34bd21][Etat]#')->execCmd(null, false, false);  // Plafond
$P12B2 = cmd::byString('#[Chambre Lysandre][Volet Lysandre][Position]#')->execCmd(null, false, false);  // Volet
$P12B3 = cmd::byString('#[Chambre Lysandre][Vanne MOES Lysandre][Consigne]#')->execCmd(null, false, false);  // Thermostat
$P12B4 = 0 ; //cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Infos
// Douche
$P13B1 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Plafond
$P13B2 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Evier
$P13B3 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Thermostat
$P13B4 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // HP
$P13B5 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // EAU
$P13B6 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Infos
// Salon
$P14B1 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Lampes
$P14B2 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Volet
$P14B3 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Thermostat
$P14B4 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Ampli
$P14B5 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Infos
// Cuisine
$P15B1 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Plafond
$P15B2 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Evier
$P15B3 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P15B4 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P15B5 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
// Salle à manger
$P16B1 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Lampes
$P16B2 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P16B3 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P16B4 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P16B5 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
// Terrasse
$P17B1 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Lampes
$P17B2 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // Store
$P17B3 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P17B4 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P17B5 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
// Autre
$P18B1 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P18B2 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P18B3 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P18B4 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 
$P18B5 = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);  // 


/* Definition of PANEL AND BUTTONS 
'XX' => array(		// The panel number to define
		'0' =>		// The Panel description, used for refresh function
        			// Name is the title of the panel, which appear on the top of the panel
                    // Format specify the format of panel, see $PAGE_XXX variable definition, and choose the corresponding panel to design
                    // NB_BUTTON : This number correspond to the number of button in panel PAGE_X_BUTTON
        '1' => 		// 1-8  Description of each button to design in the panel 
*/
global $PANELS;
$PANELS = array (
    10 => array (
        // ... ajoutez les autres boutons B101 à B108 pour le Panel 1 de 8 Boutons
        0 => array('NAME' => 'Etage 1', 'FORMAT' => $PAGE_8_BUTTON , 'NB_BUTTON' => 8 ),
        1 => array( 'ID' => 1, 'Name' => 'Parents' , 'Type' => $BUTTON_PAGE , 'Next' => 11 , 'Icon' => $ICON_BED),
        2 => array( 'ID' => 2, 'Name' => 'Lysandre' , 'Type' => $BUTTON_PAGE , 'Next' => 12 , 'Icon' => $ICON_BED),
        3 => array( 'ID' => 3, 'Name' => 'Douche' , 'Type' => $BUTTON_PAGE , 'Next' => 13 , 'Icon' => $ICON_FAN),
        4 => array( 'ID' => 4, 'Name' => 'Salon' , 'Type' => $BUTTON_PAGE , 'Next' => 14 , 'Icon' => $ICON_SOFA),
        5 => array( 'ID' => 5, 'Name' => 'Cuisine' , 'Type' => $BUTTON_PAGE , 'Next' => 15 , 'Icon' => $ICON_BELL),
        6 => array( 'ID' => 6, 'Name' => 'SAM' , 'Type' => $BUTTON_PAGE , 'Next' => 16 , 'Icon' => $ICON_SOFA),
        7 => array( 'ID' => 7, 'Name' => 'Terrasse' , 'Type' => $BUTTON_PAGE , 'Next' => 17 , 'Icon' => $ICON_SOFA),
        8 => array( 'ID' => 8, 'Name' => 'Autre' , 'Type' => $BUTTON_PAGE , 'Next' => 18 , 'Icon' => $ICON_SOFA)
    ),
    11 => array (
        // ... ajoutez les boutons pour PANEL 11 ici
        0 => array('NAME' => 'Chambre Parents', 	'FORMAT' => $PAGE_8_BUTTON , 'NB_BUTTON' => 8),
        1 => array('ID'=>1 , 'Name' => 'Plafond' , 	'Type' => $BUTTON_DIMMER , 		'Next' => 0 , 'Icon' => $ICON_BULB ),
        2 => array('ID'=>2 , 'Name' => 'Volet' , 		'Type' => $BUTTON_PAGE , 		'Next' => 0 , 'Icon' => $ICON_CURTAINS ),
        3 => array('ID'=>3 , 'Name' => 'Thermostat' , 'Type' => $BUTTON_PAGE , 		'Next' => 0 , 'Icon' => $ICON_HEAT ),
        4 => array('ID'=>4 , 'Name' => 'Prise INNR' , 'Type' => $BUTTON_PUSH , 		'Next' => 0 , 'Icon' => $ICON_BULB ),
        5 => array('ID'=>5 , 'Name' => 'Diffuseur' , 	'Type' => $BUTTON_DIMMER_COLOR ,'Next' => 0 , 'Icon' => $ICON_DIMMER_COLOR ),
        6 => array('ID'=>6 , 'Name' => 'Infos' , 		'Type' => $BUTTON_PAGE , 		'Next' => 0 , 'Icon' => $ICON_INFO )
     ),
    12 => array (
        // ... ajoutez les boutons pour PANEL 12 ici
        0 => array('NAME' => 'Chambre Lysandre', 	'FORMAT' => $PAGE_4_BUTTON , 'NB_BUTTON' => 4),
		1 => array('ID'=>1 , 'Name' => 'Plafond' , 	'Type' => $BUTTON_DIMMER , 	'Next' => 0 , 'Status' => $P12B1 , 'Icon' => $ICON_BULB ),
		2 => array('ID'=>2 , 'Name' => 'Volet' , 		'Type' => $BUTTON_PAGE , 	'Next' => 0 , 'Status' => $P12B2 , 'Icon' => $ICON_CURTAINS ),
		3 => array('ID'=>3 , 'Name' => 'Thermostat' , 'Type' => $BUTTON_PAGE , 	'Next' => 0 , 'Status' => $P12B3 , 'Icon' => $ICON_HEAT ),
		4 => array('ID'=>4 , 'Name' => 'Infos' , 		'Type' => $BUTTON_PAGE , 	'Next' => 0 , 'Status' => $P12B4 , 'Icon' => $ICON_INFO  )
	),
//[...] j'ai coupé le code des autres définitions
    18 => array (
        // ... ajoutez les boutons pour PANEL 18 ici
		0 => array('NAME' => 'Autres', 'FORMAT' => $PAGE_8_BUTTON , 'NB_BUTTON' => 8),
		1 => array('ID'=>1 , 'Name' => 'Test1' , 'Type' => $BUTTON_PUSH , 'Next' => 0 , 'Status' => $P18B1 , 'Icon' => $ICON_BULB ),
		2 => array('ID'=>2 , 'Name' => 'Test2' , 'Type' => $BUTTON_PUSH , 'Next' => 0 , 'Status' => $P18B2 , 'Icon' => $ICON_BULB ),
		3 => array('ID'=>3 , 'Name' => 'Test3' , 'Type' => $BUTTON_PAGE , 'Next' => 9 , 'Status' => $P18B3 , 'Icon' => $ICON_HEAT ),
		4 => array('ID'=>4 , 'Name' => 'Test4' , 'Type' => $BUTTON_PAGE , 'Next' => 0 , 'Status' => $P18B4 , 'Icon' => $ICON_MUSIC),
		5 => array('ID'=>5 , 'Name' => 'Test5' , 'Type' => $BUTTON_PAGE , 'Next' => 0 , 'Status' => $P18B5 , 'Icon' => $ICON_INFO)
    )
);


$scenario->setLog("$Name_scenario - Step 02 - GetValue");

// Les commandes ne doivent pas être exécutées à ce stade, elles doivent seulement être récupérées
$button_pid = cmd::byString('#[Technique][NSPANNEL01][button_pid]#')->execCmd(null, false, false);
$button_bid = cmd::byString('#[Technique][NSPANNEL01][button_bid]#')->execCmd(null, false, false);
$page_pid = cmd::byString('#[Technique][NSPANNEL01][page_pid]#')->execCmd(null, false, false);
$page_type = cmd::byString('#[Technique][NSPANNEL01][page_type]#')->execCmd(null, false, false);
$button_next = cmd::byString('#[Technique][NSPANNEL01][page_next]#')->execCmd(null, false, false);
$button_result = cmd::byString('#[Technique][NSPANNEL01][button_result]#')->execCmd(null, false, false);


function sync_panel ($PANEL_ID) {
  global $PANELS;
  // Construct JSON for SYNC_PANEL
  $nb_button = $PANELS[$PANEL_ID][0]["NB_BUTTON"];
  $SYNC_PAGE  = '{"sync":{"pid":'.$PANEL_ID.',"buttons":[';
  for ($i = 1 ; $i < ($nb_button + 1) ; $i++) {
    $BID = $PANELS[$PANEL_ID][$i]['ID']; $B_STATUS = $PANELS[$PANEL_ID][$i]['Status'] ;
    if ($i == 1) {
      $SYNC_PAGE .=  '{"bid":' . $BID . ',"state":' . $B_STATUS . '}';  
    } else  { 
      $SYNC_PAGE .=  ',{"bid":' . $BID . ',"state":' . $B_STATUS . '}'; 
    }
    
  }
  $SYNC_PAGE .= ']}}';
  // Send Panel design
      $SYNC= json_encode($SYNC_PAGE,JSON_FORCE_OBJECT);
  
  $cmd=cmd::byString('#[Technique][NSPANNEL01][CustomCMD]#');
  log::add('scenario_execution', 'info', 'PANEL_SYNC PANEL_ID ' . $PANEL_ID );
  $cmd->execCmd($options=array('title'=>'SYNC_PANEL', 'message'=> $SYNC), $cache=0);
      echo "------- $Name_scenario ----------  SYNC_PANEL $PANEL_ID \n";
      //var_dump($cmd) ;
      var_dump($SYNC) ;

  
};

function sync_button ($PANEL_ID, $PANEL_BUTTON) {
  global $PANELS;
  // Construct JSON for SYNC_BUTTON
  //$nb_button = $PANELS[$PANEL_ID][0]["NB_BUTTON"];
  $SYNC_PAGE  = '{"sync":{"pid":'.$PANEL_ID.',"buttons":[';
  $BID = $PANELS[$PANEL_ID][$PANEL_BUTTON]['ID']; $B_STATUS = $PANELS[$PANEL_ID][$PANEL_BUTTON]['Status'] ;
  $SYNC_PAGE .=  '{"bid":' . $BID . ',"state":' . $B_STATUS . '}';
  $SYNC_PAGE .= ']}}';
  // Send Panel design
  $SYNC= json_encode ($SYNC_PAGE,JSON_FORCE_OBJECT);
  
  $cmd=cmd::byString('#[Technique][NSPANNEL01][CustomCMD]#');
  log::add('scenario_execution', 'info', 'SYNC_BUTTON - PANEL_ID ' . $PANEL_ID . 'PANEL_BUTTON ' . $PANEL_BUTTON );
  $cmd->execCmd($options=array('title' => "SYNC_BUTTON", 'message'=> $SYNC), $cache=0);
      echo "------- $Name_scenario ----------  SYNC_BUTTON $PANEL_ID , $PANEL_BUTTON \n";
      // var_dump($cmd) ;
      var_dump($SYNC) ;
};

function refresh_panel ($PANEL_ID){ 
  global $PANELS;
  // Construct JSON for REFRESH_PANEL
  log::add('scenario_execution', 'info', "$Name_scenario - Step 3 - PANEL $PANEL_ID - construction");
    // Construct JSON_PANEL
    //if (isset($PANELS[$PANEL_ID][0]['NB_BUTTON'])) {
  	$nb_button = $PANELS[$PANEL_ID][0]['NB_BUTTON']; 
  	$PANEL_NAME = $PANELS[$PANEL_ID][0]['NAME'];
	$PANEL_FORMAT = $PANELS[$PANEL_ID][0]['FORMAT'];

  	$PANEL = '{"refresh":{"pid":' .$PANEL_ID. ' ,"name":"' .$PANEL_NAME. '","format":' . $PANEL_FORMAT . ',buttons:[' ;
  	//for ($i = 1 ; $i < $nb_of_button ; $i++) {
  	for ($i = 1 ; $i < $nb_button ; $i++) {
      $BID=$PANELS[$PANEL_ID][$i]['ID'] ;
      $BNAME=$PANELS[$PANEL_ID][$i]['Name'];
      $BTYPE=$PANELS[$PANEL_ID][$i]['Type'];
      $BNEXT=$PANELS[$PANEL_ID][$i]['Next'];
      $BSTATUS=$PANELS[$PANEL_ID][$i]['Status'];
      $BICON=$PANELS[$PANEL_ID][$i]['Icon'];
      // log::add('scenario_execution', 'info', $PANELS[$PANEL_ID][$i]['Name']  );
      if ($i == 1)  {
      $PANEL .=  '{"bid":'. $BID .',"label":"'. $BNAME .'","type":'. $BTYPE .',"next":'. $BNEXT .',"state":'. $BSTATUS .',"icon":'. $BICON .'}';
      } else {
      $PANEL .=  ',{"bid":'. $BID .',"label":"'. $BNAME .'","type":'. $BTYPE .',"next":'. $BNEXT .',"state":'. $BSTATUS .',"icon":'. $BICON .'}';
      }
          
    }
    $PANEL .= ']}}';
	$JSON_PANEL=json_encode($PANEL, JSON_FORCE_OBJECT);
    // Send Panel design
    $cmd=cmd::byString('#[Technique][NSPANNEL01][CustomCMD]#');
  	// log::add('scenario_execution', 'info', 'PANEL_PAGE_' . $PANELS[$PANEL_ID] . ' PANDEL_NAME ' . $PANELS[$PANEL_ID][0][NAME] );

    $cmd->execCmd($options=array('title'=>'Titre', 'message'=> "$JSON_PANEL"), $cache=0);
      echo "------- $Name_scenario ---------- refresh_panel $PANEL_ID \n";
      // var_dump($cmd) ;
      var_dump($JSON_PANEL) ;
};

Lorsque j’ai un refresh ou sync demandé, j’appel donc la fonction souhaitée dans un scénario :

require_once dirname(__FILE__) . '/../../data/php/panel.user.function.class.php';
// Construct JSON_PANEL
echo $page_pid ;
refresh_panel($page_pid);

ou

require_once dirname(__FILE__) . '/../../data/php/panel.user.function.class.php';
// Construct JSON_PANEL
echo $page_pid ;
sync_panel($page_pid);