Parlons de Gladys V4

Je suis d’accord.

Sinon on peut renommer lib en core

Ou alors mettre carrément ce dossier core dans api

api
----- controllers
----- core
----- middlewares
----- routes.js
----- index.js

Et alors renommer api en lib :stuck_out_tongue:

lib
----- controllers
----- core
----- middlewares
----- routes.js
----- index.js

En vrai je viens d’aller voir d’autres repos, j’ai vu du api et du lib, ça me choque pas tant que ça en fait.

Pour moi lib c’est vraiment Gladys core le code, et api c’est l’API Rest

1 « J'aime »

Ouais je pense que c’est un peu à la préférence de chacun la dessus, il y a pas vraiment de best pratice !
C’est ton projet donc c’est toi qui décide, si pour toi c’est bon comme ça alors banco ^^

J’aime bien avoir des avis extérieurs, c’est important :slight_smile: On va commencer comme ça on verra les autres retours.

Raah les index + foreign key avec Sequelize c’est le feu

1 « J'aime »

C’est méga lourd :open_mouth:

J’ai mis à jour le README du repo Gladys 4 Playground avec toutes les instructions d’installations pour tester :slight_smile:

N’hésitez pas à tester chez vous!

https://github.com/GladysAssistant/gladys-4-playground

Si vous cherchez un bon tool pour explorer des DB SQLite, je vous conseille TablePlus:

2 « J'aime »

Ou sqliteonline.com pour ceux qui n’ont pas envie d’installer un truc :stuck_out_tongue:

Franchement pas mal la structure très clair ! Petite question, je vois que tu utilise “joi”, pourquoi pas directement utiliser juste les contraintes de sequelize ?

Je pense que les contraintes de Sequelize ne seront pas suffisantes pour tout, je pense notamment pour valider les objets des scènes où des triggers qui seront des objets json nested.

Après tu as raison, ça vaudrait peut être le coup de tester de mettre cette validation supplémentaire dans une fonction dans le model :slight_smile: Bonne remarque, je vais regarder.

Question orienté sécurité, dans ton manifest j’ai vu que tu avais dis de bien choisir les librairies qui ne sont pas pété. Par contre j’ai pas l’impression que tu mets en place un système permettant de check qu’un jour ces librairies sont justement cassé !

Peu-être il serait intéressant de soit:

  • mettre en place un script automatique qui check npm audit et renvoie les information par mail
  • mettre en place un système qui le fait automatiquement (pm2 le fait dans sa version payante il me semble)

Mais c’est compliqué, le mettre en place chez tout le monde ? ou juste quelques développeurs ? Ca permet de peut-être mettre en place des patch sécurité si besoin.

De plus, il faudrait je pense obliger chaque développeur de module de forcer à avoir des “good practices”, par exemple valider chaque fois que tel ou tel model a bien été validé au préalable, que l’on ne peut mettre par exemple uniquement une adresse mail dans le champ adresse mail et pas injecter du code / xss etc.

Pour ça c’est super simple:

1/ Github fait des checks-automatique tout seul déjà!
2/ Il y a greenkeeper (https://greenkeeper.io/) qui fait ça automatiquement. Je l’utilisais dans le repo Gladys actuel mais j’avais tellement de notifications à cause de sails qui est un nid à dépendance pas à jour que je l’ai désactivé. Je l’activerais sur Gladys 4! :slight_smile: Greenkeeper fait carrément les PR tout seul dès que lib doit être mise à jour!

Complètement d’accord!

Les garde-fou sont fixé par deux choses

1/ Les technos qu’on utilise. Exemple: au niveau du front, Preact ne permet pas de faille XSS, c’est lui qui gère l’affichage des variables et tout est cleané par la lib pour que ce soit bien fait.

2/ Le linting statique. Ca c’est le rôle des règles très strictes que j’ai mis sur Eslint, qui force un coding style consistent et « aseptisé », et effectue un ensemble de check pour garantir une qualité de code. ( ça catch un nombre de bug/faille incalculable)

3/ les tests unitaires/d’intégrations bien évidemment. A nous d’écrire des tests très fin et d’avoir un code coverage élevé afin d’être sur que rien de non voulu ne soit possible. Gros plus, là on écrit les tests dès le premier jour, donc on peut facilement avoir un code coverage dans les > 90% et être robuste là dessus. Autre gros plus, mettre les modules interne dans le core permet d’écrire des tests sur les modules très facilement, et donc d’avoir les mêmes garanties sur les modules que sur le core.

Salut à tous!

J’ai bien avancé sur la modélisation, quasiment tous les modèles sont définis:

https://github.com/GladysAssistant/gladys-4-playground/tree/master/server/models

Niveau base de donnée les migrations ont bien avancées, il me manque à priori que les index et je suis bon! :slight_smile:

J’ai effectivement utilisé partiellement ta réflexion, je me suis inspiré de ton repo Github (j’avais les yeux dessus!) quand j’ai fais le travail dessus :slight_smile: J’avais perdu le lien du spreadsheet par contre, merci!

1/ Je ne comprend toujours pas pourquoi on voudrait des catégories + sous-catégorie! Si l’idée des catégories/sous-catégories c’est juste d’avoir dans l’UI un menu déroulant avec des catégories et des sous-catégories, alors oui c’est ce que je compte faire :slight_smile: Mais c’est juste de l’UI. La base de donnée est une représentation d’une modélisation, mettre 2 fields type “category” “sub-category” ici, est-ce que ça apporterait quelque chose? En terme de modélisation strict, si on sépare un attribut en deux attributs, c’est qu’on souhaite qu’une sous-catégorie puisse appartenir à plusieurs catégories. Pas forcément ce qu’on veut, non? (question ouverte, dis moi si tu as un contre exemple je suis preneur! :slight_smile: ).

2/ Une des valeurs que j’ai mis dans le manifeste, c’est que pour Gladys 4 “on ne fait pas tout, mais ce qu’on fait on le fait bien”.

Dans Gladys 3, j’ai trop souvent voulu partir dans tous les sens (pour “être sûr” de gérer le cas), il y a des catégories qui servent à rien, les types c’était open bar, on avait des “sentences” qui était câblé avec rien. Bref: j’ai eu les yeux plus gros que le ventre, et derrière pas mal de donnée statique n’était câblé avec aucun code. Et c’était con parce que l’utilisateur il voit la phrase il se dit “cool je peux gérer XX”, et en fait: non!

Ici j’ai listé la “base minimum” des catégories que je vois. Au fur et à mesure d’ajout des modules (qui seront dans le core je le rappelle), on rajoutera petit à petit des catégories. Mais tant qu’on a rien, pourquoi mettre des catégories si on ne sait pas les gérer? :slight_smile:

3/ Ce qu’on s’était dis au meetup développeur, c’était que:

DeviceFeature

  • type: sert à déterminer dans l’UI quel bouton mettre. “binary” devient un bouton on/off, “multilevel” un slider, “color” un color picker.
  • category: Sert pour Gladys à savoir ce “qu’est l’objet”, et donc de mettre une représentation adéquate dans l’UI au niveau de l’icône. Sert aussi au brain à aller chercher tous le périphériques d’une catégorie. “Allume les lumières du salon”, “Quelle température fait-il dans la salle de bain?”, “Est-ce que la porte du garage est ouverte?”, “Baisse la température dans la cuisine”, etc…

Si tu penses à des usages des sous-catégories (autre que trouver ça organisé pour le dev, c’est une modélisation là c’est pragmatique ce qu’on fait! :stuck_out_tongue: ), dis moi, c’est effectivement un sujet qu’il vaut mieux régler maintenant!

PS: mais sinon oui on peut ajouter le capteur de pression, de toute façon il devra être ajouté dès qu’on ajoutera dans Gladys 4 un module qui doit en créer :slight_smile:

Encore une fois je suis très surpris par la puissance de Sequelize!

Imaginons que je veuille aller chercher toutes les “locations” avec leur utiliateur associé.

Je fais:

j’obtiens:

Mais ce qui est intéressant c’est pas ça! C’est la manière dont Sequelize fait ça sous le capot.

Certains ORMs dans ce genre de situation font X requêtes:

  • Un SELECT * pour aller chercher toutes les lignes
  • Puis un SELECT par foreign key pour aller chercher l’attribut nested

Ici, Sequelize fait une seule requête

SELECT `t_location`.`id`, `t_location`.`user_id`, `t_location`.`latitude`, `t_location`.`longitude`, `t_location`.`altitude`, `t_location`.`accuracy`, `t_location`.`created_at`, `t_location`.`updated_at`, 
`user`.`id` AS `user.id`, `user`.`firstname` AS `user.firstname`, `user`.`lastname` AS `user.lastname`, `user`.`email` AS `user.email`, `user`.`birthdate` AS `user.birthdate`, `user`.`language` AS `user.language`, `user`.`password_hash` AS `user.password_hash`, `user`.`role` AS `user.role`, `user`.`created_at` AS `user.created_at`, `user`.`updated_at` AS `user.updated_at`
FROM `t_location` AS `t_location`
LEFT OUTER JOIN `t_user` AS `user` ON `t_location`.`user_id` = `user`.`id`;

Puis il recompose l’objet nested à l’aide de son format “table.attribut” dans les “AS”

C’est super perf!

Comme je disais plus haut c’était work in progress, je viens de pousser les index :slight_smile:

Ok je comprends l’idée!

C’est plus une question de nommage en fait + une finesse dans la granularité des types en fait.

J’aime bien l’idée des types assez fin.

Ce que j’arrive pas à comprendre chez toi, prenons ton exemple par exemple:

category: "light"
sub-category": "dimmable"

Qu’est ce que ça veut dire pour toi? Dans l’UI qu’est ce qu’on affiche?

Après j’aime bien l’idée des types plus fin que juste « binary », « multilevel » et « color ».

Si dans l’idée on prend une catégorie assez général + une sous catégorie (à voir pour le nommage, je trouve que nommer ça « type » est plus cohérent), que dis-tu de ce schéma ? (je viens de le dessiner)

(désolé si c’est dur à lire, de gauche à droite « light », « sensor », « switch », « fan », « door lock »)

Ce qu’il faut juste faire après, c’est que pour chaque couple (categorie/type) on ait une fonction qui retourne le genre de bouton à afficher, mais bon c’est pas très compliqué ça.

1 « J'aime »

Tu n’as pas dis ce que tu pensais de mon schéma!

Je t’avoue que je ne comprends plus totalement tout par contre là ^^

Parameters c’est pas redondant avec subcategory?

Pour revenir à notre modélisation pour qu’on parte sur des bonnes bases.

Est-ce qu’on est d’accord que:

  • Un Device c’est un périphérique physique, un objet.
  • Une Feature (qu’on appelle “DeviceFeature” pour que ça soit plus facilement compréhensible dans la DB, mais en gros c’est une feature), c’est une fonctionnalité d’un device.

?

Exemple:

Mon périphérique “Ampoule 1 Philips Hue” possède 3 fonctionnalités:

  • On/Off
  • Faire varier la luminosité
  • Faire varier la couleur

On pourrait donc représenter ce périphérique:

{
  "name": "Ampoule 1",
  "service_id": "1ec7966f-e3ef-47d7-9aef-083a59956a68",
  "features": [{
      "category": "light",
      "type/sub_category": "binary"
    },
    {
      "category": "light",
      "type/sub_category": "brightness"
    },
    {
      "category": "light",
      "type/sub_category": "color"
    }
  ]
}

Il y a de l’idée dans ton excel mais est-ce que tu pourrais un peu me donner des exemples de ce que tu vois? (à la manière de ce que j’ai fais là).

Je ne comprends pas la hiérarchie et la logique derrière chaque entité/attribut que tu me présentes.

J’aime bien la notion de paramètres qui est hyper utile pour le Z-wave, mais comment on stocke ça?

Je me rappelle qu’on en avait déjà parlé avec @MathieuA, ça pourrait être un attribut “parameters” boolean dans un deviceFeature ce qui permettrait de dire si un DeviceFeature est un paramètres où un vrai fonctionnalité.

1 « J'aime »

Salut à tous!

Je continue mes réflexions, cette fois-ci sur le style de code que va avoir la partie script/voir le core et les modules.

1/ Première approche, très orientée-objet:

Contrôler un périphérique:

const kitchenLight = await gladys.device.findOneBySelector('kitchen-main-light');
await kitchenLight.turnOn();
await kitchenLight.setBrightness(10);
await gladys.helpers.wait(100);
await kitchenLight.setBrightness(20);

Contrôler un ensemble de périphériques:

const kitchenLightGroup = await gladys.device.find('kitchen.light'); // ROOM_SELECTOR.CATEGORY
await kitchenLightGroup.turnOn();
await kitchenLightGroup.addBrightness(10);

2/ Deuxième approche, plus fonctionnelle comme on a actuellement:

await gladys.device.turnOn('kitchen-main-light');
await gladys.device.setBrightness('kitchen-main-light', 10);
await gladys.helpers.wait(100);
await gladys.device.setBrightness('kitchen-main-light', 20);
await gladys.device.turnOn('kitchen.light');
await gladys.device.addBrightness('kitchen.light', 10);

Petite analyse

Les deux approchent sont très différentes et ont des implications très différentes.

1/ Dans le premier cas, après réception des données de la BDD il faudra construire ces objets en fonction de chaque catégorie de périphérique, et rajouter toute cette couche objet. C’est assez proche de l’approche que tu avais mis sur ce channel @piznel avec le repo abstract-things.

J’aime bien visuellement ce que ça donne, ça fait propre et c’est assez sexy pour attirer des devs. Donc sur l’aspect syntaxe et communication c’est un + pour moi.

Après ma crainte c’est qu’on souhaite étendre cette logique à plein d’endroits, et ça peut être une lourdeur là où nous aimerait un soft plutôt léger et simple d’utilisation. Je suis pas persuadé qu’une approche objet ait un sens dans un backend. Un backend pour moi c’est avant tout juste un bridge qui fait du CRUD entre la DB et le frontend, pas forcément besoin de créer des objets qui peuvent être lourd en mémoire et apporte une complexité de développement.

2/ J’aime moins visuellement ce que ça donne, c’est moins sexy c’est sûr.

Après on sait clairement ce qui se passe, et en terme de management de la mémoire le boulot est donné au core: on appelle la fonction, la fonction fait son boulot, et hop c’est fini.

Conclusion

Je sais pas si c’est un choix entre l’un ou l’autre, on peut avoir une approche hybride.

J’aimerais avoir votre avis, c’est clairement un petit débat sympa :slight_smile:

Mmm la par contre je ne suis pas d’accord, on stockerait de l’information trop spécialisée et dupliquée du coup.

1/

light_dimmable: je ne comprends pas l’objectif d’avoir ça. Tu stockes 2 informations dans un champ: que c’est un périphérique qui gère de la lumière + que ce périphérique est dimmable. Là dessus je te renvoie aux dépendances fonctionnelles en base de donnée :smiley:

Si je demande à Gladys “Allume la lumière du salon”, il faut que Gladys ait un moyen d’identifier tous les périphériques gérant de la lumière (que ce soit dimmable ou non n’a peu d’importance dans cette question, c’est une information secondaire).

2/ En revanche la question de remonter la category au device est une vraie question. Pour l’instant elle est dans DeviceFeature parce-que certains périphériques (je pense au Z-Wave notamment) font un peu tout, et du coup on avait mis la category dans le deviceType pour gérer ces device “hybrides”.

Après la question se pose. Si tu as une caméra de surveillance qui fait aussi capteur de température et capteur de mouvement? Si tu as une porte qui remonte aussi des données de température et d’humidité? Comment on les classifie?

Si vous avez des exemples de périphériques bizarre je suis preneur!

Personnellement je suis pour remonter la category au device, c’est plus clair.

Je pense que le mieux pour définir tout ça, ça reste de voir des vrais cas!

Excellent cas!

Du coup, quelle category aurait ce périphérique si on se dit qu’on remonte category dans device?

1/ Je suis pas fan de séparer un périphérique en 3 périphériques, c’est pas logique par notre définition « un device est un périphérique physique ».

2/ Si on lui donne comme catégorie « bridge » par exemple.

Un deviceFeature de type « binary » n’a plus de sens: est-ce que c’est pour allumer le gateway (on/off?) ou allumer la lumière du gateway?

Du coup on en revient à devoir remettre côté deviceFeature une category supplémentaire pour dire « cette feature c’est de la gestion de la lumière »

PS: quand je vous disais que la partie modélisation c’est 80% du travail, je rigolais pas :stuck_out_tongue: Le code c’est rien!

En périphérique “bizarre” il y a aussi le Fibaro FGMS-001 (en Zwave) qui agit comme détecteur de mouvement, capteur de température, capteur de luminosité et capteur de vibration il me semble :slight_smile: