determine if running on openLuup and/or detecting json decoder

I have a plugin that needs a json decoder. If it is running on openLuup, it should use the openLuup.json. What is the best way to determine that it has that module? Should it detect it is running on openLuup and assume that it is available? Should it run the require(“openLuup.json”) and see if the result is not nil?

openLuup has a system-level attribute called… [tt]openLuup[/tt]!

Test it like this:

if luup.attr_get "openLuup" then
  -- you are running openLuup
else
  -- you are not
end

Alternatively, the dkjson module is available on both systems.

Is dkjson always there on Vera? I thought that UI5 didn’t have it by default? Could be wrong on that.

I was mulling over something like this:

[code]local json

local function prequire(m)
local ok, err = pcall(require, m)
if not ok then return nil, err end
return err
end
[/code]

Then, in startup:

json = prequire("dkjson") if not json then return false, "dkjson not found, please load it manually", "MyPlugin" en

Instead of failing on only dkjson, I could then look for openLuup.json if on openLuup.

Or is this all a waste of code since dkjson is always present?

Edit: code below doesn’t work as expected. In general I do something similar to your code.

You can check for as many parsers as you can think of and noting that that latest Vera firmwares provide dkjson.lua

local PLUGIN_NAME = 'My plugin'
local jsonLib= require('json')

-- Misc functions here

function luaStartUp(lul_device)
    if not jsonLib then
        debug('JSON library not found',1)
        return false, 'JSON library not found', PLUGIN_NAME
    end

-- More stuff

    return true, 'All OK', PLUGIN_NAME
end

Out of interest AltUI → Misc → OsCommand -->Find JSON tab shows the following on my openLuup:

/home/pi/vera/cmh-ludl/dkjson.lua /home/pi/vera/cmh-ludl/L_ALTUIjson.lua /home/pi/vera/cmh-ludl/openLuup/json.lua /home/pi/vera/cmh-ludl/files/json-dm2.lua /home/pi/vera/cmh-ludl/files/L_ALTUIjson.lua /home/pi/vera/cmh-ludl/files/dropbox_json_parser.lua /home/pi/vera/cmh-ludl/files/json.lua

and on a Vera 3 with UI7:

/mios/usr/lib/lua/dkjson.lua /overlay/usr/lib/lua/json.lua /overlay/usr/lib/lua/json-dm2.lua /overlay/usr/lib/lua/dkjson.lua /usr/lib/lua/json.lua /usr/lib/lua/json-dm2.lua /usr/lib/lua/dkjson.lua

Yes, the proliferation of JSON modules was something that Vera brought on themselves, since UI5 and earlier versions didn’t supply anything. So… individual plugin developers had to provide their own.

My plugins did the same, but aside from openLuup, all my plugins have their JSON modules wrapped in others, so EventWatcher, Netatmo, DataYours, all have them too. You probably have more than you think!

I obviously have my favourite, and you’ll find that openLuup.json module pretty-prints its output, sorts table tags, detects circular structures, …

At least on a UI5 VeraLite, the ‘require’ function is overloaded by ‘luup_require’. Does anyone have any documentation for it? As far as I can tell it returns either the error string on failure or the module when it is successful. So now I test for a table return and assume that means it found the module.

Does UI7 behave the same way? Maybe not because I can see the example said ‘if not jsonLib’ but that will always be false on the UI5 - it always seems to return a value, never nil.

The 5.1 Lua reference manual says: :If there is any error loading or running the module, or if it cannot find any loader for the module, then require signals an error." That doesn’t say how it signals an error. Is that obvious or am I missing something? Maybe the luup_require is doing the pcall internally to catch the error and then helpfully passing the error as the return value?

Yes, Vera overloads [tt]require[/tt], I think mostly to deal with compressed and encoded modules. I don’t know of any documentation (as usual.)

In the process they have lost some of the original functionality, including the ability to handle the more modern approach to defining modules (returning a table rather than using the module statement.)

Edit: modified code below, so it actually works on Vera and openLuup - thanks akbooer.

OK looks like I’ve been doing this wrong. ‘require’ does not return nil if it can’t find the required file. It returns error information. So new approach below, uses ‘package.loaded’. package.loaded[‘my_json_file’] is nil, if the required file is not loaded. The function below will return the first parser found in the array. I’ve listed the ones commonly in use by Vera/openLuup - there may be others that people use? Depending on the application, there is a chance that some modules may work, while others may not. I’ve listed them in order of likely existence and functionality and the first one found will be loaded in that order.

[code]local function loadJsonModule()
local jsonModules = {
‘openLuup.json’, – http://forum.micasaverde.com/index.php?topic=29989.0
‘akb-json’, – http://forum.micasaverde.com/index.php?topic=29989.0
‘dkjson’, – UI7 firmware
‘json’, – OWServer plugin
‘json-dm2’, – dataMine plugin
‘dropbox_json_parser’ – dropbox plugin
}

local ptr  = nil
local json = nil
for n = 1, #jsonModules do
    -- require does not load the module, if it's already loaded
    -- Vera has overloaded require to suit their requirements, so it works differently from openLuup
    -- openLuup:
    --    ok:     returns true or false indicating if the module was loaded successfully or not
    --    result: contains the ptr to the module or an error string showing the path(s) searched for the module
    -- Vera:
    --    ok:     returns true or false indicating the require function executed but require may have or may not have loaded the module
    --    result: contains the ptr to the module or an error string showing the path(s) searched for the module
    local ok, result = pcall(require, jsonModules[n])
    ptr = package.loaded[jsonModules[n]]
    if (ptr) then
        json = ptr
        print('Using: '..jsonModules[n])
        break
    end
end
if (not json) then print('No JSON library found') return json end
return json

end

local json = loadJsonModule()

return true

[/code]

Edit: code above fixed, so that the following is no longer an issue:

On a separate issue, if I make ‘SOME_PARSER’ the first entry in the array in the code above: Then it will not run in the AltUI code test box on openLuup but will in the AltUI code test box on a Vera3. Not sure why this is?

local jsonModules = { 'SOME_PARSER', 'openLuup.json', -- http://forum.micasaverde.com/index.php?topic=29989.0 'akb-json', -- http://forum.micasaverde.com/index.php?topic=29989.0 'dkjson', -- UI7 'json', -- ? 'json-dm2', -- dataMine 'dropbox_json_parser' -- dropbox plugin }

If you look at the log you may see a long error message which says that the module is not found. This is not anything that openLuup has done wrong, but must be, on Vera, a side-effect of their modified ‘require’.

You should, in your code, simply wrap the require call in a pcall, to catch and ignore the error.

@akbooer - thanks for the info. Instead of going around in circles, I’ve now modified the code above so it works. As @jswim788 points out, one could just as easily test to see if the variable ‘result’ was a table or not, rather than using ‘package.loaded’.

Looks like any plugin that can’t find it’s json file on openLuup will just die, rather than reporting a missing module in the UI. Doesn’t matter a whole lot as the console log will pick it up.

Thanks @a-lurker - your code will be very useful.

Extending this a bit with another question: I’ve noticed that some plugin writers seem to unload these modules shortly after using them. Is that to save memory space? They are trading some execution speed for memory space? I tend to think that going out to the file system is expensive, but maybe not if you are in a memory starved environment. On the other hand, peak memory usage will still be the same, so if worst case hits, there is no advantage to doing the unload. But maybe that is very rare, so it is worth unloading?

I can’t see that removing a package is, in general, a good or necessary thing. Frankly, Lua code takes up so little space that it hardly matters. On a resource-constrained Vera, the limited disc space is an issue which this wouldn’t address. The fact that Vera crashes apparently from surges in memory use and lack of available RAM, won’t be significantly improved by this either.

Hi Akbooer,

I am trying to determine if I’m running on openLuup or not. First tested on one Vera using ALTUI. Works as described.

Now I try on a VeraLite, latest version and luup.attr_get(“openLuup”) returns a table!?! When using luup.attr_get(“openLuup”,0) it does seem to work.

Cheers Rene

That’s really worrying… what’s in the table?

I have not looked. Let me try tonight. It might be that the call without device number (zero in this case) produces unpredictable results on a Vera.

Cheers Rene

I’m very surprised, because attributes in Vera are only numbers or strings. You’re sure you weren’t on openLuup by accident?!

None of my Veras (two on UI5, one on UI7, but not the latest) do this.

Hi akbooer,

No, not on openLuup. I first tested the code on a VeraEdge that has ALTUI as that makes testing snippets of code simpler in the LUA Test Code window. There it worked without specifying a device number as second parameter. Also tested it on openLuup the same way, also working. Then I put it in a plugin running on a VeraLite that does not have much on it as I use it as development box. There the code was returning a value for luup.attr_get(“openLuup”).

I just tried in the VeraLite LUA Test window and return (luup.attr_get("openLuup") == nil) returns true as expected, so I am puzzled too why it fails in my plugin.
This is the code used in the plugin, and without the device number 0 it returns 99 on the VeraLite:

local function _getui() if (luup.attr_get("openLuup",0) ~= nil) then return 99 else return luup.version_major end return 7 end

I just looked at the wiki and maybe this explains something:
function: attr_get
parameters: attribute (string), device (string or number)
returns: string or none (note: none means nothing at all. It does not mean ‘nil’)

Maybe I see the “nothing” and that throws off my code?

I’ve ALWAYS wondered what that “nothing” meant in the documentation. Certainly, I don’t implement it that way. I thought I tested this at the time (a long while ago) but would be loathe to change this now, though.

I’ve been been investigating this effect…

I run this test program:

print ("no device", luup.attr_get "openLuup")
print ("device 0", luup.attr_get ("openLuup",0))
print ("no device", nil == luup.attr_get "openLuup")
print ("device 0", nil == luup.attr_get ("openLuup",0))
if luup.attr_get "openLuup" then print "exists" else  print "doesn't exist" end
if luup.attr_get ("openLuup",0) then print "exists" else  print "doesn't exist" end

which gives this output:

no device
device 0
no device 	true
device 0 	true
doesn't exist
doesn't exist

for both my UI5 machines.

There is no difference between specifying device 0 and not doing so.
In each and every case, it gives the result I would expect.

BUT…

print (type(luup.attr_get "openLuup"))

gives the error

[string "ALTUI - LuaRunHandler"]:8: bad argument #1 to 'type' (value expected)

…as does the call with the device number.

HOWEVER…

print (type((luup.attr_get "openLuup")))

prints

nil

So, AMAZINGLY, the documentation is actually correct and the failed call returns NOTHING.

AFAIK, this is completely invalid Lua, and a stunning example of the crass implementation of MiOS. There simply is no native Lua representation of ‘nothing’ - function calls with no return evaluate to nil.

I still can’t explain your result, since my test code above seems to cover that case?