lundi 28 juillet 2014

Un exemple de script Lua | Time : Cohérence et confirmation RFXCOM

LE 433mhz: pourquoi ? 


 Le RFXTRX 433mhz de RFXCOM est un outil permettant de communiquer avec des modules en 433 Mhz.

Avantage : leur prix (compter 15€ pour un module ON/OFF).

Inconvénient : Ces modules n'ont pas de retour d'état, c'est à dire qu'il est impossible de savoir si la commande que l'on vient d'envoyer à bien fonctionné (ou pas ) ...

 Il convient de ne pas lier des objets critiques à ce genre de modules, histoire de ne pas mettre le feu à la maison !

 Pour ma part, j'ai relié via ces modules quelques objets :
  • Radiateurs électriques pour gestion du chauffage. 
  • Contrôle de la VMC. 
  • Sondes de Température et Hygrométrie des chambres.

Un premier but : Vérifier la cohérence.

En domotisant ma VMC, il m'est arrivé de drôles de choses : ma vmc étant sensée s'éteindre en heures pleines continuait de tourner plein pot !
Comme nos chers modules 433 MHz n'accusent pas réception, il va falloir de temps en temps renvoyer la commande dans laquelle ils sont sensés être.
Pour cela nous allons utiliser les scripts LUA, surtout sous la forme "TIME" (c'est à dire déclenchée par le temps à certaines périodes).

Concrètement mon script fait ce qui suit :


  • Prend les éléments d'une liste à vérifier.
  • Regarde dans quel état l'élément est sensé être.
  • Renvoi le signal dans lequel l'élément est sensé être.
Deuxième But : Verifier l'âge des mesures de température.

Pour réduire la facture de domotisation, l'utilisation de sondes Oregon Scientific réduit considérablement la facture. Cependant il est difficile de savoir quand le module est à court de piles.
Mon script va donc vérifier l'age des dernières mesures de chaque sonde , et m'envoyer un mail dès que l'âge est supérieur à une valeur.

Voici le Fameux script :



--Permet toutes les 15 minutes de renvoyer la commande actuelle sensée etre appliquée aux modules en 433 ( sans retour d'etat )


commandArray = {}

--recupere les minutes
time=os.time()
minutes=tonumber(os.date('%M',time))
hours=tonumber(os.date('%H',time))

print('!*!*!*!*!*!*!*!*!Lancement du check à '..hours..'h'..minutes)


-------Toutes les 15 minutes
------------------------------------------------------------------------

if( (minutes==15) or (minutes==0) or (minutes==30) or (minutes==45) ) then



 --------Renforcement des envois de signal-------------------------------------
 ------------------------------------------------------------------------------
   print('check de tous les materiels rfxcom (sans retour d\'etat)');


   local check={}
   --Chauffage
   check['0']='Radiateur Mathilde'
   check['1']='Radiateur Parents'
   check['2']='Radiateur Salle De Bain'
   --VMC
   check['3']='VMC'
   --Lumieres
   check['4']='Lumiere TV'
   check['5']='Lumiere Salle De Jeu'
   --Piscine
   check['6']='Filtration Piscine Temp'

   --Parcours le Tableau
   for key, valeur in pairs(check) do
      print ('CHECK : '..valeur.. ' -> ' ..otherdevices[valeur])
      commandArray[valeur]=otherdevices[valeur]
   end


 --------FIN Renforcement des envois de signal-------------------------------------
 ------------------------------------------------------------------------------




end 

-------FIN Toutes les 15 minutes
------------------------------------------------------------------------

-------Une fois par heure, verifie l'age des mesures

if((minutes==0) or (minutes==1) ) then

    local temp={}
    --temperatures
    temp['0']='Mathilde'
    temp['1']='Parents'
    temp['2']='Salle De Bain'
    temp['3']='Salon'
    temp['4']='Meteo'
    --temp['4']='Entree'

    --delai au dela duquel on alerte en secondes
    local alerte=3600

    local mail='Alerte sur sonde temperature'
    local trigger=0

       --Parcours le Tableau de stemperatures
   for key, valeur in pairs(temp) do

                        s = otherdevices_lastupdate[valeur]
   -- returns a date time like 2013-07-11 17:23:12
                        t1 = os.time()
   year = string.sub(s, 1, 4)
   month = string.sub(s, 6, 7)
   day = string.sub(s, 9, 10)
   hour = string.sub(s, 12, 13)
   minutes = string.sub(s, 15, 16)
   seconds = string.sub(s, 18, 19)

   commandArray = {}

   t2 = os.time{year=year, month=month, day=day, hour=hour, min=minutes, sec=seconds}
   difference = (os.difftime (t1, t2))

             
      print ('CHECK Temperature: '..valeur.. ' -> ' ..otherdevices[valeur].. ' age = '..difference..' secondes')
             
             if(difference > alerte)then
                mail=mail..'Age de '..valeur .. '-> '..difference..' secondes !!
'
                trigger=trigger+1  
             end
      
   end
 if(trigger>0)then
    commandArray['SendEmail']='Alerte Age Sonde Temperature #Attention aux sondes suivantes :

'..mail..' #gruelt@gmail.com'
        end


end









return commandArray


Explications partie Renvoi de signal

--recupere les minutes
time=os.time()
minutes=tonumber(os.date('%M',time))
hours=tonumber(os.date('%H',time))

Au début du script on récupère l'heure système actuelle ( dans time) , et on extrait les chiffres des minutes et des secondes.
local check={}
   --Chauffage
   check['0']='Radiateur Mathilde'
   check['1']='Radiateur Parents'
   check['2']='Radiateur Salle De Bain'
   --VMC
   check['3']='VMC'
   --Lumieres
   check['4']='Lumiere TV'
   check['5']='Lumiere Salle De Jeu'
   --Piscine
   check['6']='Filtration Piscine Temp'


Ici j'initialise ma variable "check" , sous forme de tableau avec les deux accolades.Chaque ligne représente le nom du périphérique que je veux renforcer.
for key, valeur in pairs(check) do
Cette ligne permet d’exécuter le code suivant ( jusqu'au "end" associé ) une fois par périphérique. key = index dans le tableau ( 1 , 2 , 3 ...). valeur = centenu de la ligne du tableau ( 'Radiateur Mathilde' ....). check = nom du tableau parcouru.
      commandArray[valeur]=otherdevices[valeur]
Tout simplement : Je récupère l'état du périphérique courant, et je force le renvoi de l'état dans lequel il est sensé être. Et c'est fini pour cette partie !

23 commentaires:

  1. Très bon script et très utile. Je vais l'implémenter car il est pas facile de faire une remontée d'alerte en cas de batterie faible sur un module.
    Merci

    RépondreSupprimer
  2. Je ne sais pas si le script est bon, mais il m'a aidé à réaliser le mien.....sauf qu'il ne fonctionne plus voici l'erreur retournée par le Log de domoticz"Error: Lua script did not return a commandArray" il était lancé par un contacteur de porte (chacon) j'ai dù le reconfigurer après un changement de pile (domoticz ne le reconnaissait plus) et depuis, même en supprimant son lancement, le Log me retourne l'erreur!!
    Si tu as une idée du problème !
    Merci et continu sur les scripts, je ne trouve pas grand chose en français.

    RépondreSupprimer
    Réponses
    1. Est-ce qu'il y a bien au début "commandArray={}" et à la fin "return commandARRAY" , en respectant bien la casse ?
      Sinon envoie mon ton script je le testerai de mon côté.

      Thomas

      Supprimer
  3. Salut pascal, est-ce dans ton script il y a au tout début ( en respectant bien la casse) :
    commandArray = {}

    qui te permet d'initialiser la variable renvoyée...

    Et a la fin :

    return commandArray

    qui permet de retourner le fameux commandArray.

    Sinon envois moi ton script je le testerai de mon côté.

    Thomas

    RépondreSupprimer
  4. Merci de ta réponse, entre-temps, j'ai trouvé une partie de mon problème.
    En reconfigurant mon contact de porte,.....je lui avais donné un nom différent que dans mon script.
    Donc mon script refonctionne, mais j'ai toujours cette "Error: Lua script did not return a commandArray" dans le Log toutes les 30sc!!
    Le script consiste à envoyer un sms (opérateur free) lorsque la porte est ouverte.

    commandArray = {}

    if (devicechanged['detecteur presence'] == ‘Open’) then
    os.execute(‘curl -k “https://smsapi.free-mobile.fr/sendmsg?user=xxxxxxx&pass=xxxxxxx&msg=alerte ! “’)
    end

    return commandArray

    Je l'ai mis dans la case "On action" mais j'ai rien mis en "Off action".

    RépondreSupprimer
  5. Aaaaaaaaaaaaaaaaaaaaah une vulgaire faute de frappe sur le "commandeArray = {}"
    mais, le script fonctionnait quand même !

    RépondreSupprimer
  6. Bonjour,
    super script, mais je ne comprends pas bien comment il fonctionne !
    Je souhaiterais vérifier si une sonde température Oregon fonctionne.
    Que fait exactement la ligne 42 de ton script ? A quoi correspond ce otherdevices ?
    Merci de ton aide.

    RépondreSupprimer
  7. En fait le script est en 2 parties :

    La première Vérifie que les appareils sont dans l'état ou ils devraient être : pour chaque appareil renseigné dans "check" , on regarde dans quel état il sont censés être ( on ou off ) et on renvoie le signal de cet état. L’intérêt ? Si pour X raison la lumière qui est censée être allumée ne l'est pas (interférence par exemple) , on renvoi l'information. Pour faire une analogie simple : Toutes les 15 minutes j'ai quelqu'un qui va aller se promener chez moi pour être sur que mes lumières/radiateurs sont bien dans l'état renseigné dans domoticz.

    La deuxième partie, qui je pense est celle qui t’intéresse, vérifie à quand remonte la dernière mesure d'une sonde. Dans mon cas si l'age est trop élevé, il m'envoie un mail avec les sondes ayant une mesure trop "vieille".

    La partie qui t’intéresse doit être celle de la ligne 57 jusqu'à la fin.
    Au pire envoie moi le nom de tes périphériques, l'age maximal que tu veux pour une alerte , et je te ferais une adaptation du code.

    Attention le nom des périphériques est sensible à la casse (majuscules/minuscules).

    RépondreSupprimer
  8. Bonjour et merci de ta réponse. Ci joint la version de mon script :

    --Permet toutes les 15 minutes de renvoyer la commande actuelle sensée etre appliquée aux modules en 433 ( sans retour d'etat )

    commandArray = {}
    --recupere les minutes
    time=os.time()
    minutes=tonumber(os.date('%M',time))
    hours=tonumber(os.date('%H',time))

    print('__________ Lancement du check à '..hours..'h'..minutes..' __________________')

    ------- Toutes les 15 minutes
    ------------------------------------------------------------------------

    if( (minutes==15) or (minutes==0) or (minutes==30) or (minutes==45) or (minutes>0) ) then
    --------Renforcement des envois de signal-------------------------------------
    ------------------------------------------------------------------------------
    print('__________ Check de tous les materiels rfxcom (sans retour d\'etat)')

    local check={}
    --Bureau
    check['0']='Sonde bureau'

    --Parcours le Tableau
    for key, valeur in pairs(check) do
    print ('CHECK : '..valeur.. ' -> ' ..devices[valeur])
    commandArray[valeur]=devices[valeur]
    end

    --------FIN Renforcement des envois de signal-------------------------------------
    ------------------------------------------------------------------------------
    end

    -------FIN Toutes les 15 minutes
    ------------------------------------------------------------------------

    -------Une fois par heure, verifie l'age des mesures

    if((minutes==0) or (minutes==1) ) then
    local temp={}
    --temperatures
    temp['0']='Sonde bureau'

    --delai au dela duquel on alerte en secondes
    local alerte=3600

    local mail='Alerte sur sonde temperature'
    local trigger=0

    --Parcours le Tableau de stemperatures
    for key, valeur in pairs(temp) do

    s = otherdevices_lastupdate[valeur]
    -- returns a date time like 2013-07-11 17:23:12
    t1 = os.time()
    year = string.sub(s, 1, 4)
    month = string.sub(s, 6, 7)
    day = string.sub(s, 9, 10)
    hour = string.sub(s, 12, 13)
    minutes = string.sub(s, 15, 16)
    seconds = string.sub(s, 18, 19)

    commandArray = {}

    t2 = os.time{year=year, month=month, day=day, hour=hour, min=minutes, sec=seconds}
    difference = (os.difftime (t1, t2))

    print ('CHECK Temperature: '..valeur.. ' -> ' ..otherdevices[valeur].. ' age = '..difference..' secondes')

    if(difference > alerte)then
    mail=mail..'Age de '..valeur .. '-> '..difference..' secondes !!'
    trigger=trigger+1
    end
    end

    if(trigger>0)then
    commandArray['SendEmail']='Alerte Age Sonde Temperature #Attention aux sondes suivantes :'..mail..' #gruelt@gmail.com'
    end
    end

    return commandArray

    Je cherche à contrôler l'état de la sonde de température "Sonde Bureau".
    Merci de ton aide.

    RépondreSupprimer
    Réponses
    1. Si j'ai bien compris, c'est une sonde de température ?
      Mon script permet de vérifier si l'age de la dernière mesure de la sonde est supérieur à une heure (paramétrable en secondes , à la ligne "local alerte=3600") et envoi un mail à la ligne commandArray['SendEmail'] . (D'ailleurs attendtion , dans ton script il envoie un mai là mon adresse en fin de ligne).

      Supprimer
    2. Bonjour,
      c'est bien ce que je souhaite faire. Je vais corriger l'email.
      Voilà l'erreur que j'obtiens à l'exécution du script :
      Wed Oct 1 11:56:55 2014 Error: /home/pi/domoticz/scripts/lua/script_device_agesondes.lua:25: attempt to index global 'devices' (a nil value)

      Supprimer
    3. Bonjour,
      Avez vous pu trouver quelque chose concernant mon erreur ?
      D'autre part, j'utilise deux détecteur de fumée Chacon dont je souhaiterais surveiller la présence. Ce détecteurs ne communiquent que lorsqu'il y a une détection ou un test. Pensez vous qu'il soit possible de les surveiller avec un tel script ?
      Merci d'avance.

      Supprimer
    4. Bonjour,
      j'ai un peu retravaillé le script, mais il ne fonctionne toujours pas !
      Voilà le nouveau script :

      --Permet toutes les 15 minutes de renvoyer la commande actuelle sensée etre appliquée aux modules en 433 ( sans retour d'etat )

      commandArray = {}
      --recupere les minutes
      time=os.time()
      minutes=tonumber(os.date('%M',time))
      hours=tonumber(os.date('%H',time))

      print('__________ Lancement du check à '..hours..'h'..minutes..' __________________')

      ------- Toutes les 15 minutes
      ------------------------------------------------------------------------

      if( (minutes==15) or (minutes==0) or (minutes==30) or (minutes==45) or (minutes>0) ) then
      --------Renforcement des envois de signal-------------------------------------
      ------------------------------------------------------------------------------
      print('__________ Check de tous les materiels rfxcom (sans retour d\'etat)')

      local check={}
      --Bureau
      check['0']='TH - Bureau'
      check['1']='TH1 - Ext Veranda'

      --Parcours le Tableau
      for key, valeur in pairs(check) do
      print ('__________ CHECK : '..valeur..' -> '..otherdevices[valeur])
      commandArray[valeur]=otherdevices[valeur]
      end

      --------FIN Renforcement des envois de signal-------------------------------------
      ------------------------------------------------------------------------------
      end

      -------FIN Toutes les 15 minutes
      ------------------------------------------------------------------------

      -------Une fois par heure, verifie l'age des mesures
      if((minutes==0) or (minutes==1) ) then
      local temp={}
      --temperatures
      temp['0']='TH - Bureau'
      temp['1']='TH1 - Ext Veranda'

      --delai au dela duquel on alerte en secondes
      local alerte=3600

      local mail='Alerte sur sonde temperature'
      local trigger=0

      print("__________ suite bis")
      --Parcours le Tableau de stemperatures
      for key, valeur in pairs(temp) do

      s = otherdevices_lastupdate[valeur]
      -- returns a date time like 2013-07-11 17:23:12
      t1 = os.time()
      year = string.sub(s, 1, 4)
      month = string.sub(s, 6, 7)
      day = string.sub(s, 9, 10)
      hour = string.sub(s, 12, 13)
      minutes = string.sub(s, 15, 16)
      seconds = string.sub(s, 18, 19)

      commandArray = {}

      t2 = os.time{year=year, month=month, day=day, hour=hour, min=minutes, sec=seconds}
      difference = (os.difftime (t1, t2))

      print ('CHECK Temperature: '..valeur.. ' -> ' ..otherdevices[valeur].. ' age = '..difference..' secondes')

      if(difference > alerte)then
      mail=mail..'Age de '..valeur .. '-> '..difference..' secondes !!'
      trigger=trigger+1
      end
      end

      print ('__________ '..trigger)
      if(trigger>0)then

      commandArray['SendEmail']='Alerte Age Sonde Temperature #Attention aux sondes suivantes :'..mail..' #test@gmail.com'
      end
      end

      return commandArray

      Et voilà l'erreur que j'obtiens :
      Script event triggered: /home/pi/domoticz/scripts/lua/script_device_agesondes.lua

      Je ne comprends pas pourquoi ... Une idée ?

      Supprimer
  9. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  10. Salut François, désolé je suis bien pris en ce moment.
    VOici le script simplifié qui devrait faire le boulot chez toi.
    J'ai supprimé la partie qui renvois le signal , car a priori ce n'est pas ton objectif.
    Pour ce qui est des détecteurs de fumée, je n'ai pas vraiment de solution, car il s n'émettent pas de signal à aucun moment... c'ets d'ailleurs dommage.

    Pour le script ci dessous, si tu as une erreur du genre :
    Error: /home/pi/domoticz/scripts/lua/script_time_munier.lua:21: bad argument #1 to 'sub' (string expected, got nil)

    C'est que le nom de tes périphériques n'est pas le bon (attention aux majuscules)


    Pour tester toutes les minutes au lieu de toutes les heures, je t'invite à commenter les lignes 2 et 48.



    -------Une fois par heure, verifie l'age des mesures
    if((minutes==0) or (minutes==1) ) then
    local temp={}
    --temperatures
    temp['0']='TH - Bureau'
    temp['1']='TH1 - Ext Veranda'

    --delai au dela duquel on alerte en secondes
    local alerte=3600

    local mail='Alerte sur sonde temperature'
    local trigger=0

    print("Verification des temperatures")
    --Parcours le Tableau de stemperatures
    for key, valeur in pairs(temp) do

    s = otherdevices_lastupdate[valeur]
    -- returns a date time like 2013-07-11 17:23:12
    t1 = os.time()
    year = string.sub(s, 1, 4)
    month = string.sub(s, 6, 7)
    day = string.sub(s, 9, 10)
    hour = string.sub(s, 12, 13)
    minutes = string.sub(s, 15, 16)
    seconds = string.sub(s, 18, 19)

    commandArray = {}

    t2 = os.time{year=year, month=month, day=day, hour=hour, min=minutes, sec=seconds}
    difference = (os.difftime (t1, t2))

    print ('CHECK Temperature: '..valeur.. ' -> ' ..otherdevices[valeur].. ' age = '..difference..' secondes')

    if(difference > alerte)then
    mail=mail..'Age de '..valeur .. '-> '..difference..' secondes !!'
    trigger=trigger+1
    end
    end

    print ('__________ '..trigger)
    if(trigger>0)then

    commandArray['SendEmail']='Alerte Age Sonde Temperature #Attention aux sondes suivantes :'..mail..' #test@gmail.com'
    end

    return commandArray
    end

    RépondreSupprimer
    Réponses
    1. Bonjour et merci,
      je vais tester ça dés ce soir ...
      Merci.

      Supprimer
  11. Bonjour,
    Le script est très bien fait. Je voulais l'aménager à ma sauce en rajoutant une condition : le script ne soit se déclencher qu'après 7h30 et à heures fixes.
    J'ai donc mis :
    if (((hours>=7) or (minutes>=30)) and ((minutes==0) or (minutes==1))) then
    le code...

    mais ça ne fonctionne pas ! Est-ce que je me trompe quelque part ? Qu'est ce que je fais mal ?
    Merci

    RépondreSupprimer
    Réponses
    1. Pour que ton script fonctionne il faut prendre en compte aussi une heure de fin

      if ( ( hours >7 OR (hours==7 and minutes>=30) and ( hours <21) )

      hours>=7 ---> s'il est au moins 8h00
      OU
      hours==7 ----> il est 7hxx ET minutes>=30 -> plus de 7H30

      ET
      hours<21 ---> Qu'il est moins de 21h00 ( pour faire une borne de fin)

      Supprimer
  12. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  13. Salut !

    super script !

    j ai un soucis, il ce lance toute les 1 minutes au lieu de 15 chez moi.

    voici un peu de log :

    2015-02-04 17:15:00 LUA: !*!*!*!*!*!*!*!*!Lancement du check � 17h15
    2015-02-04 17:15:00 LUA: check de tous les materiels rfxcom (sans retour d'etat)
    2015-02-04 17:15:00 LUA: CHECK : Platine Vinyle -> Off
    2015-02-04 17:15:00 LUA: CHECK : TV Loisir -> Off
    2015-02-04 17:15:00 LUA: CHECK : Anti Moustique Parents -> Off
    2015-02-04 17:15:00 LUA: CHECK : Anti Mouche Salon -> Off
    2015-02-04 17:15:00 LUA: CHECK : VMC Studio -> Off
    2015-02-04 17:15:00 LUA: CHECK : Lumiere Coin TV -> Off
    2015-02-04 17:15:00 Error: /home/pi/domoticz/scripts/lua/script_time_check.lua:55: attempt to concatenate field '?' (a nil value)
    2015-02-04 17:15:00 Script event triggered: /home/pi/domoticz/scripts/lua/script_time_check.lua
    2015-02-04 17:15:01 Schedule item started! Type: On Time, DevID: 291, Time: Wed Feb 4 17:15:01 2015

    est ce que cela aide ?

    RépondreSupprimer
    Réponses
    1. Salut,

      Le script est en effet lancé toutes les minutes (tu dois voir "lancement du check à hh:mm" toutes les minutes dans les logs).
      Mais le traitement n'est effectif que toutes les 15 minutes, à cause de la condition :

      if( (minutes==15) .....


      Tu ne dois voir que toutes les 15 minutes le message :
      LUA: check de tous les materiels rfxcom (sans retour d'etat)


      Sinon pour ton problème de script, on dirait que tu as une variable qui renvoie un caractère nul. Souvent la cause est que le peripherique n'existe pas dans domoticz (erreur de frappe , sensibilité à la casse , accents ...). Si tu veux envois moi ton script que je regarde si quelque chose cloche.

      Supprimer
  14. Merci pour ton script que jai hate de mettre en place. La derniere fois que j'ai voulu faire du Lua domoticz ignorait mes fichiers et depuis je me suis rabattu sur blocky. La logique voulant effectivement que s'il y a trop de if else et d'appareils, le rasp sature. Du coup je suis resté sur des commandes simples : à chaque changement d'état les commandes en vigueur sont renvoyées. En gros toutes les minutes.. c'est pas terrible mais ça fonctionne. Donc je ne sais pas si les Lua vont maintenant fonctionner mais ça permettrait de pas encombrer les ondes non stop grâce à des if else imbriquées, ce que blocky ne fait pas. Cela dit avec la logique que j'ai utilisé blocky marche très bien. Pour en revenir à la cohérence c'est super de comparer l'âge des update. Parce que bizarrement le symbole pile dans domoticz affiche vraiment n'importe quoi. 100% pour des piles déjà vider à 50% et Faible pour des piles neuves. Bref, y'a t il pas un bug ou un réglage à faire dans domoticz pour avoir enfin un état des piles correct ?

    RépondreSupprimer
  15. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer