What’s the correct way to establish the current device in a call_timer or call_delay callback? Vera Luup sets luup.device, but this seems to be absent from the luup table in openLuup.
Do you mean lul_device?
Hmmm. No, I don’t think so. [tt]lul_device[/tt] is, to my recollection, set in Action context for the run/job entries in the implementation file, but it’s not in context for callbacks for [tt]call_delay[/tt] and [tt]call_timer[/tt].
In Vera Luup, [tt]luup.device[/tt] is set pretty much everywhere, as you know, so it’s available in [tt]call_delay[/tt] and [tt]call_timer[/tt] callbacks with the same device number as the device that scheduled the callback.
In openLuup, what I’m seeing is that [tt]luup.device[/tt] is not set in any context, but particularly in my case, there seems to be no way in the timer callbacks to get the current device unless I arrange to pass it in as part of the single argument that the callbacks support. That’s very different from Vera Luup.
I wrote some test code, and openLuup does not set [tt]lul_device[/tt] in timer callback context either.
At the time you call luup.call_delay, or luup.call_timer you decide the context for the callback (you provide this value in the arguments of these calls).
You can pass the device if you wish. You may need to pass a table of parameters if you need to pass back more than 1 piece of information. In this case you are passing a table and you have to unpack the other data from the table.
Richard, yes, yes, I know this, although it’s not clear that passing anything other than a string is allowed. At least, according to Vera’s documentation: “and will be passed the string data.” So to be in keeping with this, I could not pass a table directly, I would have to package a string that encodes all of the parameters I want to pass, and include the device number. That’s fine, and in fact, it’s exactly how I’ve worked around this issue already. I would consider it sketchy to rely on passing anything other than a string, since it’s not the way Vera documents the function. If this was a more perfect world, Vera would have passed [tt]lul_device[/tt] to the timer callbacks explicitly, and allowed more and any type of parameters to those callbacks. Alas, we’ve got what we’ve got.
What I’m really after here is pointing out that there is a missing, documented feature in the implementation of openLuup. In section 1.1 of the “Luup Lua extensions” documentation, it describes luup.device, which is set in context for all plugin code. openLuup does not set this variable. While actions have the lul_device parameter available from the wrapper, the delay callbacks do not.
I’ve looked at akbooer’s scheduler, and it seems that the fix to implement this is simple. I did it in two lines in my own openLuup installation. Here’s the code snippet of the change to context_switch in scheduler.lua:
[tt]local function context_switch (devNo, fct, …)
local old = current_device – save current device context
current_device = devNo or old
luup.device = current_device
local function restore (ok, msg, …)
current_device = old – restore old device context
luup.device = old
if not ok then
_log (" ERROR: " … (msg or ‘?’), “openLuup.context_switch”)
end
return ok, msg, …
end
return restore (pcall (fct, …))
end
[/tt]
I can’t find much reference to this coming up before, but for most plugins, this would not be an issue and is probably why it hasn’t come up. The code for many plugins simply stores the device number passed to whatever startup function in a module-local variable, and that works for them because they are single-instance device (only one device per plugin). Some multi-device plugins don’t need to store the device number because the only operations they perform are actions (like VirtualSwitch), and lul_device is passed in to those every time. But for multi-instance plugins that have background tasks (that is, use call_delay and call_timer to schedule background work), storing the device number on the module isn’t workable, because one copy of the module is shared by all of the device instances, so each device would overwrite that of the others.
So, yes, we can change our plugin code to pass more context into call_delay/call_timer. That is surely a solution and it works. I’m merely pointing out that there is an important, defined (as much as we have “specs”) luup table variable missing that many plugins probably rely on, and I think openLuup would be improved (greater compatibility with Vera Luup) by implementing it, it seems trivial to do it, and many more plugins would probably work without modification, because they probably rely on it.
Don’t recommend that you play with the scheduler.
My timer and variable-watch callback tests suggest that luup.device is, indeed, set and available. It writes them out into the logs. lul_device is also in scope in the device code.
luup.device, itself is defined in the local context of the device at the time of loading the code.
Really not sure why you’re seeing anything different.
A snippet of test code:
function test_delay_callback (x)
luup.call_delay ("test_delay_callback", x+x, x+x)
luup.variable_set ("AKB_service","lastDelay", x, myDevNo)
luup.log (("delay callback environment: _NAME=%s, luup.device=%s, delay=%s"): format (tostring(_NAME),
tostring(luup.device), os.date ("%M:%S", tonumber(x))))
end
and the log…
2017-08-13 15:20:27.746 luup_log:377: delay callback environment: _NAME=[377] I_Test.xml, luup.device=377, delay=00:15
...
2017-08-13 15:20:58.118 luup_log:377: delay callback environment: _NAME=[377] I_Test.xml, luup.device=377, delay=00:30
...
2017-08-13 15:21:58.505 luup_log:377: delay callback environment: _NAME=[377] I_Test.xml, luup.device=377, delay=01:00
...
2017-08-13 15:23:59.026 luup_log:377: delay callback environment: _NAME=[377] I_Test.xml, luup.device=377, delay=02:00
akbooer, I think I’m beginning to see some daylight here. We may be looking at two different scopes.
I am executing code in a module that is loaded by but outside of the and section. Your loader.lua puts both luup.device and lul_device in scope when it compiles those fragments, but that scoping does not work for loaded modules – stay with me on this… here’s a vastly cut-down version of what I’m doing:
In I_SiteSensor1.xml, I have…
[tt] …
function startupSiteSensor(dev)
– luup.device and lul_device are available here, and dev is also passed in as an argument, so all good.
luup.log(“startupSiteSensor(): luup.device=” … tostring(luup.device) … “, lul_device=” … tostring(lul_device))
SiteSensor = require(“L_SiteSensor1”)
siteSensorRunQuery = SiteSensor.runQuery – make a global reference to this function so call_delay can see it
SiteSensor.init(dev)
end
startupSiteSensor
…
[/tt]
In L_SiteSensor1.lua…
[tt]module(“L_SiteSensor1”, package.seeall)
luup.log(“during module load: luup.device=” … tostring(luup.device) … “, lul_device=” … tostring(lul_device))
– This is the timer callback function
function runQuery( param )
luup.log(“runQuery(): luup.device=” … tostring(luup.device) … “, lul_device=” … tostring(lul_device))
end
function init(dev)
luup.log(“init(): luup.device=” … tostring(luup.device) … “,lul_device=” … tostring(lul_device))
call_delay( “siteSensorRunQuery”, 60, “mystuff” )
end
[/tt]
Vera Luup logs this:
[tt]50 08/13/17 11:45:55.714 luup_log:503: startupSiteSensor(): luup.device=503, lul_device=nil <0x2bec1680>
50 08/13/17 11:45:55.772 luup_log:503: module level luup.device=503, lul_device=nil <0x2bec1680>
50 08/13/17 11:45:55.992 luup_log:503: init(): luup.device=503,lul_device=nil <0x2bec1680>
50 08/13/17 11:47:41.101 luup_log:503: runQuery(): luup.device=503, lul_device=nil <0x2ecc1680>
[/tt]
but openLuup logs this:
[tt]2017-08-13 11:23:16.480 luup_log:7: startupSiteSensor(): luup.device=7, lul_device=7
2017-08-13 11:23:16.480 luup_log:7: module level luup.device=nil, lul_device=nil
2017-08-13 11:23:16.481 luup_log:7: init() luup.device=nil, lul_device=nil
2017-08-13 11:24:16.495 luup_log:7: runQuery() luup.device=nil, lul_device=nil
[/tt]
So you can see, right from the start during the module load, Lua isn’t carrying the scoping you are attempting in loader.lua into the loading of the module. I think therein lies the issue and the difference in your observation vs mine.
You can tweek each implementation … but it’s really going down the wrong path …
Global variables have always been a bad design solution. Emulating bad behavior does not make it any better.
It’s interesting that in your init function you try to print two versions of poor usage of global variables for the device, when it’s already passed explicitly to the function.
It's interesting that in your init function you try to print two versions of poor usage of global variables for the device, when it's already passed explicitly to the function.
Richard, nothing interesting there. Just printing them to show that they are not in scope at that point. Any function I have that’s passed a device number explicitly always uses what’s passed, and ignores the global variables.
In any case, this all can be made to work, specifically because the correct device number is passed in, and I can further pass it around where it needs to go. I’m just pointing out that there is a scoping issue here in openLuup that is very different from Vera Luup. Whether or not it’s worth looking into is up to akbooer. His code, his party.
And you are correct that the whole idea of using global variables is bad design. But, Vera sometimes forces, sometimes goads, us into doing things we’d rather not, which is why openLuup exists at all, I think.
Ah. If only you had said that at the start. I could have told you that loaded modules don’t share the same environment (although files loaded with do.) There was simply no way - aside from overloading the require function which I wasn’t prepared to do. There are indeed (I believe) very few plugins which require access to the global environment from within required modules.
Your earlier fix to the scheduler may work for that one specific variable, but I need to check further - I have been too quick in the past to agree to a change, only to have to undo it later for very good reasons. It’s certainly not a global panacea (sorry about the pun.)
Richard is right to some extent - there are so many oddities in Vera that it’s a bit of a lost cause to emulate bad design features. I walk that line all the time with Vera and openLuup (and with Richard - we agree, I think, to disagree on the design of Vera event triggers!)
akbooer, completely and totally understood. Since you’ve done such a fine job on cloning Luup, maybe you design and build something from scratch, addressing all Vera’s mistakes? You know, in your free time… ![]()