Luup Scenes Events

From MiOS
(Difference between revisions)
Jump to: navigation, search
(Thermostat conditioned by door/window)
 
(37 intermediate revisions by 7 users not shown)
Line 1: Line 1:
== Adding Lua code to scenes and events ==
+
== Adding Lua code to scenes and events ==
  
You can add Lua script to scenes and events for simple tasks, like making a scene or event conditional.  Conditional meaning "do something only '''if''' some condition is met", such as attaching a condition to your "Come Home" scene so it is only run '''if''' the temperature is over 70 degrees.  Basic conditional expressions are easy, and, if that's all you're looking to do, skip to the walk-through below.
+
[[Image:SceneFlowchart.png]]
  
If you're more technically inclined you can also do very advanced things too. For an overview of all the advanced things you can do with Vera's Luup engine, and how scenes and events fit in, see the [[Luup_Intro]] page.
+
You can add Lua script to scenes and events for simple tasks, like making a scene or event conditional. Conditional meaning "do something only '''if''' some condition is met", such as attaching a condition to your "Come Home" scene so it is only run '''if''' the temperature is over 70 degrees. Basic conditional expressions are easy, and, if that's all you're looking to do, skip to the walk-through below.  
  
To add Lua code to a scene, create the [[Scenes]] and click 'Luup scene'.  Fill in your Lua code in the input box.  To do a simple condition, see the sample below.  Or if you want to do advanced scripting you can use any [[http://lua.org Lua]] commands as described in the [[http://www.lua.org/manual/5.1/ Lua reference manual]] as well as the variables and functions that the Luup engine adds to Lua documented here: [[Luup_Lua_extensions]].  The Lua code will be run every time the scene is activated either by the user or a scene or a timer.  The Lua code is run before the commands that you included in the scene.  If your Lua code ends with this: "return false" then the commands in the scene will not be run.
+
If you're more technically inclined you can also do very advanced things too. For an overview of all the advanced things you can do with Vera's Luup engine, and how scenes and events fit in, see the [[Luup Intro]] page.  
  
You can also add Lua code to an event by clicking 'Luup event'. Since events are attached to scenes anyway, there is usually little difference between adding the code to an event, or to the scene the event is part of. If the Lua code in an event returns false, then the event is aborted, meaning the scene that the event is attached to will not be triggered by the event. The main reason for attaching Lua code to an event is if you have multiple events to a scene. For example, you may have a scene called "Turn lights on in hallway" which is triggered by 2 events: 1) The front door opens, and 2) the motion sensor in the hallway is tripped.  Perhaps whenever the front door opens you always want the scene to be activated, but you only want the motion sensor to activate the scene before 10:00. In this case, you would add Lua code to the motion sensor's event which does something like (pseudo-code): "if time>10:00 return false".  That way the event from the motion sensor will be aborted if it's after 10:00, but the event from the front door will always activate the scene regardless.
+
To add Lua code to a scene, create the [[Scenes]] and click 'Luup scene'. Fill in your Lua code in the input box. To do a simple condition, see the sample below. Or if you want to do advanced scripting you can use any [[http://lua.org Lua]] commands as described in the [[http://www.lua.org/manual/5.1/ Lua reference manual]] as well as the variables and functions that the Luup engine adds to Lua documented here: [[Luup Lua extensions]]. The Lua code will be run every time the scene is activated either by the user or a scene or a timer. The Lua code is run before the commands that you included in the scene. If your Lua code ends with this: "return false" then the commands in the scene will not be run.  
  
When you edit the Lua code in a scene or event you must click 'Save' before the code is saved. Then you can activate the scene or trigger the event to see what happens when your code is run. If you're doing some advanced scripting that you'll need to debug this can be tedious, but there are easy ways to test your Lua script immediately without saving first as explained in [[Lua_Debugging]].
+
You can also add Lua code to an event by clicking 'Luup event'. Since events are attached to scenes anyway, there is usually little difference between adding the code to an event, or to the scene the event is part of. If the Lua code in an event returns false, then the event is aborted, meaning the scene that the event is attached to will not be triggered by the event. The main reason for attaching Lua code to an event is if you have multiple events to a scene. For example, you may have a scene called "Turn lights on in hallway" which is triggered by 2 events: 1) The front door opens, and 2) the motion sensor in the hallway is tripped. Perhaps whenever the front door opens you always want the scene to be activated, but you only want the motion sensor to activate the scene before 10:00. In this case, you would add Lua code to the motion sensor's event which does something like (pseudo-code): "if time>10:00 return false". That way the event from the motion sensor will be aborted if it's after 10:00, but the event from the front door will always activate the scene regardless.  
  
If you're doing advanced Lua scripting, you should note that all the Lua code in your scenes and events run in a single Lua instance, which is separate from any Lua plugins. This means if you set a global variable in one scene, or create a function inside the Lua code in a scene, then in another scene's Lua code you can use that global variable or call that function. You do not need to worry about conflicting with a Luup plugin, though, since they have their own Lua instance, meaning they have their own global variables and functions.
+
When you edit the Lua code in a scene or event you must click 'Save' before the code is saved. Then you can activate the scene or trigger the event to see what happens when your code is run. If you're doing some advanced scripting that you'll need to debug this can be tedious, but there are easy ways to test your Lua script immediately without saving first as explained in [[Lua Debugging]].
 +
 
 +
If you're doing advanced Lua scripting, you should note that all the Lua code in your scenes and events run in a single Lua instance, which is separate from any Lua plugins. This means if you set a global variable in one scene, or create a function inside the Lua code in a scene, then in another scene's Lua code you can use that global variable or call that function. You do not need to worry about conflicting with a Luup plugin, though, since they have their own Lua instance, meaning they have their own global variables and functions.  
  
 
== Walk-through #1 -- At 12 noon, turn off the interior lights if the temperature is over 80 degrees  ==
 
== Walk-through #1 -- At 12 noon, turn off the interior lights if the temperature is over 80 degrees  ==
Line 19: Line 21:
 
In this walkthrough we'll assume it's Device #3, but use the actual device number of''your'' thermostat. Next, visit [[Luup Variables]] to get a list of all the variables for devices. A variable is a piece of information about the current state of a device, such as whether it's on or off, it's current temperature, etc.  
 
In this walkthrough we'll assume it's Device #3, but use the actual device number of''your'' thermostat. Next, visit [[Luup Variables]] to get a list of all the variables for devices. A variable is a piece of information about the current state of a device, such as whether it's on or off, it's current temperature, etc.  
  
Look down at ''Thermostat'', and copy the name of the service/variable which corresponds to the current temperature, namely. In this case, it's
+
Look down at ''Thermostat'', and copy the name of the service/variable which corresponds to the current temperature, namely. In this case, it's  
  
 
   urn:upnp-org:serviceId:TemperatureSensor1 CurrentTemperature
 
   urn:upnp-org:serviceId:TemperatureSensor1 CurrentTemperature
Line 31: Line 33:
 
Third, the last step is to add the condition. To the right of the scene's description you'll see the button "Luup Scene". Click it and in the code box, copy and paste the following:  
 
Third, the last step is to add the condition. To the right of the scene's description you'll see the button "Luup Scene". Click it and in the code box, copy and paste the following:  
  
   local lul_temp=luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature",3)
+
<source lang="lua">
   if( tonumber(lul_temp)&lt;26.6 ) then
+
   local lul_temp = luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature", 3)
 +
   if (tonumber(lul_temp) < 26.6) then
 
     return false
 
     return false
 
   end
 
   end
 +
</source>
  
 
Don't forget to change the "3" to whatever is the actual device number of your thermostat. Assign the result of <tt>luup.variable_get</tt> to a variable first, rather than putting it directly in the <tt>tonumber()</tt>, because <tt>luup.variable_get</tt> actually returns 2 values: the value of the variable, and the time when the variable was modified. The '<tt>tonumber</tt>' is needed because all of a device's variables are stored as plain text--not numbers--so if you want to do arithmetic or numeric comparison of a variable, you need to put <tt>tonumber()</tt> around <tt>luup.variable_get</tt>.  
 
Don't forget to change the "3" to whatever is the actual device number of your thermostat. Assign the result of <tt>luup.variable_get</tt> to a variable first, rather than putting it directly in the <tt>tonumber()</tt>, because <tt>luup.variable_get</tt> actually returns 2 values: the value of the variable, and the time when the variable was modified. The '<tt>tonumber</tt>' is needed because all of a device's variables are stored as plain text--not numbers--so if you want to do arithmetic or numeric comparison of a variable, you need to put <tt>tonumber()</tt> around <tt>luup.variable_get</tt>.  
Line 42: Line 46:
 
Now click 'Update', and then click 'Save' to save everything. The "return false" means "don't run this scene". So if the current temperature is &lt;26.6, the scene will be aborted and won't run. The timer will make it trigger every day at 12 noon.  
 
Now click 'Update', and then click 'Save' to save everything. The "return false" means "don't run this scene". So if the current temperature is &lt;26.6, the scene will be aborted and won't run. The timer will make it trigger every day at 12 noon.  
  
To test it, you can click the scene button on the dashboard. The lights will turn off only if the temperature is over 80 degrees. If it's less than 80 degrees, the scene won't do anything. Since this scene is something that happens automatically and you probably won't execute manually, you can go to the scene again and check the "Hidden" box so the scene doesn't show up on&nbsp;Vera's 'Dashboard' and on your remote controls, like the iPhone. If you want to have a scene that turns off the lights which you can run whenever you want from 'Dashboard'' or your remote control, you should create another scene that has the same commands and simply don't add the timers and Luup conditions, and don't check the "Hidden" box.  
+
To test it, you can click the scene button on the dashboard. The lights will turn off only if the temperature is over 80 degrees. If it's less than 80 degrees, the scene won't do anything. Since this scene is something that happens automatically and you probably won't execute manually, you can go to the scene again and check the "Hidden" box so the scene doesn't show up on&nbsp;Vera's 'Dashboard' and on your remote controls, like the iPhone. If you want to have a scene that turns off the lights which you can run whenever you want from 'Dashboard''or your remote control, you should create another scene that has the same commands and simply don't add the timers and Luup conditions, and don't check the "Hidden" box. ''
  
 
You can substitute other service/variables and device ID's to make other types of conditions. The "if" statement above also supports nesting with ( and ), as well as the keywords 'and' and 'or'. So the following means the scene would abort if the temperature is &lt;26.6 and &gt;25, unless it's &lt;23:  
 
You can substitute other service/variables and device ID's to make other types of conditions. The "if" statement above also supports nesting with ( and ), as well as the keywords 'and' and 'or'. So the following means the scene would abort if the temperature is &lt;26.6 and &gt;25, unless it's &lt;23:  
  
   local lul_temp=luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature",3)
+
<source lang="lua">
   if( (tonumber(lul_temp)&lt;26.6
+
   local lul_temp = luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature", 3)
     and tonumber(lul_temp)&gt;25)
+
   if ((tonumber(lul_temp) < 26.6
     or tonumber(lul_temp)&lt;23 ) then
+
     and tonumber(lul_temp) > 25)
 +
     or tonumber(lul_temp) < 23) then
 
       return false
 
       return false
 
   end
 
   end
 +
</source>
  
 
If the scene doesn't run, it's possible&nbsp;there is&nbsp;a syntax error. The easiest way to test this is to copy the Lua code from the scene, then go to Devices, Luup Plugins, and "Test Luup code". Paste the code in the box and click 'go'. If the info box above the 'go' button has a check and says "Message sent successful", your code is okay. If there's an error, it says: "Code failed".  
 
If the scene doesn't run, it's possible&nbsp;there is&nbsp;a syntax error. The easiest way to test this is to copy the Lua code from the scene, then go to Devices, Luup Plugins, and "Test Luup code". Paste the code in the box and click 'go'. If the info box above the 'go' button has a check and says "Message sent successful", your code is okay. If there's an error, it says: "Code failed".  
  
To see if that's true, use putty, or telnet or ssh to log-in to Vera, as explained in detail in [[Lua Debugging]], and type:  
+
To see if that's true, use putty, or telnet or ssh to log-in to Vera, as explained in detail in [[Logon_Vera_SSH]] and [[Lua Debugging]], and type:  
  
 
   cd /var/log/cmh
 
   cd /var/log/cmh
  tail -f LuaUPnP.log | grep '^01'
+
tail -f LuaUPnP.log | grep '^01'
  
 
Now click 'Save' in Vera's setup page, even if it's gray, as that will cause Vera to restart the Luup engine and log any syntax errors. See: [[Lua Debugging]] for in-depth details on how to debug.
 
Now click 'Save' in Vera's setup page, even if it's gray, as that will cause Vera to restart the Luup engine and log any syntax errors. See: [[Lua Debugging]] for in-depth details on how to debug.
  
== Samples ==
+
== Walk-through #2 -- Only run the scene during the daytime  ==
  
This page is a wiki which anyone can edit.  If you have some Lua code you think other users might find useful, feel free to add it here.
+
In the Luup tab for the scene paste this:
  
=== Misc actions ===
+
<source lang="lua">
 +
  return luup.is_night() == false
 +
</source>
  
Did you see the sample here already: http://wiki.micasaverde.com/index.php/Luup_Scenes_Events
+
That works because if the return is 'true' the scene runs, and if it's 'false' it doesn't. So during the daytime the expression is true. Or, an alternative long form:
  
====Turn an appliance switch or a Danfoss thermostat on for device #5====
+
<source lang="lua">
 +
  if (luup.is_night()) then
 +
    return false
 +
  else
 +
    return true
 +
  end
 +
</source>
 +
 
 +
== Samples  ==
 +
 
 +
This page is a wiki which anyone can edit. If you have some Lua code you think other users might find useful, feel free to add it here.
 +
 
 +
=== Lighting and Switch Actions  ===
 +
 
 +
Did you see the sample here already: http://wiki.micasaverde.com/index.php/Luup_Scenes_Events
 +
 
 +
==== Turn an appliance switch or a Danfoss thermostat on for device #5 ====
  
   luup.call_action("urn:upnp-org:serviceId:SwitchPower1","SetTarget",{ newTargetValue="1" },5)
+
<source lang="lua">
 +
   luup.call_action("urn:upnp-org:serviceId:SwitchPower1", "SetTarget", {newTargetValue = "1"}, 5)
 +
</source>
  
====Turn an appliance switch or a Danfoss thermostat off====
+
==== Turn an appliance switch or a Danfoss thermostat off ====
  
   luup.call_action("urn:upnp-org:serviceId:SwitchPower1","SetTarget",{ newTargetValue="0" },5)
+
<source lang="lua">
 +
   luup.call_action("urn:upnp-org:serviceId:SwitchPower1", "SetTarget", {newTargetValue = "0"}, 5)
 +
</source>
  
====Do something if switch device #5 is on====
+
==== Do something if switch device #5 is on ====
  
   local lul_tmp = luup.variable_get("urn:upnp-org:serviceId:SwitchPower1","Status",5)
+
<source lang="lua">
   if( lul_tmp=="1" ) then
+
   local lul_tmp = luup.variable_get("urn:upnp-org:serviceId:SwitchPower1", "Status", 5)
 +
   if (lul_tmp == "1") then
 
     --something to do goes here
 
     --something to do goes here
 
   end
 
   end
 +
</source>
  
====Dim switch #6 to 30%====
+
==== Dim switch #6 to 30% ====
  
   luup.call_action("urn:upnp-org:serviceId:Dimming1","SetLoadLevelTarget",{ newLoadlevelTarget="30" },6)
+
<source lang="lua">
 +
   luup.call_action("urn:upnp-org:serviceId:Dimming1", "SetLoadLevelTarget", {newLoadlevelTarget = "30"}, 6)
 +
</source>
  
====Arm motion sensor #7====
+
====Switch on, and switch off 2 seconds later ====
 +
<source lang="lua">
 +
local device  = 29
 +
-- Switch on device 29:
 +
luup.call_action("urn:upnp-org:serviceId:SwitchPower1","SetTarget",{ newTargetValue="1" },device)
 +
luup.call_delay( 'switch_off', 2) -- Call the switch off function after a delay of 2 seconds
 +
function switch_off()
 +
  luup.call_action("urn:upnp-org:serviceId:SwitchPower1","SetTarget",{ newTargetValue="0" },device)
 +
end
 +
</source>
  
  luup.variable_set("urn:micasaverde-com:serviceId:SecuritySensor1","Armed","1",7)
+
==== Blinking lights (and how to delay for a number of seconds) ====
  
====Disarm motion sensor #7====
+
http://forum.micasaverde.com/index.php?topic=5127.0
  
  luup.variable_set("urn:micasaverde-com:serviceId:SecuritySensor1","Armed","0",7)
+
=== Motion Sensor Actions  ===
  
Note, arming and disarming isn't a concept within UPnP or Z-Wave. It's just a flag that the Luup engine uses, and is stored in a variable we created called "Armed".
+
==== Arm motion sensor #7 ====
  
====Run Scene #5====
+
<source lang="lua">
 +
  luup.variable_set("urn:micasaverde-com:serviceId:SecuritySensor1", "Armed", "1", 7)
 +
</source>
  
  luup.call_action("urn:micasaverde-com:serviceId:HomeAutomationGateway1","RunScene",{ SceneNum="5" })
+
==== Disarm motion sensor #7  ====
  
In this case we left the device number off (the 4th parameter to <tt>luup.call_action</tt>), because the "<tt>RunScene</tt>" action is handled by the Luup engine itself--not by some device within Z-Wave, etc.
+
<source lang="lua">
 +
  luup.variable_set("urn:micasaverde-com:serviceId:SecuritySensor1", "Armed", "0", 7)
 +
</source>  
  
However, normally you don't need to <tt>luup.call_action</tt> in Lua code.  Rather, whatever actions, or commands, you want to run, you put into the scene itself, and the only Lua code is to simply check if some condition is true and abort the scene if the condition isn't met.
+
Note, arming and disarming isn't a concept within UPnP or Z-Wave. It's just a flag that the Luup engine uses, and is stored in a variable we created called "Armed".  
  
====Change the Temperature on Thermostat (Cool) device #19====
+
=== Scene Actions  ===
 +
 
 +
==== Run Scene #5  ====
 +
 
 +
Thanks "denix" on the forum for the correct syntax. "Actually, the 4th parameter IS required, but it's not used. Otherwise the command fails with an error message"
 +
 
 +
<source lang="lua">
 +
  luup.call_action("urn:micasaverde-com:serviceId:HomeAutomationGateway1", "RunScene", {SceneNum = "5"}, 0)
 +
</source>
 +
 
 +
In this case we left the device number off (the 4th parameter to <tt>luup.call_action</tt>), because the "<tt>RunScene</tt>" action is handled by the Luup engine itself--not by some device within Z-Wave, etc.
 +
 
 +
However, normally you don't need to <tt>luup.call_action</tt> in Lua code. Rather, whatever actions, or commands, you want to run, you put into the scene itself, and the only Lua code is to simply check if some condition is true and abort the scene if the condition isn't met.
 +
 
 +
=== Thermostat Actions  ===
 +
 
 +
==== Change the Temperature on Thermostat (Cool) device #19 ====
 +
 
 +
<source lang="lua">
 
   luup.call_action("urn:upnp-org:serviceId:TemperatureSetpoint1_Cool",
 
   luup.call_action("urn:upnp-org:serviceId:TemperatureSetpoint1_Cool",
                   "SetCurrentSetpoint", { NewCurrentSetpoint="68" },
+
                   "SetCurrentSetpoint", {NewCurrentSetpoint = "68"},
 
                   19)
 
                   19)
 +
</source>
  
====Change the Temperature on a Thermostat (Heat) device #19====
+
==== Change the Temperature on a Thermostat (Heat) device #19 ====
 +
 
 +
<source lang="lua">
 
   luup.call_action("urn:upnp-org:serviceId:TemperatureSetpoint1_Heat",
 
   luup.call_action("urn:upnp-org:serviceId:TemperatureSetpoint1_Heat",
                   "SetCurrentSetpoint",{ NewCurrentSetpoint="68" },
+
                   "SetCurrentSetpoint", {NewCurrentSetpoint = "68"},
 
                   19)
 
                   19)
 +
</source>
 +
 +
==== Change the Thermostat Operating mode device #19  ====
 +
 +
<source lang="lua">
 +
luup.call_action("urn:upnp-org:serviceId:HVAC_UserOperatingMode1",
 +
                "SetModeTarget", {NewModeTarget = "Off"},
 +
                19)
 +
</source>
 +
 +
<br>
 +
 +
=== Camera Actions  ===
 +
 +
==== 'Privacy' mode for Foscam FI8908[w]  ====
 +
 +
<source lang="lua">
 +
  local IP_address = '<IP address of camera>'
 +
  local username = '<username>'
 +
  local password = '<password>'
 +
  local timeout  = 5
 +
 +
  function move_up()
 +
    luup.inet.wget('http://' .. IP_address .. '/decoder_control.cgi?command=0', timeout, username, password)
 +
  end
 +
 +
  -- center the camera; takes some time, so we have to wait 2 minutes for the command to complete
 +
 +
  luup.inet.wget('http://' .. IP_address .. '/decoder_control.cgi?command=25', timeout, username, password)
 +
 +
  luup.call_delay('move_up', 120)
 +
</source>
 +
 +
=== Misc Actions  ===
 +
 +
==== Playing an announcement  ====
 +
 +
http://forum.micasaverde.com/index.php?topic=5466.msg36405#msg36405
 +
 +
=== Calculate sunrise and sunset  ===
 +
 +
See http://forum.micasaverde.com/index.php?topic=2073.msg8132#msg8132
 +
 +
... or use DAD: http://forum.micasaverde.com/index.php?topic=5466.0
 +
 +
=== Access the web  ===
 +
 +
==== Invoke HTTP URL with <tt>GET</tt> request (Method 1)  ====
 +
 +
<source lang="lua">
 +
  -- 5 Second timeout
 +
  local status, result = luup.inet.wget("http://www.yahoo.com", 5)
 +
</source>
 +
 +
==== Invoke HTTP URL with <tt>GET</tt> request (Method 2)  ====
 +
 +
Based on code by Jim/jgc94131 <source lang="lua">
 +
  require('ltn12')
 +
  local http = require('socket.http')
 +
 +
  -- 5 Second timeout
 +
  socket.http.TIMEOUT = 5
 +
 +
  local response_body = {}
 +
  local request_body = ''
 +
 +
  local r, c, h = socket.http.request{
 +
    url = 'http://website/page?parameter1=value&parameter2=value',
 +
    method = "GET",
 +
    port = 80,
 +
    headers = {
 +
      ["Content-Length"] = string.len(request_body),
 +
      ["Content-Type"] = "application/x-www-form-urlencoded"
 +
    },
 +
    source = ltn12.source.string(request_body),
 +
    sink = ltn12.sink.table(response_body)
 +
  }
 +
</source>
 +
 +
==== Invoke HTTP URL with <tt>POST</tt> request (Method 3)  ====
 +
 +
<source lang="lua">
 +
  local http = require("socket.http")
 +
 +
  -- 5 Second timeout
 +
  http.TIMEOUT = 5
 +
 +
  -- The return parameters are in a different order from luup.inet.wget(...)
 +
  result, status = http.request("http://192.168.0.113/runprocess.htm", "run=run")   
 +
</source>
 +
 +
=== Access the current time  ===
 +
 +
The Lua function
 +
 +
<source lang="lua">
 +
  os.date (format, time)
 +
</source>
 +
 +
converts a time value `time` into a human readable date/time string, according to `format`. If you leave out the optional `time` parameter, it defaults to current time. The `format` parameter defaults to a fairly complete format. If you specify '*t' as the format, it will return a table instead of a formatted string.
 +
 +
<source lang="lua">
 +
  t = os.date('*t')
 +
  t = {year=2010, month=2, day=19, yday=50, wday=6, hour=22, min=45, sec=45, isdst=false}
 +
</source>
 +
 +
The fields are year, month, day of month, day of year, day of week, hour in 24 hour clock, minutes, seconds and if it's Daylight Savings Time.
 +
 +
Current hour:
 +
 +
<source lang="lua">
 +
  os.date('*t').hour
 +
</source>
 +
 +
Current minute:
 +
 +
<source lang="lua">
 +
  os.date('*t').min
 +
</source>
 +
 +
Current second:
 +
 +
<source lang="lua">
 +
  os.date('*t').sec
 +
</source>
 +
 +
Do something between 16:00 and 21:15:
 +
 +
<source lang="lua">
 +
  local t = os.date('*t')
 +
  local current_second = t.hour * 3600 + t.min * 60 + t.sec  -- number of seconds since midnight
 +
  local min_time_in_seconds = 16 * 3600 +  0 * 60            -- 16:00
 +
  local max_time_in_seconds = 21 * 3600 + 15 * 60            -- 21:15
 +
 +
  if (current_second > min_time_in_seconds) and (current_second < max_time_in_seconds) then
 +
    -- do something
 +
  else
 +
    return false
 +
  end
 +
</source>
 +
 +
See http://forum.micasaverde.com/index.php?topic=2015.0 and http://www.lua.org/manual/5.1/manual.html#5.8.
 +
 +
=== Set Z-Wave parameters  ===
 +
 +
See http://forum.micasaverde.com/index.php?topic=1937.msg7803#msg7803
 +
 +
=== a scene if the temperature is outside of a range  ===
 +
 +
add snippets here...
 +
 +
=== Reload Luup at 3 AM every day  ===
 +
 +
1. Create a scene and set it to run daily at 3 AM.
 +
 +
2. Add this code in the '''Luup code''' box:
 +
 +
<source lang="lua">luup.call_action("urn:micasaverde-com:serviceId:HomeAutomationGateway1", "Reload", {}, 0)</source>
 +
 +
3. Save.
 +
 +
=== Thermostat conditioned by door/window  ===
 +
 +
Turn off the thermostat if the door/window is left open for 5 minutes or more and back on if the door/window is closed for 10 minutes or more.
 +
 +
'''1.''' Create a scene and add a '''trigger''' to run the scene when the '''door sensor is tripped''' (door is opened).
 +
 +
'''2.''' Add this code in the '''Luup code''' box: <source lang="lua">
 +
local SENSOR = 17    -- The door/window sensor device number
 +
local THERMOSTAT = 3 -- The thermostat device number
 +
local DELAY = 300    -- Seconds
 +
 +
local SES_SID = "urn:micasaverde-com:serviceId:SecuritySensor1"
 +
local HVACO_SID = "urn:upnp-org:serviceId:HVAC_UserOperatingMode1"
 +
 +
luup.call_delay( "turnOffAc", DELAY)
 +
 +
-- Turn off the thermostat if the sensor has been tripped for at least 5 minutes.
 +
function turnOffAc()
 +
    local tripped = luup.variable_get( SES_SID, "Tripped", SENSOR) or "0"
 +
    local lastTrip = luup.variable_get( SES_SID, "LastTrip", SENSOR) or os.time()
 +
    if (tripped == "1" and (os.time() - lastTrip >= DELAY)) then
 +
        local modeStatus = luup.variable_get( HVACO_SID, "ModeStatus", THERMOSTAT) or "Off"
 +
        luup.variable_set( HVACO_SID, "LastModeStatus", modeStatus, THERMOSTAT)
 +
        luup.call_action( HVACO_SID, "SetModeTarget", {NewModeTarget = "Off"}, THERMOSTAT)
 +
    end
 +
end
 +
</source>
 +
 +
<br> '''3.''' Create another scene and add a '''trigger''' to run the scene when the '''door sensor is not tripped''' (door is closed).
 +
 +
'''4.''' Add this code in the '''Luup code''' box: <source lang="lua">
 +
local SENSOR = 17    -- The door/window sensor device number
 +
local THERMOSTAT = 3 -- The thermostat device number
 +
local DELAY = 600    -- Seconds
 +
 +
local SES_SID = "urn:micasaverde-com:serviceId:SecuritySensor1"
 +
local HVACO_SID = "urn:upnp-org:serviceId:HVAC_UserOperatingMode1"
 +
 +
luup.call_delay( "turnOnAc", DELAY)
  
===  a scene if the temperature is outside of a range ===
+
-- Turn on the thermostat if the sensor hasn't been tripped in the past 10 minutes.
 +
function turnOnAc()
 +
    local tripped = luup.variable_get( SES_SID, "Tripped", SENSOR) or "0"
 +
    local lastTrip = luup.variable_get( SES_SID, "LastTrip", SENSOR) or os.time()
 +
    if (tripped == "0" and (os.time() - lastTrip >= DELAY)) then
 +
        local lastModeStatus = luup.variable_get( HVACO_SID, "LastModeStatus", THERMOSTAT) or "Off"
 +
        luup.call_action( HVACO_SID, "SetModeTarget", {NewModeTarget = lastModeStatus}, THERMOSTAT)
 +
    end
 +
end
 +
</source>
  
=== Calculate sunrise and sunset
+
'''5.''' Save.
  
=== How to access the current time
+
[[Category:Development]]
add snippets here...
+

Latest revision as of 22:05, 23 April 2013

Contents

[edit] Adding Lua code to scenes and events

SceneFlowchart.png

You can add Lua script to scenes and events for simple tasks, like making a scene or event conditional. Conditional meaning "do something only if some condition is met", such as attaching a condition to your "Come Home" scene so it is only run if the temperature is over 70 degrees. Basic conditional expressions are easy, and, if that's all you're looking to do, skip to the walk-through below.

If you're more technically inclined you can also do very advanced things too. For an overview of all the advanced things you can do with Vera's Luup engine, and how scenes and events fit in, see the Luup Intro page.

To add Lua code to a scene, create the Scenes and click 'Luup scene'. Fill in your Lua code in the input box. To do a simple condition, see the sample below. Or if you want to do advanced scripting you can use any [Lua] commands as described in the [Lua reference manual] as well as the variables and functions that the Luup engine adds to Lua documented here: Luup Lua extensions. The Lua code will be run every time the scene is activated either by the user or a scene or a timer. The Lua code is run before the commands that you included in the scene. If your Lua code ends with this: "return false" then the commands in the scene will not be run.

You can also add Lua code to an event by clicking 'Luup event'. Since events are attached to scenes anyway, there is usually little difference between adding the code to an event, or to the scene the event is part of. If the Lua code in an event returns false, then the event is aborted, meaning the scene that the event is attached to will not be triggered by the event. The main reason for attaching Lua code to an event is if you have multiple events to a scene. For example, you may have a scene called "Turn lights on in hallway" which is triggered by 2 events: 1) The front door opens, and 2) the motion sensor in the hallway is tripped. Perhaps whenever the front door opens you always want the scene to be activated, but you only want the motion sensor to activate the scene before 10:00. In this case, you would add Lua code to the motion sensor's event which does something like (pseudo-code): "if time>10:00 return false". That way the event from the motion sensor will be aborted if it's after 10:00, but the event from the front door will always activate the scene regardless.

When you edit the Lua code in a scene or event you must click 'Save' before the code is saved. Then you can activate the scene or trigger the event to see what happens when your code is run. If you're doing some advanced scripting that you'll need to debug this can be tedious, but there are easy ways to test your Lua script immediately without saving first as explained in Lua Debugging.

If you're doing advanced Lua scripting, you should note that all the Lua code in your scenes and events run in a single Lua instance, which is separate from any Lua plugins. This means if you set a global variable in one scene, or create a function inside the Lua code in a scene, then in another scene's Lua code you can use that global variable or call that function. You do not need to worry about conflicting with a Luup plugin, though, since they have their own Lua instance, meaning they have their own global variables and functions.

[edit] Walk-through #1 -- At 12 noon, turn off the interior lights if the temperature is over 80 degrees

Before you start, in Vera's setup interface go to 'Devices' and click the '+' button next to the thermostat. Make note of the Device #.

In this walkthrough we'll assume it's Device #3, but use the actual device number ofyour thermostat. Next, visit Luup Variables to get a list of all the variables for devices. A variable is a piece of information about the current state of a device, such as whether it's on or off, it's current temperature, etc.

Look down at Thermostat, and copy the name of the service/variable which corresponds to the current temperature, namely. In this case, it's

 urn:upnp-org:serviceId:TemperatureSensor1 CurrentTemperature

Notice that it states the CurrentTemperature is in Celsius. So type in "80 degrees Fahrenheit to Celsius" in Google and you'll see that it's 26.6 degrees Celsius.

Now, the first step is to create the scene that turns off the lights. In Vera's setup interface, click 'Scenes', and click 'Add Scene' to add a new scene to one of the rooms. It's not important which room you choose. Scenes are categorized in rooms just to help you keep track of them if you have a lot of scenes. You can also put the scene in 'Global Scenes', or, you can create dummy rooms on the 'Rooms' tab if you want to have more "rooms" to organize your scenes with. After you click 'Add Scene', type in a description to remember your scene by, such as "Lights off 12:00 if 80". Under the 'Commands' area you'll see all the rooms. Click '+' next to the rooms that have lights you want to control, and choose "Off" in the pull-down. At this point, you have a normal scene, and, if you were to save your changes now, whenever you click the scene on the dashboard or on a remote control, the lights should turn off.

Second, next to the scene's description click 'add timer'. You can give the timer a description too so that if you have multiple timers you can see in the logs which one is activating the scene. Choose "Day of week based". If you want this scene to only run on certain days of the week, just check off which days you want this scene to run on. Otherwise, you can leave them all unchecked (or check them all) to do it every day. Leave the pull-down at "a certain time of day", and choose 12 : 00 : 00 from the pull-downs. At this point, if you were to save your changes, the lights would turn off automatically at 12 noon.

Third, the last step is to add the condition. To the right of the scene's description you'll see the button "Luup Scene". Click it and in the code box, copy and paste the following:

  local lul_temp = luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature", 3)
  if (tonumber(lul_temp) < 26.6) then
    return false
  end

Don't forget to change the "3" to whatever is the actual device number of your thermostat. Assign the result of luup.variable_get to a variable first, rather than putting it directly in the tonumber(), because luup.variable_get actually returns 2 values: the value of the variable, and the time when the variable was modified. The 'tonumber' is needed because all of a device's variables are stored as plain text--not numbers--so if you want to do arithmetic or numeric comparison of a variable, you need to put tonumber() around luup.variable_get.

luup.variable_get is documented in Luup Lua extensions.

Now click 'Update', and then click 'Save' to save everything. The "return false" means "don't run this scene". So if the current temperature is <26.6, the scene will be aborted and won't run. The timer will make it trigger every day at 12 noon.

To test it, you can click the scene button on the dashboard. The lights will turn off only if the temperature is over 80 degrees. If it's less than 80 degrees, the scene won't do anything. Since this scene is something that happens automatically and you probably won't execute manually, you can go to the scene again and check the "Hidden" box so the scene doesn't show up on Vera's 'Dashboard' and on your remote controls, like the iPhone. If you want to have a scene that turns off the lights which you can run whenever you want from 'Dashboardor your remote control, you should create another scene that has the same commands and simply don't add the timers and Luup conditions, and don't check the "Hidden" box.

You can substitute other service/variables and device ID's to make other types of conditions. The "if" statement above also supports nesting with ( and ), as well as the keywords 'and' and 'or'. So the following means the scene would abort if the temperature is <26.6 and >25, unless it's <23:

  local lul_temp = luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature", 3)
  if ((tonumber(lul_temp) < 26.6
    and tonumber(lul_temp) > 25)
    or tonumber(lul_temp) < 23) then
      return false
  end

If the scene doesn't run, it's possible there is a syntax error. The easiest way to test this is to copy the Lua code from the scene, then go to Devices, Luup Plugins, and "Test Luup code". Paste the code in the box and click 'go'. If the info box above the 'go' button has a check and says "Message sent successful", your code is okay. If there's an error, it says: "Code failed".

To see if that's true, use putty, or telnet or ssh to log-in to Vera, as explained in detail in Logon_Vera_SSH and Lua Debugging, and type:

  cd /var/log/cmh
tail -f LuaUPnP.log | grep '^01'

Now click 'Save' in Vera's setup page, even if it's gray, as that will cause Vera to restart the Luup engine and log any syntax errors. See: Lua Debugging for in-depth details on how to debug.

[edit] Walk-through #2 -- Only run the scene during the daytime

In the Luup tab for the scene paste this:

  return luup.is_night() == false

That works because if the return is 'true' the scene runs, and if it's 'false' it doesn't. So during the daytime the expression is true. Or, an alternative long form:

  if (luup.is_night()) then
     return false
  else
     return true
  end

[edit] Samples

This page is a wiki which anyone can edit. If you have some Lua code you think other users might find useful, feel free to add it here.

[edit] Lighting and Switch Actions

Did you see the sample here already: http://wiki.micasaverde.com/index.php/Luup_Scenes_Events

[edit] Turn an appliance switch or a Danfoss thermostat on for device #5

  luup.call_action("urn:upnp-org:serviceId:SwitchPower1", "SetTarget", {newTargetValue = "1"}, 5)

[edit] Turn an appliance switch or a Danfoss thermostat off

  luup.call_action("urn:upnp-org:serviceId:SwitchPower1", "SetTarget", {newTargetValue = "0"}, 5)

[edit] Do something if switch device #5 is on

  local lul_tmp = luup.variable_get("urn:upnp-org:serviceId:SwitchPower1", "Status", 5)
  if (lul_tmp == "1") then
     --something to do goes here
  end

[edit] Dim switch #6 to 30%

  luup.call_action("urn:upnp-org:serviceId:Dimming1", "SetLoadLevelTarget", {newLoadlevelTarget = "30"}, 6)

[edit] Switch on, and switch off 2 seconds later

local device  = 29
-- Switch on device 29:
luup.call_action("urn:upnp-org:serviceId:SwitchPower1","SetTarget",{ newTargetValue="1" },device)
luup.call_delay( 'switch_off', 2) -- Call the switch off function after a delay of 2 seconds
function switch_off()
   luup.call_action("urn:upnp-org:serviceId:SwitchPower1","SetTarget",{ newTargetValue="0" },device)
end

[edit] Blinking lights (and how to delay for a number of seconds)

http://forum.micasaverde.com/index.php?topic=5127.0

[edit] Motion Sensor Actions

[edit] Arm motion sensor #7

  luup.variable_set("urn:micasaverde-com:serviceId:SecuritySensor1", "Armed", "1", 7)

[edit] Disarm motion sensor #7

  luup.variable_set("urn:micasaverde-com:serviceId:SecuritySensor1", "Armed", "0", 7)

Note, arming and disarming isn't a concept within UPnP or Z-Wave. It's just a flag that the Luup engine uses, and is stored in a variable we created called "Armed".

[edit] Scene Actions

[edit] Run Scene #5

Thanks "denix" on the forum for the correct syntax. "Actually, the 4th parameter IS required, but it's not used. Otherwise the command fails with an error message"

  luup.call_action("urn:micasaverde-com:serviceId:HomeAutomationGateway1", "RunScene", {SceneNum = "5"}, 0)

In this case we left the device number off (the 4th parameter to luup.call_action), because the "RunScene" action is handled by the Luup engine itself--not by some device within Z-Wave, etc.

However, normally you don't need to luup.call_action in Lua code. Rather, whatever actions, or commands, you want to run, you put into the scene itself, and the only Lua code is to simply check if some condition is true and abort the scene if the condition isn't met.

[edit] Thermostat Actions

[edit] Change the Temperature on Thermostat (Cool) device #19

  luup.call_action("urn:upnp-org:serviceId:TemperatureSetpoint1_Cool",
                   "SetCurrentSetpoint", {NewCurrentSetpoint = "68"},
                   19)

[edit] Change the Temperature on a Thermostat (Heat) device #19

  luup.call_action("urn:upnp-org:serviceId:TemperatureSetpoint1_Heat",
                   "SetCurrentSetpoint", {NewCurrentSetpoint = "68"},
                   19)

[edit] Change the Thermostat Operating mode device #19

luup.call_action("urn:upnp-org:serviceId:HVAC_UserOperatingMode1",
                 "SetModeTarget", {NewModeTarget = "Off"},
                 19)


[edit] Camera Actions

[edit] 'Privacy' mode for Foscam FI8908[w]

  local IP_address = '<IP address of camera>'
  local username = '<username>'
  local password = '<password>'
  local timeout  = 5
 
  function move_up()
    luup.inet.wget('http://' .. IP_address .. '/decoder_control.cgi?command=0', timeout, username, password)
  end
 
  -- center the camera; takes some time, so we have to wait 2 minutes for the command to complete
 
  luup.inet.wget('http://' .. IP_address .. '/decoder_control.cgi?command=25', timeout, username, password)
 
  luup.call_delay('move_up', 120)

[edit] Misc Actions

[edit] Playing an announcement

http://forum.micasaverde.com/index.php?topic=5466.msg36405#msg36405

[edit] Calculate sunrise and sunset

See http://forum.micasaverde.com/index.php?topic=2073.msg8132#msg8132

... or use DAD: http://forum.micasaverde.com/index.php?topic=5466.0

[edit] Access the web

[edit] Invoke HTTP URL with GET request (Method 1)

  -- 5 Second timeout
  local status, result = luup.inet.wget("http://www.yahoo.com", 5)

[edit] Invoke HTTP URL with GET request (Method 2)

Based on code by Jim/jgc94131
  require('ltn12')
  local http = require('socket.http')
 
  -- 5 Second timeout
  socket.http.TIMEOUT = 5
 
  local response_body = {}
  local request_body = ''
 
  local r, c, h = socket.http.request{
    url = 'http://website/page?parameter1=value&parameter2=value',
    method = "GET",
    port = 80,
    headers = {
      ["Content-Length"] = string.len(request_body),
      ["Content-Type"] = "application/x-www-form-urlencoded"
    },
    source = ltn12.source.string(request_body),
    sink = ltn12.sink.table(response_body)
  }

[edit] Invoke HTTP URL with POST request (Method 3)

  local http = require("socket.http")
 
  -- 5 Second timeout
  http.TIMEOUT = 5
 
  -- The return parameters are in a different order from luup.inet.wget(...)
  result, status = http.request("http://192.168.0.113/runprocess.htm", "run=run")

[edit] Access the current time

The Lua function

  os.date (format, time)

converts a time value `time` into a human readable date/time string, according to `format`. If you leave out the optional `time` parameter, it defaults to current time. The `format` parameter defaults to a fairly complete format. If you specify '*t' as the format, it will return a table instead of a formatted string.

  t = os.date('*t')
  t = {year=2010, month=2, day=19, yday=50, wday=6, hour=22, min=45, sec=45, isdst=false}

The fields are year, month, day of month, day of year, day of week, hour in 24 hour clock, minutes, seconds and if it's Daylight Savings Time.

Current hour:

  os.date('*t').hour

Current minute:

  os.date('*t').min

Current second:

  os.date('*t').sec

Do something between 16:00 and 21:15:

  local t = os.date('*t')
  local current_second = t.hour * 3600 + t.min * 60 + t.sec   -- number of seconds since midnight
  local min_time_in_seconds = 16 * 3600 +  0 * 60             -- 16:00
  local max_time_in_seconds = 21 * 3600 + 15 * 60             -- 21:15
 
  if (current_second > min_time_in_seconds) and (current_second < max_time_in_seconds) then
    -- do something
  else
    return false
  end

See http://forum.micasaverde.com/index.php?topic=2015.0 and http://www.lua.org/manual/5.1/manual.html#5.8.

[edit] Set Z-Wave parameters

See http://forum.micasaverde.com/index.php?topic=1937.msg7803#msg7803

[edit] a scene if the temperature is outside of a range

add snippets here...

[edit] Reload Luup at 3 AM every day

1. Create a scene and set it to run daily at 3 AM.

2. Add this code in the Luup code box:

luup.call_action("urn:micasaverde-com:serviceId:HomeAutomationGateway1", "Reload", {}, 0)

3. Save.

[edit] Thermostat conditioned by door/window

Turn off the thermostat if the door/window is left open for 5 minutes or more and back on if the door/window is closed for 10 minutes or more.

1. Create a scene and add a trigger to run the scene when the door sensor is tripped (door is opened).

2. Add this code in the Luup code box:
local SENSOR = 17    -- The door/window sensor device number
local THERMOSTAT = 3 -- The thermostat device number
local DELAY = 300    -- Seconds
 
local SES_SID = "urn:micasaverde-com:serviceId:SecuritySensor1"
local HVACO_SID = "urn:upnp-org:serviceId:HVAC_UserOperatingMode1"
 
luup.call_delay( "turnOffAc", DELAY)
 
-- Turn off the thermostat if the sensor has been tripped for at least 5 minutes.
function turnOffAc()
    local tripped = luup.variable_get( SES_SID, "Tripped", SENSOR) or "0"
    local lastTrip = luup.variable_get( SES_SID, "LastTrip", SENSOR) or os.time()
    if (tripped == "1" and (os.time() - lastTrip >= DELAY)) then
        local modeStatus = luup.variable_get( HVACO_SID, "ModeStatus", THERMOSTAT) or "Off"
        luup.variable_set( HVACO_SID, "LastModeStatus", modeStatus, THERMOSTAT)
        luup.call_action( HVACO_SID, "SetModeTarget", {NewModeTarget = "Off"}, THERMOSTAT)
    end
end


3. Create another scene and add a trigger to run the scene when the door sensor is not tripped (door is closed).

4. Add this code in the Luup code box:
local SENSOR = 17    -- The door/window sensor device number
local THERMOSTAT = 3 -- The thermostat device number
local DELAY = 600    -- Seconds
 
local SES_SID = "urn:micasaverde-com:serviceId:SecuritySensor1"
local HVACO_SID = "urn:upnp-org:serviceId:HVAC_UserOperatingMode1"
 
luup.call_delay( "turnOnAc", DELAY)
 
-- Turn on the thermostat if the sensor hasn't been tripped in the past 10 minutes.
function turnOnAc()
    local tripped = luup.variable_get( SES_SID, "Tripped", SENSOR) or "0"
    local lastTrip = luup.variable_get( SES_SID, "LastTrip", SENSOR) or os.time()
    if (tripped == "0" and (os.time() - lastTrip >= DELAY)) then
        local lastModeStatus = luup.variable_get( HVACO_SID, "LastModeStatus", THERMOSTAT) or "Off"
        luup.call_action( HVACO_SID, "SetModeTarget", {NewModeTarget = lastModeStatus}, THERMOSTAT)
    end
end

5. Save.

Personal tools