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 o
.load = function(section_id
) {
897 return network
.getHostHints().then(L
.bind(function(hints
) {
898 hints
.getMACHints().map(L
.bind(function(hint
) {
899 this.value(hint
[0], hint
[1] ? '%s (%s)'.format(hint
[0], hint
[1]) : hint
[0]);
902 return form
.DynamicList
.prototype.load
.apply(this, [section_id
]);
906 mode
.value('ap-wds', '%s (%s)'.format(_('Access Point'), _('WDS')));
907 mode
.value('sta-wds', '%s (%s)'.format(_('Client'), _('WDS')));
909 mode
.write = function(section_id
, value
) {
912 uci
.set('wireless', section_id
, 'mode', 'ap');
913 uci
.set('wireless', section_id
, 'wds', '1');
917 uci
.set('wireless', section_id
, 'mode', 'sta');
918 uci
.set('wireless', section_id
, 'wds', '1');
922 uci
.set('wireless', section_id
, 'mode', value
);
923 uci
.unset('wireless', section_id
, 'wds');
928 mode
.cfgvalue = function(section_id
) {
929 var mode
= uci
.get('wireless', section_id
, 'mode'),
930 wds
= uci
.get('wireless', section_id
, 'wds');
932 if (mode
== 'ap' && wds
)
934 else if (mode
== 'sta' && wds
)
940 o
= ss
.taboption('general', form
.Flag
, 'hidden', _('Hide <abbr title="Extended Service Set Identifier">ESSID</abbr>'));
941 o
.depends('mode', 'ap');
942 o
.depends('mode', 'ap-wds');
944 o
= ss
.taboption('general', form
.Flag
, 'wmm', _('WMM Mode'));
945 o
.depends('mode', 'ap');
946 o
.depends('mode', 'ap-wds');
947 o
.default = o
.enabled
;
949 o
= ss
.taboption('advanced', form
.Flag
, 'isolate', _('Isolate Clients'), _('Prevents client-to-client communication'));
950 o
.depends('mode', 'ap');
951 o
.depends('mode', 'ap-wds');
953 o
= ss
.taboption('advanced', form
.Value
, 'ifname', _('Interface name'), _('Override default interface name'));
955 o
.placeholder
= radioNet
.getIfname();
956 if (/^radio\d+\.network/.test(o
.placeholder
))
959 o
= ss
.taboption('advanced', form
.Flag
, 'short_preamble', _('Short Preamble'));
960 o
.default = o
.enabled
;
962 o
= ss
.taboption('advanced', form
.Value
, 'dtim_period', _('DTIM Interval'), _('Delivery Traffic Indication Message Interval'));
965 o
.datatype
= 'range(1,255)';
967 o
= ss
.taboption('advanced', form
.Value
, 'wpa_group_rekey', _('Time interval for rekeying GTK'), _('sec'));
970 o
.datatype
= 'uinteger';
972 o
= ss
.taboption('advanced', form
.Flag
, 'skip_inactivity_poll', _('Disable Inactivity Polling'));
974 o
.datatype
= 'uinteger';
976 o
= ss
.taboption('advanced', form
.Value
, 'max_inactivity', _('Station inactivity limit'), _('sec'));
979 o
.datatype
= 'uinteger';
981 o
= ss
.taboption('advanced', form
.Value
, 'max_listen_interval', _('Maximum allowed Listen Interval'));
983 o
.placeholder
= 65535;
984 o
.datatype
= 'uinteger';
986 o
= ss
.taboption('advanced', form
.Flag
, 'disassoc_low_ack', _('Disassociate On Low Acknowledgement'), _('Allow AP mode to disconnect STAs based on low ACK condition'));
987 o
.default = o
.enabled
;
991 encr
= o
= ss
.taboption('encryption', form
.ListValue
, 'encryption', _('Encryption'));
992 o
.depends('mode', 'ap');
993 o
.depends('mode', 'sta');
994 o
.depends('mode', 'adhoc');
995 o
.depends('mode', 'ahdemo');
996 o
.depends('mode', 'ap-wds');
997 o
.depends('mode', 'sta-wds');
998 o
.depends('mode', 'mesh');
1000 o
.cfgvalue = function(section_id
) {
1001 var v
= String(uci
.get('wireless', section_id
, 'encryption'));
1004 else if (v
.match(/\+/))
1005 return v
.replace(/\+.+$/, '');
1009 o
.write = function(section_id
, value
) {
1010 var e
= this.section
.children
.filter(function(o
) { return o
.option
== 'encryption' })[0].formvalue(section_id
),
1011 co
= this.section
.children
.filter(function(o
) { return o
.option
== 'cipher' })[0], c
= co
.formvalue(section_id
);
1013 if (value
== 'wpa' || value
== 'wpa2')
1014 uci
.unset('wireless', section_id
, 'key');
1016 if (co
.isActive(section_id
) && e
&& (c
== 'tkip' || c
== 'ccmp' || c
== 'tkip+ccmp'))
1019 uci
.set('wireless', section_id
, 'encryption', e
);
1022 o
= ss
.taboption('encryption', form
.ListValue
, 'cipher', _('Cipher'));
1023 o
.depends('encryption', 'wpa');
1024 o
.depends('encryption', 'wpa2');
1025 o
.depends('encryption', 'psk');
1026 o
.depends('encryption', 'psk2');
1027 o
.depends('encryption', 'wpa-mixed');
1028 o
.depends('encryption', 'psk-mixed');
1029 o
.value('auto', _('auto'));
1030 o
.value('ccmp', _('Force CCMP (AES)'));
1031 o
.value('tkip', _('Force TKIP'));
1032 o
.value('tkip+ccmp', _('Force TKIP and CCMP (AES)'));
1033 o
.write
= ss
.children
.filter(function(o
) { return o
.option
== 'encryption' })[0].write
;
1035 o
.cfgvalue = function(section_id
) {
1036 var v
= String(uci
.get('wireless', section_id
, 'encryption'));
1037 if (v
.match(/\+/)) {
1038 v
= v
.replace(/^[^+]+\+/, '');
1041 else if (v
== 'tkip+aes' || v
== 'aes+tkip' || v
== 'ccmp+tkip')
1048 encr
.value('none', _('No Encryption'));
1049 encr
.value('wep-open', _('WEP Open System'));
1050 encr
.value('wep-shared', _('WEP Shared Key'));
1052 if (hwtype
== 'mac80211') {
1053 var has_supplicant
= L
.hasSystemFeature('wpasupplicant'),
1054 has_hostapd
= L
.hasSystemFeature('hostapd');
1056 // Probe EAP support
1057 var has_ap_eap
= L
.hasSystemFeature('hostapd', 'eap'),
1058 has_sta_eap
= L
.hasSystemFeature('wpasupplicant', 'eap');
1060 // Probe SAE support
1061 var has_ap_sae
= L
.hasSystemFeature('hostapd', 'sae'),
1062 has_sta_sae
= L
.hasSystemFeature('wpasupplicant', 'sae');
1064 // Probe OWE support
1065 var has_ap_owe
= L
.hasSystemFeature('hostapd', 'owe'),
1066 has_sta_owe
= L
.hasSystemFeature('wpasupplicant', 'owe');
1069 if (has_hostapd
|| has_supplicant
) {
1070 encr
.value('psk', 'WPA-PSK');
1071 encr
.value('psk2', 'WPA2-PSK');
1072 encr
.value('psk-mixed', 'WPA-PSK/WPA2-PSK Mixed Mode');
1075 encr
.description
= _('WPA-Encryption requires wpa_supplicant (for client mode) or hostapd (for AP and ad-hoc mode) to be installed.');
1078 if (has_ap_sae
|| has_sta_sae
) {
1079 encr
.value('sae', 'WPA3-SAE');
1080 encr
.value('sae-mixed', 'WPA2-PSK/WPA3-SAE Mixed Mode');
1083 if (has_ap_eap
|| has_sta_eap
) {
1084 encr
.value('wpa', 'WPA-EAP');
1085 encr
.value('wpa2', 'WPA2-EAP');
1088 if (has_ap_owe
|| has_sta_owe
) {
1089 encr
.value('owe', 'OWE');
1092 encr
.crypto_support
= {
1096 'psk': has_hostapd
|| _('Requires hostapd'),
1097 'psk2': has_hostapd
|| _('Requires hostapd'),
1098 'psk-mixed': has_hostapd
|| _('Requires hostapd'),
1099 'sae': has_ap_sae
|| _('Requires hostapd with SAE support'),
1100 'sae-mixed': has_ap_sae
|| _('Requires hostapd with SAE support'),
1101 'wpa': has_ap_eap
|| _('Requires hostapd with EAP support'),
1102 'wpa2': has_ap_eap
|| _('Requires hostapd with EAP support'),
1103 'owe': has_ap_owe
|| _('Requires hostapd with OWE support')
1108 'psk': has_supplicant
|| _('Requires wpa-supplicant'),
1109 'psk2': has_supplicant
|| _('Requires wpa-supplicant'),
1110 'psk-mixed': has_supplicant
|| _('Requires wpa-supplicant'),
1111 'sae': has_sta_sae
|| _('Requires wpa-supplicant with SAE support'),
1112 'sae-mixed': has_sta_sae
|| _('Requires wpa-supplicant with SAE support'),
1113 'wpa': has_sta_eap
|| _('Requires wpa-supplicant with EAP support'),
1114 'wpa2': has_sta_eap
|| _('Requires wpa-supplicant with EAP support'),
1115 'owe': has_sta_owe
|| _('Requires wpa-supplicant with OWE support')
1120 'psk': has_supplicant
|| _('Requires wpa-supplicant'),
1121 'psk2': has_supplicant
|| _('Requires wpa-supplicant'),
1122 'psk-mixed': has_supplicant
|| _('Requires wpa-supplicant'),
1125 'sae': has_sta_sae
|| _('Requires wpa-supplicant with SAE support')
1137 encr
.crypto_support
['ap-wds'] = encr
.crypto_support
['ap'];
1138 encr
.crypto_support
['sta-wds'] = encr
.crypto_support
['sta'];
1140 encr
.validate = function(section_id
, value
) {
1141 var modeopt
= this.section
.children
.filter(function(o
) { return o
.option
== 'mode' })[0],
1142 modeval
= modeopt
.formvalue(section_id
),
1143 modetitle
= modeopt
.vallist
[modeopt
.keylist
.indexOf(modeval
)],
1144 enctitle
= this.vallist
[this.keylist
.indexOf(value
)];
1146 if (value
== 'none')
1149 if (!L
.isObject(this.crypto_support
[modeval
]) || !this.crypto_support
[modeval
].hasOwnProperty(value
))
1150 return _('The selected %s mode is incompatible with %s encryption').format(modetitle
, enctitle
);
1152 return this.crypto_support
[modeval
][value
];
1155 else if (hwtype
== 'broadcom') {
1156 encr
.value('psk', 'WPA-PSK');
1157 encr
.value('psk2', 'WPA2-PSK');
1158 encr
.value('psk+psk2', 'WPA-PSK/WPA2-PSK Mixed Mode');
1162 o
= ss
.taboption('encryption', form
.Value
, 'auth_server', _('Radius-Authentication-Server'));
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
= 'host(0)';
1170 o
= ss
.taboption('encryption', form
.Value
, 'auth_port', _('Radius-Authentication-Port'), _('Default %d').format(1812));
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' });
1176 o
.datatype
= 'port';
1178 o
= ss
.taboption('encryption', form
.Value
, 'auth_secret', _('Radius-Authentication-Secret'));
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' });
1186 o
= ss
.taboption('encryption', form
.Value
, 'acct_server', _('Radius-Accounting-Server'));
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
= 'host(0)';
1194 o
= ss
.taboption('encryption', form
.Value
, 'acct_port', _('Radius-Accounting-Port'), _('Default %d').format(1813));
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' });
1200 o
.datatype
= 'port';
1202 o
= ss
.taboption('encryption', form
.Value
, 'acct_secret', _('Radius-Accounting-Secret'));
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' });
1210 o
= ss
.taboption('encryption', form
.Value
, 'dae_client', _('DAE-Client'));
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
= 'host(0)';
1218 o
= ss
.taboption('encryption', form
.Value
, 'dae_port', _('DAE-Port'), _('Default %d').format(3799));
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' });
1224 o
.datatype
= 'port';
1226 o
= ss
.taboption('encryption', form
.Value
, 'dae_secret', _('DAE-Secret'));
1227 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1228 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1229 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1230 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1235 o
= ss
.taboption('encryption', form
.Value
, '_wpa_key', _('Key'));
1236 o
.depends('encryption', 'psk');
1237 o
.depends('encryption', 'psk2');
1238 o
.depends('encryption', 'psk+psk2');
1239 o
.depends('encryption', 'psk-mixed');
1240 o
.depends('encryption', 'sae');
1241 o
.depends('encryption', 'sae-mixed');
1242 o
.datatype
= 'wpakey';
1246 o
.cfgvalue = function(section_id
) {
1247 var key
= uci
.get('wireless', section_id
, 'key');
1248 return /^[1234]$/.test(key
) ? null : key
;
1251 o
.write = function(section_id
, value
) {
1252 uci
.set('wireless', section_id
, 'key', value
);
1253 uci
.unset('wireless', section_id
, 'key1');
1257 o
= ss
.taboption('encryption', form
.ListValue
, '_wep_key', _('Used Key Slot'));
1258 o
.depends('encryption', 'wep-open');
1259 o
.depends('encryption', 'wep-shared');
1260 o
.value('1', _('Key #%d').format(1));
1261 o
.value('2', _('Key #%d').format(2));
1262 o
.value('3', _('Key #%d').format(3));
1263 o
.value('4', _('Key #%d').format(4));
1265 o
.cfgvalue = function(section_id
) {
1266 var slot
= +uci
.get('wireless', section_id
, 'key');
1267 return (slot
>= 1 && slot
<= 4) ? slot
: 1;
1270 o
.write = function(section_id
, value
) {
1271 uci
.set('wireless', section_id
, 'key', value
);
1274 for (var slot
= 1; slot
<= 4; slot
++) {
1275 o
= ss
.taboption('encryption', form
.Value
, 'key%d'.format(slot
), _('Key #%d').format(slot
));
1276 o
.depends('encryption', 'wep-open');
1277 o
.depends('encryption', 'wep-shared');
1278 o
.datatype
= 'wepkey';
1282 o
.write = function(section_id
, value
) {
1283 if (value
!= null && (value
.length
== 5 || value
.length
== 13))
1284 value
= 's:%s'.format(value
);
1285 uci
.set('wireless', section_id
, this.option
, value
);
1290 if (hwtype
== 'mac80211') {
1291 // Probe 802.11r support (and EAP support as a proxy for Openwrt)
1292 var has_80211r
= L
.hasSystemFeature('hostapd', '11r') || L
.hasSystemFeature('hostapd', 'eap');
1294 o
= ss
.taboption('encryption', form
.Flag
, 'ieee80211r', _('802.11r Fast Transition'), _('Enables fast roaming among access points that belong to the same Mobility Domain'));
1295 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1296 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1297 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1298 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1300 o
.depends({ mode
: 'ap', encryption
: 'psk' });
1301 o
.depends({ mode
: 'ap', encryption
: 'psk2' });
1302 o
.depends({ mode
: 'ap', encryption
: 'psk-mixed' });
1303 o
.depends({ mode
: 'ap', encryption
: 'sae' });
1304 o
.depends({ mode
: 'ap', encryption
: 'sae-mixed' });
1305 o
.depends({ mode
: 'ap-wds', encryption
: 'psk' });
1306 o
.depends({ mode
: 'ap-wds', encryption
: 'psk2' });
1307 o
.depends({ mode
: 'ap-wds', encryption
: 'psk-mixed' });
1308 o
.depends({ mode
: 'ap-wds', encryption
: 'sae' });
1309 o
.depends({ mode
: 'ap-wds', encryption
: 'sae-mixed' });
1313 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.'));
1314 o
.depends({ mode
: 'ap', encryption
: 'wpa' });
1315 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1316 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa' });
1317 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1318 o
.depends({ ieee80211r
: '1' });
1321 o
= ss
.taboption('encryption', form
.Value
, 'mobility_domain', _('Mobility Domain'), _('4-character hexadecimal ID'));
1322 o
.depends({ ieee80211r
: '1' });
1323 o
.placeholder
= '4f57';
1324 o
.datatype
= 'and(hexstring,length(4))';
1327 o
= ss
.taboption('encryption', form
.Value
, 'reassociation_deadline', _('Reassociation Deadline'), _('time units (TUs / 1.024 ms) [1000-65535]'));
1328 o
.depends({ ieee80211r
: '1' });
1329 o
.placeholder
= '1000';
1330 o
.datatype
= 'range(1000,65535)';
1333 o
= ss
.taboption('encryption', form
.ListValue
, 'ft_over_ds', _('FT protocol'));
1334 o
.depends({ ieee80211r
: '1' });
1335 o
.value('1', _('FT over DS'));
1336 o
.value('0', _('FT over the Air'));
1339 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.'));
1340 o
.depends({ ieee80211r
: '1' });
1341 o
.default = o
.enabled
;
1344 o
= ss
.taboption('encryption', form
.Value
, 'r0_key_lifetime', _('R0 Key Lifetime'), _('minutes'));
1345 o
.depends({ ieee80211r
: '1' });
1346 o
.placeholder
= '10000';
1347 o
.datatype
= 'uinteger';
1350 o
= ss
.taboption('encryption', form
.Value
, 'r1_key_holder', _('R1 Key Holder'), _('6-octet identifier as a hex string - no colons'));
1351 o
.depends({ ieee80211r
: '1' });
1352 o
.placeholder
= '00004f577274';
1353 o
.datatype
= 'and(hexstring,length(12))';
1356 o
= ss
.taboption('encryption', form
.Flag
, 'pmk_r1_push', _('PMK R1 Push'));
1357 o
.depends({ ieee80211r
: '1' });
1358 o
.placeholder
= '0';
1361 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.'));
1362 o
.depends({ ieee80211r
: '1' });
1365 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.'));
1366 o
.depends({ ieee80211r
: '1' });
1368 // End of 802.11r options
1370 o
= ss
.taboption('encryption', form
.ListValue
, 'eap_type', _('EAP-Method'));
1371 o
.value('tls', 'TLS');
1372 o
.value('ttls', 'TTLS');
1373 o
.value('peap', 'PEAP');
1374 o
.value('fast', 'FAST');
1375 o
.depends({ mode
: 'sta', encryption
: 'wpa' });
1376 o
.depends({ mode
: 'sta', encryption
: 'wpa2' });
1377 o
.depends({ mode
: 'sta-wds', encryption
: 'wpa' });
1378 o
.depends({ mode
: 'sta-wds', encryption
: 'wpa2' });
1380 o
= ss
.taboption('encryption', form
.FileUpload
, 'ca_cert', _('Path to CA-Certificate'));
1381 o
.depends({ mode
: 'sta', encryption
: 'wpa' });
1382 o
.depends({ mode
: 'sta', encryption
: 'wpa2' });
1383 o
.depends({ mode
: 'sta-wds', encryption
: 'wpa' });
1384 o
.depends({ mode
: 'sta-wds', encryption
: 'wpa2' });
1386 o
= ss
.taboption('encryption', form
.FileUpload
, 'client_cert', _('Path to Client-Certificate'));
1387 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa' });
1388 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa2' });
1389 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa' });
1390 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa2' });
1392 o
= ss
.taboption('encryption', form
.FileUpload
, 'priv_key', _('Path to Private Key'));
1393 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa2' });
1394 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa' });
1395 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa2' });
1396 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa' });
1398 o
= ss
.taboption('encryption', form
.Value
, 'priv_key_pwd', _('Password of Private Key'));
1399 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa2' });
1400 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa' });
1401 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa2' });
1402 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa' });
1405 o
= ss
.taboption('encryption', form
.ListValue
, 'auth', _('Authentication'));
1406 o
.value('PAP', 'PAP');
1407 o
.value('CHAP', 'CHAP');
1408 o
.value('MSCHAP', 'MSCHAP');
1409 o
.value('MSCHAPV2', 'MSCHAPv2');
1412 o
.value('EAP-MSCHAPV2');
1414 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa2' });
1415 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa' });
1416 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa2' });
1417 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa' });
1418 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa2' });
1419 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa' });
1420 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa2' });
1421 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa' });
1422 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa2' });
1423 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa' });
1424 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa2' });
1425 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa' });
1427 o
.validate = function(section_id
, value
) {
1428 var eo
= this.section
.children
.filter(function(o
) { return o
.option
== 'eap_type' })[0],
1429 ev
= eo
.formvalue(section_id
);
1431 if (ev
!= 'ttls' && (value
== 'PAP' || value
== 'CHAP' || value
== 'MSCHAP' || value
== 'MSCHAPV2'))
1432 return _('This authentication type is not applicable to the selected EAP method.');
1437 o
= ss
.taboption('encryption', form
.FileUpload
, 'ca_cert2', _('Path to inner CA-Certificate'));
1438 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa' });
1439 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1440 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa' });
1441 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1443 o
= ss
.taboption('encryption', form
.FileUpload
, 'client_cert2', _('Path to inner Client-Certificate'));
1444 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa' });
1445 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1446 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa' });
1447 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1449 o
= ss
.taboption('encryption', form
.FileUpload
, 'priv_key2', _('Path to inner Private Key'));
1450 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa' });
1451 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1452 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa' });
1453 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1455 o
= ss
.taboption('encryption', form
.Value
, 'priv_key2_pwd', _('Password of inner Private Key'));
1456 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa' });
1457 o
.depends({ mode
: 'sta', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1458 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa' });
1459 o
.depends({ mode
: 'sta-wds', auth
: 'EAP-TLS', encryption
: 'wpa2' });
1462 o
= ss
.taboption('encryption', form
.Value
, 'identity', _('Identity'));
1463 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa2' });
1464 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa' });
1465 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa2' });
1466 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa' });
1467 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa2' });
1468 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa' });
1469 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa2' });
1470 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa' });
1471 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa2' });
1472 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa' });
1473 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa2' });
1474 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa' });
1475 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa2' });
1476 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa' });
1477 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa2' });
1478 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa' });
1480 o
= ss
.taboption('encryption', form
.Value
, 'anonymous_identity', _('Anonymous Identity'));
1481 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa2' });
1482 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa' });
1483 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa2' });
1484 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa' });
1485 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa2' });
1486 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa' });
1487 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa2' });
1488 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa' });
1489 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa2' });
1490 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa' });
1491 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa2' });
1492 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa' });
1493 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa2' });
1494 o
.depends({ mode
: 'sta', eap_type
: 'tls', encryption
: 'wpa' });
1495 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa2' });
1496 o
.depends({ mode
: 'sta-wds', eap_type
: 'tls', encryption
: 'wpa' });
1498 o
= ss
.taboption('encryption', form
.Value
, 'password', _('Password'));
1499 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa2' });
1500 o
.depends({ mode
: 'sta', eap_type
: 'fast', encryption
: 'wpa' });
1501 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa2' });
1502 o
.depends({ mode
: 'sta', eap_type
: 'peap', encryption
: 'wpa' });
1503 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa2' });
1504 o
.depends({ mode
: 'sta', eap_type
: 'ttls', encryption
: 'wpa' });
1505 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa2' });
1506 o
.depends({ mode
: 'sta-wds', eap_type
: 'fast', encryption
: 'wpa' });
1507 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa2' });
1508 o
.depends({ mode
: 'sta-wds', eap_type
: 'peap', encryption
: 'wpa' });
1509 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa2' });
1510 o
.depends({ mode
: 'sta-wds', eap_type
: 'ttls', encryption
: 'wpa' });
1514 if (hwtype
== 'mac80211') {
1515 // ieee802.11w options
1516 if (L
.hasSystemFeature('hostapd', '11w')) {
1517 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)"));
1519 o
.value('', _('Disabled (default)'));
1520 o
.value('1', _('Optional'));
1521 o
.value('2', _('Required'));
1522 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1523 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1524 o
.depends({ mode
: 'ap', encryption
: 'psk2' });
1525 o
.depends({ mode
: 'ap', encryption
: 'psk-mixed' });
1526 o
.depends({ mode
: 'ap', encryption
: 'sae' });
1527 o
.depends({ mode
: 'ap', encryption
: 'sae-mixed' });
1528 o
.depends({ mode
: 'ap', encryption
: 'owe' });
1529 o
.depends({ mode
: 'ap-wds', encryption
: 'psk2' });
1530 o
.depends({ mode
: 'ap-wds', encryption
: 'psk-mixed' });
1531 o
.depends({ mode
: 'ap-wds', encryption
: 'sae' });
1532 o
.depends({ mode
: 'ap-wds', encryption
: 'sae-mixed' });
1533 o
.depends({ mode
: 'ap-wds', encryption
: 'owe' });
1534 o
.depends({ mode
: 'sta', encryption
: 'wpa2' });
1535 o
.depends({ mode
: 'sta-wds', encryption
: 'wpa2' });
1536 o
.depends({ mode
: 'sta', encryption
: 'psk2' });
1537 o
.depends({ mode
: 'sta', encryption
: 'psk-mixed' });
1538 o
.depends({ mode
: 'sta', encryption
: 'sae' });
1539 o
.depends({ mode
: 'sta', encryption
: 'sae-mixed' });
1540 o
.depends({ mode
: 'sta', encryption
: 'owe' });
1541 o
.depends({ mode
: 'sta-wds', encryption
: 'psk2' });
1542 o
.depends({ mode
: 'sta-wds', encryption
: 'psk-mixed' });
1543 o
.depends({ mode
: 'sta-wds', encryption
: 'sae' });
1544 o
.depends({ mode
: 'sta-wds', encryption
: 'sae-mixed' });
1545 o
.depends({ mode
: 'sta-wds', encryption
: 'owe' });
1547 o
= ss
.taboption('encryption', form
.Value
, 'ieee80211w_max_timeout', _('802.11w maximum timeout'), _('802.11w Association SA Query maximum timeout'));
1548 o
.depends('ieee80211w', '1');
1549 o
.depends('ieee80211w', '2');
1550 o
.datatype
= 'uinteger';
1551 o
.placeholder
= '1000';
1554 o
= ss
.taboption('encryption', form
.Value
, 'ieee80211w_retry_timeout', _('802.11w retry timeout'), _('802.11w Association SA Query retry timeout'));
1555 o
.depends('ieee80211w', '1');
1556 o
.depends('ieee80211w', '2');
1557 o
.datatype
= 'uinteger';
1558 o
.placeholder
= '201';
1562 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.'));
1563 o
.depends({ mode
: 'ap', encryption
: 'wpa2' });
1564 o
.depends({ mode
: 'ap', encryption
: 'psk2' });
1565 o
.depends({ mode
: 'ap', encryption
: 'psk-mixed' });
1566 o
.depends({ mode
: 'ap', encryption
: 'sae' });
1567 o
.depends({ mode
: 'ap', encryption
: 'sae-mixed' });
1568 o
.depends({ mode
: 'ap-wds', encryption
: 'wpa2' });
1569 o
.depends({ mode
: 'ap-wds', encryption
: 'psk2' });
1570 o
.depends({ mode
: 'ap-wds', encryption
: 'psk-mixed' });
1571 o
.depends({ mode
: 'ap-wds', encryption
: 'sae' });
1572 o
.depends({ mode
: 'ap-wds', encryption
: 'sae-mixed' });
1574 if (L
.hasSystemFeature('hostapd', 'cli') && L
.hasSystemFeature('wpasupplicant')) {
1575 o
= ss
.taboption('encryption', form
.Flag
, 'wps_pushbutton', _('Enable WPS pushbutton, requires WPA(2)-PSK'))
1578 o
.default = o
.disabled
;
1579 o
.depends('encryption', 'psk');
1580 o
.depends('encryption', 'psk2');
1581 o
.depends('encryption', 'psk-mixed');
1588 s
.handleRemove = function(section_id
, ev
) {
1589 document
.querySelector('.cbi-section-table-row[data-sid="%s"]'.format(section_id
)).style
.opacity
= 0.5;
1590 return form
.TypedSection
.prototype.handleRemove
.apply(this, [section_id
, ev
]);
1593 s
.handleScan = function(radioDev
, ev
) {
1594 var table
= E('div', { 'class': 'table' }, [
1595 E('div', { 'class': 'tr table-titles' }, [
1596 E('div', { 'class': 'th col-2 middle center' }, _('Signal')),
1597 E('div', { 'class': 'th col-4 middle left' }, _('SSID')),
1598 E('div', { 'class': 'th col-2 middle center hide-xs' }, _('Channel')),
1599 E('div', { 'class': 'th col-2 middle left hide-xs' }, _('Mode')),
1600 E('div', { 'class': 'th col-3 middle left hide-xs' }, _('BSSID')),
1601 E('div', { 'class': 'th col-3 middle left' }, _('Encryption')),
1602 E('div', { 'class': 'th cbi-section-actions right' }, ' '),
1606 cbi_update_table(table
, [], E('em', { class: 'spinning' }, _('Starting wireless scan...')));
1608 var md
= L
.ui
.showModal(_('Join Network: Wireless Scan'), [
1610 E('div', { 'class': 'right' },
1613 'click': L
.bind(this.handleScanAbort
, this)
1617 md
.style
.maxWidth
= '90%';
1618 md
.style
.maxHeight
= 'none';
1620 this.pollFn
= L
.bind(this.handleScanRefresh
, this, radioDev
, {}, table
);
1622 L
.Poll
.add(this.pollFn
);
1626 s
.handleScanRefresh = function(radioDev
, scanCache
, table
) {
1627 return radioDev
.getScanList().then(L
.bind(function(results
) {
1630 for (var i
= 0; i
< results
.length
; i
++)
1631 scanCache
[results
[i
].bssid
] = results
[i
];
1633 for (var k
in scanCache
)
1634 if (scanCache
[k
].stale
)
1635 results
.push(scanCache
[k
]);
1637 results
.sort(function(a
, b
) {
1638 var diff
= (b
.quality
- a
.quality
) || (a
.channel
- b
.channel
);
1643 if (a
.ssid
< b
.ssid
)
1645 else if (a
.ssid
> b
.ssid
)
1648 if (a
.bssid
< b
.bssid
)
1650 else if (a
.bssid
> b
.bssid
)
1654 for (var i
= 0; i
< results
.length
; i
++) {
1655 var res
= results
[i
],
1656 qv
= res
.quality
|| 0,
1657 qm
= res
.quality_max
|| 0,
1658 q
= (qv
> 0 && qm
> 0) ? Math
.floor((100 / qm
) * qv
) : 0,
1659 s
= res
.stale
? 'opacity:0.5' : '';
1662 E('span', { 'style': s
}, render_signal_badge(q
, res
.signal
, res
.noise
)),
1663 E('span', { 'style': s
}, '%h'.format(res
.ssid
)),
1664 E('span', { 'style': s
}, '%d'.format(res
.channel
)),
1665 E('span', { 'style': s
}, '%h'.format(res
.mode
)),
1666 E('span', { 'style': s
}, '%h'.format(res
.bssid
)),
1667 E('span', { 'style': s
}, '%h'.format(network
.formatWifiEncryption(res
.encryption
))),
1668 E('div', { 'class': 'right' }, E('button', {
1669 'class': 'cbi-button cbi-button-action important',
1670 'click': L
.bind(this.handleJoin
, this, radioDev
, res
)
1671 }, _('Join Network')))
1677 cbi_update_table(table
, rows
);
1681 s
.handleScanAbort = function(ev
) {
1682 var md
= L
.dom
.parent(ev
.target
, 'div[aria-modal="true"]');
1684 md
.style
.maxWidth
= '';
1685 md
.style
.maxHeight
= '';
1689 L
.Poll
.remove(this.pollFn
);
1694 s
.handleJoinConfirm = function(radioDev
, bss
, form
, ev
) {
1695 var nameopt
= L
.toArray(form
.lookupOption('name', '_new_'))[0],
1696 passopt
= L
.toArray(form
.lookupOption('password', '_new_'))[0],
1697 zoneopt
= L
.toArray(form
.lookupOption('zone', '_new_'))[0],
1698 replopt
= L
.toArray(form
.lookupOption('replace', '_new_'))[0],
1699 nameval
= (nameopt
&& nameopt
.isValid('_new_')) ? nameopt
.formvalue('_new_') : null,
1700 passval
= (passopt
&& passopt
.isValid('_new_')) ? passopt
.formvalue('_new_') : null,
1701 zoneval
= zoneopt
? zoneopt
.formvalue('_new_') : null,
1702 enc
= L
.isObject(bss
.encryption
) ? bss
.encryption
: null,
1703 is_wep
= (enc
&& Array
.isArray(enc
.wep
)),
1704 is_psk
= (enc
&& Array
.isArray(enc
.wpa
) && Array
.isArray(enc
.authentication
) && enc
.authentication
[0] == 'psk');
1706 if (nameval
== null || (passopt
&& passval
== null))
1709 var section_id
= null;
1711 return this.map
.save(function() {
1712 if (replopt
.formvalue('_new_') == '1') {
1713 var sections
= uci
.sections('wireless', 'wifi-iface');
1715 for (var i
= 0; i
< sections
.length
; i
++)
1716 if (sections
[i
].device
== radioDev
.getName())
1717 uci
.remove('wireless', sections
[i
]['.name']);
1720 section_id
= next_free_sid(uci
.sections('wifi-iface').length
);
1722 uci
.add('wireless', 'wifi-iface', section_id
);
1723 uci
.set('wireless', section_id
, 'device', radioDev
.getName());
1724 uci
.set('wireless', section_id
, 'mode', (bss
.mode
== 'Ad-Hoc') ? 'adhoc' : 'sta');
1725 uci
.set('wireless', section_id
, 'network', nameval
);
1727 if (bss
.ssid
!= null)
1728 uci
.set('wireless', section_id
, 'ssid', bss
.ssid
);
1729 else if (bss
.bssid
!= null)
1730 uci
.set('wireless', section_id
, 'bssid', bss
.bssid
);
1733 for (var i
= enc
.wpa
.length
- 1; i
>= 0; i
--) {
1734 if (enc
.wpa
[i
] == 2) {
1735 uci
.set('wireless', section_id
, 'encryption', 'psk2');
1738 else if (enc
.wpa
[i
] == 1) {
1739 uci
.set('wireless', section_id
, 'encryption', 'psk');
1744 uci
.set('wireless', section_id
, 'key', passval
);
1747 uci
.set('wireless', section_id
, 'encryption', 'wep-open');
1748 uci
.set('wireless', section_id
, 'key', '1');
1749 uci
.set('wireless', section_id
, 'key1', passval
);
1752 var zonePromise
= zoneval
1753 ? firewall
.getZone(zoneval
).then(function(zone
) { return zone
|| firewall
.addZone(zoneval
) })
1754 : Promise
.resolve();
1756 return zonePromise
.then(function(zone
) {
1757 return network
.addNetwork(nameval
, { proto
: 'dhcp' }).then(function(net
) {
1758 firewall
.deleteNetwork(net
.getName());
1761 zone
.addNetwork(net
.getName());
1764 }).then(L
.bind(function() {
1765 return this.renderMoreOptionsModal(section_id
);
1769 s
.handleJoin = function(radioDev
, bss
, ev
) {
1770 this.handleScanAbort(ev
);
1772 var m2
= new form
.Map('wireless'),
1773 s2
= m2
.section(form
.NamedSection
, '_new_'),
1774 enc
= L
.isObject(bss
.encryption
) ? bss
.encryption
: null,
1775 is_wep
= (enc
&& Array
.isArray(enc
.wep
)),
1776 is_psk
= (enc
&& Array
.isArray(enc
.wpa
) && Array
.isArray(enc
.authentication
) && enc
.authentication
[0] == 'psk'),
1777 replace
, passphrase
, name
, zone
;
1779 s2
.render = function() {
1780 return Promise
.all([
1782 this.renderUCISection('_new_')
1783 ]).then(this.renderContents
.bind(this));
1786 replace
= s2
.option(form
.Flag
, 'replace', _('Replace wireless configuration'), _('Check this option to delete the existing networks from this radio.'));
1788 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>'));
1789 name
.datatype
= 'uciname';
1790 name
.default = 'wwan';
1791 name
.rmempty
= false;
1792 name
.validate = function(section_id
, value
) {
1793 if (uci
.get('network', value
))
1794 return _('The network name is already used');
1799 for (var i
= 2; uci
.get('network', name
.default); i
++)
1800 name
.default = 'wwan%d'.format(i
);
1802 if (is_wep
|| is_psk
) {
1803 passphrase
= s2
.option(form
.Value
, 'password', is_wep
? _('WEP passphrase') : _('WPA passphrase'), _('Specify the secret encryption key here.'));
1804 passphrase
.datatype
= is_wep
? 'wepkey' : 'wpakey';
1805 passphrase
.password
= true;
1806 passphrase
.rmempty
= false;
1809 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 <em>create</em> field to define a new zone and attach the interface to it.'));
1810 zone
.default = 'wan';
1812 return m2
.render().then(L
.bind(function(nodes
) {
1813 L
.ui
.showModal(_('Joining Network: %q').replace(/%q/, '"%h"'.format(bss
.ssid
)), [
1815 E('div', { 'class': 'right' }, [
1818 'click': L
.ui
.hideModal
1819 }, _('Cancel')), ' ',
1821 'class': 'cbi-button cbi-button-positive important',
1822 'click': L
.ui
.createHandlerFn(this, 'handleJoinConfirm', radioDev
, bss
, m2
)
1825 ], 'cbi-modal').querySelector('[id="%s"] input[class][type]'.format((passphrase
|| name
).cbid('_new_'))).focus();
1829 s
.handleAdd = function(radioDev
, ev
) {
1830 var section_id
= next_free_sid(uci
.sections('wireless', 'wifi-iface').length
);
1832 uci
.unset('wireless', radioDev
.getName(), 'disabled');
1834 uci
.add('wireless', 'wifi-iface', section_id
);
1835 uci
.set('wireless', section_id
, 'device', radioDev
.getName());
1836 uci
.set('wireless', section_id
, 'mode', 'ap');
1837 uci
.set('wireless', section_id
, 'ssid', 'OpenWrt');
1838 uci
.set('wireless', section_id
, 'encryption', 'none');
1840 this.addedSection
= section_id
;
1841 return this.renderMoreOptionsModal(section_id
);
1844 o
= s
.option(form
.DummyValue
, '_badge');
1845 o
.modalonly
= false;
1846 o
.textvalue = function(section_id
) {
1847 var inst
= this.section
.lookupRadioOrNetwork(section_id
),
1848 node
= E('div', { 'class': 'center' });
1850 if (inst
.getWifiNetworks
)
1851 node
.appendChild(render_radio_badge(inst
));
1853 node
.appendChild(render_network_badge(inst
));
1858 o
= s
.option(form
.DummyValue
, '_stat');
1859 o
.modalonly
= false;
1860 o
.textvalue = function(section_id
) {
1861 var inst
= this.section
.lookupRadioOrNetwork(section_id
);
1863 if (inst
.getWifiNetworks
)
1864 return render_radio_status(inst
, this.section
.wifis
.filter(function(e
) {
1865 return (e
.getWifiDeviceName() == inst
.getName());
1868 return render_network_status(inst
);
1871 return m
.render().then(L
.bind(function(m
, nodes
) {
1872 L
.Poll
.add(L
.bind(function() {
1873 var section_ids
= m
.children
[0].cfgsections(),
1874 tasks
= [ network
.getHostHints(), network
.getWifiDevices() ];
1876 for (var i
= 0; i
< section_ids
.length
; i
++) {
1877 var row
= nodes
.querySelector('.cbi-section-table-row[data-sid="%s"]'.format(section_ids
[i
])),
1878 dsc
= row
.querySelector('[data-name="_stat"] > div'),
1879 btns
= row
.querySelectorAll('.cbi-section-actions button');
1881 if (dsc
.getAttribute('restart') == '') {
1882 dsc
.setAttribute('restart', '1');
1883 tasks
.push(L
.Request
.post(
1884 L
.url('admin/network/wireless_reconnect', section_ids
[i
]),
1885 'token=' + L
.env
.token
,
1886 { headers
: { 'Content-Type': 'application/x-www-form-urlencoded' } }
1887 ).catch(function() {}));
1889 else if (dsc
.getAttribute('restart') == '1') {
1890 dsc
.removeAttribute('restart');
1891 btns
[0].classList
.remove('spinning');
1892 btns
[0].disabled
= false;
1896 return Promise
.all(tasks
)
1897 .then(L
.bind(function(hosts_radios
) {
1900 for (var i
= 0; i
< hosts_radios
[1].length
; i
++)
1901 tasks
.push(hosts_radios
[1][i
].getWifiNetworks());
1903 return Promise
.all(tasks
).then(function(data
) {
1904 hosts_radios
[2] = [];
1906 for (var i
= 0; i
< data
.length
; i
++)
1907 hosts_radios
[2].push
.apply(hosts_radios
[2], data
[i
]);
1909 return hosts_radios
;
1912 .then(L
.bind(function(hosts_radios_wifis
) {
1915 for (var i
= 0; i
< hosts_radios_wifis
[2].length
; i
++)
1916 tasks
.push(hosts_radios_wifis
[2][i
].getAssocList());
1918 return Promise
.all(tasks
).then(function(data
) {
1919 hosts_radios_wifis
[3] = [];
1921 for (var i
= 0; i
< data
.length
; i
++) {
1922 var wifiNetwork
= hosts_radios_wifis
[2][i
],
1923 radioDev
= hosts_radios_wifis
[1].filter(function(d
) { return d
.getName() == wifiNetwork
.getWifiDeviceName() })[0];
1925 for (var j
= 0; j
< data
[i
].length
; j
++)
1926 hosts_radios_wifis
[3].push(Object
.assign({ radio
: radioDev
, network
: wifiNetwork
}, data
[i
][j
]));
1929 return hosts_radios_wifis
;
1932 .then(L
.bind(this.poll_status
, this, nodes
));
1935 var table
= E('div', { 'class': 'table', 'id': 'wifi_assoclist_table' }, [
1936 E('div', { 'class': 'tr table-titles' }, [
1937 E('div', { 'class': 'th nowrap' }, _('Network')),
1938 E('div', { 'class': 'th hide-xs' }, _('MAC-Address')),
1939 E('div', { 'class': 'th nowrap' }, _('Host')),
1940 E('div', { 'class': 'th nowrap' }, _('Signal / Noise')),
1941 E('div', { 'class': 'th nowrap' }, _('RX Rate / TX Rate'))
1945 cbi_update_table(table
, [], E('em', { 'class': 'spinning' }, _('Collecting data...')))
1947 return E([ nodes
, E('h3', _('Associated Stations')), table
]);