Author Topic: Current device in delay/timer callback?  (Read 242 times)

Offline rigpapa

  • Full Member
  • ***
  • Posts: 129
  • Karma: +19/-0
Current device in delay/timer callback?
« on: August 12, 2017, 04:41:10 pm »
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.
Author of Rachio, Deus Ex Machina II, SiteSensor, and Auto Virtual Thermostat plugins. Using Vera Plus (1.7.3232), Vera3 (1.7.855), 50 dimmers and 40 switches (mostly Leviton, some Linear and GE), a dozen sensors, a truckload of PLEG, and of course, my own plugins.

Offline akbooer

  • Moderator
  • Master Member
  • *****
  • Posts: 5184
  • Karma: +223/-67
  • "Less is more"
Re: Current device in delay/timer callback?
« Reply #1 on: August 12, 2017, 04:56:01 pm »
Do you mean lul_device?

3x Vera Lite-UI5/Edge-UI7, 25x Fibaro, 23x TKB, 9x MiniMote, 2x NorthQ Power, 2x Netatmo, 1x Foscam FI9831P.
Razberry, MySensors Arduino, HomeWave, AltUI, DataYours, openLuup, ZWay, ZeroBrane Studio.

Offline rigpapa

  • Full Member
  • ***
  • Posts: 129
  • Karma: +19/-0
Re: Current device in delay/timer callback?
« Reply #2 on: August 12, 2017, 10:10:15 pm »
Hmmm. No, I don't think so. lul_device 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 call_delay and call_timer.

In Vera Luup, luup.device is set pretty much everywhere, as you know, so it's available in call_delay and call_timer callbacks with the same device number as the device that scheduled the callback.

In openLuup, what I'm seeing is that luup.device 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 lul_device in timer callback context either.

Author of Rachio, Deus Ex Machina II, SiteSensor, and Auto Virtual Thermostat plugins. Using Vera Plus (1.7.3232), Vera3 (1.7.855), 50 dimmers and 40 switches (mostly Leviton, some Linear and GE), a dozen sensors, a truckload of PLEG, and of course, my own plugins.

Offline RichardTSchaefer

  • Master Member
  • *******
  • Posts: 9630
  • Karma: +733/-136
    • RTS Services Plugins
Re: Current device in delay/timer callback?
« Reply #3 on: August 13, 2017, 08:52:10 am »
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.


Offline rigpapa

  • Full Member
  • ***
  • Posts: 129
  • Karma: +19/-0
Re: Current device in delay/timer callback?
« Reply #4 on: August 13, 2017, 09:41:38 am »
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 lul_device 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:

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


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.
« Last Edit: August 13, 2017, 09:53:03 am by rigpapa »
Author of Rachio, Deus Ex Machina II, SiteSensor, and Auto Virtual Thermostat plugins. Using Vera Plus (1.7.3232), Vera3 (1.7.855), 50 dimmers and 40 switches (mostly Leviton, some Linear and GE), a dozen sensors, a truckload of PLEG, and of course, my own plugins.

Offline akbooer

  • Moderator
  • Master Member
  • *****
  • Posts: 5184
  • Karma: +223/-67
  • "Less is more"
Re: Current device in delay/timer callback?
« Reply #5 on: August 13, 2017, 10:32:41 am »
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:

Code: [Select]
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...
Code: [Select]
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
3x Vera Lite-UI5/Edge-UI7, 25x Fibaro, 23x TKB, 9x MiniMote, 2x NorthQ Power, 2x Netatmo, 1x Foscam FI9831P.
Razberry, MySensors Arduino, HomeWave, AltUI, DataYours, openLuup, ZWay, ZeroBrane Studio.

Offline rigpapa

  • Full Member
  • ***
  • Posts: 129
  • Karma: +19/-0
Re: Current device in delay/timer callback?
« Reply #6 on: August 13, 2017, 11:51:09 am »
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 <actions> and <functions> 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...

    ...
    <functions>
        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
    <function>
    <startup>startupSiteSensor</startup>
    ...

In L_SiteSensor1.lua...

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


Vera Luup logs this:

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>


but openLuup logs this:

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


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.
Author of Rachio, Deus Ex Machina II, SiteSensor, and Auto Virtual Thermostat plugins. Using Vera Plus (1.7.3232), Vera3 (1.7.855), 50 dimmers and 40 switches (mostly Leviton, some Linear and GE), a dozen sensors, a truckload of PLEG, and of course, my own plugins.

Offline RichardTSchaefer

  • Master Member
  • *******
  • Posts: 9630
  • Karma: +733/-136
    • RTS Services Plugins
Re: Current device in delay/timer callback?
« Reply #7 on: August 13, 2017, 01:53:51 pm »
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.


Offline rigpapa

  • Full Member
  • ***
  • Posts: 129
  • Karma: +19/-0
Re: Current device in delay/timer callback?
« Reply #8 on: August 13, 2017, 02:11:13 pm »
Quote
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.
Author of Rachio, Deus Ex Machina II, SiteSensor, and Auto Virtual Thermostat plugins. Using Vera Plus (1.7.3232), Vera3 (1.7.855), 50 dimmers and 40 switches (mostly Leviton, some Linear and GE), a dozen sensors, a truckload of PLEG, and of course, my own plugins.

Offline akbooer

  • Moderator
  • Master Member
  • *****
  • Posts: 5184
  • Karma: +223/-67
  • "Less is more"
Re: Current device in delay/timer callback?
« Reply #9 on: August 13, 2017, 05:22:44 pm »
I am executing code in a module that is loaded by but outside of the <actions> and <functions> 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...

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 <files> 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.)

You can tweek each implementation ... but it's really going down the wrong path ...

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!)

 

3x Vera Lite-UI5/Edge-UI7, 25x Fibaro, 23x TKB, 9x MiniMote, 2x NorthQ Power, 2x Netatmo, 1x Foscam FI9831P.
Razberry, MySensors Arduino, HomeWave, AltUI, DataYours, openLuup, ZWay, ZeroBrane Studio.

Offline rigpapa

  • Full Member
  • ***
  • Posts: 129
  • Karma: +19/-0
Re: Current device in delay/timer callback?
« Reply #10 on: August 13, 2017, 07:18:32 pm »
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...  ;)
Author of Rachio, Deus Ex Machina II, SiteSensor, and Auto Virtual Thermostat plugins. Using Vera Plus (1.7.3232), Vera3 (1.7.855), 50 dimmers and 40 switches (mostly Leviton, some Linear and GE), a dozen sensors, a truckload of PLEG, and of course, my own plugins.