luci-app-travelmate: sync with travelmate 0.9.5 1465/head
authorDirk Brenken <dev@brenken.org>
Wed, 6 Dec 2017 16:05:40 +0000 (17:05 +0100)
committerDirk Brenken <dev@brenken.org>
Wed, 6 Dec 2017 16:05:40 +0000 (17:05 +0100)
* add support for hidden uplinks
* add support for wpa enterprise uplinks
* various small fixes

Signed-off-by: Dirk Brenken <dev@brenken.org>
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/overview_tab.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_add.lua
applications/luci-app-travelmate/luasrc/model/cbi/travelmate/wifi_edit.lua
applications/luci-app-travelmate/luasrc/view/travelmate/config_css.htm
applications/luci-app-travelmate/luasrc/view/travelmate/stations.htm
applications/luci-app-travelmate/luasrc/view/travelmate/wifi_scan.htm

index 27971df..2c8dfa2 100644 (file)
@@ -1,15 +1,15 @@
 -- Copyright 2017 Dirk Brenken (dev@brenken.org)
 -- This is free software, licensed under the Apache License, Version 2.0
 
 -- Copyright 2017 Dirk Brenken (dev@brenken.org)
 -- This is free software, licensed under the Apache License, Version 2.0
 
-local fs = require("nixio.fs")
-local uci = require("luci.model.uci").cursor()
-local json = require("luci.jsonc")
-local nw = require("luci.model.network").init()
-local fw = require("luci.model.firewall").init()
+local fs         = require("nixio.fs")
+local uci        = require("luci.model.uci").cursor()
+local json       = require("luci.jsonc")
+local nw         = require("luci.model.network").init()
+local fw         = require("luci.model.firewall").init()
 local trmiface = uci.get("travelmate", "global", "trm_iface") or "trm_wwan"
 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 "")
+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. ")
 
 m = Map("travelmate", translate("Travelmate"),
        translate("Configuration of the travelmate package to to enable travel router functionality. ")
@@ -22,20 +22,17 @@ function m.on_after_commit(self)
        luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate"))
 end
 
        luci.http.redirect(luci.dispatcher.build_url("admin", "services", "travelmate"))
 end
 
-s = m:section(NamedSection, "global", "travelmate")
-
 -- Interface Wizard
 
 if uplink == "" then
 -- Interface Wizard
 
 if uplink == "" then
-       dv = s:option(DummyValue, "", translate("Interface Wizard"))
-       dv.template = "cbi/nullsection"
+       ds = m:section(NamedSection, "global", "travelmate", translate("Interface Wizard"))
 
 
-       o = s:option(Value, "", translate("Uplink interface"))
+       o = ds:option(Value, "", translate("Uplink interface"))
        o.datatype = "and(uciname,rangelength(3,15))"
        o.default = trmiface
        o.rmempty = false
 
        o.datatype = "and(uciname,rangelength(3,15))"
        o.default = trmiface
        o.rmempty = false
 
-       btn = s:option(Button, "trm_iface", translate("Create Uplink Interface"),
+       btn = ds:option(Button, "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. This step has only to be done once."))
        btn.inputtitle = translate("Add 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. This step has only to be done once."))
        btn.inputtitle = translate("Add Interface")
@@ -67,6 +64,8 @@ end
 
 -- Main travelmate options
 
 
 -- Main travelmate options
 
+s = m:section(NamedSection, "global", "travelmate")
+
 o1 = s:option(Flag, "trm_enabled", translate("Enable travelmate"))
 o1.default = o1.disabled
 o1.rmempty = false
 o1 = s:option(Flag, "trm_enabled", translate("Enable travelmate"))
 o1.default = o1.disabled
 o1.rmempty = false
@@ -98,16 +97,11 @@ o4.default = 2
 o4.datatype = "range(1,90)"
 o4.rmempty = false
 
 o4.datatype = "range(1,90)"
 o4.rmempty = false
 
-o5 = s:option(Flag, "trm_debug", translate("Enable verbose debug logging"))
-o5.default = o5.disabled
-o5.rmempty = false
-
 -- Runtime information
 
 -- Runtime information
 
-ds = s:option(DummyValue, "_dummy", translate("Runtime information"))
-ds.template = "cbi/nullsection"
+ds = m:section(NamedSection, "global", "travelmate", translate("Runtime Information"))
 
 
-dv1 = s:option(DummyValue, "status", translate("Online Status"))
+dv1 = ds:option(DummyValue, "status", translate("Online Status"))
 dv1.template = "travelmate/runtime"
 if parse == nil then
        dv1.value = translate("n/a")
 dv1.template = "travelmate/runtime"
 if parse == nil then
        dv1.value = translate("n/a")
@@ -117,7 +111,7 @@ else
        dv1.value = translate("not connected")
 end
 
        dv1.value = translate("not connected")
 end
 
-dv2 = s:option(DummyValue, "travelmate_version", translate("Travelmate version"))
+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")
 dv2.template = "travelmate/runtime"
 if parse ~= nil then
        dv2.value = parse.data.travelmate_version or translate("n/a")
@@ -125,15 +119,15 @@ else
        dv2.value = translate("n/a")
 end
 
        dv2.value = translate("n/a")
 end
 
-dv3 = s:option(DummyValue, "station_ssid", translate("Station SSID"))
+dv3 = ds:option(DummyValue, "station_id", translate("Station ID (SSID/BSSID)"))
 dv3.template = "travelmate/runtime"
 if parse ~= nil then
 dv3.template = "travelmate/runtime"
 if parse ~= nil then
-       dv3.value = parse.data.station_ssid or translate("n/a")
+       dv3.value = parse.data.station_id or translate("n/a")
 else
        dv3.value = translate("n/a")
 end
 
 else
        dv3.value = translate("n/a")
 end
 
-dv4 = s:option(DummyValue, "station_interface", translate("Station Interface"))
+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")
 dv4.template = "travelmate/runtime"
 if parse ~= nil then
        dv4.value = parse.data.station_interface or translate("n/a")
@@ -141,7 +135,7 @@ else
        dv4.value = translate("n/a")
 end
 
        dv4.value = translate("n/a")
 end
 
-dv5 = s:option(DummyValue, "station_radio", translate("Station Radio"))
+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")
 dv5.template = "travelmate/runtime"
 if parse ~= nil then
        dv5.value = parse.data.station_radio or translate("n/a")
@@ -149,7 +143,7 @@ else
        dv5.value = translate("n/a")
 end
 
        dv5.value = translate("n/a")
 end
 
-dv6 = s:option(DummyValue, "last_rundate", translate("Last rundate"))
+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")
 dv6.template = "travelmate/runtime"
 if parse ~= nil then
        dv6.value = parse.data.last_rundate or translate("n/a")
@@ -162,28 +156,32 @@ end
 e = m:section(NamedSection, "global", "travelmate", translate("Extra options"),
 translate("Options for further tweaking in case the defaults are not suitable for you."))
 
 e = m:section(NamedSection, "global", "travelmate", translate("Extra options"),
 translate("Options for further tweaking in case the defaults are not suitable for you."))
 
-e1 = e:option(Value, "trm_radio", translate("Radio selection"),
+e1 = e:option(Flag, "trm_debug", translate("Enable verbose debug logging"))
+e1.default = e1.disabled
+e1.rmempty = false
+
+e2 = e:option(Value, "trm_radio", translate("Radio selection"),
        translate("Restrict travelmate to a dedicated radio, e.g. 'radio0'."))
        translate("Restrict travelmate to a dedicated radio, e.g. 'radio0'."))
-e1.datatype = "and(uciname,rangelength(6,6))"
-e1.rmempty = true
+e2.datatype = "and(uciname,rangelength(6,6))"
+e2.rmempty = true
 
 
-e2 = e:option(Value, "trm_maxretry", translate("Connection Limit"),
+e3 = e:option(Value, "trm_maxretry", translate("Connection Limit"),
        translate("How many times should travelmate try to connect to an Uplink. ")
        .. translate("To disable this feature set it to '0' which means unlimited retries."))
        translate("How many times should travelmate try to connect to an Uplink. ")
        .. translate("To disable this feature set it to '0' which means unlimited retries."))
-e2.default = 3
-e2.datatype = "range(0,30)"
-e2.rmempty = false
+e3.default = 3
+e3.datatype = "range(0,30)"
+e3.rmempty = false
 
 
-e3 = e:option(Value, "trm_maxwait", translate("Interface Timeout"),
+e4 = e:option(Value, "trm_maxwait", translate("Interface Timeout"),
        translate("How long should travelmate wait for a successful wlan interface reload."))
        translate("How long should travelmate wait for a successful wlan interface reload."))
-e3.default = 30
-e3.datatype = "range(5,60)"
-e3.rmempty = false
+e4.default = 30
+e4.datatype = "range(5,60)"
+e4.rmempty = false
 
 
-e4 = e:option(Value, "trm_timeout", translate("Overall Timeout"),
+e5 = e:option(Value, "trm_timeout", translate("Overall Timeout"),
        translate("Timeout in seconds between retries in 'automatic' mode."))
        translate("Timeout in seconds between retries in 'automatic' mode."))
-e4.default = 60
-e4.datatype = "range(60,300)"
-e4.rmempty = false
+e5.default = 60
+e5.datatype = "range(60,300)"
+e5.rmempty = false
 
 return m
 
 return m
index dcfa17c..921e1b8 100644 (file)
@@ -1,10 +1,11 @@
 -- Copyright 2017 Dirk Brenken (dev@brenken.org)
 -- This is free software, licensed under the Apache License, Version 2.0
 
 -- Copyright 2017 Dirk Brenken (dev@brenken.org)
 -- This is free software, licensed under the Apache License, Version 2.0
 
-local fs = require("nixio.fs")
-local uci = require("luci.model.uci").cursor()
-local http = require("luci.http")
+local fs       = require("nixio.fs")
+local uci      = require("luci.model.uci").cursor()
+local http     = require("luci.http")
 local trmiface = uci.get("travelmate", "global", "trm_iface") or "trm_wwan"
 local trmiface = uci.get("travelmate", "global", "trm_iface") or "trm_wwan"
+local val      = ""
 
 m = SimpleForm("add", translate("Add Wireless Uplink Configuration"))
 m.submit = translate("Save")
 
 m = SimpleForm("add", translate("Add Wireless Uplink Configuration"))
 m.submit = translate("Save")
@@ -18,6 +19,7 @@ end
 m.hidden = {
        device      = http.formvalue("device"),
        ssid        = http.formvalue("ssid"),
 m.hidden = {
        device      = http.formvalue("device"),
        ssid        = http.formvalue("ssid"),
+       bssid       = http.formvalue("bssid"),
        wep         = http.formvalue("wep"),
        wpa_suites  = http.formvalue("wpa_suites"),
        wpa_version = http.formvalue("wpa_version")
        wep         = http.formvalue("wep"),
        wpa_suites  = http.formvalue("wpa_suites"),
        wpa_version = http.formvalue("wpa_version")
@@ -25,23 +27,47 @@ m.hidden = {
 
 if m.hidden.ssid ~= "" then
        wssid = m:field(Value, "ssid", translate("SSID"))
 
 if m.hidden.ssid ~= "" then
        wssid = m:field(Value, "ssid", translate("SSID"))
-       wssid.default = m.hidden.ssid
+       wssid.datatype = "rangelength(1,32)"
+       wssid.default = m.hidden.ssid or ""
 else
        wssid = m:field(Value, "ssid", translate("SSID (hidden)"))
 end
 
 else
        wssid = m:field(Value, "ssid", translate("SSID (hidden)"))
 end
 
+bssid = m:field(Value, "bssid", translate("BSSID"))
+bssid.datatype = "macaddr"
+bssid.default = m.hidden.bssid or ""
+
 if (tonumber(m.hidden.wep) or 0) == 1 then
        wkey = m:field(Value, "key", translate("WEP passphrase"),
                translate("Specify the secret encryption key here."))
        wkey.password = true
        wkey.datatype = "wepkey"
 if (tonumber(m.hidden.wep) or 0) == 1 then
        wkey = m:field(Value, "key", translate("WEP passphrase"),
                translate("Specify the secret encryption key here."))
        wkey.password = true
        wkey.datatype = "wepkey"
-elseif (tonumber(m.hidden.wpa_version) or 0) > 0 and
-       (m.hidden.wpa_suites == "PSK" or m.hidden.wpa_suites == "PSK2")
-then
-       wkey = m:field(Value, "key", translate("WPA passphrase"),
-               translate("Specify the secret encryption key here."))
-       wkey.password = true
-       wkey.datatype = "wpakey"
+elseif (tonumber(m.hidden.wpa_version) or 0) > 0 then
+       if m.hidden.wpa_suites == "PSK" or m.hidden.wpa_suites == "PSK2" then
+               wkey = m:field(Value, "key", translate("WPA passphrase"),
+                       translate("Specify the secret encryption key here."))
+               wkey.password = true
+               wkey.datatype = "wpakey"
+       elseif m.hidden.wpa_suites == "802.1X" then
+               eaptype = m:field(ListValue, "eap_type", translate("EAP-Method"))
+               eaptype:value("TLS")
+               eaptype:value("TTLS")
+               eaptype:value("PEAP")
+               eaptype.default = "PEAP"
+
+               authentication = m:field(ListValue, "auth", translate("Authentication"))
+               authentication:value("PAP")
+               authentication:value("CHAP")
+               authentication:value("MSCHAP")
+               authentication:value("MSCHAPV2")
+               authentication.default = "MSCHAPV2"
+
+               ident = m:field(Value, "identity", translate("Identity"))
+
+               pass = m:field(Value, "password", translate("Password"))
+               pass.datatype = "wpakey"
+               pass.password = true
+       end
 end
 
 function wssid.write(self, section, value)
 end
 
 function wssid.write(self, section, value)
@@ -50,15 +76,38 @@ function wssid.write(self, section, value)
                network  = trmiface,
                device   = m.hidden.device,
                ssid     = wssid:formvalue(section),
                network  = trmiface,
                device   = m.hidden.device,
                ssid     = wssid:formvalue(section),
+               bssid    = bssid:formvalue(section),
                disabled = "1"
        })
                disabled = "1"
        })
+       if wkey ~= nil then
+               val = wkey:formvalue(section)
+               if val == "" then
+                       val = "changeme"
+               end
+       end
        if (tonumber(m.hidden.wep) or 0) == 1 then
                uci:set("wireless", newsection, "encryption", "wep-open")
                uci:set("wireless", newsection, "key", "1")
        if (tonumber(m.hidden.wep) or 0) == 1 then
                uci:set("wireless", newsection, "encryption", "wep-open")
                uci:set("wireless", newsection, "key", "1")
-               uci:set("wireless", newsection, "key1", wkey:formvalue(section))
+               uci:set("wireless", newsection, "key1", val)
        elseif (tonumber(m.hidden.wpa_version) or 0) > 0 then
        elseif (tonumber(m.hidden.wpa_version) or 0) > 0 then
-               uci:set("wireless", newsection, "encryption", "psk2")
-               uci:set("wireless", newsection, "key", wkey:formvalue(section))
+               if m.hidden.wpa_suites == "PSK" or m.hidden.wpa_suites == "PSK2" then
+                       uci:set("wireless", newsection, "encryption", "psk2")
+                       uci:set("wireless", newsection, "key", val)
+               elseif m.hidden.wpa_suites == "802.1X" then
+                       uci:set("wireless", newsection, "encryption", "wpa2")
+                       uci:set("wireless", newsection, "eap_type", eaptype:formvalue(section))
+                       uci:set("wireless", newsection, "auth", authentication:formvalue(section))
+                       val = ident:formvalue(section)
+                       if val == "" then
+                               val = "changeme"
+                       end
+                       uci:set("wireless", newsection, "identity", val)
+                       val = pass:formvalue(section)
+                       if val == "" then
+                               val = "changeme"
+                       end
+                       uci:set("wireless", newsection, "password", val)
+               end
        else
                uci:set("wireless", newsection, "encryption", "none")
        end
        else
                uci:set("wireless", newsection, "encryption", "none")
        end
index c60ff22..1baca5b 100644 (file)
@@ -1,9 +1,10 @@
 -- Copyright 2017 Dirk Brenken (dev@brenken.org)
 -- This is free software, licensed under the Apache License, Version 2.0
 
 -- Copyright 2017 Dirk Brenken (dev@brenken.org)
 -- This is free software, licensed under the Apache License, Version 2.0
 
-local fs = require("nixio.fs")
-local uci = require("luci.model.uci").cursor()
+local fs   = require("nixio.fs")
+local uci  = require("luci.model.uci").cursor()
 local http = require("luci.http")
 local http = require("luci.http")
+local val  = ""
 
 m = SimpleForm("edit", translate("Edit Wireless Uplink Configuration"))
 m.submit = translate("Save")
 
 m = SimpleForm("edit", translate("Edit Wireless Uplink Configuration"))
 m.submit = translate("Save")
@@ -21,8 +22,15 @@ m.hidden = {
 local s = uci:get_all("wireless", m.hidden.cfg)
 if s ~= nil then
        wssid = m:field(Value, "ssid", translate("SSID"))
 local s = uci:get_all("wireless", m.hidden.cfg)
 if s ~= nil then
        wssid = m:field(Value, "ssid", translate("SSID"))
-       wssid.default = s.ssid
        wssid.datatype = "rangelength(1,32)"
        wssid.datatype = "rangelength(1,32)"
+       wssid.default = s.ssid
+       bssid = m:field(Value, "bssid", translate("BSSID"))
+       bssid.datatype = "macaddr"
+       bssid.default = s.bssid
+       if s.identity then
+               ident = m:field(Value, "identity", translate("Identity"))
+               ident.default = s.identity
+       end
        if s.encryption and s.key then
                wkey = m:field(Value, "key", translatef("Passphrase (%s)", s.encryption))
        elseif s.encryption and s.password then
        if s.encryption and s.key then
                wkey = m:field(Value, "key", translatef("Passphrase (%s)", s.encryption))
        elseif s.encryption and s.password then
@@ -43,10 +51,25 @@ end
 
 function wssid.write(self, section, value)
        uci:set("wireless", m.hidden.cfg, "ssid", wssid:formvalue(section))
 
 function wssid.write(self, section, value)
        uci:set("wireless", m.hidden.cfg, "ssid", wssid:formvalue(section))
-       if s.encryption and s.key then
-               uci:set("wireless", m.hidden.cfg, "key", wkey:formvalue(section))
-       elseif s.encryption and s.password then
-               uci:set("wireless", m.hidden.cfg, "password", wkey:formvalue(section))
+       uci:set("wireless", m.hidden.cfg, "bssid", bssid:formvalue(section))
+       if s.identity then
+               val = ident:formvalue(section)
+               if val == "" then
+                       val = "changeme"
+               end
+               uci:set("wireless", m.hidden.cfg, "identity", val)
+       end
+
+       if s.encryption and s.encryption ~= "none" then
+               val = wkey:formvalue(section)
+               if val == "" then
+                       val = "changeme"
+               end
+               if s.key then
+                       uci:set("wireless", m.hidden.cfg, "key", val)
+               elseif s.password then
+                       uci:set("wireless", m.hidden.cfg, "password", val)
+               end
        end
        uci:save("wireless")
        uci:commit("wireless")
        end
        uci:save("wireless")
        uci:commit("wireless")
index 53493a1..2233a15 100644 (file)
@@ -6,5 +6,8 @@
                font-size: 12px;
                font-family: monospace;
                resize: none;
                font-size: 12px;
                font-family: monospace;
                resize: none;
+               white-space: pre;
+               overflow-wrap: normal;
+               overflow-x: scroll;
        }
 </style>
        }
 </style>
index 51db286..402b1c1 100644 (file)
@@ -22,6 +22,7 @@ This is free software, licensed under the Apache License, Version 2.0
     <tr class="cbi-section-table-titles">
       <th class="cbi-section-table-cell" style="text-align:left"><%:Device%></th>
       <th class="cbi-section-table-cell" style="text-align:left"><%:SSID%></th>
     <tr class="cbi-section-table-titles">
       <th class="cbi-section-table-cell" style="text-align:left"><%:Device%></th>
       <th class="cbi-section-table-cell" style="text-align:left"><%:SSID%></th>
+      <th class="cbi-section-table-cell" style="text-align:left"><%:BSSID%></th>
       <th class="cbi-section-table-cell" style="text-align:left"><%:Encryption%></th>
       <th class="cbi-section-table-cell" style="text-align:center" colspan="2"><%:Actions%></th>
     </tr>
       <th class="cbi-section-table-cell" style="text-align:left"><%:Encryption%></th>
       <th class="cbi-section-table-cell" style="text-align:center" colspan="2"><%:Actions%></th>
     </tr>
@@ -32,6 +33,7 @@ This is free software, licensed under the Apache License, Version 2.0
       local section = s['.name'] or ""
       local device = s.device or ""
       local ssid = s.ssid or ""
       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 = "color:#000000"
       local encryption = s.encryption or ""
       local disabled = s.disabled or ""
       local style = "color:#000000"
@@ -42,6 +44,7 @@ This is free software, licensed under the Apache License, Version 2.0
     <tr class="cbi-section-table-row cbi-rowstyle-1" style="<%=style%>">
       <td style="text-align:left"><%=device%></td>
       <td style="text-align:left"><%=ssid%></td>
     <tr class="cbi-section-table-row cbi-rowstyle-1" style="<%=style%>">
       <td style="text-align:left"><%=device%></td>
       <td style="text-align:left"><%=ssid%></td>
+      <td style="text-align:left"><%=bssid%></td>
       <td style="text-align:left"><%=encryption%></td>
       <td class="cbi-value-field" style="width:70px;text-align:right">
         <input class="cbi-button cbi-button-up" type="button" value="" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>;dir=up'" alt="<%:Move up%>" title="<%:Move up%>"/>
       <td style="text-align:left"><%=encryption%></td>
       <td class="cbi-value-field" style="width:70px;text-align:right">
         <input class="cbi-button cbi-button-up" type="button" value="" onclick="location.href='<%=luci.dispatcher.build_url('admin/services/travelmate/wifiorder')%>?cfg=<%=section%>;dir=up'" alt="<%:Move up%>" title="<%:Move up%>"/>
index 053b4ba..68ca63f 100644 (file)
@@ -17,7 +17,7 @@ This is free software, licensed under the Apache License, Version 2.0
         if info.wep == true then
             return translate("WEP")
         elseif info.wpa > 0 then
         if info.wep == true then
             return translate("WEP")
         elseif info.wpa > 0 then
-            return translate("WPA / WPA2")
+            return translate("WPA/WPA2 - " .. table.concat(info.auth_suites))
         elseif info.enabled then
             return translate("Unknown")
         else
         elseif info.enabled then
             return translate("Unknown")
         else
@@ -28,7 +28,7 @@ This is free software, licensed under the Apache License, Version 2.0
     function percent_wifi_signal(info)
         local qc = info.quality or 0
         local qm = info.quality_max or 0
     function percent_wifi_signal(info)
         local qc = info.quality or 0
         local qm = info.quality_max or 0
-        if info.ssid and qc > 0 and qm > 0 then
+        if info.bssid and qc > 0 and qm > 0 then
             return math.floor((100 / qm) * qc)
         else
             return 0
             return math.floor((100 / qm) * qc)
         else
             return 0
@@ -44,13 +44,17 @@ This is free software, licensed under the Apache License, Version 2.0
         <table class="cbi-section-table" style="empty-cells:hide">
             <tr class="cbi-section-table-titles">
                 <th class="cbi-section-table-cell" style="text-align:left"><%:Uplink SSID%></th>
         <table class="cbi-section-table" style="empty-cells:hide">
             <tr class="cbi-section-table-titles">
                 <th class="cbi-section-table-cell" style="text-align:left"><%:Uplink SSID%></th>
+                <th class="cbi-section-table-cell" style="text-align:left"><%:Uplink BSSID%></th>
                 <th class="cbi-section-table-cell" style="text-align:left"><%:Encryption%></th>
                 <th class="cbi-section-table-cell" style="text-align:left" colspan="2"><%:Signal strength%></th>
             </tr>
             <% for i, net in ipairs(iw.scanlist or { }) do %>
             <tr class="cbi-section-table-row cbi-rowstyle-1">
                 <td class="cbi-value-field" style="text-align:left">
                 <th class="cbi-section-table-cell" style="text-align:left"><%:Encryption%></th>
                 <th class="cbi-section-table-cell" style="text-align:left" colspan="2"><%:Signal strength%></th>
             </tr>
             <% for i, net in ipairs(iw.scanlist or { }) do %>
             <tr class="cbi-section-table-row cbi-rowstyle-1">
                 <td class="cbi-value-field" style="text-align:left">
-                    <strong><%=net.ssid and utl.pcdata(net.ssid) or "<em>%s</em>" % translate("hidden")%></strong>
+                    <%=net.ssid and utl.pcdata(net.ssid) or "<em>%s</em>" % translate("hidden")%>
+                </td>
+                <td class="cbi-value-field" style="text-align:left">
+                    <%=net.bssid and utl.pcdata(net.bssid)%>
                 </td>
                 <td class="cbi-value-field" style="text-align:left">
                     <%=format_wifi_encryption(net.encryption)%>
                 </td>
                 <td class="cbi-value-field" style="text-align:left">
                     <%=format_wifi_encryption(net.encryption)%>
@@ -63,6 +67,7 @@ This is free software, licensed under the Apache License, Version 2.0
                         <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="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%>"/>
                         <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%>"/>