so you should have a smart device for you light like Qubino (Goap) ZMNHDD1 Flush Dimmer, Qubino (Goap) ZMNHADx Flush 1 Relay, FIBARO System FGD212, Dimmer 2+, Qubino (Goap) ZMNHBDx Flush 2 Relays+, Qubino (Goap) ZMNHSDx Din Dimmer+
If it is the case I did a script that do what you want but with several switches, lights, PIR and areas.
Perhaps you could look at it or use it with 1 item only in the array instead of several (I hope it works with 1 item only!).
A the present time the are some issues with z-waves like trigger even though in silent mode, that's why I had to had a workaround
Code: Select all
--[[
A dzVents Script to manage lights with switches and PIR.
For a place with several areas (a set of PIR, lights and switches).
Some use cases:
When the PIR is triggered if it is enough night, the lights are switch on, if not already on.
After if the switch is used (better if a push button), the lights that were switch on by the PIR are switched off.
When the switch is used if it is enough night, the lights are switch on, if not already on.
During the day, if you switch on the lights, there are switched off
The lighs are switched off after a delay (different if on by PIR or switch)
You could have active and passive (for monitoring only) PIR: with active PIR the lights are switched on ; with passive PIR the switches are only switched off by button, by there are switched off after a delay
There is also a double-check to be sure the lights are really off
In French (pas la traduction exacte, un texte plus ancien)
Pour gérer l'éclairage d'un lieu comprenant plusieurs zones (ensemble de PIR, de lumières et de switches).
Selon les règles suivantes:
Lorsque un détecteur est activé et si c'est la nuit, on allume toutes les lumières qui ne sont pas déjà allumées.
Lorsque un interrupteur est activé on éteint les lumières qui viennent d'être allumés par un PIR et/ou, si c'est la nuit, on allume les lumières associées à cet interrupteurs
Les lumières sont éteintes au bout d'un temps défini selon que c'est un PIR ou un switche qui les a allumées, et à partir de la dernières fois qu'un PIR ou un switch les concernant a été activé
La nuit est déterminée par l'heure ou par la luminosité
V1: 20/10/2019
--]]
-- --[[
local DOMAIN = 'LumExtV4R1' -- For logging, as you like!
local ZONE_PIR= {{514, 620}, {659, 623}} -- PIR classified in zone (like the lights)
local ZONE_LIGHT= {{700, 453}, {142, 112}, {645}} -- lights classified in zone
local GROUP_LIGHT= {{700, 453}} -- group of lights (that work together)
local PIR_ALL = {620, 623} -- list of PIR that switch on all the lights - the others only switch on the lights of their zone
local PIR_MONITOR = {659} -- list of PIR that only check to switch off the lights
local DEOL = 270 -- dummy switch to pilot the on time
local DEVICES = {DEOL, 453, 142, 112, 514, 620, 659, 623, 645} -- All the devices to trigger the script ; do not put the slaves (cf. GROUP_LIGHT)
local LUX = 305 -- idx device to get the illuminance
local LUX_MIN_PIR = 250 -- illuminance minimum in Lux to consider it's night enough to switch ON lights automatically; if nul night time is used
local LUX_MIN_SWITCH = 300 -- illuminance minimum in Lux to consider it's night enough to switch ON lights manualy ; if nul night time is used
local MAX_PIR = 100 -- 180 -- seconds ; time to keep on lights when switched on by PIR
local MAX_SWITCH = 3600 -- 300 -- 3600/12 -- seconds ; time to keep on lights when switched on by switch / bigger than MAX_PIR!
local TARGET_DIM_LEVEL = 80
local TIME_INTERVAL = 'every 30 minutes' -- Period to double-check if there is any light to switch off, in case of something went wrong
-- --]]
--[[
local DOMAIN = 'TestLum' -- For logging, as you like!
local ZONE_PIR= {{202}, {207}} -- PIR classified in zone (like the lights)
local ZONE_LIGHT= {{203, 204}, {205, 206, 214}, {460}} -- lights classified in zone
local GROUP_LIGHT= {{203, 204}, {206, 214}} -- group of lights (that work together)
local PIR_ALL = {202} -- list of PIR that switch on all the lights - the others only switch on the lights of their zone
local PIR_MONITOR = {207} -- list of PIR that only check to switch off the lights
local DEOL = 271 -- dummy switch to pilot the on time
local DEVICES = {DEOL, 203, 204, 205, 206, 214, 202, 207, 460}-- All the devices to trigger the script
local LUX = 0 --305 -- idx device to get the illuminance
local LUX_MIN_PIR = 300 -- illuminance minimum in Lux to consider it's night enough to switch ON lights automatically; if nul night time is used
local LUX_MIN_SWITCH = 400 -- illuminance minimum in Lux to consider it's night enough to switch ON lights manualy ; if nul night time is used
local MAX_PIR = 20 -- seconds ; time keep on light on when switch on by PIR
local MAX_SWITCH = 40 -- 3600/12 -- seconds ; time keep on light on when switch on by switch / bigger than MAX_PIR
local TARGET_DIM_LEVEL = 60
local TIME_INTERVAL = 'every 10 minutes' -- Period to check if any light to switch off, in case of something wrong
local TEST_NIGHT = true
--]]
local CHECKOFF_SEC = 10 -- delay before checking if all light are off
local FALSETRIGGER_SEC = 6 -- delay to wait to avoid false trigger (in case of a bug on .silent)
return {
active = true,
logging = {
level = domoticz.LOG_FORCE, -- Select one of LOG_DEBUG, LOG_INFO, LOG_ERROR, LOG_FORCE to override system log level -- normal = LOG_ERROR
marker = DOMAIN
},
on = {
devices = DEVICES,
timer = {TIME_INTERVAL},
},
data = {
deviceOn = {initial={}},
timeOn = {initial={}},
checkOff = {initial=true},
},
execute = function(dz, the_device, triggerInfo)
local LOG_LEVEL = dz.LOG_FORCE -- LOG_INFO, LOG_DEBUG, LOG_ERROR, LOG_FORCE - normal = LOG_INFO
local l_ForSec = 99999
local Time = require('Time')
local l_time = Time()
local l_deviceOn = dz.data.deviceOn
local l_timeOn = dz.data.timeOn
local l_night_PIR=false
local l_night_SWITCH=false
local l_day_night ='day'
if LUX == 0 then
--print ("N/D w time " .. tostring(isNightTime))
if dz.time.isNightTime or TEST_NIGHT then
l_night_PIR = true
l_night_SWITCH = true
l_day_night ='night'
end
else
--print ("N/D w LUX idx: " .. LUX .. " Lux: " .. dz.devices(LUX).lux)
if dz.devices(LUX).lux < LUX_MIN_SWITCH then
l_night_SWITCH = true
l_day_night ='night for switch only'
if dz.devices(LUX).lux < LUX_MIN_PIR then
l_night_PIR = true
l_day_night ='night'
end
end
end
dz.log('*** start ' .. triggerInfo.type .. ' ' .. l_time.raw .. ' ' .. l_day_night, dz.LOG_FORCE)
--- FUNCTIONS BEG --------------------------------------------------------------
local function checkFalseTrigger (theItemId)
-- to check if there is a false trigger due to .silent issue
--print('function checkFalseTrigger ' .. theItemId)
--print(l_deviceOn[theItemId])
--if dz.devices(theDeviceId).active and l_deviceOn[theItemId]~= 'S' then -- 28/08/2019 -- to avoid false wrong trigger in case of switch off (13/09/2019) not after a switch on
-- 01/10/2019 removed because it is possible to have z false switch off by the deviced
local f_sec_delta = 0
if l_timeOn[theItemId] ~= nil then -- and l_timeOn[theItemId] ~= 0 and l_timeOn[theItemId].raw ~= nil then
--print ("time " .. l_time.raw)
--print("timeOn " .. l_timeOn[theItemId].raw)
f_sec_delta = l_time.compare(l_timeOn[theItemId]).secs
--if l_deviceOn[theItemId]== 'S' or l_deviceOn[theItemId]== 'P' then
if f_sec_delta < FALSETRIGGER_SEC then
dz.log('! Wrong trigger - to stop the script ' .. dz.devices(theItemId).name .. ' ' .. dz.devices(theItemId).state .. ' ' .. l_deviceOn[theItemId] .. ' delta sec ' .. f_sec_delta, dz.LOG_ERROR)
return true
end
--end
end
--end
dz.log('Trigger OK - to continue the script ' .. dz.devices(theItemId).name .. ' ' .. dz.devices(theItemId).state, dz.LOG_LEVEL)
return false
end
local function theArray2Id (theArray, theItemId)
-- find the sub array where the item is in the array (of 2 levels)
local array2Id
-- print('function theArray2Id')
-- print('theItemId: ' .. theItemId)
for i1, l1 in ipairs(theArray) do
-- print('i1: ' .. i1)
for i2, l2 in pairs(theArray[i1]) do
--print('i2: ' .. i2)
--print('l2: ' .. l2)
if theItemId == l2 then
--print 'OK'
--print("theItemId: " .. theItemId)
--print('level1: ' .. i1)
--print('level[]: ' .. l1[i2])
--print('theArray[iz]: ' .. theArray[i1][i2])
array2Id=i1
break
end
end
if array2Id ~= nill then break end
end
return array2Id
end
local function ifItemPresent2 (theArray, theItemId)
-- find if the item is in the array (of 2 levels)
local ItemPresent=false
--print('function ifItemPresent2')
--print('theItemId: ' .. theItemId)
for i1, l1 in ipairs(theArray) do
--print('i1: ' .. i1)
for i2, l2 in pairs(theArray[i1]) do
--print('i2: ' .. i2)
--print('l2: ' .. l2)
if theItemId == l2 then
--print 'OK'
--print("theItemId: " .. theItemId)
--print('level1: ' .. i1)
--print('level[]: ' .. l1[i2])
--print('theArray[iz]: ' .. theArray[i1][i2])
ItemPresent=true
break
end
end
if ItemPresent then break end
end
return ItemPresent
end
local function ifItemPresent (theArray, theItemId)
-- find if the item is in the array (of 1 level)
local ItemPresent=false
-- --print('function ifItemPresent')
-- --print('theItemId: ' .. theItemId)
for i1, l1 in ipairs(theArray) do
--print('i1: ' .. i1)
if theItemId == l1 then
--print 'OK'
--print("theItemId: " .. theItemId)
ItemPresent=true
break
end
if ItemPresent then break end
end
return ItemPresent
end
local function switchOnLight (theDeviceId, PSX, Monitor)
--switch on 1 light
--print('function switchOnLight')
--print('theDeviceId: ' .. theDeviceId)
local deviceActive=true
if dz.devices(theDeviceId).active then
--if dz.devices(theDeviceId).switchType == 'Dimmer' then
--if dz.devices(theDeviceId).level ~= TARGET_DIM_LEVEL then
--dz.devices(theDeviceId).dimTo(TARGET_DIM_LEVEL).silent()
--end
--end
--print('switchOnLight device active')
dz.log(dz.devices(theDeviceId).name .. " " .. theDeviceId .. " already active ", LOG_LEVEL)
else
if not Monitor then -- the PIR is to switch on a light or it is a switch
deviceActive=false
if dz.devices(theDeviceId).switchType == 'Dimmer' then
--print('Dimmer')
dz.devices(theDeviceId).dimTo(TARGET_DIM_LEVEL).silent()
else
--print('Not Dimmer')
dz.devices(theDeviceId).switchOn().silent()
end
dz.log(dz.devices(theDeviceId).name .. " " .. theDeviceId .. " to On", LOG_LEVEL)
end
end
if PSX == 'S' then -- 05/08/2019
l_deviceOn[theDeviceId]= 'S'
elseif PSX == 'P' then
if l_deviceOn[theDeviceId] ~= 'S' then
l_deviceOn[theDeviceId]= 'P'
end
else
dz.log('switchOnLight PSX error ' .. PSX, dz.LOG_ERROR)
end
l_timeOn[theDeviceId] = l_time -- last 'on' time
return deviceActive
end
local function switchOffLight (theDeviceId)
--switch off 1 light
-- print('function switchOffLight')
-- print('theDeviceId: ' .. theDeviceId)
-- print (l_time.raw)
-- if (triggerInfo.type == 'device') then -- 12/08/2019 comments to avoid last seen update
dz.devices(theDeviceId).switchOff().checkFirst().silent()
--else -- timer, to check if some light could have been left on
--dz.devices(theDeviceId).switchOff().silent() -- 09/08/2019 in case dz don't have the right status
--print('function switchOffLight: timer')
--end
l_deviceOn[theDeviceId]= 'X'
l_timeOn[theDeviceId] = l_time -- last change state
--local deviceActive=false
end
local function switchOnLightArray (theArray, PSX, Monitor)
-- switch on all the lights of the array (1 level)
--print('function switchOnLightArray')
local deviceActive = false
local allDeviceActive = true
for i1, l1 in ipairs(theArray) do
--print('Light: ' .. i1 .. ' ' .. l1)
--if ItemIdToIgnore == l1 then
--print ('ItemIdToIgnore: ' .. l1)
--else
deviceActive=switchOnLight(l1, PSX, Monitor)
if not deviceActive then
allDeviceActive=false
end
--end
--print('deviceActive: ' .. tostring(deviceActive))
end
return allDeviceActive
end
local function switchOffLightArray (theArray, PSX)
--switch off all the lights of the array (1 level)
--print('function switchOffLightArray ' .. PSX)
local l_sec_delta = 0
local l_ForSecTmp = 0
for i1, l1 in ipairs(theArray) do
--print('light: ' .. i1 .. ' ' .. l1 .. ' PSX: ' .. PSX)
if PSX == 'T' or PSX == 'XT' then -- to check if it is time to turn off the light
--print ('MAX_SWITCH ' .. MAX_SWITCH)
--print('MAX_PIR ' .. MAX_PIR)
--print ("l1 " .. l1)
if not l_timeOn[l1] then
--l_timeOn[l1] = 0
--l_sec_delta = 0
l_sec_delta = MAX_SWITCH+1 -- 07/07/2019
else
l_sec_delta = l_time.compare(l_timeOn[l1]).secs
end
if not l_deviceOn[l1] then
l_deviceOn[l1] = '?'
end
--print('l_sec_delta ' .. l_sec_delta) -- :-( !!!! S éteint qd P éteint !!!!!!!!!!!
if l_sec_delta > MAX_SWITCH
or (l_deviceOn[l1] == 'P' and l_sec_delta > MAX_PIR)
or (l_deviceOn[l1] == 'P' and PSX == 'XT')
or l_deviceOn[l1] == 'X' then -- 30/05/2019 removed / 07/07/2019 replaced!
switchOffLight(l1)
--print ('switchoff ' .. PSX)
end
-- calculate the time before triggering this script to switch off the next light
if l_deviceOn[l1] == 'P' then
l_ForSecTmp = MAX_PIR - l_sec_delta
dz.log("switchOffLightArray: P l_ForSecTmp " .. l_ForSecTmp .. " l_sec_delta " .. l_sec_delta, LOG_LEVEL)
if l_ForSec > l_ForSecTmp then
l_ForSec = l_ForSecTmp
--dz.log("P !!!" .. l_ForSec, LOG_LEVEL)
end
elseif l_deviceOn[l1] == 'S' then
l_ForSecTmp = MAX_SWITCH - l_sec_delta
dz.log("switchOffLightArray: S l_ForSecTmp " .. l_ForSecTmp .. " l_sec_delta " .. l_sec_delta, LOG_LEVEL)
if l_ForSec > l_ForSecTmp then
l_ForSec = l_ForSecTmp
--dz.log("S !!!" .. l_ForSec, LOG_LEVEL)
end
end
--dz.log("l_ForSec " .. l_ForSec, LOG_LEVEL)
if not l_timeOn[l1] then
--print ("l1 => " .. l1)
--print( dz.devices(l1).name)
--print( dz.devices(l1).state)
--print("devOn " .. l_deviceOn[l1])
--print("secdelta " .. l_sec_delta)
dz.log(l1 .. ' ' .. dz.devices(l1).name .. ' ' .. dz.devices(l1).state .. ' ' .. l_deviceOn[l1] .. ' timeOn 0 delta sec ' .. l_sec_delta, dz.LOG_FORCE)
else
dz.log(l1 .. ' ' .. dz.devices(l1).name .. ' ' .. dz.devices(l1).state .. ' ' .. l_deviceOn[l1] .. ' timeOn ' .. l_timeOn[l1].raw .. ' delta sec ' .. l_sec_delta, dz.LOG_FORCE)
end
else
switchOffLight(l1)
end
end
return allDeviceActive
end
local function switchOnLightArray2 (theArrayL1, theArrayL2, PSX)
-- switch on all the lights of the array L2 (lights) regarding the zones of the array L1 (PIRs)
local deviceActiveL1 = true
local allDeviceActive = true
--print('function switchOnLightArray2')
for i1, l1 in ipairs(theArrayL1) do
--print('i1: ' .. i1)
deviceActiveL1 = switchOnLightArray (theArrayL2[i1], PSX)
if not deviceActiveL1 then
allDeviceActive=false
end
end
return allDeviceActive
end
local function switchOffLightArray2 (theArray, PSX)
-- switch off all the lights of the array (of 2 levels) eventually filtered with PSX
--print('function switchOffLightArray2')
for i1, l1 in ipairs(theArray) do
--print('i1: ' .. i1)
deviceActiveL1 = switchOffLightArray (theArray[i1], PSX)
end
end
local function checkLightActive2 (theArray)
-- check if any light is already on in the array (of 2 levels)
--print('function chechLightOn2')
local anyActive=false
for i1, l1 in ipairs(theArray) do
--print('i1: ' .. i1)
for i2, l2 in pairs(theArray[i1]) do
--print('i2: ' .. i2)
--print('l2: ' .. l2)
if dz.devices(l2).active then
anyActive=true
--print('anyActive')
return anyActive
end
end
end
return anyActive
end
--- FUNCTIONS END --------------------------------------------------------------
--------------------------------------------------------------------------------
local itemId = the_device.id
local allDeviceActive = true
local theDeviceOn = false
local theSwitchOff = false
local PIRMonitor = false
local zoneMode=true
if (triggerInfo.type == 'device') then
if ifItemPresent2(ZONE_PIR, itemId) then -- this device is a PIR
dz.log('PIR: ' .. itemId .. ' ' .. the_device.name.. ' ' .. the_device.state, LOG_LEVEL)
if l_night_PIR then --at night
--dz.log('PIR: ' .. itemId .. ' ' .. the_device.name.. ' ' .. the_device.state, LOG_LEVEL)
if not the_device.active then -- a switchOff signal from the PIR: used to check if some lights to switch off
--print('PIR not active')
else
if ifItemPresent(PIR_MONITOR, itemId) then -- this PIR only check to swith off the lights
--print('PIR_MONITOR: ' .. itemId)
PIRMonitor = true
else
if ifItemPresent(PIR_ALL, itemId) then -- this PIR could switch on all light
--print('anyActive: ' .. tostring(anyActive))
if not checkLightActive2 (ZONE_LIGHT) then -- if no light is already on
--print('All lights are off')
allDeviceActive = switchOnLightArray2 (ZONE_PIR, ZONE_LIGHT, 'P')
zoneMode=false -- if any light is on, only the lights in the PIR zone are switch on / light on all lights if no light is on
end
end
end
if zoneMode then --
--print('Lights on of the PIR zone')
local zoneId = theArray2Id(ZONE_PIR, itemId)
--print('zoneId: ' .. zoneId)
allDeviceActive = switchOnLightArray(ZONE_LIGHT[zoneId], 'P', PIRMonitor)
end
end
end
elseif ifItemPresent2(ZONE_LIGHT, itemId) then -- this device is a switch
dz.log('SWITCH: ' .. itemId .. ' ' .. the_device.name .. ' ' .. the_device.state, LOG_LEVEL)
if l_night_SWITCH then -- at night
local FalseTrigger=checkFalseTrigger(itemId)
if FalseTrigger then
return -- it is a false trigger due to .silent issue
end
if the_device.active then
theDeviceOn = true
-- dz.log('l_theDeviceOn true' , LOG_LEVEL)
else
theDeviceOn = false
theSwitchOff = true
-- dz.log('l_theDeviceOn false' , LOG_LEVEL)
end
local slaveId = theArray2Id(GROUP_LIGHT, itemId)
if slaveId ~= nil then
--print('slaveId: ' .. slaveId)
--retreive the level to light the slave with the same level <= NOT MADE !!!
if theDeviceOn then
--print('go to switchOnLightArray')
allDeviceActive = switchOnLightArray(GROUP_LIGHT[slaveId], 'S') -- , itemId) !!!!!!!!!!!!!!!!!!!!
else
--print('go to switchOffLightArray')
switchOffLightArray(GROUP_LIGHT[slaveId], 'X')
end
else -- no slaves
if theDeviceOn then
--print("switchOnLight " .. itemId)
switchOnLight (itemId, 'S')
else
--print("switchOffLight " .. itemId)
switchOffLight (itemId)
end
end
else
switchOffLight (itemId)
end
elseif itemId ~= DEOL then
dz.log('Nor LIGHT, nethier PIR ?! ' .. itemId, dz.LOG_ERROR)
else
dz.log('DEOL: ' .. itemId .. ' ' .. the_device.name .. ' ' .. the_device.state, LOG_LEVEL)
end
end
-- [[
dz.log('*** end ' , dz.LOG_FORCE)
if theSwitchOff then -- somebdy switchoff a light, if after a PIR, all light switch on by the PIR are switch off
--print ('theSwitchOff')
switchOffLightArray2(ZONE_LIGHT, 'XT')
else
switchOffLightArray2(ZONE_LIGHT, 'T') -- to check if it's time to switch off some lights
end
-- activate a dummy device to trigger the script to switch off the next lights
if DEOL ~= nill then
if l_ForSec < 9999 then -- a light to switch off in a while
dz.data.checkOff = true
else -- not a light to switch off
if dz.data.checkOff then -- if first time, a 2nd round to secure the switch off
l_ForSec = CHECKOFF_SEC -- number off seconds for the 2nd round
if not checkLightActive2 (ZONE_LIGHT) then -- if any light is already on
dz.data.checkOff = false -- one more check
end
--print ('check in ' .. l_ForSec .. ' sec')
end
end
if l_ForSec < 9999 then
dz.log('Next check in ' .. l_ForSec .. ' sec', dz.LOG_FORCE)
l_ForSec = l_ForSec + 3
dz.devices(DEOL).cancelQueuedCommands()
dz.devices(DEOL).switchOn().silent()
dz.devices(DEOL).switchOff().afterSec(l_ForSec)
else
dz.log('All is switched off', dz.LOG_FORCE)
dz.devices(DEOL).cancelQueuedCommands()
dz.devices(DEOL).switchOff().silent()
end
end
--]]
dz.data.deviceOn = l_deviceOn
dz.data.timeOn = l_timeOn
end}