Merge pull request #2007 from dibdot/travelmate
authorDirk Brenken <dev@brenken.org>
Sun, 29 Jul 2018 19:30:26 +0000 (21:30 +0200)
committerGitHub <noreply@github.com>
Sun, 29 Jul 2018 19:30:26 +0000 (21:30 +0200)
luci-app-travelmate: sync with travelmate 1.2.1

12 files changed:
applications/luci-app-travelmate/luasrc/controller/travelmate.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/cfg_firewall_tab.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/cfg_network_tab.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/cfg_wireless_tab.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/configuration_tab.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/overview_tab.lua
applications/luci-app-travelmate/luasrc/view/travelmate/ap_qr.htm
applications/luci-app-travelmate/luasrc/view/travelmate/logread.htm
applications/luci-app-travelmate/luasrc/view/travelmate/runtime.htm
applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm
applications/luci-app-travelmate/luasrc/view/travelmate/stations_backup.htm [new file with mode: 0644]
applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm

index 493a387c3e363390b341f0047db1625376146d0a..00969ffe7dad645c42c71ab50104c1259e8a68cf 100644 (file)
@@ -3,9 +3,12 @@
 
 module("luci.controller.travelmate", package.seeall)
 
-local util  = require("luci.util")
-local i18n  = require("luci.i18n")
-local templ = require("luci.template")
+local sys  = require("luci.sys")
+local util = require("luci.util")
+local http = require("luci.http")
+local i18n = require("luci.i18n")
+local json = require("luci.jsonc")
+local uci  = require("luci.model.uci").cursor()
 
 function index()
        if not nixio.fs.access("/etc/config/travelmate") then
@@ -14,13 +17,16 @@ function index()
        entry({"admin", "services", "travelmate"}, firstchild(), _("Travelmate"), 40).dependent = false
        entry({"admin", "services", "travelmate", "tab_from_cbi"}, cbi("travelmate/overview_tab", {hideresetbtn=true, hidesavebtn=true}), _("Overview"), 10).leaf = true
        entry({"admin", "services", "travelmate", "stations"}, template("travelmate/stations"), _("Wireless Stations"), 20).leaf = true
-       entry({"admin", "services", "travelmate", "logfile"}, call("logread"), _("View Logfile"), 30).leaf = true
+       entry({"admin", "services", "travelmate", "log"}, template("travelmate/logread"), _("View Logfile"), 30).leaf = true
        entry({"admin", "services", "travelmate", "advanced"}, firstchild(), _("Advanced"), 100)
        entry({"admin", "services", "travelmate", "advanced", "configuration"}, form("travelmate/configuration_tab"), _("Edit Travelmate Configuration"), 110).leaf = true
        entry({"admin", "services", "travelmate", "advanced", "cfg_wireless"}, form("travelmate/cfg_wireless_tab"), _("Edit Wireless Configuration"), 120).leaf = true
        entry({"admin", "services", "travelmate", "advanced", "cfg_network"}, form("travelmate/cfg_network_tab"), _("Edit Network Configuration"), 130).leaf = true
        entry({"admin", "services", "travelmate", "advanced", "cfg_firewall"}, form("travelmate/cfg_firewall_tab"), _("Edit Firewall Configuration"), 140).leaf = true
 
+       entry({"admin", "services", "travelmate", "logread"}, call("logread"), nil).leaf = true
+       entry({"admin", "services", "travelmate", "status"}, call("status_update"), nil).leaf = true
+       entry({"admin", "services", "travelmate", "action"}, call("trm_action"), nil).leaf = true
        entry({"admin", "services", "travelmate", "apqr"}, template("travelmate/ap_qr")).leaf = true
        entry({"admin", "services", "travelmate", "wifiscan"}, template("travelmate/wifi_scan")).leaf = true
        entry({"admin", "services", "travelmate", "wifiadd"}, form("travelmate/wifi_add", {hideresetbtn=true, hidesavebtn=true})).leaf = true
@@ -29,13 +35,38 @@ function index()
        entry({"admin", "services", "travelmate", "wifiorder"}, form("travelmate/wifi_order", {hideresetbtn=true, hidesavebtn=true})).leaf = true
 end
 
+function trm_action(name)
+       if name == "do_restart" then
+               luci.sys.call("/etc/init.d/travelmate restart >/dev/null 2>&1")
+       end
+       luci.http.prepare_content("text/plain") 
+       luci.http.write("0")
+end
+
+function status_update()
+       local rt_file
+       local content
+
+       rt_file = uci:get("travelmate", "global", "trm_rtfile") or "/tmp/trm_runtime.json"
+
+       if nixio.fs.access(rt_file) then
+               content = json.parse(nixio.fs.readfile(rt_file) or "")
+               http.prepare_content("application/json")
+               http.write_json(content)
+       end
+end
+
 function logread()
-       local logfile = ""
+       local content
 
        if nixio.fs.access("/var/log/messages") then
-               logfile = util.trim(util.exec("grep -F 'travelmate-' /var/log/messages"))
-       elseif nixio.fs.access("/sbin/logread") then
-               logfile = util.trim(util.exec("logread -e 'travelmate-'"))
+               content = util.trim(util.exec("grep -F 'travelmate-' /var/log/messages"))
+       else
+               content = util.trim(util.exec("logread -e 'travelmate-'"))
+       end
+       
+       if content == "" then
+               content = "No travelmate related logs yet!"
        end
-       templ.render("travelmate/logread", {title = i18n.translate("Travelmate Logfile"), content = logfile})
+       http.write(content)
 end
index e5a048fa88518b0f69b976b764178f184ea24078..fea190e9b9a50348057518a6bb95d1424f40ef9c 100644 (file)
@@ -1,11 +1,11 @@
--- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
 -- This is free software, licensed under the Apache License, Version 2.0
 
-local fs = require("nixio.fs")
-local util = require("luci.util")
-local trminput = "/etc/config/firewall"
+local fs    = require("nixio.fs")
+local util  = require("luci.util")
+local input = "/etc/config/firewall"
 
-if not nixio.fs.access(trminput) then
+if not fs.access(input) then
        m = SimpleForm("error", nil, translate("Input file not found, please check your configuration."))
        return m
 end
@@ -23,11 +23,15 @@ f.rows = 20
 f.rmempty = true
 
 function f.cfgvalue()
-       return nixio.fs.readfile(trminput) or ""
+       return fs.readfile(input) or ""
 end
 
 function f.write(self, section, data)
-       return nixio.fs.writefile(trminput, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
+       return fs.writefile(input, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
+end
+
+function f.remove(self, section, value)
+       return fs.writefile(input, "")
 end
 
 function s.handle(self, state, data)
index 0096d6a8c2075393b06ab3bfa9ad13058e2804c6..6f0ade772d10dfd13ff7e0f377ac7c9285278058 100644 (file)
@@ -1,11 +1,11 @@
--- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
 -- This is free software, licensed under the Apache License, Version 2.0
 
-local fs = require("nixio.fs")
-local util = require("luci.util")
-local trminput = "/etc/config/network"
+local fs    = require("nixio.fs")
+local util  = require("luci.util")
+local input = "/etc/config/network"
 
-if not nixio.fs.access(trminput) then
+if not fs.access(input) then
        m = SimpleForm("error", nil, translate("Input file not found, please check your configuration."))
        return m
 end
@@ -23,11 +23,15 @@ f.rows = 20
 f.rmempty = true
 
 function f.cfgvalue()
-       return nixio.fs.readfile(trminput) or ""
+       return fs.readfile(input) or ""
 end
 
 function f.write(self, section, data)
-       return nixio.fs.writefile(trminput, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
+       return fs.writefile(input, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
+end
+
+function f.remove(self, section, value)
+       return fs.writefile(input, "")
 end
 
 function s.handle(self, state, data)
index 7ef9920a08735eb843380f0ca54d6fb5e083fd26..ab59dfb3765229f0d89921da3bd9ef43e359a0f0 100644 (file)
@@ -1,11 +1,11 @@
--- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
 -- This is free software, licensed under the Apache License, Version 2.0
 
-local fs = require("nixio.fs")
-local util = require("luci.util")
-local trminput = "/etc/config/wireless"
+local fs    = require("nixio.fs")
+local util  = require("luci.util")
+local input = "/etc/config/wireless"
 
-if not nixio.fs.access(trminput) then
+if not fs.access(input) then
        m = SimpleForm("error", nil, translate("Input file not found, please check your configuration."))
        return m
 end
@@ -23,11 +23,15 @@ f.rows = 20
 f.rmempty = true
 
 function f.cfgvalue()
-       return nixio.fs.readfile(trminput) or ""
+       return fs.readfile(input) or ""
 end
 
 function f.write(self, section, data)
-       return nixio.fs.writefile(trminput, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
+       return fs.writefile(input, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
+end
+
+function f.remove(self, section, value)
+       return fs.writefile(input, "")
 end
 
 function s.handle(self, state, data)
index 8a20ab9cce2a27c79daa5077b3b14fa081dcd811..7bb32c1ec58312453d3f092101be57ab6df9f0b3 100644 (file)
@@ -1,11 +1,11 @@
--- Copyright 2017 Dirk Brenken (dev@brenken.org)
+-- Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
 -- This is free software, licensed under the Apache License, Version 2.0
 
-local fs = require("nixio.fs")
-local util = require("luci.util")
-local trminput = "/etc/config/travelmate"
+local fs    = require("nixio.fs")
+local util  = require("luci.util")
+local input = "/etc/config/travelmate"
 
-if not nixio.fs.access(trminput) then
+if not fs.access(input) then
        m = SimpleForm("error", nil, translate("Input file not found, please check your configuration."))
        m.reset = false
        m.submit = false
@@ -25,11 +25,15 @@ f.rows = 20
 f.rmempty = true
 
 function f.cfgvalue()
-       return nixio.fs.readfile(trminput) or ""
+       return fs.readfile(input) or ""
 end
 
 function f.write(self, section, data)
-       return nixio.fs.writefile(trminput, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
+       return fs.writefile(input, "\n" .. util.trim(data:gsub("\r\n", "\n")) .. "\n")
+end
+
+function f.remove(self, section, value)
+       return fs.writefile(input, "")
 end
 
 function s.handle(self, state, data)
index a1dcbc638ce18166123dc779336a16019c779db1..ab39dab6bd6bee79e48319709aadaa499725287e 100644 (file)
@@ -3,15 +3,12 @@
 
 local fs       = require("nixio.fs")
 local uci      = require("luci.model.uci").cursor()
-local json     = require("luci.jsonc")
 local util     = require("luci.util")
 local nw       = require("luci.model.network").init()
 local fw       = require("luci.model.firewall").init()
 local dump     = util.ubus("network.interface", "dump", {})
 local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
-local trminput = uci:get("travelmate", "global", "trm_rtfile") or "/tmp/trm_runtime.json"
 local uplink   = uci:get("network", trmiface) or ""
-local parse    = json.parse(fs.readfile(trminput) or "")
 
 m = Map("travelmate", translate("Travelmate"),
        translate("Configuration of the travelmate package to to enable travel router functionality. ")
@@ -20,11 +17,9 @@ m = Map("travelmate", translate("Travelmate"),
        .. "see online documentation</a>", "https://github.com/openwrt/packages/blob/master/net/travelmate/files/README.md"))
 m:chain("network")
 m:chain("firewall")
-m.apply_on_parse = true
 
 function m.on_apply(self)
        luci.sys.call("env -i /etc/init.d/travelmate restart >/dev/null 2>&1")
-       luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate"))
 end
 
 -- Interface Wizard
@@ -33,7 +28,7 @@ if uplink == "" then
        ds = m:section(NamedSection, "global", "travelmate", translate("Interface Wizard"))
        o = ds:option(Value, "trm_iface", translate("Create Uplink interface"),
                translate("Create a new wireless wan uplink interface, configure it to use dhcp and ")
-               .. translate("add it to the wan zone of the firewall.<br />")
+               .. translate("add it to the wan zone of the firewall. ")
                .. translate("This step has only to be done once."))
        o.datatype = "and(uciname,rangelength(3,15))"
        o.default = trmiface
@@ -96,55 +91,8 @@ end
 
 -- Runtime information
 
-ds = m:section(NamedSection, "global", "travelmate", translate("Runtime Information"))
-
-dv1 = ds:option(DummyValue, "status", translate("Travelmate Status (Quality)"))
-dv1.template = "travelmate/runtime"
-if parse ~= nil then
-       dv1.value = parse.data.travelmate_status or translate("n/a")
-else
-       dv1.value = translate("n/a")
-end
-
-dv2 = ds:option(DummyValue, "travelmate_version", translate("Travelmate Version"))
-dv2.template = "travelmate/runtime"
-if parse ~= nil then
-       dv2.value = parse.data.travelmate_version or translate("n/a")
-else
-       dv2.value = translate("n/a")
-end
-
-dv3 = ds:option(DummyValue, "station_id", translate("Station ID (SSID/BSSID)"))
-dv3.template = "travelmate/runtime"
-if parse ~= nil then
-       dv3.value = parse.data.station_id or translate("n/a")
-else
-       dv3.value = translate("n/a")
-end
-
-dv4 = ds:option(DummyValue, "station_interface", translate("Station Interface"))
-dv4.template = "travelmate/runtime"
-if parse ~= nil then
-       dv4.value = parse.data.station_interface or translate("n/a")
-else
-       dv4.value = translate("n/a")
-end
-
-dv5 = ds:option(DummyValue, "station_radio", translate("Station Radio"))
-dv5.template = "travelmate/runtime"
-if parse ~= nil then
-       dv5.value = parse.data.station_radio or translate("n/a")
-else
-       dv5.value = translate("n/a")
-end
-
-dv6 = ds:option(DummyValue, "last_rundate", translate("Last rundate"))
-dv6.template = "travelmate/runtime"
-if parse ~= nil then
-       dv6.value = parse.data.last_rundate or translate("n/a")
-else
-       dv6.value = translate("n/a")
-end
+ds = s:option(DummyValue, "_dummy")
+ds.template = "travelmate/runtime"
 
 -- Extra options
 
index a92dbe1469dc2e79a73b8a085f76a94585c4cf41..3f01a81e35cf9275ba2492a9f4a83e29be099c7d 100644 (file)
@@ -6,60 +6,61 @@ This is free software, licensed under the Apache License, Version 2.0
 <%+header%>
 
 <div class="cbi-map">
-    <div class="cbi-map-descr">
-        <%=translate("Here you'll find the QR codes from all of your configured Access Points. It allows you to connect your Android or iOS devices to your router's WiFi using the QR code shown below.")%>
-    </div>
-<%-
-  local write = io.write
-  local uci   = require("luci.model.uci").cursor()
+       <div class="cbi-map-descr">
+               <%=translate("Here you'll find the QR codes from all of your configured Access Points. It allows you to connect your Android or iOS devices to your router's WiFi using the QR code shown below.")%>
+       </div>
+       <%- local uci = require("luci.model.uci").cursor()
 
-  uci:foreach("wireless", "wifi-iface", function(s)
-    local device = s.device or ""
-    local mode = s.mode or ""
-    local ssid = s.ssid or ""
-    local enc = s.encryption or ""
-    local key = s.key or ""
-    local hidden = s.hidden or "false"
-    local disabled = s.disabled or ""
-    local wep_slots = {s.key1 or "", s.key2 or "", s.key3 or "", s.key4 or ""}
+               uci:foreach("wireless", "wifi-iface", function(s)
+                       local device = s.device or ""
+                       local mode = s.mode or ""
+                       local ssid = s.ssid or ""
+                       local enc = s.encryption or ""
+                       local key = s.key or ""
+                       local hidden = s.hidden or "false"
+                       local disabled = s.disabled or ""
+                       local wep_slots = {s.key1 or "", s.key2 or "", s.key3 or "", s.key4 or ""}
 
-    if device and mode == "ap" and disabled ~= "1" then
-      if string.match(enc, '^psk') then
-        enc = "WPA"
-      elseif string.match(enc, '^wep') then
-        enc = "WEP"
-        if tonumber(key) then
-          key = wep_slots[tonumber(key)]
-        end
-      elseif enc == "none" then
-        enc = "nopass"
-        key = "nokey"
-      else
-        enc = ""
-      end
-      if hidden == "1" then
-        hidden = "true"
-      end
-      if ssid and enc and key then
-        local e_ssid = string.gsub(ssid,"[\"\\';:, ]",[[\\\%1]])
-        local e_key = string.gsub(key,"[\"\\';:, ]",[[\\\%1]])
-        local qrcode = ""
-        qrcode = luci.sys.exec("/usr/bin/qrencode --inline --8bit --type=SVG --output=- 'WIFI:S:\"'" .. e_ssid .. "'\";T:'" .. enc .. "';P:\"'" .. e_key .. "'\";H:'" .. hidden .. "';'")
--%>
-    <fieldset class="cbi-section">
-        <legend>AP on <%=device%> with SSID "<%=ssid%>"</legend>
-        <h3 name="content"><%=qrcode%></h3>
-    </fieldset>
-<%-
-      end
-    end
-  end)
-%>
+                       if device and mode == "ap" and disabled ~= "1" then
+                               if string.match(enc, '^psk') then
+                                       enc = "WPA"
+                               elseif string.match(enc, '^wep') then
+                                       enc = "WEP"
+                                       if tonumber(key) then
+                                               key = wep_slots[tonumber(key)]
+                                       end
+                               elseif enc == "none" then
+                                       enc = "nopass"
+                                       key = "nokey"
+                               else
+                                       enc = ""
+                               end
+
+                               if hidden == "1" then
+                                       hidden = "true"
+                               end
+
+                               if ssid and enc and key then
+                                       local e_ssid = string.gsub(ssid,"[\"\\';:, ]",[[\\\%1]])
+                                       local e_key = string.gsub(key,"[\"\\';:, ]",[[\\\%1]])
+                                       local qrcode = ""
+
+                                       qrcode = luci.sys.exec("/usr/bin/qrencode --inline --8bit --type=SVG --output=- 'WIFI:S:\"'" .. e_ssid .. "'\";T:'" .. enc .. "';P:\"'" .. e_key .. "'\";H:'" .. hidden .. "';'")
+       -%>
+       <div class="cbi-section">
+               <h3>AP on <%=device%> with SSID "<%=ssid%>"</h3>
+               <h3><%=qrcode%></h3>
+       </div>
+       <%-
+                               end
+                       end
+               end)
+       -%>
 </div>
 <div class="cbi-page-actions right">
-    <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/tab_from_cbi')%>" method="post">
-        <input class="cbi-button cbi-button-reset" type="submit" value="<%:Back to overview%>"/>
-    </form>
+       <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/tab_from_cbi')%>" method="post">
+               <input class="cbi-button cbi-button-reset" type="submit" value="<%:Back to overview%>" />
+       </form>
 </div>
 
 <%+footer%>
index c40bdeeb591042853d5f765dd8de0121f4c94d2c..4457296f54cfeaab6c857f52be2ec12e7d4ef6aa 100644 (file)
@@ -5,16 +5,47 @@ This is free software, licensed under the Apache License, Version 2.0
 
 <%+header%>
 
+<style type="text/css">
+       select[readonly],
+       textarea[readonly]
+       {
+               width: 100%;
+               height: 450px;
+               border: 1px solid #cccccc;
+               padding: 5px;
+               font-size: 12px;
+               font-family: monospace;
+               resize: none;
+               pointer-events: auto;
+               cursor: auto;
+       }
+</style>
+
+<script type="text/javascript">
+//<![CDATA[
+       function log_update()
+       {
+               XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "travelmate", "logread")%>', null,
+               function(x)
+               {
+                       if (!x)
+                       {
+                               return;
+                       }
+                       var view       = document.getElementById("view_id");
+                       view.value     = x.responseText;
+                       view.scrollTop = view.scrollHeight;
+               });
+       }
+       window.onload = log_update();
+//]]>
+</script>
+
 <div class="cbi-map">
        <div class="cbi-section">
-               <div class="cbi-section-descr"><%:This form shows the syslog output, pre-filtered for travelmate related messages only.%></div>
-               <textarea id="logread_id" style="width: 100%; height: 450px; border: 1px solid #cccccc; padding: 5px; font-size: 12px; font-family: monospace; resize: none;" readonly="readonly" wrap="off" rows="<%=content:cmatch("\n")+2%>"><%=content:pcdata()%></textarea>
+               <div class="cbi-section-descr"><%:The syslog output, pre-filtered for travelmate related messages only.%></div>
+               <textarea id="view_id" readonly="readonly" wrap="off" value=""></textarea>
        </div>
 </div>
 
-<script type="text/javascript">
-       var textarea = document.getElementById('logread_id');
-       textarea.scrollTop = textarea.scrollHeight;
-</script>
-
 <%+footer%>
index 7e93efab91af4acec7c08a5a7fa3379138576d55..aba4a3201862f48e84f8a99ae4c107abcbb2b24c 100644 (file)
@@ -3,8 +3,143 @@ Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
 This is free software, licensed under the Apache License, Version 2.0
 -%>
 
-<%+cbi/valueheader%>
+<style type="text/css">
+       .runtime
+       {
+               color: #37c;
+               //#0069d6;
+               font-weight: bold;
+               display: inline-block;
+               width: 100%;
+               padding-top: 0.5rem;
+       }
+</style>
 
-<input name="runtime" id="runtime" type="text" class="cbi-input-text" style="outline:none;border:none;box-shadow:none;background:transparent;color:#0069d6;font-weight:bold;line-height:30px;height:30px;width:50em;" value="<%=self:cfgvalue(section)%>" disabled="disabled" />
+<script type="text/javascript">
+//<![CDATA[
+       function status_update(json)
+       {
+                       var btn1  = document.getElementById("btn1");
+                       var view  = document.getElementById("value_1");
+                       var input = json.data.travelmate_status;
+                       
+                       btn1.value = "<%:Restart%>";
+                       btn1.name  = "do_restart";
+                       view.innerHTML = input || "-";
+                       view = document.getElementById("value_2");
+                       input = json.data.travelmate_version;
+                       view.innerHTML = input || "-";
+                       view = document.getElementById("value_3");
+                       input = json.data.station_id;
+                       view.innerHTML = input || "-";
+                       view = document.getElementById("value_4");
+                       input = json.data.station_interface;
+                       view.innerHTML = input || "-";
+                       view = document.getElementById("value_5");
+                       input = json.data.faulty_stations;
+                       view.innerHTML = input || "-";
+                       view = document.getElementById("value_6");
+                       input = json.data.last_rundate;
+                       view.innerHTML = input || "-";
+                       btn1.disabled = false;
+                       running(btn1_running, 0);
+       }
 
-<%+cbi/valuefooter%>
+       function btn_action(action)
+       {
+               var btn1 = document.getElementById("btn1");
+               var btn1_running = document.getElementById("btn1_running");
+
+               btn1.disabled = true;
+               running(btn1_running, 1);
+
+               new XHR.get('<%=luci.dispatcher.build_url("admin", "services", "travelmate")%>/action/' + action.name, null,
+               function(x)
+               {
+                       if (!x)
+                       {
+                               return;
+                       }
+               });
+       }
+
+       function running(element, state)
+       {
+               if (state === 1)
+               {
+                       var running_html = '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" width="16" height="16" style="vertical-align:middle" />';
+                       element.innerHTML = running_html;
+               }
+               else
+               {
+                       element.innerHTML = '';
+               }
+       }
+
+       XHR.get('<%=luci.dispatcher.build_url("admin", "services", "travelmate", "status")%>', null,
+       function(x, json_info)
+       {
+               if (!x || !json_info)
+               {
+                       return;
+               }
+               status_update(json_info)
+       });
+
+       XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "travelmate", "status")%>', null,
+       function(x, json_info)
+       {
+               if (!x || !json_info)
+               {
+                       return;
+               }
+               status_update(json_info)
+       });
+//]]>
+</script>
+
+<h3><%:Runtime Information%></h3>
+<div class="cbi-value" id="status_1">
+       <label class="cbi-value-title" for="status_1"><%:Travelmate Status (Quality)%></label>
+       <div class="cbi-value-field">
+               <span class="runtime" id="value_1">-</span>
+       </div>
+</div>
+<div class="cbi-value" id="status_2">
+       <label class="cbi-value-title" for="status_2"><%:Travelmate Version%></label>
+       <div class="cbi-value-field">
+               <span class="runtime" id="value_2">-</span>
+       </div>
+</div>
+<div class="cbi-value" id="status_3">
+       <label class="cbi-value-title" for="status_3"><%:Station ID (RADIO/SSID/BSSID)%></label>
+       <div class="cbi-value-field">
+               <span class="runtime" id="value_3">-</span>
+       </div>
+</div>
+<div class="cbi-value" id="status_4">
+       <label class="cbi-value-title" for="status_4"><%:Station Interface%></label>
+       <div class="cbi-value-field">
+               <span class="runtime" id="value_4">-</span>
+       </div>
+</div>
+<div class="cbi-value" id="status_5">
+       <label class="cbi-value-title" for="status_5"><%:Faulty Stations%></label>
+       <div class="cbi-value-field">
+               <span class="runtime" id="value_5">-</span>
+       </div>
+</div>
+<div class="cbi-value" id="status_6">
+       <label class="cbi-value-title" for="status_6"><%:Last Run%></label>
+       <div class="cbi-value-field">
+               <span class="runtime" id="value_6">-</span>
+       </div>
+</div>
+<hr />
+<div class="cbi-value" id="button_1">
+       <label class="cbi-value-title" for="button_1"><%:Restart Travelmate%></label>
+       <div class="cbi-value-field">
+               <input class="cbi-button cbi-button-reset" id="btn1" type="button" value="" onclick="btn_action(this)" />
+               <span id="btn1_running" style="display:inline-block; width:16px; height:16px; margin:0 5px"></span>
+       </div>
+</div>
index 74542a9ca5953e6e2ed8612a749d4dcb688fbe11..98e2e64bce673db8b77dc858aa0e7acff8fa9af4 100644 (file)
@@ -4,75 +4,151 @@ This is free software, licensed under the Apache License, Version 2.0
 -%>
 
 <%-
-  local write    = io.write
-  local uci      = require("luci.model.uci").cursor()
-  local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
+       local uci      = require("luci.model.uci").cursor()
+       local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
 -%>
 
 <%+header%>
 
+<script type="text/javascript">
+//<![CDATA[
+       function status_update(json)
+       {
+               var i;
+               var j;
+               var search;
+               var view;
+               var list;
+               var status = json.data.travelmate_status;
+               var faulty = json.data.faulty_stations;
+
+               if (faulty)
+               {
+                       var faulty_array = faulty.split(' ');
+                       for (i = 0; i < faulty_array.length; i++)
+                       {
+                               for (j = 1; j <= 5; j++)
+                               {
+                                       search = j + "_" + faulty_array[i];
+                                       view   = document.getElementById(search);
+                                       if (view)
+                                       {
+                                               view.setAttribute("name", "station_nok");
+                                               view.setAttribute("style", "color: #a22; font-weight: bold");
+                                       }
+                               }
+                       }
+               }
+               else
+               {
+                       list = document.getElementsByName("station_nok");
+                       if (list.length > 0)
+                       {
+                               for (i = 0; i < list.length; i++)
+                               {
+                                       list[i].removeAttribute("style");
+                               }
+                       }
+               }
+
+               if (status.startsWith("connected"))
+               {
+                       for (i = 1; i <= 5; i++)
+                       {
+                               search = i + "_" + json.data.station_id;
+                               view   = document.getElementById(search);
+                               if (view)
+                               {
+                                       view.setAttribute("style", "color: #37c; font-weight: bold");
+                               }
+                       }
+               }
+               else
+               {
+                       list = document.getElementsByName("station_ok");
+                       if (list.length > 0)
+                       {
+                               for (i = 0; i < list.length; i++)
+                               {
+                                       list[i].removeAttribute("style");
+                               }
+                       }
+               }
+       }
+
+       XHR.get('<%=luci.dispatcher.build_url("admin", "services", "travelmate", "status")%>', null,
+       function(x, json_info)
+       {
+               if (!x || !json_info)
+               {
+                       return;
+               }
+               status_update(json_info)
+       });
+
+       XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "travelmate", "status")%>', null,
+       function(x, json_info)
+       {
+               if (!x || !json_info)
+               {
+                       return;
+               }
+               status_update(json_info)
+       });
+//]]>
+</script>
 
 <div class="cbi-map">
-<div class="cbi-map-descr">
-  <%=translatef("Provides an overview of all configured uplinks for the travelmate interface (%s). You can edit, delete or re-order existing uplinks or scan for a new one. The currently used uplink is emphasized in blue.", trmiface)%>
-</div>
+       <div class="cbi-map-descr">
+               <%=translatef("Provides an overview of all configured uplinks for the travelmate interface (%s). You can edit, delete or re-order existing uplinks or scan for a new one. The currently used uplink is emphasized in blue, faulty stations in red.", trmiface)%>
+       </div>
 
-<div class="cbi-section">
-  <div class="table cbi-section-table">
-    <div class="tr cbi-section-table-titles">
-      <div class="th left"><%:Device%></div>
-      <div class="th left"><%:SSID%></div>
-      <div class="th left"><%:BSSID%></div>
-      <div class="th left"><%:Encryption%></div>
-      <div class="th center">&#160;</div>
-    </div>
-<%
-  uci:foreach("wireless", "wifi-iface", function(s)
-    local iface = s.network or ""
-    if iface == trmiface then
-      local section = s['.name'] or ""
-      local device = s.device or "-"
-      local ssid = s.ssid or "-"
-      local bssid = s.bssid or "-"
-      local encryption = s.encryption or "-"
-      local disabled = s.disabled or ""
-      local style = "text-align:left;color:#000000"
-      if disabled == "0" then
-        style = "text-align:left;color:#0069d6;font-weight:bold"
-      end
-%>
-    <div class="tr cbi-section-table-row cbi-rowstyle-1" style="<%=style%>">
-      <div class="td" style="<%=style%>"><%=device%></div>
-      <div class="td" style="<%=style%>"><%=ssid%></div>
-      <div class="td" style="<%=style%>"><%=bssid%></div>
-      <div class="td" style="<%=style%>"><%=encryption%></div>
-      <div class="td cbi-section-actions">
-        <input class="cbi-button cbi-button-up" type="button" value="<%:Up%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>&amp;dir=up'" alt="<%:Move up%>" title="<%:Move up%>"/>
-        <input class="cbi-button cbi-button-down" type="button" value="<%:Down%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>&amp;dir=down'" alt="<%:Move down%>" title="<%:Move down%>"/>
-        <input type="button" class="cbi-button cbi-button-edit" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiedit')%>?cfg=<%=section%>'" title="<%:Edit this Uplink%>" value="<%:Edit%>"/>
-        <input type="button" class="cbi-button cbi-button-remove" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifidelete')%>?cfg=<%=section%>'" title="<%:Delete this Uplink%>" value="<%:Delete%>"/>
-      </div>
-    </div>
-<%
-    end
-  end)
-%>
-  </div>
-</div>
-<div class="cbi-page-actions right">
-<%
-  uci:foreach("wireless", "wifi-device", function(s)
-    local device = s[".name"]
-%>
-  <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/wifiscan')%>" method="post">
-    <input type="hidden" name="device" value="<%=device%>"/>
-    <input type="hidden" name="token" value="<%=token%>"/>
-    <input type="submit" class="cbi-button cbi-button-action important" title="<%:Find and join network on%> <%=device%>" value="<%:Scan%> <%=device%>"/>
-  </form>
-<%
-  end)
-%>
-</div>
+       <div class="cbi-section">
+               <div class="table cbi-section-table">
+                       <div class="tr cbi-section-table-titles">
+                               <div class="th left"><%:Device%></div>
+                               <div class="th left"><%:SSID%></div>
+                               <div class="th left"><%:BSSID%></div>
+                               <div class="th left"><%:Encryption%></div>
+                               <div class="th center"><%:Action%></div>
+                       </div>
+                       <%- uci:foreach("wireless", "wifi-iface", function(s)
+                                       local iface = s.network or ""
+                                       if iface == trmiface then
+                                               local section = s['.name'] or ""
+                                               local device  = s.device or "-"
+                                               local ssid    = s.ssid or "-"
+                                               local bssid   = s.bssid or "-"
+                                               local encr    = s.encryption or "-"
+                       -%>
+                       <div class="tr cbi-section-table-row cbi-rowstyle-1" name="station_ok" id="1_<%=device%>/<%=ssid%>/<%=bssid%>">
+                               <div class="td left" name="station_ok" id="2_<%=device%>/<%=ssid%>/<%=bssid%>"><%=device%></div>
+                               <div class="td left" name="station_ok" id="3_<%=device%>/<%=ssid%>/<%=bssid%>"><%=ssid%></div>
+                               <div class="td left" name="station_ok" id="4_<%=device%>/<%=ssid%>/<%=bssid%>"><%=bssid%></div>
+                               <div class="td left" name="station_ok" id="5_<%=device%>/<%=ssid%>/<%=bssid%>"><%=encr%></div>
+                               <div class="td middle cbi-section-actions">
+                                       <div>
+                                               <input class="cbi-button cbi-button-up" type="button" value="<%:Up%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>&amp;dir=up'" alt="<%:Move up%>" title="<%:Move up%>" />
+                                               <input class="cbi-button cbi-button-down" type="button" value="<%:Down%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>&amp;dir=down'" alt="<%:Move down%>" title="<%:Move down%>" />
+                                               <input class="cbi-button cbi-button-edit" type="button" value="<%:Edit%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiedit')%>?cfg=<%=section%>'" title="<%:Edit this Uplink%>" />
+                                               <input class="cbi-button cbi-button-remove" type="button" value="<%:Delete%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifidelete')%>?cfg=<%=section%>'" title="<%:Delete this Uplink%>" />
+                                       </div>
+                               </div>
+                       </div>
+               <%- end; end) -%>
+               </div>
+       </div>
+       <div class="cbi-page-actions right">
+               <%- uci:foreach("wireless", "wifi-device", function(s)
+                       local device = s[".name"]
+                       local hwmode = s.hwmode or "-" -%>
+               <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/wifiscan')%>" method="post">
+                       <input type="hidden" name="device" value="<%=device%>" />
+                       <input type="hidden" name="token" value="<%=token%>" />
+                       <input type="submit" class="cbi-button cbi-button-action important" title="<%:Find and join network on%> <%=device%>" value="<%:Scan%> <%=device%> (<%=hwmode%>)" />
+               </form>
+               <%- end) -%>
+       </div>
 </div>
 
 <%+footer%>
diff --git a/applications/luci-app-travelmate/luasrc/view/travelmate/stations_backup.htm b/applications/luci-app-travelmate/luasrc/view/travelmate/stations_backup.htm
new file mode 100644 (file)
index 0000000..ecfc6cd
--- /dev/null
@@ -0,0 +1,70 @@
+<%#
+Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
+This is free software, licensed under the Apache License, Version 2.0
+-%>
+
+<%-
+       local uci      = require("luci.model.uci").cursor()
+       local trmiface = uci:get("travelmate", "global", "trm_iface") or "trm_wwan"
+-%>
+
+<%+header%>
+
+<div class="cbi-map">
+       <div class="cbi-map-descr">
+               <%=translatef("Provides an overview of all configured uplinks for the travelmate interface (%s). You can edit, delete or re-order existing uplinks or scan for a new one. The currently used uplink is emphasized in blue.", trmiface)%>
+       </div>
+
+       <div class="cbi-section">
+               <div class="table cbi-section-table">
+                       <div class="tr cbi-section-table-titles">
+                               <div class="th left"><%:Device%></div>
+                               <div class="th left"><%:SSID%></div>
+                               <div class="th left"><%:BSSID%></div>
+                               <div class="th left"><%:Encryption%></div>
+                               <div class="th center"><%:Action%></div>
+                       </div>
+                       <%- uci:foreach("wireless", "wifi-iface", function(s)
+                                       local iface = s.network or ""
+                                       if iface == trmiface then
+                                               local section  = s['.name'] or ""
+                                               local device   = s.device or "-"
+                                               local ssid     = s.ssid or "-"
+                                               local bssid    = s.bssid or "-"
+                                               local encr     = s.encryption or "-"
+                                               local disabled = s.disabled or ""
+                                               local style    = "text-align:left;color:#000000"
+                                               if disabled == "0" then
+                                                       style = "text-align:left;color:#0069d6;font-weight:bold"
+                                               end -%>
+                       <div class="tr cbi-section-table-row cbi-rowstyle-1" style="<%=style%>">
+                               <div class="td" style="<%=style%>"><%=device%></div>
+                               <div class="td" style="<%=style%>"><%=ssid%></div>
+                               <div class="td" style="<%=style%>"><%=bssid%></div>
+                               <div class="td" style="<%=style%>"><%=encr%></div>
+                               <div class="td middle cbi-section-actions">
+                                       <div>
+                                               <input class="cbi-button cbi-button-up" type="button" value="<%:Up%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>&amp;dir=up'" alt="<%:Move up%>" title="<%:Move up%>" />
+                                               <input class="cbi-button cbi-button-down" type="button" value="<%:Down%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>&amp;dir=down'" alt="<%:Move down%>" title="<%:Move down%>" />
+                                               <input class="cbi-button cbi-button-edit" type="button" value="<%:Edit%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiedit')%>?cfg=<%=section%>'" title="<%:Edit this Uplink%>" />
+                                               <input class="cbi-button cbi-button-remove" type="button" value="<%:Delete%>" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifidelete')%>?cfg=<%=section%>'" title="<%:Delete this Uplink%>" />
+                                       </div>
+                               </div>
+                       </div>
+               <%- end; end) -%>
+               </div>
+       </div>
+       <div class="cbi-page-actions right">
+               <%- uci:foreach("wireless", "wifi-device", function(s)
+                       local device = s[".name"]
+                       local hwmode = s.hwmode or "-" -%>
+               <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/wifiscan')%>" method="post">
+                       <input type="hidden" name="device" value="<%=device%>" />
+                       <input type="hidden" name="token" value="<%=token%>" />
+                       <input type="submit" class="cbi-button cbi-button-action important" title="<%:Find and join network on%> <%=device%>" value="<%:Scan%> <%=device%> (<%=hwmode%>)" />
+               </form>
+               <%- end) -%>
+       </div>
+</div>
+
+<%+footer%>
index 8a417d69c299e566d6507afd5b0b60ac987882da..ab3fe77fbc9415eb9bcd2b931ab80d9e3dfbe8fa 100644 (file)
@@ -1,97 +1,99 @@
 <%#
-Copyright 2017 Dirk Brenken (dev@brenken.org)
+Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
 This is free software, licensed under the Apache License, Version 2.0
 -%>
 
 <%-
-    local sys = require("luci.sys")
-    local utl = require("luci.util")
-    local dev = luci.http.formvalue("device")
-    local iw  = luci.sys.wifi.getiwinfo(dev)
-    local wpa_label = {translate("WPA"), translate("WPA2"), translate("WPA/WPA2")}
+       local sys   = require("luci.sys")
+       local utl   = require("luci.util")
+       local dev   = luci.http.formvalue("device")
+       local iw    = luci.sys.wifi.getiwinfo(dev)
+       local label = {translate("WPA"), translate("WPA2"), translate("WPA/WPA2")}
 
-    if not iw then
-        luci.http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
-    end
+       if not iw then
+               luci.http.redirect(luci.dispatcher.build_url("admin/services/travelmate/stations"))
+       end
 
-    function format_wifi_encryption(info)
-        if info.wep == true then
-            return translate("WEP")
-        elseif info.wpa > 0 then
-            return "%s (%s/%s)" %{wpa_label[info.wpa], table.concat(info.auth_suites), table.concat(info.group_ciphers)}
-        elseif info.enabled then
-            return translate("Unknown")
-        else
-            return translate("Open")
-        end
-    end
+       function format_wifi_encryption(info)
+               if info.wep == true then
+                       return translate("WEP")
+               elseif info.wpa > 0 then
+                       return "%s (%s/%s)" %{label[info.wpa], table.concat(info.auth_suites), table.concat(info.group_ciphers)}
+               elseif info.enabled then
+                       return translate("Unknown")
+               else
+                       return translate("Open")
+               end
+       end
 
-    function percent_wifi_signal(info)
-        local qc = info.quality or 0
-        local qm = info.quality_max or 0
-        if info.bssid and qc > 0 and qm > 0 then
-            return math.floor((100 / qm) * qc)
-        else
-            return 0
-        end
-    end
+       function percent_wifi_signal(info)
+               local qc = info.quality or 0
+               local qm = info.quality_max or 0
+               if info.bssid and qc > 0 and qm > 0 then
+                       return math.floor((100 / qm) * qc)
+               else
+                       return 0
+               end
+       end
 -%>
 
 <%+header%>
 
-
 <div class="cbi-map">
-<h2 name="content"><%:Wireless Scan%></h2>
-    <div class="cbi-section">
-        <div class="table cbi-section-table">
-            <div class="tr cbi-section-table-titles">
-                <div class="th left"><%:Uplink SSID%></div>
-                <div class="th left"><%:Uplink BSSID%></div>
-                <div class="th left"><%:Encryption%></div>
-                <div class="th left"><%:Signal strength%></div>
-            </div>
-            <% for i, net in ipairs(iw.scanlist or { }) do %>
-            <div class="tr cbi-section-table-row cbi-rowstyle-1">
-                <div class="td left">
-                    <%=net.ssid and utl.pcdata(net.ssid) or "<em>%s</em>" % translate("hidden")%>
-                </div>
-                <div class="td left">
-                    <%=net.bssid and utl.pcdata(net.bssid)%>
-                </div>
-                <div class="td left">
-                    <%=format_wifi_encryption(net.encryption)%>
-                </div>
-                <div class="td left">
-                    <%=percent_wifi_signal(net)%> %
-                </div>
-                <div class="td cbi-section-actions">
-                    <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/wifiadd')%>" method="post">
-                        <input type="hidden" name="token" value="<%=token%>"/>
-                        <input type="hidden" name="device" value="<%=utl.pcdata(dev)%>"/>
-                        <input type="hidden" name="ssid" value="<%=utl.pcdata(net.ssid)%>"/>
-                        <input type="hidden" name="bssid" value="<%=utl.pcdata(net.bssid)%>"/>
-                        <input type="hidden" name="wep" value="<%=net.encryption.wep and 1 or 0%>"/>
-                        <% if net.encryption.wpa then %>
-                            <input type="hidden" name="wpa_version" value="<%=net.encryption.wpa%>"/>
-                            <% for _, v in ipairs(net.encryption.auth_suites) do %><input type="hidden" name="wpa_suites" value="<%=v%>"/><% end %>
-                        <% end %>
-                        <input class="cbi-button cbi-button-apply" type="submit" value="<%:Add Uplink%>"/>
-                    </form>
-                </div>
-            </div>
-            <% end %>
-        </div>
-    </div>
-<div class="cbi-page-actions right">
-    <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/stations')%>" method="get">
-        <input class="cbi-button cbi-button-reset" type="submit" value="<%:Back to overview%>"/>
-    </form>
-    <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/wifiscan')%>" method="post">
-        <input type="hidden" name="token" value="<%=token%>"/>
-        <input type="hidden" name="device" value="<%=utl.pcdata(dev)%>"/>
-        <input class="cbi-button cbi-input-find" type="submit" value="<%:Repeat scan%>"/>
-    </form>
-</div>
+       <h3><%:Wireless Scan%></h3>
+       <div class="cbi-section">
+               <div class="table cbi-section-table">
+                       <div class="tr cbi-section-table-titles">
+                               <div class="th left"><%:Uplink SSID%></div>
+                               <div class="th left"><%:Uplink BSSID%></div>
+                               <div class="th left"><%:Encryption%></div>
+                               <div class="th left"><%:Signal strength%></div>
+                               <div class="th center"><%:Action%></div>
+                       </div>
+                       <%- for i, net in ipairs(iw.scanlist or { }) do -%>
+                       <div class="tr cbi-section-table-row cbi-rowstyle-1">
+                               <div class="td left">
+                                       <%=net.ssid and utl.pcdata(net.ssid) or "<em>%s</em>" % translate("hidden")%>
+                               </div>
+                               <div class="td left">
+                                       <%=net.bssid and utl.pcdata(net.bssid)%>
+                               </div>
+                               <div class="td left">
+                                       <%=format_wifi_encryption(net.encryption)%>
+                               </div>
+                               <div class="td left">
+                                       <%=percent_wifi_signal(net)%> %
+                               </div>
+                               <div class="td cbi-section-actions">
+                                       <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/wifiadd')%>" method="post">
+                                               <input type="hidden" name="token" value="<%=token%>"/>
+                                               <input type="hidden" name="device" value="<%=utl.pcdata(dev)%>"/>
+                                               <input type="hidden" name="ssid" value="<%=utl.pcdata(net.ssid)%>"/>
+                                               <input type="hidden" name="bssid" value="<%=utl.pcdata(net.bssid)%>"/>
+                                               <input type="hidden" name="wep" value="<%=net.encryption.wep and 1 or 0%>"/>
+                                               <%- if net.encryption.wpa then -%>
+                                                       <input type="hidden" name="wpa_version" value="<%=net.encryption.wpa%>"/>
+                                                       <%- for _, v in ipairs(net.encryption.auth_suites) do -%>
+                                                               <input type="hidden" name="wpa_suites" value="<%=v%>"/>
+                                                       <%- end -%>
+                                               <%- end -%>
+                                               <input class="cbi-button cbi-button-apply" type="submit" value="<%:Add Uplink%>"/>
+                                       </form>
+                               </div>
+                       </div>
+                       <%- end -%>
+               </div>
+       </div>
+       <div class="cbi-page-actions right">
+               <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/stations')%>" method="get">
+                       <input class="cbi-button cbi-button-reset" type="submit" value="<%:Back to overview%>"/>
+               </form>
+               <form class="inline" action="<%=luci.dispatcher.build_url('admin/services/travelmate/wifiscan')%>" method="post">
+                       <input type="hidden" name="token" value="<%=token%>"/>
+                       <input type="hidden" name="device" value="<%=utl.pcdata(dev)%>"/>
+                       <input class="cbi-button cbi-input-find" type="submit" value="<%:Repeat scan%>"/>
+               </form>
+       </div>
 </div>
 
 <%+footer%>