PWOL: Public Wake-On-Lan Interface Example

Thank you very much for your work. I altered this script, so it would use manual list of devices instead using DHCP, which wouldnt work always:

/www/pwol/api.lua
#!/usr/bin/lua
require("uci");
require("nixio");
require("luci.jsonc");
require("luci.http");
require("luci.ip");

--local dev_list_path = "/www/pwol/dev_list.txt";
local dev_list_json_path = "/www/pwol/dev_list.json";

function contains(arr, val) local k,v; for k,v in ipairs(arr) do if (v == val) then return true; end; end; return false; end;

function to_bool(val) if ((val == 0) or (val == nil) or (val == "")) then return false; end; return true; end;

function is_online(ip)

	local ret = os.execute(string.format("ping -4 -W 1 -c 1 '%s' > /dev/nul", ip));
	
	return not to_bool(ret);
end;

function readAll(file)

    local f = assert(io.open(file, "rb"));
    local content = f:read("*all");
	
    f:close();
	
    return content;
end

--local host_info = {};  -- { ['dev'] = { ['mac'] = '00:11:22:33:44:55', ['ip'] = "192.168.1.2" } };
--local host_info = require 'dev_list';

local host_info = luci.jsonc.parse(readAll(dev_list_json_path));

local host_ctl = {

	['hl'] = function () -- host list
	
		return host_info;
	end,

	['hs'] = function (host) -- host state
	
		local ret = {};

		if (host) then -- if value of 'host' is empty, return statuses of all known hosts
		
			local info = host_info[host];
			
			if (not info) then return nil; end;
			
			ret[host] = info;
			
			--print(host); -- debug
			
			ret[host].online = is_online(info.ip);
		else
		
			for host,info in pairs(host_info) do
			
				ret[host] = info;
				
				ret[host].online = is_online(info.ip);
			end;
		end;

		return ret;
	end,

	['hw'] = function (host) -- host wake
	
		if (not host) then return nil; end;
		
		local info = host_info[host];
		
		if (not info) then return nil; end;
		
		local ret = { [host] = host_info[host] };
		
		ret[host].wol_ok = not to_bool(os.execute(string.format("etherwake -b -i br-lan '%s' > /dev/nul", info.mac)));
		
		return ret;
	end,

};

local args = luci.http.urldecode_params(nixio.getenv('QUERY_STRING') or arg[1] or "cmd=hl"); -- NOTE: final val is for debugging, when script executed from terminal without args, e.g. `lua api.lua 'cmd=hs&host=computer1'`

local ret = {

	['ok'] = false,
	-- ['args'] = args, -- DEBUG ONLY
};

repeat -- NOTE: fake loop allows shortcuts using 'break'

	--local public_hosts = read_pub_hosts_list(dev_list_path); -- { 'dev1', 'dev2', ... }; // NOTE: cwd for FastCGI is '/www'
	--if (not public_hosts) then ret.err = "Failed reading list of available hosts"; break; end;

	--for k,v in pairs(uci.get_all('dhcp')) do --disabled
	
		-- NOTE: current behaviour is such that only *fully* matching device host names will end up in 'host_info', others will be completely ignored
		
		-- print(v.name); --debug
		
		--if (contains(public_hosts, v.name)) then host_info[v.name] = { mac = v.mac, ip = v.ip }; end;
	--end;

	local cmd = host_ctl[args.cmd];
	
	if (not cmd) then ret.err = "Unknown command"; break; end;
	
	local data = cmd(args.host);
	
	if (not data) then ret.err = "Unknown host: " .. args.host; break; end;
	
	ret.ok = true;
	
	ret.data = data;
until (true);

print("Status: 200 OK");
print("Content-type: application/json\n");
print(luci.jsonc.stringify(ret, '\t'));

and put devices into /www/pwol/dev_list.json.

{
	"PC1": {
	  "mac": "11:11:11:11:11:11",
	  "ip": "192.168.8.12"
	},
	"PC2": {
	  "mac": "22:22:22:22:22:22",
	  "ip": "192.168.8.166"
	},
	"PC3": {
	  "mac": "33:33:33:33:33:33",
	  "ip": "192.168.8.10"
	}
}

You also need to do btw if using symlink:

chmod +x /www/pwol/api.lua

2 Likes