How to require an external lua file in a script / macros

How do I include an external file?
There is the require 'filename' Lua procedure, but where should the file be located so it could be included?

They can go anywhere on the load path.
Usually in the /etc/cmh-ludl
You can see (Download) or put (Upload) files there from the Apps -> Develop Apps -> Luup Files tab.

The lua file must start with:
module(“YourModuleFileName”, package.seeall)
Read the LUA documentation about modules.

Vera has a modified LUA file loader …
A lot of things that are available to loading LUA files in a standalone environment are NOT available in Vera.

If you are using UI4 you will have to put the /etc/cmh-ludl on the load path. For a device XXX that uses lua files, Check the I_XXX.xml files for an example.

Slowly crawling out of the primaeval slime of total Vera/MiOS ignorance into the equally messy world of Luup code, via the pristine stepping stone of Lua itself, I am beginning to use [tt]require[/tt] and [b][tt]module[/tt]/b

Finding the need, also for sub-modules, I’ve followed @intveltr’s technique in:
http://forum.micasaverde.com/index.php/topic,12530.msg91989.html#msg91989
of banging my head on the desk for an afternoon. What I have found is:

A top level module is, as described here and elsewhere, loaded by:

require "module-filename"

That module file should start with:

module("module-name", package.seeall)

and may contain other [tt]require[/tt] statements for sub-modules:

require "sub-module-filename"

The trick appears to be that in the sub-module, the name has to be carefully constructed:

module("module-name.sub-module-name", package.seeall)

Within the (sub-)modules, variables and defined functions can conveniently be used without the package prefix, but it seems that executable code (for initialization on loading) has to use the full path names. A complete (but trivial) example:

File “a.lua”:

module ("a", package.seeall)

v = 42
function f()
    return  v / 2
end

require "b"

-- initialization / startup code (if any needed)
luup.log("a.f returns: " .. a.f() ) -- just an example

File “b.lua”:

module ("a.b", package.seeall)

w = 7
function f()
    return  w / 2
end

-- initialization / startup code (if any needed)
luup.log("a.b.f returns: " .. a.b.f() ) -- just an example

The above leaves any chunk which calls [tt]require “a”[/tt] with access to variables [tt]a.v, a.b.w[/tt] and (different) functions [tt]a.f(), a.b.f()[/tt].

This is perhaps all obvious to Luup demi-gods, but I am, as yet, just crawling on my front flippers. Hope, at least, that (a) this is correct, and (b) this may be useful documentation for someone else.

Hi

Im looking to clean up my lua startup script, so instead of a long list of code snippets, with comment separators, Id like to load each script as a Module.

Reading the above guidance i could not see it mentioned if you need to use the file extension or not in the Lua Startup section on Vera ? Is it…

require "xxloftlightmotion" 

Or

require "xxloftlightmotion.lua" 

Same question about the header in the lua file itself is it

module("xxloftlightmotion", package.seeall)

Or

module("xxloftlightmotion.lua", package.seeall)

Or does it not matter, just curious if the best practice. E.g could the lua be in a txt file, does it care ?

[ol][li]require - no, it looks for .lua files (did you try?)[/li]
[li]module - this name is not a filename, so no .lua here either[/li][/ol]

Thanks @akbooer

I have not tried anything yet (I?ve just started to put my code into separate standalone files)
I just wanted to ensure I understood the naming requirements of require and module commands in the Vera world as you alluded to them being a bit different in an earlier post.

One point of clarity / the files I eventually upload should still have a .lua file extension ?

Plus from what Id read elsewhere, the Module name and its file name should be the same ?

Yes.

Plus from what Id read elsewhere, the Module name and its file name should be the same ?

This seems to be a Vera restriction - they re-wrote ‘require’ to handle compressed and encrypted files.

The more ‘modern’ approach in Lua doesn’t use the ‘module’ statement at all, anyway.

That sounds much simpler to follow…

Humm, I thought I followed it all correctly but I get an error ??

I added this line to the Vera Lua start up

require "xxprowlstartupfunction"

Then the following is the contents of that required file, (which I had already uploaded via Apps/Development) …

[code]module(“xxprowlstartupfunction”, package.seeall)

function prowl_encode (s)
s = string.gsub(s, " ", “+”)
return s
end

function my_prowl (appl, event, description)
local prowl_url = “https://api.prowlapp.com/publicapi/add?apikey=
… “yourabc123code”
… “&application=” … prowl_encode(appl)
… “&event=” … prowl_encode(event)
… “&description=” … prowl_encode(description)
… “&priority=-1”
luup.inet.wget(prowl_url)
end
[/code]

Once done, and luup reloaded I then went into the Dev App / Test Lua Code and submitted this line to make use my_prowl

my_prowl ("Vera Home Controller", "Testing code", "this could any description")

And then this was the error in the log - any ideas why ?

08 10/29/17 22:04:07.951 JobHandler_LuaUPnP::HandleActionRequest device: 0 service: urn:micasaverde-com:serviceId:HomeAutomationGateway1 action: RunLua <0x303b0680> 08 10/29/17 22:04:07.951 JobHandler_LuaUPnP::HandleActionRequest argument Code=my_prowl ("Vera Home Controller", "Testing code", "this could any description") <0x303b0680> 01 10/29/17 22:04:07.952 LuaInterface::StartEngine failed run: 0 [string "my_prowl ("Vera Home Controller", "Testing ..."]:1: attempt to call global 'my_prowl' (a nil value) <0x303b0680> [color=red]01 10/29/17 22:04:07.953 JobHandler_LuaUPnP::RunLua failed: my_prowl ("Vera Home Controller", "Testing code", "this could any description")[/color] <0x303b0680>
And then this was the error in the log - any ideas why ?

Well, the module name defines a global namespace within which its functions are defined.

I’d suggest that you give this a go:

xxprowlstartupfunction.my_prowl ("Vera Home Controller", "Testing code", "this could any description")

However, it looks like there may also be an error within your function too. luup.inet.wget() does not, AFAIK, do https protocol.

You’re trying to push notifications to Prowl? I have code that does that.

[quote=“akbooer, post:10, topic:173990”]I’d suggest that you give this a go:

xxprowlstartupfunction.my_prowl ("Vera Home Controller", "Testing code", "this could any description")

However, it looks like there may also be an error within your function too. luup.inet.wget() does not, AFAIK, do https protocol.[/quote]

Many thanks @akbooer,

The addition of the module name before the loaded function looks to have sorted it, and the luup.inet.wget() call does seem to work fine, as I received the associated notification when I ran it.

Having to use the Module name as a prefix to the function is not nice though, I had assumed my prowl function would be loaded as a global function, allowing me to then call it just by using my_prowl().

Good news.

and the luup.inet.wget() call does seem to work fine, as I received the associated notification when I ran it.

It may accept the HTTPS string, but I’d be unconvinced that it’s actually using SSL as a transport layer.

Having to use the Module name as a prefix to the function is not nice though, I had assumed my prowl function would be loaded as a global function, allowing me to then call it just by using [b]my_prowl()[/b].

You can, of course, always define it as such when you do the initial ‘require’:

require "xxprowlstartupfunction"
my_prowl = xxprowlstartupfunction.my_prowl

This is basic Lua behaviour, and probably worth reading up on at https://www.lua.org/pil/

It may accept the HTTPS string, but I’d be unconvinced that it’s actually using SSL as a transport layer.[/quote]

Humm interesting, so what would be the best call to use ?

Would I just add these lines to the my_prowl code

local socket = require('socket') local https = require('ssl.https')

Yes, there’s an https.request() call with the same syntax as for http.request()

Ok, so does that mean I replace the original luup.inet.wget () with https.request () making it…

function my_prowl (appl, event, description) local prowl_url = "https://api.prowlapp.com/publicapi/add?apikey=" .. "yourabc123code" .. "&application=" .. prowl_encode(appl) .. "&event=" .. prowl_encode(event) .. "&description=" .. prowl_encode(description) .. "&priority=-1" https.request(prowl_url)

Yes. It wraps all the secure connection socket stuff into one simple-to-use procedure.

Did you know that Vera Alerts already does prowl ?

Forever the salesmen :slight_smile:

Maybe a little … but you can do more when you build on what has been done before you …

Agreed.

But for me its more about learning Lua/Luup.

After all they say to learn programming try to find something you love that you can apply it too - and thats Vera and HA for me…

I still have your PLEG app (and love it,) but the satisfaction I get when I manage to code something completely in Lua/Luup is amazing !! :wink: