luci-mod-status: add WPS control for wifi info 4508/head
authorAnsuel Smith <ansuelsmth@gmail.com>
Sat, 10 Oct 2020 01:45:06 +0000 (03:45 +0200)
committerAnsuel Smith <ansuelsmth@gmail.com>
Sat, 17 Oct 2020 15:47:50 +0000 (17:47 +0200)
Add a way to trigger and get the WPS Push Button status from the webui if supported.

Fixes: #1072
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js
modules/luci-mod-status/root/usr/share/rpcd/acl.d/luci-mod-status.json

index deb6f8609f681867c7602d000e7269f919de4ba1..928b5959ba6054a65bc1ac843ceafd2929e349e3 100644 (file)
@@ -2,90 +2,44 @@
 'require baseclass';
 'require dom';
 'require network';
+'require uci';
+'require fs';
 'require rpc';
 
-var callSessionAccess = rpc.declare({
-       object: 'session',
-       method: 'access',
-       params: [ 'scope', 'object', 'function' ],
-       expect: { 'access': false }
-});
-
-function renderbox(radio, networks) {
-       var chan = null,
-           freq = null,
-           rate = null,
-           badges = [];
-
-       for (var i = 0; i < networks.length; i++) {
-               var net = networks[i],
-                   is_assoc = (net.getBSSID() != '00:00:00:00:00:00' && net.getChannel() && !net.isDisabled()),
-                   quality = net.getSignalPercent();
-
-               var icon;
-               if (net.isDisabled())
-                       icon = L.resource('icons/signal-none.png');
-               else if (quality <= 0)
-                       icon = L.resource('icons/signal-0.png');
-               else if (quality < 25)
-                       icon = L.resource('icons/signal-0-25.png');
-               else if (quality < 50)
-                       icon = L.resource('icons/signal-25-50.png');
-               else if (quality < 75)
-                       icon = L.resource('icons/signal-50-75.png');
-               else
-                       icon = L.resource('icons/signal-75-100.png');
-
-               var badge = renderBadge(
-                       icon,
-                       '%s: %d dBm / %s: %d%%'.format(_('Signal'), net.getSignal(), _('Quality'), quality),
-                       _('SSID'), net.getActiveSSID() || '?',
-                       _('Mode'), net.getActiveMode(),
-                       _('BSSID'), is_assoc ? (net.getActiveBSSID() || '-') : null,
-                       _('Encryption'), is_assoc ? net.getActiveEncryption() : null,
-                       _('Associations'), is_assoc ? (net.assoclist.length || '-') : null,
-                       null, is_assoc ? null : E('em', net.isDisabled() ? _('Wireless is disabled') : _('Wireless is not associated')));
-
-               badges.push(badge);
-
-               chan = (chan != null) ? chan : net.getChannel();
-               freq = (freq != null) ? freq : net.getFrequency();
-               rate = (rate != null) ? rate : net.getBitRate();
-       }
-
-       return E('div', { class: 'ifacebox' }, [
-               E('div', { class: 'ifacebox-head center ' + (radio.isUp() ? 'active' : '') },
-                       E('strong', radio.getName())),
-               E('div', { class: 'ifacebox-body left' }, [
-                       L.itemlist(E('span'), [
-                               _('Type'), radio.getI18n().replace(/^Generic | Wireless Controller .+$/g, ''),
-                               _('Channel'), chan ? '%d (%.3f %s)'.format(chan, freq, _('GHz')) : '-',
-                               _('Bitrate'), rate ? '%d %s'.format(rate, _('Mbit/s')) : '-'
-                       ]),
-                       E('div', {}, badges)
-               ])
-       ]);
-}
-
-function wifirate(rt) {
-       var s = '%.1f\xa0%s, %d\xa0%s'.format(rt.rate / 1000, _('Mbit/s'), rt.mhz, _('MHz')),
-           ht = rt.ht, vht = rt.vht,
-               mhz = rt.mhz, nss = rt.nss,
-               mcs = rt.mcs, sgi = rt.short_gi;
-
-       if (ht || vht) {
-               if (vht) s += ', VHT-MCS\xa0%d'.format(mcs);
-               if (nss) s += ', VHT-NSS\xa0%d'.format(nss);
-               if (ht)  s += ', MCS\xa0%s'.format(mcs);
-               if (sgi) s += ', ' + _('Short GI').replace(/ /g, '\xa0');
-       }
-
-       return s;
-}
-
 return baseclass.extend({
        title: _('Wireless'),
 
+       WPSTranslateTbl: {
+               Disabled: _('Disabled'),
+               Active: _('Active'),
+               'Timed-out': _('Timed-out'),
+               Overlap: _('Overlap'),
+               Unknown: _('Unknown')
+       },
+
+       callSessionAccess: rpc.declare({
+               object: 'session',
+               method: 'access',
+               params: [ 'scope', 'object', 'function' ],
+               expect: { 'access': false }
+       }),
+
+       wifirate: function(rt) {
+               var s = '%.1f\xa0%s, %d\xa0%s'.format(rt.rate / 1000, _('Mbit/s'), rt.mhz, _('MHz')),
+                   ht = rt.ht, vht = rt.vht,
+                       mhz = rt.mhz, nss = rt.nss,
+                       mcs = rt.mcs, sgi = rt.short_gi;
+       
+               if (ht || vht) {
+                       if (vht) s += ', VHT-MCS\xa0%d'.format(mcs);
+                       if (nss) s += ', VHT-NSS\xa0%d'.format(nss);
+                       if (ht)  s += ', MCS\xa0%s'.format(mcs);
+                       if (sgi) s += ', ' + _('Short GI').replace(/ /g, '\xa0');
+               }
+       
+               return s;
+       },
+
        handleDelClient: function(wifinet, mac, ev) {
                dom.parent(ev.currentTarget, '.tr').style.opacity = 0.5;
                ev.currentTarget.classList.add('spinning');
@@ -95,25 +49,143 @@ return baseclass.extend({
                wifinet.disconnectClient(mac, true, 5, 60000);
        },
 
+       handleGetWPSStatus: function(wifinet) {
+               return rpc.declare({
+                       object: 'hostapd.%s'.format(wifinet),
+                       method: 'wps_status',
+               })()
+       },
+
+       handleCallWPS: function(wifinet, ev) {
+               ev.currentTarget.classList.add('spinning');
+               ev.currentTarget.disabled = true;
+               ev.currentTarget.blur();
+
+               return rpc.declare({
+                       object: 'hostapd.%s'.format(wifinet),
+                       method: 'wps_start',
+               })();
+       },
+
+       handleCancelWPS: function(wifinet, ev) {
+               ev.currentTarget.classList.add('spinning');
+               ev.currentTarget.disabled = true;
+               ev.currentTarget.blur();
+
+               return rpc.declare({
+                       object: 'hostapd.%s'.format(wifinet),
+                       method: 'wps_cancel',
+               })();
+       },
+
+       renderbox: function(radio, networks) {
+               var chan = null,
+                   freq = null,
+                   rate = null,
+                   badges = [];
+       
+               for (var i = 0; i < networks.length; i++) {
+                       var net = networks[i],
+                           is_assoc = (net.getBSSID() != '00:00:00:00:00:00' && net.getChannel() && !net.isDisabled()),
+                           quality = net.getSignalPercent();
+       
+                       var icon;
+                       if (net.isDisabled())
+                               icon = L.resource('icons/signal-none.png');
+                       else if (quality <= 0)
+                               icon = L.resource('icons/signal-0.png');
+                       else if (quality < 25)
+                               icon = L.resource('icons/signal-0-25.png');
+                       else if (quality < 50)
+                               icon = L.resource('icons/signal-25-50.png');
+                       else if (quality < 75)
+                               icon = L.resource('icons/signal-50-75.png');
+                       else
+                               icon = L.resource('icons/signal-75-100.png');
+
+                       var WPS_button;
+
+                       if (this.isWPSEnabled[net.sid]) {
+                               if (net.wps_status == 'Active') {
+                                       WPS_button = E('button', {
+                                               'class' : 'cbi-button cbi-button-remove',
+                                               'click': L.bind(this.handleCancelWPS, this, net.getIfname()),
+                                       }, [ _('Stop WPS') ])
+                               } else {
+                                       WPS_button = E('button', {
+                                               'class' : 'cbi-button cbi-button-apply',
+                                               'click': L.bind(this.handleCallWPS, this, net.getIfname()),
+                                       }, [ _('Start WPS') ])
+                               }
+                       }
+       
+                       var badge = renderBadge(
+                               icon,
+                               '%s: %d dBm / %s: %d%%'.format(_('Signal'), net.getSignal(), _('Quality'), quality),
+                               _('SSID'), net.getActiveSSID() || '?',
+                               _('Mode'), net.getActiveMode(),
+                               _('BSSID'), is_assoc ? (net.getActiveBSSID() || '-') : null,
+                               _('Encryption'), is_assoc ? net.getActiveEncryption() : null,
+                               _('Associations'), is_assoc ? (net.assoclist.length || '-') : null,
+                               null, is_assoc ? null : E('em', net.isDisabled() ? _('Wireless is disabled') : _('Wireless is not associated')),
+                               _('WPS status'), this.WPSTranslateTbl[net.wps_status],
+                               '', WPS_button
+                       );
+       
+                       badges.push(badge);
+       
+                       chan = (chan != null) ? chan : net.getChannel();
+                       freq = (freq != null) ? freq : net.getFrequency();
+                       rate = (rate != null) ? rate : net.getBitRate();
+               }
+       
+               return E('div', { class: 'ifacebox' }, [
+                       E('div', { class: 'ifacebox-head center ' + (radio.isUp() ? 'active' : '') },
+                               E('strong', radio.getName())),
+                       E('div', { class: 'ifacebox-body left' }, [
+                               L.itemlist(E('span'), [
+                                       _('Type'), radio.getI18n().replace(/^Generic | Wireless Controller .+$/g, ''),
+                                       _('Channel'), chan ? '%d (%.3f %s)'.format(chan, freq, _('GHz')) : '-',
+                                       _('Bitrate'), rate ? '%d %s'.format(rate, _('Mbit/s')) : '-',
+                               ]),
+                               E('div', {}, badges)
+                       ])
+               ]);
+       },
+
+       isWPSEnabled: {},
+
        load: function() {
                return Promise.all([
                        network.getWifiDevices(),
                        network.getWifiNetworks(),
                        network.getHostHints(),
-                       callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'read'),
-                       callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'write')
-               ]).then(function(radios_networks_hints) {
-                       var tasks = [];
-
-                       for (var i = 0; i < radios_networks_hints[1].length; i++)
-                               tasks.push(L.resolveDefault(radios_networks_hints[1][i].getAssocList(), []).then(L.bind(function(net, list) {
+                       this.callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'read'),
+                       this.callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'write'),
+                       uci.load('wireless')
+               ]).then(L.bind(function(data) {
+                       var tasks = [],
+                           radios_networks_hints = data[1],
+                           hasWPS = L.hasSystemFeature('hostapd', 'wps');
+
+                       for (var i = 0; i < radios_networks_hints.length; i++) {
+                               tasks.push(L.resolveDefault(radios_networks_hints[i].getAssocList(), []).then(L.bind(function(net, list) {
                                        net.assoclist = list.sort(function(a, b) { return a.mac > b.mac });
-                               }, this, radios_networks_hints[1][i])));
+                               }, this, radios_networks_hints[i])));
+
+                               if (hasWPS && uci.get('wireless', radios_networks_hints[i].sid, 'wps_pushbutton') == '1') {
+                                       this.isWPSEnabled[radios_networks_hints[i].sid] = true;
+                                       tasks.push(L.resolveDefault(this.handleGetWPSStatus(radios_networks_hints[i].getIfname()), null)
+                                               .then(L.bind(function(net, data) {
+                                                       net.wps_status = data ? data.pbc_status : _('No Data');
+                                       }, this, radios_networks_hints[i])));
+                               }
+                       }
 
                        return Promise.all(tasks).then(function() {
-                               return radios_networks_hints;
+                               return data;
                        });
-               });
+               }, this));
        },
 
        render: function(data) {
@@ -127,7 +199,7 @@ return baseclass.extend({
                var table = E('div', { 'class': 'network-status-table' });
 
                for (var i = 0; i < radios.sort(function(a, b) { a.getName() > b.getName() }).length; i++)
-                       table.appendChild(renderbox(radios[i],
+                       table.appendChild(this.renderbox(radios[i],
                                networks.filter(function(net) { return net.getWifiDeviceName() == radios[i].getName() })));
 
                if (!table.lastElementChild)
@@ -215,9 +287,9 @@ return baseclass.extend({
                                                ])
                                        ]),
                                        E('span', {}, [
-                                               E('span', wifirate(bss.rx)),
+                                               E('span', this.wifirate(bss.rx)),
                                                E('br'),
-                                               E('span', wifirate(bss.tx))
+                                               E('span', this.wifirate(bss.tx))
                                        ])
                                ];
 
index 05569d76039fb154370af6554359a19593decc23..db52c731374a384eb24bf4ebe1205ab32433eba2 100644 (file)
                },
                "write": {
                        "ubus": {
-                               "hostapd.*": [ "del_client" ]
+                               "hostapd.*": [ "del_client", "wps_start", "wps_cancel", "wps_status" ]
                        }
                }
        }