Aller au contenu
Inkew

QA Moyenne Glissante

Recommended Posts

Bonjour,

 

Je souhaite créer un QA qui me calcule la moyenne glissante des températures (d'un périphérique ZWave ou QA Netatmo) sur les 5 derniers jours.

Est-ce que quelqu'un aurait une idée de comment procéder?

Partager ce message


Lien à poster
Partager sur d’autres sites

Une boucle infinie, avec une table suffisamment grande pour stocker l'historique des mesures sur 5 jours.

Par exemple chaque minute sur 5 jours ça fait : 60*24*5 = 7200 éléments dans le tableau, rien de méchant.

Puis avec une petite boucle for tu additionnes toutes les valeurs, tu divises par le nombre d'éléments.

A chaque passage dans la boucle infinie, il faut faire un table.insert() pour ajouter la nouvelle valeur, et faire un table.delete() si l'historique dépasse 7200 valeurs.

Partager ce message


Lien à poster
Partager sur d’autres sites

Not exactly a moving average but still "smoothing" out the value can be achieved by subtracting the average... no buffer needed.

function smoothFilter(size)
  local sum,n,average=0,0
  return function(x)
    sum,n=sum+x,n+1
    average = sum/n
    if n >= size then 
      sum,n=sum-average,n-1
    end
    return average
  end
end

local filter = smoothFilter(40)
for i=1,10000 do
  print(filter(math.random(1,500)))
end

 

  • Like 1

Partager ce message


Lien à poster
Partager sur d’autres sites

Bonjour,

 

Je n'ai pas du tout un gros niveau en lua, mais je comprend le principe. Une question cependant : est-ce qu'une table dans un QA est sauvegardée en dehors de ce QA (en gros si la HC3 s'éteint par exemple, est-ce que cette table est toujours accessible par ce QA?)

Savez-vous où je peux trouver de la doc pour la manipulation de ces tables en lua?

 

Merci!

Partager ce message


Lien à poster
Partager sur d’autres sites

La table c'est une variable en RAM, donc non elle n'est pas sauvegardée lors du redémarrage du QA, comme tout programme informatique en fait.

On pourrait imaginer sauvegarder cette table dans une variable du QA (donc stocké dans la base de données de la HC3), mais je ne te le conseille pas du tout (impact sur les performances, usure de la mémoire flash)

 

La solution de @jang est élégante, mais ce n'est pas une vraie moyenne glissante comme tu le voulais, et elle ne résout pas non plus le problème du redémarrage du QA.

 

Après là tu as plusieurs notions à prendre en compte :

- du LUA pur, mais c'est un faux problème car la manipulation des tables est hyper simple (je t'ai donné les 2 fonctions à utiliser) , donc on en revient à un principe de programmation en général (du coup est-ce que tu es à l'aise sur un autre langage, car tout ceci n'est que de l'algorithme)

- des mathématiques.... c'est à dire comment calculer une moyenne glissante

- le QA en particulier, et la façon que Fibaro propose pour mémoriser des données de façon persistante

 

Pour ce dernier point, comme dit plus haut, je ne te conseille pas du tout de mémoriser la table complète à chaque passage dans la boucle.

De toute façon le QA va calculer sa moyenne glissante via sa table en mémoire vive, et mettre à jour sa propriété "value", puisque ton QA aura comme type "com.fibaro.temperaturesensor" je suppose.

Du coup, en cas de redémarrage du QA, dans le onInit() je relirais la valeur courante de la value (puisque celle-ci est bien persistante), et je m'en servirait pour initialiser ma table de 7200 valeurs.

Je trouve que c'est un moyen simple et efficace de résoudre la problème du redémarrage du QA.

Partager ce message


Lien à poster
Partager sur d’autres sites

Merci pour ton retour, je pense avoir compris la méthode. Non je ne suis pas très à l'aise avec la programmation, mais je vais me tester.

Je reviendrai sur ce forum si je coince quelque part. Merci encore pour tes éclairages.

Partager ce message


Lien à poster
Partager sur d’autres sites

Bon, j'ai essayé mais je sèche sur plusieurs choses. La première est que je découvre que la table est vide dans ma fonction loop. Visiblement la variable TempMoy également.

Ensuite, je ne sais pas comment m'y prendre ni pour extraire les valeurs de la table et calculer leur moyenne, ni pour connaitre le nombre d'entrées de cette table, ni pour supprimer première ligne..

 

function QuickApp:onInit()
    self:debug("onInit")

    local TempMoy, TempMoyGliss, i
    local Table = {};
    TempMoy = fibaro.getValue(309, "value")
    for i = 1, 10 do
        table.insert(Table, TempMoy)
    end
    self:debug(json.encode(Table))
    self:loop("Calcul_Temp_Moy_Gliss")
end

function QuickApp:loop(text)
    self:debug("Text from loop: ", text)
    fibaro.sleep(3000)
    --table.insert
    self:debug(json.encode(Table))
    
    self.updateProperty("value", TempMoy)
    self:loop(text)
end

Un peu d'aide serait bienvenu!!!

Partager ce message


Lien à poster
Partager sur d’autres sites

C'est un bon début :)

 

Alors c'est normal, car tu as déclaré tes variables en local, donc elles ne sont connues que dans la fonction onInit(), mais pas dans loop()

 

Voir :

 

 

Ensuite, il ne faut surtout pas utiliser sleep(), ça va bloquer ton QA, donc mener à un plantage de celui-ci, voire à un reboot de la box.

Il faut utiliser setTimeout()

C'est de l'asynchronisme, un mécanisme pas évident à comprendre....

Voir ici :

 

Ce que tu peux faire, c'est affecter tes variables à self (l'objet qui désigne le QuickApp en mémoire), ainsi tu peux utiliser les variables dans toutes les fonctions du QA.

 

 

 

Pour accéder à un élément particulier d'une table, il faut utiliser les crochets, et le dièse pour compter le nombre d'éléments de la table :

for i=1, #Table do
  	print(Table[i])
end

 

 

ce qui devrait donner un truc dans le genre (non testé, à affiner, mais ça te donne une base de travail) :

function QuickApp:onInit()
	self:debug("onInit")

	local tempOld = self.properties.value
	self.Table = {}
	for i = 1, 10 do
		table.insert(self.Table, tempOld)
	end
	self:debug(json.encode(self.Table))

	fibaro.setTimeout(0, function() loop() end) -- on lance la boucle
end

function QuickApp:loop()
	self:debug("loop !")

	table.remove(self.Table, 1)
	local newValue = fibaro.getValue(123, "value") -- il s'agit de l'ID de ton module température Z-Wave ou Netatmo
	table.insert(self.Table, newValue)
	self:debug(json.encode(self.Table))

	local tempSum = 0
	for i = 1, #self.Table do
		tempSum = tempSum + self.Table[i]
	end
	local tempMoy = tempSum / #self.Table
	self:debug("Nouvelle moyenne :", tempMoy)
	self.updateProperty("value", tempMoy)

	fibaro.setTimeout(3000, function() loop() end) -- prochaine boucle dans 3 secondes
end

 

 

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Merci pour toutes ces infos! Je vais lire ce que tu as posté pour que je comprenne mieux, et je teste ton code dans la foulée :)

Partager ce message


Lien à poster
Partager sur d’autres sites

Salut Lazer, Tu verras qu'il y a beaucoup de début dans mon code, mais il n'y a qu'une chose qui a l'air de ne pas fonctionner : la fonction settimeout. Lorsque je la fais à l'ancienne (pas bien), ça tourne rond :

function QuickApp:onInit()
    self:debug("onInit")
    self:updateProperty("value", -100)
    local tempOld = self.properties.value
    self:debug(tempOld)
    self.Table = {}
    for i = 1, 10 do
        table.insert(self.Table, tempOld)
    end
    self:debug(json.encode(self.Table))

    --self:loop("Calcul_Temp_Moy_Gliss") -- on lance la boucle
    fibaro.setTimeout(0, function() loop() end) -- on lance la boucle
end

function QuickApp:loop()
    self:debug("loop!")
    table.remove(self.Table, 1)
    self:debug(json.encode(self.Table))
    local newValue = fibaro.getValue(309, "value") -- il s'agit de l'ID de ton module température Z-Wave ou Netatmo
    self:debug(newValue)
    table.insert(self.Table, newValue)
	self:debug(json.encode(self.Table))

    local tempSum = 0
	for i = 1, #self.Table do
		tempSum = tempSum + self.Table[i]
	end
    self:debug(tempSum)
    local tempMoy = tempSum / #self.Table
    self:debug(tempMoy)
    self:updateProperty("value", tempMoy)

    ---fibaro.sleep(3000)
    fibaro.setTimeout(3000, function() loop() end) -- prochaine boucle dans 3 secondes
    self:loop(text)
end

 

Partager ce message


Lien à poster
Partager sur d’autres sites

En effet, normal, il fallait écrire avec self :

fibaro.setTimeout(0, function() self:loop() end)

fibaro.setTimeout(3000, function() self:loop() end)

Je t'ai dis, c'est pas testé, et écris à l'arrache ;)

 

Partager ce message


Lien à poster
Partager sur d’autres sites

T'inquiète j'avais bien compris que tu n'avais pas eu le temps de tester! Et en même temps ça aide à comprendre!

Ca fonctionne, je suis trop content! Merci encore!

Partager ce message


Lien à poster
Partager sur d’autres sites

Petite question bonus : peut-on supprimer des valeurs qui construisent la courbe dans l'onglet Advanced du QA (avec les tests du début ça ne ressemble pas à grand chose)!

image.png.d8af28c5cbae2da335ec34519ab7166e.png

Partager ce message


Lien à poster
Partager sur d’autres sites

Je ne sais pas.... je suppose que ça va tout supprimer.

Sinon tu attends, ça va se purger automatiquement.

Partager ce message


Lien à poster
Partager sur d’autres sites

×