5 function renderbox(radio
) {
6 var net0
= radio
.networks
[0],
7 chan
= net0
? net0
.getChannel() : null,
8 freq
= net0
? net0
.getFrequency() : null,
9 rate
= net0
? net0
.getBitRate() : null,
12 for (var i
= 0; i
< radio
.networks
.length
; i
++) {
13 var net
= radio
.networks
[i
],
14 is_assoc
= (net
.getBSSID() != '00:00:00:00:00:00' && net
.getChannel() && !net
.isDisabled()),
15 quality
= net
.getSignalPercent();
19 icon
= L
.resource('icons/signal-none.png');
20 else if (quality
<= 0)
21 icon
= L
.resource('icons/signal-0.png');
22 else if (quality
< 25)
23 icon
= L
.resource('icons/signal-0-25.png');
24 else if (quality
< 50)
25 icon
= L
.resource('icons/signal-25-50.png');
26 else if (quality
< 75)
27 icon
= L
.resource('icons/signal-50-75.png');
29 icon
= L
.resource('icons/signal-75-100.png');
31 var badge
= renderBadge(
33 '%s: %d dBm / %s: %d%%'.format(_('Signal'), net
.getSignal(), _('Quality'), quality
),
34 _('SSID'), net
.getActiveSSID() || '?',
35 _('Mode'), net
.getActiveMode(),
36 _('BSSID'), is_assoc
? (net
.getActiveBSSID() || '-') : null,
37 _('Encryption'), is_assoc
? net
.getActiveEncryption() : null,
38 _('Associations'), is_assoc
? (net
.assoclist
.length
|| '-') : null,
39 null, is_assoc
? null : E('em', net
.isDisabled() ? _('Wireless is disabled') : _('Wireless is not associated')));
41 badge
.lastElementChild
.style
.overflow
= 'hidden';
42 badge
.lastElementChild
.style
.textOverflow
= 'ellipsis';
47 return E('div', { class: 'ifacebox' }, [
48 E('div', { class: 'ifacebox-head center ' + (radio
.isUp() ? 'active' : '') },
49 E('strong', radio
.getName())),
50 E('div', { class: 'ifacebox-body left' }, [
51 L
.itemlist(E('span'), [
52 _('Type'), radio
.getI18n().replace(/^Generic | Wireless Controller .+$/g, ''),
53 _('Channel'), chan
? '%d (%.3f %s)'.format(chan
, freq
, _('GHz')) : '-',
54 _('Bitrate'), rate
? '%d %s'.format(rate
, _('Mbit/s')) : '-'
61 function wifirate(rt
) {
62 var s
= '%.1f %s, %d%s'.format(rt
.rate
/ 1000, _('Mbit/s'), rt
.mhz
, _('MHz')),
63 ht
= rt
.ht
, vht
= rt
.vht
,
64 mhz
= rt
.mhz
, nss
= rt
.nss
,
65 mcs
= rt
.mcs
, sgi
= rt
.short_gi
;
68 if (vht
) s
+= ', VHT-MCS %d'.format(mcs
);
69 if (nss
) s
+= ', VHT-NSS %d'.format(nss
);
70 if (ht
) s
+= ', MCS %s'.format(mcs
);
71 if (sgi
) s
+= ', ' + _('Short GI');
77 return L
.Class
.extend({
80 handleDelClient: function(ifname
, mac
, ev
) {
81 ev
.currentTarget
.classList
.add('spinning');
82 ev
.currentTarget
.disabled
= true;
83 ev
.currentTarget
.blur();
86 object
: 'hostapd.%s'.format(ifname
),
88 params
: [ 'addr', 'deauth', 'reason', 'ban_time' ]
89 })(mac
, true, 5, 60000);
93 return L
.resolveDefault(network
.getWifiDevices(), []).then(function(devices
) {
96 for (var i
= 0; i
< devices
.length
; i
++)
97 tasks
.push(L
.resolveDefault(devices
[i
].getWifiNetworks(), []).then(L
.bind(function(r
, l
) {
99 }, this, devices
[i
])));
101 return Promise
.all(tasks
).then(function() {
104 }).then(function(devices
) {
105 var tasks
= [], deauth
= {};
107 for (var i
= 0; i
< devices
.length
; i
++) {
108 for (var j
= 0; j
< devices
[i
].networks
.length
; j
++) {
109 tasks
.push(L
.resolveDefault(devices
[i
].networks
[j
].getAssocList(), []).then(L
.bind(function(n
, l
) {
110 n
.assoclist
= l
.sort(function(a
, b
) { return a
.mac
> b
.mac
});
111 }, this, devices
[i
].networks
[j
])));
113 tasks
.push(L
.resolveDefault(rpc
.list('hostapd.%s'.format(devices
[i
].networks
[j
].getIfname())), {}).then(L
.bind(function(n
, l
) {
114 for (var k
in L
.isObject(l
) ? l
: {})
115 n
.supports_deauth
= l
[k
].hasOwnProperty('del_client');
116 }, this, devices
[i
].networks
[j
])));
120 return Promise
.all(tasks
).then(function() {
121 return network
.getHostHints().then(function(hosthints
) {
122 return [ devices
, hosthints
];
128 render: function(data
) {
129 var devices
= data
[0],
132 var table
= E('div', { 'class': 'network-status-table' });
134 for (var i
= 0; i
< devices
.length
; i
++)
135 table
.appendChild(renderbox(devices
[i
]));
137 if (!table
.lastElementChild
)
140 var supports_deauth
= false;
142 for (var i
= 0; i
< devices
.length
; i
++)
143 for (var j
= 0; j
< devices
[i
].networks
.length
; j
++)
144 supports_deauth
= supports_deauth
|| devices
[i
].networks
[j
].supports_deauth
;
146 var assoclist
= E('div', { 'class': 'table' }, [
147 E('div', { 'class': 'tr table-titles' }, [
148 E('div', { 'class': 'th nowrap' }, _('Network')),
149 E('div', { 'class': 'th hide-xs' }, _('MAC-Address')),
150 E('div', { 'class': 'th' }, _('Host')),
151 E('div', { 'class': 'th nowrap' }, '%s / %s'.format(_('Signal'), _('Noise'))),
152 E('div', { 'class': 'th nowrap' }, '%s / %s'.format(_('RX Rate'), _('TX Rate'))),
153 supports_deauth
? E('div', { 'class': 'th nowrap right' }, _('Disconnect')) : null
159 for (var i
= 0; i
< devices
.length
; i
++) {
160 for (var j
= 0; j
< devices
[i
].networks
.length
; j
++) {
161 for (var k
= 0; k
< devices
[i
].networks
[j
].assoclist
.length
; k
++) {
162 var bss
= devices
[i
].networks
[j
].assoclist
[k
],
163 name
= hosthints
.getHostnameByMACAddr(bss
.mac
),
164 ipv4
= hosthints
.getIPAddrByMACAddr(bss
.mac
),
165 ipv6
= hosthints
.getIP6AddrByMACAddr(bss
.mac
);
168 var q
= (-1 * (bss
.noise
- bss
.signal
)) / 5;
170 icon
= L
.resource('icons/signal-0.png');
172 icon
= L
.resource('icons/signal-0-25.png');
174 icon
= L
.resource('icons/signal-25-50.png');
176 icon
= L
.resource('icons/signal-50-75.png');
178 icon
= L
.resource('icons/signal-75-100.png');
180 var sig_title
, sig_value
;
183 sig_value
= '%d / %d %s'.format(bss
.signal
, bss
.noise
, _('dBm'));
184 sig_title
= '%s: %d %s / %s: %d %s / %s %d'.format(
185 _('Signal'), bss
.signal
, _('dBm'),
186 _('Noise'), bss
.noise
, _('dBm'),
187 _('SNR'), bss
.signal
- bss
.noise
);
190 sig_value
= '%d %s'.format(bss
.signal
, _('dBm'));
191 sig_title
= '%s: %d %s'.format(_('Signal'), bss
.signal
, _('dBm'));
196 if (name
&& ipv4
&& ipv6
)
197 hint
= '%s (%s, %s)'.format(name
, ipv4
, ipv6
);
198 else if (name
&& (ipv4
|| ipv6
))
199 hint
= '%s (%s)'.format(name
, ipv4
|| ipv6
);
201 hint
= name
|| ipv4
|| ipv6
|| '?';
204 E('span', { 'class': 'ifacebadge', 'title': devices
[i
].networks
[j
].getI18n() }, [
205 E('img', { 'src': L
.resource('icons/wifi.png') }),
206 ' ', devices
[i
].networks
[j
].getShortName(),
207 E('small', {}, [ ' (', devices
[i
].networks
[j
].getIfname(), ')' ])
211 E('span', { 'class': 'ifacebadge', 'title': sig_title
}, [
212 E('img', { 'src': icon
}),
216 E('span', wifirate(bss
.rx
)),
218 E('span', wifirate(bss
.tx
))
220 devices
[i
].networks
[j
].supports_deauth
? E('button', {
221 'class': 'cbi-button cbi-button-remove',
222 'click': L
.bind(this.handleDelClient
, this, devices
[i
].networks
[j
].getIfname(), bss
.mac
)
223 }, [ _('Disconnect') ]) : '-'
229 cbi_update_table(assoclist
, rows
, E('em', _('No information available')));
233 E('h3', _('Associated Stations')),