Gladys 4 - Développement du service Arduino

@billona
Tu peux ajouter (si c’est pas trop complexe) une LED test pour allumer une PIN? , ca permet de savoir si c’est l’arduino qui BUG ou que le code n’est pas recu par le device.
Function: “LED” ON
STATE “ON”/“OFF”

@benPi tu peux faire ça facilement avec l’émission IR en remplçant la LED IR en LED classique. Après tu peux augmenter le nombre d’impulsions pour mieux visualiser le résultat :wink:

1 « J'aime »

Bonjour à tous !

Petit point sur l’avancement du service :

1. La partie émission

J’ai travaillé un peu dessus afin que les émissions IR et 433 MHz soient compatibles avec les fonctionnalités “interrupteur” et “lumière”. Grâce à ça, on peut maintenant définir dans le device des codes ON/OFF qui seront envoyés lorsque le device changera de valeur :smile:

2. La partie réception

Alors… Je dois avouer que cette partie m’a pas mal pris la tête :sweat_smile: Mais après un peu de boulot je suis enfin parvenu à quelque chose “d’à peu près correct” dans un premier temps ! J’ai modifié la création des devices Arduino afin qu’ils soient poll toutes les minutes, ce qui permet de recevoir les messages de la carte. Par la suite, je liste les devices rattachés à cette carte, et j’affecte les valeurs dans les devices qui ont les fonctions respectives.

3. Interface

Concrètement, voilà à quoi ressemble l’interface pour l’instant :

Des devices dans la page des devices :

Mon dashboard :

4. Le code Arduino

J’ai donc ajouté l’implémentation des DHT11, et d’une fonction de contrôle de servomoteurs. Il me reste encore du travail pour permettre le paramétrage dans Gladys, mais la base est là :slightly_smiling_face:

Pour info, le dev étant encore loin d’être terminé, il est certain que le code va beaucoup évoluer dans les prochains jours. Mon objectif maintenant est d’optimiser la base qui existe grâce aux retours d’utilisation, et d’implémenter le plus de fonctionnalités possibles dans le code Arduino :wink:

Encore merci aux testeurs de prendre de votre temps pour m’envoyer des retours d’expérience, ça m’aide énormément et ça fait évoluer le service dans le bon sens :smile:

2 « J'aime »

@billona J’ai lu tout le thread, beau boulot ! C’est vraiment très propre, et je vois que tu améliore bien le service suite aux retours donnés, c’est cool :clap:

Mmm c’est à dire ? Tu poll quoi ?

Au niveau du dashboard, pour le « RX », il faudrait laisser à l’utilisateur le choix de le mettre soit en category « MOTION_SENSOR », soit en category « OPENING_SENSOR », cela donnera un affichage custom qui affichera uniquement le temps relatif depuis le dernier mouvement/la dernière ouverture de porte.

Si ça t’intéresse de voir comment fonctionne l’affichage de cette box, ça se passe ici =>

https://github.com/GladysAssistant/Gladys/blob/master/front/src/components/boxs/device-in-room/device-features/SensorDeviceFeature.jsx#L52

Petite question en plus: qu’est ce qui te manque pour qu’on puisse publier une première version du service?

Je pense qu’il vaut mieux publier une première version, même si tous les périphériques que tu comptes gérer ne sont pas géré, et ensuite itérer sur le code. Comme ça je pourrais aussi te faire une première code review de ta PR :slight_smile:

Merci @pierre-gilles ça fait plaisir de lire ça :smile:

J’utilise poll pour faire un parser.on(‹ data ›, async (data) => « … »); au niveau de la carte Arduino :

https://github.com/billonalex/Gladys/blob/master/server/services/arduino/lib/poll.js

A voir si c’est la solution idéale, ou si un simple script d’initialisation de la carte ne suffit pas… Mais ça me paraissait le mieux étant donné qu’à une certaine fréquence, on s’assure comme ça d’avoir toujours le port ouvert en ‹ data ›.

Oui ça me paraît plus intéressant comme idée, je vais taffer ça ! Après je ne te cache pas que la manière dont je l’ai présenté sur le dashboard était surtout de pouvoir faire dire au device « Je suis là ! Regardez, j’ai capté une valeur » :stuck_out_tongue:

Et bien si je me base sur tes commentaires j’ai ça à faire, et après effectivement on peut envisager de publier une première version :smile:

Après je ne te cache pas que le code n’est pas entièrement optimisé donc il y aura à coup sûr encore du taff derrière, mais si on s’en tient à tout ce qui a déjà été fait alors l’API est en place. Donc à toi de me dire ce qui est mieux :wink:

Oh et pourrais-tu également m’aiguiller sur la démarche que j’ai à faire pour la PR ? Étant donné que c’est la première fois que je taffe sur un si gros projet je n’ai pas encore tout les bons réflexes :sweat_smile:

Oula, c’est très lourd ça :smiley:

Un simple script d’initialisation de la carte suffit !

Tu appelle une seule fois le parser.on('data', async (data) => dans une fonction « init.js » que tu appelle au start du service :slight_smile:

Si tu peux fixer l’histoire du poll + le dashboard du RX, ensuite ça me semble bon niveau feature.

Pas de soucis :slight_smile:

En haut de ta PR, tu as du remarquer qu’il y a une checklist de chose à vérifier avant de soumettre ta PR pour review :

Chaque point doit être vérifié et valide. Je te laisser les regarder un par un :slight_smile:

Les plus importants:

Les tests

Tu dois avoir écris les tests pour ton intégration, je n’ai pas l’impression que ce soit le cas pour toi ? Tu peux t’inspirer des tests des autres services ( Gladys/server/test/services at master · GladysAssistant/Gladys · GitHub )

Pour lancer les tests, tu peux faire npm test dans le dossier server. Si tu ne veux exécuter qu’un seul test, tu peux passer le test en it.only (cf la documentation de mocha : Mocha - the fun, simple, flexible JavaScript test framework )

L’objectif des tests, c’est qu’absolument tout ce que tu as codé soit couvert par des tests et qu’ainsi, dans le futur, nous soyons sûr que des régressions ne soient pas introduites par une autre PR.

Le linting

Nous utilisons eslint et prettier dans Gladys 4 afin d’assurer que le code soit formatté exactement pareil quel que soit le développeur qui code sur le projet.

Si tu as des questions, n’hésite pas ! Et si tu as vraiment du mal, on peut toujours s’appeler pour te faire une formation express :slight_smile:

Oui je me disais bien aussi :joy: Sauf qu’avant de coder le poll sur les cartes Arduino… J’avais codé un poll sur CHAQUE device qui devait recevoir quelque chose ! Encore plus lourd, mais au moins on était sûr :joy:
Du coup je regarde pour corriger ça dans la journée.

Pas de problème, je fais ça et dès que c’est bon je te tiens au courant !

Pas de problème, je m’en occupe après les quelques modifs que j’ai à faire :slightly_smiling_face:

Non je n’ai pas encore écrit de test, je vais m’en charger.
Concernant prettier, j’ai modifié VS Code pour prendre en compte les exigences souhaitées, je revérifierai fichier par fichier.

Comme précise la checklist, tu as 2 CLI qui te permettent de vérifier automatiquement le linting:

npm run eslint
npm run prettier

Dans (server et front)

Les modifs de versions faites dans package.json et package-lock.json après un npm run prettier je peux garder aussi ? Ou j’unstage ça ? Typiquement l’installation et le prettier a passé ça :

"prettier": "^1.17.1"

en ça :

"prettier": "^1.19.1"

Tu peux retirer ça ! C’est pas lié à ta PR, on fera les mises à jour des dépendances séparément :slight_smile:

C’est retiré :slightly_smiling_face:

Bon pour la function init() c’est bon ! Seul souci… Lorsque je crées des devices après avoir créé l’Arduino, il faut que le serveur restarte pour qu’ils soient pris en compte, autrement si je mets un récepteur 433 et que je ne reboote pas le serveur, aucune valeur ne lui sera assigné…

Pareil, lorsque je crée un arduino, je fais une requête API pour appeler la fonction init(), autrement il faudrer relancer le serveur pour que l’arduino soit pris en compte…

Voilà mon script :

Aurais-tu une astuce pour éviter ça ? Il suffit de régler ça, puis le dashboard du RX ça devrait aller tout seul ^^

Ah c’est un vrai problème ça ^^ Gladys n’est pas censé rebooter fréquemment. Il faut que tu ajoute un listener: quand un device est ajouté/modifié, il faut que tu ajoute l’écoute du port!

Ce que je te conseille, c’est de diviser ta fonction init en 2 fonctions:

  1. une function genre « listen » qui prend en paramètre l’arduino qu’il faut écouter
  2. la fonction init, qui va chercher la list des arduinos et qui pour chaque arduino appelle la fonction listen

Quand un arduino est ajouté, il faut que cette fonction listen soit appelée. Pareil, si un arduino est modifié (je ne sais pas si c’est possible de modifier le port?), alors la fonction doit être appelée.

Attention à bien gérer tous les états, pour pas qu’un port soit écouté 2 fois! Sinon, on risque de recevoir les messages en double/ ou ça peut poser problème.

Je pense qu’il faut que tu créé ton port + parser en enregistrant une référence vers le port et ton parser dans ton objet ArduinoManager.

Dans ton code:

 const port = new SerialPort(arduinoPath, {
        baudRate: 9600,
        lock: false,
      });

 const parser = port.pipe(new Readline({ delimiter: '\n' }));

Tes variables sont « perdus », on ne peut pas les appeler depuis l’objet ArduinoManager

Tu pourrais par exemple les stocker dans un objet:

// index.js
this.arduinosPorts = {};
this.arduinoParsers = {};

// listen.js
this.arduinosPorts[arduinoPath] = new SerialPort(arduinoPath, {
            baudRate: 9600,
            lock: false,
          });
this.arduinoParsers[arduinoPath] = = port.pipe(new Readline({ delimiter: '\n' }));

(c’est une suggestion écrite en 30seconde là comme ça, mais tu vois l’idée? :slight_smile:

Grâce à ça, tu peux tester dans ta fonction « listen » si un arduino est déjà écouté, et éviter une double écoute. En cas de deuxième écoute, tu peux fermer le serialport et ensuite le rouvrir, tu vois l’idée?

J’ai suivi ton conseil du mieux que j’ai pu !

Voilà mon script listen.js :

Concernant la vérification des ports déjà ouverts, est-ce que ça correspond à peu près à ce que tu as en tête ?

Pourquoi n’as tu pas découpé en 2 fonctions? Là de ce que je vois, à chaque fois que quelqu’un ajoute un arduino, tu fermes tous les ports, puis tu les ré-ouvre, pas très optimisé non? on risque de louper des messages

Je te conseille vraiment de séparer en 2 fonctions pour que ce soit plus simple et clair :slight_smile:

Je crois que je ne t’ai pas très bien compris alors :sweat_smile:

Dans ma tête, les 2 fonctions en question étaient init() et listen(arduino), j’ai peut-être loupé une étape là ^^

c’est bien ça ! Pardon j’avais lu ton code un peu vite, ça me semble bon en fait :smiley:

Ah tant mieux alors :smile:

En revanche, il m’arrive d’avoir par moment cette erreur dans mes logs :

2020-05-25T15:06:45+0200 <warn> listen.js:78 (listen) Unable to listen to device
2020-05-25T15:06:45+0200 <debug> listen.js:79 (listen) TypeError: Cannot read property 'device' of undefined
at listen (/home/pi/Gladys/server/services/arduino/lib/listen.js:26:31)
at arduinoList.forEach.arduino (/home/pi/Gladys/server/services/arduino/lib/init.js:41:7)
at Array.forEach (<anonymous>)
at ArduinoManager.init (/home/pi/Gladys/server/services/arduino/lib/init.js:40:17)

Par moment, quand je fais appel à this.gladys pour récupérer mes devices, si je fais un logger.warn(this.gladys); le résultat affiché est “undefined”.

Edit : de ce que j’aperçois actuellement, l’erreur apparait au premier listen du service, lorsqu’on appelle listen(arduino) dans init.js. Après modification de l’Arduino (et donc nouvelle appel de listen), ça fonctionne…

Booooon !! Après une lutte acharnée j’ai enfin trouvé l’erreur :smile:

Donc normalement au niveau de l’init et du listen on est bon, plus qu’à dégager mes scripts inutiles :wink:

En revanche @pierre-gilles j’ai travaillé sur les sensors comme tu m’as demandé. Pour le motion sensor on est bon, il m’indique bien combien de temps s’est écoulé depuis le dernier changement de valeur. En revanche, j’ai un peu de mal à voir comment me débrouiller pour l’opening sensor… Etant donné que c’est un type binary, est-ce que ça vaut vraiment le coup de stocker le code reçu dans un opening sensor ?

C’est à dire? Comme valeur, à mon avis tu stocke « 1 » quand la porte est ouverte, « 0 » quand la porte est fermée, non?

Pareil pour le détecteur de mouvement, sauf que là tu stocke « 1 » quand il y a un mouvement, c’est tout :slight_smile:

Ne stocke pas le code reçu dans la valeur si c’est ta question