6fedd2f41a4625d6a6a2b9d14cc6bae8f47e67a3
[project/luci.git] / applications / luci-app-usteer / htdocs / luci-static / resources / view / usteer / usteer.js
1 'use strict';
2 'require view';
3 'require rpc';
4 'require poll';
5 'require dom';
6 'require ui';
7 'require form';
8 'require uci';
9 'require tools.widgets as widgets';
10
11 var Hosts, Remotehosts, Remoteinfo, Localinfo, Clients;
12
13 var dns_cache = [];
14
15 function SplitWlan(wlan) {
16 var wlansplit = [];
17 if (typeof wlan.split('#')[1] !== 'undefined') {
18 wlansplit=wlan.split('#');
19 if (typeof dns_cache[wlansplit[0]] !== 'undefined') {
20 wlansplit[0]=dns_cache[wlansplit[0]];
21 }
22 } else {
23 wlansplit[0]=_('This AP');
24 wlansplit[1]=wlan;
25 }
26 return wlansplit;
27 }
28
29
30 function collectHearingClient(client_table_entries, mac) {
31 if (typeof Clients[mac] !== 'undefined') {
32 for (var wlanc in Clients[mac]) {
33 var SSID = '';
34 var freq = 0;
35 if (typeof Localinfo[wlanc] !== 'undefined') {
36 SSID = Localinfo[wlanc]['ssid'];
37 freq = Localinfo[wlanc]['freq'];
38 }
39 if (typeof Remoteinfo[wlanc] !== 'undefined') {
40 SSID = Remoteinfo[wlanc]['ssid'];
41 freq = Remoteinfo[wlanc]['freq'];
42 }
43 var wlansplit=SplitWlan(wlanc);
44 client_table_entries.push([
45 '<nobr>' + '%h'.format(wlansplit[0]) + '</nobr>',
46 '<nobr>' + '%h'.format(wlansplit[1]) + '</nobr>',
47 SSID,
48 freq,
49 Clients[mac][wlanc]['connected'] === true ? 'Yes' : 'No',
50 typeof Clients[mac][wlanc]['signal'] !== 'undefined' ? Clients[mac][wlanc]['signal'] : ''
51 ]);
52 }
53 }
54 }
55
56 var HearingMap = form.DummyValue.extend({
57 renderWidget: function () {
58 var body = E([
59 E('h3', _('Hearing map')),
60 E('div', _('Refresh page to get new mac addresses to show up'))
61 ]);
62 for (var mac in Clients) {
63 var maciphost = '';
64 maciphost = mac;
65 var macUp = mac.toUpperCase();
66 var macn = macUp.replace(/:/g,'');
67 if (typeof Hosts[macUp] !== 'undefined') {
68 if ((String(Hosts[macUp]['ipaddrs'][0]).length > 0) && (typeof Hosts[macUp]['ipaddrs'][0] !== 'undefined'))
69 maciphost += '\u2003' + Hosts[macUp]['ipaddrs'];
70 if ((String(Hosts[macUp]['name']).length > 0) && (typeof Hosts[macUp]['name'] !== 'undefined'))
71 maciphost += '\u2003%h'.format(Hosts[macUp]['name']);
72 }
73 body.appendChild(
74 E('h4', maciphost)
75 );
76 var client_table = E('table', {'class': 'table cbi-section-table','id':'client_table'+macn}, [
77 E('tr', {'class': 'tr table-titles'}, [
78 E('th', {'class': 'th'}, _('AP','Name or IP address of access point')),
79 E('th', {'class': 'th'}, _('Interface name','interface name in usteer overview')),
80 E('th', {'class': 'th', 'style': 'width:25%'}, _('SSID')),
81 E('th', {'class': 'th', 'style': 'width:15%'}, _('Frequency','BSS operating frequency in usteer overview')),
82 E('th', {'class': 'th', 'style': 'width:15%'}, _('Connected','Connection state in usteer overview')),
83 E('th', {'class': 'th', 'style': 'width:15%'}, _('Signal','Signal strength reported by wireless station in usteer overview'))
84 ])
85 ]);
86 var client_table_entries = [];
87 collectHearingClient(client_table_entries, mac);
88 cbi_update_table(client_table, client_table_entries, E('em', _('No data')));
89 body.appendChild(client_table);
90 }
91 return E('div', {'class': 'cbi-section cbi-tblsection'}, [body]);
92 }
93 });
94
95
96
97
98 function collectWlanAPInfoEntries(connectioninfo_table_entries, wlanAPInfos) {
99 for (var wlan in wlanAPInfos) {
100 var wlansplit=SplitWlan(wlan);
101 connectioninfo_table_entries.push([
102 '<nobr>' + '%h'.format(wlansplit[0]) + '</nobr>',
103 '<nobr>' + '%h'.format(wlansplit[1]) + '</nobr>',
104 wlanAPInfos[wlan]['bssid'],
105 wlanAPInfos[wlan]['ssid'],
106 wlanAPInfos[wlan]['freq'],
107 wlanAPInfos[wlan]['n_assoc'],
108 wlanAPInfos[wlan]['noise'],
109 wlanAPInfos[wlan]['load'],
110 wlanAPInfos[wlan]['max_assoc'],
111 typeof wlanAPInfos[wlan]['roam_events']['source'] !== 'undefined' ? wlanAPInfos[wlan]['roam_events']['source'] : '',
112 typeof wlanAPInfos[wlan]['roam_events']['target'] !== 'undefined' ? wlanAPInfos[wlan]['roam_events']['target'] : ''
113 ]);
114 }
115 };
116
117 function tootltip(mac, IP, hostname) {
118 var body= E([]);
119 body.appendChild(E('div', mac));
120 if (typeof IP !== 'undefined') {
121 for (var IPaddr in IP['ipaddrs']) body.appendChild(E('div', IP['ipaddrs'][IPaddr]));
122 for (var IPaddr in IP['ip6addrs']) body.appendChild(E('div', IP['ip6addrs'][IPaddr]));;
123 }
124 if (hostname !== '') {
125 body.appendChild(E('div', '%h'.format(hostname)));
126 }
127 return body;
128 }
129
130 function collectWlanAPInfos(compactconnectioninfo_table_entries, wlanAPInfos) {
131 for (var wlan in wlanAPInfos) {
132 var hostl = E([]);
133 for (var mac in Clients) {
134 if (typeof Clients[mac] !== 'undefined')
135 if (typeof Clients[mac][wlan] !== 'undefined')
136 if (String(Clients[mac][wlan]['connected']).valueOf() === 'true') {
137 var foundname = mac;
138 var IP = '';
139 var hostname = '';
140 var macUp = mac.toUpperCase();
141 if (typeof Hosts[macUp] !== 'undefined') {
142 if ((typeof Hosts[macUp]['ipaddrs'][0] !== 'undefined') && (String(Hosts[macUp]['ipaddrs'][0]).length > 0)) {
143 IP = Hosts[macUp]['ipaddrs'][0];
144 foundname = IP;
145 }
146 if ((typeof Hosts[macUp]['name'] !== 'undefined') && (String(Hosts[macUp]['name']).length > 0)) {
147 hostname = Hosts[macUp]['name'];
148 foundname = hostname;
149 }
150 }
151 hostl.appendChild(
152 E('span', { 'class': 'cbi-tooltip-container' }, [
153 '%h\u2003'.format(foundname),
154 E('div', { 'class': 'cbi-tooltip' }, tootltip(mac, Hosts[macUp], hostname))
155 ])
156 );
157 }
158 }
159 var wlansplit=SplitWlan(wlan);
160 compactconnectioninfo_table_entries.push([
161 '<nobr>' + '%h'.format(wlansplit[0]) + '</nobr>',
162 '<nobr>' + '%h'.format(wlansplit[1]) + '</nobr>',
163 wlanAPInfos[wlan]['ssid'],
164 wlanAPInfos[wlan]['freq'],
165 wlanAPInfos[wlan]['load'],
166 wlanAPInfos[wlan]['n_assoc'],
167 hostl
168 ]);
169 }
170 };
171
172 var callNetworkRrdnsLookup = rpc.declare({
173 object: 'network.rrdns',
174 method: 'lookup',
175 params: [ 'addrs', 'timeout', 'limit' ],
176 expect: { '': {} }
177 });
178
179
180 function collectRemoteHosts (remotehosttableentries,Remotehosts) {
181 const getUndefinedDnsCacheIPs = (Remotehosts, dns_cache) =>
182 Object.keys(Remotehosts).filter(IPaddr => !dns_cache.hasOwnProperty(IPaddr));
183
184 var ipAddrs = getUndefinedDnsCacheIPs(Remotehosts, dns_cache);
185
186 L.resolveDefault(callNetworkRrdnsLookup(ipAddrs, 1000, 1000), {}).then(function(replies) {
187 for (var address of ipAddrs) {
188 if (!address)
189 continue;
190 if (replies[address]) {
191 dns_cache[address] = replies[address];
192 continue;
193 } else {
194 dns_cache[address]=Hosts[
195 Object.keys(Hosts).find(mac =>
196 ((typeof Hosts[mac]['name'] !== 'undefined') &&
197 ((Object.keys(Hosts[mac]['ip6addrs']).find(IPaddr2 => (address === Hosts[mac]['ip6addrs'][IPaddr2]))) ||
198 (Object.keys(Hosts[mac]['ipaddrs']).find(IPaddr2 => (address === Hosts[mac]['ipaddrs'][IPaddr2])))))
199 )
200 ]['name'];
201 }
202 }
203 });
204
205 for (var IPaddr in Remotehosts) {
206 remotehosttableentries.push([IPaddr,'%h'.format(dns_cache[IPaddr]),Remotehosts[IPaddr]['id']]);
207 }
208 }
209
210
211 var Clientinfooverview = form.DummyValue.extend({
212 renderWidget: function () {
213 var body = E([
214 E('h3', _('Remote hosts'))
215 ]);
216 var remotehost_table = E('table', {'class': 'table cbi-section-table', 'id': 'remotehost_table'}, [
217 E('tr', {'class': 'tr table-titles'}, [
218 E('th', {'class': 'th'}, _('IP address')),
219 E('th', {'class': 'th'}, _('Hostname')),
220 E('th', {'class': 'th'}, _('Identifier'))
221 ])
222 ]);
223 var remotehosttableentries = [];
224 collectRemoteHosts(remotehosttableentries,Remotehosts);
225 cbi_update_table(remotehost_table, remotehosttableentries, E('em', _('No data')));
226 body.appendChild(remotehost_table);
227 body.appendChild(
228 E('h3', _('Client list'))
229 );
230 var connectioninfo_table = E('table', {'class': 'table cbi-section-table', 'id': 'connectioninfo_table'}, [
231 E('tr', {'class': 'tr table-titles'}, [
232 E('th', {'class': 'th'}, _('AP','Name or IP address of access point')),
233 E('th', {'class': 'th'}, _('Interface name','interface name in usteer overview')),
234 E('th', {'class': 'th'}, _('BSSID')),
235 E('th', {'class': 'th'}, _('SSID')),
236 E('th', {'class': 'th'}, _('Frequency','BSS operating frequency in usteer overview')),
237 E('th', {'class': 'th'}, _('N','Number of associated clients in usteer overview')),
238 E('th', {'class': 'th'}, _('Noise','Channel noise in usteer overview')),
239 E('th', {'class': 'th'}, _('Load','Channel load in usteer overview')),
240 E('th', {'class': 'th'}, _('Max assoc','Max associated clients in usteer overview')),
241 E('th', {'class': 'th'}, _('Roam src','Roam source in usteer overview')),
242 E('th', {'class': 'th'}, _('Roam tgt','Roam target in usteer overview'))
243 ])
244 ]);
245 var connectioninfo_table_entries = [];
246 collectWlanAPInfoEntries(connectioninfo_table_entries, Localinfo);
247 collectWlanAPInfoEntries(connectioninfo_table_entries, Remoteinfo);
248
249 cbi_update_table(connectioninfo_table, connectioninfo_table_entries, E('em', _('No data')));
250 body.appendChild(connectioninfo_table);
251 var compactconnectioninfo_table = E('table', {'class': 'table cbi-section-table','id': 'compactconnectioninfo_table'}, [
252 E('tr', {'class': 'tr table-titles'}, [
253 E('th', {'class': 'th'}, _('AP','Name or IP address of access point')),
254 E('th', {'class': 'th'}, _('Interface name','interface name in usteer overview')),
255 E('th', {'class': 'th'}, _('SSID')),
256 E('th', {'class': 'th'}, _('Frequency', 'BSS operating frequency in usteer overview')),
257 E('th', {'class': 'th'}, _('Load', 'Channel load in usteer overview')),
258 E('th', {'class': 'th'}, _('N', 'Number of associated clients in usteer overview')),
259 E('th', {'class': 'th'}, _('Host', 'host hint in usteer overview'))
260 ])
261 ]);
262 var compactconnectioninfo_table_entries = [];
263 collectWlanAPInfos(compactconnectioninfo_table_entries, Localinfo);
264 collectWlanAPInfos(compactconnectioninfo_table_entries, Remoteinfo);
265 cbi_update_table(compactconnectioninfo_table, compactconnectioninfo_table_entries, E('em', _('No data')));
266 body.appendChild(compactconnectioninfo_table);
267 return E('div', {'class': 'cbi-section cbi-tblsection'}, [body]);
268 }
269 });
270
271 var Settingstitle = form.DummyValue.extend({
272 renderWidget: function () {
273 var body = E([
274 E('h3', _('Settings')),
275 E('div',
276 _('The first four options below are mandatory.') + ' ' +
277 _('Also be sure to enable rrm reports, 80211kv, etc.') + ' ' +
278 _('See <a %s>documentation</a>').format('href="https://openwrt.org/docs/guide-user/network/wifi/usteer"')
279 ),
280 ]);
281 return E('div', [body]);
282 }
283 });
284
285 var footerdata;
286 var Settingsfooter = form.DummyValue.extend({
287 renderWidget: function () {
288 var body = E([
289 E('body', footerdata),
290 ]);
291 return E('div', {'style': 'width:100%'}, [footerdata]);
292 }
293 });
294
295
296 return view.extend({
297 callHostHints: rpc.declare({
298 object: 'luci-rpc',
299 method: 'getHostHints',
300 expect: {'': {}}
301 }),
302 callGetRemotehosts: rpc.declare({
303 object: 'usteer',
304 method: 'remote_hosts',
305 expect: {'': {}}
306 }),
307 callGetRemoteinfo: rpc.declare({
308 object: 'usteer',
309 method: 'remote_info',
310 expect: {'': {}}
311 }),
312 callGetLocalinfo: rpc.declare({
313 object: 'usteer',
314 method: 'local_info',
315 expect: {'': {}}
316 }),
317 callGetClients: rpc.declare({
318 object: 'usteer',
319 method: 'get_clients',
320 expect: {'': {}}
321 }),
322 load: function () {
323 return Promise.all([
324 rpc.list('usteer'),
325 this.callHostHints().catch (function (){return null;}),
326 this.callGetRemotehosts().catch (function (){return null;}),
327 this.callGetRemoteinfo().catch (function (){return null;}),
328 this.callGetLocalinfo().catch (function (){return null;}),
329 this.callGetClients().catch (function (){return null;})
330 ]);
331 },
332
333 poll_status: function(nodes, data) {
334
335 Hosts = data[1];
336 Remotehosts = data[2];
337 Remoteinfo = data[3];
338 Localinfo = data[4];
339 Clients = data[5];
340
341 var remotehosttableentries = [];
342 collectRemoteHosts(remotehosttableentries,Remotehosts);
343 cbi_update_table(nodes.querySelector('#remotehost_table'), remotehosttableentries, E('em', _('No data')));
344
345 var connectioninfo_table_entries = [];
346 collectWlanAPInfoEntries(connectioninfo_table_entries, Localinfo);
347 collectWlanAPInfoEntries(connectioninfo_table_entries, Remoteinfo);
348 cbi_update_table(nodes.querySelector('#connectioninfo_table'), connectioninfo_table_entries, E('em', _('No data')));
349
350 var compactconnectioninfo_table_entries = [];
351 collectWlanAPInfos(compactconnectioninfo_table_entries, Localinfo);
352 collectWlanAPInfos(compactconnectioninfo_table_entries, Remoteinfo);
353 cbi_update_table(nodes.querySelector('#compactconnectioninfo_table'), compactconnectioninfo_table_entries, E('em', _('No data')));
354
355 for (var mac in Clients) {
356 var macn = mac.toUpperCase().replace(/:/g,'');
357 var client_table_entries = [];
358 collectHearingClient(client_table_entries, mac);
359 cbi_update_table(nodes.querySelector('#client_table'+macn), client_table_entries, E('em', _('No data')));
360 }
361 return;
362 },
363
364 render: function (data) {
365 var m, s, o;
366
367 if (!('usteer' in data[0])) {
368 m = new form.Map('usteer', _('Usteer'),
369 _('Usteer is not running. Make sure it is installed and running.') +
370 _('To start it running try %s').format('<code>/etc/init.d/usteer start</code>')
371 );
372 return m.render();
373 }
374
375 m = new form.Map('usteer', _('Usteer'));
376
377 Hosts = data[1];
378 Remotehosts = data[2];
379 Remoteinfo = data[3];
380 Localinfo = data[4];
381 Clients = data[5];
382
383 s = m.section(form.TypedSection);
384 s.anonymous = true;
385 s.tab('status', _('Status'));
386 s.tab('hearingmap', _('Hearing map'));
387 s.tab('settings', _('Settings'));
388
389 o = s.taboption('status', Clientinfooverview);
390 o.readonly = true;
391
392 o = s.taboption('hearingmap', HearingMap);
393 o.readonly = true;
394
395 o = s.taboption('settings', Settingstitle);
396 o.readonly = true;
397
398 o = s.taboption('settings', widgets.NetworkSelect, 'network', _('Network'), _('The network interface for inter-AP communication'));
399
400 o = s.taboption('settings', form.Flag, 'syslog', _('Log messages to syslog'));
401 o.default = '1';
402 o.rmempty = false;
403
404 o = s.taboption('settings', form.Flag, 'ipv6', _('IPv6 mode'), _('Use IPv6 for remote exchange'));
405 o.rmempty = false;
406
407 o = s.taboption('settings', form.ListValue, 'debug_level', _('Debug level'));
408 o.value('0', _('Fatal'));
409 o.value('1', _('Info'));
410 o.value('2', _('Verbose'));
411 o.value('3', _('Some debug'));
412 o.value('4', _('Network packet info'));
413 o.value('5', _('All debug messages'));
414 o.rmempty = false;
415 o.editable = true;
416
417 o = s.taboption('settings', form.Value, 'max_neighbour_reports', _('Max neighbour reports'), _('Maximum number of neighbor reports set for a node'));
418 o.optional = true;
419 o.placeholder = 8;
420 o.datatype = 'uinteger';
421
422 o = s.taboption('settings', form.Value, 'sta_block_timeout', _('Sta block timeout'), _('Maximum amount of time (ms) a station may be blocked due to policy decisions'));
423 o.optional = true;
424 o.placeholder = 30000;
425 o.datatype = 'uinteger';
426
427 o = s.taboption('settings', form.Value, 'local_sta_timeout', _('Local sta timeout'), _('Maximum amount of time (ms) a local unconnected station is tracked'));
428 o.optional = true;
429 o.placeholder = 12000;
430 o.datatype = 'uinteger';
431
432 o = s.taboption('settings', form.Value, 'measurement_report_timeout', _('Measurement report timeout'), _('Maximum amount of time (ms) a measurement report is stored'));
433 o.optional = true;
434 o.placeholder = 120000;
435 o.datatype = 'uinteger';
436
437 o = s.taboption('settings', form.Value, 'local_sta_update', _('Local sta update'), _('Local station information update interval (ms)'));
438 o.optional = true;
439 o.placeholder = 1000;
440 o.datatype = 'uinteger';
441
442 o = s.taboption('settings', form.Value, 'max_retry_band', _('Max retry band'), _('Maximum number of consecutive times a station may be blocked by policy'));
443 o.optional = true;
444 o.placeholder = 5;
445 o.datatype = 'uinteger';
446
447 o = s.taboption('settings', form.Value, 'seen_policy_timeout', _('Seen policy timeout'), _('Maximum idle time of a station entry (ms) to be considered for policy decisions'));
448 o.optional = true;
449 o.placeholder = 30000;
450 o.datatype = 'uinteger';
451
452 o = s.taboption('settings', form.Value, 'load_balancing_threshold', _('Load balancing threshold'), _('Minimum number of stations delta between APs before load balancing policy is active'));
453 o.optional = true;
454 o.placeholder = 0;
455 o.datatype = 'uinteger';
456
457 o = s.taboption('settings', form.Value, 'band_steering_threshold', _('Band steering threshold'), _('Minimum number of stations delta between bands before band steering policy is active'));
458 o.optional = true;
459 o.placeholder = 5;
460 o.datatype = 'uinteger';
461
462 o = s.taboption('settings', form.Value, 'remote_update_interval', _('Remote update interval'), _('Interval (ms) between sending state updates to other APs'));
463 o.optional = true;
464 o.placeholder = 1000;
465 o.datatype = 'uinteger';
466
467 o = s.taboption('settings', form.Value, 'remote_node_timeout', _('Remote node timeout'), _('Number of remote update intervals after which a remote-node is deleted'));
468 o.optional = true;
469 o.placeholder = 10;
470 o.datatype = 'uinteger';
471
472 o = s.taboption('settings', form.Flag, 'assoc_steering', _('Assoc steering'), _('Allow rejecting assoc requests for steering purposes'));
473 o.optional = true;
474
475 o = s.taboption('settings', form.Flag, 'probe_steering', _('Probe steering'), _('Allow ignoring probe requests for steering purposes'));
476 o.optional = true;
477
478 o = s.taboption('settings', form.Value, 'min_connect_snr', _('Min connect SNR'), _('Minimum signal-to-noise ratio or signal level (dBm) to allow connections'));
479 o.optional = true;
480 o.placeholder = 0;
481 o.datatype = 'integer';
482
483 o = s.taboption('settings', form.Value, 'min_snr', _('Min SNR'), _('Minimum signal-to-noise ratio or signal level (dBm) to remain connected'));
484 o.optional = true;
485 o.placeholder = 0;
486 o.datatype = 'integer';
487
488 o = s.taboption('settings', form.Value, 'min_snr_kick_delay', _('Min SNR kick delay'), _('Timeout after which a station with SNR < min_SNR will be kicked'));
489 o.optional = true;
490 o.placeholder = 5000;
491 o.datatype = 'uinteger';
492
493 o = s.taboption('settings', form.Value, 'roam_process_timeout', _('Roam process timeout'), _('Timeout (in ms) after which a association following a disassociation is not seen as a roam'));
494 o.optional = true;
495 o.placeholder = 5000;
496 o.datatype = 'uinteger';
497
498 o = s.taboption('settings', form.Value, 'roam_scan_snr', _('Roam scan SNR'), _('Minimum signal-to-noise ratio or signal level (dBm) before attempting to trigger client scans for roam'));
499 o.optional = true;
500 o.placeholder = 0;
501 o.datatype = 'integer';
502
503 o = s.taboption('settings', form.Value, 'roam_scan_tries', _('Roam scan tries'), _('Maximum number of client roaming scan trigger attempts'));
504 o.optional = true;
505 o.placeholder = 3;
506 o.datatype = 'uinteger';
507
508 o = s.taboption('settings', form.Value, 'roam_scan_timeout', _('Roam scan timeout'),
509 _('Retry scanning when roam_scan_tries is exceeded after this timeout (in ms).') +
510 _(' In case this option is disabled, the client is kicked instead')
511 );
512 o.optional = true;
513 o.placeholder = 0;
514 o.datatype = 'uinteger';
515
516 o = s.taboption('settings', form.Value, 'roam_scan_interval', _('Roam scan interval'), _('Minimum time (ms) between client roaming scan trigger attempts'));
517 o.optional = true;
518 o.placeholder = 10000;
519 o.datatype = 'uinteger';
520
521 o = s.taboption('settings', form.Value, 'roam_trigger_snr', _('Roam trigger SNR'), _('Minimum signal-to-noise ratio or signal level (dBm) before attempting to trigger forced client roaming'));
522 o.optional = true;
523 o.placeholder = 0;
524 o.datatype = 'integer';
525
526 o = s.taboption('settings', form.Value, 'roam_trigger_interval', _('Roam trigger interval'), _('Minimum time (ms) between client roaming trigger attempts'));
527 o.optional = true;
528 o.placeholder = 60000;
529 o.datatype = 'uinteger';
530
531 o = s.taboption('settings', form.Value, 'roam_kick_delay', _('Roam kick delay'), _('Timeout (in 100ms beacon intervals) for client roam requests'));
532 o.optional = true;
533 o.placeholder = 100;
534 o.datatype = 'uinteger';
535
536 o = s.taboption('settings', form.Value, 'signal_diff_threshold', _('Signal diff threshold'), _('Minimum signal strength difference until AP steering policy is active'));
537 o.optional = true;
538 o.placeholder = 0;
539 o.datatype = 'uinteger';
540
541 o = s.taboption('settings', form.Value, 'initial_connect_delay', _('Initial connect delay'), _('Initial delay (ms) before responding to probe requests (to allow other APs to see packets as well)'));
542 o.optional = true;
543 o.placeholder = 0;
544 o.datatype = 'uinteger';
545
546 o = s.taboption('settings', form.Flag, 'load_kick_enabled', _('Load kick enabled'), _('Enable kicking client on excessive channel load'));
547 o.optional = true;
548
549 o = s.taboption('settings', form.Value, 'load_kick_threshold', _('Load kick threshold'), _('Minimum channel load (%) before kicking clients'));
550 o.optional = true;
551 o.placeholder = 75;
552 o.datatype = 'uinteger';
553
554 o = s.taboption('settings', form.Value, 'load_kick_delay', _('Load kick delay'), _('Minimum amount of time (ms) that channel load is above threshold before starting to kick clients'));
555 o.optional = true;
556 o.placeholder = 10000;
557 o.datatype = 'uinteger';
558
559 o = s.taboption('settings', form.Value, 'load_kick_min_clients', _('Load kick min clients'), _('Minimum number of connected clients before kicking based on channel load'));
560 o.optional = true;
561 o.placeholder = 10;
562 o.datatype = 'uinteger';
563
564 o = s.taboption('settings', form.Value, 'load_kick_reason_code', _('Load kick reason code'),
565 _('Reason code on client kick based on channel load.') + ' Default: WLAN_REASON_DISASSOC_AP_BUSY)'
566 );
567 o.optional = true;
568 o.placeholder = 5;
569 o.datatype = 'uinteger';
570
571 o = s.taboption('settings', form.Value, 'band_steering_interval', _('Band steering interval'), _('Attempting to steer clients to a higher frequency-band every n ms. A value of 0 disables band-steering.'));
572 o.optional = true;
573 o.placeholder = 120000;
574 o.datatype = 'uinteger';
575
576 o = s.taboption('settings', form.Value, 'band_steering_min_snr', _('Band steering min SNR'), _('Minimal SNR or absolute signal a device has to maintain over band_steering_interval to be steered to a higher frequency band.'));
577 o.optional = true;
578 o.placeholder = -60;
579 o.datatype = 'integer';
580
581 o = s.taboption('settings', form.Value, 'link_measurement_interval', _('Link measurement interval'),
582 _('Interval (ms) the device is sent a link-measurement request to help assess the bi-directional link quality.') +
583 _('Setting the interval to 0 disables link-measurements.')
584 );
585 o.optional = true;
586 o.placeholder = 30000;
587 o.datatype = 'uinteger';
588
589 o = s.taboption('settings', form.Value, 'node_up_script', _('Node up script'), _('Script to run after bringing up a node'));
590 o.optional = true;
591 o.datatype = 'string';
592
593 o = s.taboption('settings', form.MultiValue, 'event_log_types', _('Event log types'), _('Message types to include in log.'));
594 o.value('probe_req_accept');
595 o.value('probe_req_deny');
596 o.value('auth_req_accept');
597 o.value('auth_req_deny');
598 o.value('assoc_req_accept');
599 o.value('assoc_req_deny');
600 o.value('load_kick_trigger');
601 o.value('load_kick_reset');
602 o.value('load_kick_min_clients');
603 o.value('load_kick_no_client');
604 o.value('load_kick_client');
605 o.value('signal_kick');
606 o.optional = true;
607 o.datatype = 'list(string)';
608
609 o = s.taboption('settings', form.DynamicList, 'ssid_list', _('SSID list'), _('List of SSIDs to enable steering on'));
610 o.optional = true;
611 o.datatype = 'list(string)';
612
613 footerdata = this.super('addFooter', []);
614 o = s.taboption('settings', Settingsfooter);
615 o.readonly = true;
616
617 return m.render().then(L.bind(function(m, nodes) {
618 poll.add(L.bind(function() {
619 return Promise.all([
620 rpc.list('usteer'),
621 this.callHostHints().catch (function (){return null;}),
622 this.callGetRemotehosts().catch (function (){return null;}),
623 this.callGetRemoteinfo().catch (function (){return null;}),
624 this.callGetLocalinfo().catch (function (){return null;}),
625 this.callGetClients().catch (function (){return null;})
626 ]).then(L.bind(this.poll_status, this, nodes));
627 }, this), 5);
628 return nodes;
629 }, this, m));
630 },
631
632
633 addFooter: function () {
634 return null;
635 },
636 });