Author Topic: Conditional Scene Execution: Some Examples  (Read 156565 times)

Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples
« on: December 17, 2013, 02:41:31 pm »
One of the most frequent questions on this forum is How do I stop my scene running when... This has been asked and answered for many different types of condition and diligent searching will often find you a solution. To help newcomers to Vera, I am posting a few of the most common scenarios in this thread.

The mechanism for preventing a scene running is simple: You insert Luup code into the scene that returns false if you want the scene blocked or true if you want to allow it to run. You can insert the code in a Trigger's Luup event to allow or block only that trigger. You can also insert the code into the scene's main LUUP tab where it will allow or block all triggers and manual operation. You can use a combination of both types for more complex scenarios. UI7 does not currently allow code to be attached to individual triggers so only the main Luup Code tab may be used.


Day or Night conditions

To allow a scene to run only at night:
Code: [Select]
return luup.is_night()
To prevent a scene running at night:
Code: [Select]
return not luup.is_night()
Or a more generic form:
Code: [Select]
local allow = true       -- true runs scene at night, false blocks it
return ((luup.is_night()) == allow)

Many of us use the DayTime (Day or Night) plugin as an alternative to the luup.is_night() function. It has a few advantages: You can configure offsets from sunrise and sunset to control your definition of daytime; You can manually set it to Day or Night to test your scenes; It gives you an indicator on your dashboard to show its current state.

Generic DayTime:
Code: [Select]
local dID = 23           -- Device ID of your DayTime plugin
local allow = true       -- true runs scene during daytime, false runs it at night
local status = luup.variable_get("urn:rts-services-com:serviceId:DayTime","Status",dID)
return ((status == "1") == allow)


Other examples

Z-Wave and Virtual Switches
Time Period
Temperature Range
Humidity Range
Light Level
VariableContainer
Day Range
Date Range
Multiple Conditions
Using Functions
Multiple Triggers
Time Window
Service IDs, Variables and Actions
Delayed Actions
Delayed Actions - Passing a Serialized Table
Debugging Lua Code - kwikLog
Testing Lua Code - LuaTest
Run Scene when a Variable Changes
Finding the Correct Service ID
« Last Edit: March 14, 2015, 08:04:11 am by RexBeckett »

Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples - Switches
« Reply #1 on: December 17, 2013, 03:04:40 pm »
We often want to allow or block scenes depending on the state of a Z-Wave switch (E.g. a light is on) or a VirtualSwitch (e.g. Home/Away). The VirtualSwitch (VS) plugin is very useful as a means of controlling scenes. You can create several different VS devices to signify various states of your home. E.g. Home/Away, OnVacation, HaveGuests, etc. VS devices can be set manually through the UI, by scenes or with other plugins.

Testing the state of a switch is essentially the same whether it is real or virtual but the serviceID must match the type of switch being tested.

Z-Wave Switch:
Code: [Select]
local dID = 66           -- Device ID of your Z-Wave Switch
local allow = true       -- true runs scene if switch is on, false blocks it
local status = luup.variable_get("urn:upnp-org:serviceId:SwitchPower1","Status",dID)
return ((status == "1") == allow)

Virtual Switch:
Code: [Select]
local dID = 32           -- Device ID of your VirtualSwitch
local allow = true       -- true runs scene if switch is on, false blocks it
local status = luup.variable_get("urn:upnp-org:serviceId:VSwitch1","Status",dID)
return ((status == "1") == allow)

MultiSwitch:
Code: [Select]
local dID = 94          -- Device ID of your MultiSwitch
local button = 1 -- MultiSwitch button number (1-8)
local allow = true      -- true runs scene if switch is on, false blocks it
local status = luup.variable_get("urn:dcineco-com:serviceId:MSwitch1","Status"..button,dID)
return ((status == "1") == allow)
« Last Edit: March 22, 2014, 05:41:03 am by RexBeckett »

Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples - Time Period
« Reply #2 on: December 17, 2013, 03:22:05 pm »
We frequently want to control the time periods during which a scene may run. You would probably prefer that your bedroom light did not come on in the middle of the night when you are clocked by the motion-sensor on your way to the bathroom.  ;D 

Here is a generic routine that can be set to allow or block a scene in the period between two times. The start and end times can be set within the same day or either side of midnight. Both times must be in 24-hour form and entered as HH:MM. As with previous generic examples, the variable allow determines whether to allow or block the scene during the specified period.

Generic Time Period:
Code: [Select]
local pStart = "22:30"   -- Start of time period
local pEnd = "06:15"     -- End of time period
local allow = true       -- true runs scene during period, false blocks it
local hS, mS = string.match(pStart,"(%d+)%:(%d+)")
local mStart = (hS * 60) + mS
local hE, mE = string.match(pEnd,"(%d+)%:(%d+)")
local mEnd = (hE * 60) + mE
local tNow = os.date("*t")
local mNow = (tNow.hour * 60) + tNow.min
if mEnd >= mStart then
     return (((mNow >= mStart) and (mNow <= mEnd)) == allow)
else
     return (((mNow >= mStart) or (mNow <= mEnd)) == allow)
end

With a small modification, we can take the start time as sunset with a +/- offset. Strictly speaking, this will use the time of the next sunset so will give a slightly different result before and after sunset. The difference is about two minutes so should not be a major problem.

Sunset to End Time:
Code: [Select]
local pStart = 0         -- Start of time period, minutes offset from sunset
local pEnd = "06:15"     -- End of time period
local allow = true       -- true runs scene during period, false blocks it
local mStart = math.floor( (luup.sunset() % 86400) / 60 ) + pStart
local hE, mE = string.match(pEnd,"(%d+)%:(%d+)")
local mEnd = (hE * 60) + mE
local tNow = os.date("*t")
local mNow = (tNow.hour * 60) + tNow.min
if mEnd >= mStart then
     return (((mNow >= mStart) and (mNow <= mEnd)) == allow)
else
     return (((mNow >= mStart) or (mNow <= mEnd)) == allow)
end

We can also use sunrise as one of our times. This version has a specified start time and the end time is sunrise with a +/- minutes offset.

Start Time to Sunrise:
Code: [Select]
local pStart = "22:30"   -- Start of time period
local pEnd = 0           -- End of time period, minutes offset from sunrise
local allow = true       -- true runs scene during period, false blocks it
local hS, mS = string.match(pStart,"(%d+)%:(%d+)")
local mStart = (hS * 60) + mS
local mEnd = math.floor( (luup.sunrise() % 86400) / 60 ) + pEnd
local tNow = os.date("*t")
local mNow = (tNow.hour * 60) + tNow.min
if mEnd >= mStart then
     return (((mNow >= mStart) and (mNow <= mEnd)) == allow)
else
     return (((mNow >= mStart) or (mNow <= mEnd)) == allow)
end
« Last Edit: February 04, 2014, 05:28:54 am by RexBeckett »

Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples - Temperature Range
« Reply #3 on: December 17, 2013, 05:49:19 pm »
This code will enable you to set a range of temperatures within which your scene will, or will not, be run. Set tLow and tHigh to define the range. As with previous generic examples, the variable allow determines whether to allow or block the scene when the current temperature is within the specified range.

Generic Temperature Range:
Code: [Select]
local dID = 55           -- Device ID of your thermostatic/temperature sensor
local tLow = 18          -- Lowest temperature of range
local tHigh = 22         -- Highest temperature of range
local allow = true       -- true runs scene when in range, false blocks it
local tCurrent = tonumber((luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature",dID)))
return (((tCurrent >= tLow) and (tCurrent <= tHigh)) == allow)



Coming next: Humidity Range

Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples - Humidity Range
« Reply #4 on: December 17, 2013, 05:58:03 pm »
This code will enable you to set a range of humidity within which your scene will, or will not, be run. Set hLow and hHigh to define the range. As with previous generic examples, the variable allow determines whether to allow or block the scene when the current humidity level is within the specified range.

Generic Humidity Range:
Code: [Select]
local dID = 31           -- Device ID of your humidity sensor
local hLow = 50          -- Lowest humidity of range
local hHigh = 80         -- Highest humidity of range
local allow = true       -- true runs scene when in range, false blocks it
local hCurrent = tonumber((luup.variable_get("urn:micasaverde-com:serviceId:HumiditySensor1","CurrentLevel",dID)))
return (((hCurrent >= hLow) and (hCurrent <= hHigh)) == allow)


Coming next: Light Level
« Last Edit: December 17, 2013, 06:06:33 pm by RexBeckett »

Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples - Light Level
« Reply #5 on: December 17, 2013, 06:05:53 pm »
This code will enable you to set a range of light level within which your scene will, or will not, be run. Set lLow and lHigh to define the range. As with previous generic examples, the variable allow determines whether to allow or block the scene when the current light level is within the specified range.

Generic Light Level Range:
Code: [Select]
local dID = 30           -- Device ID of your light sensor
local lLow = 0           -- Lowest level of range
local lHigh = 20         -- Highest level of range
local allow = true       -- true runs scene when in range, false blocks it
local lCurrent = tonumber((luup.variable_get("urn:micasaverde-com:serviceId:LightSensor1","CurrentLevel",dID)))
return (((lCurrent >= lLow) and (lCurrent <= lHigh)) == allow)


Coming next: VariableContainer
« Last Edit: December 17, 2013, 06:37:12 pm by RexBeckett »

Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples - VariableContainer
« Reply #6 on: December 17, 2013, 06:48:43 pm »
The VariableContainer (VC) plugin provides a convenient way in which values may be entered and changed without requiring a Vera restart. It also provides a form of scratchpad that can be used to convey values from one scene or plugin to another.

This code will enable you to set a range of values in a VC variable within which your scene will, or will not, be run. Set vLow and vHigh to define the range and vNo to define the VC variable number (1-5) in which the value is stored. As with previous generic examples, the variable allow determines whether to allow or block the scene when the VC variable value is within the specified range.

Generic VirtualContainer Value Range:
Code: [Select]
local dID = 76           -- Device ID of your VariableContainer
local vNo = 5            -- Variable number (1-5) to test
local vLow = 100         -- Lowest value of range
local vHigh = 199        -- Highest value of range
local allow = true       -- true runs scene when in range, false blocks it
local sVC = luup.variable_get("urn:upnp-org:serviceId:VContainer1","Variable" .. vNo,dID)
local vVC = tonumber(sVC) or 0
return (((vVC >= vLow) and (vVC <= vHigh)) == allow)


Another great use for VC is to allow us to change the limits in our scene Luup without restarting Vera. The following code tests for the current time to be within the period set by two VC variables. These variables must contain valid times in HH:MM form. Other than taking the Start and Stop times from VC variables, this code works the same as the earlier Time Range example.

Generic Time Range from VC Variables:
Code: [Select]
local dID = 76           -- Device ID of your VariableContainer
local vStart = 4         -- Variable number (1-5) of Start time
local vEnd = 5           -- Variable number (1-5) of End time
local allow = true       -- true runs scene when during time range, false blocks it
local pStart = luup.variable_get("urn:upnp-org:serviceId:VContainer1","Variable" .. vStart,dID) or ""
if #pStart == 0 then pStart = "00:00" end
local pEnd = luup.variable_get("urn:upnp-org:serviceId:VContainer1","Variable" .. vEnd,dID) or ""
if #pEnd == 0 then pEnd = "00:00" end
local hS, mS = string.match(pStart,"(%d+)%:(%d+)")
local mStart = (hS * 60) + mS
local hE, mE = string.match(pEnd,"(%d+)%:(%d+)")
local mEnd = (hE * 60) + mE
local tNow = os.date("*t")
local mNow = (tNow.hour * 60) + tNow.min
if mEnd >= mStart then
     return (((mNow >= mStart) and (mNow <= mEnd)) == allow)
else
     return (((mNow >= mStart) or (mNow <= mEnd)) == allow)
end
« Last Edit: December 17, 2013, 07:14:22 pm by RexBeckett »

Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples - Day Range
« Reply #7 on: December 17, 2013, 08:00:15 pm »
This code will enable you to set a range of days during which your scene will, or will not, be run. Set dFirst and dLast to define the range. These are day numbers (1-7) where 1 is Sunday. As with previous generic examples, the variable allow determines whether to allow or block the scene when the current day is within the specified range.

Generic Day Range:
Code: [Select]
local dFirst = 2         -- Start day of period (1-7) Sunday = 1
local dLast = 6          -- End day of period (1-7) Sunday = 1
local allow = true       -- true runs scene during period, false blocks it
local tNow = os.date("*t")
local dNow = tNow.wday
if dLast >= dFirst then
     return (((dNow >= dFirst) and (dNow <= dLast)) == allow)
else
     return (((dNow >= dFirst) or (dNow <= dLast)) == allow)
end


Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples - Date Range
« Reply #8 on: December 17, 2013, 09:11:40 pm »
This code will enable you to set a range of dates during which your scene will, or will not, be run. Set mdStart and mdEnd to define the range. These are dates in the form of MM/DD (in deference to our US members). The included period may span the change of year if required. As with previous generic examples, the variable allow determines whether to allow or block the scene when the current date is within the specified range.

Generic Date Range:
Code: [Select]
local mdStart = "12/01"  -- Start of period (MM/DD)
local mdEnd = "12/31"    -- End of period (MM/DD)
local allow = true       -- true runs scene during period, false blocks it
local smS, sdS = string.match(mdStart,"(%d+)%/(%d+)")
local smE, sdE = string.match(mdEnd,"(%d+)%/(%d+)")
local mS = tonumber(smS)
local dS = tonumber(sdS)
local mE = tonumber(smE)
local dE = tonumber(sdE)
local tNow = os.date("*t")
local mN = tNow.month
local dN = tNow.day
if (mE > mS) or ((mE == mS) and (dE >= dS)) then
     return (((mN > mS) or ((mN == mS) and (dN >= dS))) and ((mN < mE) or ((mN == mE) and (dN <= dE))) == allow)
else
     return (((mN > mS) or ((mN == mS) and (dN >= dS))) or ((mN < mE) or ((mN == mE) and (dN <= dE))) == allow)
end


Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples
« Reply #9 on: December 17, 2013, 10:08:38 pm »
If the preceding examples don't cover what you want to achieve:

Try the good old search facility. There's a very good chance that someone else had a similar requirement and found a solution. Google search with site:forum.micasaverde.com may give you better results than the forum's own search-engine.

You may need to combine more than one piece of code. This can be done by using a variable to hold the result of one test and and-ing or or-ing it with another. For example, to run a scene when a temperature is within a range but only during the day, try this:

Code: [Select]
local isDay = not luup.is_night()

local dID = 55           -- Device ID of your thermostatic/temperature sensor
local tLow = 18          -- Lowest temperature of range
local tHigh = 22         -- Highest temperature of range
local allow = true       -- true runs scene when in range, false blocks it
local tCurrent = tonumber((luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature",dID)))
return (((tCurrent >= tLow) and (tCurrent <= tHigh)) == allow) and isDay


If that still isn't enough, you should probably be considering the Program Logic Event Generator (PLEG) plugin. This has been designed to make complex logic simple to implement. It is particularly good for handling situations where the order or timing of events is important. It also provides highly-flexible schedules with optional random elements and it will maintain those schedules despite Vera restarts. Learning to use PLEG will almost certainly inspire you to add a level of sophistication to your automation that you never dreamed possible.   8)

Offline parkerc

  • Sr. Hero Member
  • ******
  • Posts: 2330
  • Karma: +32/-44
  • Life Moves Pretty Fast....
    • Node Central
Re: Conditional Scene Execution: Some Examples - Day or Night
« Reply #10 on: December 18, 2013, 02:21:09 am »
Excellent job !!!

Thank you so much for doing this. (Karma Points heading your way)

If you're feeling up to it, a brief tutorial on functions would be much appreciated too.

Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Conditional Scene Execution: Some Examples - Multiple Conditions
« Reply #11 on: December 18, 2013, 11:01:08 am »
You can combine multiple conditions in scene or trigger Luup by converting each piece of code into a Lua function. Then you can combine the results of each test in a single return statement using and/or operators.

Converting to a function is easy:
Code: [Select]
local function functionName()
     <lines of code>
end

Combining the results is also simple:
Code: [Select]
return function1() and function2()

Example to allow a scene to run between 08:00 and 22:30 at weekends:
Code: [Select]
local function checkTime()
     local pStart = "08:00"   -- Start of time period
     local pEnd = "22:30"     -- End of time period
     local allow = true       -- true runs scene during period, false blocks it
     local hS, mS = string.match(pStart,"(%d+)%:(%d+)")
     local mStart = (hS * 60) + mS
     local hE, mE = string.match(pEnd,"(%d+)%:(%d+)")
     local mEnd = (hE * 60) + mE
     local tNow = os.date("*t")
     local mNow = (tNow.hour * 60) + tNow.min
     if mEnd >= mStart then
          return (((mNow >= mStart) and (mNow <= mEnd)) == allow)
     else
          return (((mNow >= mStart) or (mNow <= mEnd)) == allow)
     end
end

local function checkDay()
     local dFirst = 7         -- Start day of period (1-7) Sunday = 1
     local dLast = 1          -- End day of period (1-7) Sunday = 1
     local allow = true       -- true runs scene during period, false blocks it
     local tNow = os.date("*t")
     local dNow = tNow.wday
     if dLast >= dFirst then
          return (((dNow >= dFirst) and (dNow <= dLast)) == allow)
     else
          return (((dNow >= dFirst) or (dNow <= dLast)) == allow)
     end
end

return checkTime() and checkDay()
« Last Edit: April 02, 2014, 03:12:34 pm by RexBeckett »

Offline niharmehta

  • Sr. Member
  • ****
  • Posts: 322
  • Karma: +14/-0
Re: Conditional Scene Execution: Some Examples
« Reply #12 on: December 19, 2013, 03:53:33 am »
Fantastic work!.   This should be recommended reading for everyone and should be made sticky.
2x VeraLite; 2xTrane Tstats; 45 x Switches/Dimmers/Appliance Modules; 4x Everspring Water Sensors; DSC Integration; 2 x Zwave Door Locks; 1x Ted5K; 1x Rainforest Eagle; Onkyo AVR; 6x Squeezebox;

Offline garrettwp

  • Beta Testers
  • Master Member
  • *****
  • Posts: 6376
  • Karma: +227/-128
  • Vera 3, Lite, ISY994
Re: Conditional Scene Execution: Some Examples
« Reply #13 on: December 19, 2013, 07:59:02 am »
Made this topic a stick. To make things easier, can we have some sort of index on the first post pointing to each post? This will allow a member to click and go strait to the post without having to scroll and work through other posts that other members reply to.

- Garrett

Offline RexBeckett

  • Master Member
  • *******
  • Posts: 3888
  • Karma: +477/-10
Re: Conditional Scene Execution: Some Examples
« Reply #14 on: December 19, 2013, 05:48:30 pm »
Made this topic a stick. To make things easier, can we have some sort of index on the first post pointing to each post? This will allow a member to click and go strait to the post without having to scroll and work through other posts that other members reply to.

- Garrett
Thanks for making this a sticky, Garrett. The index is a good idea - duly added.