Luup Lua extensions

From MiOS
(Difference between revisions)
Jump to: navigation, search
m
(function: attr_get)
 
(137 intermediate revisions by 9 users not shown)
Line 1: Line 1:
In addition to the [[http://lua.org Lua]] commands described in the [[http://www.lua.org/manual/5.1/ Lua reference manual]], you can also reference in your Lua code variables and functions from modules which the Luup engine provides as follows:
+
In addition to the [[http://lua.org Lua]] commands described in the [[http://www.lua.org/manual/5.1/ Lua reference manual]], you can also reference in your Lua code variables and functions from modules which the Luup engine provides as follows:  
  
== Module: luup ==
+
== Module: luup ==
  
These are general purpose functions and variables. Call them by using the luup. module, such as:
+
These are general purpose functions and variables. Call them by using the luup. module, such as:<br>
  
luup.log('Now running version: ' .. luup.version)
+
<source lang="lua">luup.log('Now running version: ' .. luup.version)</source>
 +
 
 +
=== variable: device  ===
 +
 
 +
The ID of this device instance, if it's running as part of a device
  
 
=== variable: version, version_branch, version_major, version_minor  ===
 
=== variable: version, version_branch, version_major, version_minor  ===
  
''version'' contains the version of the luup engine, such as "1.0.843", as a string. The version consists of 3 numbers separated by a period: the branch, the major version, and the minor version. To make it easier to compare against a minimum acceptable version, ''version_branch, version_major and version_minor'' return each component as a number. You can put a comparison in the startup function in your Luup plugin and return false if the minimum version isn't met. The user will then see that the Luup device's startup check failed:
+
''version'' contains the version of the luup engine, such as "1.0.843", as a string. The version consists of 3 numbers separated by a period: the branch, the major version, and the minor version. To make it easier to compare against a minimum acceptable version, ''version_branch, version_major and version_minor'' return each component as a number. You can put a comparison in the startup function in your Luup plugin and return false if the minimum version isn't met. The user will then see that the Luup device's startup check failed:<br>
  
if( version_branch~=1 or version_major~=0 or version_minor<843 ) then
+
<source lang="lua">if( version_branch ~= 1 or version_major ~= 0 or version_minor < 843 ) then
  luup.log("I need version 1.0.843 minimum to run")
+
    luup.log("I need version 1.0.843 minimum to run")
  return false
+
    return false
end
+
end</source>
  
=== variable: longitude ===
+
=== variable: longitude ===
  
Contains the longitude as a number, as found on the location tab in the setup UI.
+
Contains the longitude as a number, as found on the location tab in the setup UI.  
  
=== variable: latitude ===
+
=== variable: latitude ===
  
Contains the latitude as a number, as found on the location tab in the setup UI.
+
Contains the latitude as a number, as found on the location tab in the setup UI.  
  
=== variable: timezone ===
+
=== variable: timezone ===
  
Contains the timezone as a number, as found on the location tab in the setup UI.
+
Contains the timezone as a number of hours offset from UTC, as found on the location tab in the setup UI.  It accounts for DST, so, for example, Pacific Standard time will be -8 or -9 depending on DST.
  
=== variable: city ===
+
'''Note:''' Contains 0 for MiOS < 1.5.250 (Vera V2) / < 1.5.249 (Vera V3).
  
Contains the city as a string, as found on the location tab in the setup UI.
+
=== variable: city ===
  
=== variable: devices ===
+
Contains the city as a string, as found on the location tab in the setup UI.
  
Contains all the devices in the system as a table indexed by the device number. The members are: room_num (number), device_type (string), category_num (number), device_num_parent (number), ip (string), mac (string), id (string), description (string), udn (string).  See also: [[Lua Device Structure]].  Example to log device #5's IP addess and it's internal ID:
+
=== variable: devices  ===
  
  luup.log('Device #5 ip: ' .. luup.devices[5].ip .. ' id: ' .. luup.devices[5].id)
+
Contains all the devices in the system as a table indexed by the device number.
  
room_num: This is the number of the room the device is in.  
+
The members are:
 +
* '''room_num''': (number) This is the number of the room the device is in.
 +
* '''device_type''': (string) This is a string representing the type of the device.
 +
* '''category_num''': (number) This is a category for the device.  See: [[Luup_Device_Categories]] for a list.
 +
* '''subcategory_num''': (number) This is a sub category for the device.
 +
* '''device_num_parent''': (number) This is the number of the parent device. See: [[Lua Device Structure]] for details.
 +
* '''ip''': (string) If this device is IP based, this is the IP address.
 +
* '''mac''': (string) If this device is IP based, this is the MAC address.
 +
* '''user''': (string) If this device is IP based and requires http authentication, this is the username
 +
* '''pass''': (string) If this device is IP based and requires http authentication, this is the password
 +
* '''id''': (string) If this device has an internal ID that is specific to the device, it is contained here. For example, for Z-Wave devices this is the Node ID, and for Insteon device it is the Insteon ID.
 +
* '''embedded''': (boolean) If this device is embedded, it means that it doesn't have its own room or exist as a separate device. It should be considered part of its parent. Like a 3-in-1 sensor is a device with 3 embedded child devices.
 +
* '''hidden''': (boolean) If true the user checked the 'hidden' box and doesn't want to see the device on the dashboard.
 +
* '''invisible''': (boolean) If true the device is 'for internal use only' and shouldn't be presented to the user.
 +
* '''description''': (string) This is the text description for the device as supplied by the user in the web UI.
 +
* '''udn''': (string) This is the UDN for the UPnP device.
  
device_type: This is a number representing the type of device. See: [[Luup Device Types Categories]] for a list.
 
  
category_num: This is a category for the device. See: [[Luup Device Types Categories]]  
+
Example to log device #5's IP address and its internal ID:
 +
<source lang="lua">luup.log('Device #5 ip: ' .. luup.devices[5].ip .. ' id: ' .. luup.devices[5].id)</source>
  
device_num_parent: This is the number of the parent device. See: [[Lua Device Structure]] for details.
 
  
ip: If this device is IP based, this is the IP address.  
+
This code will log all the attributes from all the devices:
 +
<source lang="lua">for k, v in pairs(luup.devices) do
 +
    for k2, v2 in pairs(v) do
 +
        luup.log("Device #" .. k .. ":" .. k2 .. "=" .. tostring(v2))
 +
    end
 +
end
  
mac: If this device is IP based, this is the MAC address.
+
return true</source>
  
id: If this device has an internal ID that is specific to the device, it is contained here. For example, for Z-Wave devices this is the Node ID, and for Insteon device it is the Insteon ID.  
+
Note: if an attribute is modified, it will not persist between Luup engine restarts. You need to use [[Luup_Lua_extensions#function:_attr_set|luup.attr_set]] instead. Eg:
 +
<source lang="lua">
 +
local ipAddress = '192.168.l.l2'
  
description: This is the text description for the device as supplied by the user in the web ui.  
+
-- this will not persist between restarts
 +
luup.devices[lul_device].ip = ipAddress
  
udn: This is the UDN for the UPnP device.  
+
-- this will persist between restarts
 +
luup.attr_set('ip', ipAddress, lul_device)</source>
  
This code will log all the attributes from all the devices:  
+
=== variable: rooms  ===
  
  for k,v in pairs(lug_device) do
+
Contains all the rooms as a table of strings indexed by the room number. Example:<br>
    for k2,v2 in v
+
      lu_log("Device #" .. k .. ":" .. k2 .. "=" .. "v2")
+
    end
+
  end
+
  
=== variable: rooms ===
+
<source lang="lua">luup.log('Room #1 is called: ' .. luup.rooms[1])</source>
  
Contains all the rooms as a table of strings indexed by the room number.  Example:
+
=== variable: scenes  ===
  
  luup.log('Room #1 is called: ' .. luup.rooms[1])
+
Contains all the scenes in the system as a table indexed by the scene number. The members are: room_num (number), description(string), hidden(boolean)
  
=== variable: scenes ===
+
=== variable: remotes  ===
  
Contains all the scenes in the system as a table indexed by the device number. The members are: room_num (number), description(string)
+
Contains all the remotes in the system as a table indexed by the remote id. The members are: remote_file (string), room_num (number), description(string)  
  
=== variable: remotes ===
+
=== variable: event_server ===
  
Contains all the remotes in the system as a table indexed by the remote id.  The members are: remote_file (string), room_num (number), description(string)
+
type: string
  
=== function: log ===
+
Contains the notification/event server. On UI5 it can be either ''cms1.mios.com'' or ''cms2.mios.com''.
  
parameters: what_to_log (string), log_level (optional, number)
+
=== variable: ra_server ===
  
return: nothing
+
type: string
  
Writes what_to_log to the log, /var/log/cmh/LuaUPnP.log, with the given log_level, which is 50 by default. See: [[Luup_Loglevels]]
+
Contains the remote access server. Can be either ''fwd1.mios.com'' or ''fwd2.mios.com''.
  
=== function: call_delay ===
+
=== variable: pk_accesspoint ===
  
parameters: function_name (string), seconds (number), data (string)
+
type: number
  
returns: result (number)
+
Contains the serial number of this Vera.
  
The function 'function_name', which must be passed as a string, will be called in 'seconds' seconds, and will be past the string 'data'.  The function returns 0 if successful.  See: [[Luup_Declarations#timed_function_callback]] for the names and syntax of the parameters that will be passed to function_name.  function_name will only be called once and you must call call_delay again if you want it called again, or use call_timer instead.
+
=== variable: hw_key ===
  
=== function: call_timer ===
+
type: string
  
parameters: function_name (string), type (number), time (string), days (string), data (string)
+
Contains the Vera hardware key.
  
returns: result (number)
+
=== function: log  ===
  
The function 'function_name', which must be passed as a string, will be called when the timer is triggered, and will be past the string 'data'. The function returns 0 if successful. See: [[Luup_Declarations#timed_function_callback]] for the names and syntax of the parameters that will be passed to function_name.
+
parameters: what_to_log (string), log_level (optional, number)
 +
 
 +
return: nothing
 +
 
 +
Writes what_to_log to the log, /var/log/cmh/LuaUPnP.log, with the given log_level, which is 50 by default. See: [[Luup Loglevels]]
 +
 
 +
=== function: task  ===
 +
 
 +
parameters: message (string), status (number), description (string), handle (number)
 +
 
 +
return: handle (number)
 +
 
 +
When the Luup engine is starting status messages are displayed for the various modules as they're initialized. Normally each device, including Luup devices, automatically log their status and the user is shown an error if the device doesn't start, such as if the 'startup' function returns an error.
 +
 
 +
If you have other startup sequences which you want the user to see to know that startup hasn't finished yet, call this function passing in a handle of -1 for the first call. The status should be: 1=Busy, 2=Error, 4=Successful. Message is the current state, such as 'downloading', and description describes the module, like 'Smartphone UI'. After the first call, store the handle and pass it on future calls to update the status rather than add a new one.
 +
 
 +
=== function: call_delay  ===
 +
 
 +
parameters: function_name (string), seconds (number), data (string), thread (bool)
 +
 
 +
returns: result (number)
 +
 
 +
The function ''function_name'' (the first parameter), which must be passed as a string, will be called in ''seconds'' seconds (the second parameter), and will be passed the string ''data''. The function returns 0 if successful. See: [[Luup Declarations#timed_function_callback]] for the names and syntax of the parameters that will be passed to function_name. function_name will only be called once and you must call call_delay again if you want it called again, or use call_timer instead.
 +
 
 +
If thread is specified and is true or 1, the call back will be made in it's own thread and can block if needed. Normally it is called by a worker thread and is expected to return immediately.
 +
 
 +
As of December 19, 2011, for all builds after 1.5.237, the 'thread' will be ignored.  Each Lua state has its own worker thread now, so all calls to call_delay and call_timer will occur in a separate thread.
 +
 
 +
=== function: call_timer  ===
 +
 
 +
parameters: function_name (string), type (number), time (string), days (string), data (string)
 +
 
 +
returns: result (number)
 +
 
 +
The function 'function_name', which must be passed as a string, will be called when the timer is triggered, and will be passed the string 'data'. The function returns 0 if successful. See: [[Luup Declarations#timed_function_callback]] for the names and syntax of the parameters that will be passed to function_name.  
  
 
Type is 1=Interval timer, 2=Day of week timer, 3=Day of month timer, 4=Absolute timer. For an interval timer, days is not used, and Time should be a number of seconds, minutes, or hours using an optional 'h' or 'm' suffix. Example: 30=call in 30 seconds, 5m=call in 5 minutes, 2h=call in 2 hours. For a day of week timer, Days is a comma separated list with the days of the week where 1=Monday and 7=Sunday. Time is the time of day in hh:mm:ss format. Time can also include an 'r' at the end for Sunrise or a 't' for Sunset and the time is relative to sunrise/sunset. For example: Days="3,5" Time="20:30:00" means your function will be called on the next Wed or Fri at 8:30pm. Days="1,7" Time="-3:00:00r" means your function will be called on the next Monday or Sunday 3 hours before sunrise. Day of month works the same way except Days is a comma separated list of days of the month, such as "15,20,30". For an absolute timer, Days is not used, and Time should be in the format: "yyyy-mm-dd hh:mm:ss"  
 
Type is 1=Interval timer, 2=Day of week timer, 3=Day of month timer, 4=Absolute timer. For an interval timer, days is not used, and Time should be a number of seconds, minutes, or hours using an optional 'h' or 'm' suffix. Example: 30=call in 30 seconds, 5m=call in 5 minutes, 2h=call in 2 hours. For a day of week timer, Days is a comma separated list with the days of the week where 1=Monday and 7=Sunday. Time is the time of day in hh:mm:ss format. Time can also include an 'r' at the end for Sunrise or a 't' for Sunset and the time is relative to sunrise/sunset. For example: Days="3,5" Time="20:30:00" means your function will be called on the next Wed or Fri at 8:30pm. Days="1,7" Time="-3:00:00r" means your function will be called on the next Monday or Sunday 3 hours before sunrise. Day of month works the same way except Days is a comma separated list of days of the month, such as "15,20,30". For an absolute timer, Days is not used, and Time should be in the format: "yyyy-mm-dd hh:mm:ss"  
  
Data can be a string passed back to the function. The function should be declared so it takes a single argument, which is this data.  
+
Data can be a string passed back to the function. The function should be declared so it takes a single argument, which is this data.<br>
<pre>function refreshCache(stuff)
+
 
 +
<source lang="lua">function refreshCache(stuff)
 
     ....
 
     ....
 
end
 
end
Line 115: Line 173:
 
     -- at the end of the refreshCache() implementation.
 
     -- at the end of the refreshCache() implementation.
 
     --
 
     --
     lu_CallFunctionTimer("refreshCache", 1, "30m", "",&nbsp;"SomeStuff")
+
     luup.call_timer("refreshCache", 1, "30m", "", "SomeStuff")
end
+
end</source>  
</pre>
+
  
=== is_ready ===
+
=== function: is_ready ===
  
parameters: device (string or number)
+
parameters: [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
  
returns: ready (boolean)
+
returns: ready (boolean)  
  
Checks if  device, which, if it's a string, is interpreted as a udn, and if it's a number, is the device number, is ready and has successfully completed the startup sequence.  If so, ready is true.  If your device shouldn't process incoming data until the startup sequence is finished, you may want to add a check to the incoming function handler that ignores any data if is_ready(lul_device) isn't true.
+
version: UI5 and above
  
=== call_action ===
+
Checks whether a device has successfully completed it's startup sequence. If so, is_ready returns true. If your device shouldn't process incoming data until the startup sequence is finished, you may want to add a condition to the <incoming> block that only processes data if is_ready(lul_device) is true.
  
parameters: service (string), action (string), arguments (table), device (string or number)
+
<source lang="lua"><incoming>
 +
    if (luup.is_ready(lul_device) == false) then
 +
        return
 +
    end
  
returns: error (number), error_msg (string), job (number), arguments (table)
+
    doSomething(lul_device)
 +
</incoming></source>
  
Invokes the UPnP service + action, passing in the arguments (table of string->string pairs) to the device, which, if it's a string, is interpreted as a udn, and if it's a number, is the device number. If the invocation could not be made, only error will be returned with a value of -1.  Otherwise, all 4 values are returned.  error is 0 if the UPnP device reported the action was successful.  arguments is a table of string->string pairs with the return arguments from the action.  If the action is handled asynchronously by a Luup job, then the job number will be returned as a positive integer.
+
=== function: call_action ===
  
Example to dim device #5 to 50%:
+
parameters: service (string), action (string), arguments (table), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
  
  local lul_arguments = {}
+
returns: error (number), error_msg (string), job (number), arguments (table)  
  lul_arguments["newLoadlevelTarget"]=50
+
  lul_resultcode,lul_resultstring,lul_job,lul_returnarguments = luup.call_action("urn:upnp-org:serviceId:Dimming1","SetLoadLevelTarget",lul_arguments,5)
+
  
=== function: variable_set ===
+
Invokes the UPnP service + action, passing in the arguments (table of string-&gt;string pairs) to the device. If the invocation could not be made, only ''error'' will be returned with a value of -1. Otherwise, all 4 values are returned. ''error'' is 0 if the UPnP device reported the action was successful. ''arguments'' is a table of string-&gt;string pairs with the return arguments from the action. If the action is handled asynchronously by a Luup job, then the job number will be returned as a positive integer.
  
parameters: service (string), variable (string), value (string), device (string or number)
+
{{Warning|Currently only Z-Wave and Insteon jobs are returned in the ''job'' parameter.}}
  
returns: nothing
+
Example to dim device #5 to 50%:<br>
  
The UPnP service+variable will be set to value for device, which if it's a string, is interpreted as a udn, and if it's a number, as a device id.  If there are events or notifications tied to the variable they will be fired.
+
<source lang="lua">local resultCode, resultString, job, returnArguments = luup.call_action("urn:upnp-org:serviceId:Dimming1", "SetLoadLevelTarget", {["newLoadlevelTarget"] = "50"}, 5)</source>
 +
<br>
  
=== function: variable_get ===
+
=== function: variable_set  ===
  
parameters: service (string), variable (string), device (string or number)
+
parameters: service (string), variable (string), value (string), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]], [startup (bool)]
  
returns: value (string), time (number)
+
returns: nothing
  
If the service+variable or device does not exist, it returns nothing. Otherwise it returns the value of the UPnP service+variable and the time when the variable was last modified, as a unix time stamp (number of seconds since 1/1/1970). You can assign just the value to a variable, as follows:
+
The UPnP 'service' + 'variable' will be set to the 'value' for this device. If there are events or notifications tied to the variable they will be fired.  
  
  local value = luup.variable_get("urn:upnp-org:serviceId:Dimming1","LoadLevelTarget",5)
+
The device parameter: if it's a string, is interpreted as a udn.  If it's a number, it's interpreted as a device number.
  luup.log("Dim level for device #5 is: " .. value)
+
  
=== function: register_handler ===
+
Optionally, you can add an argument 'startup'. If startup is true, this change will be considered a startup value, and if the variable is set to it's existing value, events and notifications will ''not'' be fired.
  
parameters: function_name (string), request_name (string)
+
=== function: variable_get  ===
  
returns: nothing
+
parameters: service (string), variable (string), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
  
When a certain URL is requested from a web browser or other HTTP get, function_name will be called and whatever string it returns will be returned.
+
returns: value (string) and Unix time stamp (number) of when the variable last changed
  
See the WAP mobile phone plugin as an example:
+
Returns the value of the UPnP service + variable and the time when the variable was last modified, as a unix time stamp (number of seconds since 1/1/1970). If the service+variable or device does not exist, it returns nothing.<br>
  
  luup.register_handler("lug_WapRequest","wap")
+
[[Example usage]]
  function lug_WapRequest(lul_request,lul_parameters,lul_outputformat)
+
<source lang="lua">local value, tstamp = luup.variable_get("urn:upnp-org:serviceId:Dimming1", "LoadLevelTarget", 5)
local lul_html = "<head>\n" ..
+
luup.log("Dim level for device #5 is: " .. value .. " last changed (Epoch): " .. tstamp)</source>
"<title>Main</title>\n" ..
+
"</head>\n" ..
+
"<body>\n" ..
+
"Choose a room:<br>\n"
+
  end
+
  
The request is made with the URL: data_request?id=lr_[the registered name] on port 49451So: http://192.168.1.1:49451/data_request?id=lr_wap will return the web page defined in the function lug_WapRequest in the example above.
+
The device parameter: if it's a string, is interpreted as a udnIf it's a number, it's interpreted as a device number.
  
=== function: variable_watch ===
+
[[Caution - Incorrect usages]]
 +
<source lang="lua">local value = tonumber(luup.variable_get("urn:upnp-org:serviceId:Dimming1", "LoadLevelTarget", 5))
 +
</source>
  
parameters: function_name (string), service (string), variable (string or nill), device (string or number)
+
<code>luup.variable_get</code> returns two parameters and <code>tonumber</code> also accepts two parameters. However the parameters are incompatible: the Unix timestamp returned by <code>luup.variable_get</code> is being used as a number base in the function <code>tonumber</code>. The number base is limited to a power of 36 or less and the current timestamps are in the range of thousands of millions.
  
returns: nothing
+
<source lang="lua">local value = tostring(luup.variable_get("urn:upnp-org:serviceId:Dimming1", "LoadLevelTarget", 5))
 +
</source>
  
Whenever the UPnP variable is changed for the specified device, which if a string is interpreted as a udn and if a number as a device id, function_name will be called.  See [[Luup_Declarations#watch_callback]] for the values that will be passed to function_name.  If variable is nill, function_name will be called whenever any variable in the service is changed.
+
<code>luup.variable_get</code> returns two parameters and <code>tostring</code> only expects one
  
 +
=== function: attr_set  ===
  
=== function: devices_by_service ===
+
parameters: attribute (string), value (string), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]], refresh_user_data (boolean)
  
parameters:  
+
returns: none
  
returns:  
+
Sets the top level attribute for the device to value. Examples of attributes are 'mac', 'name', 'id', etc.  Like [[#function:_attr_get|attr_get]], if the device is zero it sets the top-level user_data json tag.
 +
If 'refresh_user_data' is ''true'', setting the attribute will increment the DataVersion in the user_data.
  
=== function: device_supports_service ===
+
=== function: attr_get  ===
 +
parameters: attribute (string), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
 +
 
 +
returns: if the [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]] is valid, then the attribute is returned as a string.
 +
 
 +
If the [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]] is valid but the attribute doesn't exist, then the returned string is empty.
 +
 
 +
If the [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]] is invalid and due to a MIOS coding problem, the expected ''''nil' is not returned''' (as of March 2018). Instead '(null)' is returned.
 +
 
 +
Gets the requested attribute for the device:  Examples of attributes are 'mac', 'name', 'id', etc. If the number 0 is passed as the device, it gets the top level attribute from the master userdata, like 'firmware_version', etc.
 +
 
 +
=== function: ip_set ===
 +
 
 +
''<span style="color: red">Not available in UI5 or lower</span>''
 +
 
 +
parameters: value (string), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
 +
 
 +
returns: none
 +
 
 +
Sets the IP address for a device.  This is better than setting the "ip" attribute using attr_set because it updates internal values additionally, so a reload isn't required.
 +
 
 +
=== function: mac_set ===
 +
 
 +
''<span style="color: red">Not available in UI5 or lower</span>''
 +
 
 +
parameters: value (string), device (string or number)
 +
 
 +
returns: none
 +
 
 +
Sets the mac address for a device.  This is better than setting the "mac" attribute using attr_set because it updates internal values additionally, so a reload isn't required.
 +
 
 +
=== function: reload ===
 +
 
 +
''<span style="color: red">Not available in UI5 or lower</span>''
 +
 
 +
parameters: none
 +
 
 +
returns: none
 +
 
 +
Reloads the Luup engine.
 +
 
 +
=== function: create_device ===
 +
 
 +
''<span style="color: red">Not available in UI5 or lower</span>''
 +
 
 +
parameters:
 +
* '''device_type''' (string) : This is the device type and should be set to an empty string, so it will be retrieved from the UPnP device file.
 +
* '''internal_id''' (string) : This is the device '''altid'''.
 +
* '''description''' (string) : This is the device name.
 +
* '''upnp_file''' (string) : This is the UPnP device file.
 +
* '''upnp_impl''' (string) : This is the implementation file.
 +
* '''ip''' (string)
 +
* '''mac''' (string)
 +
* '''hidden''' (boolean)
 +
* '''invisible''' (boolean)
 +
* '''parent''' (number) : The device ID of the parent device. Set it to '0' if this device shouldn't have any parents.
 +
* '''room''' (number) : The room ID. If set to '0', the device won't be put in any room.
 +
* '''pluginnum''' (number) : Which plugin to install after the device is created. Set it to '0' if no plugin should be installed.
 +
* '''statevariables''' (string) : Variables and attributes you want set when the device is created. You can specify multiple variables by separating them with a line feed (\n) and use '<code>,</code>' and '<code>=</code>' to separate service, variable and value, like this: <code>service,variable=value\nservice,variable=value</code>
 +
To set an attribute, leave "service" empty, like this: <code>,variable=value</code>
 +
Example: <source lang="lua">"urn:upnp-org:serviceId:TemperatureSensor1,CurrentTemperature=50\n,manufacturer=MiOS"</source>
 +
* '''pnpid''' (number) : If this is a device from KitDevice.json, this is the ''PK_KitDevice'' number. Otherwise set this to '0'.
 +
* '''nochildsync''' (string) : If set to "1", ignore this device when syncing child devices. Unless you know what you're doing, set it to an empty string.
 +
* '''aeskey''' (string) : This should be set to an empty string.
 +
* '''reload''' (boolean) : If ''true'', the Luup engine will be restarted after the device is created.
 +
* '''nodupid''' (boolean) : Set this to ''false''.
 +
 
 +
returns: the device ID
 +
 
 +
{{Warning|All parameters are mandatory.}}
 +
{{Important|<tt>device_type</tt> '''MUST''' be either an '''empty string''', or '''the same''' as the one in the device file, otherwise the Luup engine will restart continuously.}}<br>
 +
This creates the device with the parameters given, and returns the device ID.
 +
Example: <source lang="lua">luup.create_device("", "testdevice", "Test Device", "D_BinaryLight1.xml", "", "", "", false, false, 0, 0, 0, ",manufacturer=MiOS", 0, "", "", true, false)</source>
 +
<br>
 +
 
 +
=== function: device_message ===
 +
 
 +
''<span style="color: red">Available in releases after Feb 2017</span>''
 +
 
 +
This adds a system message that is attached to a device and appears in the UI under the device.
 +
 
 +
parameters:
 +
* '''device_id''' (number) : This is the device id number
 +
* '''status''' (int) : This is the status of message, and corresponds to the job status, and generally determines what color the message appears:
 +
** -1 : No job: black message
 +
**  0 : Job waiting to start: <span style="color: blue">blue</span> message
 +
**  1 : Job in progress: <span style="color: blue">blue</span> message
 +
**  2 : Job error: <span style="color: red">red</span> message
 +
**  3 : Job aborted: <span style="color: red">red</span> message
 +
**  4 : Job done: <span style="color: green">green</span> message
 +
**  5 : Job waiting for callback: <span style="color: blue">blue</span> message
 +
**  6 : Job requeue: <span style="color: blue">blue</span> message
 +
**  7 : Job in progress with pending data: black message
 +
* '''message''' (string) : This is the text that appears in the message.
 +
* '''timeout''' (int) : This is the number of seconds to display the message.  Pass 0 to display it indefinitely
 +
* '''source''' (string) : This is the source module of the message.  It can be anything, and is generally informational.  It is recommended to use the name of the luup plugin.
 +
 
 +
return: nothing
 +
 
 +
{{Warning|All parameters are mandatory.}}
 +
<br>
 +
 
 +
=== function: register_handler  ===
 +
 
 +
parameters: function_name (string), request_name (string)
 +
 
 +
returns: nothing
 +
 
 +
When a certain URL is requested from a web browser or other HTTP get, function_name will be called and whatever string and content_type it returns will be returned.
 +
 
 +
See the Smartphone Web Interface plugin as an example:<br>
 +
 
 +
<source lang="lua">luup.register_handler("lug_WapRequest","wap")
 +
 
 +
function lug_WapRequest (lul_request, lul_parameters, lul_outputformat)
 +
    local lul_html = "<head>\n" ..
 +
                    "<title>Main</title>\n" ..
 +
                    "</head>\n" ..
 +
                    "<body>\n" ..
 +
                    "Choose a room:<br/>\n"
 +
    local lul_content_type = "text/html"
 +
    return lul_html, lul_content_type
 +
end</source>
 +
 
 +
The request is made with the URL: data_request?id=lr_[the registered name] on port 49451. So: http://192.168.1.1:49451/data_request?id=lr_wap will return the web page defined in the function lug_WapRequest in the example above.
 +
 
 +
=== function: variable_watch  ===
 +
 
 +
parameters: function_name (string), service (string), variable (string or nil), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
 +
 
 +
returns: nothing
 +
 
 +
Whenever the UPnP variable is changed for the specified device, which if a string is interpreted as a UDN and if a number as a device ID, ''function_name'' will be called. See [[Luup Declarations#watch_callback]] for the values that will be passed to ''function_name''. If variable is nil, ''function_name'' will be called whenever any variable in the service is changed.
 +
 
 +
=== function: job_watch  ===
 +
 
 +
parameters: function_name (string), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
 +
 
 +
returns: nothing
 +
 
 +
Whenever a job is created, finished, or changes state then ''function_name'' will be called.  If the device is nil or not specified, ''function_name'' will be called for all jobs, otherwise only for jobs that involve the specified device.
 +
 
 +
Example:
 +
 
 +
  luup.job_watch("mycallback")
 +
  luup.job_watch("mycallback",6)
 +
 
 +
The first one registers a callback for all devices, the second one only for device 6.  Note that multiple registrations will result in multiple callbacks, so two calls like that means that "mycallback" would be called once for all devices except 6, and for 6 it would be called twice.
 +
 
 +
The callback function will be passed a table which contains:
 +
 
 +
device_num: the number of the device
 +
 
 +
status: the job status, 0-7 as follows
 +
 
 +
  WaitingToStart=0
 +
  InProgress=1
 +
  Error=2
 +
  Aborted=3
 +
  Done=4
 +
  WaitingForCallback=5
 +
  Requeue=6
 +
  InProgressPendingData=7
 +
 
 +
name: the name of the job
 +
 
 +
type: the C++ class name for the type of job
 +
 
 +
notes: any notes or progress that set for the job
 +
 
 +
Here is an example of the callback:
 +
 
 +
  function mycallback(lul_job)
 +
    luup.log("mycallback device #" .. lul_job.device_num .. " status " .. lul_job.status .. " name " .. lul_job.name .. " type " .. lul_job.type .. " notes " .. lul_job.notes);
 +
  end
 +
 
 +
and here is the output in LuaUPnP.log from the above function when it is registered to watch a ZWave device which was turned ON:
 +
 
 +
  50      07/09/14 17:43:43.491  luup_log:3: mycallback device #6 status 0 name ON node 2 type ZWJob_SendData notes  <00E998D0>
 +
  50      07/09/14 17:43:43.492  luup_log:3: mycallback device #6 status 1 name ON node 2 type ZWJob_SendData notes  <00E93328>
 +
  50      07/09/14 17:43:43.493  luup_log:3: mycallback device #6 status 7 name ON node 2 type ZWJob_SendData notes Sending the Z-Wave command after 0 retries <00E93328>
 +
  50      07/09/14 17:43:43.520  luup_log:3: mycallback device #6 status 5 name ON node 2 type ZWJob_SendData notes Waiting for node to reply after 0 retries <00E93328>
 +
  50      07/09/14 17:43:43.543  luup_log:3: mycallback device #6 status 0 name ON node 2 type ZWJob_SendData notes Waiting to send again with ack <00E92A58>
 +
  50      07/09/14 17:43:43.544  luup_log:3: mycallback device #6 status 1 name ON node 2 type ZWJob_SendData notes Waiting to send again with ack <00E93328>
 +
  50      07/09/14 17:43:43.545  luup_log:3: mycallback device #6 status 7 name ON node 2 type ZWJob_SendData notes Sending the Z-Wave command after 0 retries <00E93328>
 +
  50      07/09/14 17:43:43.576  luup_log:3: mycallback device #6 status 5 name ON node 2 type ZWJob_SendData notes Waiting for node to reply after 0 retries <00E93328>
 +
  50      07/09/14 17:43:43.631  luup_log:3: mycallback device #6 status 4 name ON node 2 type ZWJob_SendData notes Transmit was ok <00E92A58>
 +
 
 +
=== function: devices_by_service  ===
  
 
parameters:  
 
parameters:  
Line 202: Line 449:
 
returns:  
 
returns:  
  
=== function: set_failure ===
+
=== function: device_supports_service  ===
 +
 
 +
parameters: service ID (string), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
 +
 
 +
returns: ''true'' if the device supports the service, ''false'' otherwise
 +
 
 +
A device supports a service if there is at least a command or state variable defined for that device using that service. Setting UPnP variables is unrestricted and free form, and the engine doesn't really know if a device actually uses it or does anything with it. So this function isn't really definitive.
 +
 
 +
=== function: set_failure ===
  
parameters: value (boolean), device (string or number)
+
parameters: value (int), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
  
 
returns:  
 
returns:  
  
Luup maintains a 'failure' flag for every device to indicate if it is not functioning. You can set the flag to true if the device is failing. If device is a string it is interpreted as a udn, if it's a number, as a device id.
+
Luup maintains a 'failure' flag for every device to indicate if it is not functioning. You can set the flag to 1 if the device is failing, 0 if it's working, and 2 if the device is reachable but there's an authentication error. If device is a string it is interpreted as a udn, if it's a number, as a device id. The lu_status URL will show for the device: <tooltip display="1" tag2="Lua Failure"/> and Lua Failure is shown in red in UI5 for the device.
  
== Module: luup.chdev ==
+
=== function: is_night  ===
  
Contains functions for a parent to synchronize its child devices.  Whenever a device has multiple end-points, the devices are represented in a parent/child fashion where the parent device is responsible for reporting what child devices it has and giving each one a unique id.  For example in the sample [[Luup_Somfy_Walkthrough]] there is a parent device, which is the interface module that controls up to 16 blinds, and up to 16 child devices, one for each blind.  As shown in that sample, the parent calls start, then enumerates each child device with append, and finally calls sync.  You will need to pass the same value for device to append and sync that you passed to start.
+
parameters: none
  
=== function: start ===
+
returns: ''true'' if it's past sunset and before sunrise, ''false'' otherwise.
  
parameters: device (string or number)
+
=== function: sleep  ===
  
returns: ptr (binary object)
+
parameters: number of milliseconds
  
Tells Luup you will start enumerating the children of device.  If device is a string it is interpreted as a udn, if it's a number, as a device id.  The return value is a binary object which you cannot do anything with in Lua, but you do pass it to the append and sync functions.
+
returns: none
  
=== function: append ===
+
Sleeps a certain number of milliseconds
  
parameters: device (string or number), ptr (binary object), id (string), description (string), device_type (string), device_filename (string), implementation_filename (string), parameters (string), embedded (boolean)
+
=== function: sunset / sunrise ===
  
returns: nothing
+
parameters: none
  
Enumerates one child of device. If device is a string it is interpreted as a udn, if it's a number, as a device id. Pass in the ptr which you received from the start function.  Give each child a unique id so you can keep track of which is which. You can optionally provide a description which the user sees in the user interfacedevice_type is the UPnP device type, such as urn:schemas-upnp-org:device:BinaryLight:1. If device_filename is specified, that is the name of the xml file with the UPnP device specification. The deviceType from the filename will override any device_type you set manually.  If the device_file contains the implementation file for this child device you do not need to specify it in implementation_filename.  Otherwise, if there is a Luup implementation for this child device and it's not being handled by the parent device, you can specify it in implementation_filename.  If embedded is true, the 'embedded' flag is set for the device which generally means that the parent and all the children will be displayed as one compound device, or group, rather than as separate devices which you can put in their own rooms.
+
returns: The next sunset / sunrise in a Unix timestamp (i.e. the number of seconds since 1/1/1970 in UTC time). You can do a diff with os.time to see how long it will be for the next eventluup.sunset-os.time is the number of seconds before the next sunset. Be sure the location and timezone are properly set or the sunset/sunrise will be wrong.
  
The parameters are upnp service+variables you want set when the device is created.  You can specify multiple variables by separating them with a line feed (\n) and use a , and = to separate service, variable and value, like this: service,variable=value \n service...
+
required firmware: 1.5.353
  
=== function: sync ===
+
== Module: luup.inet  ==
  
parameters: device (string or number), ptr (binary object),
+
=== function: wget  ===
  
returns: nothing
+
parameters: URL (String), Timeout (Number), Username (String), Password (String)
  
If device is a string it is interpreted as a udn, if it's a number, as a device id.  Pass in the ptr which you received from the start function.  Tells the Luup engine you have finished enumerating the child devices.  If the child devices have changed in anyway, the new device tree will be written to the configuration file and the Luup engine is reset.
+
returns: operationStatusCode (Number), content (String), httpStatusCode (Number)
  
== module: io ==
+
This reads the URL and returns 3 variables: the first is a numeric error code which is 0 if successful. The second variable is a string containing the contents of the page. The third variable is the HTTP status code. If '''Timeout''' is specified, the function will timeout after that many seconds. The default value for '''Timeout''' is 5 seconds. If '''Username''' and '''Password''' are specified, they will be used for HTTP Basic Authentication.
 +
 
 +
<br>
 +
 
 +
== Module: luup.chdev  ==
 +
 
 +
Contains functions for a parent to synchronize its child devices. Whenever a device has multiple end-points, the devices are represented in a parent/child fashion where the parent device is responsible for reporting what child devices it has and giving each one a unique id. For example in the sample [[Luup Somfy Walkthrough]] there is a parent device, which is the interface module that controls up to 16 blinds, and up to 16 child devices, one for each blind. As shown in that sample, the parent calls start, then enumerates each child device with append, and finally calls sync. You will need to pass the same value for device to append and sync that you passed to start.
 +
 
 +
=== function: start  ===
 +
 
 +
parameters: [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
 +
 
 +
returns: ptr (binary object)
 +
 
 +
Tells Luup you will start enumerating the children of device. If device is a string it is interpreted as a udn, if it's a number, as a device id. The return value is a binary object which you cannot do anything with in Lua, but you do pass it to the append and sync functions.
 +
 
 +
=== function: append  ===
 +
 
 +
"append" is a misnomer as every time you use the luup.chdev module, you must enumerate all the children:
 +
 
 +
* if you do not "append" an existing child, that child will be deleted
 +
* if you "append" an existing child, you have the opportunity to change its parameters. All the parameters are required, even for existing children.
 +
* if you "append" a non-existent child, it will be created.
 +
 
 +
If you have the situation where child devices may come and go; eg say children are created for a WiFi sensor but the WiFi drops out on occasion, then you need to keep a record of the all the children, so they can all be enumerated at start up. Otherwise missing children will be deleted. If they reappear, say WiFi reconnects in this example, then the children will be added back in but will have a new device id.
 +
 
 +
When the final luup.chdev.sync is executed the children are checked. Any additions, changes or deletions will result in a Luup engine restart. Any incorrect coding of luup.chdev.append may result in a situation where the engine goes into a loop and continually restarts. As an example, if two children are added with the same id parameter, this will occur. To recover, you need to quickly upload a new file that has the faulty "append" code commented out. In the interim the engine will be incrementing the device ID numbers with no end in sight.
 +
 
 +
parameters:
 +
* [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
 +
* ptr (binary object)
 +
* id (string)  Note: labeled "altid" in the UI
 +
* description (string)
 +
* device_type (string)
 +
* device_filename (string)
 +
* implementation_filename (string)
 +
* parameters (string)
 +
* embedded (boolean)
 +
* [invisible (boolean)] optional
 +
 
 +
returns: nothing
 +
 
 +
Adds one child to device.
 +
 
 +
Pass in the ptr which you received from the <tt>uup.chdev.start</tt> call. Give each child a unique id so you can keep track of which is which. You can optionally provide a description which the user sees in the user interface.
 +
 
 +
<tt>device_type</tt> is the UPnP device type, such as ''urn:schemas-upnp-org:device:BinaryLight:1''.
 +
{{Important|On UI7, the <tt>device_type</tt> '''MUST''' be either the '''empty string''', or '''the same''' as the one in the device file, otherwise the Luup engine will restart continuously.}}
 +
 
 +
If <tt>device_filename</tt> is specified, that is the name of the XML file with the UPnP device specification. If the <tt>device_file</tt> contains the implementation file for this child device you do not need to specify it in <tt>implementation_filename</tt>. Otherwise, if there is a Luup implementation for this child device and it's not being handled by the parent device, you can specify it in <tt>implementation_filename</tt>. The <tt>deviceType</tt> from the filename will override any <tt>device_type</tt> you set manually ''('''NOTE''': This applies only for UI5 and older UIs.)''.
 +
 
 +
If <tt>embedded</tt> is true, the ''embedded'' flag is set for the device which generally means that the parent and all the children will be displayed as one compound device, or group, rather than as separate devices which you can put in their own rooms.
 +
 
 +
The <tt>parameters</tt> are UPnP service,variables you want set when the device is created. You can specify multiple variables by separating them with a line feed (\n) and use a, and = to separate service, variable and value, like this: service,variable=value\nservice...
 +
 
 +
<source lang="lua">
 +
luup.chdev.append(device, children,
 +
    string.format("Input-%d", i), string.format("Input %d", i),
 +
    "urn:schemas-micasaverde-com:device:TemperatureSensor:1", "D_TemperatureSensor1.xml",
 +
    "", "urn:upnp-org:serviceId:TemperatureSensor1,CurrentTemperature=50", true)
 +
</source>
 +
 
 +
=== function: sync  ===
 +
 
 +
parameters: [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]], ptr (binary object),
 +
 
 +
returns: nothing
 +
 
 +
Pass in the ptr which you received from the start function. Tells the Luup engine you have finished enumerating the child devices. If the child devices have changed in any way, the new device tree will be written to the configuration file and the Luup engine is reset.
 +
 
 +
== Module: io ==
 +
io.open<br/>
 +
io.write<br/>
 +
io.intercept<br/>
 +
io.read<br/>
 +
io.is_connected
  
 
=== function: open  ===
 
=== function: open  ===
  
parameters: device (string or number), ip (string) port (number),  
+
parameters: [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]], ip (string), port (as number or string),  
  
 
returns: nothing  
 
returns: nothing  
  
If device is a string it is interpreted as a UDN, if it's a number, as a device ID. This opens a socket on 'port' to 'ip' and stores the handle to the socket in 'device'. The opening of a socket can take time depending on the network, and a Luup function should return quickly whenever possible because each top-level device's Lua implementation runs in a single thread. So the actual opening of the socket occurs asynchronously and this function returns nothing. You will know that the socket opening failed if your subsequent call to write fails.  
+
This opens a socket on 'port' to 'ip' and stores the handle to the socket in 'device'. The opening of a socket can take time depending on the network, and a Luup function should return quickly whenever possible because each top-level device's Lua implementation runs in a single thread. So the actual opening of the socket occurs asynchronously and this function returns nothing. You will know that the socket opening failed if your subsequent call to write fails.  
  
 
Generally you do not need to call the open function because the socket is usually started automatically when the Luup engine starts. This is because the user typically either (a) associates a device with the destination io device, such as selecting an RS232 port for an alarm panel, where the RS232 is proxied by a socket, or (b) because the configuration settings for the device already include an IP address and port.
 
Generally you do not need to call the open function because the socket is usually started automatically when the Luup engine starts. This is because the user typically either (a) associates a device with the destination io device, such as selecting an RS232 port for an alarm panel, where the RS232 is proxied by a socket, or (b) because the configuration settings for the device already include an IP address and port.
 +
 +
There is no 'function: close'.
  
 
=== function: write  ===
 
=== function: write  ===
  
parameters: data (string), device (string or number)  
+
parameters: data (string), [[Luup_Lua_extensions#device:_string_or_number|optional device (string or number)]]
  
returns: result (boolean)  
+
returns: result (boolean or nil)  
  
In Lua a string can contain binary data, so data may be a binary block. If 'device' is a string it is interpreted as a UDN; if it's a number, as a device ID. This sends data on the socket that was opened automatically or with the open function above, and associated to 'device'. If the socket is not already open, write will wait up to 5 seconds for the socket before it returns an error. Result is 'true' if the data was sent successfully, and is 'false' or nill if an error occurred.
+
The device id defaults to self, if omitted. In Lua a string can contain binary data, so data may be a binary block. This sends data on the socket that was opened automatically or with the open function above, and associated to 'device'. If the socket is not already open, write will wait up to 5 seconds for the socket before it returns an error. Result is 'true' if the data was sent successfully, and is 'false' or nil if an error occurred.
  
=== function: intercept ===
+
The written data is modified depending upon the value of the [[Luup_Plugins_ByHand#<protocol>|<protocol> tag]].
  
parameters: device (string or number)
+
=== function: intercept  ===
  
returns: nothing
+
parameters: [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
  
Normally when data comes in on a socket (IO Port), the block of data is first past to any pending jobs that are running for the device and are marked as 'waiting for data'.  If there are none, or if none of the jobs' incoming data handlers report that they consumed (ie processed) the data, then the block of data is past to the general 'incoming' function handler for the device.  If you want to bypass this normal mechanism and read data directly from the socket call intercept first to tell Luup you want to read incoming data with the read function.  This is generally used during the initialization or startup sequences for devices.  For example, you may need to send some data (a), receive some response (b), send some more data (c), receive another response (d), etc.  In this case you would call 'intercept' first, then send a, then call read and confirm you got b, then call intercept again, then send c, then read d, and so on.
+
returns: nothing
  
You can call the read function without calling intercept and any incoming data will be returned by that function after it's called. The reason why you generally must call intercept is because normally you want to send some data and get a response. If you write the code like this ''send(data) data=read()'' then it's possible the response will arrive in the brief moment between the execution of send() and read(), and therefore get sent to the incoming data handler for the device. Intercept tells Luup to buffer any incoming data until the next read, bypassing the normal incoming data handler. So ''intercept() send(data) data=read()'' ensures that read will always get the response. If the device you're communicating with sends unsolicited data then there's the risk that the data you read is not the response you're looking for. If so, you can manually pass the response packet to the incoming data handler.
+
Normally when data comes in on a socket (I/O Port), the block of data is first passed to any pending jobs that are running for the device and are marked as 'waiting for data'. If there are none, or if none of the jobs' incoming data handlers report that they consumed (i.e. processed) the data, then the block of data is passed to the general 'incoming' function handler for the device. If you want to bypass this normal mechanism and read data directly from the socket, call intercept first to tell Luup you want to read incoming data with the read function. This is generally used during the initialization or startup sequences for devices. For example, you may need to send some data (a), receive some response (b), send some more data (c), receive another response (d), etc. In this case you would call 'intercept' first, then send a, then call read and confirm you got b, then call intercept again, then send c, then read d, and so on.
 +
 
 +
You can call the read function without calling intercept and any incoming data will be returned by that function after it's called. The reason why you generally must call intercept is because normally you want to send some data and get a response. If you write the code like this ''send(data) data=read()'' then it's possible the response will arrive in the brief moment between the execution of send() and read(), and therefore get sent to the incoming data handler for the device. Intercept tells Luup to buffer any incoming data until the next read, bypassing the normal incoming data handler. So ''intercept() send(data) data=read()'' ensures that read will always get the response. If the device you're communicating with sends unsolicited data then there's the risk that the data you read is not the response you're looking for. If so, you can manually pass the response packet to the incoming data handler.  
  
 
**TBD: Add a function to do this**
 
**TBD: Add a function to do this**
  
=== function: read ===
+
=== function: read ===
 +
 
 +
parameters: timeout (number), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
 +
 
 +
returns: data (string)
 +
 
 +
This reads a block of data from the socket. You must have called ''intercept'' previously so the data is passed. The time unit for ''timeout'' is seconds.<br>
 +
 
 +
=== function: is_connected  ===
 +
 
 +
parameters: [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
 +
 
 +
returns: connected (boolean)
 +
 
 +
This function returns true if there is a valid IO port connected, otherwise returns false. Unplugging the LAN cable associated with the port, will not set the flag to false.
 +
 
 +
== Module: luup.job  ==
 +
 
 +
=== function: status  ===
 +
 
 +
parameters: job_number (number), [[Luup_Lua_extensions#device:_string_or_number|device (string or number)]]
 +
 
 +
returns: job_status (number), notes (string)
 +
 
 +
If '''job_number''' is invalid the function returns ''-1''. If '''device''' is a string it is interpreted as an UDN, if it's a number, as a device ID.
 +
 
 +
This is the list with all job statuses and their meaning:
 +
* '''-1''': No job, i.e. job doesn't exist.
 +
*  '''0''': Job waiting to start.
 +
*  '''1''': Job in progress.
 +
*  '''2''': Job error.
 +
*  '''3''': Job aborted.
 +
*  '''4''': Job done.
 +
*  '''5''': Job waiting for callback. Used in special cases.
 +
*  '''6''': Job requeue. If the job was aborted and needs to be started, use this special value.
 +
*  '''7''': Job in progress with pending data. This means the job is waiting for data, but can't take it now.
 +
 
 +
=== function: set  ===
 +
 
 +
parameters: job (userdata), setting (string), value (string)
 +
 
 +
returns: nothing
  
parameters: timeout (number), device (string or number)
+
This stores a setting for a job.
  
returns: data (string)
+
<source lang="lua"><job>
 +
    luup.job.set(lul_job, "comments", "In progress...")
 +
    local comments = luup.job.setting(lul_job, "comments")
 +
    luup.log("job comments = " .. comments)
 +
</job></source>
  
This reads a block of data from the socket. You must have called intercept previously so the data is passed
+
=== function: setting ===
  
=== I/O data ===
+
parameters: job (userdata), setting (string)
  
lu_iop_intercept_incoming
+
returns: value (string)
  
lu_iop_open
+
This returns a setting for a job.
  
lu_iop_recv_block
+
== Notes ==
 +
=== device: string or number ===
 +
*If a number, it is the device ID
 +
*If a string, it is the UDN for the UPnP device
 +
Both of these can be found in the User Interface (UI5) under the advanced Tab as "id" and "local_udn" respectively.
  
lu_iop_send
+
Examples:
  
=== Job Handling ===
+
<source lang="lua">
 +
local update_frequency = luup.variable_get("S_WebcamDropboxUploaderSettings1.xml","SendFrequency",87)
 +
local update_frequency = luup.variable_get("S_WebcamDropboxUploaderSettings1.xml","SendFrequency","uuid:4d494342-5342-5645-0057-000001c9d682")
 +
</source>
  
lu_job_set
 
  
lu_job_get
+
[[Category:Development]]

Latest revision as of 07:51, 27 March 2018

In addition to the [Lua] commands described in the [Lua reference manual], you can also reference in your Lua code variables and functions from modules which the Luup engine provides as follows:

Contents

[edit] Module: luup

These are general purpose functions and variables. Call them by using the luup. module, such as:

luup.log('Now running version: ' .. luup.version)

[edit] variable: device

The ID of this device instance, if it's running as part of a device

[edit] variable: version, version_branch, version_major, version_minor

version contains the version of the luup engine, such as "1.0.843", as a string. The version consists of 3 numbers separated by a period: the branch, the major version, and the minor version. To make it easier to compare against a minimum acceptable version, version_branch, version_major and version_minor return each component as a number. You can put a comparison in the startup function in your Luup plugin and return false if the minimum version isn't met. The user will then see that the Luup device's startup check failed:

if( version_branch ~= 1 or version_major ~= 0 or version_minor < 843 ) then
    luup.log("I need version 1.0.843 minimum to run")
    return false
end

[edit] variable: longitude

Contains the longitude as a number, as found on the location tab in the setup UI.

[edit] variable: latitude

Contains the latitude as a number, as found on the location tab in the setup UI.

[edit] variable: timezone

Contains the timezone as a number of hours offset from UTC, as found on the location tab in the setup UI. It accounts for DST, so, for example, Pacific Standard time will be -8 or -9 depending on DST.

Note: Contains 0 for MiOS < 1.5.250 (Vera V2) / < 1.5.249 (Vera V3).

[edit] variable: city

Contains the city as a string, as found on the location tab in the setup UI.

[edit] variable: devices

Contains all the devices in the system as a table indexed by the device number.

The members are:

  • room_num: (number) This is the number of the room the device is in.
  • device_type: (string) This is a string representing the type of the device.
  • category_num: (number) This is a category for the device. See: Luup_Device_Categories for a list.
  • subcategory_num: (number) This is a sub category for the device.
  • device_num_parent: (number) This is the number of the parent device. See: Lua Device Structure for details.
  • ip: (string) If this device is IP based, this is the IP address.
  • mac: (string) If this device is IP based, this is the MAC address.
  • user: (string) If this device is IP based and requires http authentication, this is the username
  • pass: (string) If this device is IP based and requires http authentication, this is the password
  • id: (string) If this device has an internal ID that is specific to the device, it is contained here. For example, for Z-Wave devices this is the Node ID, and for Insteon device it is the Insteon ID.
  • embedded: (boolean) If this device is embedded, it means that it doesn't have its own room or exist as a separate device. It should be considered part of its parent. Like a 3-in-1 sensor is a device with 3 embedded child devices.
  • hidden: (boolean) If true the user checked the 'hidden' box and doesn't want to see the device on the dashboard.
  • invisible: (boolean) If true the device is 'for internal use only' and shouldn't be presented to the user.
  • description: (string) This is the text description for the device as supplied by the user in the web UI.
  • udn: (string) This is the UDN for the UPnP device.


Example to log device #5's IP address and its internal ID:

luup.log('Device #5 ip: ' .. luup.devices[5].ip .. ' id: ' .. luup.devices[5].id)


This code will log all the attributes from all the devices:

for k, v in pairs(luup.devices) do
    for k2, v2 in pairs(v) do
        luup.log("Device #" .. k .. ":" .. k2 .. "=" .. tostring(v2))
    end
end
 
return true

Note: if an attribute is modified, it will not persist between Luup engine restarts. You need to use luup.attr_set instead. Eg:

local ipAddress = '192.168.l.l2'
 
-- this will not persist between restarts
luup.devices[lul_device].ip = ipAddress
 
-- this will persist between restarts
luup.attr_set('ip', ipAddress, lul_device)

[edit] variable: rooms

Contains all the rooms as a table of strings indexed by the room number. Example:

luup.log('Room #1 is called: ' .. luup.rooms[1])

[edit] variable: scenes

Contains all the scenes in the system as a table indexed by the scene number. The members are: room_num (number), description(string), hidden(boolean)

[edit] variable: remotes

Contains all the remotes in the system as a table indexed by the remote id. The members are: remote_file (string), room_num (number), description(string)

[edit] variable: event_server

type: string

Contains the notification/event server. On UI5 it can be either cms1.mios.com or cms2.mios.com.

[edit] variable: ra_server

type: string

Contains the remote access server. Can be either fwd1.mios.com or fwd2.mios.com.

[edit] variable: pk_accesspoint

type: number

Contains the serial number of this Vera.

[edit] variable: hw_key

type: string

Contains the Vera hardware key.

[edit] function: log

parameters: what_to_log (string), log_level (optional, number)

return: nothing

Writes what_to_log to the log, /var/log/cmh/LuaUPnP.log, with the given log_level, which is 50 by default. See: Luup Loglevels

[edit] function: task

parameters: message (string), status (number), description (string), handle (number)

return: handle (number)

When the Luup engine is starting status messages are displayed for the various modules as they're initialized. Normally each device, including Luup devices, automatically log their status and the user is shown an error if the device doesn't start, such as if the 'startup' function returns an error.

If you have other startup sequences which you want the user to see to know that startup hasn't finished yet, call this function passing in a handle of -1 for the first call. The status should be: 1=Busy, 2=Error, 4=Successful. Message is the current state, such as 'downloading', and description describes the module, like 'Smartphone UI'. After the first call, store the handle and pass it on future calls to update the status rather than add a new one.

[edit] function: call_delay

parameters: function_name (string), seconds (number), data (string), thread (bool)

returns: result (number)

The function function_name (the first parameter), which must be passed as a string, will be called in seconds seconds (the second parameter), and will be passed the string data. The function returns 0 if successful. See: Luup Declarations#timed_function_callback for the names and syntax of the parameters that will be passed to function_name. function_name will only be called once and you must call call_delay again if you want it called again, or use call_timer instead.

If thread is specified and is true or 1, the call back will be made in it's own thread and can block if needed. Normally it is called by a worker thread and is expected to return immediately.

As of December 19, 2011, for all builds after 1.5.237, the 'thread' will be ignored. Each Lua state has its own worker thread now, so all calls to call_delay and call_timer will occur in a separate thread.

[edit] function: call_timer

parameters: function_name (string), type (number), time (string), days (string), data (string)

returns: result (number)

The function 'function_name', which must be passed as a string, will be called when the timer is triggered, and will be passed the string 'data'. The function returns 0 if successful. See: Luup Declarations#timed_function_callback for the names and syntax of the parameters that will be passed to function_name.

Type is 1=Interval timer, 2=Day of week timer, 3=Day of month timer, 4=Absolute timer. For an interval timer, days is not used, and Time should be a number of seconds, minutes, or hours using an optional 'h' or 'm' suffix. Example: 30=call in 30 seconds, 5m=call in 5 minutes, 2h=call in 2 hours. For a day of week timer, Days is a comma separated list with the days of the week where 1=Monday and 7=Sunday. Time is the time of day in hh:mm:ss format. Time can also include an 'r' at the end for Sunrise or a 't' for Sunset and the time is relative to sunrise/sunset. For example: Days="3,5" Time="20:30:00" means your function will be called on the next Wed or Fri at 8:30pm. Days="1,7" Time="-3:00:00r" means your function will be called on the next Monday or Sunday 3 hours before sunrise. Day of month works the same way except Days is a comma separated list of days of the month, such as "15,20,30". For an absolute timer, Days is not used, and Time should be in the format: "yyyy-mm-dd hh:mm:ss"

Data can be a string passed back to the function. The function should be declared so it takes a single argument, which is this data.

function refreshCache(stuff)
    ....
end
 
function startup()
    --
    -- Setup an interval-based timer to call refreshCache after 30 minutes.
    -- Note that if you want it to "recur" then you need to call this function again
    -- at the end of the refreshCache() implementation.
    --
    luup.call_timer("refreshCache", 1, "30m", "", "SomeStuff")
end

[edit] function: is_ready

parameters: device (string or number)

returns: ready (boolean)

version: UI5 and above

Checks whether a device has successfully completed it's startup sequence. If so, is_ready returns true. If your device shouldn't process incoming data until the startup sequence is finished, you may want to add a condition to the <incoming> block that only processes data if is_ready(lul_device) is true.

<incoming>
    if (luup.is_ready(lul_device) == false) then
        return
    end
 
    doSomething(lul_device)
</incoming>

[edit] function: call_action

parameters: service (string), action (string), arguments (table), device (string or number)

returns: error (number), error_msg (string), job (number), arguments (table)

Invokes the UPnP service + action, passing in the arguments (table of string->string pairs) to the device. If the invocation could not be made, only error will be returned with a value of -1. Otherwise, all 4 values are returned. error is 0 if the UPnP device reported the action was successful. arguments is a table of string->string pairs with the return arguments from the action. If the action is handled asynchronously by a Luup job, then the job number will be returned as a positive integer.

Warning.png Currently only Z-Wave and Insteon jobs are returned in the job parameter.

Example to dim device #5 to 50%:

local resultCode, resultString, job, returnArguments = luup.call_action("urn:upnp-org:serviceId:Dimming1", "SetLoadLevelTarget", {["newLoadlevelTarget"] = "50"}, 5)


[edit] function: variable_set

parameters: service (string), variable (string), value (string), device (string or number), [startup (bool)]

returns: nothing

The UPnP 'service' + 'variable' will be set to the 'value' for this device. If there are events or notifications tied to the variable they will be fired.

The device parameter: if it's a string, is interpreted as a udn. If it's a number, it's interpreted as a device number.

Optionally, you can add an argument 'startup'. If startup is true, this change will be considered a startup value, and if the variable is set to it's existing value, events and notifications will not be fired.

[edit] function: variable_get

parameters: service (string), variable (string), device (string or number)

returns: value (string) and Unix time stamp (number) of when the variable last changed

Returns the value of the UPnP service + variable and the time when the variable was last modified, as a unix time stamp (number of seconds since 1/1/1970). If the service+variable or device does not exist, it returns nothing.

Example usage

local value, tstamp = luup.variable_get("urn:upnp-org:serviceId:Dimming1", "LoadLevelTarget", 5)
luup.log("Dim level for device #5 is: " .. value .. " last changed (Epoch): " .. tstamp)

The device parameter: if it's a string, is interpreted as a udn. If it's a number, it's interpreted as a device number.

Caution - Incorrect usages

local value = tonumber(luup.variable_get("urn:upnp-org:serviceId:Dimming1", "LoadLevelTarget", 5))

luup.variable_get returns two parameters and tonumber also accepts two parameters. However the parameters are incompatible: the Unix timestamp returned by luup.variable_get is being used as a number base in the function tonumber. The number base is limited to a power of 36 or less and the current timestamps are in the range of thousands of millions.

local value = tostring(luup.variable_get("urn:upnp-org:serviceId:Dimming1", "LoadLevelTarget", 5))

luup.variable_get returns two parameters and tostring only expects one

[edit] function: attr_set

parameters: attribute (string), value (string), device (string or number), refresh_user_data (boolean)

returns: none

Sets the top level attribute for the device to value. Examples of attributes are 'mac', 'name', 'id', etc. Like attr_get, if the device is zero it sets the top-level user_data json tag. If 'refresh_user_data' is true, setting the attribute will increment the DataVersion in the user_data.

[edit] function: attr_get

parameters: attribute (string), device (string or number)

returns: if the device (string or number) is valid, then the attribute is returned as a string.

If the device (string or number) is valid but the attribute doesn't exist, then the returned string is empty.

If the device (string or number) is invalid and due to a MIOS coding problem, the expected 'nil' is not returned (as of March 2018). Instead '(null)' is returned.

Gets the requested attribute for the device: Examples of attributes are 'mac', 'name', 'id', etc. If the number 0 is passed as the device, it gets the top level attribute from the master userdata, like 'firmware_version', etc.

[edit] function: ip_set

Not available in UI5 or lower

parameters: value (string), device (string or number)

returns: none

Sets the IP address for a device. This is better than setting the "ip" attribute using attr_set because it updates internal values additionally, so a reload isn't required.

[edit] function: mac_set

Not available in UI5 or lower

parameters: value (string), device (string or number)

returns: none

Sets the mac address for a device. This is better than setting the "mac" attribute using attr_set because it updates internal values additionally, so a reload isn't required.

[edit] function: reload

Not available in UI5 or lower

parameters: none

returns: none

Reloads the Luup engine.

[edit] function: create_device

Not available in UI5 or lower

parameters:

  • device_type (string) : This is the device type and should be set to an empty string, so it will be retrieved from the UPnP device file.
  • internal_id (string) : This is the device altid.
  • description (string) : This is the device name.
  • upnp_file (string) : This is the UPnP device file.
  • upnp_impl (string) : This is the implementation file.
  • ip (string)
  • mac (string)
  • hidden (boolean)
  • invisible (boolean)
  • parent (number) : The device ID of the parent device. Set it to '0' if this device shouldn't have any parents.
  • room (number) : The room ID. If set to '0', the device won't be put in any room.
  • pluginnum (number) : Which plugin to install after the device is created. Set it to '0' if no plugin should be installed.
  • statevariables (string) : Variables and attributes you want set when the device is created. You can specify multiple variables by separating them with a line feed (\n) and use ',' and '=' to separate service, variable and value, like this: service,variable=value\nservice,variable=value

To set an attribute, leave "service" empty, like this: ,variable=value

Example:
"urn:upnp-org:serviceId:TemperatureSensor1,CurrentTemperature=50\n,manufacturer=MiOS"
  • pnpid (number) : If this is a device from KitDevice.json, this is the PK_KitDevice number. Otherwise set this to '0'.
  • nochildsync (string) : If set to "1", ignore this device when syncing child devices. Unless you know what you're doing, set it to an empty string.
  • aeskey (string) : This should be set to an empty string.
  • reload (boolean) : If true, the Luup engine will be restarted after the device is created.
  • nodupid (boolean) : Set this to false.

returns: the device ID

Warning.png All parameters are mandatory.
Important.png device_type MUST be either an empty string, or the same as the one in the device file, otherwise the Luup engine will restart continuously.

This creates the device with the parameters given, and returns the device ID.

Example:
luup.create_device("", "testdevice", "Test Device", "D_BinaryLight1.xml", "", "", "", false, false, 0, 0, 0, ",manufacturer=MiOS", 0, "", "", true, false)


[edit] function: device_message

Available in releases after Feb 2017

This adds a system message that is attached to a device and appears in the UI under the device.

parameters:

  • device_id (number) : This is the device id number
  • status (int) : This is the status of message, and corresponds to the job status, and generally determines what color the message appears:
    • -1 : No job: black message
    • 0 : Job waiting to start: blue message
    • 1 : Job in progress: blue message
    • 2 : Job error: red message
    • 3 : Job aborted: red message
    • 4 : Job done: green message
    • 5 : Job waiting for callback: blue message
    • 6 : Job requeue: blue message
    • 7 : Job in progress with pending data: black message
  • message (string) : This is the text that appears in the message.
  • timeout (int) : This is the number of seconds to display the message. Pass 0 to display it indefinitely
  • source (string) : This is the source module of the message. It can be anything, and is generally informational. It is recommended to use the name of the luup plugin.

return: nothing

Warning.png All parameters are mandatory.


[edit] function: register_handler

parameters: function_name (string), request_name (string)

returns: nothing

When a certain URL is requested from a web browser or other HTTP get, function_name will be called and whatever string and content_type it returns will be returned.

See the Smartphone Web Interface plugin as an example:

luup.register_handler("lug_WapRequest","wap")
 
function lug_WapRequest (lul_request, lul_parameters, lul_outputformat)
    local lul_html = "<head>\n" ..
                     "<title>Main</title>\n" ..
                     "</head>\n" ..
                     "<body>\n" ..
                     "Choose a room:<br/>\n"
    local lul_content_type = "text/html"
    return lul_html, lul_content_type
end

The request is made with the URL: data_request?id=lr_[the registered name] on port 49451. So: http://192.168.1.1:49451/data_request?id=lr_wap will return the web page defined in the function lug_WapRequest in the example above.

[edit] function: variable_watch

parameters: function_name (string), service (string), variable (string or nil), device (string or number)

returns: nothing

Whenever the UPnP variable is changed for the specified device, which if a string is interpreted as a UDN and if a number as a device ID, function_name will be called. See Luup Declarations#watch_callback for the values that will be passed to function_name. If variable is nil, function_name will be called whenever any variable in the service is changed.

[edit] function: job_watch

parameters: function_name (string), device (string or number)

returns: nothing

Whenever a job is created, finished, or changes state then function_name will be called. If the device is nil or not specified, function_name will be called for all jobs, otherwise only for jobs that involve the specified device.

Example:

 luup.job_watch("mycallback")
 luup.job_watch("mycallback",6)

The first one registers a callback for all devices, the second one only for device 6. Note that multiple registrations will result in multiple callbacks, so two calls like that means that "mycallback" would be called once for all devices except 6, and for 6 it would be called twice.

The callback function will be passed a table which contains:

device_num: the number of the device

status: the job status, 0-7 as follows

 WaitingToStart=0
 InProgress=1
 Error=2
 Aborted=3
 Done=4
 WaitingForCallback=5
 Requeue=6
 InProgressPendingData=7

name: the name of the job

type: the C++ class name for the type of job

notes: any notes or progress that set for the job

Here is an example of the callback:

 function mycallback(lul_job)
   luup.log("mycallback device #" .. lul_job.device_num .. " status " .. lul_job.status .. " name " .. lul_job.name .. " type " .. lul_job.type .. " notes " .. lul_job.notes);
 end

and here is the output in LuaUPnP.log from the above function when it is registered to watch a ZWave device which was turned ON:

 50      07/09/14 17:43:43.491   luup_log:3: mycallback device #6 status 0 name ON node 2 type ZWJob_SendData notes  <00E998D0>
 50      07/09/14 17:43:43.492   luup_log:3: mycallback device #6 status 1 name ON node 2 type ZWJob_SendData notes  <00E93328>
 50      07/09/14 17:43:43.493   luup_log:3: mycallback device #6 status 7 name ON node 2 type ZWJob_SendData notes Sending the Z-Wave command after 0 retries <00E93328>
 50      07/09/14 17:43:43.520   luup_log:3: mycallback device #6 status 5 name ON node 2 type ZWJob_SendData notes Waiting for node to reply after 0 retries <00E93328>
 50      07/09/14 17:43:43.543   luup_log:3: mycallback device #6 status 0 name ON node 2 type ZWJob_SendData notes Waiting to send again with ack <00E92A58>
 50      07/09/14 17:43:43.544   luup_log:3: mycallback device #6 status 1 name ON node 2 type ZWJob_SendData notes Waiting to send again with ack <00E93328>
 50      07/09/14 17:43:43.545   luup_log:3: mycallback device #6 status 7 name ON node 2 type ZWJob_SendData notes Sending the Z-Wave command after 0 retries <00E93328>
 50      07/09/14 17:43:43.576   luup_log:3: mycallback device #6 status 5 name ON node 2 type ZWJob_SendData notes Waiting for node to reply after 0 retries <00E93328>
 50      07/09/14 17:43:43.631   luup_log:3: mycallback device #6 status 4 name ON node 2 type ZWJob_SendData notes Transmit was ok <00E92A58>

[edit] function: devices_by_service

parameters:

returns:

[edit] function: device_supports_service

parameters: service ID (string), device (string or number)

returns: true if the device supports the service, false otherwise

A device supports a service if there is at least a command or state variable defined for that device using that service. Setting UPnP variables is unrestricted and free form, and the engine doesn't really know if a device actually uses it or does anything with it. So this function isn't really definitive.

[edit] function: set_failure

parameters: value (int), device (string or number)

returns:

Luup maintains a 'failure' flag for every device to indicate if it is not functioning. You can set the flag to 1 if the device is failing, 0 if it's working, and 2 if the device is reachable but there's an authentication error. If device is a string it is interpreted as a udn, if it's a number, as a device id. The lu_status URL will show for the device: <tooltip display="1" tag2="Lua Failure"/> and Lua Failure is shown in red in UI5 for the device.

[edit] function: is_night

parameters: none

returns: true if it's past sunset and before sunrise, false otherwise.

[edit] function: sleep

parameters: number of milliseconds

returns: none

Sleeps a certain number of milliseconds

[edit] function: sunset / sunrise

parameters: none

returns: The next sunset / sunrise in a Unix timestamp (i.e. the number of seconds since 1/1/1970 in UTC time). You can do a diff with os.time to see how long it will be for the next event. luup.sunset-os.time is the number of seconds before the next sunset. Be sure the location and timezone are properly set or the sunset/sunrise will be wrong.

required firmware: 1.5.353

[edit] Module: luup.inet

[edit] function: wget

parameters: URL (String), Timeout (Number), Username (String), Password (String)

returns: operationStatusCode (Number), content (String), httpStatusCode (Number)

This reads the URL and returns 3 variables: the first is a numeric error code which is 0 if successful. The second variable is a string containing the contents of the page. The third variable is the HTTP status code. If Timeout is specified, the function will timeout after that many seconds. The default value for Timeout is 5 seconds. If Username and Password are specified, they will be used for HTTP Basic Authentication.


[edit] Module: luup.chdev

Contains functions for a parent to synchronize its child devices. Whenever a device has multiple end-points, the devices are represented in a parent/child fashion where the parent device is responsible for reporting what child devices it has and giving each one a unique id. For example in the sample Luup Somfy Walkthrough there is a parent device, which is the interface module that controls up to 16 blinds, and up to 16 child devices, one for each blind. As shown in that sample, the parent calls start, then enumerates each child device with append, and finally calls sync. You will need to pass the same value for device to append and sync that you passed to start.

[edit] function: start

parameters: device (string or number)

returns: ptr (binary object)

Tells Luup you will start enumerating the children of device. If device is a string it is interpreted as a udn, if it's a number, as a device id. The return value is a binary object which you cannot do anything with in Lua, but you do pass it to the append and sync functions.

[edit] function: append

"append" is a misnomer as every time you use the luup.chdev module, you must enumerate all the children:

  • if you do not "append" an existing child, that child will be deleted
  • if you "append" an existing child, you have the opportunity to change its parameters. All the parameters are required, even for existing children.
  • if you "append" a non-existent child, it will be created.

If you have the situation where child devices may come and go; eg say children are created for a WiFi sensor but the WiFi drops out on occasion, then you need to keep a record of the all the children, so they can all be enumerated at start up. Otherwise missing children will be deleted. If they reappear, say WiFi reconnects in this example, then the children will be added back in but will have a new device id.

When the final luup.chdev.sync is executed the children are checked. Any additions, changes or deletions will result in a Luup engine restart. Any incorrect coding of luup.chdev.append may result in a situation where the engine goes into a loop and continually restarts. As an example, if two children are added with the same id parameter, this will occur. To recover, you need to quickly upload a new file that has the faulty "append" code commented out. In the interim the engine will be incrementing the device ID numbers with no end in sight.

parameters:

  • device (string or number)
  • ptr (binary object)
  • id (string) Note: labeled "altid" in the UI
  • description (string)
  • device_type (string)
  • device_filename (string)
  • implementation_filename (string)
  • parameters (string)
  • embedded (boolean)
  • [invisible (boolean)] optional

returns: nothing

Adds one child to device.

Pass in the ptr which you received from the uup.chdev.start call. Give each child a unique id so you can keep track of which is which. You can optionally provide a description which the user sees in the user interface.

device_type is the UPnP device type, such as urn:schemas-upnp-org:device:BinaryLight:1.

Important.png On UI7, the device_type MUST be either the empty string, or the same as the one in the device file, otherwise the Luup engine will restart continuously.

If device_filename is specified, that is the name of the XML file with the UPnP device specification. If the device_file contains the implementation file for this child device you do not need to specify it in implementation_filename. Otherwise, if there is a Luup implementation for this child device and it's not being handled by the parent device, you can specify it in implementation_filename. The deviceType from the filename will override any device_type you set manually (NOTE: This applies only for UI5 and older UIs.).

If embedded is true, the embedded flag is set for the device which generally means that the parent and all the children will be displayed as one compound device, or group, rather than as separate devices which you can put in their own rooms.

The parameters are UPnP service,variables you want set when the device is created. You can specify multiple variables by separating them with a line feed (\n) and use a, and = to separate service, variable and value, like this: service,variable=value\nservice...

luup.chdev.append(device, children,
    string.format("Input-%d", i), string.format("Input %d", i),
    "urn:schemas-micasaverde-com:device:TemperatureSensor:1", "D_TemperatureSensor1.xml",
    "", "urn:upnp-org:serviceId:TemperatureSensor1,CurrentTemperature=50", true)

[edit] function: sync

parameters: device (string or number), ptr (binary object),

returns: nothing

Pass in the ptr which you received from the start function. Tells the Luup engine you have finished enumerating the child devices. If the child devices have changed in any way, the new device tree will be written to the configuration file and the Luup engine is reset.

[edit] Module: io

io.open
io.write
io.intercept
io.read
io.is_connected

[edit] function: open

parameters: device (string or number), ip (string), port (as number or string),

returns: nothing

This opens a socket on 'port' to 'ip' and stores the handle to the socket in 'device'. The opening of a socket can take time depending on the network, and a Luup function should return quickly whenever possible because each top-level device's Lua implementation runs in a single thread. So the actual opening of the socket occurs asynchronously and this function returns nothing. You will know that the socket opening failed if your subsequent call to write fails.

Generally you do not need to call the open function because the socket is usually started automatically when the Luup engine starts. This is because the user typically either (a) associates a device with the destination io device, such as selecting an RS232 port for an alarm panel, where the RS232 is proxied by a socket, or (b) because the configuration settings for the device already include an IP address and port.

There is no 'function: close'.

[edit] function: write

parameters: data (string), optional device (string or number)

returns: result (boolean or nil)

The device id defaults to self, if omitted. In Lua a string can contain binary data, so data may be a binary block. This sends data on the socket that was opened automatically or with the open function above, and associated to 'device'. If the socket is not already open, write will wait up to 5 seconds for the socket before it returns an error. Result is 'true' if the data was sent successfully, and is 'false' or nil if an error occurred.

The written data is modified depending upon the value of the <protocol> tag.

[edit] function: intercept

parameters: device (string or number)

returns: nothing

Normally when data comes in on a socket (I/O Port), the block of data is first passed to any pending jobs that are running for the device and are marked as 'waiting for data'. If there are none, or if none of the jobs' incoming data handlers report that they consumed (i.e. processed) the data, then the block of data is passed to the general 'incoming' function handler for the device. If you want to bypass this normal mechanism and read data directly from the socket, call intercept first to tell Luup you want to read incoming data with the read function. This is generally used during the initialization or startup sequences for devices. For example, you may need to send some data (a), receive some response (b), send some more data (c), receive another response (d), etc. In this case you would call 'intercept' first, then send a, then call read and confirm you got b, then call intercept again, then send c, then read d, and so on.

You can call the read function without calling intercept and any incoming data will be returned by that function after it's called. The reason why you generally must call intercept is because normally you want to send some data and get a response. If you write the code like this send(data) data=read() then it's possible the response will arrive in the brief moment between the execution of send() and read(), and therefore get sent to the incoming data handler for the device. Intercept tells Luup to buffer any incoming data until the next read, bypassing the normal incoming data handler. So intercept() send(data) data=read() ensures that read will always get the response. If the device you're communicating with sends unsolicited data then there's the risk that the data you read is not the response you're looking for. If so, you can manually pass the response packet to the incoming data handler.

    • TBD: Add a function to do this**

[edit] function: read

parameters: timeout (number), device (string or number)

returns: data (string)

This reads a block of data from the socket. You must have called intercept previously so the data is passed. The time unit for timeout is seconds.

[edit] function: is_connected

parameters: device (string or number)

returns: connected (boolean)

This function returns true if there is a valid IO port connected, otherwise returns false. Unplugging the LAN cable associated with the port, will not set the flag to false.

[edit] Module: luup.job

[edit] function: status

parameters: job_number (number), device (string or number)

returns: job_status (number), notes (string)

If job_number is invalid the function returns -1. If device is a string it is interpreted as an UDN, if it's a number, as a device ID.

This is the list with all job statuses and their meaning:

  • -1: No job, i.e. job doesn't exist.
  • 0: Job waiting to start.
  • 1: Job in progress.
  • 2: Job error.
  • 3: Job aborted.
  • 4: Job done.
  • 5: Job waiting for callback. Used in special cases.
  • 6: Job requeue. If the job was aborted and needs to be started, use this special value.
  • 7: Job in progress with pending data. This means the job is waiting for data, but can't take it now.

[edit] function: set

parameters: job (userdata), setting (string), value (string)

returns: nothing

This stores a setting for a job.

<job>
    luup.job.set(lul_job, "comments", "In progress...")
    local comments = luup.job.setting(lul_job, "comments")
    luup.log("job comments = " .. comments)
</job>

[edit] function: setting

parameters: job (userdata), setting (string)

returns: value (string)

This returns a setting for a job.

[edit] Notes

[edit] device: string or number

  • If a number, it is the device ID
  • If a string, it is the UDN for the UPnP device

Both of these can be found in the User Interface (UI5) under the advanced Tab as "id" and "local_udn" respectively.

Examples:

local update_frequency = luup.variable_get("S_WebcamDropboxUploaderSettings1.xml","SendFrequency",87)
local update_frequency = luup.variable_get("S_WebcamDropboxUploaderSettings1.xml","SendFrequency","uuid:4d494342-5342-5645-0057-000001c9d682")
Personal tools