mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-01 05:53:56 +08:00
💐 Sync 2026-09-14 23:49:49
This commit is contained in:
1 parent
06a2e88aee
commit
2bab511fb4
60 files changed
+11427
-1
No files matched your search
@@ -0,0 +1,337 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require fs';
|
||||
'require rpc';
|
||||
'require network';
|
||||
'require uci';
|
||||
'require view.dashboard.lib.charts as charts';
|
||||
|
||||
var callSystemBoard = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'board'
|
||||
});
|
||||
|
||||
var callSystemInfo = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'info'
|
||||
});
|
||||
|
||||
var callGetUnixtime = rpc.declare({
|
||||
object: 'luci',
|
||||
method: 'getUnixtime',
|
||||
expect: { result: 0 }
|
||||
});
|
||||
|
||||
return baseclass.extend({
|
||||
|
||||
params: [],
|
||||
|
||||
load() {
|
||||
return Promise.all([
|
||||
network.getWANNetworks(),
|
||||
network.getWAN6Networks(),
|
||||
L.resolveDefault(callSystemBoard(), {}),
|
||||
L.resolveDefault(callSystemInfo(), {}),
|
||||
L.resolveDefault(callGetUnixtime(), 0),
|
||||
uci.load('system')
|
||||
]);
|
||||
},
|
||||
|
||||
renderValue(value, mono) {
|
||||
if (Array.isArray(value))
|
||||
return E('span', {}, value.map(v => E('div', {}, [ mono ? E('code', {}, [ v ]) : v ])));
|
||||
|
||||
if (value == null || value === '')
|
||||
return '-';
|
||||
|
||||
return mono ? E('code', {}, [ value ]) : value;
|
||||
},
|
||||
|
||||
// Same markup as the status page's system table.
|
||||
renderKeyValueTable(rows) {
|
||||
return E('table', { 'class': 'table' }, rows.map(row => E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left', 'width': '33%' }, [ row.title ]),
|
||||
E('td', { 'class': 'td left' }, [ this.renderValue(row.value, row.mono) ])
|
||||
])));
|
||||
},
|
||||
|
||||
renderInternetColumn(group) {
|
||||
const connected = group.connected.value === true;
|
||||
const rows = [];
|
||||
|
||||
for (const key in group) {
|
||||
if (key == 'title' || key == 'connected' || !group[key].visible)
|
||||
continue;
|
||||
|
||||
let value = group[key].value;
|
||||
|
||||
if (key == 'addrsv4' && Array.isArray(value))
|
||||
value = value.map(a => a.split('/')[0]);
|
||||
|
||||
rows.push({
|
||||
title: group[key].title,
|
||||
value: value,
|
||||
mono: /^(addrs|gateway|dns|ipprefix)/.test(key)
|
||||
});
|
||||
}
|
||||
|
||||
return E('div', {}, [
|
||||
E('h3', {}, [
|
||||
group.title, ' ',
|
||||
charts.badge(connected ? _('Connected') : _('Not connected'), connected ? 'success' : 'warning')
|
||||
]),
|
||||
connected ? this.renderKeyValueTable(rows) : charts.empty(_('Not configured or no address acquired'))
|
||||
]);
|
||||
},
|
||||
|
||||
renderInternetTab() {
|
||||
return E('div', {}, [
|
||||
this.renderInternetColumn(this.params.internet.v4),
|
||||
this.renderInternetColumn(this.params.internet.v6)
|
||||
]);
|
||||
},
|
||||
|
||||
renderSystemTab() {
|
||||
const rows = [];
|
||||
|
||||
for (const key in this.params.router)
|
||||
rows.push({
|
||||
title: this.params.router[key].title,
|
||||
value: this.params.router[key].value,
|
||||
mono: (key == 'kernel' || key == 'localtime')
|
||||
});
|
||||
|
||||
return this.renderKeyValueTable(rows);
|
||||
},
|
||||
|
||||
renderInternetKpi() {
|
||||
const v4 = this.params.internet.v4;
|
||||
const v6 = this.params.internet.v6;
|
||||
const connected = (v4.connected.value === true || v6.connected.value === true);
|
||||
const sub = [
|
||||
charts.badge('IPv4', (v4.connected.value === true) ? 'success' : 'warning'),
|
||||
charts.badge('IPv6', (v6.connected.value === true) ? 'success' : 'warning')
|
||||
];
|
||||
|
||||
if (v4.connected.value === true && Array.isArray(v4.addrsv4.value) && v4.addrsv4.value.length)
|
||||
sub.push(E('code', {}, [ v4.addrsv4.value[0].split('/')[0] ]));
|
||||
|
||||
return charts.kpi({
|
||||
className: 'internet-status-self',
|
||||
icon: connected ? 'internet' : 'not-internet',
|
||||
title: _('Internet'),
|
||||
value: [ connected ? _('Connected') : _('Not connected') ],
|
||||
sub: sub
|
||||
});
|
||||
},
|
||||
|
||||
renderSystemKpi() {
|
||||
const router = this.params.router;
|
||||
|
||||
return charts.kpi({
|
||||
className: 'router-status-self',
|
||||
icon: 'router',
|
||||
title: router.uptime.title,
|
||||
value: [ router.uptime.value || '-' ],
|
||||
sub: [ router.model.value || '' ]
|
||||
});
|
||||
},
|
||||
|
||||
renderUpdateWanData(data, v6) {
|
||||
|
||||
let min_metric = 2000000000;
|
||||
let min_metric_i = 0;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const metric = data[i].getMetric();
|
||||
if (metric < min_metric) {
|
||||
min_metric = metric;
|
||||
min_metric_i = i;
|
||||
}
|
||||
}
|
||||
|
||||
const ifc = data[min_metric_i];
|
||||
if(ifc){
|
||||
if (v6) {
|
||||
const uptime = ifc.getUptime();
|
||||
this.params.internet.v6.uptime.value = (uptime > 0) ? '%t'.format(uptime) : '-';
|
||||
this.params.internet.v6.ipprefixv6.value = ifc.getIP6Prefix() || '-';
|
||||
this.params.internet.v6.gatewayv6.value = ifc.getGateway6Addr() || '-';
|
||||
this.params.internet.v6.protocol.value= ifc.getI18n() || E('em', _('Not connected'));
|
||||
this.params.internet.v6.addrsv6.value = ifc.getIP6Addrs() || [ '-' ];
|
||||
this.params.internet.v6.dnsv6.value = ifc.getDNS6Addrs() || [ '-' ];
|
||||
this.params.internet.v6.connected.value = ifc.isUp();
|
||||
} else {
|
||||
const uptime = ifc.getUptime();
|
||||
this.params.internet.v4.uptime.value = (uptime > 0) ? '%t'.format(uptime) : '-';
|
||||
this.params.internet.v4.protocol.value= ifc.getI18n() || E('em', _('Not connected'));
|
||||
this.params.internet.v4.gatewayv4.value = ifc.getGatewayAddr() || '0.0.0.0';
|
||||
this.params.internet.v4.connected.value = ifc.isUp();
|
||||
this.params.internet.v4.addrsv4.value = ifc.getIPAddrs() || [ '-'];
|
||||
this.params.internet.v4.dnsv4.value = ifc.getDNSAddrs() || [ '-' ];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderInternetBox(data) {
|
||||
|
||||
this.params.internet = {
|
||||
|
||||
v4: {
|
||||
title: _('IPv4 Internet'),
|
||||
|
||||
connected: {
|
||||
title: _('Connected'),
|
||||
visible: true,
|
||||
value: false
|
||||
},
|
||||
|
||||
uptime: {
|
||||
title: _('Uptime'),
|
||||
visible: true,
|
||||
value: '-'
|
||||
},
|
||||
|
||||
protocol: {
|
||||
title: _('Protocol'),
|
||||
visible: true,
|
||||
value: '-'
|
||||
},
|
||||
|
||||
addrsv4: {
|
||||
title: _('IPv4'),
|
||||
visible: true,
|
||||
value: [ '-' ]
|
||||
},
|
||||
|
||||
gatewayv4: {
|
||||
title: _('GatewayV4'),
|
||||
visible: true,
|
||||
value: '-'
|
||||
},
|
||||
|
||||
dnsv4: {
|
||||
title: _('DNSv4'),
|
||||
visible: true,
|
||||
value: ['-']
|
||||
}
|
||||
},
|
||||
|
||||
v6: {
|
||||
title: _('IPv6 Internet'),
|
||||
|
||||
connected: {
|
||||
title: _('Connected'),
|
||||
visible: true,
|
||||
value: false
|
||||
},
|
||||
|
||||
uptime: {
|
||||
title: _('Uptime'),
|
||||
visible: true,
|
||||
value: '-'
|
||||
},
|
||||
|
||||
protocol: {
|
||||
title: _('Protocol'),
|
||||
visible: true,
|
||||
value: ' - '
|
||||
},
|
||||
|
||||
ipprefixv6 : {
|
||||
title: _('IPv6 prefix'),
|
||||
visible: true,
|
||||
value: ' - '
|
||||
},
|
||||
|
||||
addrsv6: {
|
||||
title: _('IPv6'),
|
||||
visible: false,
|
||||
value: [ '-' ]
|
||||
},
|
||||
|
||||
gatewayv6: {
|
||||
title: _('GatewayV6'),
|
||||
visible: true,
|
||||
value: '-'
|
||||
},
|
||||
|
||||
dnsv6: {
|
||||
title: _('DNSv6'),
|
||||
visible: true,
|
||||
value: [ '-' ]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.renderUpdateWanData(data[0], false);
|
||||
this.renderUpdateWanData(data[1], true);
|
||||
},
|
||||
|
||||
renderRouterBox(data) {
|
||||
|
||||
const boardinfo = data[2];
|
||||
const systeminfo = data[3];
|
||||
const unixtime = data[4];
|
||||
|
||||
let datestr = null;
|
||||
|
||||
if (unixtime) {
|
||||
const date = new Date(unixtime * 1000);
|
||||
const zn = uci.get('system', '@system[0]', 'zonename')?.replaceAll(' ', '_') || 'UTC';
|
||||
const ts = uci.get('system', '@system[0]', 'clock_timestyle') || 0;
|
||||
const hc = uci.get('system', '@system[0]', 'clock_hourcycle') || 0;
|
||||
|
||||
datestr = new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: (ts == 0) ? 'long' : 'full',
|
||||
hourCycle: (hc == 0) ? undefined : hc,
|
||||
timeZone: zn
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
this.params.router = {
|
||||
uptime: {
|
||||
title: _('Uptime'),
|
||||
value: systeminfo.uptime ? '%t'.format(systeminfo.uptime) : null,
|
||||
},
|
||||
|
||||
localtime: {
|
||||
title: _('Local Time'),
|
||||
value: datestr
|
||||
},
|
||||
|
||||
kernel: {
|
||||
title: _('Kernel Version'),
|
||||
value: boardinfo.kernel
|
||||
},
|
||||
|
||||
model: {
|
||||
title: _('Model'),
|
||||
value: boardinfo.model
|
||||
},
|
||||
|
||||
system: {
|
||||
title: _('Architecture'),
|
||||
value: boardinfo.system
|
||||
},
|
||||
|
||||
release: {
|
||||
title: _('Firmware Version'),
|
||||
value: boardinfo?.release?.description
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
render(data) {
|
||||
this.renderInternetBox(data);
|
||||
this.renderRouterBox(data);
|
||||
|
||||
return {
|
||||
kpi: [ this.renderInternetKpi(), this.renderSystemKpi() ],
|
||||
tabs: [
|
||||
{ id: 'internet', title: _('Internet'), content: this.renderInternetTab() },
|
||||
{ id: 'system', title: _('System'), content: this.renderSystemTab() }
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require rpc';
|
||||
'require network';
|
||||
'require view.dashboard.lib.charts as charts';
|
||||
|
||||
var callLuciDHCPLeases = rpc.declare({
|
||||
object: 'luci-rpc',
|
||||
method: 'getDHCPLeases',
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
return baseclass.extend({
|
||||
title: _('DHCP Devices'),
|
||||
|
||||
params: {},
|
||||
|
||||
load() {
|
||||
return Promise.all([
|
||||
callLuciDHCPLeases(),
|
||||
]);
|
||||
},
|
||||
|
||||
renderKpi() {
|
||||
const count = this.params.lan.devices.length;
|
||||
|
||||
return charts.kpi({
|
||||
className: 'router-status-lan',
|
||||
icon: 'devices',
|
||||
title: this.title,
|
||||
value: [ String(count) ],
|
||||
sub: [ count ? _('Active leases') : _('No active leases') ]
|
||||
});
|
||||
},
|
||||
|
||||
renderTable() {
|
||||
return charts.table({
|
||||
className: 'assoclist devices-info',
|
||||
head: [ _('Hostname'), _('IP Address'), _('MAC') ],
|
||||
rows: this.params.lan.devices.map(device => [
|
||||
device.hostname,
|
||||
E('code', {}, [ device.ipv4 ]),
|
||||
E('code', {}, [ device.macaddr ])
|
||||
]),
|
||||
emptyText: _('No active leases'),
|
||||
foot: [ '', _('Total'), String(this.params.lan.devices.length) ]
|
||||
});
|
||||
},
|
||||
|
||||
renderUpdateData(leases) {
|
||||
const dev_arr = [];
|
||||
|
||||
leases.forEach(({ hostname = '?', ipaddr: ipv4 = '-', macaddr = '00:00:00:00:00:00' }) => {
|
||||
dev_arr.push({ hostname, ipv4, macaddr });
|
||||
});
|
||||
|
||||
this.params.lan = { devices: dev_arr };
|
||||
},
|
||||
|
||||
render([leases]) {
|
||||
if (!L.hasSystemFeature('dnsmasq') && !L.hasSystemFeature('odhcpd'))
|
||||
return null;
|
||||
|
||||
this.renderUpdateData([...leases.dhcp_leases]);
|
||||
|
||||
return {
|
||||
kpi: [ this.renderKpi() ],
|
||||
tabs: [
|
||||
{ id: 'dhcp', title: this.title, count: this.params.lan.devices.length, content: this.renderTable() }
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,361 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require dom';
|
||||
'require network';
|
||||
'require rpc';
|
||||
'require view.dashboard.lib.charts as charts';
|
||||
|
||||
/* -67 dBm is the minimum RSSI Cisco specifies for voice-grade coverage;
|
||||
below it the client is still usable, just not at that level. */
|
||||
const GRADES = [
|
||||
{ min: -67, className: 'dashboard-bar-ok', kind: 'success' },
|
||||
{ min: -Infinity, className: 'dashboard-bar-mid', kind: 'warning' }
|
||||
];
|
||||
|
||||
const NO_SIGNAL = { className: 'dashboard-bar-none', kind: '' };
|
||||
|
||||
function signalGrade(rssi) {
|
||||
return (rssi != null) ? GRADES.find(grade => rssi >= grade.min) : NO_SIGNAL;
|
||||
}
|
||||
|
||||
function rssiOf(device) {
|
||||
return device.signal.value.rssi ?? -Infinity;
|
||||
}
|
||||
|
||||
return baseclass.extend({
|
||||
|
||||
title: _('Wireless'),
|
||||
|
||||
params: [],
|
||||
|
||||
load() {
|
||||
return Promise.all([
|
||||
network.getWifiDevices(),
|
||||
network.getWifiNetworks(),
|
||||
network.getHostHints()
|
||||
]).then(radios_networks_hints => {
|
||||
const tasks = [];
|
||||
|
||||
for (let i = 0; i < radios_networks_hints[1].length; i++)
|
||||
tasks.push(L.resolveDefault(radios_networks_hints[1][i].getAssocList(), []).then(L.bind((net, list) => {
|
||||
net.assoclist = list.sort((a, b) => a.mac.localeCompare(b.mac));
|
||||
}, this, radios_networks_hints[1][i])));
|
||||
|
||||
return Promise.all(tasks).then(() => {
|
||||
return radios_networks_hints;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
renderKpi() {
|
||||
const radios = this.params.wifi.radios;
|
||||
const active = radios.filter(radio => radio.isactive.value === true).length;
|
||||
|
||||
return charts.kpi({
|
||||
className: 'router-status-wifi',
|
||||
icon: 'wireless',
|
||||
title: _('Wireless clients'),
|
||||
value: [ String(this.params.wifi.devices.length) ],
|
||||
sub: [ _('%d of %d SSIDs active').format(active, radios.length) ]
|
||||
});
|
||||
},
|
||||
|
||||
renderDistributionChart() {
|
||||
const devices = this.params.wifi.devices;
|
||||
const series = this.params.wifi.radios.map(radio => ({
|
||||
label: [ radio.ssid.value, ' ', E('small', {}, [ radio.chan.value ]) ],
|
||||
value: (radio.isactive.value === true) ? (parseInt(radio.associations.value) || 0) : 0
|
||||
}));
|
||||
|
||||
return charts.card({
|
||||
className: 'router-status-wifi',
|
||||
title: _('Client distribution'),
|
||||
desc: _('by SSID'),
|
||||
body: devices.length
|
||||
? charts.donut({ series: series, centerLabel: _('clients'), ariaLabel: _('Client distribution') })
|
||||
: charts.empty(_('No wireless clients connected'))
|
||||
});
|
||||
},
|
||||
|
||||
renderSignalChart() {
|
||||
const devices = this.params.wifi.devices.slice().sort((a, b) => rssiOf(b) - rssiOf(a));
|
||||
|
||||
return charts.card({
|
||||
className: 'router-status-wifi',
|
||||
title: _('Signal Strength'),
|
||||
desc: devices.length ? '%s · %s'.format(_('%d clients').format(devices.length), _('dBm')) : '',
|
||||
body: devices.length ? [ charts.barChart({
|
||||
id: 'signal',
|
||||
// iwinfo's nl80211 backend clamps the signal to -110..-40 dBm
|
||||
// (the cfg80211 wext compat range) before deriving quality.
|
||||
min: -110,
|
||||
max: -40,
|
||||
ticks: [ -110, -100, -90, -80, -70, -60, -50, -40 ].map(v => ({ value: v, label: '%d'.format(v) })),
|
||||
items: devices.map(device => ({
|
||||
label: device.hostname.value,
|
||||
title: [ device.hostname.value, device.ssid.value, this.signalText(device) ].join(' · '),
|
||||
values: [ { value: device.signal.value.rssi, className: signalGrade(device.signal.value.rssi).className } ]
|
||||
}))
|
||||
}), charts.legend([
|
||||
{ className: 'dashboard-bar-ok', label: '%s ≥ -67 %s'.format(_('Good'), _('dBm')) },
|
||||
{ className: 'dashboard-bar-mid', label: '%s < -67 %s'.format(_('Weak'), _('dBm')) }
|
||||
]) ] : charts.empty(_('No wireless clients connected'))
|
||||
});
|
||||
},
|
||||
|
||||
signalText(device) {
|
||||
if (device.signal.value.rssi == null)
|
||||
return _('No RX signal');
|
||||
|
||||
const rssi = '%d %s'.format(device.signal.value.rssi, _('dBm'));
|
||||
|
||||
if (device.signal.value.noise != null)
|
||||
return '%d / %d %s'.format(device.signal.value.rssi, device.signal.value.noise, _('dBm'));
|
||||
|
||||
return rssi;
|
||||
},
|
||||
|
||||
// Fixed five ticks in the unit the peak falls into; the interval is the
|
||||
// smallest 1-2-5 step whose fourth multiple covers the peak.
|
||||
byteScale(peak) {
|
||||
const units = [ 'B', 'KiB', 'MiB', 'GiB', 'TiB' ];
|
||||
let exp = 0;
|
||||
|
||||
while (peak >= 1024 && exp < units.length - 1) {
|
||||
peak /= 1024;
|
||||
exp++;
|
||||
}
|
||||
|
||||
const raw = peak / 4;
|
||||
const magnitude = Math.pow(10, Math.floor(Math.log10(Math.max(raw, 1e-9))));
|
||||
const step = [ 1, 2, 5, 10 ].map(n => n * magnitude).find(candidate => candidate >= raw) * Math.pow(1024, exp);
|
||||
const unit = Math.pow(1024, exp);
|
||||
const decimals = (step / unit < 1) ? 1 : 0;
|
||||
|
||||
return {
|
||||
step: step,
|
||||
format: value => '%s %s'.format((value / unit).toFixed(decimals), units[exp])
|
||||
};
|
||||
},
|
||||
|
||||
renderTrafficChart() {
|
||||
const devices = this.params.wifi.devices.slice().sort((a, b) =>
|
||||
(b.transferred.value.bytes.rx + b.transferred.value.bytes.tx) - (a.transferred.value.bytes.rx + a.transferred.value.bytes.tx));
|
||||
const peak = Math.max(1, ...devices.map(device => Math.max(device.transferred.value.bytes.rx, device.transferred.value.bytes.tx)));
|
||||
const scale = this.byteScale(peak);
|
||||
|
||||
return charts.card({
|
||||
className: 'router-status-wifi',
|
||||
title: _('Client traffic'),
|
||||
desc: devices.length ? _('Transferred') : '',
|
||||
body: devices.length ? [
|
||||
charts.barChart({
|
||||
id: 'traffic',
|
||||
max: scale.step * 4,
|
||||
ticks: [ 0, 1, 2, 3, 4 ].map(n => ({ value: scale.step * n, label: scale.format(scale.step * n) })),
|
||||
items: devices.map(device => ({
|
||||
label: device.hostname.value,
|
||||
title: '%s · %s %s · %s %s'.format(device.hostname.value, _('Up.'), device.transferred.value.rx, _('Down.'), device.transferred.value.tx),
|
||||
values: [
|
||||
{ value: device.transferred.value.bytes.rx, className: 'dashboard-bar-up' },
|
||||
{ value: device.transferred.value.bytes.tx, className: 'dashboard-bar-down' }
|
||||
]
|
||||
}))
|
||||
}),
|
||||
charts.legend([
|
||||
{ className: 'dashboard-bar-up', label: _('Up.') },
|
||||
{ className: 'dashboard-bar-down', label: _('Down.') }
|
||||
])
|
||||
] : charts.empty(_('No wireless clients connected'))
|
||||
});
|
||||
},
|
||||
|
||||
renderNetworkCard(radio) {
|
||||
const active = radio.isactive.value === true;
|
||||
const fields = [];
|
||||
|
||||
for (const key of [ 'chan', 'rate', 'encryption', 'bssid' ])
|
||||
if (radio[key].visible)
|
||||
fields.push(E('div', {}, [
|
||||
E('span', {}, [ radio[key].title ]),
|
||||
E((key == 'bssid') ? 'code' : 'b', {}, [ radio[key].value ])
|
||||
]));
|
||||
|
||||
return E('div', { 'class': 'ifacebox' }, [
|
||||
E('div', { 'class': 'ifacebox-head' + (active ? ' active' : '') }, [
|
||||
E('strong', {}, [ radio.ssid.value ])
|
||||
]),
|
||||
E('div', { 'class': 'ifacebox-body' }, [
|
||||
E('div', { 'class': 'dashboard-net-count' }, [
|
||||
E('b', {}, [ String(active ? radio.associations.value : 0) ]),
|
||||
E('small', {}, [ radio.associations.title ])
|
||||
]),
|
||||
active
|
||||
? E('div', { 'class': 'dashboard-net-fields' }, fields)
|
||||
: E('div', { 'class': 'dashboard-net-note' }, [ _('Interface is disabled') ])
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
renderClientTable() {
|
||||
return charts.table({
|
||||
className: 'assoclist devices-info',
|
||||
head: [
|
||||
_('Hostname'),
|
||||
_('SSID'),
|
||||
'%s / %s'.format(_('Signal'), _('Noise floor')),
|
||||
{ text: _('Up.'), className: 'right' },
|
||||
{ text: _('Down.'), className: 'right' }
|
||||
],
|
||||
rows: this.params.wifi.devices.map(device => [
|
||||
device.hostname.value,
|
||||
device.ssid.value,
|
||||
E('span', { 'class': 'nowrap' }, [
|
||||
charts.badge((device.signal.value.rssi != null) ? '%d %s'.format(device.signal.value.rssi, _('dBm')) : _('No RX signal'), signalGrade(device.signal.value.rssi).kind),
|
||||
' ',
|
||||
(device.signal.value.noise != null)
|
||||
? E('small', {}, [ '/ %d %s'.format(device.signal.value.noise, _('dBm')) ])
|
||||
: ''
|
||||
]),
|
||||
{ text: device.transferred.value.rx, className: 'right' },
|
||||
{ text: device.transferred.value.tx, className: 'right' }
|
||||
]),
|
||||
emptyText: _('No wireless clients connected'),
|
||||
foot: [ '', _('Total'), String(this.params.wifi.devices.length), '', '' ]
|
||||
});
|
||||
},
|
||||
|
||||
renderTab() {
|
||||
return E('div', {}, [
|
||||
E('div', { 'class': 'dashboard-net-grid' }, this.params.wifi.radios.map(radio => this.renderNetworkCard(radio))),
|
||||
E('h3', {}, [ _('Wireless clients') ]),
|
||||
this.renderClientTable()
|
||||
]);
|
||||
},
|
||||
|
||||
renderUpdateData(radios, networks, hosthints) {
|
||||
|
||||
for (let i = 0; i < radios.sort((a, b) => a.getName().localeCompare(b.getName())).length; i++) {
|
||||
const network_items = networks.filter(net => { return net.getWifiDeviceName() == radios[i].getName() });
|
||||
|
||||
for (let j = 0; j < network_items.length; j++) {
|
||||
const net = network_items[j];
|
||||
const is_assoc = (net.getBSSID() != '00:00:00:00:00:00' && net.getChannel() && !net.isDisabled());
|
||||
const chan = net.getChannel();
|
||||
const freq = net.getFrequency();
|
||||
const rate = net.getBitRate();
|
||||
|
||||
this.params.wifi.radios.push(
|
||||
{
|
||||
ssid : {
|
||||
title: _('SSID'),
|
||||
visible: true,
|
||||
value: net.getActiveSSID() || '?'
|
||||
},
|
||||
|
||||
isactive : {
|
||||
title: _('Active'),
|
||||
visible: true,
|
||||
value: !net.isDisabled()
|
||||
},
|
||||
|
||||
chan : {
|
||||
title: _('Channel'),
|
||||
visible: true,
|
||||
value: chan ? '%d (%.3f %s)'.format(chan, freq, _('GHz')) : '-'
|
||||
},
|
||||
|
||||
rate : {
|
||||
title: _('Bitrate'),
|
||||
visible: true,
|
||||
value: rate ? '%d %s'.format(rate, _('Mbit/s')) : '-'
|
||||
},
|
||||
|
||||
bssid : {
|
||||
title: _('BSSID'),
|
||||
visible: true,
|
||||
value: is_assoc ? (net.getActiveBSSID() || '-') : '-'
|
||||
},
|
||||
|
||||
encryption : {
|
||||
title: _('Encryption'),
|
||||
visible: true,
|
||||
value: is_assoc ? net.getActiveEncryption() : '-'
|
||||
},
|
||||
|
||||
associations : {
|
||||
title: _('Devices Connected'),
|
||||
visible: true,
|
||||
value: is_assoc ? (net.assoclist.length || '0') : 0
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < networks.length; i++) {
|
||||
for (let k = 0; k < networks[i].assoclist.length; k++) {
|
||||
const bss = networks[i].assoclist[k];
|
||||
const name = hosthints.getHostnameByMACAddr(bss.mac);
|
||||
|
||||
this.params.wifi.devices.push(
|
||||
{
|
||||
hostname : {
|
||||
title: _('Hostname'),
|
||||
visible: true,
|
||||
value: name || '?'
|
||||
},
|
||||
|
||||
ssid : {
|
||||
title: _('SSID'),
|
||||
visible: true,
|
||||
value: networks[i].getActiveSSID()
|
||||
},
|
||||
|
||||
signal : {
|
||||
title: _('Signal Strength'),
|
||||
visible: true,
|
||||
value: {
|
||||
rssi: (typeof(bss.signal) == 'number' && bss.signal < 0) ? bss.signal : null,
|
||||
noise: (typeof(bss.noise) == 'number' && bss.noise != 0) ? bss.noise : null
|
||||
}
|
||||
},
|
||||
|
||||
// AP-side station counters: rx = received from the client (its
|
||||
// upload), tx = sent to the client (its download)
|
||||
transferred : {
|
||||
title: _('Transferred'),
|
||||
visible: true,
|
||||
value: {
|
||||
rx: '%s'.format('%1024.2mB'.format(bss.rx.bytes)),
|
||||
tx: '%s'.format('%1024.2mB'.format(bss.tx.bytes)),
|
||||
bytes: { rx: bss.rx.bytes || 0, tx: bss.tx.bytes || 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render([radios, networks, hosthints]) {
|
||||
|
||||
this.params.wifi = {
|
||||
radios: [],
|
||||
devices: []
|
||||
};
|
||||
|
||||
this.renderUpdateData(radios, networks, hosthints);
|
||||
|
||||
if (!this.params.wifi.radios.length)
|
||||
return null;
|
||||
|
||||
return {
|
||||
kpi: [ this.renderKpi() ],
|
||||
charts: [ this.renderDistributionChart(), this.renderSignalChart(), this.renderTrafficChart() ],
|
||||
tabs: [
|
||||
{ id: 'wifi', title: this.title, count: this.params.wifi.devices.length, content: this.renderTab() }
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user