Aller au contenu

Messages recommandés

Posté(e) (modifié)

Dawn & Dusk Manager

 

Dawn & Dusk Manager est composé d'une Scène et d'un Module Virtuel destinés à permettre de déclencher des actions à l'aube et au crépuscule, en enrichissant les simples Sunrise et Sunset proposés nativement puisque sont disponibles les trois phases de l'aube et du crépuscule, ainsi que le midi solaire.

 

* * *

 

INSTALLATION

 

N.B. : Ce programme a été conçu et testé sur la version 4.140 de la HC2.

 

1. Importer le Module Virtuel dans le Home Center 2                        >>>> Panel for Dawn & Dusk Manager v. 1.3.0.vfib

2. Créer une scène en LUA en faisant un copier-coller du code        >>>> Dawn & Dusk Manager v. 1.3.0.lua

3. Démarrer la scène.

 

UTILISATION

 

Le Module Virtuel Dawn & Dusk Panel permet de consulter les différents horaires, la mise à jour intervenant automatiquement tous les jours à 2 h du matin. D'un point de vue technique, c'est lui qui fournit à la scène les données horaires dont elle a besoin pour fonctionner.

 

Pour déclencher l'exécution d'une scène, il suffit d'y insérer le trigger correspondant à l'horaire souhaité dans la zone %% globals :

--[[
%% properties
%% events
%% globals
onAstroDawn         --Déclencheur Aube astronomique
onNauticalDawn      --Déclencheur Aube nautique
onCivilDawn         --Déclencheur Aube civile
onSunrise           --Déclencheur Lever du Soleil
onSolarNoon         --Déclencheur Midi solaire
onSunset            --Déclencheur Coucher du Soleil
onCivilDusk         --Déclencheur Crépuscule civil
onNauticalDusk      --Déclencheur Crépuscule nautique
onAstroDusk         --Déclencheur Crépuscule astronomique
--]]

 

ICONES

 

large.59f328b005f75_DawnDuskPanel.png.87713ed84e080df70cb8792eaf8a81a3.png    large.59f328af5148d_DawnDuskManager.png.efca313177b512b3065235bb5eef5c0b.png

Panel                           Manager

 

 

* * *

 

ASPECTS TECHNIQUES

 

Ce programme est un exemple d'utilisation de la fonction setTimeout() en remplacement de la fonction fibaro:sleep(), l'asynchronicité étant utilisée depuis la version 1.1.0 pour créer deux 'boucles' indépendantes, l'une qui s'occupe de la mise à jour des données du Module Virtuel sur un cycle de 24 h, et l'autre qui s'occupe de mettre à jour à l'heure dite les variables globales faisant office de triggers suivant un cycle déterminé par les données récupérées dans le module virtuel. Avec la version 1.2.0, ces deux 'boucles' fonctionnent de manière enchaînée : lors la 'boucle' principale déclenche le dernier trigger, aucune instruction setTimeout() ne vient la relancer. C'est la 'boucle' de mise à jour qui le fera lors de la prochaine actualisation des horaires.

 

Le code de la scène peut être consulté ci-dessous :

 

Révélation

--[[
%% autostart
%% properties
%% events
%% globals
--]]

--[[
    Dawn & Dusk Manager v. 1.3.0 (2017) by OJC

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
--]]

--[[
    Versions Notes

    1.3.0   Add onSolarNoon trigger
            Code Optimization
    1.2.2   Use api.get to get Virtual Module and Scene ID
            [bug] sendTrigger() called CreateGV(), deleted since v. 1.2.1
    1.2.1   Add Switch Test Mode button in Virtual Device
            Use api.post for Global Variable creation
            Use a Global Variable to store Virtual Device ID
            Use filter {type=virtual_device] in getVirtualIDbyName()
            Add %% autostart
    1.2.0   Improving primary and secondary setTimeout() chaining 
            Adding a Test Mode to run an entire cycle in less than 5 min
    1.1.0   Using secondary setTimeout() cycle to update values in Virtual Device
            More readable format for duration in logInfo
            Secondary checks for Globals Variables and associated Virtual Device
    1.0.0   Initial release
--]]


----------------------------------------------------------------------------------
--                                 LIBRARY                                      --
----------------------------------------------------------------------------------

local lib = {}
function lib:ddigit(a) if (a < 10) then return "0"..a else return a end end
function lib:iif(a,b,c) if (a) then return b else return c end end
function lib:log(a,b) print('<span style="color:' .. ({"YellowGreen", "SkyBlue", "NavajoWhite", "Tomato"})[b or 1] .. ';">' .. a .. '</span>') end


----------------------------------------------------------------------------------
--                          VARIABLES DECLARATIONS                              --
----------------------------------------------------------------------------------

local nextAction, init, updateTime, updateInterval, updateWait = 0, true, "02:00:00", 86400, 300
local logInfo = true

local steps = {
  "AstroDawn",
  "NauticalDawn",
  "CivilDawn",
  "Sunrise",
  "SolarNoon",
  "Sunset",
  "CivilDusk",
  "NauticalDusk",
  "AstroDusk"
  }


----------------------------------------------------------------------------------
--                             GENERAL FUNCTIONS                                --
----------------------------------------------------------------------------------

function setGlobalVariables()
  for _, name in ipairs(steps) do
    if (fibaro:getGlobalValue("on" .. name) == nil) then api.post("/globalVariables", {name="on"..name, isEnum=0}) end
  end
end

function setIdsVariable()
  if (fibaro:getGlobalValue("DawnDuskManagerID") ~= nil) then api.delete("/globalVariables/DawnDuskManagerID") end
  if (fibaro:getGlobalValue("DawnDuskManagerIDs") == nil) then api.post("/globalVariables", {name="DawnDuskManagerIDs", isEnum=0}) end
  local DDID = json.decode(fibaro:getGlobalValue("DawnDuskManagerIDs"))
  if (tostring(DDID) == "" or tostring(DDID) == "null") then DDID = {manager = 0, panel = 0} end
  if (DDID.manager ~= __fibaroSceneId) then
    DDID.manager = __fibaroSceneId
    fibaro:setGlobal("DawnDuskManagerIDs", json.encode(DDID))
  end
end

function getLinkedID(d)
  local DDID = json.decode(fibaro:getGlobalValue("DawnDuskManagerIDs"))
  if (DDID[string.lower(d)] == nil) then
    lib:log("[FATAL ERROR] " .. d .. " was not found !", 4)
    fibaro:abort()
  end
  return DDID[string.lower(d)]
end

function getNow()
  return SwitchTime(os.date("%H:%M:%S"))
end

function getStepTime(step)
  return SwitchTime(fibaro:getValue(getLinkedID("Panel"),"ui.lbl"..steps[step]..".value"))
end

function getUpdateTime()
  return SwitchTime(updateTime)
end

function sendTrigger(trigger)
  if (fibaro:getGlobalValue("on"..steps[trigger]) == nil) then api.post("/globalVariables", {name="on"..steps[trigger], isEnum=0}) end  
  fibaro:setGlobal("on"..steps[trigger],os.date("%Y-%m-%d %H:%M:%S", os.time()))
end

function SwitchTime(t, readable)
  if (readable) then
    local d = ""
    local h = math.floor(t/3600)
    local m = math.floor(t/60 - (h * 60))
    local s = math.floor(t - h * 3600 - m * 60)
    if (h > 0) then d = d .. lib:ddigit(h) .. " h " end
    if (m > 0) then d = d .. lib:iif(h ~= 0, lib:ddigit(m), m) .. " m " end
    if (s > 0) then d = d .. lib:iif(m ~= 0, lib:ddigit(s), s) .. " s" end
    return d
  else
    local h, m, s = string.match(t, "(%d+):(%d+):(%d+)")
    return h * 3600 + m * 60 + s
  end
end


----------------------------------------------------------------------------------
--                              ROOT FUNCTIONS                                  --
----------------------------------------------------------------------------------

function Update()
  local duration = updateInterval - getNow() + getUpdateTime()
  if (init) and (getNow() < getUpdateTime()) then
    duration = getUpdateTime() - getNow()
  end
  if (not init) then
    fibaro:call(getLinkedID("Panel"),"pressButton",10)
    if (logInfo) then lib:log("[INFO] Data is now up to date") end
    if (nextAction == 0) then setTimeout(SceneRun, updateWait * 1000) end
  end
  if (logInfo) then lib:log("[INFO] Next data update in " .. SwitchTime(duration, true)) end
  init = false
  setTimeout(Update,duration * 1000)
end

function SceneRun()
  local duration = 0
  if (init) then
    Update()
    for k, _ in ipairs(steps) do
      if (getStepTime(k) - getNow() > 0) then
        nextAction = k
        duration = getStepTime(nextAction) - getNow()
        break
      end
    end
    if nextAction > 0 then
      if (logInfo) then lib:log("[INFO] Next trigger for " .. steps[nextAction] .. " in " .. SwitchTime(duration, true)) end
      setTimeout(SceneRun,duration * 1000)
    end
  else
    if (nextAction > 0) then
      sendTrigger(nextAction)
      lib:log("[ACTION] Trigger for " .. steps[nextAction] .. " was sent", 2)
    end
    if (nextAction < 9) then
      nextAction = nextAction + 1
      duration = getStepTime(nextAction) - getNow()
      if (logInfo) then lib:log("[INFO] Next trigger for " .. steps[nextAction] .. " in " .. SwitchTime(duration, true)) end
      setTimeout(SceneRun,duration * 1000)
    else
      nextAction = 0
      if (logInfo) then lib:log("[INFO] New cycle will start after next data update...") end
    end
  end
end


----------------------------------------------------------------------------------
--                                    BOOT                                      --
----------------------------------------------------------------------------------

if (fibaro:countScenes() > 1) then fibaro:abort() end

lib:log("Welcome to Dawn & Dusk Manager v. 1.3.0", 3)
setIdsVariable()
setGlobalVariables()
fibaro:call(getLinkedID("Panel"),"pressButton",10)
fibaro:sleep(1000)
SceneRun()

 

 

* * *

 

 

Modifié par OJC
Mise à jour Dawn & Dusk Manager v. 1.3.0
  • Like 1
  • Upvote 4
Posté(e)

Merci :)

 

Je suis en train d'essayer encore plus loin la logique de setTimeout(), mon joujou du moment...

 

Concrètement, lorsque la 'boucle' principale a terminé le dernier trigger de la journée (Crépuscule Astronomique / Astro Dusk), elle s'arrête sans être relancée par un nouveau setTimeout(). C'est la 'boucle' secondaire qui s'occupe de la mise à jour des données qui relancera la 'boucle' principale après la mise à jour des données, avec un délai d'attente de 5 minutes pour ceux qui utilisent toujours un modem 56k :rolleyes: ...

 

C'est en phase de test... et le problème avec ce bouzin, c'est qu'un test complet prend 24 h :2:

Posté(e)

Voilà, ce mode d'enchaînement fonctionne :60:

Et j'ai ajouté un mode de test, qui m'a permis d'aller plus vite dans le test du fonctionnement, et qui vous permettra de voir en moins de 10 minutes un cycle complet.

Posté(e)

Bonsoir !

 

Je continue à améliorer Dawn & Dusk Manager, ce qui me permet surtout d'approfondir le langage Lua que je connaissais pas avant d'acheter la Home Center 2...

 

Au menu de la version 1.2.1, j'ai ajouté un bouton Switch Test Mode sur le Module Virtuel pour pouvoir lancer le mode de test du programme sans avoir à modifier le code. Ce fut l'occasion d'adapter le code de détection de l'ID d'un Module Virtuel à partir de son nom à la détection de l'ID d'une Scène à partir de son nom, pour pouvoir redémarrer la Scène en mode de test après avoir cliqué sur le bouton.

Sur ce point de la communication automatique entre une Scène et le Module Virtuel qui lui est rattaché, le programme stocke désormais l'ID du Module Virtuel dans une Variable Globale pour ne pas avoir à faire la recherche à chaque fois. Il ne la fait plus que si l'ID qu'il trouve dans la Variable Globale ne correspond manifestement pas à ce qu'il cherche.

 

J'ai aussi découvert qu'il y avait bien plus simple et élégant pour créer les Variables Globales, en utilisant api.post. Un grand merci @Steven et son wiki sur ce point ;).

 

Bref... C'est bien sympa, cette centrale domotique... Je ne regrette pas openHab 2, qui m'apparaît avec le recul beaucoup moins souple, en dépit de la rigidité inhérente à un système propriétaire.

  • Upvote 2
Posté(e)

Bonjour,

Beau boulot. :-)
Et api.get pour recupere tes ids, noms :-) et autre:-)

Envoyé de mon Nexus 5X en utilisant Tapatalk

Posté(e) (modifié)

Merci :)

 

Utiliser api.get pour récupérer l'ID du Module Virtuel, est-ce vraiment le plus pertinent vu qu'on ne peut pas filtrer a priori pour limiter les itérations de la boucle qui recherche via le nom ? Quel est le gain ?

 

EDIT = OK, oublie la question... La page /docs est bien pratique, mais n'explique pas comment utiliser l'api directement dans le code sans passer par une connexion http... Mais bon, j'ai trouvé... Bien plus pratique, en effet !

Modifié par OJC
Posté(e)

Pour connaitre l'ID d'un VD lorsque l'on est dans le VD : fibaro:getSelfId()

Pour connaitre l'ID d'une scène lorsque l'on est dans la scène : __fibaroSceneId

 

Pour connaitre l'ID d'un VD depuis une scène ou inversement ... pas le choix, soit l'utilisateur les renseignes soit on fait un api.get() et on va chercher par programmation comme tu l'as très bien fait.

 

Par contre, si je peux me permettre une petite optimisation, je ferais ainsi au lieu de parcourir presque tous les VD :

 

function getDeviceIDbyName(name)
  local devices = api.get("/devices?name="..name)
  if (devices) then
    return devices[1].id
  else
    log("Critical Error : Virtual Device Dawn & Dusk Panel not found !","Tomato",true)
    fibaro:abort()
  end
end

 

Bon boulot.

 

  • Upvote 4
Posté(e)

Oui, c'est la modification que j'ai apportée suite à la suggestion de @pepite (version pas encore publiée)... J'ai un peu galéré pour comprendre comment fonctionne api, mais j'ai fini par trouver en farfouillant à droite et à gauche. Dommage qu'il n'y ait pas un tuto, la page /docs de la HC2 est intéressante mais pas suffisante.

  • Upvote 2
Posté(e) (modifié)

Je découvre ce post : BRAVO pour ce super boulot.

Il y a plein d'idées intéressantes dans ton code.

Je récupère ta fonction de récupération d'un ID par le nom (version améliorée par steven) que je vais probablement intégrer à pas mal de mes scènes :-)

Modifié par Gazous
Posté(e)

Merci :)

Là je suis sur Heating Manager en train de me pencher sur le code de la régulation PID et de me rendre compte que celui que j'ai repiqué est, disons... limité, mais il faut que je reprenne encore celui-ci puisque je découvre toujours plus de possibilités d'optimisations.

 

A ce sujet, je conseille vivement la lecture de Programming in Lua de Roberto Ierusalimschy lui-même (architecte en chef du projet Lua), qui est une mine d'informations. La première édition du bouquin est librement accessible sur leur site.

 

Et il faut que je rajoute onSolarNoon (midi solaire) que j'ai zappé alors qu'il est disponible sur l'API + la gestion des erreurs retournées par l'API puisque j'ai vu comment faire...

Posté(e)

Bon site, bonne info, régulation PID ;-) Toujours plus. Je crois qu'il y avait un VD espagnol qui le faisait, si ca peut t'aider, mais je ne me souviens plus du nom.

 

Attention tout de même, le LUA FIbaro est "bridé"  ;-)

Posté(e) (modifié)

Petite mise à jour pour rajouter le déclencheur onSolarNoon correspondant au midi solaire...

 

J'en ai profité pour changer la logique initiale permettant à la scène de retrouver toute seule le module virtuel sans avoir à renseigner l'ID : chaque élément du programme (scène et module virtuel) se charge lui-même de mettre son ID dans une variable globale. Inutile d'aller le chercher via l'API... Du coup, le nom de la variable globale a changé (mais pas de soucis, la scène s'occupe de supprimer l'ancienne désormais inutile).

Modifié par OJC
  • Like 2
  • 5 ans après...
×
×
  • Créer...