viewtopic.php?f=59&t=21914
Plans in dzVents version 2.5
I wrote a script that easily allows you to add plans, and by the way they are kept in one place, in this script.
In addition, thanks to dzVents, the script can do much more than traditional plans.
Now You can trigger not only on time but also on device and much more. See examples in script
The script is with comments and in English (although is not my native, I apologizes for any linguistic mistakes).
Functionality of version 2.5.
- support for switches and selectors (for now).
- 5 functions: switchOn, switchOff, dimTo, deviceOn, deviceOff
- 3 special action: forSec, repeatAfterSec, statusDevice
- 5 properties: name, description, dontLog, notify, fun
For syntax check inline doc and example plan on begin of script
(note: complete script is in file to download plans_2_5.txt)
A very important warning.
Only one script can be active at a time.
If you have more scripts with "Plans" only the last one in the list is still being processed by dzVents. If it results from it that an event is activated, it is executed as many times as the number of copies of the script you have (no matter if they have the same plans).
I am looking for a cause and solution to the problem.
But of course, the single works well, as reported by a colleague testing in his home automation. (Thanks again, Tom for inspirations to write this scrip and tests).
Plans example:
Code: Select all
local plans = {
{'test11', switchOn = 'at 11:00'}, -- switch test11 ON when time is 11:00
{'test12', switchOn ={'at 12:00','at nighttime'}, switchOff='at daytime'}, -- switch ON test12 in to times conditions. also have function switch OFF
{'test13', switchOff ={'at 13:00','at 13:10'}, fun='checkFirst().aferSec(5).forSec(10)'}, -- switch test13 OFF with add metods checkFirst().... when times conditions.
{'test14', switchOff ='at 14:00', description=' description for write log'}, -- ... with write to log ' description for write log'
{'test16', switchOff ='at sunset ', description=' description for write log and notify', notify=true}, -- ... with notify
{'test17', switchOff ='10 minutes before sunrise', description=' description for only notify', notify=true, dontLog=true}, -- ... with notify without write to log
{'test20', dimTo = {10,'at daytime'}}, -- switch selector test21 to lvl = 10 (Level1) when time is 21:00
{'test20', dimTo = {'Level2','at 22:00', 'at 22:30'}}, -- switch selector test20 to lvl = Level2 when time is 22:00 and 22;30
{'test20', dimTo = {'Level3','at 23:00'}, fun='afterSec(5)'}, -- switch selector test20 to lvl = Level3 when time is 23:00 after 5 second from trigger
{'test20', dimTo = {'Off','at nighttime'}, fun='afterSec(1)', notify=true}, -- switch selector test20 to lvl = Off when time is 00:00 after 5 second from trigger
{'test31', deviceOn = 'testA'}, -- switch test31 ON when device testA trigger
{'test32', deviceOn ={'testB','Locked'}}, -- switch test32 ON when device testB trigger and its state = 'Locked'
{'test33', deviceOn ={'testC','Locked','at daytime'}}, -- switch test33 ON when device testC trigger and its state = 'Locked' and if is daytime
{'test34', deviceOff ='testD', fun='checkFirst().aferSec(5).forSec(10)'}, -- switch test34 OFF with add metods checkFirst().... when device testD trigger
{'test35', deviceOff ='testE', description=' text for write log'}, -- ... with write to log text =' text for write log'
{'test36', deviceOff ='testF', description=' description for write log and notify', notify=true}, -- ... with write to log text =' text for write log' and send notify PUSHOVER
{'test37', deviceOff ='testG', notify=true, dontLog=true}, -- .., without write to log and send notify PUSHOVER
{'test38', deviceOff ='testH', statusDevice={'testS','Open'}}, -- switch test38 OFF when trigger device testH and device testS has state Open
{'test39', deviceOff ={'testI','at daytime'}, statusDevice={'testS','Open'}}, -- switch test39 OFF when trigger device testI (only at daytime) and device testS has state Open
{'test40', deviceOn ={'testJ','at daytime'}, statusDevice={'testS','Open'}, forSec=5}, -- switch test40 ON for 5 sec when trigger device testJ (only at daytime) and device testS has state Open
}
Code: Select all
--==================================================================================
-- PLANS in dzVents
-- *********************************************************************************
-- dzVents have to be version 2.3 and newer
-- First element of plan must be "device" which have to be change (On, Off, dimTo) from domoticz eg 'test1'
-- Script operate on something called FUNCTION, PROPERTY, ACTION. List of each type below
-- Only one instance of the function and property and action allowed on the plan,
-- Action and property are not obligatory but at least one function must occur
-- Specjal action fun= can handle any allowed string function in dzVents see eg
-- The order function and property does not matter.
-- Function have to have list of time conditions
-- List of time conditions can be any time conditions from dzVents wiki (reference eg list on end of this script).
-- List of time conditions if is only one condition can be just string if is more condition have to be array
-- The device must be able to operate functions
-- Write to log domoticz (FORCE) when LOGGING=true and trigger device and plan dont have properies dontLog=true
-- *********************************************************************************
-- FUNCTION trigger by time :
-- > switchOn: switchOn{list of time conditions} - ON switch with checkFirst eg: switchOn='at 11:00' or for list switchOn={'at 01:33', 'at sunrise'}
-- > switchOff: switchOff{list of time conditions} - OFF switch with checkFirst eg: switchOff = {'at 01:33', 'at sunrise'}
-- > dimTo: dimTo{percentage, {list of time conditions} - Switch a dimming device on and/or dim to the specified level. Level can be number or name of level - string
-- eg dimTo={20, {'at 01:33', 'at sunrise'}}
-- eg dimTo={'Level1', {'at 01:33', 'at sunrise'}}
-- FUNCTION trigger by device:
-- > deviceOn: deviceOn{device,state,list of time conditions} - ON switch when trigger device with state eg: deviceOn = {'test4,'Locked','at 01:33', 'at sunrise'}
-- > deviceOff: deviceOff{device,state,list of time conditions} - OFF switch when trigger device with state eg: deviceOn = {'test4,'Locked','at 01:33', 'at sunrise'}
-- PROPERTY: (all property is not obligatory)
-- > name: short name used in write to log end and in header notify eg name = ' It is name '
-- > description: used in write to log end and body notify eg description = ' It is description '
-- > notify: notify only if present and is true to PUSHOVER (defined in function fun_notify() in script. eg. notify = true
-- > dontLog: if true script dont write to log action. If not present or is false script will write to log when LOGGING = true eg. donLog = true
--- ACTION:
-- > statusDevice: device to check status other device instead of checkFirst() eg statusDevice={'test4','Unlocked'}, in this case trigger only if state 'test4' == 'Unlocked'
-- > repeatAfterSec - repeat function eg. repeatAfterSec={1,3}
-- > forSec - work only with SwitchOn and switchOff eg forSec=10
-- > fun: can be any string of methods controlling a delay or a duration for switches eg fun='afterSec(5).forMin(2)'
--==================================================================================
local plans = {
{'test11', switchOn = 'at 11:00'}, -- switch test11 ON when time is 11:00
{'test12', switchOn ={'at 12:00','at nighttime'}, switchOff='at daytime'}, -- switch ON test12 in to times conditions. also switch OFF
{'test13', switchOff ={'at 13:00','at 13:10'}, fun='checkFirst().aferSec(5).forSec(10)'}, -- switch test13 OFF with add metods checkFirst().... when times conditions.
{'test14', switchOff ='at 14:00', description=' description for write log'}, -- ... with write to log ' description for write log'
{'test16', switchOff ='testAF with notify', description=' description for write log and notify', notify=true}, -- ... with notify
{'test17', switchOff ='testAG with notify', description=' description for only notify', notify=true, dontLog=true}, -- ... with notify without write to log
{'test20', dimTo = {10,'at daytime'}}, -- switch selector test21 to lvl = 10 (Level1) when time is 21:00
{'test20', dimTo = {'Level2','at 22:00', 'at 22:30'}}, -- switch selector test20 to lvl = Level2 when time is 22:00 and 22;30
{'test20', dimTo = {'Level3','at 23:00'}, fun='afterSec(5)'}, -- switch selector test20 to lvl = Level3 when time is 23:00 after 5 second from trigger
{'test20', dimTo = {'Off','at nighttime'}, fun='afterSec(1)', notify=true}, -- switch selector test20 to lvl = Off when time is 00:00 after 5 second from trigger
{'test31', deviceOn = 'testA'}, -- switch test31 ON when device testA trigger
{'test32', deviceOn ={'testB','Locked'}}, -- switch test32 ON when device testB trigger and its state = 'Locked'
{'test33', deviceOn ={'testC','Locked','at daytime'}}, -- switch test33 ON when device testC trigger and its state = 'Locked' and if is daytime
{'test34', deviceOff ='testD', fun='checkFirst().aferSec(5).forSec(10)'}, -- switch test34 OFF with add metods checkFirst().... when device testD trigger
{'test35', deviceOff ='testE', description=' text for write log'}, -- ... with write to log text =' text for write log'
{'test36', deviceOff ='testF', description=' description for write log and notify', notify=true}, -- ... with write to log text =' text for write log' and send notify PUSHOVER
{'test37', deviceOff ='testG', notify=true, dontLog=true}, -- .., without write to log and send notify PUSHOVER
{'test38', deviceOff ='testH', statusDevice={'testS','Open'}}, -- switch test38 OFF when trigger device testH and device testS has state Open
{'test39', deviceOff ={'testI','at daytime'}, statusDevice={'testS','Open'}}, -- switch test39 OFF when trigger device testI (only at daytime) and device testS has state Open
{'test40', deviceOn ={'testJ','at daytime'}, statusDevice={'testS','Open'}, forSec=5}, -- switch test40 ON for 5 sec when trigger device testJ (only at daytime) and device testS has state Open
}
-------------------------------------------------------------------------------------
local version = '2.5' -- current version of script
local LOGGING = true -- true or false LOGGING info to domoticz log when trigger and plan dont have properies dontLog=true
local NOTIFY_TIME = 'at 07:00-22:00' -- matchesRule when notify will be send (default used - PUSHOVER)
local NOTIFY_TEXT = 'Plany: ' -- main text for notify
local LOG_TEXT = 'Plany: ' -- main text for notify
local TIME_WORK = 'every minute' -- when script work. and how offen!!! eg 'every minute between 01:00 and 23:45'
local TRIGGER_DEVICE ='*' -- used for trigger on devices for function deviceOn, deviceOff * wild-card mean used for All devices. if You dont needed change to TRIGGER_DEVICE =''
-- PIR_* or *_PIR. eg is use in devices = {TRIGGER_DEVICE}
local DEBUG = false -- used only for debbuging
-------------------------------------------------------------------------------------
--=================================
-- helpers variable and function
--=================================
local switchOn ='switchOn'
local switchOff ='switchOff'
local dimTo ='dimTo'
local deviceOn ='deviceOn'
local deviceOff ='deviceOff'
local functions_time = {switchOn,switchOff, dimTo}
local functions_device = {deviceOn,deviceOff}
function fun_notify(domoticz,plan,device, fun,text)
if (plan.notify ~= nil and plan.notify == true) and domoticz.time.matchesRule(NOTIFY_TIME) then
local heding = NOTIFY_TEXT..' '..device
domoticz.notify(heding ,text, domoticz.PRIORITY_HIGH, domoticz.SOUND_PUSHOVER, '', domoticz.NSS_PUSHOVER) -- notify system default - PUSHOVER
end
end
function fun_device(domoticz,device)
for i,plan in pairs(plans) do
for fun, params in pairs(plan) do
local trigger_device = device
if isFun(domoticz,fun,functions_device) then
if isDeviceTrigger(domoticz,plan,device,fun) then
fun_acction(domoticz,plan,fun,trigger_device)
end
end
end
end
return false
end
function fun_time(domoticz)
for p , plan in ipairs(plans) do
for fun, params in pairs(plan) do
if isFun(domoticz,fun,functions_time) then fun_acction(domoticz,plan,fun) end
end
end
end
function fun_acction(domoticz,plan, fun, trigger_device)
local log_text = nil
if isMatchRule(domoticz,plan,fun) then
local device = plan[1]
log_text = fun_switch(domoticz,plan,device,fun,trigger_device)
if log_text ~= nil then fun_notify(domoticz,plan,device,fun, log_text, trigger_device) end
end
return log_text
end
function isDeviceTrigger(domoticz,plan,trigger_device,fun)
local ret = false
local name= nil
local state = nil
if fun==deviceOn then
if type(plan.deviceOn) == 'string' then name = plan.deviceOn else name = plan.deviceOn[1] end
if plan.deviceOn[2]~= nil and plan.deviceOn[2]~= '' then state = plan.deviceOn[2] end
end
if fun==deviceOff then
if type(plan.deviceOff) == 'string' then name = plan.deviceOff else name = plan.deviceOff[1] end
if plan.deviceOff[2]~= nil and plan.deviceOff[2]~= '' then state = plan.deviceOff[2] end
end
if name == trigger_device.name and (state == nil or state == trigger_device.state) then
return true
else
return false
end
end
function fun_switch(domoticz,plan,device,fun, trigger_device)
local log_text = nil
local param = ''
if plan.statusDevice ~= nil then
if domoticz.devices(plan.statusDevice[1]).state == plan.statusDevice[2] then
prop = 'statusDevice: '..tostring(plan.statusDevice[1])..' = '.. tostring(plan.statusDevice[2])
else
return log_text --- without trigger cos statusDevice is not true.
end
end
if fun == deviceOn then fun=switchOn end
if fun == deviceOff then fun=switchOff end
if fun == dimTo then param = getLvl(domoticz,device,plan.dimTo[1]) end
local forRun="domoticz.devices('"..device.."')."..fun..'('..param..')'
if plan.fun ~= nil then forRun=forRun..'.'..plan.fun end
if plan.repeatAfterSec ~= nil then
param ='repeatAfterSec('..tostring(plan.repeatAfterSec[1])..','..tostring(plan.repeatAfterSec[2])..')'
forRun=forRun..'.'..param
end
if plan.forSec ~= nil then
param = 'forSec('..tostring(plan.forSec)..')'
forRun=forRun..'.'..param
end
runFunDomoticz(domoticz,forRun)
log_text = writeLogTrigger(domoticz,plan,device,fun,param)
return log_text
end
function runFunDomoticz(dz,forRun)
domoticz=dz
debug(domoticz,forRun)
fun=load(forRun, chunkname)
fun(dz)
end
--=================================
-- other helpers function
--=================================
function writeLogTrigger(domoticz,plan,device,fun,text)
local log_text = ''
if LOGGING and (plan.dontLog==nil or plan.dontLog==false) then
if plan.description ~= nil then
log_text = log_text..' - '..plan.description
else
if plan.name ~= nil then log_text =log_text..plan.name end
if text ~= '' then text='.'..text end
log_text = log_text ..' -> '..device ..'.'..fun..'()'..text
end
if plan.dontLog == nil or plan.dontLog == false then
domoticz.log(LOG_TEXT..log_text, domoticz.LOG_FORCE)
end
end
return log_text
end
function isMatchRule(domoticz,plan,fun)
local ret = false
if fun == switchOn then ret = isTimeMatchRule(domoticz, plan.switchOn, fun, plan) end
if fun == switchOff then ret = isTimeMatchRule(domoticz, plan.switchOff, fun, plan ) end
if fun == dimTo then ret = isTimeMatchRule(domoticz, plan.dimTo, fun, plan) end
if fun == deviceOn then ret = plan.deviceOn[3]==nil or isTimeMatchRule(domoticz, plan.deviceOn, fun, plan) end
if fun == deviceOff then ret = plan.deviceOff[3]==nil or isTimeMatchRule(domoticz, plan.deviceOff, fun, plan ) end
return ret
end
function isTimeMatchRule(domoticz,time_rule, fun, plan)
if type(time_rule) == 'string' then
return domoticz.time.matchesRule(time_rule)
else
for j, value in ipairs(time_rule) do
if type(value) == 'string' then
if domoticz.time.matchesRule(value) then return true end
end
if type(value) == 'table' then
for k, value2 in ipairs(value) do
if type(value2) == 'string' then
if domoticz.time.matchesRule(value2) then return true end
end
end
end
end
end
return false
end
function getLvl(domoticz,device,lvl)
local ret = ''
if type(lvl) == 'number' then ret= lvl end
if type(lvl) == 'string' then
if type(domoticz.devices(device).levelNames) == 'table' then
for i,l in ipairs(domoticz.devices(device).levelNames) do
if l == lvl then
ret = (i-1)*10 -- we calculate value level for name lvl in selector.
end
end
else
domoticz.log(LOG_TEXT..device..' should be type selector',domoticz.LOG_ERROR)
end
end
return ret
end
function isFun(domoticz,val,functions)
for v, fun in ipairs(functions) do
if fun == val then return true end
end
return false
end
function isParam(domoticz,val)
for v, param in ipairs(params) do
if param == val then return true end
end
return false
end
function getParams(domoticz,plan)
local list_params = {}
for fun, params in pairs(plan) do
if isParam(domoticz,fun) then table.insert(list_params, fun) end
end
return list_params
end
---- FUNCTION HELPING FOR DEBUGING
function debug(domoticz,text1, text2, text3, text4, text5, text6, text7, text8, text9)
local text = ''
if text1 ~=nil then text = tostring(text1) end
if text2 ~=nil then text = text..' '..tostring(text2) end
if text3 ~=nil then text = text..' '..tostring(text3) end
if text4 ~=nil then text = text..' '..tostring(text4) end
if text5 ~=nil then text = text..' '..tostring(text5) end
if text6 ~=nil then text = text..' '..tostring(text6) end
if text7 ~=nil then text = text..' '..tostring(text7) end
if text8 ~=nil then text = text..' '..tostring(text8) end
if text9 ~=nil then text = text..' '..tostring(text9) end
if DEBUG then domoticz.log(tostring(text),domoticz.LOG_FORCE) end
end
function logDEBUG(domoticz,plan,device,fun)
if DEBUG then
local name = ''
local description =''
local notify = ' notify= '
if plan.name ~= nil then name = plan.name end
if plan.description ~= nil then description = plan.description end
if plan.notify ~= nil then notify = notify..tostring(plan.notify) else notify = notify..tostring(false) end
domoticz.log(LOG_TEXT..' checking ... '..tostring(device)..' '..tostring(fun)..' '..tostring(name)..' '..tostring(description),domoticz.LOG_FORCE)
end
end
function logPref(domoticz)
if DEBUG then
domoticz.log('',domoticz.LOG_FORCE)
domoticz.log('*********************************************************************************',domoticz.LOG_FORCE)
domoticz.log('PLANS ver '..version,domoticz.LOG_FORCE)
domoticz.log('*********************************************************************************',domoticz.LOG_FORCE)
end
end
function logSuffix(domoticz)
if DEBUG then
domoticz.log('*********************************************************************************',domoticz.LOG_FORCE)
domoticz.log('',domoticz.LOG_FORCE)
end
end
--=================================
-- main dzVents body of script
--=================================
return {
on = {
timer = {TIME_WORK},
devices = {TRIGGER_DEVICE}
},
execute = function(domoticz,device,triggerInfo)
if triggerInfo.type==domoticz.EVENT_TYPE_TIMER then
fun_time(domoticz)
else
fun_device(domoticz,device)
end
end
}
-- *********************************************************************************
-- List of time conditions from dzVents wiki https://www.domoticz.com/wiki/DzVents:_next_generation_LUA_scripting
-- *********************************************************************************
--
-- 'every minute', -- causes the script to be called every minute
-- 'every other minute', -- minutes: xx:00, xx:02, xx:04, ..., xx:58
-- 'every <xx> minutes', -- starting from xx:00 triggers every xx minutes
-- -- (0 > xx < 60)
-- 'every hour', -- 00:00, 01:00, ..., 23:00 (24x per 24hrs)
-- 'every other hour', -- 00:00, 02:00, ..., 22:00 (12x per 24hrs)
-- 'every <xx> hours', -- starting from 00:00, triggers every xx
-- -- hours (0 > xx < 24)
-- 'at 13:45', -- specific time
-- 'at *:45', -- every 45th minute in the hour
-- 'at 15:*', -- every minute between 15:00 and 16:00
-- 'at 12:45-21:15', -- between 12:45 and 21:15. You cannot use '*'!
-- 'at 19:30-08:20', -- between 19:30 and 8:20 then next day
-- 'at 13:45 on mon,tue', -- at 13:45 only on Mondays and Tuesdays (english)
-- 'every hour on sat', -- you guessed it correctly
-- 'at sunset', -- uses sunset/sunrise info from Domoticz
-- 'at sunrise',
-- 'at sunset on sat,sun',
-- 'xx minutes before sunset',
-- 'xx minutes after sunset',
-- 'xx minutes before sunrise',
-- 'xx minutes after sunrise' -- guess ;-)
-- 'between aa and bb' -- aa/bb can be a time stamp like 15:44
-- -- aa/bb can be sunrise/sunset
-- -- aa/bb can be 'xx minutes before/after
-- sunrise/sunset'
-- 'at nighttime', -- between sunset and sunrise
-- 'at daytime', -- between sunrise and sunset
-- 'at daytime on mon,tue', -- between sunrise and sunset
-- only on Mondays and Tuesdays
--
-- -- or if you want to go really wild:
-- 'at nighttime at 21:32-05:44 every 5 minutes on sat, sun',
-- 'every 10 minutes between 20 minutes before sunset and 30 minutes after sunrise on mon,fri,tue'
--
-- *********************************************************************************