Help with callbackHandler javascript and implementation files.

I am trying to set up the following scenario (very simple overview).

  1. User opens a javascript tab.
  2. Ajax.Request sent back to vera.
  3. Implementation file points to lua routine.
  4. callbackHandler returns result from lua routine to user.

This does not work as the callbackHandler returns undefined as it returns before routine has completed.
I have seen some code that looks like it handles this (CaddxNW584 plugin) but I can?t wrap my head around it.

Anyone got some very simple code that I can lever off?

Thanks

John

Hi John,

Only two of my plugins do this, and the other one (Combination Switch) is even more complicated.

I’m happy to talk you through the Caddx JavaScript if you need. Point at the line number where you start to get lost.

@futzle
Where do I start :slight_smile:
I don’t understand how the jobID gets passed around. It looks like you pass the jobID to the request then once the request has been filled the actual data is sent, with routines to handle to actual job status.

Is that correct? Not that I understand how to implement it.

Also don’t understand how the actual data is sent back but that is probably more to do with my lack of knowledge in respect to javascript and ajax implementation.

Another question is how do you test? Currently I change a little bit of code and upload it to see what happens it is a very time consuming process and was wondering if there is a quicker way specially with the javascript.

Aaargh, this is a bit out of my league.

Apologies if this post is a bit disjointed as I am not sure where to start myself.

thanks

John

John,

How about I walk you through an example in the Caddx code?

I’ll refer to line numbers of this version of the JavaScript, this version of the Implementation file and this version of the Lua.

The Zones tab shows you which partition(s) each zone is in. The plugin has to ask the alarm panel for this information; this takes a moment. When the plugin Lua code gets the response, it passes the partition information back to the JavaScript. Result: the “Info” column that you see in the screenshot.

The JavaScript function that draws the Zones tab starts at J_CaddxNX584Security.js line 170. The table cell to be filled in is created at J_CaddxNX584Security.js line 219. Lines 267-274 steps through all the zones scheduling a delayed call to the JavaScript function scanExistingZone for each zone, staggered to not overwhelm the alarm panel’s serial protocol.

scanExistingZone (J_CaddxNX584Security.js line 277) does an Ajax call, which calls action I_CaddxNX584Security.xml lines 52-61. This runs the Lua function jobZoneScan at L_CaddxNX584Security.lua lines 1433 onwards. Most of the function is specific to the Caddx alarm panel; the important part is the return statement at L_CaddxNX584Security.lua line 1489. Returning 5 is the MiOS way of saying “ask me later”. What is immediately returned to the JavaScript is a Vera-generated job Id. This is collected by J_CaddxNX584Security.js line 290.

Now the Lua code waits for the alarm panel to respond with the zone’s partition information. This might take a while. While this is happening, the JavaScript code is in a recursive loop (waitForScanExistingZoneJob, J_CaddxNX584Security.js lines 304-332) asking the Lua code if there has been a response yet. As long as the Lua code keeps returning 5 (“Ask me later”) the function keeps recursing.

Eventually the alarm panel returns with the zone’s partition information, which is remembered at L_CaddxNX584Security.lua lines 1462-1465. The return value from the Lua code (line 1466) is now 4 (“Job completed successfully”). Now the partition information is known to the Lua code, and the JavaScript code can request it. The infinite recursion is broken out of at J_CaddxNX584Security.js line 326, calling getScanExistingZoneResult at line 334.

In getScanExistingZoneResult is another Ajax call, this time to the registered Luup callback lr_ZoneScan (registered at L_CaddxNX584Security.lua line 328). The request is picked up by L_CaddxNX584Security.lua lines 1410-1412. The Lua code there constructs a string that is valid JSON and returns it. This string contains the partition(s) that the zone is in. The JavaScript code collects this at J_CaddxNX584Security.js line 346, and sets the HTML on the web page (J_CaddxNX584Security.js line 355). Having done its job, the function exits.

As you can see, this is all quite roundabout, and only the last stage is actually a proper Luup registered callback. It has been the only way that I ever found to get information from the Lua code back to the JavaScript code without going through Luup variables. Even then there are limitations: if you have more than one device running the same plugin, then it’s not clear which device’s Lua context will receive the requests. That’s not a problem for the Caddx alarm plugin?most people have only one alarm system?but it required extra fiddling for the Combination Switch plugin.

Don’t spend too much effort trying to figure out the Lua in jobZoneScan(). It is complex mostly because of the fact that it has to reconstruct messages from the alarm panel interface one byte at a time. If you don’t have that problem in your plugin then in really is a case of just deciding when to return 5 or 4 from your Lua: 5 until you have the result, then 4.

Edit: it occurs to me that your original post may not even need a full-fledged asynchronous job. If your Lua code can get its answer quickly, it can simply return the answer as a JSON-formatted string. Make the action rather than . I’ve never done this but I think it works.

@futzle
Thank you very much for your explanation.

It is going to take me a while to digest and work through so though I would say that up front as I will probably disappear for awhile trying to figure this out.

Thanks again

John

@futzle

Going to show my ignorance here but what is the significance of lr_ before callback identifiers in the java script:-

javascript: -

id: "lr_ZoneScan"

lua code: -

luup.register_handler("callbackHandler", "ZoneScan")

regards

John

It seems that the Luup engine prepends “lr_” for you: http://wiki.micasaverde.com/index.php/Luup_Lua_extensions#function:_register_handler

@futzle
Oops did miss that.

Thanks

@futzle
Yes it does require a job. I had the basics working but have given up as I would somehow have to lever the job status into the existing code which I am finding rather difficult so I am putting it on the back burner for now.

Thanks for your help.

regards

John

@futzle
Your code has incoming sections in each job. I don’t require this across the board as most of it is just run not jobs. I have tried the following for the particular job as part of trying to understand how it all works but it does not get called.

<action> <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPanel1</serviceId> <name>GetElkLog</name> <job> getLog(lul_device, lul_settings, lul_job) </job> <incoming> processMessage(lul_job, lul_device, jobHandler(lul_data)) </incoming> </action>

What am I missing?

regards

John

A lot of interesting information in this topic. Thank you futzle. 8)

John, I’m sorry, there’s not enough information in your post for me to be any help. You’ll have to flesh it out with much more detail. Is your Lua code being reached? Have you put logging statements in and seen them in the LuaUPnP.log file? What kind of data is your job trying to obtain, where from, and how does it decide that the job is complete? Do you even need an section? You may not, it depends on the form your data takes.

@futzle

What I want to do is retrieve the log files from the Elk Alarm panel. This is retrieved one line at a time, so to retrieve a range I have to send the command for each line I want to receive until I have the data I want. Code as follows: -

for i=logListBegin,logListEnd do
    --send command to receive single log line.
end

What comes back for each call is something along the lines of the following (some data not shown but it is number string with sections to signify date, time etc): -

1174001111540218001213

I did not want to store this data in a variable as the Elk stores 511 alarms and though it best not to over burden the Vera. So made the decision just to generate in a tab when asked by the user (very similar to what you have done in your plugin).

As it takes sometime to loop through 10 log entry’s I decided to make it a job and then push data to user once request had been completed on the server side.

I have learnt a lot from your code especially the javascript side of all this (thank you very much for all that) but how the lul_job and how the messaging works has escaped me.

P.S.
The code I put in place is never called so even though I have put logging statements to follow the flow they have done me no good. It still comes down to a lack of understanding of how it is all glued together.

Also as this is the first “job” all other actions are “run” it has been difficult ascertain where to place the intercept in the existing code.

regards

John

My advice is to break the problem down into smaller parts. Ignore the callback handler for the moment. Just get the Lua code in your plugin to successfully fetch the log entries from the alarm panel, and store them in a Lua variable.

It looks to me that the process from the point of view of the Lua code is:

  1. The initial request is made to the that you’ve defined.
  2. Your Lua function getLog() runs, and sends whatever serial command is necessary to the alarm panel. getLog() returns 5 to say Job Running. (There’s one error that I can already see in your Implementation file is that you don’t return anything. Compare with my code.)
  3. The alarm panel returns a string over the serial line. Your has an section, so processMessage() is called in your Lua code.
  4. The Lua code deals with the string, then decides whether to fetch the next log message (in which case we return to step 2), or there are enough log messages (in which case we return 4 to say Job Done).
  5. Now the Alarm panel’s logs are safely in a Lua variable waiting for the browser’s JavaScript to request them.

From your description, the getLog() function isn’t even being entered, because the luup.log() that you put at the top of it isn’t being reached. If that’s the case then there is presumably an error message from LuaUPnP in the same log file, saying Function Not Found, or something. It won’t just silently fail. Find that error message.

Where is your getLog() function? Is it in the section of the implementation file? Is it in a separate Lua file that gets require()d? If the function can’t be found then it’s probably that you are being hit by Lua’s scoping rules. Notice how in the Caddx implementation file I use the local variable “plugin” to ensure that the function can be found.

@futzle

If it wasn’t for you I think I would be still struggling :).

Because the incoming string was being intercepted it was never reaching my incoming code (rooky mistake)

The road becomes clearer as I do more coding but also makes me realise how far I have to go. I tip my hat to you.

Thanks again

P.S. First picture of your code (Most of the javascript except for the CSS is yours).

John

Looks nice! But maybe you should switch date & time :D.

Very pretty. Well done on getting this far.

Well Done!
Most folks do not realize the amount of understanding and the coordination between the many fragments of code … placed in exactly the right place … to pull this off!

@nlrb
Could be right about that ;D

Is it possible to return job status when using luup io intercept. In this circumstance the incoming data does not go via the normal mechanism and I am unsure if it is possible.

This is how I have done it without interception but for what I want to do I prefer to have the interception.

<action>
  <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPanel1</serviceId>
  <name>GetElkLog</name>
    <job>
      getLog(lul_device, lul_settings.LogStart, lul_job)
    </job>
    <incoming>
      return incomingGetLog(lul_device, lul_settings.LogStart, lul_job, lul_data)
    </incoming>
</action>