OJC Posté(e) le 27 octobre 2017 Signaler Posté(e) le 27 octobre 2017 (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 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é le 13 novembre 2017 par OJC Mise à jour Dawn & Dusk Manager v. 1.3.0 1 4
OJC Posté(e) le 27 octobre 2017 Auteur Signaler Posté(e) le 27 octobre 2017 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 ... C'est en phase de test... et le problème avec ce bouzin, c'est qu'un test complet prend 24 h
OJC Posté(e) le 28 octobre 2017 Auteur Signaler Posté(e) le 28 octobre 2017 Voilà, ce mode d'enchaînement fonctionne 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.
OJC Posté(e) le 28 octobre 2017 Auteur Signaler Posté(e) le 28 octobre 2017 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. 2
pepite Posté(e) le 29 octobre 2017 Signaler Posté(e) le 29 octobre 2017 Bonjour, Beau boulot. :-) Et api.get pour recupere tes ids, noms :-) et autre:-)Envoyé de mon Nexus 5X en utilisant Tapatalk
OJC Posté(e) le 29 octobre 2017 Auteur Signaler Posté(e) le 29 octobre 2017 (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é le 30 octobre 2017 par OJC
Steven Posté(e) le 30 octobre 2017 Signaler Posté(e) le 30 octobre 2017 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. 4
OJC Posté(e) le 30 octobre 2017 Auteur Signaler Posté(e) le 30 octobre 2017 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. 2
pepite Posté(e) le 30 octobre 2017 Signaler Posté(e) le 30 octobre 2017 (modifié) Salut, Pour avoir pus d'infos sur l'API, tu peux aller sur https://developer.fibaro.com/ Ou encore : ici, MAM78 a fait le boulot de recensement Modifié le 30 octobre 2017 par pepite 1
Gazous Posté(e) le 1 novembre 2017 Signaler Posté(e) le 1 novembre 2017 (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é le 1 novembre 2017 par Gazous
pepite Posté(e) le 2 novembre 2017 Signaler Posté(e) le 2 novembre 2017 Excellentes les modifs d'optimisation ;-) beau et gros boulot
OJC Posté(e) le 2 novembre 2017 Auteur Signaler Posté(e) le 2 novembre 2017 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...
pepite Posté(e) le 2 novembre 2017 Signaler Posté(e) le 2 novembre 2017 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é" ;-)
OJC Posté(e) le 13 novembre 2017 Auteur Signaler Posté(e) le 13 novembre 2017 (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é le 13 novembre 2017 par OJC 2
Johan_14 Posté(e) le 30 mars 2023 Signaler Posté(e) le 30 mars 2023 i tried to use this useful vd / scene, but i think it is not compatible with the latest fw version of the hc2. could you update it?
Messages recommandés