Last active
September 28, 2021 08:02
-
-
Save cgmartin/d52409fe473e1d4fc5044c3d676d109c to your computer and use it in GitHub Desktop.
RBLuaTest.lua Modifications (1.7) [Works in UI7 v1.7.3015]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module("RBLuaTest", package.seeall) | |
local version = "1.7" | |
local luadir = "/etc/cmh-ludl/" | |
--[[ | |
LuaTest is a tool for testing Vera scene Lua code. It runs on Vera as an http handler. | |
Upload RBLuaTest.lua to Vera using APPS->Develop Apps->Luup files then restart Vera. | |
Enter following three lines into APPS->Develop Apps->Test Luup code (LUA) and click GO: | |
local rblt = require("RBLuaTest") | |
rbLuaTest = rblt.rbLuaTest | |
luup.register_handler("rbLuaTest","LuaTest") | |
The three lines may also be added to Startup Lua for permanent availability. | |
Usage: <veraip>:3480/data_request?id=lr_LuaTest&file=<filenameorpath> | |
<veraip>:3480/data_request?id=lr_LuaTest&run=<filenameorpath> | |
Written by Rex Beckett - 12 March 2014. | |
See also: http://forum.micasaverde.com/index.php?topic=24018.0 | |
1.7 modifications: September 2017 | |
- Added CodeMirror lua syntax highlighting editor support | |
- Test results displayed in iframe on same page | |
]] | |
function rbLuaTestRun(luafile) | |
local locdmp = "" | |
local btdt = {} | |
local function pretty(val,name,same) | |
if not (same) then btdt = {} end | |
local tmp = "" | |
if name then tmp = tmp .. name .. "=" end | |
if type(val) == "number" then | |
tmp = tmp .. tostring(val) | |
elseif type(val) == "nil" then | |
tmp = tmp .. "nil" | |
elseif type(val) == "string" then | |
tmp = tmp .. string.format("%q", val) | |
elseif type(val) == "boolean" then | |
tmp = tmp .. (val and "true" or "false") | |
elseif type(val) == "table" then | |
if btdt[val] then tmp = tmp .. btdt[val] | |
else | |
btdt[val] = name | |
tmp = tmp .. "{ " | |
for k, v in pairs(val) do | |
if type(k) == "number" then | |
tmp = tmp .. pretty(v,"["..k.."]",true) .. ", " | |
else | |
tmp = tmp .. pretty(v, k, true) .. ", " | |
end | |
end | |
tmp = string.gsub(tmp,", $"," ",1) .. "}" | |
end | |
else | |
tmp = tmp .. type(val) | |
end | |
return tmp | |
end | |
function rbLuaErr(errobj) | |
for level=2,10 do | |
local fname = debug.getinfo(level) | |
if fname == nil or ((fname.what ~= "Lua") and (fname.what ~= "main")) then break end | |
locdmp = locdmp .. "[" .. (fname.name or "main") .. "]<br>" | |
for locn=1, math.huge do | |
local lname, lval = debug.getlocal(level,locn) | |
if (lname == nil) or (string.sub(lname,1,1) == "(") then break end | |
locdmp = locdmp .. pretty(lval,lname) .. "<br>" | |
end | |
end | |
return string.gsub(errobj,"(.+):(%d+):","Line %2:") | |
end | |
local lualist = "" | |
local code, ferr = io.open(luafile) | |
if code ~= nil then | |
local tab = " " | |
for l=1,math.huge do | |
line = code:read() | |
if line == nil then break end | |
line = string.gsub(line,"\t",tab) | |
lualist = lualist .. string.gsub(string.format("%4d %s<br>",l,line)," "," ") | |
end | |
code:close() | |
end | |
local luaerr = "" | |
rbLuaCode, luaerr = loadfile(luafile) | |
if rbLuaCode ~= nil then | |
_G.pretty = pretty | |
local socket = require("socket") | |
local tstart = socket.gettime() | |
local res, msg = xpcall(rbLuaCode,rbLuaErr) | |
local trun = string.format("%6.1f ms",(socket.gettime() - tstart) * 1000) | |
_G.pretty = nil | |
local retstr | |
if res then | |
retstr = "No errors<br>Runtime: " .. trun .. "<br>Code returned: " | |
else | |
retstr = "Runtime error: " | |
end | |
retstr = retstr .. tostring(msg) | |
luup.log("LuaTest " .. retstr) | |
return retstr, lualist, locdmp | |
else | |
luup.log("LuaTest Code error: " .. luaerr) | |
return "Code error: " .. string.gsub(luaerr,"(.+):(%d+):","Line %2:"), lualist, locdmp | |
end | |
end | |
function rbLuaEditOpen(luafile) | |
local strcode = "" | |
local file, ferr = io.open(luafile) | |
if file ~= nil then | |
strcode = file:read("*a") | |
file:close() | |
end | |
return (file ~= nil),strcode | |
end | |
function rbLuaEditSave(luafile,savecode) | |
local file, ferr = io.open(luafile,"w+") | |
if file ~= nil then | |
strcode = file:write(savecode) | |
file:close() | |
end | |
return (file ~= nil),ferr | |
end | |
function rbLuaTest (lul_request, lul_parameters, lul_outputformat) | |
rbPrintOut = "" | |
function rbPrint(...) | |
local fields = { ... } | |
local tab = " " | |
for i=1, #fields do | |
rbPrintOut = rbPrintOut .. tostring(fields[i]) .. tab | |
end | |
rbPrintOut = rbPrintOut .. "<br>" | |
end | |
local function filePath(filename) | |
if (filename == nil) or (string.sub(filename,1,1) == "/") then return filename | |
else return luadir .. filename | |
end | |
end | |
local html = [[ | |
<head> | |
<title>LuaTest</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.30.0/codemirror.min.js"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.30.0/codemirror.min.css"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.30.0/mode/lua/lua.min.js"></script> | |
<style>.CodeMirror { border: 1px solid #ccc; }</style> | |
</head> | |
<body style="font-family:arial;"> | |
]] | |
local content_type = "text/html" | |
local function doDevTitle(device) | |
local devname = (luup.devices[tonumber(device)] or {description = 'Not found'}).description | |
html = html..string.format("<br><b>Device: %d %s</b><br>",device, devname) | |
end | |
local listopt = lul_parameters["list"] | |
local devopt = lul_parameters["device"] | |
if (listopt == "variables") or (listopt == "values") then | |
html = html .. "<b><u>LuaTest " .. version .. "</u></b><br><br>" | |
local err, resp | |
if tonumber(devopt or "0") > 0 then | |
devopt = tonumber(devopt) | |
html = html .. "<b><u>Device Status</u></b><br>" | |
doDevTitle(devopt) | |
err,resp = luup.inet.wget("127.0.0.1:3480/data_request?id=status&output_format=xml&DeviceNum="..devopt) | |
else | |
html = html .. "<b><u>Device Variables</u></b><br>" | |
err,resp = luup.inet.wget("127.0.0.1:3480/data_request?id=status&output_format=xml") | |
end | |
if err == 0 then | |
local ptrs = string.find(resp,"<",1,true) | |
local ptre = 0 | |
local ptrm = #resp | |
while (ptrs ~= nil) do | |
ptre = string.find(resp,">",ptrs,true) | |
local line = string.sub(resp,ptrs,ptre) | |
if string.find(line,"^</devices>",1,true)~=nil then break end | |
local devno = string.match(line,"^<device id=\"(%d+)\"") | |
if devno ~= nil then doDevTitle(devno) | |
else | |
local svc,var,val = string.match(line,"^<state id=.-service=(\".-\")%svariable=(\".-\")%svalue=(\".-\")") | |
if (svc ~= nil) and (var ~= nil) then | |
if val == nil then val = "" end | |
local strend | |
if (listopt == "values") then | |
strend = " value = "..val.."<br>" | |
else strend = "<br>" | |
end | |
html = html .. svc..","..var..strend | |
end | |
end | |
ptrs = string.find(resp,"<",ptre+1,true) | |
end | |
html = html .. "<br><b>End</b>" | |
else | |
html = html .. "<br>Unable to read Vera status!<br>" | |
end | |
resp = nil | |
return html, content_type | |
end | |
if (listopt == "actions") then | |
html = html .. "<b><u>LuaTest " .. version .. "</u></b><br><br>".. | |
"<b><u>Device Actions</u></b><br>" | |
for devno=1, table.maxn(luup.devices) do | |
if luup.devices[devno] ~= nil then | |
local devname = luup.devices[devno].description | |
local zwave = ((luup.devices[devno].id or "") ~= "") or (devno == 1) | |
html = html..string.format("<br><b>Device: %d %s</b><br>",devno, devname) | |
local err,resp = luup.inet.wget("127.0.0.1:3480/data_request?id=lu_invoke&DeviceNum="..devno) | |
if err == 0 then | |
local ptrs = string.find(resp,"<a href=",1,true) | |
local ptre = 0 | |
while (ptrs ~= nil) do | |
ptre = string.find(resp,"</a>",ptrs,true) | |
local line = string.sub(resp,ptrs,ptre) | |
local svc,act,imp = string.match(line,"serviceId=(.-)&action=(.-)\">(%p?)") | |
if (act ~= nil) and (zwave or (imp ~= "")) then | |
local action = "\""..(string.match(act,"(.-)&") or act).."\",{" | |
local sep = "" | |
for k in string.gmatch(act,"&(.-=)") do | |
action = action .. sep .. k .. " " | |
sep = "," | |
end | |
action = action .. "}" | |
html = html.."\""..svc.."\","..action.."<br>" | |
end | |
ptrs = string.find(resp,"<a href=",ptre+4,true) | |
end | |
end | |
end | |
end | |
html = html .. "<br><b>End</b>" | |
resp = nil | |
return html, content_type | |
end | |
local testfile = filePath(lul_parameters["run"]) | |
if testfile ~= nil then | |
html = html .. "<b><u>Results</u></b><br>" | |
local oldprint = _G.print | |
_G.print = rbPrint | |
rbLuaRun = coroutine.create(rbLuaTestRun) | |
local status, retn, code, locd = coroutine.resume(rbLuaRun, testfile) | |
_G.print = oldprint | |
html = html .. retn .. "<br>" | |
if #(locd or "") > 0 then | |
html = html .. "<br><b><u>Locals</u></b><br>" .. locd | |
end | |
if rbPrintOut == "" then rbPrintOut = "(none)<br>" end | |
html = html .. "<br><b><u>Print output</u></b><br><pre>" .. string.gsub(rbPrintOut,"\n","<br>") .. "</pre>" | |
rbPrintOut = nil | |
return html, content_type | |
else | |
html = html .. "<b><u>LuaTest " .. version .. "</u></b><br><br>" | |
local luafile = filePath(lul_parameters["file"] or "luatest.lua") | |
local savefile = filePath(lul_parameters["save"]) | |
if savefile ~= nil then | |
local newcode = lul_parameters["code"] | |
luafile = savefile | |
rbLuaPut = coroutine.create(rbLuaEditSave) | |
local status,fileok,serr = coroutine.resume(rbLuaPut,luafile,newcode) | |
end | |
if string.match(luafile,".lzo$") then | |
luafile = string.sub(luafile,1,-5) | |
os.execute('pluto-lzo d '..luafile..'.lzo '..luafile) | |
end | |
rbLuaGet = coroutine.create(rbLuaEditOpen) | |
local status,fileok,code = coroutine.resume(rbLuaGet,luafile) | |
local message = "" | |
if not fileok then | |
message = " <u>File not found - will be created on <b>Save</b></u>" | |
end | |
html = html .. "<b>Lua file: </b>" .. luafile .. message .. "<br>" .. | |
[[<br><b><u>Code</u></b> | |
<form action="/data_request?" method="get"> | |
<input type="hidden" name="id" value="lr_LuaTest"> ]] .. | |
'<input type="hidden" name="save" value="'..luafile..'">' .. | |
'<textarea id="code" rows="40" cols="90" name="code" wrap="off" onchange="disTest()"' .. | |
' style="font-size:14px;font-weight:normal;font-family:Consolas,Monaco,Courier,monospace;width:100%;">' .. | |
code .. '</textarea><br>' .. | |
'<input id="bSave" type="submit" value="Save Code" >' .. | |
'<input id="bTest" type="button" value="Test Code"' .. | |
' onclick="window.open(\'/data_request?id=lr_LuaTest&run='..luafile .. | |
'\',\'testfileOutput\');">' .. | |
'<input id="bVars" type="button" value="Device Variable List"' .. | |
' onclick="window.open(\'/data_request?id=lr_LuaTest&list=variables\',\'_blank\');">' .. | |
'<input id="bVals" type="button" value="Variable Values List"' .. | |
' onclick="window.open(\'/data_request?id=lr_LuaTest&list=values\',\'_blank\');">' .. | |
'<input id="bActs" type="button" value="Device Action List"' .. | |
' onclick="window.open(\'/data_request?id=lr_LuaTest&list=actions\',\'_blank\');">' .. | |
'<input id="bStatus" type="button" value="Show Device Status"' .. | |
' onclick="window.open(\'/data_request?id=lr_LuaTest&list=values&device=\'+devno.value,\'_blank\');">' .. | |
' <span style="font-size:13px;">Device Number:</span>' .. | |
'<input type="text" id="devno" value="" size="2">' .. | |
[[</form> | |
<iframe name="testfileOutput" width="800" height="200"></iframe> | |
<script> | |
var codeTextarea = document.getElementById("code"); | |
var codeMirror = CodeMirror.fromTextArea(codeTextarea, {lineNumbers: true}); | |
codeMirror.on("change", disTest); | |
function disTest() { | |
document.getElementById("bTest").disabled = true; | |
} | |
</script>]] | |
return html, content_type | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment