luci-app-usteer: Removed save/apply button on status pages, and added "friendly"...
authorRamon Van Gorkom <Ramon00c00@gmail.com>
Sun, 17 Dec 2023 11:42:14 +0000 (12:42 +0100)
committerPaul Donald <newtwen@gmail.com>
Thu, 28 Dec 2023 18:49:54 +0000 (18:49 +0000)
Added refresh of status page

Add the template pot file that is needed to start translating the app.

Co-authored-by: Sergey Ponomarev <stokito@gmail.com>
Co-authored-by: Miguel Angel Mulero Martinez <migmul@gmail.com>
Signed-off-by: Ramon Van Gorkom <Ramon00c00@gmail.com>
(cherry picked from commit 0b2aad03599c4f5c7b513f9274b529e7dd3d7e29)

applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js
applications/luci-app-usteer/po/template/usteer.pot [new file with mode: 0644]

index 5e9582fe6236fee2736310c79bf2005a5715cd50..ccca06203aa43b467054c65f9d775ba192eac6ea 100644 (file)
 'require ui';
 'require form';
 'require uci';
+'require tools.widgets as widgets';
 
+var Hosts, Remotehosts, Remoteinfo, Localinfo, Clients;
+
+
+function collectHearingClient(client_table_entries, mac) {
+       if (typeof Clients[mac] !== 'undefined') {
+               for (var wlanc in Clients[mac]) {
+                       var SSID = '';
+                       var freq = 0;
+                       if (typeof Localinfo[wlanc] !== 'undefined') {
+                               SSID = Localinfo[wlanc]['ssid'];
+                               freq = Localinfo[wlanc]['freq'];
+                       }
+                       if (typeof Remoteinfo[wlanc] !== 'undefined') {
+                               SSID = Remoteinfo[wlanc]['ssid'];
+                               freq = Remoteinfo[wlanc]['freq'];
+                       }
+                       client_table_entries.push([
+                               '<nobr>' + wlanc + '</nobr>',
+                               SSID,
+                               freq,
+                               Clients[mac][wlanc]['connected'] === true ? 'Yes' : 'No',
+                               typeof Clients[mac][wlanc]['signal'] !== 'undefined' ? Clients[mac][wlanc]['signal'] : ''
+                       ]);
+               }
+       }
+}
 
-var Hosts,Remotehosts,Remoteinfo,Localinfo,Clients;
 var HearingMap = form.DummyValue.extend({
-       renderWidget: function() {
+       renderWidget: function () {
                var body = E([
-                       E('h3', _('Hearing map'))
+                       E('h3', _('Hearing map')),
+                       E('div', _('Refresh page to get new mac addresses to show up'))
                ]);
-               for(var mac in Clients) {
-                       var maciphost='';
-                       maciphost=mac;
-                       var macUp=String(mac).toUpperCase()
+               for (var mac in Clients) {
+                       var maciphost = '';
+                       maciphost = mac;
+                       var macUp = mac.toUpperCase();
+                       var macn = macUp.replace(/:/g,'');
                        if (typeof Hosts[macUp] !== 'undefined') {
-                               if ((String(Hosts[macUp]['ipaddrs'][0]).length>0) && (typeof Hosts[macUp]['ipaddrs'][0] !== 'undefined'))
-                                       maciphost+='\u2003'+Hosts[macUp]['ipaddrs'];
-                               if ((String(Hosts[macUp]['name']).length>0) && (typeof Hosts[macUp]['name'] !== 'undefined')) 
-                                       maciphost+='\u2003%h'.format(Hosts[macUp]['name']);
-                               }
+                               if ((String(Hosts[macUp]['ipaddrs'][0]).length > 0) && (typeof Hosts[macUp]['ipaddrs'][0] !== 'undefined'))
+                                       maciphost += '\u2003' + Hosts[macUp]['ipaddrs'];
+                               if ((String(Hosts[macUp]['name']).length > 0) && (typeof Hosts[macUp]['name'] !== 'undefined'))
+                                       maciphost += '\u2003%h'.format(Hosts[macUp]['name']);
+                       }
                        body.appendChild(
                                E('h4', maciphost)
                        );
-                       var client_table = E('table', { 'class': 'table cbi-section-table' }, [
-                               E('tr', { 'class': 'tr table-titles' }, [
-                                       E('th', { 'class': 'th', 'style': 'width:35%' }, _('IP & Interface','Combination of IP and interface name in usteer overview')),
-                                       E('th', { 'class': 'th', 'style': 'width:25%' }, _('SSID')),
-                                       E('th', { 'class': 'th', 'style': 'width:15%' }, _('Frequency', 'BSS operating frequency in usteer overview')),
-                                       E('th', { 'class': 'th', 'style': 'width:15%' }, _('Connected', 'Connection state in usteer overview')),
-                                       E('th', { 'class': 'th', 'style': 'width:15%' }, _('Signal','Signal strength reported by wireless station in usteer overview'))
+                       var client_table = E('table', {'class': 'table cbi-section-table','id':'client_table'+macn}, [
+                               E('tr', {'class': 'tr table-titles'}, [
+                                       E('th', {'class': 'th', 'style': 'width:35%'}, _('IP & Interface','Combination of IP and interface name in usteer overview')),
+                                       E('th', {'class': 'th', 'style': 'width:25%'}, _('SSID')),
+                                       E('th', {'class': 'th', 'style': 'width:15%'}, _('Frequency','BSS operating frequency in usteer overview')),
+                                       E('th', {'class': 'th', 'style': 'width:15%'}, _('Connected','Connection state in usteer overview')),
+                                       E('th', {'class': 'th', 'style': 'width:15%'}, _('Signal','Signal strength reported by wireless station in usteer overview'))
                                ])
                        ]);
-                       var client_table_entries =[];
-                       for(var wlanc in Clients[mac]) {
-                               var SSID='';
-                               var freq=0;
-                               if (typeof Localinfo[wlanc] !== 'undefined') { 
-                                       SSID=Localinfo[wlanc]['ssid'];
-                                       freq=Localinfo[wlanc]['freq'];
-                               } 
-                               if (typeof Remoteinfo[wlanc] !== 'undefined') { 
-                                       SSID=Remoteinfo[wlanc]['ssid'];
-                                       freq=Remoteinfo[wlanc]['freq'];
-                               }
-                               client_table_entries.push([
-                                       '<nobr>'+wlanc+'</nobr>', 
-                                       SSID,
-                                       freq,
-                                       Clients[mac][wlanc]['connected'] === true ? 'Yes' : 'No',
-                                       Clients[mac][wlanc]['signal']
-                               ]);
-                       }
+                       var client_table_entries = [];
+                       collectHearingClient(client_table_entries, mac);
                        cbi_update_table(client_table, client_table_entries, E('em', _('No data')));
                        body.appendChild(client_table);
                }
-               return E('div', { 'class': 'cbi-section cbi-tblsection' }, [body]);
+               return E('div', {'class': 'cbi-section cbi-tblsection'}, [body]);
        }
 });
+
+
+function collectWlanAPInfoEntries(connectioninfo_table_entries, wlanAPInfos) {
+       for (var wlan in wlanAPInfos) {
+               connectioninfo_table_entries.push([
+                       '<nobr>' + wlan + '</nobr>',
+                       wlanAPInfos[wlan]['bssid'],
+                       wlanAPInfos[wlan]['ssid'],
+                       wlanAPInfos[wlan]['freq'],
+                       wlanAPInfos[wlan]['n_assoc'],
+                       wlanAPInfos[wlan]['noise'],
+                       wlanAPInfos[wlan]['load'],
+                       wlanAPInfos[wlan]['max_assoc'],
+                       typeof wlanAPInfos[wlan]['roam_events']['source'] !== 'undefined' ? wlanAPInfos[wlan]['roam_events']['source'] : '',
+                       typeof wlanAPInfos[wlan]['roam_events']['target'] !== 'undefined' ? wlanAPInfos[wlan]['roam_events']['target'] : ''
+               ]);
+       }
+};
+
+
+function collectWlanAPInfos(compactconnectioninfo_table_entries, wlanAPInfos) {
+       for (var wlan in wlanAPInfos) {
+               var hostl = '';
+               for (var mac in Clients) {
+                       if (typeof Clients[mac] !== 'undefined')
+                               if (typeof Clients[mac][wlan] !== 'undefined')
+                                       if (String(Clients[mac][wlan]['connected']).valueOf() == String('true').valueOf()) {
+                                               var foundname = mac;
+                                               var macUp = mac.toUpperCase();
+                                               if (typeof Hosts[macUp] !== 'undefined') {
+                                                       if ((String(Hosts[macUp]['ipaddrs'][0]).length > 0) && (typeof Hosts[macUp]['ipaddrs'][0] !== 'undefined')) {
+                                                               foundname = Hosts[macUp]['ipaddrs'][0];
+                                                       }
+                                                       if ((String(Hosts[macUp]['name']).length > 0) && (typeof Hosts[macUp]['name'] !== 'undefined')) {
+                                                               foundname = Hosts[macUp]['name'];
+                                                       }
+                                               }
+                                               hostl += '%h\u2003'.format(foundname);
+                                       }
+               }
+               compactconnectioninfo_table_entries.push([
+                       '<nobr>'+wlan+'</nobr>', 
+                       wlanAPInfos[wlan]['ssid'],
+                       wlanAPInfos[wlan]['freq'],
+                       wlanAPInfos[wlan]['load'],
+                       wlanAPInfos[wlan]['n_assoc'],
+                       hostl
+               ]);
+       }
+};
+
+function collectRemoteHosts (remotehosttableentries,Remotehosts) {
+       for (var IPaddr in Remotehosts) {
+                       remotehosttableentries.push([IPaddr, Remotehosts[IPaddr]['id']]);
+               }
+}
+
+
 var Clientinfooverview = form.DummyValue.extend({
-       renderWidget: function() {
+
+       renderWidget: function () {
                var body = E([
-                       E('h3', 'Remotehosts')
+                       E('h3', _('Remote hosts'))
                ]);
-               var remotehost_table = E('table', { 'class': 'table cbi-section-table' }, [
-                       E('tr', { 'class': 'tr table-titles' }, [
-                               E('th', { 'class': 'th' }, _('IP address')),
-                               E('th', { 'class': 'th' }, _('Identifier'))
+               var remotehost_table = E('table', {'class': 'table cbi-section-table', 'id': 'remotehost_table'}, [
+                       E('tr', {'class': 'tr table-titles'}, [
+                               E('th', {'class': 'th'}, _('IP address')),
+                               E('th', {'class': 'th'}, _('Identifier'))
                        ])
                ]);
-               var remotehosttableentries =[];
-               for(var IPaddr in Remotehosts) {
-                       remotehosttableentries.push([IPaddr, Remotehosts[IPaddr]['id']]);
-               }
+               var remotehosttableentries = [];
+               collectRemoteHosts(remotehosttableentries,Remotehosts);
                cbi_update_table(remotehost_table, remotehosttableentries, E('em', _('No data')));
                body.appendChild(remotehost_table);
                body.appendChild(
-                       E('h3', 'Client list')
+                       E('h3', _('Client list'))
                );
-               var connectioninfo_table = E('table', { 'class': 'table cbi-section-table' }, [
-                       E('tr', { 'class': 'tr table-titles' }, [
-                               E('th', { 'class': 'th' }, _('IP & Interface name','Combination of IP and interface name in usteer overview')),
-                               E('th', { 'class': 'th' }, _('BSSID')),
-                               E('th', { 'class': 'th' }, _('SSID')),
-                               E('th', { 'class': 'th' }, _('Frequency', 'BSS operating frequency in usteer overview')),
-                               E('th', { 'class': 'th' }, _('N', 'Number of associated clients in usteer overview')),
-                               E('th', { 'class': 'th' }, _('Noise', 'Channel noise in usteer overview')),
-                               E('th', { 'class': 'th' }, _('Load', 'Channel load in usteer overview')),
-                               E('th', { 'class': 'th' }, _('Max assoc','Max associated clients in usteer overview')),
-                               E('th', { 'class': 'th' }, _('Roam src','Roam source in usteer overview')),
-                               E('th', { 'class': 'th' }, _('Roam tgt','Roam target in usteer overview'))
+               var connectioninfo_table = E('table', {'class': 'table cbi-section-table', 'id': 'connectioninfo_table'}, [
+                       E('tr', {'class': 'tr table-titles'}, [
+                               E('th', {'class': 'th'}, _('IP & Interface name','Combination of IP and interface name in usteer overview')),
+                               E('th', {'class': 'th'}, _('BSSID')),
+                               E('th', {'class': 'th'}, _('SSID')),
+                               E('th', {'class': 'th'}, _('Frequency','BSS operating frequency in usteer overview')),
+                               E('th', {'class': 'th'}, _('N','Number of associated clients in usteer overview')),
+                               E('th', {'class': 'th'}, _('Noise','Channel noise in usteer overview')),
+                               E('th', {'class': 'th'}, _('Load','Channel load in usteer overview')),
+                               E('th', {'class': 'th'}, _('Max assoc','Max associated clients in usteer overview')),
+                               E('th', {'class': 'th'}, _('Roam src','Roam source in usteer overview')),
+                               E('th', {'class': 'th'}, _('Roam tgt','Roam target in usteer overview'))
                        ])
                ]);
-               var connectioninfo_table_entries =[];
-               for(var wlan in Localinfo) {
-                       connectioninfo_table_entries.push([
-                                                       '<nobr>'+wlan+'</nobr>',
-                                                       Localinfo[wlan]['bssid'],
-                                                       Localinfo[wlan]['ssid'],
-                                                       Localinfo[wlan]['freq'],
-                                                       Localinfo[wlan]['n_assoc'],
-                                                       Localinfo[wlan]['noise'],
-                                                       Localinfo[wlan]['load'],
-                                                       Localinfo[wlan]['max_assoc'],
-                                                       typeof Localinfo[wlan]['roam_events']['source'] !== 'undefined' ?  Localinfo[wlan]['roam_events']['source'] : '',
-                                                       typeof Localinfo[wlan]['roam_events']['target'] !== 'undefined' ?  Localinfo[wlan]['roam_events']['target'] : ''
-                       ]);
-               }
-               for(var wlan in Remoteinfo) {
-                       connectioninfo_table_entries.push([
-                                                       '<nobr>'+wlan+'</nobr>',
-                                                       Remoteinfo[wlan]['bssid'],
-                                                       Remoteinfo[wlan]['ssid'],
-                                                       Remoteinfo[wlan]['freq'],
-                                                       Remoteinfo[wlan]['n_assoc'],
-                                                       Remoteinfo[wlan]['noise'],
-                                                       Remoteinfo[wlan]['load'],
-                                                       Remoteinfo[wlan]['max_assoc'],
-                                                       typeof Remoteinfo[wlan]['roam_events']['source'] !== 'undefined' ?  Remoteinfo[wlan]['roam_events']['source'] : '',
-                                                       typeof Remoteinfo[wlan]['roam_events']['target'] !== 'undefined' ?  Remoteinfo[wlan]['roam_events']['target'] : ''
-                       ]);
-               }
+               var connectioninfo_table_entries = [];
+               collectWlanAPInfoEntries(connectioninfo_table_entries, Localinfo);
+               collectWlanAPInfoEntries(connectioninfo_table_entries, Remoteinfo);
+
                cbi_update_table(connectioninfo_table, connectioninfo_table_entries, E('em', _('No data')));
                body.appendChild(connectioninfo_table);
-               var compactconnectioninfo_table = E('table', { 'class': 'table cbi-section-table' }, [
-                               E('tr', { 'class': 'tr table-titles' }, [
-                                       E('th', { 'class': 'th' }, _('IP & Interface name','Combination of IP and interface name in usteer overview')),
-                                       E('th', { 'class': 'th' }, _('SSID')),
-                                       E('th', { 'class': 'th' }, _('Frequency', 'BSS operating frequency in usteer overview')),
-                                       E('th', { 'class': 'th' }, _('Load', 'Channel load in usteer overview')),
-                                       E('th', { 'class': 'th' }, _('N', 'Number of associated clients in usteer overview')),
-                                       E('th', { 'class': 'th' }, _('Host','host hint in usteer overview'))
-
-                               ])
+               var compactconnectioninfo_table = E('table', {'class': 'table cbi-section-table','id': 'compactconnectioninfo_table'}, [
+                       E('tr', {'class': 'tr table-titles'}, [
+                               E('th', {'class': 'th'}, _('IP & Interface name', 'Combination of IP and interface name in usteer overview')),
+                               E('th', {'class': 'th'}, _('SSID')),
+                               E('th', {'class': 'th'}, _('Frequency', 'BSS operating frequency in usteer overview')),
+                               E('th', {'class': 'th'}, _('Load', 'Channel load in usteer overview')),
+                               E('th', {'class': 'th'}, _('N', 'Number of associated clients in usteer overview')),
+                               E('th', {'class': 'th'}, _('Host', 'host hint in usteer overview'))
+                       ])
                ]);
-               var compactconnectioninfo_table_entries =[];
-               for(var wlan in Localinfo) {
-                       var hostl=''
-                       for(var mac in Clients) {
-                               if (typeof Clients[mac] !== 'undefined') 
-                                       if (typeof Clients[mac][wlan] !== 'undefined') 
-                                               if (String(Clients[mac][wlan]['connected']).valueOf()==String("true").valueOf()) {
-                                                       var foundname=mac;
-                                                       var macUp=String(mac).toUpperCase()
-                                                       if (typeof Hosts[macUp] !== 'undefined') {
-                                                               if ((String(Hosts[macUp]['ipaddrs'][0]).length>0) && (typeof Hosts[macUp]['ipaddrs'][0] !== 'undefined')) {
-                                                                       foundname=Hosts[macUp]['ipaddrs'][0];
-                                                               }
-                                                               if ((String(Hosts[macUp]['name']).length>0) && (typeof Hosts[macUp]['name'] !== 'undefined')) {
-                                                                       foundname=Hosts[macUp]['name'];
-                                                               }
-                                                       }
-                                                       hostl += '%h\u2003'.format(foundname);
-                               }
-                       }
-                       compactconnectioninfo_table_entries.push([
-                                                               '<nobr>'+wlan+'</nobr>', 
-                                                               Localinfo[wlan]['ssid'],
-                                                               Localinfo[wlan]['freq'],
-                                                               Localinfo[wlan]['load'],
-                                                               Localinfo[wlan]['n_assoc'],
-                                                               hostl
-                                                       ]);
-               }
-               for(var wlan in Remoteinfo) {
-                       var hostl=''
-                       for(var mac in Clients) {
-                               if (typeof Clients[mac] !== 'undefined')
-                                       if (typeof Clients[mac][wlan] !== 'undefined')
-                                               if (String(Clients[mac][wlan]['connected']).valueOf()==String("true").valueOf()) {
-                                                       var foundname=mac;
-                                                       var macUp=String(mac).toUpperCase()
-                                                       if (typeof Hosts[macUp] !== 'undefined') {
-                                                               if ((String(Hosts[macUp]['ipaddrs'][0]).length>0) && (typeof Hosts[macUp]['ipaddrs'][0] !== 'undefined')) {
-                                                                       foundname=Hosts[macUp]['ipaddrs'][0];
-                                                               }
-                                                               if ((String(Hosts[macUp]['name']).length>0) &&  (typeof Hosts[macUp]['name'] !== 'undefined')) {
-                                                                       foundname=Hosts[macUp]['name'];
-                                                               }
-                                                       }
-                                                       hostl += '%h\u2003'.format(foundname);
-                               }
-                       }
-                       compactconnectioninfo_table_entries.push([
-                                                               '<nobr>'+wlan+'</nobr>',
-                                                               Remoteinfo[wlan]['ssid'],
-                                                               Remoteinfo[wlan]['freq'],
-                                                               Remoteinfo[wlan]['load'],
-                                                               Remoteinfo[wlan]['n_assoc'],
-                                                               hostl
-                       ]);
-               }
+               var compactconnectioninfo_table_entries = [];
+               collectWlanAPInfos(compactconnectioninfo_table_entries, Localinfo);
+               collectWlanAPInfos(compactconnectioninfo_table_entries, Remoteinfo);
                cbi_update_table(compactconnectioninfo_table, compactconnectioninfo_table_entries, E('em', _('No data')));
                body.appendChild(compactconnectioninfo_table);
-               return E('div', { 'class': 'cbi-section cbi-tblsection' }, [body]);
+               return E('div', {'class': 'cbi-section cbi-tblsection'}, [body]);
        }
 });
 
 var Settingstitle = form.DummyValue.extend({
-       renderWidget: function() {
+       renderWidget: function () {
                var body = E([
-                       E('h3', 'Settings'),
-                       E('body','First four options are mandatory. Also be sure to enable rrm reports, 80211kv, etc see: https://openwrt.org/docs/guide-user/network/wifi/usteer')
+                       E('h3', _('Settings')),
+                       E('div',
+                               _('The first four options below are mandatory.') + ' ' +
+                               _('Also be sure to enable rrm reports, 80211kv, etc.') + ' ' +
+                               _('See <a %s>documentation</a>').format('href="https://openwrt.org/docs/guide-user/network/wifi/usteer"')
+                       ),
                ]);
                return E('div', [body]);
        }
 });
 
+var footerdata;
+var Settingsfooter = form.DummyValue.extend({
+       renderWidget: function () {
+               var body = E([
+                       E('body', footerdata),
+               ]);
+               return E('div', {'style': 'width:100%'}, [footerdata]);
+       }
+});
+
+
 return view.extend({
        callHostHints: rpc.declare({
                object: 'luci-rpc',
                method: 'getHostHints',
-               expect: { '': {} }
+               expect: {'': {}}
        }),
        callGetRemotehosts: rpc.declare({
                object: 'usteer',
                method: 'remote_hosts',
-               expect: {  '': {}}
+               expect: {'': {}}
        }),
        callGetRemoteinfo: rpc.declare({
                object: 'usteer',
                method: 'remote_info',
-               expect: { '': {} }
+               expect: {'': {}}
        }),
        callGetLocalinfo: rpc.declare({
                object: 'usteer',
                method: 'local_info',
-               expect: { '': {} }
+               expect: {'': {}}
        }),
        callGetClients: rpc.declare({
                object: 'usteer',
                method: 'get_clients',
-               expect: { '': {} }
+               expect: {'': {}}
        }),
-       load: function() {
+       load: function () {
                return Promise.all([
-                       this.callHostHints(),
-                       this.callGetRemotehosts(),
-                       this.callGetRemoteinfo(),
-                       this.callGetLocalinfo(),
-                       this.callGetClients()
+                       rpc.list('usteer'),
+                       this.callHostHints().catch (function (){return null;}),
+                       this.callGetRemotehosts().catch (function (){return null;}),
+                       this.callGetRemoteinfo().catch (function (){return null;}),
+                       this.callGetLocalinfo().catch (function (){return null;}),
+                       this.callGetClients().catch (function (){return null;})
                ]);
        },
-       render: function(data) {
+
+       poll_status: function(nodes, data) {
+
+               Hosts = data[1];
+               Remotehosts = data[2];
+               Remoteinfo = data[3];
+               Localinfo = data[4];
+               Clients = data[5];
+
+               var remotehosttableentries = [];
+               collectRemoteHosts(remotehosttableentries,Remotehosts);
+               cbi_update_table(nodes.querySelector('#remotehost_table'), remotehosttableentries, E('em', _('No data')));
+
+               var connectioninfo_table_entries = [];
+               collectWlanAPInfoEntries(connectioninfo_table_entries, Localinfo);
+               collectWlanAPInfoEntries(connectioninfo_table_entries, Remoteinfo);
+               cbi_update_table(nodes.querySelector('#connectioninfo_table'), connectioninfo_table_entries, E('em', _('No data')));
+
+               var compactconnectioninfo_table_entries = [];
+               collectWlanAPInfos(compactconnectioninfo_table_entries, Localinfo);
+               collectWlanAPInfos(compactconnectioninfo_table_entries, Remoteinfo);
+               cbi_update_table(nodes.querySelector('#compactconnectioninfo_table'), compactconnectioninfo_table_entries, E('em', _('No data')));
+               
+               for (var mac in Clients) {
+                       var macn = mac.toUpperCase().replace(/:/g,'');
+                       var client_table_entries = [];
+                       collectHearingClient(client_table_entries, mac);
+                       cbi_update_table(nodes.querySelector('#client_table'+macn), client_table_entries, E('em', _('No data')));
+               }
+               return;
+       },
+
+       render: function (data) {
                var m, s, o;
-               Hosts = data[0];
-               Remotehosts=data[1];
-               Remoteinfo=data[2];
-               Localinfo=data[3];
-               Clients=data[4];
+
+               if (!('usteer' in data[0])) {
+                       m = new form.Map('usteer', _('Usteer'),
+                               _('Usteer is not running. Make sure it is installed and running.') +
+                               _('To start it running try %s').format('<code>/etc/init.d/usteer start</code>')
+                       );
+                       return m.render();
+               }
 
                m = new form.Map('usteer', _('Usteer'));
 
+               Hosts = data[1];
+               Remotehosts = data[2];
+               Remoteinfo = data[3];
+               Localinfo = data[4];
+               Clients = data[5];
+
                s = m.section(form.TypedSection);
                s.anonymous = true;
-               s.tab('status', _("Status"));
-               s.tab('hearingmap', _("Hearing map"));
-               s.tab('settings', _("Settings"));
+               s.tab('status', _('Status'));
+               s.tab('hearingmap', _('Hearing map'));
+               s.tab('settings', _('Settings'));
 
-               o = s.taboption('status',Clientinfooverview);
+               o = s.taboption('status', Clientinfooverview);
+               o.readonly = true;
 
-               o = s.taboption('hearingmap',HearingMap);
+               o = s.taboption('hearingmap', HearingMap);
+               o.readonly = true;
 
-               o = s.taboption('settings',Settingstitle);
-               o = s.taboption('settings',form.Value, 'network', _('Network'), _('The network interface for inter-AP communication'));
+               o = s.taboption('settings', Settingstitle);
+               o.readonly = true;
 
-               o = s.taboption('settings',form.Flag, 'syslog', _('Syslog'), _('Log messages to syslog (0/1)'));
+               o = s.taboption('settings', widgets.NetworkSelect, 'network', _('Network'), _('The network interface for inter-AP communication'));
+
+               o = s.taboption('settings', form.Flag, 'syslog', _('Log messages to syslog'));
                o.default = '1';
                o.rmempty = false;
 
-               o = s.taboption('settings',form.Flag, 'ipv6', _('IPv6 mode'), _('Use IPv6 for remote exchange'));
+               o = s.taboption('settings', form.Flag, 'ipv6', _('IPv6 mode'), _('Use IPv6 for remote exchange'));
                o.rmempty = false;
 
-               o = s.taboption('settings',form.ListValue, 'debug_level', _('Debug level'), _('Debug level'));
-               o.placeholder = 'lan';
-               o.value('0','0 Fatal');
-               o.value('1','1 Info');
-               o.value('2','2 Verbose');
-               o.value('3','3 Some debug');
-               o.value('4','4 Network packet info');
-               o.value('5','5 All debug messages');
+               o = s.taboption('settings', form.ListValue, 'debug_level', _('Debug level'));
+               o.value('0', _('Fatal'));
+               o.value('1', _('Info'));
+               o.value('2', _('Verbose'));
+               o.value('3', _('Some debug'));
+               o.value('4', _('Network packet info'));
+               o.value('5', _('All debug messages'));
                o.rmempty = false;
                o.editable = true;
 
-               o = s.taboption('settings',form.Value, 'max_neighbour_reports', _('Max neighbour reports'), _('Maximum number of neighbor reports set for a node'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'max_neighbour_reports', _('Max neighbour reports'), _('Maximum number of neighbor reports set for a node'));
+               o.optional = true;
                o.placeholder = 8;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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'));
-               o.optional    = true;
+               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'));
+               o.optional = true;
                o.placeholder = 30000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'local_sta_timeout', _('Local sta timeout'), _('Maximum amount of time (ms) a local unconnected station is tracked'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'local_sta_timeout', _('Local sta timeout'), _('Maximum amount of time (ms) a local unconnected station is tracked'));
+               o.optional = true;
                o.placeholder = 12000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'measurement_report_timeout', _('Measurement report timeout'), _('Maximum amount of time (ms) a measurement report is stored'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'measurement_report_timeout', _('Measurement report timeout'), _('Maximum amount of time (ms) a measurement report is stored'));
+               o.optional = true;
                o.placeholder = 120000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'local_sta_update', _('Local sta update'), _('Local station information update interval (ms)'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'local_sta_update', _('Local sta update'), _('Local station information update interval (ms)'));
+               o.optional = true;
                o.placeholder = 1000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'max_retry_band', _('Max retry band'), _('Maximum number of consecutive times a station may be blocked by policy'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'max_retry_band', _('Max retry band'), _('Maximum number of consecutive times a station may be blocked by policy'));
+               o.optional = true;
                o.placeholder = 5;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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'));
-               o.optional    = true;
+               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'));
+               o.optional = true;
                o.placeholder = 30000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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'));
-               o.optional    = true;
+               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'));
+               o.optional = true;
                o.placeholder = 0;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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'));
-               o.optional    = true;
+               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'));
+               o.optional = true;
                o.placeholder = 5;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'remote_update_interval', _('Remote update interval'), _('Interval (ms) between sending state updates to other APs'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'remote_update_interval', _('Remote update interval'), _('Interval (ms) between sending state updates to other APs'));
+               o.optional = true;
                o.placeholder = 1000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'remote_node_timeout', _('Remote node timeout'), _('Number of remote update intervals after which a remote-node is deleted'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'remote_node_timeout', _('Remote node timeout'), _('Number of remote update intervals after which a remote-node is deleted'));
+               o.optional = true;
                o.placeholder = 10;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Flag, 'assoc_steering', _('Assoc steering'), _('Allow rejecting assoc requests for steering purposes (0/1)'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Flag, 'assoc_steering', _('Assoc steering'), _('Allow rejecting assoc requests for steering purposes'));
+               o.optional = true;
 
-               o = s.taboption('settings',form.Flag, 'probe_steering', _('Probe steering'), _('Allow ignoring probe requests for steering purposes (0/1)'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Flag, 'probe_steering', _('Probe steering'), _('Allow ignoring probe requests for steering purposes'));
+               o.optional = true;
 
-               o = s.taboption('settings',form.Value, 'min_connect_snr', _('Min connect snr'), _('Minimum signal-to-noise ratio or signal level (dBm) to allow connections'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'min_connect_snr', _('Min connect SNR'), _('Minimum signal-to-noise ratio or signal level (dBm) to allow connections'));
+               o.optional = true;
                o.placeholder = 0;
-               o.datatype    = 'integer';
+               o.datatype = 'integer';
 
-               o = s.taboption('settings',form.Value, 'min_snr', _('Min snr'), _('Minimum signal-to-noise ratio or signal level (dBm) to remain connected'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'min_snr', _('Min SNR'), _('Minimum signal-to-noise ratio or signal level (dBm) to remain connected'));
+               o.optional = true;
                o.placeholder = 0;
-               o.datatype    = 'integer';
+               o.datatype = 'integer';
 
-               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'));
-               o.optional    = true;
+               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'));
+               o.optional = true;
                o.placeholder = 5000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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'));
-               o.optional    = true;
+               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'));
+               o.optional = true;
                o.placeholder = 5000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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'));
-               o.optional    = true;
+               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'));
+               o.optional = true;
                o.placeholder = 0;
-               o.datatype    = 'integer';
+               o.datatype = 'integer';
 
-               o = s.taboption('settings',form.Value, 'roam_scan_tries', _('Roam scan tries'), _('Maximum number of client roaming scan trigger attempts'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'roam_scan_tries', _('Roam scan tries'), _('Maximum number of client roaming scan trigger attempts'));
+               o.optional = true;
                o.placeholder = 3;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'roam_scan_timeout', _('Roam scan timeout'), _('Retry scanning when roam_scan_tries is exceeded after this timeout (in ms). In case this option is set to 0, the client is kicked instead'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'roam_scan_timeout', _('Roam scan timeout'),
+                       _('Retry scanning when roam_scan_tries is exceeded after this timeout (in ms).') +
+                       _(' In case this option is disabled, the client is kicked instead')
+               );
+               o.optional = true;
                o.placeholder = 0;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'roam_scan_interval', _('Roam scan interval'), _('Minimum time (ms) between client roaming scan trigger attempts'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'roam_scan_interval', _('Roam scan interval'), _('Minimum time (ms) between client roaming scan trigger attempts'));
+               o.optional = true;
                o.placeholder = 10000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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'));
-               o.optional    = true;
+               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'));
+               o.optional = true;
                o.placeholder = 0;
-               o.datatype    = 'integer';
+               o.datatype = 'integer';
 
-               o = s.taboption('settings',form.Value, 'roam_trigger_interval', _('Roam trigger interval'), _('Minimum time (ms) between client roaming trigger attempts'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'roam_trigger_interval', _('Roam trigger interval'), _('Minimum time (ms) between client roaming trigger attempts'));
+               o.optional = true;
                o.placeholder = 60000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'roam_kick_delay', _('Roam kick delay'), _('Timeout (in 100ms beacon intervals) for client roam requests'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'roam_kick_delay', _('Roam kick delay'), _('Timeout (in 100ms beacon intervals) for client roam requests'));
+               o.optional = true;
                o.placeholder = 100;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'signal_diff_threshold', _('Signal diff threshold'), _('Minimum signal strength difference until AP steering policy is active'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'signal_diff_threshold', _('Signal diff threshold'), _('Minimum signal strength difference until AP steering policy is active'));
+               o.optional = true;
                o.placeholder = 0;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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)'));
-               o.optional    = true;
+               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)'));
+               o.optional = true;
                o.placeholder = 0;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Flag, 'load_kick_enabled', _('Load kick enabled'), _('Enable kicking client on excessive channel load (0/1)'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Flag, 'load_kick_enabled', _('Load kick enabled'), _('Enable kicking client on excessive channel load'));
+               o.optional = true;
 
-               o = s.taboption('settings',form.Value, 'load_kick_threshold', _('Load kick threshold'), _('Minimum channel load (%) before kicking clients'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'load_kick_threshold', _('Load kick threshold'), _('Minimum channel load (%) before kicking clients'));
+               o.optional = true;
                o.placeholder = 75;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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'));
-               o.optional    = true;
+               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'));
+               o.optional = true;
                o.placeholder = 10000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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'));
-               o.optional    = true;
+               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'));
+               o.optional = true;
                o.placeholder = 10;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'load_kick_reason_code', _('Load kick reason code'), _('Reason code on client kick based on channel load (default: WLAN_REASON_DISASSOC_AP_BUSY)'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'load_kick_reason_code', _('Load kick reason code'),
+                       _('Reason code on client kick based on channel load.') + ' Default: WLAN_REASON_DISASSOC_AP_BUSY)'
+               );
+               o.optional = true;
                o.placeholder = 5;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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.'));
-               o.optional    = true;
+               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.'));
+               o.optional = true;
                o.placeholder = 120000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               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.'));
-               o.optional    = true;
+               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.'));
+               o.optional = true;
                o.placeholder = -60;
-               o.datatype    = 'integer';
+               o.datatype = 'integer';
 
-               o = s.taboption('settings',form.Value, 'link_measurement_interval', _('Link measurement interval'), _('Interval (ms) the device is sent a link-measurement request to help assess the bi-directional link quality. Setting the interval to 0 disables link-measurements.'));
-               o.optional    = true;
+               o = s.taboption('settings', form.Value, 'link_measurement_interval', _('Link measurement interval'),
+                       _('Interval (ms) the device is sent a link-measurement request to help assess the bi-directional link quality.') +
+                       _('Setting the interval to 0 disables link-measurements.')
+               );
+               o.optional = true;
                o.placeholder = 30000;
-               o.datatype    = 'uinteger';
+               o.datatype = 'uinteger';
 
-               o = s.taboption('settings',form.Value, 'node_up_script', _('Node up script'), _('Script to run after bringing up a node'));
-               o.optional    = true;
-               o.datatype    = 'string';
+               o = s.taboption('settings', form.Value, 'node_up_script', _('Node up script'), _('Script to run after bringing up a node'));
+               o.optional = true;
+               o.datatype = 'string';
 
-               o = s.taboption('settings',form.MultiValue, 'event_log_types', _('Event log types'), _('Message types to include in log. Available types: probe_req_accept probe_req_deny, auth_req_accept, auth_req_deny, assoc_req_accept, assoc_req_deny, load_kick_trigger, load_kick_reset, load_kick_min_clients, load_kick_no_client, load_kick_client, signal_kick'));
+               o = s.taboption('settings', form.MultiValue, 'event_log_types', _('Event log types'), _('Message types to include in log.'));
                o.value('probe_req_accept');
                o.value('probe_req_deny');
                o.value('auth_req_accept');
@@ -468,13 +523,34 @@ return view.extend({
                o.value('load_kick_no_client');
                o.value('load_kick_client');
                o.value('signal_kick');
-               o.optional    = true;
-               o.datatype    = 'list(string)';
+               o.optional = true;
+               o.datatype = 'list(string)';
+
+               o = s.taboption('settings', form.DynamicList, 'ssid_list', _('SSID list'), _('List of SSIDs to enable steering on'));
+               o.optional = true;
+               o.datatype = 'list(string)';
+
+               footerdata = this.super('addFooter', []);
+               o = s.taboption('settings', Settingsfooter);
+               o.readonly = true;
+
+               return m.render().then(L.bind(function(m, nodes) {
+                       poll.add(L.bind(function() {
+                               return Promise.all([
+                               rpc.list('usteer'),
+                               this.callHostHints().catch (function (){return null;}),
+                               this.callGetRemotehosts().catch (function (){return null;}),
+                               this.callGetRemoteinfo().catch (function (){return null;}),
+                               this.callGetLocalinfo().catch (function (){return null;}),
+                               this.callGetClients().catch (function (){return null;})
+                               ]).then(L.bind(this.poll_status, this, nodes));
+                       }, this), 5);
+                       return nodes;
+               }, this, m));
+       },
 
-               o = s.taboption('settings',form.DynamicList, 'ssid_list', _('SSID list'), _('List of SSIDs to enable steering on'));
-               o.optional    = true;
-               o.datatype    = 'list(string)';
 
-               return m.render();
+       addFooter: function () {
+               return null;
        },
 });
diff --git a/applications/luci-app-usteer/po/template/usteer.pot b/applications/luci-app-usteer/po/template/usteer.pot
new file mode 100644 (file)
index 0000000..16bf830
--- /dev/null
@@ -0,0 +1,533 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+
+msgid "Refresh page to get new mac addresses to show up"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:313
+msgid "Fatal"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:314
+msgid "Info"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:315
+msgid "Verbose"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:316
+msgid "Some debug"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:317
+msgid "Network packet info"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:318
+msgid "All debug messages"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:380
+msgid "Allow ignoring probe requests for steering purposes"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:377
+msgid "Allow rejecting assoc requests for steering purposes"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:187
+msgid "Also be sure to enable rrm reports, 80211kv, etc."
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:377
+msgid "Assoc steering"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:476
+msgid ""
+"Attempting to steer clients to a higher frequency-band every n ms. A value "
+"of 0 disables band-steering."
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:145
+msgid "BSSID"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:476
+msgid "Band steering interval"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:481
+msgid "Band steering min SNR"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:362
+msgid "Band steering threshold"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:140
+msgid "Client list"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:35
+msgctxt "Connection state in usteer overview"
+msgid "Connected"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:311
+msgid "Debug level"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:451
+msgid "Enable kicking client on excessive channel load"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:498
+msgid "Event log types"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:34
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:147
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:166
+msgctxt "BSS operating frequency in usteer overview"
+msgid "Frequency"
+msgstr ""
+
+#: applications/luci-app-usteer/root/usr/share/rpcd/acl.d/luci-app-usteer.json:3
+msgid "Grant UCI access to LuCI app usteer"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:15
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:290
+msgid "Hearing map"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:169
+msgctxt "host hint in usteer overview"
+msgid "Host"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:32
+msgctxt "Combination of IP and interface name in usteer overview"
+msgid "IP & Interface"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:144
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:164
+msgctxt "Combination of IP and interface name in usteer overview"
+msgid "IP & Interface name"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:131
+msgid "IP address"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:308
+msgid "IPv6 mode"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:132
+msgid "Identifier"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:415
+msgid "In case this option is disabled, the client is kicked instead"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:446
+msgid "Initial connect delay"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:446
+msgid ""
+"Initial delay (ms) before responding to probe requests (to allow other APs "
+"to see packets as well)"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:367
+msgid "Interval (ms) between sending state updates to other APs"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:487
+msgid ""
+"Interval (ms) the device is sent a link-measurement request to help assess "
+"the bi-directional link quality."
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:486
+msgid "Link measurement interval"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:514
+msgid "List of SSIDs to enable steering on"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:150
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:167
+msgctxt "Channel load in usteer overview"
+msgid "Load"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:357
+msgid "Load balancing threshold"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:459
+msgid "Load kick delay"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:451
+msgid "Load kick enabled"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:464
+msgid "Load kick min clients"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:469
+msgid "Load kick reason code"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:454
+msgid "Load kick threshold"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:332
+msgid "Local sta timeout"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:342
+msgid "Local sta update"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:342
+msgid "Local station information update interval (ms)"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:304
+msgid "Log messages to syslog"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:151
+msgctxt "Max associated clients in usteer overview"
+msgid "Max assoc"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:322
+msgid "Max neighbour reports"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:347
+msgid "Max retry band"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:332
+msgid "Maximum amount of time (ms) a local unconnected station is tracked"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:337
+msgid "Maximum amount of time (ms) a measurement report is stored"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:327
+msgid ""
+"Maximum amount of time (ms) a station may be blocked due to policy decisions"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:352
+msgid ""
+"Maximum idle time of a station entry (ms) to be considered for policy "
+"decisions"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:408
+msgid "Maximum number of client roaming scan trigger attempts"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:347
+msgid "Maximum number of consecutive times a station may be blocked by policy"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:322
+msgid "Maximum number of neighbor reports set for a node"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:337
+msgid "Measurement report timeout"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:498
+msgid "Message types to include in log."
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:388
+msgid "Min SNR"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:393
+msgid "Min SNR kick delay"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:383
+msgid "Min connect SNR"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:481
+msgid ""
+"Minimal SNR or absolute signal a device has to maintain over "
+"band_steering_interval to be steered to a higher frequency band."
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:459
+msgid ""
+"Minimum amount of time (ms) that channel load is above threshold before "
+"starting to kick clients"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:454
+msgid "Minimum channel load (%) before kicking clients"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:464
+msgid ""
+"Minimum number of connected clients before kicking based on channel load"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:357
+msgid ""
+"Minimum number of stations delta between APs before load balancing policy is "
+"active"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:362
+msgid ""
+"Minimum number of stations delta between bands before band steering policy "
+"is active"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:441
+msgid "Minimum signal strength difference until AP steering policy is active"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:403
+msgid ""
+"Minimum signal-to-noise ratio or signal level (dBm) before attempting to "
+"trigger client scans for roam"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:426
+msgid ""
+"Minimum signal-to-noise ratio or signal level (dBm) before attempting to "
+"trigger forced client roaming"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:383
+msgid ""
+"Minimum signal-to-noise ratio or signal level (dBm) to allow connections"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:388
+msgid "Minimum signal-to-noise ratio or signal level (dBm) to remain connected"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:421
+msgid "Minimum time (ms) between client roaming scan trigger attempts"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:431
+msgid "Minimum time (ms) between client roaming trigger attempts"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:148
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:168
+msgctxt "Number of associated clients in usteer overview"
+msgid "N"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:302
+msgid "Network"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:59
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:137
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:160
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:175
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:253
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:258
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:263
+msgid "No data"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:494
+msgid "Node up script"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:149
+msgctxt "Channel noise in usteer overview"
+msgid "Noise"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:372
+msgid "Number of remote update intervals after which a remote-node is deleted"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:380
+msgid "Probe steering"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:470
+msgid "Reason code on client kick based on channel load."
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:127
+msgid "Remote hosts"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:372
+msgid "Remote node timeout"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:367
+msgid "Remote update interval"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:414
+msgid ""
+"Retry scanning when roam_scan_tries is exceeded after this timeout (in ms)."
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:436
+msgid "Roam kick delay"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:398
+msgid "Roam process timeout"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:403
+msgid "Roam scan SNR"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:421
+msgid "Roam scan interval"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:413
+msgid "Roam scan timeout"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:408
+msgid "Roam scan tries"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:152
+msgctxt "Roam source in usteer overview"
+msgid "Roam src"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:153
+msgctxt "Roam target in usteer overview"
+msgid "Roam tgt"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:426
+msgid "Roam trigger SNR"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:431
+msgid "Roam trigger interval"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:33
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:146
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:165
+msgid "SSID"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:514
+msgid "SSID list"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:494
+msgid "Script to run after bringing up a node"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:188
+msgid "See <a %s>documentation</a>"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:352
+msgid "Seen policy timeout"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:488
+msgid "Setting the interval to 0 disables link-measurements."
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:184
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:291
+msgid "Settings"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:36
+msgctxt "Signal strength reported by wireless station in usteer overview"
+msgid "Signal"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:441
+msgid "Signal diff threshold"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:327
+msgid "Sta block timeout"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:289
+msgid "Status"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:304
+msgid "Syslog"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:186
+msgid "The first four options below are mandatory."
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:302
+msgid "The network interface for inter-AP communication"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:436
+msgid "Timeout (in 100ms beacon intervals) for client roam requests"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:398
+msgid ""
+"Timeout (in ms) after which a association following a disassociation is not "
+"seen as a roam"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:393
+msgid "Timeout after which a station with SNR < min_SNR will be kicked"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:274
+msgid "To start it running try %s"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:308
+msgid "Use IPv6 for remote exchange"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:272
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:279
+#: applications/luci-app-usteer/root/usr/share/luci/menu.d/luci-app-usteer.json:3
+msgid "Usteer"
+msgstr ""
+
+#: applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js:273
+msgid "Usteer is not running. Make sure it is installed and running."
+msgstr ""