--luasql = require "luasql.sqlite3" --http = require "socket.http"; -------------------------------- ------ USER SETTINGS ------ -------------------------------- -- domoticz domoticzIP = '192.168.1.3' --'127.0.0.1' domoticzPORT = '81' domoticzUSER = '' -- nom d'utilisateur domoticzPSWD = '' -- mot de pass domoticzPASSCODE = '' -- pour interrupteur protégés domoticzURL = 'http://'..domoticzIP..':'..domoticzPORT -- Ne fonctionne plus --require "luasql.sqlite3" --package.preload['luasql.sqlite3'] delai = 300 -- delai avant renvoi consigne maxVariation = 0.15 radiateurs = {'RadiateurManon','RadiateurTheo','RadiateurChambre','RadiateurBureau','RadiateurCuisine','RadiateurMilieu','RadiateurCheminee', 'RadiateurPorteSalon'} function updatenum(dev, value1) print("NRJ updatenum "..dev.." "..tostring(value1)) --local cmd = string.format("%d|0|%d", otherdevices_idx[dev], math.floor(value1)) local cmd = tostring(otherdevices_idx[dev]).."|0|"..tostring(round(value1, 3)) table.insert (commandArray, { ['UpdateDevice'] = cmd } ) end function ternary ( cond , T , F ) if cond then return T else return F end end function isempty(s) return s == nil or s == '' end -- Retourne le jour de la semaine (lundi...dimanche) josdGetJourSemaineTab={[0]="dimanche",[1]="lundi",[2]="mardi",[3]="mercredi",[4]="jeudi",[5]="vendredi",[6]="samedi"} function josdGetJourSemaine() return josdGetJourSemaineTab[tonumber(os.date("%w"))] end -- Retourne le jour de Pâques au format epoch -- annee : année (Integer) dont on désire connaître le jour de Pâques (ex : 2014) -- La fonction n'effectue le calcul que si l'année a changée depuis son dernier appel josdGetJourPaquesAnnee=0 -- Variable globale (année du dernier calcul) pour ne pas recalculer le jour de Pâques à chaque appel josdGetJourPaquesEpochPaque=0 -- Variable globale (jour de Pâques au format epoch) pour ne pas recalculer le jour de Pâques à chaque appel function josdGetJourPaques(annee) if(josdGetJourPaquesAnnee~=annee or josdGetJourPaquesEpochPaque==0) then local a=math.floor(annee/100) local b=math.fmod(annee,100) local c=math.floor((3*(a+25))/4) local d=math.fmod((3*(a+25)),4) local e=math.floor((8*(a+11))/25) local f=math.fmod((5*a+b),19) local g=math.fmod((19*f+c-e),30) local h=math.floor((f+11*g)/319) local j=math.floor((60*(5-d)+b)/4) local k=math.fmod((60*(5-d)+b),4) local m=math.fmod((2*j-k-g+h),7) local n=math.floor((g-h+m+114)/31) local p=math.fmod((g-h+m+114),31) local jour=p+1 local mois=n josdGetJourPaquesAnnee=annee josdGetJourPaquesEpochPaque=os.time{year=annee,month=mois,day=jour,hour=12,min=0} end return josdGetJourPaquesEpochPaque end -- Retourne true si le jour courant est un jour férié -- Le calcul des jours férié n'est fait qu'un fois par an (ou si la Vera reboot) josdJourFerieAnnee=0 -- Variable globale (année du dernier calcul) pour ne pas recalculer le tableau à chaque appel josdJourFerieTab = {} -- Variable globale (tableau des jours fériés) pour ne pas recalculer le tableau à chaque appel function josdJourFerie() local today=os.date("%m-%d") local annee=tonumber(os.date("%Y")) if(annee~=josdJourFerieAnnee) then josdJourFerieAnnee=annee -- Dates fixes josdJourFerieTab["01-01"] = true -- 1er janvier josdJourFerieTab["05-01"] = true -- Fête du travail josdJourFerieTab["05-08"] = true -- Victoire des alliés josdJourFerieTab["07-14"] = true -- Fête nationale josdJourFerieTab["08-15"] = true -- Assomption josdJourFerieTab["11-01"] = true -- Toussaint josdJourFerieTab["11-11"] = true -- Armistice josdJourFerieTab["12-25"] = true -- Noël -- Dates variables local epochPaques=josdGetJourPaques(annee) josdJourFerieTab[os.date("%m-%d",epochPaques)] = true -- Pâques josdJourFerieTab[os.date("%m-%d",epochPaques+24*60*60)] = true -- Lundi de Pâques = Pâques + 1 jour josdJourFerieTab[os.date("%m-%d",epochPaques+24*60*60*39)] = true -- Ascension = Pâques + 39 jours josdJourFerieTab[os.date("%m-%d",epochPaques+24*60*60*49)] = true -- Pentecôte = Ascension + 49 jours end return josdJourFerieTab[today] -- (nldr : Both nil and false make a condition false) end -- Retourne true si le jour courant est un jour chômé (passé à la maison) -- Le calcul effectif n'est fait qu'une fois par jour (ou si la Vera reboot) josdJourChomeToday="NULL" -- Variable globale (date du dernier calcul) pour ne pas recalculer le résultat à chaque appel josdJourChomeReturn=false -- Variable globale (résulat du dernier calcul) pour ne pas recalculer le résultat à chaque appel function josdJourChome() local today=os.date("%Y-%m-%d") if(today~=josdJourChomeToday) then -- Faut-il refaire le calcul ? local jour=josdGetJourSemaine() josdJourChomeToday=today josdJourChomeReturn=(jour=="samedi" or jour=="dimanche" --or jour=="mercredi" or josdJourFerie()) -- or josdJourVacances()) end return josdJourChomeReturn end -- ------------------------------------------------------------------------------ -- transforme une heure en minute -- ------------------------------------------------------------------------------ function heureEnMinute(t1) s1 = split(t1, ":") h1 = tonumber(s1[1]) m1 = tonumber(s1[2]) return h1 * 60 + m1 end -- ------------------------------------------------------------------------------ -- compare 2 heures -- ------------------------------------------------------------------------------ function greater(t1, t2) return greater2(t1, t2, 0) end -- ------------------------------- -- retourne vrai si t1 > t2 + m -- ------------------------------- function greater2(t1, t2, m) s1 = split(t1, ":") s2 = split(t2, ":") h1 = tonumber(s1[1]) h2 = tonumber(s2[1]) m1 = tonumber(s1[2]) m2 = tonumber(s2[2]) if h1 * 60 + m1 > h2 * 60 + m2 + m then return true end --if h1 == h2 and m1 > m2 then return true end return false end -- ------------------------------------------------------------------------------ -- Function sleep n seconds -- ------------------------------------------------------------------------------ function sleep(n) os.execute("sleep " .. tonumber(n)) end -- ------------------------------------------------------------------------------ -- Truncate number to nb decimal -- ------------------------------------------------------------------------------ function round(num, idp) local mult = 10^(idp or 0) return math.floor(num * mult + 0.5) / mult end -- ------------------------------------------------------------------------------ -- Get the temperature from a device which contains humidity and pressure too -- ------------------------------------------------------------------------------ function getTemperatureFromDevice(device) return tonumber(split(otherdevices_svalues[device],";")[1]) end -- ------------------------------------------------------------------------------ -- Get the humidity from a device which contains temperature and pressure too -- ------------------------------------------------------------------------------ function getHumidityFromDevice(device) return tonumber(split(otherdevices_svalues[device],";")[2]) end -- Test if string is empty or null function isempty(s) return s == nil or s == '' end -- ------------------------------------------------------------------------------ -- Permet de couper une chaine de caracteres avec separateur en plusieurs chaines -- ------------------------------------------------------------------------------ function split(str, pat) local t = {} -- NOTE: use {n = 0} in Lua-5.0 local fpat = "(.-)" .. pat local last_end = 1 local s, e, cap = str:find(fpat, 1) while s do if s ~= 1 or cap ~= "" then table.insert(t,cap) end last_end = e+1 s, e, cap = str:find(fpat, last_end) end if last_end <= #str then cap = str:sub(last_end) table.insert(t, cap) end return t end -- ------------------------------------------------------------------------------ -- Met à jour un switch si nécessaire (déjà à la bonne position) -- ------------------------------------------------------------------------------ function switchIfNeeded( switch, value ) if switch == "RadiateurGrenier" then local currentValue = tonumber(otherdevices_svalues["RadiateurGrenier"]) if value == "On" then if (otherdevices[switch] == 'Off') then switchOn(switch) -- commandArray[switch]=value end switchOnDimmable(switch, currentValue + 1) else if currentValue <= 1 then switchOnDimmable(switch, 0) else if (otherdevices[switch] ~= 'Off') then switchOnDimmable(switch, currentValue - 1) end end end print(switch .. ' ' ..value..' current='..currentValue..' '..otherdevices[switch]) else if (otherdevices[switch] == value and not string.match(switch, "Radiateur")) then -- and lastUpdateOfDevice(switch) < 300) then debug(switch..' already '..value) else if value == "On" then debug(switch..' '..value) switchOn(switch) -- commandArray[switch]=value else switchOff(switch) debug(switch..' '..value) end end --sleep(1) end end -- ------------------------------------------------------------------------------ -- Met à jour un switch si nécessaire (déjà à la bonne position) -- ------------------------------------------------------------------------------ function switchLightsIfNeeded( value ) switchIfNeeded('Lampe_Halogene', value) sleep(1) switchIfNeeded('Lampe_Buffet', value) sleep(1) switchIfNeeded('Lampe_Tele', value) sleep(1) switchIfNeeded('Lampe_Led', value) end -- ------------------------------------------------------------------------------ -- Function to get result of HTTP request -- ------------------------------------------------------------------------------ function os.capture(cmd, raw) local f = assert(io.popen(cmd, 'r')) local s = assert(f:read('*a')) f:close() if raw then return s end s = string.gsub(s, '^%s+', '') s = string.gsub(s, '%s+$', '') s = string.gsub(s, '[\n\r]+', ' ') return s end -- ------------------------------------------------------------------------------ -- Function to get the last update of device from now in seconds -- ------------------------------------------------------------------------------ function lastUpdateOfDevice(switch) t1 = os.time() s = otherdevices_lastupdate[switch] difference = 0 if (s == nil) then debug("Erreur détermination lastUpdate "..switch) else -- returns a date time like 2013-07-11 17:23:12 year = string.sub(s, 1, 4) month = string.sub(s, 6, 7) day = string.sub(s, 9, 10) hour = string.sub(s, 12, 13) minu = string.sub(s, 15, 16) seconds = string.sub(s, 18, 19) t2 = os.time{year=year, month=month, day=day, hour=hour, min=minu, sec=seconds} difference = (os.difftime (t1, t2)) --debug(switch..tonumber(difference)) end return difference end -- --------------------------------------------------- -- Function debug -- --------------------------------------------------- function debug(string) if otherdevices['Debug'] == 'On' and string ~= nil then print("DEBUG "..string) end end -- --------------------------------------------------- -- Switch off radiateur SIC ! -- --------------------------------------------------- function switchOffRadiateur(switch) debug("Radiateur "..switch.." Off.") if (otherdevices[switch] == 'On' or lastUpdateOfDevice(switch) > delai) then --commandArray[switch]='Off' switchOff(switch) end --switchIfNeeded(switch, 'Off') -- Inversion pour les radiateurs end -- --------------------------------------------------- -- Switch on radiateur SIC ! -- --------------------------------------------------- function switchOnRadiateur(switch) debug("Radiateur "..switch.." On.") if (otherdevices[switch] == 'Off' or lastUpdateOfDevice(switch) > delai) then --commandArray[switch]='On' switchOn(switch) end --switchIfNeeded(switch, 'On') -- Inversion pour les radiateurs end -- --------------------------------------------------- -- Get a map of switch values -- --------------------------------------------------- function getValuesInTab(switch) tmp = otherdevices_svalues[switch] debug('temperature lue pour '..switch..' '..tmp) tab = split(tmp, ";") return tab end -- --------------------------------------------------- -- Get the last values of a switch (inverse order) -- --------------------------------------------------- function getEvolutionFromSwitch(switch) env = luasql.sqlite3() conn = env:connect("/opt/domoticz/domoticz.db") cursor = assert(conn:execute("select temperature,date from temperature,DeviceStatus where DeviceStatus.name = '"..switch.."' and devicerowid = DeviceStatus.id order by date desc limit 10")) row = {} while cursor:fetch(row) do debug(table.concat(row, '|')) end cursor:close() conn:close() env:close() end -- --------------------------------------------------- -- Execute a query from domoticz db and returns rows -- --------------------------------------------------- function executeQuery( switch, query) q = "/usr/bin/sqlite3 -list /opt/domoticz/domoticz.db '" .. query .. "' 2>&1" --debug(q) --debug("avant execute"..q) local rHandle = io.popen( "/usr/bin/sqlite3 -list /opt/domoticz/domoticz.db '" .. query .. "' 2>&1" ) local aRows = {} ; local aRow = {}; local iRow = 1 ; local iColumn = 1 --debug("avant while true") while true do local sResultRow = rHandle:read( '*line' ) if ( sResultRow == nil ) then break end debug(sResultRow) for sColumn in sResultRow:gmatch( "([^|]+)" ) do aRow[iColumn] = sColumn debug(sColumn) iColumn = iColumn + 1 end aRows[iRow] = aRow iRow = iRow + 1 iColumn = 1 aRow = {} end --debug("Fin while") local oReturn = { rHandle:close() } if ( tonumber( oReturn[3] ) ~= 0 ) then -- check return code (0 = ok) debug( sQuery_, 4 ) if ( iRow > 1 ) then debug( aRows[iRow - 1][1], 4 ) else debug( 'Error while executing query.', 4 ) end return false else debug( sQuery_, 1 ) return true, aRows, iRow - 1 end --debug("Fin execute") end function executeQueryAndGetValues( switch ) local bSuccess, aRows, iCount = executeQuery( switch ) if ( bSuccess ) then if ( iCount > 0 ) then return table.unpack( aRows[1] ) else return nil end else return false end end -- -------------------------------------------------------- -- Détermine la variation de température à prendre en compte -- -------------------------------------------------------- function getVariation( switch ) local time = 10 variation = variationTemp(switch) debug("Variation 10' ="..tostring(variation)) if variation == 0 then time = 20 variation = variationTemp2(switch, 20) debug("Variation 20' ="..tostring(variation)) end if variation == 0 then time = 30 variation = variationTemp2(switch, 30) debug("Variation 30' ="..tostring(variation)) end return variation, time end -- -------------------------------------------------------- -- calcul la variation de température depuis n * 5 minutes -- -------------------------------------------------------- function variationTemp(switch) return variationTemp2(switch, 10) end function variationTemp2(switch, time) local limit = time / 5 + 1 query = 'select temperature,date from temperature,DeviceStatus where DeviceStatus.Name = "'..switch..'" and devicerowid = DeviceStatus.id order by date desc limit '..tostring(limit) --debug("Avant query "..tostring(limit).." "..switch) local bSuccess, aRows, iCount = executeQuery(switch, query) --debug("Retour executeQuery"..tostring(bSuccess).." icount="..iCount) local firstTemp = 0 local lastTemp = 0 if (bSuccess and iCount > 0) then firstTemp = tonumber(aRows[1][1]) lastTemp = tonumber(aRows[iCount][1]) for i = 1, iCount do local t = tonumber(aRows[i][1]) --debug("RETOUR Query"..tostring(aRows[i][1])) end end return firstTemp - lastTemp end -- --------------------------------------------------- -- calcul la variation de pression depuis ?? minutes -- Basse pression == < à 1013mbar (pluie) -- Haute pression == > à 1013mbar (beau temps) -- Diminution rapide ==> vent et mauvais temps -- superieur à 1020 ==> beau temps calme -- Tendance -- Tendance du baromètre hPa par heure Évolution du temps -- montée 0,25 à 0,5 venue d'une haute pression (à long terme) -- montée 1 à 2 moyenne pression (à court terme) -- descente 0,25 à 0,5 venue d'une basse pression (à long terme) -- descente 1 à 2 tempête ; en été, orage. -- Conversion -- 10^5 Pa = 1 bar = 1000 mbar = 10,2 mCE (mètres de colonne d'eau) = 0,987 atm. -- --------------------------------------------------- function variationPressure(switch, time) -- time en minute local limit = time / 5 + 1 -- SELECT barometer,date FROM temperature where devicerowid = 94 and date like "2016-02-24 %" order by date desc limit 25 query = 'select barometer,date from temperature,DeviceStatus where DeviceStatus.Name = "'..switch..'" and devicerowid = DeviceStatus.id order by date desc limit '..tostring(limit) local bSuccess, aRows, iCount = executeQuery(switch, query) debug("Retour executeQuery"..tostring(bSuccess).." icount="..iCount) local firstTemp = 0 local lastTemp = 0 if (bSuccess and iCount > 0) then firstTemp = tonumber(aRows[1][1]) lastTemp = tonumber(aRows[iCount][1]) for i = 1, iCount do local t = tonumber(aRows[i][1]) debug("RETOUR Query"..tostring(aRows[i][1])) end end return firstTemp - lastTemp end -- ----------------- -- Multiple update -- ----------------- function update(idx, value1, value2) local cmd = string.format("%d|0|%.2f;%.2f", idx, value1, value2) table.insert (commandArray, { ['UpdateDevice'] = cmd } ) end function updateCmd(idx, value1) local cmd = idx.."|"..value1 table.insert (commandArray, { ['UpdateDevice'] = cmd } ) end -- PERF -- /!\ require Domoticz v3.5776 and after /!\ curl = '/usr/bin/curl -m 3 ' -- don't forgot the final space domoticzIP = 'localhost' domoticzPORT = '81' domoticzURL = 'http://'..domoticzIP..':'..domoticzPORT -- switch On a device and set level if dimmmable function switchOnDimmable(device, level) print(device..' '..tostring(level)) os.execute(curl..'"'..domoticzURL..'/json.htm?type=command¶m=switchlight&idx='..otherdevices_idx[device]..'&switchcmd=Set%20Level&level='..level..'" &') end function switchOn(device) if (otherdevices_idx[device] ~= nil) then os.execute(curl..'"'..domoticzURL..'/json.htm?type=command¶m=switchlight&idx='..otherdevices_idx[device]..'&switchcmd=On" &') end end -- switch On a devive for x seconds -- non compatible avec le script sendTwice function switchOnFor(device, secs) if (otherdevices_idx[device] ~= nil) then switchOn(device) commandArray[device] = "Off AFTER "..secs end end -- switch Off a device function switchOff(device) if (otherdevices_idx[device] ~= nil) then os.execute(curl..'"'..domoticzURL..'/json.htm?type=command¶m=switchlight&idx='..otherdevices_idx[device]..'&switchcmd=Off" &') end end -- Toggle a device function switch(device) if (otherdevices_idx[device] ~= nil) then os.execute(curl..'"'..domoticzURL..'/json.htm?type=command¶m=switchlight&idx='..otherdevices_idx[device]..'&switchcmd=Toggle" &') end end -- switch On a group or scene function groupOn(device) if (otherdevices_idx[device] ~= nil) then os.execute(curl..'"'..domoticzURL..'/json.htm?type=command¶m=switchscene&idx='..otherdevices_scenesgroups_idx[device]..'&switchcmd=On" &') end end -- switch Off a group function groupOff(device) if (otherdevices_idx[device] ~= nil) then os.execute(curl..'"'..domoticzURL..'/json.htm?type=command¶m=switchscene&idx='..otherdevices_scenesgroups_idx[device]..'&switchcmd=Off" &') end end -- Retourne le nombre de minutes avant la nuit function whenDark() local hour = tonumber(os.date("%H")); local min = tonumber(os.date("%M")); local now = 60 * hour + min local sunsetMin = heureEnMinute(uservariables["Couche"]) when_dark = sunsetMin - now print("####### Dark in "..tostring(when_dark)) return when_dark end -- Retourne le nombre de minutes avant le jour function whenLight() local hour = tonumber(os.date("%H")); local min = tonumber(os.date("%M")); local now = 60 * hour + min local sunriseMin = heureEnMinute(uservariables["Lever"]) when_light = 1440 - (sunriseMin - now) debug("####### Light in "..tostring(when_light)) return when_light end function actionSomfy(name, action) debug(name..' '..action) local url = "mosquitto_pub -h www.maqiatto.com -u jerome.delacotte@gmail.com -P setaou -I clientId-9FG7vBimhk -t jerome.delacotte@gmail.com/"..name.."/volet -m "..action debug(url) os.execute(url) end -- update an existing variable function updateVar(name,value) local api = '/json.htm?type=command¶m=updateuservariable' local name = '&vname='..url_encode(name) local vtype = '&vtype=2' local value = '&vvalue='..url_encode(value) api = api..name..vtype..value url = curl..'-u '..domoticzUSER..':'..domoticzPSWD..' "'..domoticzURL..api..'" &' debug(url) os.execute(url) end function url_encode(str) if (str) then str = string.gsub (str, "\n", "\r\n") str = string.gsub (str, "([^%w %-%_%.%~])", function (c) return string.format ("%%%02X", string.byte(c)) end) str = string.gsub (str, " ", "+") end return str end -- recursive search value in table function searchValueInTable(value, tbl) for k, v in pairs(tbl) do if v == value then return k -- Retourne la clé si la valeur est trouvée elseif type(v) == "table" then local result = searchValueInTable(value, v) -- Appel récursif si la valeur est un tableau if result then return k .. "." .. result -- Retourne la clé trouvée concaténée avec la clé actuelle end end end return nil -- Retourne nil si la valeur n'est pas trouvée end -- Recursive search key in array function searchKeyInTable(key, tbl) for k, v in pairs(tbl) do if k == key then return v elseif type(v) == "table" then local result = searchKeyInTable(key, v) if result then return result end end end return nil end function printTableAsTree(tbl, indent) indent = indent or "" for key, value in pairs(tbl) do if type(value) == "table" then print(indent .. tostring(key) .. ":") printTableAsTree(value, indent .. "___") else print(indent .. tostring(key) .. ": " .. tostring(value)) end end end function sendNotification(message) commandArray['SendNotification'] = message end function getJSONContent(url) local cmd = 'curl -s --connect-timeout 2 -m 5 "'..url..'"' --local cmd = 'curl "'..url..'"' local jsonContent = os.capture(cmd, true) return jsonContent end function decodeJSON(jsonContent) json = (loadfile "/opt/domoticz/scripts/lua/JSON.lua")() return json:decode(jsonContent) end function updateDeviceValue(deviceId, svalue) local url = domoticzURL..'/json.htm?type=command¶m=udevice&idx='..deviceId..'&svalue='..svalue print(url) result = os.execute(curl..'"'..url..'" &') print(result) end function getExternalTempRef(device_temp) local val local val2 val, val2 = otherdevices_svalues[device_temp]:match("([^;]+);([^;]+)") print("Volets "..device_temp.." "..otherdevices["Exterieur_Cellier"].." "..val.. otherdevices['VoletCuisine'].." "..otherdevices['VoletSalonTele'].." "..otherdevices['VoletPorteSalon']) return tonumber(val) end function convertirAngle(angle) local pointsCardinaux = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "N" } local index = math.floor(((angle + 22.5) % 360) / 45) + 1 return pointsCardinaux[index] end