Parlons de Gladys V4


#142

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.


#143

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.


#144

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.


#145

Salut à tous!

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

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


#146

Salut @pierre-gilles,
Je ne sais pas si tu as utilisé le travail que j’avais fait sur les features des devices, mais je pense qu’il y a des trous dans la raquette dans les catégories, comme par exemple la pression (c’est le premier qui m’a sauté aux yeux).
Après, j’imaginais plus un système catégorie/sous-catégorie, histoire de ne pas avoir de liste à rallonge (ou alors, il y a un truc que je ne sais pas encore sur l’affichage à terme).
Pour rappel, voici le lien.


#147

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:


#148

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!


#149

Je me rappelle de ce post de @Pti_Nico, parlant de perf et d’index.
Je n’en ai pas vu dans le modèle. Ils seront définis plus tard ? ou l’analyse est caduque ?


#150

Dans ton approche, le type sert pour l’UI, et la catégorie pour le brain et l’UI.
Donc le couple “type/catégorie” sert à définir principalement les règles d’affichage, si je comprends bien :slight_smile:

Pour ma part, avec un raisonnement “non-dev”, une feature est définie avec une catégorie (genre “light”), puis une sous-catégorie (genre “dimmable”).
Dans ce cas, on aurait un couple “light/dimmable”, qui, permet de définir l’affichage et le comportement attendu, et le “type” disparaîtrait dans cette table.

Du coup, le dev d’un module définit la catégorie et la sous-catégorie, puis les méthodes associées (exposées par Gladys).

Pour moi, cela me semble plus naturel, car plus représentatif du matériel physique. Après, ça implique que dans Gladys, toutes les fonctionnalités soit codées. Mais cela me semble une approche plus “sereine”, avec une uniformité des comportements.

pas sûr que mon explication soit très clair… :sweat_smile:

PS : Dans un autre post, nous avions parlé de pouvoir réorganiser l’affichage des features dans les views . Cela n’implique-t-il pas un champ “order” dans la table des features ?


#151

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.


#152

Bravo déjà, tu m’as compris, ce qui n’est pas toujours simple ! (demande à @MathieuA !!!)
Oui, c’est exactement l’idée :slight_smile:

Après, je ne t’ai pas tout dis sur le travail que j’avais fait sur les catégories : j’étais allé un “peu” plus loin dans la granulation et les définitions.

Par exemple :

Prenons une alarme de porte de Garage ; elle peut avoir comme paramètres tous ceux de la colonne “Paramètres”.
Mais dans le cas d’une alarme CO2, elle n’aura pas le paramètre “OBSTRUCTION”, qui n’a pas de sens pour elle.


#153

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é.


#154

Je prépare le déjeuner et te réponds :slight_smile:

Il est clair, et j’ai bien compris l’idée. Enfin, je pense !
Après, effectivement, il faut que’on se mette d’accords sur le vocabulaire.

Je te fais ça ASAP.


#155

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:


#156

Salut @pierre-gilles,

On est d’accord. Du coup, pour moi, ça donne :

 {
  "name": "Ampoule 1",
  "service_id": "1ec7966f-e3ef-47d7-9aef-083a59956a68",
  "category": "light_dimmable",    // le device EST le périphérique physique.
  "features": [{
      "type/sub_category": "binary"
    },
    {
      "type/sub_category": "brightness"
    },
    {
      "type/sub_category": "color"
    }
  ]
}

Avec, dans le modèle device :

category: {
  allowNull: false,
  type: DataTypes.ENUM(
    // light
    'light_dimmable',
    'light_no_dimmable',
  ),
},

#157

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.


#158

C’est juste une façon de prédéfinir les features possibles, light me va aussi bien.

Pour moi, 2 possibilités :
1- Soit on le traite comme 3 devices :

  • Caméra
  • capteur de température
  • capteur de mouvement

2- Soit on le traite selon la fonctionnalité principale, mais du coup, il faut être capable de définir ce device avec des features qui n’ont rien à voir avec une caméra.

Ma préférence va vers la 1ère solution (cela pose du coup la question de l’identifier), mais ouvre totalement le champ des possibles, tout en restant dans un système à granularité fine.

La gateway Xiaomi, qui fait aussi sonde de luminosité, détecteur de mouvement, HP et light !


#159

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!


#160

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:


#161

Je viens de regarder chez imperihome :
la gateway Xiaomi est intégré dans leur système de base.

A priori, c’est pareil chez Home Assistant.

J’ai l’impression qu’ils font du cas par cas systématique, tout en proposant une API pour développer son propre truc, mais qui est bien plus cadré et ne gère pas les cas de la gateway de façon simple.

Du coup, je ne vois pas de solutions simples autre que de mettre catégorie/sub categ (ou type) au niveau des features.