Conditional Scene Execution: Some Examples

[quote=“Brientim, post:20, topic:178331”]Then other was I forget to cover is drilling into the device utilising lu_invoke function. It will eventually display the service ID and actions available for the selected device.

http://vera.IP.address/port_3480/data_request?id=lu_invoke[/quote]

A point well made, my antipodean friend. We should perhaps warn though that, just because lu_invoke lists an action doesn’t mean that a device or plugin actually implements it.

Of course, the warning should be noted by those who venture below the surface of the Vera…

I am a new user to Vera. I have a lamp plugged into a Wemo unit that is access through Vera and a switch for the overhead lights. I am trying to create a scene that will turn out the lamp when the overhead is turned on, turn the lamp back on when the over head light is shut off, and the ability to turn them both out at night.

This seems very difficult to me. Step by step, if this is possible, is the only way for me at this point. Please help.

Thank you.

Another favourite forum question concerns using delays in scene Lua. It is tempting to do this with luup.sleep(milliseconds) but this can lead to problems. While luup.sleep(…) is executing, other Vera events can be blocked. A peak of activity at this point can cause a Vera restart. This is one possible cause of unexplained restarts.

Luup provides a more-robust way of achieving delays with luup.call_delay(“callfunction”,secs,“parmstring”). This will call the function named callfunction after a delay of secs seconds and pass it the optional parameter parmstring. The called function must be global (i.e. not local). After the luup.call_delay(…), the main code continues to execute and may terminate. Multiple luup.call_delay(…) calls may be made to the same or different called functions.

For example, the following code turns on a light and then turns it off after ten seconds. You could, of course, do this simply with scene action delays but it’s a stepping stone.

[code]luup.call_action(“urn:upnp-org:serviceId:SwitchPower1”,“SetTarget”,{ newTargetValue=1 },66)
luup.call_delay(“delayOff”,10)
– Main code terminates at this point

– Function to be called after delay
function delayOff()
luup.call_action(“urn:upnp-org:serviceId:SwitchPower1”,“SetTarget”,{ newTargetValue=0},66)
end[/code]

Because the called function is global, it does not have access to any of the local variables in the main part of your code. You could make your variables global but be careful how you name them to avoid side effects with other code. A cleaner approach is to pass them via the optional parameter. This parameter is a string so you will need to convert numeric data for some purposes.

In this example, the device ID of the switch is passed as the parameter.

[code]local dID = 66
luup.call_action(“urn:upnp-org:serviceId:SwitchPower1”,“SetTarget”,{ newTargetValue=1 },dID)
luup.call_delay(“delayOff”,10,dID)

function delayOff(dev)
local devno = tonumber(dev)
luup.call_action(“urn:upnp-org:serviceId:SwitchPower1”,“SetTarget”,{ newTargetValue=0},devno)
end[/code]

The called function can also issue a luup.call_delay(…) to itself. This allows timed sequences to be programmed. The following example will dim a light by 10% every two seconds until it reaches zero.

[code]local dID = 99
luup.call_delay(“delayDim”,2,dID)

function delayDim(dev)
local devno = tonumber(dev)
local lls = tonumber((luup.variable_get(“urn:upnp-org:serviceId:Dimming1”, “LoadLevelStatus”, devno)))
local newlls = lls - 10
if newlls < 0 then newlls = 0 end
luup.call_action(“urn:upnp-org:serviceId:Dimming1”, “SetLoadLevelTarget”, {newLoadlevelTarget = newlls}, devno)
if newlls > 0 then luup.call_delay(“delayDim”,2,dev) end
end[/code]

Only one parameter string can be passed to the called function. If you need to pass more than one item, you will need to encode them into the string. Simple numeric items can be passed as a comma-separated list. The next example uses this technique to pass three numeric values. The resulting code will either dim a light down to zero or up to 100% - depending on its level when the scene is run.

[code]local dID = 99
local dstep, dtarget
local level = tonumber((luup.variable_get(“urn:upnp-org:serviceId:Dimming1”, “LoadLevelStatus”, dID)))
if level > 50 then
dtarget = 0
dstep = -10
else
dtarget = 100
dstep = 10
end
local prms = string.format("%d,%d,%d",dID,dtarget,dstep)
luup.call_delay(“delayDim”,2,prms)

function delayDim(parms)
local pdev,ptgt,pstp = string.match(parms,"(%d+),(%d+),([%-%d]+)")
local devno = tonumber(pdev)
local target = tonumber(ptgt)
local step = tonumber(pstp)
local lls = tonumber((luup.variable_get(“urn:upnp-org:serviceId:Dimming1”, “LoadLevelStatus”, devno)))
local newlls = lls + step
if step > 0 then
if newlls > target then newlls = target end
else
if newlls < target then newlls = target end
end
luup.call_action(“urn:upnp-org:serviceId:Dimming1”, “SetLoadLevelTarget”, {newLoadlevelTarget = newlls}, devno)
if newlls ~= target then luup.call_delay(“delayDim”,2,parms) end
end[/code]

Parameters may also be passed as a serialized table. See Passing a Serialized Table.

I hope some of these examples will help to point the way to a solution for your particular requirements. If not, I recommend taking a look at the Program Logic Event Generator (PLEG) plugin.

[quote=“henryiii, post:23, topic:178331”]I am a new user to Vera. I have a lamp plugged into a Wemo unit that is access through Vera and a switch for the overhead lights. I am trying to create a scene that will turn out the lamp when the overhead is turned on, turn the lamp back on when the over head light is shut off, and the ability to turn them both out at night.

This seems very difficult to me. Step by step, if this is possible, is the only way for me at this point. Please help.

Thank you.[/quote]

@henryiii, you might get more replies if you posted your question in the main Scene Scripting part of the forum. I don’t think too many people come here.

It sounds as though you need two scenes: One triggered by the overhead light being turned on that turns off the Wemo light; One triggered by the overhead light being turned off that turns on the Wemo light. Is that really what you want? No other conditions, times, etc?

The idea is that both lights are not consuming electricity to light the same room at the same time. The over heal light washes out the lamp light. So, it sound like that is what I need. How would I configure that in two scenes then bring then to work the way I intend?

As explained in an earlier post, the optional parameter passed by luup.call_delay(…) is a single string. If you need to pass several parameters to the called function, a Lua table can be serialized into that string and recreated when the called function executes.

The general-purpose serialize function in the following example will convert a table containing numeric, string, boolean or nested table elements into a single string. The table can be recreated using the Lua loadstring statement.

This example performs the same function as the previous one but a serialized table is used to pass the three numeric values. The values are initially set in the table dparms which is then serialized and sent as the parameter in luup.call_delay(…). The called function uses loadstring to recreate the table as xparms and can then access the individual variables using xparms. notation. The resulting code will either dim a light down to zero or up to 100% - depending on its level when the scene is run.

[code]local function serialize(val,name)
local tmp = “”
if name then tmp = tmp … name … “=” end
if type(val) == “table” then
tmp = tmp … “{”
for k, v in pairs(val) do
if type(k) == “number” then
tmp = tmp … serialize(v) … “,”
else
tmp = tmp … serialize(v, k) … “,”
end
end
tmp = tmp … “}”
elseif type(val) == “number” then
tmp = tmp … tostring(val)
elseif type(val) == “string” then
tmp = tmp … string.format("%q", val)
elseif type(val) == “boolean” then
tmp = tmp … (val and “true” or “false”)
else
tmp = tmp … “”[inserializeable datatype:" … type(val) … “]”"
end
return tmp
end

local dID = 99
local dparms = {}
dparms.dev = dID
local level = tonumber((luup.variable_get(“urn:upnp-org:serviceId:Dimming1”, “LoadLevelStatus”, dID)))
if level > 50 then
dparms.target = 0
dparms.step = -10
else
dparms.target = 100
dparms.step = 10
end
luup.call_delay(“delayDim”,2,serialize(dparms))

function delayDim(parms)
assert(loadstring(“xparms=”…parms))()
local lls = tonumber((luup.variable_get(“urn:upnp-org:serviceId:Dimming1”, “LoadLevelStatus”, xparms.dev)))
local newlls = lls + xparms.step
if xparms.step > 0 then
if newlls > xparms.target then newlls = xparms.target end
else
if newlls < xparms.target then newlls = xparms.target end
end
luup.call_action(“urn:upnp-org:serviceId:Dimming1”, “SetLoadLevelTarget”, {newLoadlevelTarget = newlls}, xparms.dev)
if newlls ~= xparms.target then luup.call_delay(“delayDim”,2,parms) end
end[/code]

Not a code expert by any means, but i could use a little help. When a door is triggered a light will come on for 5 minutes during certain times of the day (done by LUUP code) What I want to do is add code to only run the scene if the light being triggered is off. Its kind of a pain if the light was on when the scene ran and the light goes off 5 minutes later when someone else is still working in the room. So here is the code i have been playing with. The first two lines is what i added to the code that was working. the device ID is 32 which is a GE/JASCO single pole wall switch. While we are at it I would like to change the local start time to sunset and leave the end time as something I can configure. Thanks

local switchOnOff = luup.variable_get(“urn:upnp-org:serviceId:SwitchPower1”, “Status”, 32)
if (switchOnOff == “0”) then

local startTime = “18:00”
local endTime = “23:59”

local hour = tonumber( startTime:sub( startTime:find("%d+") ) )
local minute = tonumber(startTime:sub(-2))

if hour and minute then
startTime = hour * 100 + minute
else
luup.log(“ERROR: invalid start time”)
return false
end

hour = tonumber( endTime:sub( endTime:find("%d+") ) )
minute = tonumber(endTime:sub(-2))

if hour and minute then
endTime = hour * 100 + minute
else
luup.log(“ERROR: invalid end time”)
return false
end

local currentTime = os.date("*t")
currentTime = currentTime.hour * 100 + currentTime.min

luup.log("startTime = " … startTime … "; currentTime = " … currentTime … "; endTime = " … endTime)

if startTime <= endTime then
– Both the start time and the end time are in the same day:
– if the current time is in the given interval, run the scene.
if startTime <= currentTime and currentTime <= endTime then
return true
end
else
– The start time is before midnight, and the end time is after midnight:
– if the current time is not outside the given interval, run the scene.
if not (endTime < currentTime and currentTime < startTime) then
return true
end
end

return false

Hi @kiethr

What are you using to check if someone is still in the room working?

I have a similar set up for my kitchen light but using PLEG, but rather than use the light switch as the trigger I have the EZMotion 3in1 sensor working as an occupancy sensor, so when it’s tripped (between a certain time) it comes on and then turns off after x mins when the sensor reports there’s no longer any motion/occupancy

Try this, @kiethr:

[code]local switchOnOff = luup.variable_get(“urn:upnp-org:serviceId:SwitchPower1”, “Status”, 32)
if (switchOnOff == “1”) then return false end

local endTime = “23:59”

local ssTime = luup.sunset() % 86400

local hour = math.floor(ssTime / 3600)
local minute = math.floor((ssTime % 3600) / 60)

local startTime = hour * 100 + minute

hour = tonumber( endTime:sub( endTime:find("%d+") ) )
minute = tonumber(endTime:sub(-2))

if hour and minute then
endTime = hour * 100 + minute
else
luup.log(“ERROR: invalid end time”)
return false
end

local currentTime = os.date("*t")
currentTime = currentTime.hour * 100 + currentTime.min

luup.log("startTime = " … startTime … "; currentTime = " … currentTime … "; endTime = " … endTime)

if startTime <= endTime then
– Both the start time and the end time are in the same day:
– if the current time is in the given interval, run the scene.
if startTime <= currentTime and currentTime <= endTime then
return true
end
else
– The start time is before midnight, and the end time is after midnight:
– if the current time is not outside the given interval, run the scene.
if not (endTime < currentTime and currentTime < startTime) then
return true
end
end

return false[/code]

Just the status of the light switch, I do not have a motion sensor…yet.

Thank you I will give that a try and report back!

You’ve got me thinking about ways of checking if someone is still in the room.

If the ‘work’ you are referring to involves the use of a networked PC in that room, then you could also look to add the Ping Sensor as a method for telling you if someone is there and working.

If you have a music player in there too. E.g like a Sonos, you could look to see if it is being used (status as Playing) as another way of checking occupancy.

No actually i was painting the hallway. The kids kept coming and going and that darn light would shut off after 5 minutes every time they left :slight_smile:

@RexBeckett

First thank you for attempting to help me!

The code you supplied did change what time the trigger started to run, so that part is working just fine. However the timer still runs when the light is on. I’m not really sure where the problem is within the code.

Sent from my iPad using Tapatalk HD

:slight_smile: Considering the way the Internet of Things (IoT) is going, it’s probably only a matter of time before you’ll have an IP address for our paint brushes 8)

The code you supplied did change what time the trigger started to run, so that part is working just fine. However the timer still runs when the light is on. I'm not really sure where the problem is within the code.
When you say [i]the timer still runs[/i], do you mean that the scene actions are still being executed even if the light is on? Do you have an instant action to turn the light on and a delayed one to turn it off?

Where did you place the Lua code - in the scene Luup tab or in the trigger Luup event? Is there just one trigger for the scene? Will you re-post your current code in case a typo crept in?

I created the scene in the following way: I have Aeon labs hidden door sensor on the front door. When the sensor is triggered (from sunset until 2359) it triggers the hallway light (controlled by a GE/JASCO wall switch) to come on. Then I set a delay to turn the hallway light off 5 minutes later. I entered the code contained below in the scene under the LUUP tab.

local minute = math.floor((ssTime % 3600) / 60)

local startTime = hour * 100 + minute

hour = tonumber( endTime:sub( endTime:find("%d+") ) )
minute = tonumber(endTime:sub(-2))

if hour and minute then
endTime = hour * 100 + minute
else
luup.log(“ERROR: invalid end time”)
return false
end

local currentTime = os.date("*t")
currentTime = currentTime.hour * 100 + currentTime.min

luup.log("startTime = " … startTime … "; currentTime = " … currentTime … "; endTime = " … endTime)

if startTime <= endTime then
– Both the start time and the end time are in the same day:
– if the current time is in the given interval, run the scene.
if startTime <= currentTime and currentTime <= endTime then
return true
end
else
– The start time is before midnight, and the end time is after midnight:
– if the current time is not outside the given interval, run the scene.
if not (endTime < currentTime and currentTime < startTime) then
return true
end
end

return false

@kiethr, that isn’t the code I posted. The first part is missing. When you change the code, make sure you click the Save lua button at the bottom before clicking Confirm changes.

@RexBeckett

Thanks, I would be lying if I said that has never happened to me before. The SAVE button is off my laptop screen and I do forget to click that sucker every now and then!! I’ll throw the code in and give it a try when i get home.

Also i stumbled across your PLEG Basics manual, its good reading. Thanks for putting it together!

Sent from my iPad

Hello @RexBeckett

I did add the updated code to my veralite. The light that would normally trigger will not trigger. If the light that is being triggered is already on when triggered it doesn’t go off after the delay in the scene is reached.

Any thoughts?

Sent from my iPad using Tapatalk HD