Reactor 2.0 and run Lua

Rigpapa,
i was trying to run some of my code in an Activity triggered by Reactor 2.0. It seems to error out. was trying to debug, but didn’t see the issue. perhaps a scope issue, but i can run the exact code (sans the print statements) in the test luup code window. here is the output from the logs if it helps:

50 11/20/18 19:33:36.741 luup_log:224: Reactor: Child 253 ("UTIL - Run Therms") configuration change, updating! <0x6f88b520> 06 11/20/18 19:33:36.752 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: lastacc was: 1542760398 now: 1542760416 #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:0 <0x6f88b520> 50 11/20/18 19:33:36.784 luup_log:224: Reactor: "UTIL - Run Therms" (#253) now "tripped" <0x6f88b520> 06 11/20/18 19:33:36.785 Device_Variable::m_szValue_set device: 253 service: urn:micasaverde-com:serviceId:SecuritySensor1 variable: LastTrip was: 1542760391 now: 1542760416 #hooks: 0 upnp: 0 skip: 0 v:0xdd5788/NONE duplicate:0 <0x6f88b520> 06 11/20/18 19:33:36.785 Device_Variable::m_szValue_set device: 253 service: urn:micasaverde-com:serviceId:SecuritySensor1 variable: Tripped was: 0 now: 1 #hooks: 0 upnp: 0 skip: 0 v:0xdd5660/NONE duplicate:0 <0x6f88b520> 06 11/20/18 19:33:36.786 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: TripCount was: 29 now: 30 #hooks: 0 upnp: 0 skip: 0 v:0xe650a0/NONE duplicate:0 <0x6f88b520> 06 11/20/18 19:33:36.787 Device_Variable::m_szValue_set device: 224 service: urn:toggledbits-com:serviceId:Reactor variable: runscene was: [ ] now: [ ] #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:1 <0x6f88b520> 06 11/20/18 19:33:36.789 Device_Variable::m_szValue_set device: 224 service: urn:toggledbits-com:serviceId:Reactor variable: runscene was: [ ] now: { "ctx253-sc__trip": { "starttime": 1542760416, "scene": "__trip", "taskid": "ctx253-sc__trip", "options": { "contextDevice": 253, "stopPriorScenes": true }, "owner": 253, "context": 253, "lastgroup": 0 } } #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:0 <0x6f88b520> 50 11/20/18 19:33:36.790 luup_log:224: Reactor: "UTIL - Run Therms" (253) ["scene__trip_action1"]: before comment <0x6f88b520> 50 11/20/18 19:33:36.791 luup_log:224: Reactor: "UTIL - Run Therms" (253) ["scene__trip_action1"]: start of code <0x6f88b520> 01 11/20/18 19:33:36.792 luup_log:224: Reactor: "UTIL - Run Therms" (253) aborting scene "__trip" Lua execution at group step 1, Lua failed: 1 <0x6f88b520> 02 11/20/18 19:33:36.793 luup_log:224: Reactor: Lua: print("before comment") -- RUN THE THERMOSTATS EVERY HOUR IN CASE THE WATCHERS ARE IDLE print("start of code") sebbyCode.vLog("FROM Reactor Trigger", "Kicking off THERMOSTATS from a Reactor schedule") print("sent vlog") sebbyCode.setLocation() print("ran setLocation") return true <0x6f88b520> 06 11/20/18 19:33:36.793 Device_Variable::m_szValue_set device: 224 service: urn:toggledbits-com:serviceId:Reactor variable: runscene was: { "ctx253-sc__trip": { "starttime": 1542760416, "scene": "__trip", "taskid": "ctx253-sc__trip", "options": { "contextDevice": 253, "stopPriorScenes": true }, "owner": 253, "context": 253, "lastgroup": 0 } } now: [ ] #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:0 <0x6f88b520> 06 11/20/18 19:33:36.794 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: Message was: Not tripped now: Tripped #hooks: 0 upnp: 0 skip: 0 v:0xe59fb0/NONE duplicate:0 <0x6f88b520> 06 11/20/18 19:33:36.795 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: cstate was: { "cond167327a08d5": { "evalstate": false, "statestamp": 1542760026, "stateedge": { "1": 1542740136, "0": 1542760026 }, "valuestamp": 1542760011, "id": "cond167327a08d5", "evalstamp": 1542760026, "laststate": false, "lastvalue": 1542760011 }, "lastSaved": 1542760398, "grp166b1d95e77": { "evalstate": false, "evalstamp": 1542760026, "hastimer": false, "changed": false } } now: { "cond167327a08d5": { "evalstate": true, "statestamp": 1542760416, "stateedge": { "1": 1542760416, "1": 1542740136, "0": 1542760026 }, "valuestamp": 1542760416, "id": "cond167327a08d5", "evalstamp": 1542760416, "laststate": true, "lastvalue": 1542760416 }, "lastSaved": 1542760416, "grp166b1d95e77": { "evalstate": true, "evalstamp": 1542760416, "hastimer": false, "changed": true } } #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:0 <0x6f88b520> 50 11/20/18 19:33:36.797 luup_log:224: Reactor: Child 253 ("UTIL - Run Therms") configuration change, updating! <0x6f88b520> 06 11/20/18 19:33:36.803 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: Runtime was: 7585 now: 7585 #hooks: 0 upnp: 0 skip: 0 v:0xe5a228/NONE duplicate:1 <0x6f88b520> 06 11/20/18 19:33:36.803 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: lastacc was: 1542760416 now: 1542760416 #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:1 <0x6f88b520> 50 11/20/18 19:33:36.804 luup_log:224: Reactor: "UTIL - Run Therms" (#253) now "untripped" <0x6f88b520>

[quote=“sebby, post:1, topic:200118”]Rigpapa,
i was trying to run some of my code in an Activity triggered by Reactor 2.0. It seems to error out. was trying to debug, but didn’t see the issue. perhaps a scope issue, but i can run the exact code (sans the print statements) in the test luup code window. here is the output from the logs if it helps:

50 11/20/18 19:33:36.741 luup_log:224: Reactor: Child 253 ("UTIL - Run Therms") configuration change, updating! <0x6f88b520> 06 11/20/18 19:33:36.752 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: lastacc was: 1542760398 now: 1542760416 #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:0 <0x6f88b520> 50 11/20/18 19:33:36.784 luup_log:224: Reactor: "UTIL - Run Therms" (#253) now "tripped" <0x6f88b520> 06 11/20/18 19:33:36.785 Device_Variable::m_szValue_set device: 253 service: urn:micasaverde-com:serviceId:SecuritySensor1 variable: LastTrip was: 1542760391 now: 1542760416 #hooks: 0 upnp: 0 skip: 0 v:0xdd5788/NONE duplicate:0 <0x6f88b520> 06 11/20/18 19:33:36.785 Device_Variable::m_szValue_set device: 253 service: urn:micasaverde-com:serviceId:SecuritySensor1 variable: Tripped was: 0 now: 1 #hooks: 0 upnp: 0 skip: 0 v:0xdd5660/NONE duplicate:0 <0x6f88b520> 06 11/20/18 19:33:36.786 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: TripCount was: 29 now: 30 #hooks: 0 upnp: 0 skip: 0 v:0xe650a0/NONE duplicate:0 <0x6f88b520> 06 11/20/18 19:33:36.787 Device_Variable::m_szValue_set device: 224 service: urn:toggledbits-com:serviceId:Reactor variable: runscene was: [ ] now: [ ] #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:1 <0x6f88b520> 06 11/20/18 19:33:36.789 Device_Variable::m_szValue_set device: 224 service: urn:toggledbits-com:serviceId:Reactor variable: runscene was: [ ] now: { "ctx253-sc__trip": { "starttime": 1542760416, "scene": "__trip", "taskid": "ctx253-sc__trip", "options": { "contextDevice": 253, "stopPriorScenes": true }, "owner": 253, "context": 253, "lastgroup": 0 } } #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:0 <0x6f88b520> 50 11/20/18 19:33:36.790 luup_log:224: Reactor: "UTIL - Run Therms" (253) ["scene__trip_action1"]: before comment <0x6f88b520> 50 11/20/18 19:33:36.791 luup_log:224: Reactor: "UTIL - Run Therms" (253) ["scene__trip_action1"]: start of code <0x6f88b520> 01 11/20/18 19:33:36.792 luup_log:224: Reactor: "UTIL - Run Therms" (253) aborting scene "__trip" Lua execution at group step 1, Lua failed: 1 <0x6f88b520> 02 11/20/18 19:33:36.793 luup_log:224: Reactor: Lua: print("before comment") -- RUN THE THERMOSTATS EVERY HOUR IN CASE THE WATCHERS ARE IDLE print("start of code") sebbyCode.vLog("FROM Reactor Trigger", "Kicking off THERMOSTATS from a Reactor schedule") print("sent vlog") sebbyCode.setLocation() print("ran setLocation") return true <0x6f88b520> 06 11/20/18 19:33:36.793 Device_Variable::m_szValue_set device: 224 service: urn:toggledbits-com:serviceId:Reactor variable: runscene was: { "ctx253-sc__trip": { "starttime": 1542760416, "scene": "__trip", "taskid": "ctx253-sc__trip", "options": { "contextDevice": 253, "stopPriorScenes": true }, "owner": 253, "context": 253, "lastgroup": 0 } } now: [ ] #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:0 <0x6f88b520> 06 11/20/18 19:33:36.794 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: Message was: Not tripped now: Tripped #hooks: 0 upnp: 0 skip: 0 v:0xe59fb0/NONE duplicate:0 <0x6f88b520> 06 11/20/18 19:33:36.795 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: cstate was: { "cond167327a08d5": { "evalstate": false, "statestamp": 1542760026, "stateedge": { "1": 1542740136, "0": 1542760026 }, "valuestamp": 1542760011, "id": "cond167327a08d5", "evalstamp": 1542760026, "laststate": false, "lastvalue": 1542760011 }, "lastSaved": 1542760398, "grp166b1d95e77": { "evalstate": false, "evalstamp": 1542760026, "hastimer": false, "changed": false } } now: { "cond167327a08d5": { "evalstate": true, "statestamp": 1542760416, "stateedge": { "1": 1542760416, "1": 1542740136, "0": 1542760026 }, "valuestamp": 1542760416, "id": "cond167327a08d5", "evalstamp": 1542760416, "laststate": true, "lastvalue": 1542760416 }, "lastSaved": 1542760416, "grp166b1d95e77": { "evalstate": true, "evalstamp": 1542760416, "hastimer": false, "changed": true } } #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:0 <0x6f88b520> 50 11/20/18 19:33:36.797 luup_log:224: Reactor: Child 253 ("UTIL - Run Therms") configuration change, updating! <0x6f88b520> 06 11/20/18 19:33:36.803 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: Runtime was: 7585 now: 7585 #hooks: 0 upnp: 0 skip: 0 v:0xe5a228/NONE duplicate:1 <0x6f88b520> 06 11/20/18 19:33:36.803 Device_Variable::m_szValue_set device: 253 service: urn:toggledbits-com:serviceId:ReactorSensor variable: lastacc was: 1542760416 now: 1542760416 #hooks: 0 upnp: 0 skip: 0 v:(nil)/NONE duplicate:1 <0x6f88b520> 50 11/20/18 19:33:36.804 luup_log:224: Reactor: "UTIL - Run Therms" (#253) now "untripped" <0x6f88b520>[/quote]

Just off the cuff, if I had to guess, because Luup is running the plugin in its own environment, your “sebbyCode” global isn’t exposed through. When it’s Luup itself running code (startup, scenes, test lua), it gets a choice on environment, but Reactor has to run in whatever environment Luup gives it. Let me play around and see what might be done.

OK, so I’ve confirmed this. Luup runs the plugin in its own environment that isn’t shared with the “startup Lua”. Modules loaded in the startup Lua, and globals created there, are not accessible to the plugin. I cannot break out of that jail (that’s actually a Good Thing).

However, you can load your module in your runLua code. The next version of the beta will offer some enhancement to the runLua environment, and it will share loaded modules among the runLua fragments (one copy of the module for the entire Reactor ecosystem), so a module isn’t loaded once for every Lua fragment. That’s as economical on memory as I can be given the limitation that I can’t get to the already-loaded module in your Startup Lua.

Caveat though: the “one time” initializations the module performs are one time per load of the module, meaning that if you use the module in your Startup Lua, and you also use it in a number of Reactor runLua fragments, the initializations will be performed twice, once by the load at Startup, and once again when the first runLua fragment calls for it. For this reason, you may want to segment your module into two or more that separate generic functionality from actual work.

The next beta version will also have additional helper features to put more diagnostic messages in the ReactorSensor’s event log (visible in the Logic Summary), to help you more easily debug scripts.

Thanks Rigpapa, i will test loading the module in my runLua. need to do some cleanup there, but will keep you posted.

Happy Thanksgiving!

Very good. New release package here: Release beta2.0-18112201 · toggledbits/Reactor · GitHub

This version creates a unified Lua environment within Reactor, so that a loaded module used my multiple scripts is only loaded into memory once. See the beta2.0 sticky post announcement for other details.

Happy Thanksgiving to you as well!

Rigpapa,
question about loading the module, do i need to load it in any instance where i do runLua, or can I load it into just one (i.e. a reactor that just kicks off whenever luup is reloaded) and then it can be available to all my other reactors? Reason i ask is because i can get it to work if i call it every time, but not if it’s just loaded once. Running on the latest beta just in case that helps.

The answer depends more on where you store the module reference than the loading of the module itself. The environment created by Reactor keeps the module loaded across all ReactorSensors and their Lua actions, But…

…if you declare your module like this:

local mymodule = require "SebbyCode"

…then because of the [tt]local[/tt] declaration, the variable [tt]mymodule[/tt] is only available within the scope of the Lua block in which it is declared (i.e. that particular Lua script, or the block within it if inside a block like if…then…end, for…end, function definition, etc.). If you have a second runLua action in the same Reactor or any other, the variable [tt]mymodule[/tt] will not exist there, because it is out of scope. However, if you [tt]require[/tt] the library again, Lua does not reload the library, it just returns a reference to the already-loaded copy of the library.

Now, you might think you can be clever and use a global variable like this:

myGlobalModule = require "SebbyCode" -- no "local" will create a global variable

…then any runLua action that follows will have access to the module via that global variable. However, this practice is something I will discourage for several reasons. First and foremost, any reload of Luup will dump the environment and force a new one to be rebuilt, so the existence of the global variable depends heavily on the order in which runLua actions are executed. Even if you create a ReactorSensor conditioned on Luup restart, there is no guarantee that Reactor will run that ReactorSensor’s actions before those of any other ReactorSensors. Another reason not to do it is that I will not guarantee that global variables will be available across all runLua actions–it’s not a declared feature, and in fact, I view the creation of global variables as an error (more on that below). At the moment, they are available across scripts, as a side-effect of the implementation as currently written, but again, that’s not an official feature, it’s a side-effect. Key rule for every programmer: don’t rely on undocumented side-effects.

The runLua environment in ReactorSensors actually has additional code to warn you when you are creating a global, or referencing one that doesn’t exist (which means you are trying to access an undeclared variable). This is part of my “soft enforcement” of the notion that globals are to be avoided. These messages appear in the Vera log file (at warning level) and the ReactorSensor’s event log (displayed in the Logic Summary).

So, the upshot is this: you should “require” your module in every runLua script that needs to use that module, and store the module reference Lua gives you to it in a local variable. That’s good style and makes your code fragments more portable, and not dependent on magic happening elsewhere. Just because you are storing it in a local variable, doesn’t mean it isn’t being shared–once it’s loaded, Lua’s [tt]require()[/tt] is giving you back a reference to the one and only previously loaded library, not reloading it, so it’s just as efficient as keeping it in a global variable, without the problems global variables will create.

Thank you, great explanation!

I apologize in advance for a likely newbie question here. Just learning Vera, Lua, and Reactor (which by the way is an amazing plugin!)

I’d like to create a set of Lua functions (in a module or set of modules) that can be reused across any Reactor device created in Vera. For example, such modules might access a non-Vera device or computer, create remote log files, etc.

What are the mechanics and syntax of defining and using a Lua module so it can be (re-)used across Reactor Devices?

Specifically,

  • syntax
    What is your recommended approach to define and then use Lua modules? (Lua supports so many forms, and I hope to avoid unnecessary reloads.)
  • mechanics
    Can Lua module code be defined in a single Reactor Device? If so, does it matter which device? And where (under Expressions or Activity, or either)? Or does it need to be in a file? If so, what directory should it be placed?
  • Any examples?

Thanks,
Ernie

I think the most common form at the moment is to do the following:

  1. Place your Lua module in a file called L_modulename.lua. I’ll use L_ErnieF.lua for a consistent example.
  2. At the start of that file, the first executable statement should be:
    module("L_ErnieF", package.seeall)
    There are other forms for creating a module, but this is very common for Lua 5.1.
  3. Your functions follow. Functions that will be called from outside the module should not be declared with the local keyword, just plain function myFirstFunction( ...etc.. ). If the function is a utility function used only within the module, you may (probably should) precede the function declaration with the local keyword (it will thus not be visible outside the module).
  4. Module-specific data should also be declared local, but if you have data (like a table containing specific device numbers) you declare it without local and it will be accessible from outside the module.

In your Run Lua actions, you can then load the module and call functions like this:

-- Load the module. You only need to do this at the start of the code
-- block.
local L_ErnieF = require "L_ErnieF"

-- Call a function.
L_ErnieF.myFirstFunction(argument, argument, etc...)

-- Iterate over a table (as array) of device numbers you created in the module
for n,device in ipairs( L_ErnieF.LivingRoomLights ) do
    -- etc
end

You can use print() statements in Run Lua actions and they will log both to the Vera LuaUPnP log and the ReactorSensor’s Logic Summary “Events” section, which is intended to speed debugging of Lua in the Reactor environment. There’s more in the Wiki, here.

Rigpapa – Thanks for your response. I was able to get it to work!

The final missing step for me was using the “Apps->Develop Apps->Luup files” menu item to load the “L_ErnieF.lua” file into the system. Very nice and easy to use.

Is it possible to delete this uploaded file (“L_ErnieF.lua”) thru the Vera user interface? (I could probably ssh and remove it, but I don’t know the system well enough to try such a drastic approach.)

A couple of random thoughts:

  • I verified that a Lua file uploaded thru this interface has scope for the entire system … all devices, scenes, and plugins (even plugins running in their own sandbox).
  • Using a module is definitely a good way to avoid inadvertent name space conflicts.
  • I am not sure what happens if I screw up this file with some syntax errors and how that is reported. I am hesitant to play with too many error conditions for fear I’ll brick my system until I have a spare.

Thanks for your help and all the excellent documentation you have provided on this site and github. A donation for your efforts will soon be coming from me. You deserve it as I would have probably deep-six’ed my VeraEdge without Reactor and being stuck with the default limited scene creation. Now I am about ready to see my Ezlo Atom toy can do much without something like Reactor.

Yes, this is one way, very common. Another is to execute the following in the “Test Luup Code” tool: os.execute( "rm /etc/cmh-ludl/L_ErnieF.lua.lzo" )

Note the added “.lzo” suffix–the uploader automatically compresses files, so you need to give the name of the compressed file. In contrast, if you use scp to copy the file to the Vera, it won’t be compressed (but it will run fine).

Hopefully the Atom gets plugins some day, and if it does, I will definitely be working at getting Reactor there.