a058b3fe52bd640223d03c82f2408dfaa6eb0131
7 'require tools.widgets as widgets';
9 function count_changes(section_id
) {
10 var changes
= L
.ui
.changes
.changes
, n
= 0;
12 if (!L
.isObject(changes
))
15 if (Array
.isArray(changes
.wireless
))
16 for (var i
= 0; i
< changes
.wireless
.length
; i
++)
17 n
+= (changes
.wireless
[i
][1] == section_id
);
22 function render_radio_badge(radioDev
) {
23 return E('span', { 'class': 'ifacebadge' }, [
24 E('img', { 'src': L
.resource('icons/wifi%s.png').format(radioDev
.isUp() ? '' : '_disabled') }),
30 function render_signal_badge(signalPercent
, signalValue
, noiseValue
, wrap
) {
33 if (signalPercent
< 0)
34 icon
= L
.resource('icons/signal-none.png');
35 else if (signalPercent
== 0)
36 icon
= L
.resource('icons/signal-0.png');
37 else if (signalPercent
< 25)
38 icon
= L
.resource('icons/signal-0-25.png');
39 else if (signalPercent
< 50)
40 icon
= L
.resource('icons/signal-25-50.png');
41 else if (signalPercent
< 75)
42 icon
= L
.resource('icons/signal-50-75.png');
44 icon
= L
.resource('icons/signal-75-100.png');
46 if (signalValue
!= null && signalValue
!= 0) {
47 title
= '%s %d %s'.format(_('Signal'), signalValue
, _('dBm'));
49 if (noiseValue
!= null && noiseValue
!= 0)
50 title
+= ' / %s: %d %s'.format(_('Noise'), noiseValue
, _('dBm'));
53 title
= _('No signal');
56 return E('div', { 'class': wrap
? 'center' : 'ifacebadge', 'title': title
},
57 [ E('img', { 'src': icon
}), wrap
? E('br') : ' ', '%d%%'.format(Math
.max(signalPercent
, 0)) ]);
60 function render_network_badge(radioNet
) {
61 return render_signal_badge(radioNet
.isUp() ? radioNet
.getSignalPercent() : -1, radioNet
.getSignal(), radioNet
.getNoise());
64 function render_radio_status(radioDev
, wifiNets
) {
65 var name
= radioDev
.getI18n().replace(/ Wireless Controller
.+$/, ''),
66 node
= E('div', [ E('big', {}, E('strong', {}, name
)), E('div') ]),
67 channel
, frequency
, bitrate
;
69 for (var i
= 0; i
< wifiNets
.length
; i
++) {
70 channel
= channel
|| wifiNets
[i
].getChannel();
71 frequency
= frequency
|| wifiNets
[i
].getFrequency();
72 bitrate
= bitrate
|| wifiNets
[i
].getBitRate();
76 L
.itemlist(node
.lastElementChild
, [
77 _('Channel'), '%s (%s %s)'.format(channel
|| '?', frequency
|| '?', _('GHz')),
78 _('Bitrate'), '%s %s'.format(bitrate
|| '?', _('Mbit/s'))
81 node
.lastElementChild
.appendChild(E('em', _('Device is not active')));
86 function render_network_status(radioNet
) {
87 var mode
= radioNet
.getActiveMode(),
88 bssid
= radioNet
.getActiveBSSID(),
89 channel
= radioNet
.getChannel(),
90 disabled
= (radioNet
.get('disabled') == '1'),
91 is_assoc
= (bssid
&& bssid
!= '00:00:00:00:00:00' && channel
&& mode
!= 'Unknown' && !disabled
),
92 changecount
= count_changes(radioNet
.getName()),
96 status_text
= E('a', {
98 click
: L
.bind(L
.ui
.changes
.displayChanges
, L
.ui
.changes
)
99 }, _('Interface has %d pending changes').format(changecount
));
101 status_text
= E('em', disabled
? _('Wireless is disabled') : _('Wireless is not associated'));
103 return L
.itemlist(E('div'), [
104 _('SSID'), radioNet
.getSSID() || '?',
106 _('BSSID'), (!changecount
&& is_assoc
) ? bssid
: null,
107 _('Encryption'), (!changecount
&& is_assoc
) ? radioNet
.getActiveEncryption() || _('None') : null,
109 ], [ ' | ', E('br') ]);
112 function render_modal_status(node
, radioNet
) {
113 var mode
= radioNet
.getActiveMode(),
114 noise
= radioNet
.getNoise(),
115 bssid
= radioNet
.getActiveBSSID(),
116 channel
= radioNet
.getChannel(),
117 disabled
= (radioNet
.get('disabled') == '1'),
118 is_assoc
= (bssid
&& bssid
!= '00:00:00:00:00:00' && channel
&& mode
!= 'Unknown' && !disabled
);
121 node
= E('span', { 'class': 'ifacebadge large', 'data-network': radioNet
.getName() }, [ E('small'), E('span') ]);
123 L
.dom
.content(node
.firstElementChild
, render_signal_badge(disabled
? -1 : radioNet
.getSignalPercent(), radioNet
.getSignal(), noise
, true));
125 L
.itemlist(node
.lastElementChild
, [
127 _('SSID'), radioNet
.getSSID() || '?',
128 _('BSSID'), is_assoc
? bssid
: null,
129 _('Encryption'), is_assoc
? radioNet
.getActiveEncryption() || _('None') : null,
130 _('Channel'), is_assoc
? '%d (%.3f %s)'.format(radioNet
.getChannel(), radioNet
.getFrequency() || 0, _('GHz')) : null,
131 _('Tx-Power'), is_assoc
? '%d %s'.format(radioNet
.getTXPower(), _('dBm')) : null,
132 _('Signal'), is_assoc
? '%d %s'.format(radioNet
.getSignal(), _('dBm')) : null,
133 _('Noise'), (is_assoc
&& noise
!= null) ? '%d %s'.format(noise
, _('dBm')) : null,
134 _('Bitrate'), is_assoc
? '%.1f %s'.format(radioNet
.getBitRate() || 0, _('Mbit/s')) : null,
135 _('Country'), is_assoc
? radioNet
.getCountryCode() : null
136 ], [ ' | ', E('br'), E('br'), E('br'), E('br'), E('br'), ' | ', E('br'), ' | ' ]);
139 L
.dom
.append(node
.lastElementChild
, E('em', disabled
? _('Wireless is disabled') : _('Wireless is not associated')));
144 function format_wifirate(rate
) {
145 var s
= '%.1f Mbit/s, %dMHz'.format(rate
.rate
/ 1000, rate
.mhz
);
147 if (rate
.ht
|| rate
.vht
) {
148 if (rate
.vht
) s
+= ', VHT-MCS %d'.format(rate
.mcs
);
149 if (rate
.nss
) s
+= ', VHT-NSS %d'.format(rate
.nss
);
150 if (rate
.ht
) s
+= ', MCS %s'.format(rate
.mcs
);
151 if (rate
.short_gi
) s
+= ', Short GI';
157 function radio_restart(id
, ev
) {
158 var row
= document
.querySelector('.cbi-section-table-row[data-sid="%s"]'.format(id
)),
159 dsc
= row
.querySelector('[data-name="_stat"] > div'),
160 btn
= row
.querySelector('.cbi-section-actions button');
163 btn
.classList
.add('spinning');
166 dsc
.setAttribute('restart', '');
167 L
.dom
.content(dsc
, E('em', _('Device is restarting…')));
170 function network_updown(id
, map
, ev
) {
171 var radio
= uci
.get('wireless', id
, 'device'),
172 disabled
= (uci
.get('wireless', id
, 'disabled') == '1') ||
173 (uci
.get('wireless', radio
, 'disabled') == '1');
176 uci
.unset('wireless', id
, 'disabled');
177 uci
.unset('wireless', radio
, 'disabled');
180 uci
.set('wireless', id
, 'disabled', '1');
182 var all_networks_disabled
= true,
183 wifi_ifaces
= uci
.sections('wireless', 'wifi-iface');
185 for (var i
= 0; i
< wifi_ifaces
.length
; i
++) {
186 if (wifi_ifaces
[i
].device
== radio
&& wifi_ifaces
[i
].disabled
!= '1') {
187 all_networks_disabled
= false;
192 if (all_networks_disabled
)
193 uci
.set('wireless', radio
, 'disabled', '1');
196 return map
.save().then(function() {
201 function next_free_sid(offset
) {
202 var sid
= 'wifinet' + offset
;
204 while (uci
.get('wireless', sid
))
205 sid
= 'wifinet' + (++offset
);
210 var CBIWifiFrequencyValue
= form
.Value
.extend({
211 callFrequencyList
: rpc
.declare({
214 params
: [ 'device' ],
215 expect
: { results
: [] }
218 load: function(section_id
) {
220 network
.getWifiDevice(section_id
),
221 this.callFrequencyList(section_id
)
222 ]).then(L
.bind(function(data
) {
224 '11g': L
.hasSystemFeature('hostapd', 'acs') ? [ 'auto', 'auto', true ] : [],
225 '11a': L
.hasSystemFeature('hostapd', 'acs') ? [ 'auto', 'auto', true ] : []
228 for (var i
= 0; Array
.isArray(data
[1]) && i
< data
[1].length
; i
++)
229 this.channels
[(data
[1][i
].mhz
> 2484) ? '11a' : '11g'].push(
231 '%d (%d Mhz)'.format(data
[1][i
].channel
, data
[1][i
].mhz
),
232 !data
[1][i
].restricted
235 var hwmodelist
= L
.toArray(data
[0] ? data
[0].getHWModes() : null)
236 .reduce(function(o
, v
) { o
[v
] = true; return o
}, {});
240 'n', 'N', hwmodelist
.n
,
241 'ac', 'AC', hwmodelist
.ac
244 var htmodelist
= L
.toArray(data
[0] ? data
[0].getHTModes() : null)
245 .reduce(function(o
, v
) { o
[v
] = true; return o
}, {});
248 '': [ '', '-', true ],
250 'HT20', '20 MHz', htmodelist
.HT20
,
251 'HT40', '40 MHz', htmodelist
.HT40
254 'VHT20', '20 MHz', htmodelist
.VHT20
,
255 'VHT40', '40 MHz', htmodelist
.VHT40
,
256 'VHT80', '80 MHz', htmodelist
.VHT80
,
257 'VHT160', '160 MHz', htmodelist
.VHT160
263 '11g', '2.4 GHz', this.channels
['11g'].length
> 3,
264 '11a', '5 GHz', this.channels
['11a'].length
> 3
267 '11g', '2.4 GHz', this.channels
['11g'].length
> 3,
268 '11a', '5 GHz', this.channels
['11a'].length
> 3
277 setValues: function(sel
, vals
) {
279 sel
.vals
.selected
= sel
.selectedIndex
;
281 while (sel
.options
[0])
284 for (var i
= 0; vals
&& i
< vals
.length
; i
+= 3)
286 sel
.add(E('option', { value
: vals
[i
+0] }, [ vals
[i
+1] ]));
288 if (!isNaN(vals
.selected
))
289 sel
.selectedIndex
= vals
.selected
;
291 sel
.parentNode
.style
.display
= (sel
.options
.length
<= 1) ? 'none' : '';
295 toggleWifiMode: function(elem
) {
296 this.toggleWifiHTMode(elem
);
297 this.toggleWifiBand(elem
);
300 toggleWifiHTMode: function(elem
) {
301 var mode
= elem
.querySelector('.mode');
302 var bwdt
= elem
.querySelector('.htmode');
304 this.setValues(bwdt
, this.htmodes
[mode
.value
]);
307 toggleWifiBand: function(elem
) {
308 var mode
= elem
.querySelector('.mode');
309 var band
= elem
.querySelector('.band');
311 this.setValues(band
, this.bands
[mode
.value
]);
312 this.toggleWifiChannel(elem
);
315 toggleWifiChannel: function(elem
) {
316 var band
= elem
.querySelector('.band');
317 var chan
= elem
.querySelector('.channel');
319 this.setValues(chan
, this.channels
[band
.value
]);
322 setInitialValues: function(section_id
, elem
) {
323 var mode
= elem
.querySelector('.mode'),
324 band
= elem
.querySelector('.band'),
325 chan
= elem
.querySelector('.channel'),
326 bwdt
= elem
.querySelector('.htmode'),
327 htval
= uci
.get('wireless', section_id
, 'htmode'),
328 hwval
= uci
.get('wireless', section_id
, 'hwmode'),
329 chval
= uci
.get('wireless', section_id
, 'channel');
331 this.setValues(mode
, this.modes
);
333 if (/VHT20|VHT40|VHT80|VHT160/.test(htval
))
335 else if (/HT20|HT40/.test(htval
))
340 this.toggleWifiMode(elem
);
347 this.toggleWifiBand(elem
);
355 renderWidget: function(section_id
, option_index
, cfgvalue
) {
358 L
.dom
.content(elem
, [
359 E('label', { 'style': 'float:left; margin-right:3px' }, [
363 'style': 'width:auto',
364 'change': L
.bind(this.toggleWifiMode
, this, elem
)
367 E('label', { 'style': 'float:left; margin-right:3px' }, [
371 'style': 'width:auto',
372 'change': L
.bind(this.toggleWifiBand
, this, elem
)
375 E('label', { 'style': 'float:left; margin-right:3px' }, [
376 _('Channel'), E('br'),
379 'style': 'width:auto'
382 E('label', { 'style': 'float:left; margin-right:3px' }, [
386 'style': 'width:auto'
389 E('br', { 'style': 'clear:left' })
392 return this.setInitialValues(section_id
, elem
);
395 cfgvalue: function(section_id
) {
397 uci
.get('wireless', section_id
, 'htmode'),
398 uci
.get('wireless', section_id
, 'hwmode'),
399 uci
.get('wireless', section_id
, 'channel')
403 formvalue: function(section_id
) {
404 var node
= this.map
.findElement('data-field', this.cbid(section_id
));
407 node
.querySelector('.htmode').value
,
408 node
.querySelector('.band').value
,
409 node
.querySelector('.channel').value
413 write: function(section_id
, value
) {
414 uci
.set('wireless', section_id
, 'htmode', value
[0] || null);
415 uci
.set('wireless', section_id
, 'hwmode', value
[1]);
416 uci
.set('wireless', section_id
, 'channel', value
[2]);
420 var CBIWifiTxPowerValue
= form
.ListValue
.extend({
421 callTxPowerList
: rpc
.declare({
423 method
: 'txpowerlist',
424 params
: [ 'device' ],
425 expect
: { results
: [] }
428 load: function(section_id
) {
429 return this.callTxPowerList(section_id
).then(L
.bind(function(pwrlist
) {
430 this.powerval
= this.wifiNetwork
? this.wifiNetwork
.getTXPower() : null;
431 this.poweroff
= this.wifiNetwork
? this.wifiNetwork
.getTXPowerOffset() : null;
433 this.value('', _('driver default'));
435 for (var i
= 0; i
< pwrlist
.length
; i
++)
436 this.value(pwrlist
[i
].dbm
, '%d dBm (%d mW)'.format(pwrlist
[i
].dbm
, pwrlist
[i
].mw
));
438 return form
.ListValue
.prototype.load
.apply(this, [section_id
]);
442 renderWidget: function(section_id
, option_index
, cfgvalue
) {
443 var widget
= form
.ListValue
.prototype.renderWidget
.apply(this, [section_id
, option_index
, cfgvalue
]);
444 widget
.firstElementChild
.style
.width
= 'auto';
446 L
.dom
.append(widget
, E('span', [
447 ' - ', _('Current power'), ': ',
448 E('span', [ this.powerval
!= null ? '%d dBm'.format(this.powerval
) : E('em', _('unknown')) ]),
449 this.poweroff
? ' + %d dB offset = %s dBm'.format(this.poweroff
, this.powerval
!= null ? this.powerval
+ this.poweroff
: '?') : ''
456 var CBIWifiCountryValue
= form
.Value
.extend({
457 callCountryList
: rpc
.declare({
459 method
: 'countrylist',
460 params
: [ 'device' ],
461 expect
: { results
: [] }
464 load: function(section_id
) {
465 return this.callCountryList(section_id
).then(L
.bind(function(countrylist
) {
466 if (Array
.isArray(countrylist
) && countrylist
.length
> 0) {
467 this.value('', _('driver default'));
469 for (var i
= 0; i
< countrylist
.length
; i
++)
470 this.value(countrylist
[i
].iso3166
, '%s - %s'.format(countrylist
[i
].iso3166
, countrylist
[i
].country
));
473 return form
.Value
.prototype.load
.apply(this, [section_id
]);
477 validate: function(section_id
, formvalue
) {
478 if (formvalue
!= null && formvalue
!= '' && !/^[A-Z0-9][A-Z0-9]$/.test(formvalue
))
479 return _('Use ISO/IEC 3166 alpha2 country codes.');
484 renderWidget: function(section_id
, option_index
, cfgvalue
) {
485 var typeClass
= this.keylist
.length
? form
.ListValue
: form
.Value
;
486 return typeClass
.prototype.renderWidget
.apply(this, [section_id
, option_index
, cfgvalue
]);
490 return L
.view
.extend({
491 poll_status: function(map
, data
) {
492 var rows
= map
.querySelectorAll('.cbi-section-table-row[data-sid]');
494 for (var i
= 0; i
< rows
.length
; i
++) {
495 var section_id
= rows
[i
].getAttribute('data-sid'),
496 radioDev
= data
[1].filter(function(d
) { return d
.getName() == section_id
})[0],
497 radioNet
= data
[2].filter(function(n
) { return n
.getName() == section_id
})[0],
498 badge
= rows
[i
].querySelector('[data-name="_badge"] > div'),
499 stat
= rows
[i
].querySelector('[data-name="_stat"]'),
500 btns
= rows
[i
].querySelectorAll('.cbi-section-actions button'),
501 busy
= btns
[0].classList
.contains('spinning') || btns
[1].classList
.contains('spinning') || btns
[2].classList
.contains('spinning');
504 L
.dom
.content(badge
, render_radio_badge(radioDev
));
505 L
.dom
.content(stat
, render_radio_status(radioDev
, data
[2].filter(function(n
) { return n
.getWifiDeviceName() == radioDev
.getName() })));
508 L
.dom
.content(badge
, render_network_badge(radioNet
));
509 L
.dom
.content(stat
, render_network_status(radioNet
));
512 if (stat
.hasAttribute('restart'))
513 L
.dom
.content(stat
, E('em', _('Device is restarting…')));
515 btns
[0].disabled
= busy
;
516 btns
[1].disabled
= busy
;
517 btns
[2].disabled
= busy
;
520 var table
= document
.querySelector('wifi_assoclist_table'),
524 for (var i
= 0; i
< data
[3].length
; i
++) {
525 var bss
= data
[3][i
],
526 name
= hosts
.getHostnameByMACAddr(bss
.mac
),
527 ipv4
= hosts
.getIPAddrByMACAddr(bss
.mac
),
528 ipv6
= hosts
.getIP6AddrByMACAddr(bss
.mac
);
531 E('span', { 'class': 'ifacebadge' }, [
533 'src': L
.resource('icons/wifi%s.png').format(bss
.network
.isUp() ? '' : '_disabled'),
534 'title': bss
.radio
.getI18n()
536 ' %s '.format(bss
.network
.getShortName()),
537 E('small', '(%s)'.format(bss
.network
.getIfname()))
540 name
? '%s (%s)'.format(name
, ipv4
|| ipv6
|| '?') : ipv4
|| ipv6
|| '?',
541 render_signal_badge(Math
.min((bss
.signal
+ 110) / 70 * 100, 100), bss
.signal
, bss
.noise
),
543 E('span', format_wifirate(bss
.rx
)),
545 E('span', format_wifirate(bss
.tx
))
550 cbi_update_table('#wifi_assoclist_table', trows
, E('em', _('No information available')));
552 var stat
= document
.querySelector('.cbi-modal [data-name="_wifistat_modal"] .ifacebadge.large');
555 render_modal_status(stat
, data
[2].filter(function(n
) { return n
.getName() == stat
.getAttribute('data-network') })[0]);
557 return network
.flushCache();
567 checkAnonymousSections: function() {
568 var wifiIfaces
= uci
.sections('wireless', 'wifi-iface');
570 for (var i
= 0; i
< wifiIfaces
.length
; i
++)
571 if (wifiIfaces
[i
]['.anonymous'])
577 callUciRename
: rpc
.declare({
580 params
: [ 'config', 'section', 'name' ]
584 if (this.checkAnonymousSections())
585 return this.renderMigration();
587 return this.renderOverview();
590 handleMigration: function(ev
) {
591 var wifiIfaces
= uci
.sections('wireless', 'wifi-iface'),
595 for (var i
= 0; i
< wifiIfaces
.length
; i
++) {
596 if (!wifiIfaces
[i
]['.anonymous'])
599 var new_name
= next_free_sid(id_offset
);
601 tasks
.push(this.callUciRename('wireless', wifiIfaces
[i
]['.name'], new_name
));
602 id_offset
= +new_name
.substring(7) + 1;
605 return Promise
.all(tasks
)
606 .then(L
.bind(L
.ui
.changes
.init
, L
.ui
.changes
))
607 .then(L
.bind(L
.ui
.changes
.apply
, L
.ui
.changes
));
610 renderMigration: function() {
611 L
.ui
.showModal(_('Wireless configuration migration'), [
612 E('p', _('The existing wireless configuration needs to be changed for LuCI to function properly.')),
613 E('p', _('Upon pressing "Continue", anonymous "wifi-iface" sections will be assigned with a name in the form <em>wifinet#</em> and the network will be restarted to apply the updated configuration.')),
614 E('div', { 'class': 'right' },
616 'class': 'btn cbi-button-action important',
617 'click': L
.ui
.createHandlerFn(this, 'handleMigration')
622 renderOverview: function() {
625 m
= new form
.Map('wireless');
629 s
= m
.section(form
.GridSection
, 'wifi-device', _('Wireless Overview'));
633 s
.load = function() {
634 return network
.getWifiDevices().then(L
.bind(function(radios
) {
635 this.radios
= radios
.sort(function(a
, b
) {
636 return a
.getName() > b
.getName();
641 for (var i
= 0; i
< radios
.length
; i
++)
642 tasks
.push(radios
[i
].getWifiNetworks());
644 return Promise
.all(tasks
);
645 }, this)).then(L
.bind(function(data
) {
648 for (var i
= 0; i
< data
.length
; i
++)
649 this.wifis
.push
.apply(this.wifis
, data
[i
]);
653 s
.cfgsections = function() {
656 for (var i
= 0; i
< this.radios
.length
; i
++) {
657 rv
.push(this.radios
[i
].getName());
659 for (var j
= 0; j
< this.wifis
.length
; j
++)
660 if (this.wifis
[j
].getWifiDeviceName() == this.radios
[i
].getName())
661 rv
.push(this.wifis
[j
].getName());
667 s
.modaltitle = function(section_id
) {
668 var radioNet
= this.wifis
.filter(function(w
) { return w
.getName() == section_id
})[0];
669 return radioNet
? radioNet
.getI18n() : _('Edit wireless network');
672 s
.lookupRadioOrNetwork = function(section_id
) {
673 var radioDev
= this.radios
.filter(function(r
) { return r
.getName() == section_id
})[0];
677 var radioNet
= this.wifis
.filter(function(w
) { return w
.getName() == section_id
})[0];
684 s
.renderRowActions = function(section_id
) {
685 var inst
= this.lookupRadioOrNetwork(section_id
), btns
;
687 if (inst
.getWifiNetworks
) {
690 'class': 'cbi-button cbi-button-neutral',
691 'title': _('Restart radio interface'),
692 'click': L
.ui
.createHandlerFn(this, radio_restart
, section_id
)
695 'class': 'cbi-button cbi-button-action important',
696 'title': _('Find and join network'),
697 'click': L
.ui
.createHandlerFn(this, 'handleScan', inst
)
700 'class': 'cbi-button cbi-button-add',
701 'title': _('Provide new network'),
702 'click': L
.ui
.createHandlerFn(this, 'handleAdd', inst
)
707 var isDisabled
= (inst
.get('disabled') == '1');
711 'class': 'cbi-button cbi-button-neutral enable-disable',
712 'title': isDisabled
? _('Enable this network') : _('Disable this network'),
713 'click': L
.ui
.createHandlerFn(this, network_updown
, section_id
, this.map
)
714 }, isDisabled
? _('Enable') : _('Disable')),
716 'class': 'cbi-button cbi-button-action important',
717 'title': _('Edit this network'),
718 'click': L
.ui
.createHandlerFn(this, 'renderMoreOptionsModal', section_id
)
721 'class': 'cbi-button cbi-button-negative remove',
722 'title': _('Delete this network'),
723 'click': L
.ui
.createHandlerFn(this, 'handleRemove', section_id
)
728 return E('div', { 'class': 'td middle cbi-section-actions' }, E('div', btns
));
731 s
.addModalOptions = function(s
) {
732 return network
.getWifiNetwork(s
.section
).then(function(radioNet
) {
733 var hwtype
= uci
.get('wireless', radioNet
.getWifiDeviceName(), 'type');
736 o
= s
.option(form
.SectionValue
, '_device', form
.NamedSection
, radioNet
.getWifiDeviceName(), 'wifi-device', _('Device Configuration'));
740 ss
.tab('general', _('General Setup'));
741 ss
.tab('advanced', _('Advanced Settings'));
743 var isDisabled
= (radioNet
.get('disabled') == '1');
745 o
= ss
.taboption('general', form
.DummyValue
, '_wifistat_modal', _('Status'));
746 o
.cfgvalue
= L
.bind(function(radioNet
) {
747 return render_modal_status(null, radioNet
);
749 o
.write = function() {};
751 o
= ss
.taboption('general', form
.Button
, '_toggle', isDisabled
? _('Wireless network is disabled') : _('Wireless network is enabled'));
752 o
.inputstyle
= isDisabled
? 'apply' : 'reset';
753 o
.inputtitle
= isDisabled
? _('Enable') : _('Disable');
754 o
.onclick
= L
.ui
.createHandlerFn(s
, network_updown
, s
.section
, s
.map
);
756 o
= ss
.taboption('general', CBIWifiFrequencyValue
, '_freq', '<br />' + _('Operating frequency'));
757 o
.ucisection
= s
.section
;
759 if (hwtype
== 'mac80211') {
760 o
= ss
.taboption('general', CBIWifiTxPowerValue
, 'txpower', _('Maximum transmit power'), _('Specifies the maximum transmit power the wireless radio may use. Depending on regulatory requirements and wireless usage, the actual transmit power may be reduced by the driver.'));
761 o
.wifiNetwork
= radioNet
;
763 o
= ss
.taboption('advanced', CBIWifiCountryValue
, 'country', _('Country Code'));
764 o
.wifiNetwork
= radioNet
;
766 o
= ss
.taboption('advanced', form
.Flag
, 'legacy_rates', _('Allow legacy 802.11b rates'));
767 o
.default = o
.enabled
;
769 o
= ss
.taboption('advanced', form
.Value
, 'distance', _('Distance Optimization'), _('Distance to farthest network member in meters.'));
770 o
.datatype
= 'range(0,114750)';
771 o
.placeholder
= 'auto';
773 o
= ss
.taboption('advanced', form
.Value
, 'frag', _('Fragmentation Threshold'));
774 o
.datatype
= 'min(256)';
775 o
.placeholder
= _('off');
777 o
= ss
.taboption('advanced', form
.Value
, 'rts', _('RTS/CTS Threshold'));
778 o
.datatype
= 'uinteger';
779 o
.placeholder
= _('off');
781 o
= ss
.taboption('advanced', form
.Flag
, 'noscan', _('Force 40MHz mode'), _('Always use 40MHz channels even if the secondary channel overlaps. Using this option does not comply with IEEE 802.11n-2009!'));
784 o
= ss
.taboption('advanced', form
.Value
, 'beacon_int', _('Beacon Interval'));
785 o
.datatype
= 'range(15,65535)';
791 o
= s
.option(form
.SectionValue
, '_device', form
.NamedSection
, radioNet
.getName(), 'wifi-iface', _('Interface Configuration'));
795 ss
.tab('general', _('General Setup'));
796 ss
.tab('encryption', _('Wireless Security'));
797 ss
.tab('macfilter', _('MAC-Filter'));
798 ss
.tab('advanced', _('Advanced Settings'));
800 o
= ss
.taboption('general', form
.ListValue
, 'mode', _('Mode'));
801 o
.value('ap', _('Access Point'));
802 o
.value('sta', _('Client'));
803 o
.value('adhoc', _('Ad-Hoc'));
805 o
= ss
.taboption('general', form
.Value
, 'mesh_id', _('Mesh Id'));
806 o
.depends('mode', 'mesh');
808 o
= ss
.taboption('advanced', form
.Flag
, 'mesh_fwding', _('Forward mesh peer traffic'));
811 o
.depends('mode', 'mesh');
813 o
= ss
.taboption('advanced', form
.Value
, 'mesh_rssi_threshold', _('RSSI threshold for joining'), _('0 = not using RSSI threshold, 1 = do not change driver default'));
816 o
.datatype
= 'range(-255,1)';
817 o
.depends('mode', 'mesh');
819 o
= ss
.taboption('general', form
.Value
, 'ssid', _('<abbr title="Extended Service Set Identifier">ESSID</abbr>'));
820 o
.datatype
= 'maxlength(32)';
821 o
.depends('mode', 'ap');
822 o
.depends('mode', 'sta');
823 o
.depends('mode', 'adhoc');
824 o
.depends('mode', 'ahdemo');
825 o
.depends('mode', 'monitor');
826 o
.depends('mode', 'ap-wds');
827 o
.depends('mode', 'sta-wds');
828 o
.depends('mode', 'wds');
830 o
= ss
.taboption('general', form
.Value
, 'bssid', _('<abbr title="Basic Service Set Identifier">BSSID</abbr>'));
831 o
.datatype
= 'macaddr';
833 o
= ss
.taboption('general', widgets
.NetworkSelect
, 'network', _('Network'), _('Choose the network(s) you want to attach to this wireless interface or fill out the <em>create</em> field to define a new network.'));
837 o
.write = function(section_id
, value
) {
838 return network
.getDevice(section_id
).then(L
.bind(function(dev
) {
839 var old_networks
= dev
.getNetworks().reduce(function(o
, v
) { o
[v
.getName()] = v
; return o
}, {}),
841 values
= L
.toArray(value
),
844 for (var i
= 0; i
< values
.length
; i
++) {
845 new_networks
[values
[i
]] = true;
847 if (old_networks
[values
[i
]])
850 tasks
.push(network
.getNetwork(values
[i
]).then(L
.bind(function(name
, net
) {
851 return net
|| network
.addNetwork(name
, { proto
: 'none' });
852 }, this, values
[i
])).then(L
.bind(function(dev
, net
) {
855 net
.set('type', 'bridge');
861 for (var name
in old_networks
)
862 if (!new_networks
[name
])
863 tasks
.push(network
.getNetwork(name
).then(L
.bind(function(dev
, net
) {
865 net
.deleteDevice(dev
);
868 return Promise
.all(tasks
);
872 if (hwtype
== 'mac80211') {
873 var mode
= ss
.children
[0],
874 bssid
= ss
.children
[5],
877 mode
.value('mesh', '802.11s');
878 mode
.value('ahdemo', _('Pseudo Ad-Hoc (ahdemo)'));
879 mode
.value('monitor', _('Monitor'));
881 bssid
.depends('mode', 'adhoc');
882 bssid
.depends('mode', 'sta');
883 bssid
.depends('mode', 'sta-wds');
885 o
= ss
.taboption('macfilter', form
.ListValue
, 'macfilter', _('MAC-Address Filter'));
886 o
.depends('mode', 'ap');
887 o
.depends('mode', 'ap-wds');
888 o
.value('', _('disable'));
889 o
.value('allow', _('Allow listed only'));
890 o
.value('deny', _('Allow all except listed'));
892 o
= ss
.taboption('macfilter', form
.DynamicList
, 'maclist', _('MAC-List'));
893 o
.datatype
= 'macaddr';
894 o
.depends('macfilter', 'allow');
895 o
.depends('macfilter', 'deny');
896 //nt.mac_hints(function(mac, name) ml:value(mac, '%s (%s)' %{ mac, name }) end);
898 mode
.value('ap-wds', '%s (%s)'.format(_('Access Point'), _('WDS')));
899 mode
.value('sta-wds', '%s (%s)'.format(_('Client'), _('WDS')));
901 mode
.write = function(section_id
, value
) {
904 uci
.set('wireless', section_id
, 'mode', 'ap');
905 uci
.set('wireless', section_id
, 'wds', '1');
909 uci
.set('wireless', section_id
, 'mode', 'sta');
910 uci
.set('wireless', section_id
, 'wds', '1');
914 uci
.set('wireless', section_id
, 'mode', value
);
915 uci
.unset('wireless', section_id
, 'wds');
920 mode
.cfgvalue = function(section_id
) {
921 var mode
= uci
.get('wireless', section_id
, 'mode'),
922 wds
= uci
.get('wireless', section_id
, 'wds');
924 if (mode
== 'ap' && wds
)
926 else if (mode
== 'sta' && wds
)
932 o
= ss
.taboption('general', form
.Flag
, 'hidden', _('Hide <abbr title="Extended Service Set Identifier">ESSID</abbr>'));
933 o
.depends('mode', 'ap');
934 o
.depends('mode', 'ap-wds');
936 o
= ss
.taboption('general', form
.Flag
, 'wmm', _('WMM Mode'));
937 o
.depends('mode', 'ap');
938 o
.depends('mode', 'ap-wds');
939 o
.default = o
.enabled
;
941 o
= ss
.taboption('advanced', form
.Flag
, 'isolate', _('Isolate Clients'), _('Prevents client-to-client communication'));
942 o
.depends('mode', 'ap');
943 o
.depends('mode', 'ap-wds');
945 o
= ss
.taboption('advanced', form
.Value
, 'ifname', _('Interface name'), _('Override default interface name'));
947 o
.placeholder
= radioNet
.getIfname();
948 if (/^radio\d+\.network/.test(o
.placeholder
))
951 o
= ss
.taboption('advanced', form
.Flag
, 'short_preamble', _('Short Preamble'));
952 o
.default = o
.enabled
;
954 o
= ss
.taboption('advanced', form
.Value
, 'dtim_period', _('DTIM Interval'), _('Delivery Traffic Indication Message Interval'));
957 o
.datatype
= 'range(1,255)';
959 o
= ss
.taboption('advanced', form
.Value
, 'wpa_group_rekey', _('Time interval for rekeying GTK'), _('sec'));
962 o
.datatype
= 'uinteger';
964 o
= ss
.taboption('advanced', form
.Flag
, 'skip_inactivity_poll', _('Disable Inactivity Polling'));
966 o
.datatype
= 'uinteger';
968 o
= ss
.taboption('advanced', form
.Value
, 'max_inactivity', _('Station inactivity limit'), _('sec'));
971 o
.datatype
= 'uinteger';
973 o
= ss
.taboption('advanced', form
.Value
, 'max_listen_interval', _('Maximum allowed Listen Interval'));
975 o
.placeholder
= 65535;
976 o
.datatype
= 'uinteger';
978 o
= ss
.taboption('advanced', form
.Flag
, 'disassoc_low_ack', _('Disassociate On Low Acknowledgement'), _('Allow AP mode to disconnect STAs based on low ACK condition'));
979 o
.default = o
.enabled
;
983 encr
= o
= ss
.taboption('encryption', form
.ListValue
, 'encryption', _('Encryption'));
984 o
.depends('mode', 'ap');
985 o
.depends('mode', 'sta');
986 o
.depends('mode', 'adhoc');
987 o
.depends('mode', 'ahdemo');
988 o
.depends('mode', 'ap-wds');
989 o
.depends('mode', 'sta-wds');
990 o
.depends('mode', 'mesh');
992 o
.cfgvalue = function(section_id
) {
993 var v
= String(uci
.get('wireless', section_id
, 'encryption'));
996 else if (v
.match(/\+/))
997 return v
.replace(/\+.+$/, '');
1001 o
.write = function(section_id
, value
) {
1002 var e
= this.section
.children
.filter(function(o
) { return o
.option
== 'encryption' })[0].formvalue(section_id
),
1003 co
= this.section
.children
.filter(function(o
) { return o
.option
== 'cipher' })[0], c
= co
.formvalue(section_id
);
1005 if (value
== 'wpa' || value
== 'wpa2')
1006 uci
.unset('wireless', section_id
, 'key');
1008 if (co
.isActive(section_id
) && e
&& (c
== 'tkip' || c
== 'ccmp' || c
== 'tkip+ccmp'))
1011 uci
.set('wireless', section_id
, 'encryption', e
);
1014 o
= ss
.taboption('encryption', form
.ListValue
, 'cipher', _('Cipher'));
1015 o
.depends('encryption', 'wpa');
1016 o
.depends('encryption', 'wpa2');
1017 o
.depends('encryption', 'psk');
1018 o
.depends('encryption', 'psk2');
1019 o
.depends('encryption', 'wpa-mixed');
1020 o
.depends('encryption', 'psk-mixed');
1021 o
.value('auto', _('auto'));
1022 o
.value('ccmp', _('Force CCMP (AES)'));
1023 o
.value('tkip', _('Force TKIP'));
1024 o
.value('tkip+ccmp', _('Force TKIP and CCMP (AES)'));
1025 o
.write
= ss
.children
.filter(function(o
) { return o
.option
== 'encryption' })[0].write
;
1027 o
.cfgvalue = function(section_id
) {
1028 var v
= String(uci
.get('wireless', section_id
, 'encryption'));
1029 if (v
.match(/\+/)) {
1030 v
= v
.replace(/^[^+]+\+/, '');
1033 else if (v
== 'tkip+aes' || v
== 'aes+tkip' || v
== 'ccmp+tkip')
1040 encr
.value('none', _('No Encryption'));
1041 encr
.value('wep-open', _('WEP Open System'));
1042 encr
.value('wep-shared', _('WEP Shared Key'));
1044 if (hwtype
== 'mac80211') {
1045 var has_supplicant
= L
.hasSystemFeature('wpasupplicant'),
1046 has_hostapd
= L
.hasSystemFeature('hostapd');
1048 // Probe EAP support
1049 var has_ap_eap
= L
.hasSystemFeature('hostapd', 'eap'),
1050 has_sta_eap
= L
.hasSystemFeature('wpasupplicant', 'eap');
1052 // Probe SAE support
1053 var has_ap_sae
= L
.hasSystemFeature('hostapd', 'sae'),
1054 has_sta_sae
= L
.hasSystemFeature('wpasupplicant', 'sae');
1056 // Probe OWE support
1057 var has_ap_owe
= L
.hasSystemFeature('hostapd', 'owe'),
1058 has_sta_owe
= L
.hasSystemFeature('wpasupplicant', 'owe');
1061 if (has_hostapd
|| has_supplicant
) {
1062 encr
.value('psk', 'WPA-PSK');
1063 encr
.value('psk2', 'WPA2-PSK');
1064 encr
.value('psk-mixed', 'WPA-PSK/WPA2-PSK Mixed Mode');
1067 encr
.description
= _('WPA-Encryption requires wpa_supplicant (for client mode) or hostapd (for AP and ad-hoc mode) to be installed.');
1070 if (has_ap_sae
|| has_sta_sae
) {
1071 encr
.value('sae', 'WPA3-SAE');
1072 encr
.value('sae-mixed', 'WPA2-PSK/WPA3-SAE Mixed Mode');
1075 if (has_ap_eap
|| has_sta_eap
) {
1076 encr
.value('wpa', 'WPA-EAP');
1077 encr
.value('wpa2', 'WPA2-EAP');
1080 if (has_ap_owe
|| has_sta_owe
) {
1081 encr
.value('owe', 'OWE');
1084 encr
.crypto_support
= {
1088 'psk': has_hostapd
|| _('Requires hostapd'),
1089 'psk2': has_hostapd
|| _('Requires hostapd'),
1090 'psk-mixed': has_hostapd
|| _('Requires hostapd'),
1091 'sae': has_ap_sae
|| _('Requires hostapd with SAE support'),
1092 'sae-mixed': has_ap_sae
|| _('Requires hostapd with SAE support'),
1093 'wpa': has_ap_eap
|| _('Requires hostapd with EAP support'),
1094 'wpa2': has_ap_eap
|| _('Requires hostapd with EAP support'),
1095 'owe': has_ap_owe
|| _('Requires hostapd with OWE support')
1100 'psk': has_supplicant
|| _('Requires wpa-supplicant'),
1101 'psk2': has_supplicant
|| _('Requires wpa-supplicant'),
1102 'psk-mixed': has_supplicant
|| _('Requires wpa-supplicant'),
1103 'sae': has_sta_sae
|| _('Requires wpa-supplicant with SAE support'),
1104 'sae-mixed': has_sta_sae
|| _('Requires wpa-supplicant with SAE support'),
1105 'wpa': has_sta_eap
|| _('Requires wpa-supplicant with EAP support'),
1106 'wpa2': has_sta_eap
|| _('Requires wpa-supplicant with EAP support'),
1107 'owe': has_sta_owe
|| _('Requires wpa-supplicant with OWE support')
1112 'psk': has_supplicant
|| _('Requires wpa-supplicant'),
1113 'psk2': has_supplicant
|| _('Requires wpa-supplicant'),
1114 'psk-mixed': has_supplicant
|| _('Requires wpa-supplicant'),
1117 'sae': has_sta_sae
|| _('Requires wpa-supplicant with SAE support')
1129 encr
.crypto_support
['ap-wds'] = encr
.crypto_support
['ap'];
1130 encr
.crypto_support
['sta-wds'] = encr
.crypto_support
['sta'];
1132 encr
.validate = function(section_id
, value
) {
1133 var modeopt
= this.section
.children
.filter(function(o
) { return o
.option
== 'mode' })[0],
1134 modeval
= modeopt
.formvalue(section_id
),
1135 modetitle
= modeopt
.vallist
[modeopt
.keylist
.indexOf(modeval
)],
1136 enctitle
= this.vallist
[this.keylist
.indexOf(value
)];
1138 if (value
== 'none')
1141 if (!L
.isObject(this.crypto_support
[modeval
]) || !this.crypto_support
[modeval
].hasOwnProperty(value
))
1142 return _('The selected %s mode is incompatible with %s encryption').format(modetitle
, enctitle
);
1144 return this.crypto_support
[modeval
][value
];
1147 else if (hwtype
== 'broadcom') {
1148 encr
.value('psk', 'WPA-PSK');
1149 encr
.value('psk2', 'WPA2-PSK');
1150 encr
.value('psk+psk2', 'WPA-PSK/WPA2-PSK Mixed Mode');
1154 o
= ss
.taboption('encryption', form
.Value
, 'auth_server', _('Radius-Authentication-Server'));
1155 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1156 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1157 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1158 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1160 o
.datatype
= 'host(0)';
1162 o
= ss
.taboption('encryption', form
.Value
, 'auth_port', _('Radius-Authentication-Port'), _('Default %d').format(1812));
1163 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1164 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1165 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1166 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1168 o
.datatype
= 'port';
1170 o
= ss
.taboption('encryption', form
.Value
, 'auth_secret', _('Radius-Authentication-Secret'));
1171 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1172 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1173 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1174 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1178 o
= ss
.taboption('encryption', form
.Value
, 'acct_server', _('Radius-Accounting-Server'));
1179 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1180 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1181 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1182 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1184 o
.datatype
= 'host(0)';
1186 o
= ss
.taboption('encryption', form
.Value
, 'acct_port', _('Radius-Accounting-Port'), _('Default %d').format(1813));
1187 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1188 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1189 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1190 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1192 o
.datatype
= 'port';
1194 o
= ss
.taboption('encryption', form
.Value
, 'acct_secret', _('Radius-Accounting-Secret'));
1195 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1196 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1197 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1198 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1202 o
= ss
.taboption('encryption', form
.Value
, 'dae_client', _('DAE-Client'));
1203 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1204 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1205 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1206 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1208 o
.datatype
= 'host(0)';
1210 o
= ss
.taboption('encryption', form
.Value
, 'dae_port', _('DAE-Port'), _('Default %d').format(3799));
1211 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1212 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1213 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1214 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1216 o
.datatype
= 'port';
1218 o
= ss
.taboption('encryption', form
.Value
, 'dae_secret', _('DAE-Secret'));
1219 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1220 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1221 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1222 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1227 o
= ss
.taboption('encryption', form
.Value
, '_wpa_key', _('Key'));
1228 o
.depends('encryption', 'psk');
1229 o
.depends('encryption', 'psk2');
1230 o
.depends('encryption', 'psk+psk2');
1231 o
.depends('encryption', 'psk-mixed');
1232 o
.depends('encryption', 'sae');
1233 o
.depends('encryption', 'sae-mixed');
1234 o
.datatype
= 'wpakey';
1238 o
.cfgvalue = function(section_id
) {
1239 var key
= uci
.get('wireless', section_id
, 'key');
1240 return /^[1234]$/.test(key
) ? null : key
;
1243 o
.write = function(section_id
, value
) {
1244 uci
.set('wireless', section_id
, 'key', value
);
1245 uci
.unset('wireless', section_id
, 'key1');
1249 o
= ss
.taboption('encryption', form
.ListValue
, '_wep_key', _('Used Key Slot'));
1250 o
.depends('encryption', 'wep-open');
1251 o
.depends('encryption', 'wep-shared');
1252 o
.value('1', _('Key #%d').format(1));
1253 o
.value('2', _('Key #%d').format(2));
1254 o
.value('3', _('Key #%d').format(3));
1255 o
.value('4', _('Key #%d').format(4));
1257 o
.cfgvalue = function(section_id
) {
1258 var slot
= +uci
.get('wireless', section_id
, 'key');
1259 return (slot
>= 1 && slot
<= 4) ? slot
: 1;
1262 o
.write = function(section_id
, value
) {
1263 uci
.set('wireless', section_id
, 'key', value
);
1266 for (var slot
= 1; slot
<= 4; slot
++) {
1267 o
= ss
.taboption('encryption', form
.Value
, 'key%d'.format(slot
), _('Key #%d').format(slot
));
1268 o
.depends('encryption', 'wep-open');
1269 o
.depends('encryption', 'wep-shared');
1270 o
.datatype
= 'wepkey';
1274 o
.write = function(section_id
, value
) {
1275 if (value
!= null && (value
.length
== 5 || value
.length
== 13))
1276 value
= 's:%s'.format(value
);
1277 uci
.set('wireless', section_id
, this.option
, value
);
1282 if (hwtype
== 'mac80211') {
1283 // Probe 802.11r support (and EAP support as a proxy for Openwrt)
1284 var has_80211r
= L
.hasSystemFeature('hostapd', '11r') || L
.hasSystemFeature('hostapd', 'eap');
1286 o
= ss
.taboption('encryption', form
.Flag
, 'ieee80211r', _('802.11r Fast Transition'), _('Enables fast roaming among access points that belong to the same Mobility Domain'));
1287 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1288 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1289 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1290 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1292 o
.depends({ mode
: 'ap', encryption
: 'psk' });
1293 o
.depends({ mode
: 'ap', encryption
: 'psk2' });
1294 o
.depends({ mode
: 'ap', encryption
: 'psk-mixed' });
1295 o
.depends({ mode
: 'ap', encryption
: 'sae' });
1296 o
.depends({ mode
: 'ap', encryption
: 'sae-mixed' });
1297 o
.depends({ mode
: 'ap-wds', encryption
: 'psk' });
1298 o
.depends({ mode
: 'ap-wds', encryption
: 'psk2' });
1299 o
.depends({ mode
: 'ap-wds', encryption
: 'psk-mixed' });
1300 o
.depends({ mode
: 'ap-wds', encryption
: 'sae' });
1301 o
.depends({ mode
: 'ap-wds', encryption
: 'sae-mixed' });
1305 o
= ss
.taboption('encryption', form
.Value
, 'nasid', _('NAS ID'), _('Used for two different purposes: RADIUS NAS ID and 802.11r R0KH-ID. Not needed with normal WPA(2)-PSK.'));
1306 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1307 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1308 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1309 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1310 o
.depends({ ieee80211r
: '1' });
1313 o
= ss
.taboption('encryption', form
.Value
, 'mobility_domain', _('Mobility Domain'), _('4-character hexadecimal ID'));
1314 o
.depends({ ieee80211r
: '1' });
1315 o
.placeholder
= '4f57';
1316 o
.datatype
= 'and(hexstring,length(4))';
1319 o
= ss
.taboption('encryption', form
.Value
, 'reassociation_deadline', _('Reassociation Deadline'), _('time units (TUs / 1.024 ms) [1000-65535]'));
1320 o
.depends({ ieee80211r
: '1' });
1321 o
.placeholder
= '1000';
1322 o
.datatype
= 'range(1000,65535)';
1325 o
= ss
.taboption('encryption', form
.ListValue
, 'ft_over_ds', _('FT protocol'));
1326 o
.depends({ ieee80211r
: '1' });
1327 o
.value('1', _('FT over DS'));
1328 o
.value('0', _('FT over the Air'));
1331 o
= ss
.taboption('encryption', form
.Flag
, 'ft_psk_generate_local', _('Generate PMK locally'), _('When using a PSK, the PMK can be automatically generated. When enabled, the R0/R1 key options below are not applied. Disable this to use the R0 and R1 key options.'));
1332 o
.depends({ ieee80211r
: '1' });
1333 o
.default = o
.enabled
;
1336 o
= ss
.taboption('encryption', form
.Value
, 'r0_key_lifetime', _('R0 Key Lifetime'), _('minutes'));
1337 o
.depends({ ieee80211r
: '1' });
1338 o
.placeholder
= '10000';
1339 o
.datatype
= 'uinteger';
1342 o
= ss
.taboption('encryption', form
.Value
, 'r1_key_holder', _('R1 Key Holder'), _('6-octet identifier as a hex string - no colons'));
1343 o
.depends({ ieee80211r
: '1' });
1344 o
.placeholder
= '00004f577274';
1345 o
.datatype
= 'and(hexstring,length(12))';
1348 o
= ss
.taboption('encryption', form
.Flag
, 'pmk_r1_push', _('PMK R1 Push'));
1349 o
.depends({ ieee80211r
: '1' });
1350 o
.placeholder
= '0';
1353 o
= ss
.taboption('encryption', form
.DynamicList
, 'r0kh', _('External R0 Key Holder List'), _('List of R0KHs in the same Mobility Domain. <br />Format: MAC-address,NAS-Identifier,128-bit key as hex string. <br />This list is used to map R0KH-ID (NAS Identifier) to a destination MAC address when requesting PMK-R1 key from the R0KH that the STA used during the Initial Mobility Domain Association.'));
1354 o
.depends({ ieee80211r
: '1' });
1357 o
= ss
.taboption('encryption', form
.DynamicList
, 'r1kh', _('External R1 Key Holder List'), _ ('List of R1KHs in the same Mobility Domain. <br />Format: MAC-address,R1KH-ID as 6 octets with colons,128-bit key as hex string. <br />This list is used to map R1KH-ID to a destination MAC address when sending PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD that can request PMK-R1 keys.'));
1358 o
.depends({ ieee80211r
: '1' });
1360 // End of 802.11r options
1362 o
= ss
.taboption('encryption', form
.ListValue
, 'eap_type', _('EAP-Method'));
1363 o
.value('tls', 'TLS');
1364 o
.value('ttls', 'TTLS');
1365 o
.value('peap', 'PEAP');
1366 o
.value('fast', 'FAST');
1367 o
.depends({ mode
: 'sta', encryption
: 'wpa' });
1368 o
.depends({ mode
: 'sta', encryption
: 'wpa2' });
1369 o
.depends({ mode
: 'sta-wds', encryption
: 'wpa' });
1370 o
.depends({ mode
: 'sta-wds', encryption
: 'wpa2' });
1372 o
= ss
.taboption('encryption', form
.FileUpload
, 'ca_cert', _('Path to CA-Certificate'));
1373 o
.depends({ mode
: 'sta', encryption
: 'wpa' });
1374 o
.depends({ mode
: 'sta', encryption
: 'wpa2' });
1375 o
.depends({ mode
: 'sta-wds', encryption
: 'wpa' });
1376 o
.depends({ mode
: 'sta-wds', encryption
: 'wpa2' });
1378 o
= ss
.taboption('encryption', form
.FileUpload
, 'client_cert', _('Path to Client-Certificate'));
1379 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa' });
1380 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa2' });
1381 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa' });
1382 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa2' });
1384 o
= ss
.taboption('encryption', form
.FileUpload
, 'priv_key', _('Path to Private Key'));
1385 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa2' });
1386 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa' });
1387 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa2' });
1388 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa' });
1390 o
= ss
.taboption('encryption', form
.Value
, 'priv_key_pwd', _('Password of Private Key'));
1391 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa2' });
1392 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa' });
1393 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa2' });
1394 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa' });
1397 o
= ss
.taboption('encryption', form
.ListValue
, 'auth', _('Authentication'));
1398 o
.value('PAP', 'PAP');
1399 o
.value('CHAP', 'CHAP');
1400 o
.value('MSCHAP', 'MSCHAP');
1401 o
.value('MSCHAPV2', 'MSCHAPv2');
1404 o
.value('EAP-MSCHAPV2');
1406 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa2' });
1407 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa' });
1408 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa2' });
1409 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa' });
1410 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa2' });
1411 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa' });
1412 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa2' });
1413 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa' });
1414 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa2' });
1415 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa' });
1416 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa2' });
1417 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa' });
1419 o
.validate = function(section_id
, value
) {
1420 var eo
= this.section
.children
.filter(function(o
) { return o
.option
== 'eap_type' })[0],
1421 ev
= eo
.formvalue(section_id
);
1423 if (ev
!= 'ttls' && (value
== 'PAP' || value
== 'CHAP' || value
== 'MSCHAP' || value
== 'MSCHAPV2'))
1424 return _('This authentication type is not applicable to the selected EAP method.');
1429 o
= ss
.taboption('encryption', form
.FileUpload
, 'ca_cert2', _('Path to inner CA-Certificate'));
1430 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa' });
1431 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1432 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa' });
1433 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1435 o
= ss
.taboption('encryption', form
.FileUpload
, 'client_cert2', _('Path to inner Client-Certificate'));
1436 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa' });
1437 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1438 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa' });
1439 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1441 o
= ss
.taboption('encryption', form
.FileUpload
, 'priv_key2', _('Path to inner Private Key'));
1442 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa' });
1443 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1444 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa' });
1445 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1447 o
= ss
.taboption('encryption', form
.Value
, 'priv_key2_pwd', _('Password of inner Private Key'));
1448 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa' });
1449 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1450 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa' });
1451 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1454 o
= ss
.taboption('encryption', form
.Value
, 'identity', _('Identity'));
1455 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa2' });
1456 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa' });
1457 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa2' });
1458 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa' });
1459 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa2' });
1460 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa' });
1461 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa2' });
1462 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa' });
1463 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa2' });
1464 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa' });
1465 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa2' });
1466 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa' });
1467 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa2' });
1468 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa' });
1469 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa2' });
1470 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa' });
1472 o
= ss
.taboption('encryption', form
.Value
, 'anonymous_identity', _('Anonymous Identity'));
1473 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa2' });
1474 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa' });
1475 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa2' });
1476 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa' });
1477 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa2' });
1478 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa' });
1479 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa2' });
1480 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa' });
1481 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa2' });
1482 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa' });
1483 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa2' });
1484 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa' });
1485 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa2' });
1486 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa' });
1487 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa2' });
1488 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa' });
1490 o
= ss
.taboption('encryption', form
.Value
, 'password', _('Password'));
1491 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa2' });
1492 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa' });
1493 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa2' });
1494 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa' });
1495 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa2' });
1496 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa' });
1497 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa2' });
1498 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa' });
1499 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa2' });
1500 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa' });
1501 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa2' });
1502 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa' });
1506 if (hwtype
== 'mac80211') {
1507 // ieee802.11w options
1508 if (L
.hasSystemFeature('hostapd', '11w')) {
1509 o
= ss
.taboption('encryption', form
.ListValue
, 'ieee80211w', _('802.11w Management Frame Protection'), _("Requires the 'full' version of wpad/hostapd and support from the wifi driver <br />(as of Jan 2019: ath9k, ath10k, mwlwifi and mt76)"));
1511 o
.value('', _('Disabled (default)'));
1512 o
.value('1', _('Optional'));
1513 o
.value('2', _('Required'));
1514 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1515 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1516 o
.depends({ mode
: 'ap', encryption
: 'psk2' });
1517 o
.depends({ mode
: 'ap', encryption
: 'psk-mixed' });
1518 o
.depends({ mode
: 'ap', encryption
: 'sae' });
1519 o
.depends({ mode
: 'ap', encryption
: 'sae-mixed' });
1520 o
.depends({ mode
: 'ap', encryption
: 'owe' });
1521 o
.depends({ mode
: 'ap-wds', encryption
: 'psk2' });
1522 o
.depends({ mode
: 'ap-wds', encryption
: 'psk-mixed' });
1523 o
.depends({ mode
: 'ap-wds', encryption
: 'sae' });
1524 o
.depends({ mode
: 'ap-wds', encryption
: 'sae-mixed' });
1525 o
.depends({ mode
: 'ap-wds', encryption
: 'owe' });
1526 o
.depends({ mode
: 'sta', encryption
: 'wpa2' });
1527 o
.depends({ mode
: 'sta-wds', encryption
: 'wpa2' });
1528 o
.depends({ mode
: 'sta', encryption
: 'psk2' });
1529 o
.depends({ mode
: 'sta', encryption
: 'psk-mixed' });
1530 o
.depends({ mode
: 'sta', encryption
: 'sae' });
1531 o
.depends({ mode
: 'sta', encryption
: 'sae-mixed' });
1532 o
.depends({ mode
: 'sta', encryption
: 'owe' });
1533 o
.depends({ mode
: 'sta-wds', encryption
: 'psk2' });
1534 o
.depends({ mode
: 'sta-wds', encryption
: 'psk-mixed' });
1535 o
.depends({ mode
: 'sta-wds', encryption
: 'sae' });
1536 o
.depends({ mode
: 'sta-wds', encryption
: 'sae-mixed' });
1537 o
.depends({ mode
: 'sta-wds', encryption
: 'owe' });
1539 o
= ss
.taboption('encryption', form
.Value
, 'ieee80211w_max_timeout', _('802.11w maximum timeout'), _('802.11w Association SA Query maximum timeout'));
1540 o
.depends('ieee80211w', '1');
1541 o
.depends('ieee80211w', '2');
1542 o
.datatype
= 'uinteger';
1543 o
.placeholder
= '1000';
1546 o
= ss
.taboption('encryption', form
.Value
, 'ieee80211w_retry_timeout', _('802.11w retry timeout'), _('802.11w Association SA Query retry timeout'));
1547 o
.depends('ieee80211w', '1');
1548 o
.depends('ieee80211w', '2');
1549 o
.datatype
= 'uinteger';
1550 o
.placeholder
= '201';
1554 o
= ss
.taboption('encryption', form
.Flag
, 'wpa_disable_eapol_key_retries', _('Enable key reinstallation (KRACK) countermeasures'), _('Complicates key reinstallation attacks on the client side by disabling retransmission of EAPOL-Key frames that are used to install keys. This workaround might cause interoperability issues and reduced robustness of key negotiation especially in environments with heavy traffic load.'));
1555 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1556 o
.depends({ mode
: 'ap', encryption
: 'psk2' });
1557 o
.depends({ mode
: 'ap', encryption
: 'psk-mixed' });
1558 o
.depends({ mode
: 'ap', encryption
: 'sae' });
1559 o
.depends({ mode
: 'ap', encryption
: 'sae-mixed' });
1560 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1561 o
.depends({ mode
: 'ap-wds', encryption
: 'psk2' });
1562 o
.depends({ mode
: 'ap-wds', encryption
: 'psk-mixed' });
1563 o
.depends({ mode
: 'ap-wds', encryption
: 'sae' });
1564 o
.depends({ mode
: 'ap-wds', encryption
: 'sae-mixed' });
1566 if (L
.hasSystemFeature('hostapd', 'cli') && L
.hasSystemFeature('wpasupplicant')) {
1567 o
= ss
.taboption('encryption', form
.Flag
, 'wps_pushbutton', _('Enable WPS pushbutton, requires WPA(2)-PSK'))
1570 o
.default = o
.disabled
;
1571 o
.depends('encryption', 'psk');
1572 o
.depends('encryption', 'psk2');
1573 o
.depends('encryption', 'psk-mixed');
1580 s
.handleRemove = function(section_id
, ev
) {
1581 document
.querySelector('.cbi-section-table-row[data-sid="%s"]'.format(section_id
)).style
.opacity
= 0.5;
1582 return form
.TypedSection
.prototype.handleRemove
.apply(this, [section_id
, ev
]);
1585 s
.handleScan = function(radioDev
, ev
) {
1586 var table
= E('div', { 'class': 'table' }, [
1587 E('div', { 'class': 'tr table-titles' }, [
1588 E('div', { 'class': 'th col-2 middle center' }, _('Signal')),
1589 E('div', { 'class': 'th col-4 middle left' }, _('SSID')),
1590 E('div', { 'class': 'th col-2 middle center hide-xs' }, _('Channel')),
1591 E('div', { 'class': 'th col-2 middle left hide-xs' }, _('Mode')),
1592 E('div', { 'class': 'th col-3 middle left hide-xs' }, _('BSSID')),
1593 E('div', { 'class': 'th col-3 middle left' }, _('Encryption')),
1594 E('div', { 'class': 'th cbi-section-actions right' }, ' '),
1598 cbi_update_table(table
, [], E('em', { class: 'spinning' }, _('Starting wireless scan...')));
1600 var md
= L
.ui
.showModal(_('Join Network: Wireless Scan'), [
1602 E('div', { 'class': 'right' },
1605 'click': L
.bind(this.handleScanAbort
, this)
1609 md
.style
.maxWidth
= '90%';
1610 md
.style
.maxHeight
= 'none';
1612 this.pollFn
= L
.bind(this.handleScanRefresh
, this, radioDev
, {}, table
);
1614 L
.Poll
.add(this.pollFn
);
1618 s
.handleScanRefresh = function(radioDev
, scanCache
, table
) {
1619 return radioDev
.getScanList().then(L
.bind(function(results
) {
1622 for (var i
= 0; i
< results
.length
; i
++)
1623 scanCache
[results
[i
].bssid
] = results
[i
];
1625 for (var k
in scanCache
)
1626 if (scanCache
[k
].stale
)
1627 results
.push(scanCache
[k
]);
1629 results
.sort(function(a
, b
) {
1630 var diff
= (b
.quality
- a
.quality
) || (a
.channel
- b
.channel
);
1635 if (a
.ssid
< b
.ssid
)
1637 else if (a
.ssid
> b
.ssid
)
1640 if (a
.bssid
< b
.bssid
)
1642 else if (a
.bssid
> b
.bssid
)
1646 for (var i
= 0; i
< results
.length
; i
++) {
1647 var res
= results
[i
],
1648 qv
= res
.quality
|| 0,
1649 qm
= res
.quality_max
|| 0,
1650 q
= (qv
> 0 && qm
> 0) ? Math
.floor((100 / qm
) * qv
) : 0,
1651 s
= res
.stale
? 'opacity:0.5' : '';
1654 E('span', { 'style': s
}, render_signal_badge(q
, res
.signal
, res
.noise
)),
1655 E('span', { 'style': s
}, '%h'.format(res
.ssid
)),
1656 E('span', { 'style': s
}, '%d'.format(res
.channel
)),
1657 E('span', { 'style': s
}, '%h'.format(res
.mode
)),
1658 E('span', { 'style': s
}, '%h'.format(res
.bssid
)),
1659 E('span', { 'style': s
}, '%h'.format(network
.formatWifiEncryption(res
.encryption
))),
1660 E('div', { 'class': 'right' }, E('button', {
1661 'class': 'cbi-button cbi-button-action important',
1662 'click': L
.bind(this.handleJoin
, this, radioDev
, res
)
1663 }, _('Join Network')))
1669 cbi_update_table(table
, rows
);
1673 s
.handleScanAbort = function(ev
) {
1674 var md
= L
.dom
.parent(ev
.target
, 'div[aria-modal="true"]');
1676 md
.style
.maxWidth
= '';
1677 md
.style
.maxHeight
= '';
1681 L
.Poll
.remove(this.pollFn
);
1686 s
.handleJoinConfirm = function(radioDev
, bss
, form
, ev
) {
1687 var nameopt
= L
.toArray(form
.lookupOption('name', '_new_'))[0],
1688 passopt
= L
.toArray(form
.lookupOption('password', '_new_'))[0],
1689 zoneopt
= L
.toArray(form
.lookupOption('zone', '_new_'))[0],
1690 replopt
= L
.toArray(form
.lookupOption('replace', '_new_'))[0],
1691 nameval
= (nameopt
&& nameopt
.isValid('_new_')) ? nameopt
.formvalue('_new_') : null,
1692 passval
= (passopt
&& passopt
.isValid('_new_')) ? passopt
.formvalue('_new_') : null,
1693 zoneval
= zoneopt
? zoneopt
.formvalue('_new_') : null,
1694 enc
= L
.isObject(bss
.encryption
) ? bss
.encryption
: null,
1695 is_wep
= (enc
&& Array
.isArray(enc
.wep
)),
1696 is_psk
= (enc
&& Array
.isArray(enc
.wpa
) && Array
.isArray(enc
.authentication
) && enc
.authentication
[0] == 'psk');
1698 if (nameval
== null || (passopt
&& passval
== null))
1701 var section_id
= null;
1703 return this.map
.save(function() {
1704 if (replopt
.formvalue('_new_') == '1') {
1705 var sections
= uci
.sections('wireless', 'wifi-iface');
1707 for (var i
= 0; i
< sections
.length
; i
++)
1708 if (sections
[i
].device
== radioDev
.getName())
1709 uci
.remove('wireless', sections
[i
]['.name']);
1712 section_id
= next_free_sid(uci
.sections('wifi-iface').length
);
1714 uci
.add('wireless', 'wifi-iface', section_id
);
1715 uci
.set('wireless', section_id
, 'device', radioDev
.getName());
1716 uci
.set('wireless', section_id
, 'mode', (bss
.mode
== 'Ad-Hoc') ? 'adhoc' : 'sta');
1717 uci
.set('wireless', section_id
, 'network', nameval
);
1719 if (bss
.ssid
!= null)
1720 uci
.set('wireless', section_id
, 'ssid', bss
.ssid
);
1721 else if (bss
.bssid
!= null)
1722 uci
.set('wireless', section_id
, 'bssid', bss
.bssid
);
1725 for (var i
= enc
.wpa
.length
- 1; i
>= 0; i
--) {
1726 if (enc
.wpa
[i
] == 2) {
1727 uci
.set('wireless', section_id
, 'encryption', 'psk2');
1730 else if (enc
.wpa
[i
] == 1) {
1731 uci
.set('wireless', section_id
, 'encryption', 'psk');
1736 uci
.set('wireless', section_id
, 'key', passval
);
1739 uci
.set('wireless', section_id
, 'encryption', 'wep-open');
1740 uci
.set('wireless', section_id
, 'key', '1');
1741 uci
.set('wireless', section_id
, 'key1', passval
);
1744 var zonePromise
= zoneval
1745 ? firewall
.getZone(zoneval
).then(function(zone
) { return zone
|| firewall
.addZone(zoneval
) })
1746 : Promise
.resolve();
1748 return zonePromise
.then(function(zone
) {
1749 return network
.addNetwork(nameval
, { proto
: 'dhcp' }).then(function(net
) {
1750 firewall
.deleteNetwork(net
.getName());
1753 zone
.addNetwork(net
.getName());
1756 }).then(L
.bind(function() {
1757 return this.renderMoreOptionsModal(section_id
);
1761 s
.handleJoin = function(radioDev
, bss
, ev
) {
1762 this.handleScanAbort(ev
);
1764 var m2
= new form
.Map('wireless'),
1765 s2
= m2
.section(form
.NamedSection
, '_new_'),
1766 enc
= L
.isObject(bss
.encryption
) ? bss
.encryption
: null,
1767 is_wep
= (enc
&& Array
.isArray(enc
.wep
)),
1768 is_psk
= (enc
&& Array
.isArray(enc
.wpa
) && Array
.isArray(enc
.authentication
) && enc
.authentication
[0] == 'psk'),
1769 replace
, passphrase
, name
, zone
;
1771 s2
.render = function() {
1772 return Promise
.all([
1774 this.renderUCISection('_new_')
1775 ]).then(this.renderContents
.bind(this));
1778 replace
= s2
.option(form
.Flag
, 'replace', _('Replace wireless configuration'), _('Check this option to delete the existing networks from this radio.'));
1780 name
= s2
.option(form
.Value
, 'name', _('Name of the new network'), _('The allowed characters are: <code>A-Z</code>, <code>a-z</code>, <code>0-9</code> and <code>_</code>'));
1781 name
.datatype
= 'uciname';
1782 name
.default = 'wwan';
1783 name
.rmempty
= false;
1784 name
.validate = function(section_id
, value
) {
1785 if (uci
.get('network', value
))
1786 return _('The network name is already used');
1791 for (var i
= 2; uci
.get('network', name
.default); i
++)
1792 name
.default = 'wwan%d'.format(i
);
1794 if (is_wep
|| is_psk
) {
1795 passphrase
= s2
.option(form
.Value
, 'password', is_wep
? _('WEP passphrase') : _('WPA passphrase'), _('Specify the secret encryption key here.'));
1796 passphrase
.datatype
= is_wep
? 'wepkey' : 'wpakey';
1797 passphrase
.password
= true;
1798 passphrase
.rmempty
= false;
1801 zone
= s2
.option(widgets
.ZoneSelect
, 'zone', _('Create / Assign firewall-zone'), _('Choose the firewall zone you want to assign to this interface. Select <em>unspecified</em> to remove the interface from the associated zone or fill out the create field to define a new zone and attach the interface to it.'));
1802 zone
.default = 'wan';
1804 return m2
.render().then(L
.bind(function(nodes
) {
1805 L
.ui
.showModal(_('Joining Network: %q').replace(/%q/, '"%h"'.format(bss
.ssid
)), [
1807 E('div', { 'class': 'right' }, [
1810 'click': L
.ui
.hideModal
1811 }, _('Cancel')), ' ',
1813 'class': 'cbi-button cbi-button-positive important',
1814 'click': L
.ui
.createHandlerFn(this, 'handleJoinConfirm', radioDev
, bss
, m2
)
1817 ], 'cbi-modal').querySelector('[id="%s"] input[class][type]'.format((passphrase
|| name
).cbid('_new_'))).focus();
1821 s
.handleAdd = function(radioDev
, ev
) {
1822 var section_id
= next_free_sid(uci
.sections('wireless', 'wifi-iface').length
);
1824 uci
.unset('wireless', radioDev
.getName(), 'disabled');
1826 uci
.add('wireless', 'wifi-iface', section_id
);
1827 uci
.set('wireless', section_id
, 'device', radioDev
.getName());
1828 uci
.set('wireless', section_id
, 'mode', 'ap');
1829 uci
.set('wireless', section_id
, 'ssid', 'OpenWrt');
1830 uci
.set('wireless', section_id
, 'encryption', 'none');
1832 this.addedSection
= section_id
;
1833 return this.renderMoreOptionsModal(section_id
);
1836 o
= s
.option(form
.DummyValue
, '_badge');
1837 o
.modalonly
= false;
1838 o
.textvalue = function(section_id
) {
1839 var inst
= this.section
.lookupRadioOrNetwork(section_id
),
1840 node
= E('div', { 'class': 'center' });
1842 if (inst
.getWifiNetworks
)
1843 node
.appendChild(render_radio_badge(inst
));
1845 node
.appendChild(render_network_badge(inst
));
1850 o
= s
.option(form
.DummyValue
, '_stat');
1851 o
.modalonly
= false;
1852 o
.textvalue = function(section_id
) {
1853 var inst
= this.section
.lookupRadioOrNetwork(section_id
);
1855 if (inst
.getWifiNetworks
)
1856 return render_radio_status(inst
, this.section
.wifis
.filter(function(e
) {
1857 return (e
.getWifiDeviceName() == inst
.getName());
1860 return render_network_status(inst
);
1863 return m
.render().then(L
.bind(function(m
, nodes
) {
1864 L
.Poll
.add(L
.bind(function() {
1865 var section_ids
= m
.children
[0].cfgsections(),
1866 tasks
= [ network
.getHostHints(), network
.getWifiDevices() ];
1868 for (var i
= 0; i
< section_ids
.length
; i
++) {
1869 var row
= nodes
.querySelector('.cbi-section-table-row[data-sid="%s"]'.format(section_ids
[i
])),
1870 dsc
= row
.querySelector('[data-name="_stat"] > div'),
1871 btns
= row
.querySelectorAll('.cbi-section-actions button');
1873 if (dsc
.getAttribute('restart') == '') {
1874 dsc
.setAttribute('restart', '1');
1875 tasks
.push(L
.Request
.post(
1876 L
.url('admin/network/wireless_reconnect', section_ids
[i
]),
1877 'token=' + L
.env
.token
,
1878 { headers
: { 'Content-Type': 'application/x-www-form-urlencoded' } }
1879 ).catch(function() {}));
1881 else if (dsc
.getAttribute('restart') == '1') {
1882 dsc
.removeAttribute('restart');
1883 btns
[0].classList
.remove('spinning');
1884 btns
[0].disabled
= false;
1888 return Promise
.all(tasks
)
1889 .then(L
.bind(function(hosts_radios
) {
1892 for (var i
= 0; i
< hosts_radios
[1].length
; i
++)
1893 tasks
.push(hosts_radios
[1][i
].getWifiNetworks());
1895 return Promise
.all(tasks
).then(function(data
) {
1896 hosts_radios
[2] = [];
1898 for (var i
= 0; i
< data
.length
; i
++)
1899 hosts_radios
[2].push
.apply(hosts_radios
[2], data
[i
]);
1901 return hosts_radios
;
1904 .then(L
.bind(function(hosts_radios_wifis
) {
1907 for (var i
= 0; i
< hosts_radios_wifis
[2].length
; i
++)
1908 tasks
.push(hosts_radios_wifis
[2][i
].getAssocList());
1910 return Promise
.all(tasks
).then(function(data
) {
1911 hosts_radios_wifis
[3] = [];
1913 for (var i
= 0; i
< data
.length
; i
++) {
1914 var wifiNetwork
= hosts_radios_wifis
[2][i
],
1915 radioDev
= hosts_radios_wifis
[1].filter(function(d
) { return d
.getName() == wifiNetwork
.getWifiDeviceName() })[0];
1917 for (var j
= 0; j
< data
[i
].length
; j
++)
1918 hosts_radios_wifis
[3].push(Object
.assign({ radio
: radioDev
, network
: wifiNetwork
}, data
[i
][j
]));
1921 return hosts_radios_wifis
;
1924 .then(L
.bind(this.poll_status
, this, nodes
));
1927 var table
= E('div', { 'class': 'table', 'id': 'wifi_assoclist_table' }, [
1928 E('div', { 'class': 'tr table-titles' }, [
1929 E('div', { 'class': 'th nowrap' }, _('Network')),
1930 E('div', { 'class': 'th hide-xs' }, _('MAC-Address')),
1931 E('div', { 'class': 'th nowrap' }, _('Host')),
1932 E('div', { 'class': 'th nowrap' }, _('Signal / Noise')),
1933 E('div', { 'class': 'th nowrap' }, _('RX Rate / TX Rate'))
1937 cbi_update_table(table
, [], E('em', { 'class': 'spinning' }, _('Collecting data...')))
1939 return E([ nodes
, E('h3', _('Associated Stations')), table
]);