Ajout d'une requête APIRest Gladys permettant de récupérer les états des features sur une plage

Feature description
Le but de cette feature serait d’avoir une requête APIRest Gladys en local permettant de récupérer tous les états des device_features avec une plage de date/heure à date/heure.

L’utilisation recherchée serait, notamment, pour pouvoir récupérer l’énergie consommée ou produite en 1h pour pouvoir exécuter des calculs via Node Red. En effet à ce jour Gladys ne peut pas faire de calcul, et Node Red ne contient pas de base de donnée (pourquoi d’ailleurs stocker les données 2 fois) mais on peut y faire des calculs.

Lien avec le sujet suivant :

Bon bah en effet c’est rapide @pierre-gilles ^^

C’est fonctionnel par l’intervalle de temps, finalement j’ai repris exactement tes methodes utilisées pour l’agrégation ^^ mais en ne laissant que les données ‘live’

Donc juste 2 petites questions :

  • si tu peux me dire rapidement la commande en base pour trier de date début à date fin et le format à utiliser en requêtes (genre start_interval = “01032022 12:00” & end_interval = “3103202 12:00:00”), si ca te prend trop temps pas de soucis je chercherais.
  • j’ai repris egalement le “multi”, je vois que tu split lzs “,” j ai donc supposé que tu pouvais envoyer plus device_feature_selector séparés par des “,”. Mais en testant avec insomnia, il me retourne “Not found” fais-je quelque chose de mal ?^^

Bon du coup en fait c’est good !!^^ facile en cherchant au bon endroit ^^ op.lte !!
Pour le format de date je fais : "2022-03-01T10:00:00.000Z, tu me diras si tu veux qu on fasse mieux ^^ mais ca marche bien :

Je vais creer la PR.

Edit : C’est fait ici Add API request states features by Terdious · Pull Request #1496 · GladysAssistant/Gladys · GitHub

J’avais pas compris que tu voulais faire quand même du downsampling « live » (ça risque d’être lourdingue niveau CPU, surtout sur le main thread), pourquoi ne pas juste retourner la data « raw » dans cette route ? Je pensais que c’était à but d’export ? Tu veux pas l’exhaustivité ?

Limite ce qu’on peut faire, c’est de mettre un « take » et un « skip » pour paginer, mais renvoyer toute la data quand même, non ?

Haha !!
Alors là pour tout t’avouer je ne fait que rarement de la récupération en DB et je suis plutot autodidacte. J’ai une formation milieu d’annee au boulot car je vais y toucher de plus en plus.

Donc tu as très certainement raison, sur ce que tu me dis mais du coup il va falloir que tu m’en dise si possible plus sur ce que tu entends par récupérer la data raw plutot que le json. (recuperer juste les valeurs de date à date sans les les created_at ?)
La pagination apporte quoi du coup ??^^
Désolé piur mon ignorance il est vrai que je ne comprends pas encore les impacts cpu sur ce genre de requêtes.

Et oui en effet le but est de recuperer tout les states entre 2 dates.

Merci d’avance pour tes précisions.

Dans ta PR tu fais du downsampling :

Quand je disais “raw”, c’est ne pas faire de downsampling et de juste retourner la data brute venant de la DB, je pense que c’est ce que tu veux faire non ?

Oui je pense que ce sera suffisant en effet, je teste comme ça.

Si je pars comme ça, c est mieux ? :
image
Résultat :

Et du coup je vire le maxState en fait. Pas utile sur ce genre de requête.

Edit : La PR est à jour !!

@pierre-gilles,

Je demandais sur la PR si c’était à moi de modifier la documentation API Rest ou si c’était toi qui t’en occupais ? Je n’ai pas trouvé comment modifié celle-ci et comme il est inscrit dans la PR :


mais si je regarde dans la doc Gladys REST API, le chapitre “Device - getDeviceFeaturesAggregated” ne contient pas les exemples et les queries mentionnées dans le fichier :

Ca n’a donc pas l’air automatique si ?

Edit :
Bon en fait je parle trop vite, je pense avoir trouvé avec l’exemple du dashboard ^^ Par contre a-t-on un moyen de visualiser la chose ?

C’est bien à toi de modifier la documentation :slight_smile:

Pour tester tes changements, dans le dossier server, tu fais:

npm run generate-apidoc

Ce qui te génère un dossier apidoc.

Dedans tu ouvres le dossier et tu ouvre le fichier index.html dans ton navigateur tu auras la doc générée :slight_smile:

Ok super merci.

Normalement plus qu’a vérifier et c’est good ^^ le dossier généré je le laisse quand je git push ou il faut que je le supprime avant ?

Il est dans le .gitignore donc il ne sera pas poussé :slight_smile:

Top, merci beaucoup.

Bon et bien je viens de push les dernières modifs, de mon côté la doc à l’air bonne,

Si c’est bon pour toi, tu peux en prendre possession ^^

Edit :
My bad, je n’avais pas vu les champs “optionnel” sur les paramètres, je retire et repush dans 2 min

Edit2 : C’est good ^^

Merci pour ta PR !

J’ai quelques retours, je te fais un retour dans la journée !

1 « J'aime »

En fait j’ai plusieurs questions par rapport à ce qui a été développé.

Là en l’état tu as copié coller le fonctionnement qui était fait sur la route “aggregated_states”, route qui avait un but précis : pouvoir afficher les graphiques de valeurs de capteurs de plusieurs capteurs.

Les paramètres et le format de cette route sont spécifique à ce besoin, et je suis pas sûr que ce soit le même besoin qu’ici, donc je ne pense pas forcément qu’il faille garder le même format d’API.

Je me demande si le besoin ici pour toi ce n’est pas plutôt une route de ce style là ( à débattre ) :

GET /api/v1/device_feature/:device_feature_selector/state

Avec en paramètres GET effectivement:

?from=DATE
&to=DATE 

( J’ai changé start_interval / end_interval parce qu’on avait déjà ce from/to dans une autre route, /api/v1/user/:user_selector/location )

Ce qui permettrait éventuellement de mettre des paramètres optionnelles: ?take= et &skip pour ceux qui veulent utiliser cette route pour exporter des grandes quantités de données en chunk pour par exemple injecter ces données dans un Grafana, sans défoncer les performances de leur instance ^^

La réponse de cette route serait un tableau de state dans leur format en DB:

[
  {
    "id": "4ad21395-686b-41e5-988c-6e9201ba985a",
    "value": 10,
    "created_at": "2022-04-04 19:26:16.815 +00:00",
    "updated_at": "2022-04-04 19:26:16.815 +00:00"
  }
]

Je pense que c’est plus lisible, et plus adapté à ce qu’on veut faire là. Le fait de ne demander qu’un seul device_feature dans cette route permet de pouvoir gérer la pagination si besoin, pour quelqu’un qui veut faire des exports sans défoncer son instance Gladys (ce qui est sûrement ton cas j’imagine!! :smiley: )

Qu’en penses-tu ?

Je reviendrais ensuite sur le code, mais là le plus important pour moi c’est de se mettre d’accord sur la spec !

1 « J'aime »

Tout d’abord merci pour ton retour.

Alors en effet ce que tu présentes là peut tout à fait être un autre besoin, à voir si les 2 sont compatibles dans le même format.

Le but ici était de faire du calcul, le besoin est donc totalement différent - exemple :

  • J’enregistre des valeurs d’énergie sur chacunes de mes phases et pour chaque parties de la propriété toutes les heures pendant 1 jour, 7 jours, 1 mois, 1 an…
  • Sur Node-red je passe une requête tous les jours “from=j-2” & “to=j-1” pour récupérer toutes les valeurs de toutes les phases de la journée précédente (chaque device_features).
  • Je récupère le selector de chaque device_feature, additionne le tableau de sortie affilié, et le renvoie dans un autre device feature (qui a un selector presque identique - “jour” au lieu d’“heure”).
  • J’additionne ensuite l’ensemble des conso de chaque phase que je renvoi sur un autre device_feature, et enfin additionne l’ensemble des phases pour un device_feature global de consommation.
  • Je fais de même tout les 7 jours, les 1 mois, …
  • Je fais de même pour la production des panneaux solaires.
  • Lors des étapes précédentes j’ai pris soin de tout enregistrer dans des variables Node-Red au nom des selector des device_feature, et je peux calculer la différence production/consommation sur les mêmes intervalles de temps.

Pour le coup, pourquoi limiter à un seul device_feature_selector ? On est d’accord que si on ne met qu’un seul device_feature_selector dans l’option ça reviendra au même ?

Bon en soit, ça ne me dérange pas du tout pour le format de sortie que tu proposes, je pensais à tord sûrement qu’il était plus lourd de faire 3 / 6 / 9 requêtes séparées qu’une seule si je souhaitais avoir 3 / 6 / 9 device_features à traiter en même temps. Mais si c’est plus light comme ça, pour mon cas personnel, il suffira de faire une boucle sur la fonction node-red ^^ Ca me va.

C’est pas la même chose car on ne peut pas faire de pagination quand on fait du multi-devices

Tout dépend de la taille des réponses de l’API.

Dans le front la route aggregated_states est une toute petite route car les données sont agrégées, et donc on a un JSON qui est de taille acceptable même avec 3-4 device_features sélectionnées.

Dans un but d’export de donnée, potentiellement ton appel API va renvoyer 2000 lignes par capteurs (et encore 2000 lignes je suis gentil, plus 10-20-30k dans ton cas parfois).

Si tu groupes + sans pagination, ça fait un payload JSON énorme, et ça c’est pas dingue pour plusieurs raisons:

  • Faire un JSON.stringify d’un énorme objet, c’est une opération bloquante en Node.js qui va bloquer le CPU
  • Usage RAM important lors de ces requêtes si la réponse est trop grosse
  • La DB risque d’être saturée d’un coup, et ça peut entrainer des blocages

Là où toi tu veux juste faire un export, donc la rapidité d’export n’est pas importante, il vaut mieux faire plusieurs requêtes de tailles convenable qu’une seule requête titanesque :slight_smile:

(Attention, ce que je te dis ne s’applique pas à une logique frontend ici, je pense uniquement au cas d’export)

Ah ok, je comprends mieux !!

Merci pour ta reponse, ca m’eclaire sur beaucoup de chose et en effet, de l’autre côté (côté Node Red ou autre) y a que du positif en somme, ce n’est que le traitement de l’info qui sera different.

Je fais les modifs en ce sens et reviens vers toi dès que c’est fait apres mes tests.

Bonjour @pierre-gilles,

Bon c’est bon les modifs sont faites, et en effet c’est beaucoup plus logique de faire ainsi. Faut que je revois ma copie du coup pour faire les boucles sur tout les devices_feature de type puissance/énergie mais ça se fait très bien.

Donc tout fonctionne parfaitement, je me suis permis de rajouter également une option dans la req.query pour les attributes car il se trouve que je n’ai au final pas besoin du champs « created_at » et ça permet de pouvoir faire l’addition directement sur l’objet (enfin il me semble, dans tout les cas ça allège de mon côté. Je lui ai mis une option par défaut attributes: ['created_at', 'value'] pour la rendre non obligatoire.

Par contre, encore une fois désolé de mon ignorance, j’ai testé sans l’option skip (=0 par défaut) et avec une option skip = 10 dans la requête, et la réponse retournée reste la même … ai-je zappé quelque chose ? Comment l’utilise-t-on ? J’ai toujours pensé que cela servait à envoyer plusieurs objet de la longueur de la valeur de skip (donc si 50 résultats on a une réponse avec 5 objets de longueur 10) ?

Edit :
Je laisse ma question … posée trop vite !! Désolé pas besoin de me répondre, il suffisait de lire la doc :

paramètre skip
Utilisez le paramètre de requête $skip pour définir le nombre d’éléments à ignorer au début d’une collection. Par exemple, la requête suivante renvoie les événements pour l’utilisateur triés par date de création, en commençant par le 21e événement de la collection :

image

Je nettoie donc le code, revois les tests et ça devrait être bon pour la review (si tu valide l’option.attributes bien sûr ^^)

Salut @pierre-gilles,

C’est tout bon pour moi, les tests sont terminé, tu peux review quand tu as un peu de temps.