2022-05-18 11:49:00 -06:00
|
|
|
#!/usr/bin/lua
|
|
|
|
--[[
|
|
|
|
|
|
|
|
Copyright (C) 2022 Tim Wilkinson
|
|
|
|
See Contributors file for additional contributors
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation version 3 of the License.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional Terms:
|
|
|
|
|
|
|
|
Additional use restrictions exist on the AREDN(TM) trademark and logo.
|
|
|
|
See AREDNLicense.txt for more info.
|
|
|
|
|
|
|
|
Attributions to the AREDN Project must be retained in the source code.
|
|
|
|
If importing this code into a new or existing project attribution
|
|
|
|
to the AREDN project must be added to the source code.
|
|
|
|
|
|
|
|
You must not misrepresent the origin of the material contained within.
|
|
|
|
|
|
|
|
Modified versions must be modified to attribute to the original source
|
|
|
|
and be marked in reasonable ways as differentiate it from the original
|
|
|
|
version
|
|
|
|
|
|
|
|
--]]
|
|
|
|
|
|
|
|
require("uci")
|
|
|
|
require("aredn.http")
|
|
|
|
require("aredn.hardware")
|
|
|
|
local html = require("aredn.html")
|
|
|
|
local info = require("aredn.info")
|
|
|
|
|
|
|
|
local cursor = uci.cursor()
|
|
|
|
|
|
|
|
if cursor:get("aredn", "@lqm[0]", "enable") ~= "1" then
|
|
|
|
print "Content-type: text/text\r"
|
|
|
|
print "Cache-Control: no-store\r"
|
|
|
|
print "\r"
|
|
|
|
print "Disabled"
|
|
|
|
os.exit()
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local node = info.get_nvram("node")
|
|
|
|
local node_desc = cursor:get("system", "@system[0]", "description") or ""
|
|
|
|
local lat_lon = "<strong>Location Not Available</strong>"
|
|
|
|
local lat = cursor:get("aredn", "@location[0]", "lat")
|
|
|
|
local lon = cursor:get("aredn", "@location[0]", "lon")
|
|
|
|
if lat and lon then
|
|
|
|
lat_lon = string.format("<center><strong>Location: </strong> %s %s</center>", lat, lon)
|
|
|
|
end
|
|
|
|
|
|
|
|
http_header()
|
|
|
|
html.header(node .. " Neighbor Status")
|
|
|
|
html.print("<body>")
|
|
|
|
html.alert_banner()
|
|
|
|
html.print([[
|
|
|
|
<style>
|
|
|
|
.lt {
|
|
|
|
font-weight: bold;
|
|
|
|
padding: 30px 0 4px 0;
|
|
|
|
}
|
|
|
|
#links {
|
|
|
|
padding-bottom: 16px;
|
|
|
|
min-height: 300px;
|
|
|
|
}
|
|
|
|
#links > div {
|
|
|
|
padding: 2px 0;
|
|
|
|
}
|
|
|
|
.m, .b {
|
|
|
|
display: inline-block;
|
|
|
|
width: 190px;
|
|
|
|
}
|
|
|
|
.s {
|
|
|
|
display: inline-block;
|
|
|
|
width: 80px;
|
|
|
|
}
|
|
|
|
.p {
|
|
|
|
display: inline-block;
|
|
|
|
width: 130px;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
<center>
|
|
|
|
<h1>]] .. node .. [[ neighbor status</h1>]] .. lat_lon)
|
|
|
|
if node_desc ~= "" then
|
|
|
|
html.print([[<table id='node_description_display'><tr><td>]] .. node_desc .. [[</td></tr></table>]])
|
|
|
|
end
|
|
|
|
html.print([[<hr>
|
|
|
|
<table width=750>
|
|
|
|
<tr><td>
|
|
|
|
<center>
|
|
|
|
<button type=button onClick='window.location.reload()' title='Refresh this page'>Refresh</button>
|
|
|
|
|
|
|
|
<button type=button onClick='window.location="status"' title='Return to the status page'>Quit</button>
|
|
|
|
</center>
|
|
|
|
</td></tr>
|
|
|
|
<tr><td>
|
|
|
|
<div class="lt">
|
|
|
|
<span class="m">Neighbor</span><span class="s">SNR</span><span class="p">Distance</span><span class="s">Quality</span><span class="p">TX Estimate</span><span class="p">Status</span>
|
|
|
|
</div>
|
|
|
|
<div id="links"></div>
|
|
|
|
</td></tr>
|
|
|
|
</table>
|
|
|
|
</center>
|
|
|
|
<script>
|
|
|
|
const meters_to_miles = 0.000621371;
|
|
|
|
const meters_to_km = 0.001;
|
|
|
|
const wifi_scale = 0.2;
|
|
|
|
const get_status = (track, data) => {
|
|
|
|
if (track.pending > data.info.now) {
|
|
|
|
return "pending";
|
|
|
|
}
|
|
|
|
if (track.blocked) {
|
|
|
|
if (track.blocks.user) {
|
|
|
|
return "blocked - user";
|
|
|
|
}
|
|
|
|
if (track.blocks.dtd) {
|
|
|
|
return "blocked - dtd";
|
|
|
|
}
|
|
|
|
if (track.blocks.distance) {
|
|
|
|
return "blocked - distance";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (track.blocks.signal) {
|
|
|
|
return "blocked - signal";
|
|
|
|
}
|
|
|
|
if (track.blocks.dup) {
|
|
|
|
return "blocked - dup";
|
|
|
|
}
|
|
|
|
if (track.blocks.quality) {
|
|
|
|
return "blocked - quality";
|
|
|
|
}
|
|
|
|
return "blocked";
|
|
|
|
}
|
|
|
|
if (track.routable) {
|
|
|
|
return "active";
|
|
|
|
}
|
|
|
|
return "idle";
|
|
|
|
}
|
|
|
|
const name = track => {
|
2022-05-19 09:51:41 -06:00
|
|
|
if (track.hostname) {
|
|
|
|
return `<a href="http://${track.hostname}.local.mesh:8080">${track.hostname}</a>`;
|
|
|
|
}
|
|
|
|
if (track.ip) {
|
|
|
|
return `<a href="http://${track.ip}:8080">${track.ip}</a>`;
|
2022-05-18 11:49:00 -06:00
|
|
|
}
|
|
|
|
return track.mac || "-";
|
|
|
|
}
|
|
|
|
let convertd = (d) => (meters_to_km * d).toFixed(1) + " km";
|
|
|
|
switch (navigator.language.split("-")[1] || "unknown") {
|
|
|
|
case "US":
|
|
|
|
case "GB":
|
|
|
|
convertd = (d) => (meters_to_miles * d).toFixed(1) + " miles";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const update = data => {
|
|
|
|
let links = "";
|
|
|
|
for (let mac in data.info.trackers) {
|
|
|
|
const track = data.info.trackers[mac];
|
|
|
|
let txspeed = "-";
|
|
|
|
let txquality = "-";
|
|
|
|
let distance = "-";
|
|
|
|
let status = get_status(track, data);
|
|
|
|
if (status === "pending" || !track.blocked) {
|
|
|
|
txspeed = (track.tx_rate * wifi_scale).toFixed(2) + " Mbps";
|
|
|
|
}
|
|
|
|
if (typeof track.tx_quality === "number" && (status === "pending" || !track.blocked || (track.blocks.quality && !(track.blocks.dtd || track.blocks.signal || track.blocks.distance || track.blocks.user || track.blocks.dup)))) {
|
|
|
|
txquality = track.tx_quality + "%";
|
|
|
|
}
|
|
|
|
if (typeof track.distance === "number") {
|
|
|
|
distance = convertd(track.distance);
|
|
|
|
}
|
|
|
|
links += `<div><span class="m">${name(track)}</span><span class="s">${track.snr}${"rev_snr" in track ? "/" + track.rev_snr : ""}</span><span class="p">${distance}</span><span class="s">${txquality}</span><span class="p">${txspeed}</span><span class="p">${status}</span></div>`;
|
|
|
|
}
|
|
|
|
document.getElementById("links").innerHTML = links;
|
|
|
|
}
|
|
|
|
const fetchAndUpdate = () => {
|
|
|
|
fetch("/cgi-bin/sysinfo.json?lqm=1").then(r => r.json()).then(data => {
|
|
|
|
update(data.lqm);
|
|
|
|
setTimeout(fetchAndUpdate, 60000);
|
|
|
|
}).catch(_ => {
|
|
|
|
setTimeout(fetchAndUpdate, 30000);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
setTimeout(fetchAndUpdate, 30000);
|
|
|
|
]])
|
|
|
|
local ls = nixio.fs.stat("/tmp/lqm.info")
|
|
|
|
if ls and ls.size > 0 then
|
|
|
|
html.print("update({info:" .. io.open("/tmp/lqm.info"):read("*a") .. "})")
|
|
|
|
end
|
|
|
|
html.print([[</script>]])
|
|
|
|
html.footer()
|
|
|
|
html.print("</body></html>")
|
|
|
|
http_footer()
|