Using ‘require’ - with luup.variable_watch functions


I thought I’d have a go at moving more things out of the Lua start up and into dedicated files on Vera. One area I can’t seem to get working is having a luup.variable_watch calling a function within a file. Is it possible to do ?

require "xxprowlstartupfunction"
require "xxloftlightmotion"
require "xxlogrestarts"

xxprowlstartupfunction.my_prowl ("VeraPlus Home", "Luup Reload Notification", "Your VeraPlus Home Automation controller has reloaded luup")
luup.variable_watch("xxloftlightmotion.turn_remote_light_on_with_motion","urn:micasaverde-com:serviceId:SecuritySensor1", "Tripped", 213)

Because the callbacks are based by name and not as a function reference, the function has to be global. Your module functions will not work directly. You have to do something more like this:

mymodule = require "mymodule"
myModuleWatchHandler = mymodule.watch_handler -- the name of your module function that is the callback
luup.variable_watch( 'myModuleWatchHandler', ... )

Your module functions are in module scope, not global scope, so you use a temporary variable in global scope and assign it to the function reference of the module function. You pass the name of the temporary variable to the Luup function.

If they had used function references, this would have been much easier, and you could use closures (like lambdas in Python or anonymous functions in many other languages). Alas, they did not, so you have to jump through luups hoops to make it work.

Thanls @rigpapa , sadly much of that was a outside my realm of experience/understanding, but looking at your example, I think I’ve got what you mean ?

How does this look?

mymodule = require "xxloftlightmotion"
parkerc_WatchHandler = mymodule.turn_remote_light_on_with_motion
luup.variable_watch( 'parkerc_WatchHandler',"urn:micasaverde-com:serviceId:SecuritySensor1", "Tripped", 213 )

Yes, that looks right.

If you run into deadlocks in your watch handler, chime in here and I’ll give you some potential workarounds. It will depend on how much work you are doing in there, and what.

Thanks again; and just to check the opening line or that uploaded file (module) - I’ve got it as this,…

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

i’m never 100% if I have to include the file type ‘.lua’ or not ?
In either the ‘require =‘ line, or in the ‘module’ line * :thinking:

No .lua in either the require or module statements.

1 Like

Thanks @rigpapa !

1 Like

Hi @rigpapa

Does a similar logic apply if you are using a luup.call_delay within the same function within the same module/file ?

By that I mean, I have a function called ‘checkLastTrip’ stored within that same module/file - that is called within the above luup.variable_watch ‘ mymodule.turn_remote_light_on_with_motion’ function?

… and it does not seem to be being found/called?

01 10/05/20 20:55:37.100 LuaInterface::CallFunction_Timer-5 function checkLastTrip failed attempt to call a nil value <0x726a6520>

Yes, applies to all functions with callbacks (so call_timer(), call_delay(), job_watch() and variable_watch()).

Thanks so much, I did wonder… ok, so with that in mind and building on from what the did for the luup.variable_watch,

mymodule = require "xxloftlightmotion"
parkerc_WatchHandler = mymodule.turn_remote_light_on_with_motion
parkerc_WatchHandler2 = mymodule.checkLastTrip
luup.variable_watch( 'parkerc_WatchHandler',"urn:micasaverde-com:serviceId:SecuritySensor1", "Tripped", 213)
luup.call_delay ("parkerc_WatchHandler2", period) 

Can I leave the local variable ‘period’ in as above, even if that is only defined within the module/file itself?

I guess I can’t do that if it a luup.variable watch as that is invoked outside the module/file ?

Yes. Primitive types are passed by value, so the function is receiving the value from the variable. If the variable is garbage collected later because it’s out of scope, that’s no matter to the call; the work has been done (timer set up).

Not sure what you mean here.

Now I’m not sure I do either :slight_smile:

My thinking was that for a luup.variable_watch to work when called from within the Lua Start up, it would need to be fully defined otherwise it would see ‘dev’ as a nil value.

But looking at it again, I can see that the brackets/parentheses around the function, must tell it where to go to find out what ‘dev’ means.

Is that correct ?

Another QQ: when working with ‘modules’ - can you define all the variables up front that are applicable to that module’s purpose, and then have the functions created in that same module refer to them?

devFrontDoorLock  = 5
devReadDoorLock  = 10

function  TurnOnFrontDoorLight() 
  devFrontDoorLock  + devReadDoorLock

Or does each function still need to have its own variables etc. defined ?

devFrontDoorLock  = 5
devReadDoorLock  = 10

function  TurnOnFrontDoorLight() 
   devFrontDoorLock  = 5
   devReadDoorLock  = 10
   devFrontDoorLock  + devReadDoorLock

If dev isn’t in scope when the call to variable_watch() is made, it will be nil and so will not work. There’s no magic in the parentheses that make it go on a hunt for a variable with that name anywhere in the code. Every variable reference has to refer to something that’s in scope, or it’s nil.


So to confirm, to use the example variable reference of ‘dev’ within the Lua startup, I would need to define it first before any related requests are made.

Sorry for what must be an obvious question for you, I’ve just been getting myself confused by how the use of modules work

Variable scope is one of the most challenging concepts to grasp, and a big source of bugs in “loose” languages like Lua and JavaScript. Keep at it.

One thing that may help is installing Lua on some system you can just play with, where the limits of where it logs and how you access it are less stringent than Vera’s. You can write all kinds of little programs and just use print statements to see what’s happening (without resorting to a full blown IDE with a debugger), and things often become very clear quickly when you’re not fighting the rest of system just trying to see what’s happening.