I'm sharing this DZVents scripts : a system to store and restore state of a light / switch (it should work with all lights and switch, let me know if something doesn't work for you)
For instance : you have your lights turned on in blue with low intensity; then you want to switch the lights on white with high intensity, because you opened a door, or because you triggered a motion sensor.
After some time you want to return the lights to their previous state : blue / low intensity (instead of simply turning the light off)
The script comes as global functions and a standalone custom event script.
The global functions are to be used in your own scripts.
For instance, you create a DZVents script for a motion sensor :
Code: Select all
return {
on = {
devices = { 'Motion Sensor' },
},
logging = {
level = domoticz.LOG_INFO,
marker = 'MOTION SENSOR TEST',
},
execute = function(domoticz, device)
-- Change the light to bright white for a minute
-- then restore its state
if(device.isDevice) then
-- Store the current state and ask for restoring it after 60 sec.
domoticz.helpers.restoreStateAfter(domoticz, 'My Light', 60)
-- Change the color of the light (white / 100% luminosity)
domoticz.devices('My Light').setColor(0,0,0,100,0,0,2,128)
end
end
}
You'll need the CURL utility, you can download CURL here if you don't have it already installed on your system. CURL is available for many platforms (windows / linux).This is a command line utility to make http requests (and other), that will be used by the script to make synchronous http requests (DZVents does offer asynchronous requests, taht's why CURL is needed). Once you downloaded the binary, put it on the folder of your choice and take note of the path you put CURL, we will need it later.
You need to have the "global_data" DZVents scripts. If you don't have a "global_data" script, go into "Setup" -> "More Options" -> "Events", click on "+" and select "DZVENTS" -> "GLOBAL DATA". Instead of "Script #1" type "global_data" (without the quotes). You can now copy and paste the code below and click on "SAVE" (check in the domoticz log for any errors, sometimes on Windows you have to make sure Domoticz can save the scripts file correctly)
If you do have an existing "global_data" script, then you'll need to add the functions and global variables you'll find in the code below.
Code: Select all
-- this scripts holds all the globally persistent variables and helper functions
-- see the documentation in the wiki
-- NOTE:
-- THERE CAN BE ONLY ONE global_data SCRIPT in your Domoticz install. So if
return {
-- Persistent Global Variables
data = {
-- RESTORE STATE VARS --
previousStates = { initial = { [0] = nil } },
-- END RESTORE STATE VARS
},
-- Global Functions
helpers = {
-- Does the argument is a number
-- x : the value to check
isNumber = function (x)
if tonumber(x) ~= nil then return true end
return false
end,
-- get the color of a light
-- deviceId : IDX or device name
getColor = function(domoticz, deviceId)
local idx = deviceId
if(not domoticz.helpers.isNumber(deviceId)) then idx = domoticz.devices(deviceId).idx end
cmd='"C:\\Program Files (x86)\\Domoticz\\scripts\\lua\\curl.exe" '..domoticz.settings.url..'/json.htm?type=devices^&rid='..idx
local httpRequest = assert(io.popen(cmd))
local html = httpRequest:read('*all')
local json = domoticz.utils.fromJSON(html)
-- adding brigthness to the color object
local color = domoticz.utils.fromJSON(json.result[1].Color)
color.br = domoticz.devices(deviceId).level
-- returning the result
return color
end,
-- RESTORE STATE FUNC --
-- store the state of a light
-- domoticz : domoticz object
-- deviceName : device name
-- holdFor : validity of the state. if nil the state can be restored at anytime, otherwise state can be restored upto the "holdFor" duration (in seconds)
storeState = function(domoticz, deviceName, holdFor)
-- Get the device ID
local device = domoticz.devices(deviceName)
local deviceId = device.id
domoticz.log('Sotring state for device :'..deviceName, domoticz.LOG_INFO)
-- time when the state will expire (ie : when it should be restored)
local validTill
local storeState = true
local checkStates = false
if(holdFor == nil) then validTill = nil else validTill = domoticz.time.addSeconds(holdFor).dDate end
-- should we store this new state
if(validTill ~= nil) then
-- we know when this state must end, we have
-- to check that the previous state should end
-- before this new one
local lastState = domoticz.globalData.previousStates[deviceId]
if(lastState ~= nil) then
if(lastState.value.validTill == nil) then
-- nok : our time is supposed to be past
-- the one already stored
-- we change the date of the stored state by
-- the date of the new state
lastState.value.validTill = validTill
storeState = false
checkStates = true
elseif(lastState.value.validTill > validTill) then
-- ok our date is past the date of the
-- already stored state
storeState = true
elseif(lastState.value.validTill <= validTill) then
-- nok our date is before the date of
-- the already stored state
-- we change the date of the stored state by
-- the one of the new state
lastState.value.validTill = validTill
storeState = false
checkStates = true
end
end
else
-- we dont know the duration of the state we are storing
-- so we cannot store this state if one state is already stored
if(domoticz.globalData.previousStates[deviceId] ~= nil) then storeState = false end
end
-- checking if states are not conflicting
while(checkStates) do
checkStates = false
local lastState = domoticz.globalData.previousStates[deviceId]
while(lastState.next ~= nil) do
if((lastState.next.value.validTill == nill) or
(lastState.next.value.validTill <= lastState.value.validTill)) then
-- current state will expire before next state
-- replacing expiration time of next state by current
lastState.next.value.validTill = lastState.value.validTill
-- deleting current state
lastState.next = lastState.next.next
lastState.value = lastState.next.value
domoticz.globalData.previousStates[deviceId] = lastState
-- need one more pass to check conflicts
checkStates = true
else
-- all is ok no need to continue
break
end
end
end
if(storeState) then
-- storing state (on/off)
local currentState = { ['validTill'] = validTill, ['state'] = 'Off', ['color'] = nil }
currentState.state = device.state
-- storing color (when RGB+ device)
if(device.deviceType == 'Color Switch') then
local color = domoticz.helpers.getColor(domoticz, device.idx)
currentState.color = color
else
if(device.switchType == 'Dimmer') then
--storing brightness level
currentState.color = {['br'] = device.level }
end
end
-- push device state in the states linked list
domoticz.log('Stored state for device '..deviceId..' : '..domoticz.utils.toJSON(currentState), domoticz.LOG_INFO)
domoticz.globalData.previousStates[deviceId] = { next = domoticz.globalData.previousStates[deviceId], value = currentState }
end
end,
-- restore a device state (if a stored state exists)
-- deviceName : idx or device name to restore
restoreState = function(domoticz, deviceName)
-- get device ID
local deviceId = domoticz.devices(deviceName).id
-- ask for device state restoration now (custom event)
domoticz.emitEvent('restoreState', deviceId)
end,
-- store the current state (color / power) of light and schedule its restoration
-- deviceName = idx / device name (light or switch)
-- holdFor = time in seconds before restoring the state
restoreStateAfter = function(domoticz, deviceName, holdFor)
-- get device ID
local deviceId = domoticz.devices(deviceName).id
-- store device state with "holdFor" lifetime
domoticz.helpers.storeState(domoticz, deviceName, holdFor)
-- schedule device restoration in "holdFor" sec.
domoticz.emitEvent('restoreState', deviceId).afterSec(holdFor)
end,
-- clear the stored state of a device, canceling its future restoration
-- this is useful to prevent light to turn back on when you want them
-- to stay off and don't know if a state is about to be restored.
-- deviceName = idx or device name
cancelRestoreState = function(domoticz, deviceName)
-- get device ID
local deviceId = domoticz.devices(deviceName).id
domoticz.log('Cancel stored states for device '..deviceId, domoticz.LOG_INFO)
-- clear all stored device states
domoticz.globalData.previousStates[deviceId] = nil
end
-- END RESTORE STATE FUNC
}
}
cmd='"C:\\Program Files (x86)\\Domoticz\\scripts\\lua\\curl.exe" '..domoticz.settings.url..'/json.htm?type=devices^&rid='..idx
Replace the current path with the path on your system.
Once this is done you can create the script that will hande the state restore : in the script editor, click on "+" then select "DZVENTS" -> "CUSTOM EVENTS", give it the name you want instead of "Script #1", for instance "Restore State", copy and paste the code below and click on "SAVE".
Code: Select all
return {
on = { customEvents = { 'restoreState' } },
logging = { level = domoticz.LOG_INFO,
marker = 'RESTORE STATE', },
execute = function(domoticz, item)
-- Restore State
if(item.isCustomEvent) then
-- Custom Event = restoreState
if(item.trigger == 'restoreState') then
-- Get the ID of the device to restore state
local deviceId = tonumber(item.data)
domoticz.log('Restore state for device '..item.data, domoticz.LOG_INFO)
-- if the stored state had a validTill date
-- greater that the current time : we do nothing
-- because we expect to restore this state at a later time
local lastState = nil
if(domoticz.globalData.previousStates[deviceId]) then
lastState = domoticz.globalData.previousStates[deviceId].value
if(lastState.validTill ~= nil and
lastState.validTill > domoticz.time.dDate) then
domoticz.log('State restoration delayed', domoticz.LOG_INFO)
return
end
end
-- we have a state to restore
if(lastState ~= nil) then
-- restoring previous state
domoticz.log('State to restore for device '..deviceId..' : '..domoticz.utils.toJSON(lastState), domoticz.LOG_INFO)
if(lastState.state ~= 'Off') then
if(domoticz.devices(deviceId).deviceType == 'Color Switch' and lastState.color ~= nil) then
-- color device : restoring color / white level as well as brightness
domoticz.devices(deviceId).setColor(lastState.color.r, lastState.color.g, lastState.color.b,
lastState.color.br,
lastState.color.cw, lastState.color.ww,
lastState.color.m, lastState.color.t)
else
-- restoring brightness only
domoticz.devices(deviceId).switchOn()
if(domoticz.devices(deviceId).switchType=='Dimmer' and lastState.color ~= nil) then
domoticz.devices(deviceId).dimTo(lastState.color.br)
end
end
else
-- restoring power state only
domoticz.devices(deviceId).switchOff()
end
-- clean up stored states
domoticz.globalData.previousStates[deviceId] = domoticz.globalData.previousStates[deviceId].next
else
domoticz.log('No stored state found', domoticz.LOG_INFO)
end
end
end
end
}
domoticz.helpers.storeState(domoticz, deviceName, holdFor)
To store the current state of a the device deviceName, holdFor is the time in seconds after which this stored state is supposed to be restored. For instance if you store the state of a light, because you'll change its luminosity for 1 minutes after a motion detection event, holdFor is 60 seconds. If you don't know when the state will be restored just use nil.
You can call this function multiple times, the script can store multiple states for a device. The script will decide if the state should be stored or not (that's why the holdFor variable is for)
domoticz.helpers.restoreState(domoticz, deviceName)
Restore the previous stored state of the device deviceName. Multiple state may be stored, the script choose the state to restore in the logical order (often last in, last out, but not always)
domoticz.helpers.restoreStateAfter(domoticz, deviceName, holdFor)
Store the current state of the device deviceName and restore it after holdFor seconds. Usually right after using this function, you'll actively change the state of the light (for instance turning it on, or changing its brightness / color)
domoticz.helpers.cancelRestoreState(domoticz, deviceName)
Erase all stored state for the device deviceName. This will prevent future restore of the state of this device if any was scheduled. It's mostely used when you turn off a device and you want to make sure it will stays off.