Use a browser reload/refresh to (re)run a scene

Hi

I generate a number of html reports (hosted on Vera) , which are created/update via a scene every x minutes.

Does anyone know of a way in which, when viewing the report I could re-run the scene ( to get the most up to date information ) by just reloading/refreshing the browser ?

If that’s not possible, what other options are there ?

If you define a short piece of code in Lua Startup, you could define a specific data request handler which would run every time you accessed that specific URL.


Edit: simply described here:

Many thanks @akbooer

All the reports are currently stored on my Vera, and I access them individually via their own urls… (examples below)

http://192.168.102.22/hue.html
http://192.168.102.22/dsc.html
http://192.168.102.22/lasttrip.html
http://192.168.102.22/lights.html

So, with this http_handler route (to be sure I understand) - i would move away from using scenes ? and need to run them differently, this time creating the html file within a dedicated function ?

function HTTP_reports ()
... my report generation code here..
end

global = {}  -- 'global' table, call it whatever you like
luup.register_handler ("HTTP_reports", "dsc")

Then access it via…

http://veraip:3480/data_request?id=lr_dsc

As my code is currently creating html files (with html tables in) - how will that be presented via this new http-reports url ?

An alternative idea I’ve thought of is to have a url at the bottom of the report to re-run the scene?

luup.call_action("urn:micasaverde-com:serviceId:HomeAutomationGateway1","RunScene",{ SceneNum="34" }, 0)

There are likely lots of options, just curious which one people feel is best.

With a request handler, you’d not have any files at all, but simply produce the report on the fly.

The handler returns the entire HTML for the desired page.

That’s sounds great !

So if I’m not writing to an actual file, where do I send the ‘file:write’ requests ? See report code below writes to /www/sb1.html ?

local d, n
local file = io.open("/www/sb1.html", "w")
  
  file:write("<html><head><meta charset=\"utf-8\"> <meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">")
  file:write("<style type=\"text/css\">body {font-family: monospace;font-size: 13px; white-space: pre-wrap;}.custom { font-size: 1em; font-family: Gill Sans Extrabold, sans-serif; padding:5px; border-collapse: collapse; border: 1px solid black; } table { width:100% } .th { width:50% }</style></head>\n")
  file:write("<body><table class=custom><tr class=custom><th class=custom>Security Sensor</th><th class=custom>Last Tripped</th></tr><br/>\n")

local tt = {}
for n,d in pairs(luup.devices) do

    if d.category_num == 4 then
       local lasttrip = tonumber(luup.variable_get ("urn:micasaverde-com:serviceId:SecuritySensor1", "LastTrip", n) or os.time())
       local timeString = os.date(" %Y/%m/%d - %X", lasttrip)
       if lasttrip > 0 then
           table.insert(tt, { devnum=n, last=lasttrip, ltime=timeString })
       end
   end
end

table.sort(tt, function(a,b) return a.last > b.last end) -- sort highest to lowest
    for n,d in ipairs(tt) do
    
        file:write("<tr class=custom><td class=custom>" .. luup.devices[d.devnum].description .. "</td><td class=custom>".. d.ltime .. "</td></tr>\n")

        print (d.ltime ..' : ' .. luup.devices[d.devnum].description .. " Sensor was last tripped")
    end
file:write("</table>\n")
file:close()

Use a string buffer.

local html = {[[
<html>
  <head>
    <meta charset="utf-8"> 
    <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <style type="text/css">
      body {font-family: monospace;font-size: 13px; white-space: pre-wrap;}
      .custom { font-size: 1em; font-family: Gill Sans Extrabold, sans-serif; padding:5px; border-collapse: collapse; border: 1px solid black; } 
      table { width:100% } 
      .th { width:50% }
    </style>
  </head>
  <body>
    <table class=custom>
      <tr class=custom>
        <th class=custom>Security Sensor</th>
        <th class=custom>
        Last Tripped</th>
      </tr>
      <br/>
]]}

then replace each write, with a string assignment:

html[#html+1] = "<tr class=custom><td class=custom>" .. luup.devices[d.devnum].description .. "</td><td class=custom>".. d.ltime .. "</td></tr>\n"

Finally, return the concatenation of the total:

return table.concat(html), "text/html"

Thanks again @akbooer

I’ve updated the code as suggested, does all this have to go in the Lua startup area or can each report be stored (and run) separately as a module by putting the following into a .Lua file and uploading it to Vera ?

function HTTP_reports ()
local d, n
local html = {[[
<html>
  <head>
    <meta charset="utf-8"> 
    <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <style type="text/css">
      body {font-family: monospace;font-size: 13px; white-space: pre-wrap;}
      .custom { font-size: 1em; font-family: Gill Sans Extrabold, sans-serif; padding:5px; border-collapse: collapse; border: 1px solid black; } 
      table { width:100% } 
      .th { width:50% }
    </style>
  </head>
  <body>
    <table class=custom>
      <tr class=custom>
        <th class=custom>Security Sensor</th>
        <th class=custom>
        Last Tripped</th>
      </tr>
      <br/>
	  ]]}
local tt = {}
for n,d in pairs(luup.devices) do
	if d.category_num == 4 then
    	local lasttrip = tonumber(luup.variable_get ("urn:micasaverde-com:serviceId:SecuritySensor1", "LastTrip", n) or os.time())
    	local timeString = os.date(" %Y/%m/%d - %X", lasttrip)
    if lasttrip > 0 then
        table.insert(tt, { devnum=n, last=lasttrip, ltime=timeString })
    end
  end
end

table.sort(tt, function(a,b) return a.last > b.last end) -- sort highest to lowest

for n,d in ipairs(tt) do  
   html[#html+1] = "<tr class=custom><td class=custom>" .. luup.devices[d.devnum].description .. "</td><td class=custom>".. d.ltime .. "</td></tr>\n"
    print (d.ltime .. ' : ' .. luup.devices[d.devnum].description .. " Sensor was last tripped")
end
return table.concat(html), "text/html"
end

global = {}
luup.register_handler ("HTTP_reports", "dsc")

Ok, progress !! :partying_face:

I found an example that I could use as a reference (Rex’s LuaTest) - and created the following

local vreport = require("xxreportlasttriphttphandler")  -- lua filename/module
lasttrip = vreport.HTTP_reports  -- call lua filename/module and required function
luup.register_handler ("lasttrip", "LastTrip")  -- register http handler

And then, call it like this.,

192.168.1.22:3480/data_request?id=lr_LastTrip

Thanks again @akbooer

The only bit I’m not sure about is what the global table/array is for ?

global = {}

In this case, nothing. It’s a left over from the piece of code you copied it from. It was useful there, but not used by you here.

I’d make a couple more points, so that you don’t unnecessarily duplicate effort:

All your reports can be generated (separately) by one handler if you add a parameter such as `&report=dsc’ to the request. The handler has to parse this parameter and then dispatch the appropriate function to generate the output. The header and style information would be shared between reports.

local function hue_rep()
   -- whatever needed to consruct report HTML
  return report_html
end

local function dsc_rep()
   -- whatever for this report, etc...
end

function HTTP_reports (_, params)
  local reports = {hue = hue_rep, dsc = dsc_rep, lasttrip = last_rep, lights = light_rep}
  local fct = reports[params.report] or dsc_rep    -- dsc is default if not specified
  return fct, "text/html"
end

Your loop which builds each line of the report would be much clearer (and more efficient) if written like this:

local line = [[
<tr class=custom>
  <td class=custom>%s</td>
  <td class=custom>%s</td>
</tr>
]]
for n,d in ipairs(tt) do  
   html[#html+1] = line: format (luup.devices[d.devnum].description, d.ltime)
end

Thanks @akbooer

Ok, so does that mean I start each function after the header/style part?

This section is the default for all…

function HTTP_reports ()
    local d, n
    local html = {[[
    <html>
      <head>
        <title>Vera Last Trip</title>
        <meta charset="utf-8"> 
        <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    	<meta http-equiv="refresh" content="10">
    	<link rel="icon" type="image/png" href="https://community.getvera.com/uploads/default/optimized/3X/4/1/41fe9abf0b738352cb5bb34abfe0c22d2e3ef100_2_32x32.png">
        <style type="text/css">
          body {font-family: monospace;font-size: 14px; white-space: pre-wrap;}
          .custom { font-size: 1em; font-family: Gill Sans Extrabold, sans-serif; padding:5px; border-collapse: collapse; border: 1px solid black; } 
          table { width:100% } 
          .th { width:50% }
        </style>
      </head> ]]}

And then for each one begin with the html body to record the different column headers ?

local function dsc_rep()
  local html = {[[ <body>
    <table class=custom>
      <tr class=custom>
        <th class=custom>Security Sensor</th>
        <th class=custom>Last Tripped</th>
      </tr>
      <br/>
	  ]]}
local tt = {}

-- rest of the code here ... etc...

end

Well, it all depends. I imagine that your other report functions will want to write a similar table header, so you could abstract out a function which had the column headers as a parameter.

What overall structure were you planning to put the routines together?

I think I know what you mean…

So use the html as a sort of template, so for the table headers, I would do define their values elsewhere within their associated function e.g.


local Column1 = "Sensor Name"
local Column2 = "Sensor Value"

{[[ <body>
    <table class=custom>
      <tr class=custom>
        <th class=custom>]].. Column1 ..[[</th>
        <th class=custom>]].. Column2 ..[[</th>
      </tr>
      <br/>]]}

As the number of columns differ, I’m probably going to need a different route, maybe like this?

local Columns =  "<th class=custom>ID</th><th class=custom>Hostname</th><th class=custom>iP Address</th><th class=custom>Last Check-In</th>"

{[[ <body>
    <table class=custom>
      <tr class=custom>]]
          .. Columns ..
     [[ </tr>
      <br/>]]}