9 return baseclass
.extend({
13 Disabled
: _('Disabled'),
15 'Timed-out': _('Timed-out'),
16 Overlap
: _('Overlap'),
20 callSessionAccess
: rpc
.declare({
23 params
: [ 'scope', 'object', 'function' ],
24 expect
: { 'access': false }
27 wifirate: function(rt
) {
28 var s
= '%.1f\xa0%s, %d\xa0%s'.format(rt
.rate
/ 1000, _('Mbit/s'), rt
.mhz
, _('MHz')),
29 ht
= rt
.ht
, vht
= rt
.vht
,
30 mhz
= rt
.mhz
, nss
= rt
.nss
,
31 mcs
= rt
.mcs
, sgi
= rt
.short_gi
;
34 if (vht
) s
+= ', VHT-MCS\xa0%d'.format(mcs
);
35 if (nss
) s
+= ', VHT-NSS\xa0%d'.format(nss
);
36 if (ht
) s
+= ', MCS\xa0%s'.format(mcs
);
37 if (sgi
) s
+= ', ' + _('Short GI').replace(/ /g
, '\xa0');
43 handleDelClient: function(wifinet
, mac
, ev
, cmd
) {
44 var exec
= cmd
|| 'disconnect';
46 dom
.parent(ev
.currentTarget
, '.tr').style
.opacity
= 0.5;
47 ev
.currentTarget
.classList
.add('spinning');
48 ev
.currentTarget
.disabled
= true;
49 ev
.currentTarget
.blur();
51 if (exec
== 'addlist') {
54 for (var mac
in this.iface_maclist
) {
58 uci
.set('wireless', wifinet
.sid
, 'maclist', macs
);
61 .then(L
.bind(L
.ui
.changes
.init
, L
.ui
.changes
))
62 .then(L
.bind(L
.ui
.changes
.displayChanges
, L
.ui
.changes
));
65 wifinet
.disconnectClient(mac
, true, 5, 60000);
68 handleGetWPSStatus: function(wifinet
) {
70 object
: 'hostapd.%s'.format(wifinet
),
75 handleCallWPS: function(wifinet
, ev
) {
76 ev
.currentTarget
.classList
.add('spinning');
77 ev
.currentTarget
.disabled
= true;
78 ev
.currentTarget
.blur();
81 object
: 'hostapd.%s'.format(wifinet
),
86 handleCancelWPS: function(wifinet
, ev
) {
87 ev
.currentTarget
.classList
.add('spinning');
88 ev
.currentTarget
.disabled
= true;
89 ev
.currentTarget
.blur();
92 object
: 'hostapd.%s'.format(wifinet
),
97 renderbox: function(radio
, networks
) {
103 for (var i
= 0; i
< networks
.length
; i
++) {
104 var net
= networks
[i
],
105 is_assoc
= (net
.getBSSID() != '00:00:00:00:00:00' && net
.getChannel() && !net
.isDisabled()),
106 quality
= net
.getSignalPercent();
109 if (net
.isDisabled())
110 icon
= L
.resource('icons/signal-none.png');
111 else if (quality
<= 0)
112 icon
= L
.resource('icons/signal-0.png');
113 else if (quality
< 25)
114 icon
= L
.resource('icons/signal-0-25.png');
115 else if (quality
< 50)
116 icon
= L
.resource('icons/signal-25-50.png');
117 else if (quality
< 75)
118 icon
= L
.resource('icons/signal-50-75.png');
120 icon
= L
.resource('icons/signal-75-100.png');
124 if (this.isWPSEnabled
[net
.sid
]) {
125 if (net
.wps_status
== 'Active') {
126 WPS_button
= E('button', {
127 'class' : 'cbi-button cbi-button-remove',
128 'click': L
.bind(this.handleCancelWPS
, this, net
.getIfname()),
129 }, [ _('Stop WPS') ])
131 WPS_button
= E('button', {
132 'class' : 'cbi-button cbi-button-apply',
133 'click': L
.bind(this.handleCallWPS
, this, net
.getIfname()),
134 }, [ _('Start WPS') ])
138 var badge
= renderBadge(
140 '%s: %d dBm / %s: %d%%'.format(_('Signal'), net
.getSignal(), _('Quality'), quality
),
141 _('SSID'), net
.getActiveSSID() || '?',
142 _('Mode'), net
.getActiveMode(),
143 _('BSSID'), is_assoc
? (net
.getActiveBSSID() || '-') : null,
144 _('Encryption'), is_assoc
? net
.getActiveEncryption() : null,
145 _('Associations'), is_assoc
? (net
.assoclist
.length
|| '-') : null,
146 null, is_assoc
? null : E('em', net
.isDisabled() ? _('Wireless is disabled') : _('Wireless is not associated')),
147 _('WPS status'), this.WPSTranslateTbl
[net
.wps_status
],
153 chan
= (chan
!= null) ? chan
: net
.getChannel();
154 freq
= (freq
!= null) ? freq
: net
.getFrequency();
155 rate
= (rate
!= null) ? rate
: net
.getBitRate();
158 return E('div', { class: 'ifacebox' }, [
159 E('div', { class: 'ifacebox-head center ' + (radio
.isUp() ? 'active' : '') },
160 E('strong', radio
.getName())),
161 E('div', { class: 'ifacebox-body left' }, [
162 L
.itemlist(E('span'), [
163 _('Type'), radio
.getI18n().replace(/^Generic | Wireless Controller .+$/g, ''),
164 _('Channel'), chan
? '%d (%.3f %s)'.format(chan
, freq
, _('GHz')) : '-',
165 _('Bitrate'), rate
? '%d %s'.format(rate
, _('Mbit/s')) : '-',
176 network
.getWifiDevices(),
177 network
.getWifiNetworks(),
178 network
.getHostHints(),
179 this.callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'read'),
180 this.callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'write'),
182 ]).then(L
.bind(function(data
) {
184 radios_networks_hints
= data
[1],
185 hasWPS
= L
.hasSystemFeature('hostapd', 'wps');
187 for (var i
= 0; i
< radios_networks_hints
.length
; i
++) {
188 tasks
.push(L
.resolveDefault(radios_networks_hints
[i
].getAssocList(), []).then(L
.bind(function(net
, list
) {
189 net
.assoclist
= list
.sort(function(a
, b
) { return a
.mac
> b
.mac
});
190 }, this, radios_networks_hints
[i
])));
192 if (hasWPS
&& uci
.get('wireless', radios_networks_hints
[i
].sid
, 'wps_pushbutton') == '1') {
193 this.isWPSEnabled
[radios_networks_hints
[i
].sid
] = true;
194 tasks
.push(L
.resolveDefault(this.handleGetWPSStatus(radios_networks_hints
[i
].getIfname()), null)
195 .then(L
.bind(function(net
, data
) {
196 net
.wps_status
= data
? data
.pbc_status
: _('No Data');
197 }, this, radios_networks_hints
[i
])));
201 return Promise
.all(tasks
).then(function() {
209 render: function(data
) {
214 hasReadPermission
= data
[3],
215 hasWritePermission
= data
[4];
217 var table
= E('div', { 'class': 'network-status-table' });
219 for (var i
= 0; i
< radios
.sort(function(a
, b
) { a
.getName() > b
.getName() }).length
; i
++)
220 table
.appendChild(this.renderbox(radios
[i
],
221 networks
.filter(function(net
) { return net
.getWifiDeviceName() == radios
[i
].getName() })));
223 if (!table
.lastElementChild
)
226 var assoclist
= E('table', { 'class': 'table assoclist' }, [
227 E('tr', { 'class': 'tr table-titles' }, [
228 E('th', { 'class': 'th nowrap' }, _('Network')),
229 E('th', { 'class': 'th hide-xs' }, _('MAC-Address')),
230 E('th', { 'class': 'th' }, _('Host')),
231 E('th', { 'class': 'th' }, '%s / %s'.format(_('Signal'), _('Noise'))),
232 E('th', { 'class': 'th' }, '%s / %s'.format(_('RX Rate'), _('TX Rate')))
238 for (var i
= 0; i
< networks
.length
; i
++) {
239 var macfilter
= uci
.get('wireless', networks
[i
].sid
, 'macfilter');
241 if (macfilter
!= null && macfilter
!= 'disable') {
242 this.isDeviceAdded
= {};
243 var macs
= L
.toArray(uci
.get('wireless', networks
[i
].sid
, 'maclist'));
244 for (var j
= 0; j
< macs
.length
; j
++) {
245 var mac
= macs
[j
].toUpperCase();
246 this.isDeviceAdded
[mac
] = true;
250 for (var k
= 0; k
< networks
[i
].assoclist
.length
; k
++) {
251 var bss
= networks
[i
].assoclist
[k
],
252 name
= hosthints
.getHostnameByMACAddr(bss
.mac
),
253 ipv4
= hosthints
.getIPAddrByMACAddr(bss
.mac
),
254 ipv6
= hosthints
.getIP6AddrByMACAddr(bss
.mac
);
257 var q
= Math
.min((bss
.signal
+ 110) / 70 * 100, 100);
259 icon
= L
.resource('icons/signal-0.png');
261 icon
= L
.resource('icons/signal-0-25.png');
263 icon
= L
.resource('icons/signal-25-50.png');
265 icon
= L
.resource('icons/signal-50-75.png');
267 icon
= L
.resource('icons/signal-75-100.png');
269 var sig_title
, sig_value
;
272 sig_value
= '%d/%d\xa0%s'.format(bss
.signal
, bss
.noise
, _('dBm'));
273 sig_title
= '%s: %d %s / %s: %d %s / %s %d'.format(
274 _('Signal'), bss
.signal
, _('dBm'),
275 _('Noise'), bss
.noise
, _('dBm'),
276 _('SNR'), bss
.signal
- bss
.noise
);
279 sig_value
= '%d\xa0%s'.format(bss
.signal
, _('dBm'));
280 sig_title
= '%s: %d %s'.format(_('Signal'), bss
.signal
, _('dBm'));
285 if (name
&& ipv4
&& ipv6
)
286 hint
= '%s <span class="hide-xs">(%s, %s)</span>'.format(name
, ipv4
, ipv6
);
287 else if (name
&& (ipv4
|| ipv6
))
288 hint
= '%s <span class="hide-xs">(%s)</span>'.format(name
, ipv4
|| ipv6
);
290 hint
= name
|| ipv4
|| ipv6
|| '?';
294 'class': 'ifacebadge',
295 'title': networks
[i
].getI18n(),
296 'data-ifname': networks
[i
].getIfname(),
297 'data-ssid': networks
[i
].getActiveSSID()
299 E('img', { 'src': L
.resource('icons/wifi.png') }),
301 ' ', networks
[i
].getShortName(),
302 E('small', {}, [ ' (', networks
[i
].getIfname(), ')' ])
308 'class': 'ifacebadge',
310 'data-signal': bss
.signal
,
311 'data-noise': bss
.noise
313 E('img', { 'src': icon
}),
319 E('span', this.wifirate(bss
.rx
)),
321 E('span', this.wifirate(bss
.tx
))
325 if (networks
[i
].isClientDisconnectSupported() && hasWritePermission
) {
326 if (assoclist
.firstElementChild
.childNodes
.length
< 6)
327 assoclist
.firstElementChild
.appendChild(E('th', { 'class': 'th cbi-section-actions' }));
329 if (macfilter
!= null && macfilter
!= 'disable' && !this.isDeviceAdded
[bss
.mac
]) {
330 row
.push(new L
.ui
.ComboButton('button', {
331 'addlist': macfilter
== 'allow' ? _('Add to Whitelist') : _('Add to Blacklist'),
332 'disconnect': _('Disconnect')
334 'click': L
.bind(this.handleDelClient
, this, networks
[i
], bss
.mac
),
335 'sort': [ 'disconnect', 'addlist' ],
337 'addlist': 'btn cbi-button cbi-button-remove',
338 'disconnect': 'btn cbi-button cbi-button-remove'
344 row
.push(E('button', {
345 'class': 'cbi-button cbi-button-remove',
346 'click': L
.bind(this.handleDelClient
, this, networks
[i
], bss
.mac
)
347 }, [ _('Disconnect') ]));
358 cbi_update_table(assoclist
, rows
, E('em', _('No information available')));
362 hasReadPermission
? E('h3', _('Associated Stations')) : E([]),
363 hasReadPermission
? assoclist
: E([])