Repository: brvphoenix/luci-app-wrtbwmon
Branch: master
Commit: f1b59b2309a0
Files: 16
Total size: 55.3 KB
Directory structure:
gitextract_89cv8w6g/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── CI.yml
│ └── stale.yml
├── README.md
└── luci-app-wrtbwmon/
├── Makefile
├── htdocs/
│ └── luci-static/
│ └── resources/
│ └── view/
│ └── wrtbwmon/
│ ├── config.js
│ ├── custom.js
│ ├── details.js
│ └── wrtbwmon.css
├── po/
│ ├── templates/
│ │ └── wrtbwmon.pot
│ ├── zh_Hans/
│ │ └── wrtbwmon.po
│ └── zh_Hant/
│ └── wrtbwmon.po
└── root/
├── etc/
│ └── luci-wrtbwmon.conf
└── usr/
├── libexec/
│ └── rpcd/
│ └── luci.wrtbwmon
└── share/
├── luci/
│ └── menu.d/
│ └── luci-app-wrtbwmon.json
└── rpcd/
└── acl.d/
└── luci-app-wrtbwmon.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-minor", "version-update:semver-patch"]
================================================
FILE: .github/workflows/CI.yml
================================================
name: CI
on:
push:
branches:
- master
tags:
- 'release-*'
paths:
- 'luci-app-wrtbwmon/**'
pull_request:
branches:
- master
paths:
- 'luci-app-wrtbwmon/**'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
build:
name: Build the IPK
runs-on: ubuntu-latest
container:
image: ghcr.io/openwrt/sdk:x86-64-23.05-SNAPSHOT
options: --user root
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker Build
working-directory: /builder
run: |
sed \
-e 's,git\.openwrt\.org/feed/,github.com/openwrt/,' \
-e 's,git\.openwrt\.org/openwrt/,github.com/openwrt/,' \
-e 's,git\.openwrt\.org/project/,github.com/openwrt/,' \
feeds.conf.default | grep -Ev "^src-git(-full)? (routing|telephony) .*" > feeds.conf
echo "src-cpy local ${GITHUB_WORKSPACE}" >> feeds.conf
./scripts/feeds update -f luci local
./scripts/feeds install -p local luci-app-wrtbwmon
make defconfig
make package/luci-app-wrtbwmon/compile V=sc -j$(nproc) BUILD_LOG=1
tar -cJf logs.tar.xz logs
- name: Release
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/release-')
uses: actions/github-script@v7
with:
script: |
const tag = context.ref.replace("refs/tags/", "");
try {
// Get release for this tag
const release = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag: tag,
});
// Delete obsolete release
await github.rest.repos.deleteRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.data.id,
});
}
catch(err) {
console.log(err);
}
finally {
// Create release for this tag
const release = await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: tag,
draft: false,
prerelease: true,
});
// Upload the release asset
const fs = require('fs');
const patterns = ['/builder/bin/packages/x86_64/*/*wrtbwmon*.ipk']
const globber = await glob.create(patterns.join('\n'))
for await (const file of globber.globGenerator()) {
await github.rest.repos.uploadReleaseAsset({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.data.id,
name: file.substr(file.lastIndexOf('/') + 1),
data: await fs.readFileSync(file)
});
}
}
- name: Upload app
if: ${{ ! (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/release-')) }}
uses: actions/upload-artifact@v4
with:
name: luci-app-wrtbwmon
path: /builder/bin/packages/x86_64/*/*wrtbwmon*
if-no-files-found: error
- name: Upload Log
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: buildlog
path: /builder/logs.tar.xz
================================================
FILE: .github/workflows/stale.yml
================================================
name: "Close stale issues"
on:
schedule:
- cron: "0 0 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open 120 days with no activity. Remove stale label or comment or this will be closed in 5 days'
stale-pr-message: 'This pr is stale has been open 120 days with no activity. Remove stale label or comment or this will be closed in 5 days'
stale-issue-label: 'no-issue-activity'
exempt-issue-labels: 'awaiting-approval,work-in-progress'
stale-pr-label: 'no-pr-activity'
exempt-pr-labels: 'awaiting-approval,work-in-progress'
days-before-stale: 120
days-before-close: 5
================================================
FILE: README.md
================================================
# luci-app-wrtbwmon
[](https://github.com/brvphoenix/luci-app-wrtbwmon/actions)
[](https://github.com/brvphoenix/luci-app-wrtbwmon/releases)
[](https://github.com/brvphoenix/luci-app-wrtbwmon/releases/latest)
This repo provides yet another LuCI module for wrtbwmon, which has similar features with [Kiougar's one](https://github.com/Kiougar/luci-wrtbwmon). The differnence is that this one has more features supported:
1. Support IPV6.
1. Identify a host by the unique MAC rather than its IP.
1. Use the progress bar to display the total bandwidth.
1. For brevity, some columns are hidden by default.
1. Convert to client side for rendering just as what the new openwrt release has done.
## Known issues
* **Incompatible** with the [pyrovski's wrtbwmon](https://github.com/pyrovski/wrtbwmon). **You must download the compatible one from [here](https://github.com/brvphoenix/wrtbwmon)**.
* **Incompatible** with Routing/NAT, Flow Offloading and so on.
## Screenshots

## Downloading
Openwrt 19.07 has been fully supported after commit: [ff4909d](https://github.com/brvphoenix/luci-app-wrtbwmon/tree/ff4909d8f5d06fee87f7ec5a365ac5dde6492130).
* `openwrt-19.07.3 ... latest`: [latest](https://github.com/brvphoenix/luci-app-wrtbwmon/releases/latest)
* `openwrt-19.07.0 ... 19.07.2`: [release-2.0.7](https://github.com/brvphoenix/luci-app-wrtbwmon/releases/tag/release-2.0.7)
* `openwrt-18.06`: [release-1.6.3](https://github.com/brvphoenix/luci-app-wrtbwmon/releases/tag/release-1.6.3)
After installing, you will see a new `Traffic status` menu item in the `Network` menu list in the LuCI Page.
## Information
In principle, the lua version (based on the old openwrt 18.06) has been dropped support since [ff4909d](https://github.com/brvphoenix/luci-app-wrtbwmon/tree/ff4909d8f5d06fee87f7ec5a365ac5dde6492130), and the new features will not backport to the old lua version. However, it is welcomed if someone can implement it and make a pr.
If anyone would like to help translate this luci app, just upload the translation files or make a pr.
## Credits
Thanks to
* [Kiougar](https://github.com/Kiougar/luci-wrtbwmon) for the original codes.
* [pyrovski](https://github.com/pyrovski) for creating `wrtbwmon`.
================================================
FILE: luci-app-wrtbwmon/Makefile
================================================
#
# Copyright (C) 2020 xxx <xxx@xxx.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-wrtbwmon
PKG_VERSION:=2.0.13
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=
LUCI_TITLE:=A Luci module that uses wrtbwmon to track bandwidth usage
LUCI_DEPENDS:=+wrtbwmon
LUCI_PKGARCH:=all
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
================================================
FILE: luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/config.js
================================================
'use strict';
'require form';
'require rpc';
'require view';
var callChangeDatabasePath = rpc.declare({
object: 'luci.wrtbwmon',
method: 'change_db_path',
params: [ 'state' ]
});
return view.extend({
render: function() {
var m, s, o;
m = new form.Map('wrtbwmon', _('Usage - Configuration'));
s = m.section(form.NamedSection, 'general', 'wrtbwmon', _('General settings'));
s.addremove = false;
o = s.option(form.Flag, 'enabled', _('Keep running in the background'));
o.rmempty = true;
o = s.option(form.Value, 'path', _('Database path'), _('This box is used to select the Database path, which is /tmp/usage.db by default.'));
o.value('/tmp/usage.db');
o.value('/etc/usage.db');
o.default = '/tmp/usage.db';
o.rmempty = false;
return m.render();
},
handleSaveApply: function(ev, mode) {
return callChangeDatabasePath('before')
.then(this.super.bind(this, 'handleSaveApply', arguments))
.then(callChangeDatabasePath.bind(this, 'after'));
}
});
================================================
FILE: luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/custom.js
================================================
'use strict';
'require fs';
'require ui';
'require view';
return view.extend({
load: function() {
return fs.trimmed('/etc/wrtbwmon.user').catch(function(err) {
ui.addNotification(null, E('p', {}, _('Unable to load the customized hostname file: ' + err.message)));
return '';
});
},
render: function(data) {
return E('div', {'class': 'cbi-map'}, [
E('h2', {'name': 'content'}, [ _('Usage - Custom User File') ]),
E('div', {'class': 'cbi-map-descr'}, [
_('Each line must have the following format:'),
E('em', {'style': 'color:red'}, '00:aa:bb:cc:ee:ff,hostname')
]),
E('div', {'class': 'cbi-section'}, [
E('textarea', {
'id': 'custom_hosts',
'style': 'width: 100%;padding: .5em;',
'rows': 20
}, data)
])
]);
},
handleSave: function(ev) {
var map = document.querySelector('#custom_hosts');
return fs.write('/etc/wrtbwmon.user', map.value.trim().replace(/\r\n/g, '\n') + '\n');
},
handleSaveApply: null,
handleReset: null
});
================================================
FILE: luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/details.js
================================================
'use strict';
'require dom';
'require fs';
'require poll';
'require rpc';
'require ui';
'require validation';
'require view';
var cachedData = [];
var luciConfig = '/etc/luci-wrtbwmon.conf';
var hostNameFile = '/etc/wrtbwmon.user';
var columns = {
thClient: _('Clients'),
thMAC: _('MAC'),
thDownload: _('Download'),
thUpload: _('Upload'),
thTotalDown: _('Total Down'),
thTotalUp: _('Total Up'),
thTotal: _('Total'),
thFirstSeen: _('First Seen'),
thLastSeen: _('Last Seen')
};
var callLuciDHCPLeases = rpc.declare({
object: 'luci-rpc',
method: 'getDHCPLeases',
expect: { '': {} }
});
var callLuciDSLStatus = rpc.declare({
object: 'luci-rpc',
method: 'getDSLStatus',
expect: { '': {} }
});
var callGetDatabaseRaw = rpc.declare({
object: 'luci.wrtbwmon',
method: 'get_db_raw',
params: [ 'protocol' ]
});
var callGetDatabasePath = rpc.declare({
object: 'luci.wrtbwmon',
method: 'get_db_path',
params: [ 'protocol' ]
});
var callRemoveDatabase = rpc.declare({
object: 'luci.wrtbwmon',
method: 'remove_db',
params: [ 'protocol' ]
});
function $(tid) {
return document.getElementById(tid);
}
function clickToResetDatabase(settings, table, updated, updating, ev) {
if (confirm(_('This will delete the database file. Are you sure?'))) {
return callRemoveDatabase(settings.protocol)
.then(function() {
updateData(settings, table, updated, updating, true);
});
}
}
function clickToSaveConfig(keylist, cstrs) {
var data = {};
for (var i = 0; i < keylist.length; i++) {
data[keylist[i]] = cstrs[keylist[i]].getValue();
}
ui.showModal(_('Configuration'), [
E('p', { 'class': 'spinning' }, _('Saving configuration data...'))
]);
return fs.write(luciConfig, JSON.stringify(data, undefined, '\t') + '\n')
.catch(function(err) {
ui.addNotification(null, E('p', {}, [ _('Unable to save %s: %s').format(luciConfig, err) ]));
})
.then(ui.hideModal)
.then(function() { document.location.reload(); });
}
function clickToSelectInterval(settings, updating, ev) {
if (ev.target.value > 0) {
settings.interval = parseInt(ev.target.value);
if (!poll.active()) poll.start();
}
else {
poll.stop();
setUpdateMessage(updating, -1);
}
}
function clickToSelectProtocol(settings, table, updated, updating, ev) {
settings.protocol = ev.target.value;
updateData(settings, table, updated, updating, true);
}
function createOption(args, val) {
var cstr = args[0], title = args[1], desc = args.slice(-1), widget, frame;
widget = args.length == 4 ? new cstr(val, args[2]) : new cstr(val, args[2], args[3]);
frame = E('div', {'class': 'cbi-value'}, [
E('label', {'class': 'cbi-value-title'}, title),
E('div', {'class': 'cbi-value-field'}, E('div', {}, widget.render()))
]);
if (desc && desc != '')
dom.append(frame.lastChild, E('div', { 'class': 'cbi-value-description' }, desc));
return [widget, frame];
}
function displayTable(tb, settings) {
var elm, elmID, col, sortedBy, flag, IPVer;
elm = tb.querySelector('.th.sorted');
elmID = elm ? elm.id : 'thTotal';
sortedBy = elm && elm.classList.contains('ascent') ? 'asc' : 'desc';
col = Object.keys(columns).indexOf(elmID);
IPVer = col == 0 ? settings.protocol : null;
flag = sortedBy == 'desc' ? 1 : -1;
cachedData[0].sort(sortTable.bind(this, col, IPVer, flag));
//console.time('show');
updateTable(tb, cachedData, '<em>%s</em>'.format(_('Collecting data...')), settings);
//console.timeEnd('show');
progressbar('downstream', cachedData[1][0], settings.downstream, settings.useBits, settings.useMultiple);
progressbar('upstream', cachedData[1][1], settings.upstream, settings.useBits, settings.useMultiple);
}
function formatSize(size, useBits, useMultiple) {
// String.format automatically adds the i for KiB if the multiple is 1024
return String.format('%%%s.2m%s'.format(useMultiple, (useBits ? 'bit' : 'B')), useBits ? size * 8 : size);
}
function formatSpeed(speed, useBits, useMultiple) {
return formatSize(speed, useBits, useMultiple) + '/s';
}
function formatDate(d) {
var Y = d.getFullYear(), M = d.getMonth() + 1, D = d.getDate(),
hh = d.getHours(), mm = d.getMinutes(), ss = d.getSeconds();
return '%04d/%02d/%02d %02d:%02d:%02d'.format(Y, M, D, hh, mm, ss);
}
function getDSLBandwidth() {
return callLuciDSLStatus().then(function(res) {
return {
upstream : res.max_data_rate_up || null,
downstream : res.max_data_rate_down || null
};
});
}
function handleConfig(ev) {
ui.showModal(_('Configuration'), [
E('p', { 'class': 'spinning' }, _('Loading configuration data...'))
]);
parseDefaultSettings(luciConfig)
.then(function(settings) {
var arglist, keylist = Object.keys(settings), res, cstrs = {}, node = [], body;
arglist = [
[ui.Select, _('Default Protocol'), {'ipv4': _('ipv4'), 'ipv6': _('ipv6')}, {}, ''],
[ui.Select, _('Default Refresh Interval'), {'-1': _('Disabled'), '2': _('2 seconds'), '5': _('5 seconds'), '10': _('10 seconds'), '30': _('30 seconds')}, {sort: ['-1', '2', '5', '10', '30']}, ''],
[ui.Dropdown, _('Default Columns'), columns, {multiple: true, sort: false, custom_placeholder: '', dropdown_items: 3}, ''],
[ui.Checkbox, _('Show Zeros'), {value_enabled: true, value_disabled: false}, ''],
[ui.Checkbox, _('Transfer Speed in Bits'), {value_enabled: true, value_disabled: false}, ''],
[ui.Select, _('Multiple of Unit'), {'1000': _('SI - 1000'), '1024': _('IEC - 1024')}, {}, ''],
[ui.Checkbox, _('Use DSL Bandwidth'), {value_enabled: true, value_disabled: false}, ''],
[ui.Textfield, _('Upstream Bandwidth'), {datatype: 'ufloat'}, 'Mbps'],
[ui.Textfield, _('Downstream Bandwidth'), {datatype: 'ufloat'}, 'Mbps'],
[ui.DynamicList, _('Hide MAC Addresses'), '', {datatype: 'macaddr'}, '']
]; // [constructor, label(, all_choices), options, description]
for (var i = 0; i < keylist.length; i++) {
res = createOption(arglist[i], settings[keylist[i]]);
cstrs[keylist[i]] = res[0];
node.push(res[1]);
}
body = [
E('p', {}, _('Configure the default values for luci-app-wrtbwmon.')),
E('div', {}, node),
E('div', { 'class': 'right' }, [
E('div', {
'class': 'btn cbi-button-neutral',
'click': ui.hideModal
}, _('Cancel')),
' ',
E('div', {
'class': 'btn cbi-button-positive',
'click': clickToSaveConfig.bind(this, keylist, cstrs),
'disabled': (L.hasViewPermission ? !L.hasViewPermission() : null) || null
}, _('Save'))
])
];
ui.showModal(_('Configuration'), body);
})
}
function loadCss(path) {
var head = document.head || document.getElementsByTagName('head')[0];
var link = E('link', {
'rel': 'stylesheet',
'href': path,
'type': 'text/css'
});
head.appendChild(link);
}
function parseDatabase(raw, hosts, showZero, hideMACs) {
var values = [],
totals = [0, 0, 0, 0, 0],
rows = raw.trim().split(/\r?\n|\r/g),
rowIndex = [1, 0, 3, 4, 5, 6, 7, 8, 9, 0];
rows.shift();
for (var i = 0; i < rows.length; i++) {
var row = rows[i].split(',');
if ((!showZero && row[7] == 0) || hideMACs.indexOf(row[0]) >= 0) continue;
for (var j = 0; j < totals.length; j++) {
totals[j] += parseInt(row[3 + j]);
}
var newRow = rowIndex.map(function(i) { return row[i] });
if (newRow[1].toLowerCase() in hosts) {
newRow[9] = hosts[newRow[1].toLowerCase()];
}
values.push(newRow);
}
return [values, totals];
}
function parseDefaultSettings(file) {
var defaultColumns = ['thClient', 'thDownload', 'thUpload', 'thTotalDown', 'thTotalUp', 'thTotal'],
keylist = ['protocol', 'interval', 'showColumns', 'showZero', 'useBits', 'useMultiple', 'useDSL', 'upstream', 'downstream', 'hideMACs'],
valuelist = ['ipv4', '5', defaultColumns, true, false, '1000', false, '100', '100', []];
return fs.read_direct(file, 'json').then(function(oldSettings) {
var settings = {};
for (var i = 0; i < keylist.length; i++) {
if (!(keylist[i] in oldSettings))
settings[keylist[i]] = valuelist[i];
else
settings[keylist[i]] = oldSettings[keylist[i]];
}
if (settings.useDSL) {
return getDSLBandwidth().then(function(dsl) {
for (var s in dsl)
settings[s] = dsl[s];
return settings;
});
}
else {
return settings;
}
})
.catch(function() { return {} });
}
function progressbar(query, v, m, useBits, useMultiple) {
// v = B/s, m = Mb/s
var pg = $(query),
vn = (v * 8) || 0,
mn = (m || 100) * Math.pow(1000, 2),
fv = formatSpeed(v, useBits, useMultiple),
pc = '%.2f'.format((100 / mn) * vn),
wt = Math.floor(pc > 100 ? 100 : pc),
bgc = (pc >= 95 ? 'red' : (pc >= 80 ? 'darkorange' : (pc >= 60 ? 'yellow' : 'lime')));
if (pg) {
pg.firstElementChild.style.width = wt + '%';
pg.firstElementChild.style.background = bgc;
pg.setAttribute('title', '%s (%f%%)'.format(fv, pc));
}
}
function setupThisDOM(settings, table) {
document.addEventListener('poll-stop', function() {
$('selectInterval').value = -1;
});
document.addEventListener('poll-start', function() {
$('selectInterval').value = settings.interval;
});
table.querySelectorAll('.th').forEach(function(e) {
if (e) {
e.addEventListener('click', function (ev) {
setSortedColumn(ev.target);
displayTable(table, settings);
});
if (settings.showColumns.indexOf(e.id) >= 0)
e.classList.remove('hide');
else
e.classList.add('hide');
}
});
}
function renameFile(str, tag) {
var n = str.lastIndexOf('/'), fn = n > -1 ? str.slice(n + 1) : str, dir = n > -1 ? str.slice(0, n + 1) : '';
var n = fn.lastIndexOf('.'), bn = n > -1 ? fn.slice(0, n) : fn;
var n = fn.lastIndexOf('.'), en = n > -1 ? fn.slice(n + 1) : '';
return dir + bn + '.' + tag + (en ? '.' + en : '');
}
function resolveCustomizedHostName() {
return fs.stat(hostNameFile).then(function() {
return fs.read_direct(hostNameFile).then(function(raw) {
var arr = raw.trim().split(/\r?\n/), hosts = {}, row;
for (var i = 0; i < arr.length; i++) {
row = arr[i].split(',');
if (row.length == 2 && row[0])
hosts[row[0].toLowerCase()] = row[1];
}
return hosts;
})
})
.catch(function() { return []; });
}
function resolveHostNameByMACAddr() {
return Promise.all([
resolveCustomizedHostName(),
callLuciDHCPLeases()
]).then(function(res) {
var hosts = res[0];
for (var key in res[1]) {
var leases = Array.isArray(res[1][key]) ? res[1][key] : [];
for (var i = 0; i < leases.length; i++) {
if(leases[i].macaddr) {
var macaddr = leases[i].macaddr.toLowerCase();
if (!(macaddr in hosts) && Boolean(leases[i].hostname))
hosts[macaddr] = leases[i].hostname;
}
}
}
return hosts;
});
}
function setSortedColumn(sorting) {
var sorted = document.querySelector('.th.sorted') || $('thTotal');
if (sorting.isSameNode(sorted)) {
sorting.classList.toggle('ascent');
}
else {
sorting.classList.add('sorted');
sorted.classList.remove('sorted', 'ascent');
}
}
function setUpdateMessage(e, sec) {
e.innerHTML = sec < 0 ? '' : _('Updating again in %s second(s).').format('<b>' + sec + '</b>');
}
function sortTable(col, IPVer, flag, x, y) {
var byCol = x[col] == y[col] ? 1 : col;
var a = x[byCol], b = y[byCol];
if (!IPVer || byCol != 0) {
if (!(a.match(/\D/g) || b.match(/\D/g)))
a = parseInt(a), b = parseInt(b);
}
else {
IPVer == 'ipv4'
? (a = validation.parseIPv4(a) || [0, 0, 0, 0], b = validation.parseIPv4(b) || [0, 0, 0, 0])
: (a = validation.parseIPv6(a) || [0, 0, 0, 0, 0, 0, 0, 0], b = validation.parseIPv6(b) || [0, 0, 0, 0, 0, 0, 0, 0]);
}
if (Array.isArray(a) && Array.isArray(b)) {
for (var i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return (b[i] - a[i]) * flag;
}
}
return 0;
}
return a == b ? 0 : (a < b ? 1 : -1) * flag;
}
function updateData(settings, table, updated, updating, once) {
var tick = poll.tick,
interval = settings.interval,
sec = (interval - tick % interval) % interval;
if (!sec || once) {
callGetDatabasePath()
.then(function(res) {
var params = settings.protocol == 'ipv4' ? '-4' : '-6';
return fs.exec_direct('/usr/sbin/wrtbwmon', [params, '-f', res.file_4])
})
.then(function() {
return Promise.all([
callGetDatabaseRaw(settings.protocol),
resolveHostNameByMACAddr()
]);
})
.then(function(res) {
//console.time('start');
cachedData = parseDatabase(res[0].data || '', res[1], settings.showZero, settings.hideMACs);
displayTable(table, settings);
updated.textContent = _('Last updated at %s.').format(formatDate(new Date(document.lastModified)));
//console.timeEnd('start');
});
}
setUpdateMessage(updating, sec);
if (!sec)
setTimeout(setUpdateMessage.bind(this, updating, interval), 100);
}
function updateTable(tb, values, placeholder, settings) {
var fragment = document.createDocumentFragment(), nodeLen = tb.childElementCount - 2;
var formData = values[0], tbTitle = tb.firstElementChild, newNode, childTD;
// Update the table data.
for (var i = 0; i < formData.length; i++) {
if (i < nodeLen) {
newNode = tbTitle.nextElementSibling;
}
else {
if (nodeLen > 0) {
newNode = fragment.firstChild.cloneNode(true);
}
else {
newNode = document.createElement('tr');
childTD = document.createElement('td');
for (var j = 0; j < tbTitle.children.length; j++) {
childTD.className = 'td' + (settings.showColumns.indexOf(tbTitle.children[j].id) >= 0 ? '' : ' hide');
childTD.setAttribute('data-title', tbTitle.children[j].textContent);
newNode.appendChild(childTD.cloneNode(true));
}
}
newNode.className = 'tr cbi-rowstyle-%d'.format(i % 2 ? 2 : 1);
}
childTD = newNode.firstElementChild;
childTD.title = formData[i].slice(-1);
for (var j = 0; j < tbTitle.childElementCount; j++, childTD = childTD.nextElementSibling) {
switch (j) {
case 2:
case 3:
childTD.textContent = formatSpeed(formData[i][j], settings.useBits, settings.useMultiple);
break;
case 4:
case 5:
case 6:
childTD.textContent = formatSize(formData[i][j], settings.useBits, settings.useMultiple);
break;
case 7:
case 8:
childTD.textContent = formatDate(new Date(formData[i][j] * 1000));
break;
default:
childTD.textContent = formData[i][j];
}
}
fragment.appendChild(newNode);
}
// Remove the table data which has been deleted from the database.
while (tb.childElementCount > 1) {
tb.removeChild(tbTitle.nextElementSibling);
}
//Append the totals or placeholder row.
if (formData.length == 0) {
newNode = document.createElement('tr');
newNode.className = 'tr placeholder';
childTD = document.createElement('td');
childTD.className = 'td';
childTD.innerHTML = placeholder;
newNode.appendChild(childTD);
}
else{
newNode = fragment.firstChild.cloneNode(true);
newNode.className = 'tr table-totals';
newNode.children[0].textContent = _('TOTAL') + (settings.showColumns.indexOf('thMAC') >= 0 ? '' : ': ' + formData.length);
newNode.children[1].textContent = formData.length + ' ' + _('Clients');
for (var j = 0; j < tbTitle.childElementCount; j++) {
switch(j) {
case 0:
case 1:
newNode.children[j].removeAttribute('title');
newNode.children[j].style.fontWeight = 'bold';
break;
case 2:
case 3:
newNode.children[j].textContent = formatSpeed(values[1][j - 2], settings.useBits, settings.useMultiple);
break;
case 4:
case 5:
case 6:
newNode.children[j].textContent = formatSize(values[1][j - 2], settings.useBits, settings.useMultiple);
break;
default:
newNode.children[j].textContent = '';
newNode.children[j].removeAttribute('data-title');
}
}
}
fragment.appendChild(newNode);
tb.appendChild(fragment);
}
function initOption(options, selected) {
var res = [], attr = {};
for (var idx in options) {
attr.value = idx;
attr.selected = idx == selected ? '' : null;
res.push(E('option', attr, options[idx]));
}
return res;
}
return view.extend({
load: function() {
return Promise.all([
parseDefaultSettings(luciConfig),
loadCss(L.resource('view/wrtbwmon/wrtbwmon.css'))
]);
},
render: function(data) {
var settings = data[0],
labelUpdated = E('label'),
labelUpdating = E('label'),
table = E('table', { 'class': 'table', 'id': 'traffic' }, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th', 'id': 'thClient' }, _('Clients')),
E('th', { 'class': 'th hide', 'id': 'thMAC' }, _('MAC')),
E('th', { 'class': 'th', 'id': 'thDownload' }, _('Download')),
E('th', { 'class': 'th', 'id': 'thUpload' }, _('Upload')),
E('th', { 'class': 'th', 'id': 'thTotalDown' }, _('Total Down')),
E('th', { 'class': 'th', 'id': 'thTotalUp' }, _('Total Up')),
E('th', { 'class': 'th sorted', 'id': 'thTotal' }, _('Total')),
E('th', { 'class': 'th hide', 'id': 'thFirstSeen' }, _('First Seen')),
E('th', { 'class': 'th hide', 'id': 'thLastSeen' }, _('Last Seen'))
]),
E('tr', {'class': 'tr placeholder'}, [
E('td', { 'class': 'td' }, E('em', {}, _('Collecting data...')))
])
]);
poll.add(updateData.bind(this, settings, table, labelUpdated, labelUpdating, false), 1);
setupThisDOM(settings, table);
return E('div', { 'class': 'cbi-map' }, [
E('h2', {}, _('Usage - Details')),
E('div', { 'class': 'cbi-section' }, [
E('div', { 'id': 'control_panel' }, [
E('div', {}, [
E('label', {}, _('Protocol:')),
E('select', {
'id': 'selectProtocol',
'change': clickToSelectProtocol.bind(this, settings, table, labelUpdated, labelUpdating)
}, initOption({
'ipv4': 'ipv4',
'ipv6': 'ipv6'
}, settings.protocol))
]),
E('div', {}, [
E('button', {
'class': 'btn cbi-button cbi-button-reset important',
'id': 'resetDatabase',
'click': clickToResetDatabase.bind(this, settings, table, labelUpdated, labelUpdating)
}, _('Reset Database')),
' ',
E('button', {
'class': 'btn cbi-button cbi-button-neutral',
'click': handleConfig
}, _('Configure Options'))
])
]),
E('div', {}, [
E('div', {}, [ labelUpdated, labelUpdating ]),
E('div', {}, [
E('label', { 'for': 'selectInterval' }, _('Auto update every:')),
E('select', {
'id': 'selectInterval',
'change': clickToSelectInterval.bind(this, settings, labelUpdating)
}, initOption({
'-1': _('Disabled'),
'2': _('2 seconds'),
'5': _('5 seconds'),
'10': _('10 seconds'),
'30': _('30 seconds')
}, settings.interval))
])
]),
E('div', { 'id': 'progressbar_panel' }, [
E('div', {}, [
E('label', {}, _('Downstream:')),
E('div', {
'id': 'downstream',
'class': 'cbi-progressbar',
'title': '-'
}, E('div')
)
]),
E('div', {}, [
E('label', {}, _('Upstream:')),
E('div', {
'id': 'upstream',
'class': 'cbi-progressbar',
'title': '-'
}, E('div')
)
]),
]),
table
])
]);
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});
================================================
FILE: luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/wrtbwmon.css
================================================
.th.sorted::after {
content: '\25bc';
}
.th.sorted.ascent::after {
content: '\25b2';
}
.hide {
display: none !important;
}
#control_panel {
display: flex;
margin-bottom: 1rem;
padding: .5rem;
line-height: 2rem;
}
#control_panel > :nth-child(1) {
display: inline-block;
flex: 1 1 50%;
}
#control_panel > :nth-child(2) {
flex: 1 1 50%;
text-align: right;
}
#control_panel > * > * {
vertical-align: middle;
}
#control_panel > div > label {
margin-right: .5rem;
}
div > label + select {
min-width: unset;
width: auto;
}
#control_panel + div {
display: flex;
margin-bottom: .5rem;
}
#control_panel + div > div:nth-of-type(1) {
flex: 1 1 65%;
}
#control_panel + div > div:nth-of-type(2) {
flex: 1 1 35%;
text-align: right;
}
#thClient {
width: 17%;
}
#thMAC {
width: 10%;
}
#thDownload, #thUpload {
width: 8%;
}
#thTotalDown, #thTotalUp, #thTotal {
width: 9%;
}
#thFirstSeen, #thLastSeen {
width: 15%;
}
.tr.table-totals {
font-weight: bold;
}
#traffic .tr:not(.table-totals):not(.placeholder) > .td:not(.th):first-child::before {
content: attr(title)'\a';
white-space: pre-line;
}
================================================
FILE: luci-app-wrtbwmon/po/templates/wrtbwmon.pot
================================================
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:611
msgid "10 seconds"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:609
msgid "2 seconds"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:612
msgid "30 seconds"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:610
msgid "5 seconds"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:603
msgid "Auto update every:"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:201
msgid "Cancel"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:14
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:503
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:556
msgid "Clients"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:132
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:567
msgid "Collecting data..."
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:75
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:167
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:210
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:25
msgid "Configuration"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:597
msgid "Configure Options"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:195
msgid "Configure the default values for luci-app-wrtbwmon."
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24
msgid "Database path"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:178
msgid "Default Columns"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176
msgid "Default Protocol"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
msgid "Default Refresh Interval"
msgstr ""
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:16
msgid "Details"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:608
msgid "Disabled"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:16
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:558
msgid "Download"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:184
msgid "Downstream Bandwidth"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:618
msgid "Downstream:"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:18
msgid "Each line must have the following format:"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:21
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:563
msgid "First Seen"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:18
msgid "General settings"
msgstr ""
#: root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json:3
msgid "Grant access to LuCI app wrtbwmon"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:185
msgid "Hide MAC Addresses"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181
msgid "IEC - 1024"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:21
msgid "Keep running in the background"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:22
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:564
msgid "Last Seen"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:425
msgid "Last updated at %s."
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:168
msgid "Loading configuration data..."
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:15
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:557
msgid "MAC"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181
msgid "Multiple of Unit"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:578
msgid "Protocol:"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:592
msgid "Reset Database"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181
msgid "SI - 1000"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:207
msgid "Save"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:76
msgid "Saving configuration data..."
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179
msgid "Show Zeros"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:502
msgid "TOTAL"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24
msgid ""
"This box is used to select the Database path, which is /tmp/usage.db by "
"default."
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:60
msgid "This will delete the database file. Are you sure?"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:20
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:562
msgid "Total"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:18
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:560
msgid "Total Down"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:19
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:561
msgid "Total Up"
msgstr ""
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:3
msgid "Traffic Status"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:180
msgid "Transfer Speed in Bits"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:9
msgid "Unable to load the customized hostname file:"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:81
msgid "Unable to save %s: %s"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:376
msgid "Updating again in %s second(s)."
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:17
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:559
msgid "Upload"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:183
msgid "Upstream Bandwidth"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:627
msgid "Upstream:"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:16
msgid "Usage - Configuration"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:16
msgid "Usage - Custom User File"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:574
msgid "Usage - Details"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:182
msgid "Use DSL Bandwidth"
msgstr ""
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:34
msgid "User file"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176
msgid "ipv4"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176
msgid "ipv6"
msgstr ""
================================================
FILE: luci-app-wrtbwmon/po/zh_Hans/wrtbwmon.po
================================================
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:611
msgid "10 seconds"
msgstr "10秒"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:609
msgid "2 seconds"
msgstr "2秒"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:612
msgid "30 seconds"
msgstr "30秒"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:610
msgid "5 seconds"
msgstr "5秒"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:603
msgid "Auto update every:"
msgstr "自动刷新:"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:201
msgid "Cancel"
msgstr "取消"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:14
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:503
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:556
msgid "Clients"
msgstr "客户端"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:132
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:567
msgid "Collecting data..."
msgstr "收集数据中..."
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:75
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:167
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:210
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:25
msgid "Configuration"
msgstr "配置"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:597
msgid "Configure Options"
msgstr "配置选项"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:195
msgid "Configure the default values for luci-app-wrtbwmon."
msgstr "配置luci-app-wrtbwmon的默认值。"
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24
msgid "Database path"
msgstr "数据路径"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:178
msgid "Default Columns"
msgstr "默认显示的列"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176
msgid "Default Protocol"
msgstr "默认协议"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
msgid "Default Refresh Interval"
msgstr "默认刷新间隔"
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:16
msgid "Details"
msgstr "流量信息"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:608
msgid "Disabled"
msgstr "禁用"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:16
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:558
msgid "Download"
msgstr "下载"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:184
msgid "Downstream Bandwidth"
msgstr "下行带宽"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:618
msgid "Downstream:"
msgstr "下行:"
#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:18
msgid "Each line must have the following format:"
msgstr "每行需要满足以下格式:"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:21
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:563
msgid "First Seen"
msgstr "初次记录"
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:18
msgid "General settings"
msgstr "通用设置"
#: root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json:3
msgid "Grant access to LuCI app wrtbwmon"
msgstr "授予访问LuCI应用程序wrtbwmon的权限"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:185
msgid "Hide MAC Addresses"
msgstr "隐藏MAC地址"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181
msgid "IEC - 1024"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:21
msgid "Keep running in the background"
msgstr "保持后台运行"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:22
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:564
msgid "Last Seen"
msgstr "最后记录"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:425
msgid "Last updated at %s."
msgstr "最后更新于%s。"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:168
msgid "Loading configuration data..."
msgstr "载入配置数据..."
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:15
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:557
msgid "MAC"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181
msgid "Multiple of Unit"
msgstr "单位之间倍数"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:578
msgid "Protocol:"
msgstr "协议:"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:592
msgid "Reset Database"
msgstr "重置"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181
msgid "SI - 1000"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:207
msgid "Save"
msgstr "保存"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:76
msgid "Saving configuration data..."
msgstr "保存配置数据..."
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179
msgid "Show Zeros"
msgstr "显示0流量"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:502
msgid "TOTAL"
msgstr "总共"
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24
msgid ""
"This box is used to select the Database path, which is /tmp/usage.db by "
"default."
msgstr "该选项用于选择数据存储路径,默认路径为/tmp/usage.db。"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:60
msgid "This will delete the database file. Are you sure?"
msgstr "该操作将删除数据统计文件,确定执行该操作?"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:20
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:562
msgid "Total"
msgstr "总计"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:18
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:560
msgid "Total Down"
msgstr "总下载"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:19
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:561
msgid "Total Up"
msgstr "总上传"
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:3
msgid "Traffic Status"
msgstr "流量监控"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:180
msgid "Transfer Speed in Bits"
msgstr "以bits显示传输速度"
#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:9
msgid "Unable to load the customized hostname file:"
msgstr "不能载入自定义用户名文件:"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:81
msgid "Unable to save %s: %s"
msgstr "不能保存%s: %s"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:376
msgid "Updating again in %s second(s)."
msgstr "下次更新将于%s秒之后。"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:17
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:559
msgid "Upload"
msgstr "上传"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:183
msgid "Upstream Bandwidth"
msgstr "上传带宽"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:627
msgid "Upstream:"
msgstr "上行:"
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:16
msgid "Usage - Configuration"
msgstr "文件设置"
#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:16
msgid "Usage - Custom User File"
msgstr "用户文件配置"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:574
msgid "Usage - Details"
msgstr "流量详情"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:182
msgid "Use DSL Bandwidth"
msgstr "使用DSL带宽"
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:34
msgid "User file"
msgstr "用户文件"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176
msgid "ipv4"
msgstr ""
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176
msgid "ipv6"
msgstr ""
#~ msgid "Show More Columns:"
#~ msgstr "显示更多列:"
#~ msgid "This will revert the changes. Are you sure?"
#~ msgstr "更改将会重置,确定吗?"
================================================
FILE: luci-app-wrtbwmon/po/zh_Hant/wrtbwmon.po
================================================
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2023-04-26 18:31+0800\n"
"Last-Translator: Victor Tseng (palatis@gmail.com)\n"
"Language-Team: \n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.2.2\n"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:611
msgid "10 seconds"
msgstr "10 秒"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:609
msgid "2 seconds"
msgstr "2 秒"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:612
msgid "30 seconds"
msgstr "30 秒"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:610
msgid "5 seconds"
msgstr "5 秒"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:603
msgid "Auto update every:"
msgstr "重新整理間隔:"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:201
msgid "Cancel"
msgstr "取消"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:14
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:503
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:556
msgid "Clients"
msgstr "用戶端"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:132
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:567
msgid "Collecting data..."
msgstr "蒐集資料中…"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:75
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:167
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:210
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:25
msgid "Configuration"
msgstr "設定"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:597
msgid "Configure Options"
msgstr "設定選項"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:195
msgid "Configure the default values for luci-app-wrtbwmon."
msgstr "設定 luci-app-wrtbwmon 的預設值。"
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24
msgid "Database path"
msgstr "資料庫路徑"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:178
msgid "Default Columns"
msgstr "預設顯示的欄位"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176
msgid "Default Protocol"
msgstr "預設協定"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
msgid "Default Refresh Interval"
msgstr "預設重新整理間隔"
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:16
msgid "Details"
msgstr "詳細"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:608
msgid "Disabled"
msgstr "已停用"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:16
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:558
msgid "Download"
msgstr "下載"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:184
msgid "Downstream Bandwidth"
msgstr "下行頻寬"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:618
msgid "Downstream:"
msgstr "下行:"
#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:18
msgid "Each line must have the following format:"
msgstr "每一行皆必須使用以下格式:"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:21
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:563
msgid "First Seen"
msgstr "初次發現"
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:18
msgid "General settings"
msgstr "通用設定"
#: root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json:3
msgid "Grant access to LuCI app wrtbwmon"
msgstr "授權給 LuCI app wrtbwmon"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:185
msgid "Hide MAC Addresses"
msgstr "隱藏 MAC 地址"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181
msgid "IEC - 1024"
msgstr "IEC - 1024"
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:21
msgid "Keep running in the background"
msgstr "保持背景執行"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:22
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:564
msgid "Last Seen"
msgstr "最後發現"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:425
msgid "Last updated at %s."
msgstr "最後於 %s 發現。"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:168
msgid "Loading configuration data..."
msgstr "載入設定資料…"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:15
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:557
msgid "MAC"
msgstr "MAC"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181
msgid "Multiple of Unit"
msgstr "速率計量單位格式"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:578
msgid "Protocol:"
msgstr "協定:"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:592
msgid "Reset Database"
msgstr "重置資料庫"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:181
msgid "SI - 1000"
msgstr "SI - 1000"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:207
msgid "Save"
msgstr "儲存"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:76
msgid "Saving configuration data..."
msgstr "正在保存設定資料…"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:179
msgid "Show Zeros"
msgstr "顯示無流量的紀錄"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:502
msgid "TOTAL"
msgstr "總計"
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:24
msgid ""
"This box is used to select the Database path, which is /tmp/usage.db by "
"default."
msgstr "選擇資料庫路徑,預設為 /tmp/usage.db。"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:60
msgid "This will delete the database file. Are you sure?"
msgstr "這會刪除目前的資料庫檔案,您確定嗎?"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:20
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:562
msgid "Total"
msgstr "總頻寬"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:18
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:560
msgid "Total Down"
msgstr "總下載"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:19
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:561
msgid "Total Up"
msgstr "總上傳"
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:3
msgid "Traffic Status"
msgstr "頻寬使用狀況"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:180
msgid "Transfer Speed in Bits"
msgstr "以位元表示傳輸速率"
#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:9
msgid "Unable to load the customized hostname file:"
msgstr "無法載入自訂的主機名稱檔案:"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:81
msgid "Unable to save %s: %s"
msgstr "無法保存 %s:%s"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:376
msgid "Updating again in %s second(s)."
msgstr "於 %s 秒後更新。"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:17
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:559
msgid "Upload"
msgstr "上傳"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:183
msgid "Upstream Bandwidth"
msgstr "上行頻寬"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:627
msgid "Upstream:"
msgstr "上行:"
#: htdocs/luci-static/resources/view/wrtbwmon/config.js:16
msgid "Usage - Configuration"
msgstr "頻寬使用狀況 - 設定"
#: htdocs/luci-static/resources/view/wrtbwmon/custom.js:16
msgid "Usage - Custom User File"
msgstr "頻寬使用狀況 - 自訂主機名稱列表"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:574
msgid "Usage - Details"
msgstr "頻寬使用狀況 - 詳細資料"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:182
msgid "Use DSL Bandwidth"
msgstr "使用 DSL 頻寬"
#: root/usr/share/luci/menu.d/luci-app-wrtbwmon.json:34
msgid "User file"
msgstr "使用者檔案"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176
msgid "ipv4"
msgstr "ipv4"
#: htdocs/luci-static/resources/view/wrtbwmon/details.js:176
msgid "ipv6"
msgstr "ipv6"
#~ msgid "Default More Columns"
#~ msgstr "預設顯示更多欄位"
#~ msgid "Show More Columns:"
#~ msgstr "顯示更多欄位:"
================================================
FILE: luci-app-wrtbwmon/root/etc/luci-wrtbwmon.conf
================================================
{
"protocol": "ipv4",
"interval": "5",
"showMore": false,
"showZero": true,
"useBits": false,
"useMultiple": "1000",
"useDSL": false,
"upstream": "100",
"downstream": "100",
"hideMACs": []
}
================================================
FILE: luci-app-wrtbwmon/root/usr/libexec/rpcd/luci.wrtbwmon
================================================
#!/bin/sh
. "$IPKG_INSTROOT/usr/share/libubox/jshn.sh"
renamefile() {
local base=$(basename -- "$1")
local ext=$([ -z "${base/*.*/}" ] && echo ".${base##*.}" || echo '')
local base="${base%.*}"
echo "$(dirname $1)/${base}$2$ext" && return
}
_get_db_path() {
local db db_4 db_6 db_46
db="$(uci -q get wrtbwmon.general.path)"
db_4=$(renamefile "${db:-/tmp/usage.db}" "")
db_6=$(renamefile "${db:-/tmp/usage.db}" ".6")
db_46=$(renamefile "${db:-/tmp/usage.db}" ".46")
json_init
json_add_string file_4 "$db_4"
json_add_string file_6 "$db_6"
json_add_string file_46 "$db_46"
json_dump
json_cleanup
}
_get_db_raw() {
json_init
json_add_string file "$1"
json_add_string data "$(cat "$1")"
json_dump
json_cleanup
}
_remove_db() {
local result
rm "$1" && result=1 || result=0
json_init
json_add_boolean result "$result"
json_dump
json_cleanup
}
_change_db_path() {
json_init
json_load "$(_get_db_path)"
json_get_var db_4 'file_4'
json_get_var db_6 'file_6'
json_get_var db_46 'file_46'
json_cleanup
if [ "$1" = "before" ]; then
mv "$db_4" /tmp/usage.db.tmp
mv "$db_6" /tmp/usage.6.db.tmp
mv "$db_46" /tmp/usage.46.db.tmp
elif [ "$1" = "after" ]; then
mv -f /tmp/usage.db.tmp "$db_4"
mv -f /tmp/usage.6.db.tmp "$db_6"
mv -f /tmp/usage.46.db.tmp "$db_46"
fi
json_init
json_add_boolean result $([ "$?" = 0 ] && echo 1 || echo 0 )
json_dump
json_cleanup
}
case "$1" in
list)
json_init
json_add_object remove_db
json_add_string protocol "protocol"
json_close_object
json_add_object get_db_raw
json_add_string protocol "protocol"
json_close_object
json_add_object get_db_path
json_close_object
json_add_object change_db_path
json_add_string state "state"
json_close_object
json_dump
json_cleanup
;;
call)
case "$2" in
remove_db)
read -r input
json_init
json_load "$input"
json_get_var protocol 'protocol'
json_cleanup
json_load "$(_get_db_path)"
json_get_var db_s $([ "$protocol" = "ipv4" ] && echo file_4 || echo file_6)
json_cleanup
_remove_db "$db_s"
;;
get_db_raw)
read -r input
json_init
json_load "$input"
json_get_var protocol 'protocol'
json_cleanup
json_load "$(_get_db_path)"
json_get_var db_s $([ "$protocol" = "ipv4" ] && echo file_4 || echo file_6)
json_cleanup
_get_db_raw "$db_s"
;;
get_db_path)
read -r input
json_init
json_load "$input"
json_get_var protocol 'protocol'
json_cleanup
_get_db_path
;;
change_db_path)
read -r input
json_init
json_load "$input"
json_get_var state 'state'
json_cleanup
_change_db_path "$state"
;;
esac
;;
esac
================================================
FILE: luci-app-wrtbwmon/root/usr/share/luci/menu.d/luci-app-wrtbwmon.json
================================================
{
"admin/network/usage": {
"title": "Traffic Status",
"order": 60,
"action": {
"type": "alias",
"path": "admin/network/usage/details"
},
"depends": {
"acl": [ "luci-app-wrtbwmon" ],
"uci": { "wrtbwmon": true }
}
},
"admin/network/usage/details": {
"title": "Details",
"order": 10,
"action": {
"type": "view",
"path": "wrtbwmon/details"
}
},
"admin/network/usage/config": {
"title": "Configuration",
"order": 20,
"action": {
"type": "view",
"path": "wrtbwmon/config"
}
},
"admin/network/usage/custom": {
"title": "User file",
"order": 30,
"action": {
"type": "view",
"path": "wrtbwmon/custom"
}
}
}
================================================
FILE: luci-app-wrtbwmon/root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json
================================================
{
"luci-app-wrtbwmon": {
"description": "Grant access to LuCI app wrtbwmon",
"read": {
"ubus": {
"luci.wrtbwmon": [
"get_db_raw",
"get_db_path"
],
"luci-rpc": [
"getDHCPLeases",
"getDSLStatus"
]
},
"file": {
"/etc/wrtbwmon.user": [ "read" ],
"/usr/sbin/wrtbwmon": [ "exec" ],
"/etc/luci-wrtbwmon.conf": [ "read" ]
},
"uci": [ "wrtbwmon" ]
},
"write": {
"ubus": {
"luci.wrtbwmon": [
"remove_db",
"change_db_path"
]
},
"file": {
"/etc/luci-wrtbwmon.conf": [ "write" ],
"/etc/wrtbwmon.user": [ "write" ]
},
"uci": [ "wrtbwmon" ]
}
}
}
gitextract_89cv8w6g/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── CI.yml
│ └── stale.yml
├── README.md
└── luci-app-wrtbwmon/
├── Makefile
├── htdocs/
│ └── luci-static/
│ └── resources/
│ └── view/
│ └── wrtbwmon/
│ ├── config.js
│ ├── custom.js
│ ├── details.js
│ └── wrtbwmon.css
├── po/
│ ├── templates/
│ │ └── wrtbwmon.pot
│ ├── zh_Hans/
│ │ └── wrtbwmon.po
│ └── zh_Hant/
│ └── wrtbwmon.po
└── root/
├── etc/
│ └── luci-wrtbwmon.conf
└── usr/
├── libexec/
│ └── rpcd/
│ └── luci.wrtbwmon
└── share/
├── luci/
│ └── menu.d/
│ └── luci-app-wrtbwmon.json
└── rpcd/
└── acl.d/
└── luci-app-wrtbwmon.json
SYMBOL INDEX (26 symbols across 1 files)
FILE: luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/details.js
function $ (line 55) | function $(tid) {
function clickToResetDatabase (line 59) | function clickToResetDatabase(settings, table, updated, updating, ev) {
function clickToSaveConfig (line 68) | function clickToSaveConfig(keylist, cstrs) {
function clickToSelectInterval (line 87) | function clickToSelectInterval(settings, updating, ev) {
function clickToSelectProtocol (line 98) | function clickToSelectProtocol(settings, table, updated, updating, ev) {
function createOption (line 103) | function createOption(args, val) {
function displayTable (line 118) | function displayTable(tb, settings) {
function formatSize (line 138) | function formatSize(size, useBits, useMultiple) {
function formatSpeed (line 143) | function formatSpeed(speed, useBits, useMultiple) {
function formatDate (line 147) | function formatDate(d) {
function getDSLBandwidth (line 153) | function getDSLBandwidth() {
function handleConfig (line 162) | function handleConfig(ev) {
function loadCss (line 210) | function loadCss(path) {
function parseDatabase (line 221) | function parseDatabase(raw, hosts, showZero, hideMACs) {
function parseDefaultSettings (line 247) | function parseDefaultSettings(file) {
function progressbar (line 275) | function progressbar(query, v, m, useBits, useMultiple) {
function setupThisDOM (line 291) | function setupThisDOM(settings, table) {
function renameFile (line 316) | function renameFile(str, tag) {
function resolveCustomizedHostName (line 323) | function resolveCustomizedHostName() {
function resolveHostNameByMACAddr (line 338) | function resolveHostNameByMACAddr() {
function setSortedColumn (line 358) | function setSortedColumn(sorting) {
function setUpdateMessage (line 370) | function setUpdateMessage(e, sec) {
function sortTable (line 374) | function sortTable(col, IPVer, flag, x, y) {
function updateData (line 400) | function updateData(settings, table, updated, updating, once) {
function updateTable (line 430) | function updateTable(tb, values, placeholder, settings) {
function initOption (line 527) | function initOption(options, selected) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (64K chars).
[
{
"path": ".github/dependabot.yml",
"chars": 638,
"preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
},
{
"path": ".github/workflows/CI.yml",
"chars": 3437,
"preview": "name: CI\n\non:\n push:\n branches:\n - master\n tags:\n - 'release-*'\n paths:\n - 'luci-app-wrtbwmon/*"
},
{
"path": ".github/workflows/stale.yml",
"chars": 800,
"preview": "name: \"Close stale issues\"\non:\n schedule:\n - cron: \"0 0 * * *\"\n\njobs:\n stale:\n runs-on: ubuntu-latest\n steps:\n "
},
{
"path": "README.md",
"chars": 2622,
"preview": "# luci-app-wrtbwmon\n\n[](https://github.com/"
},
{
"path": "luci-app-wrtbwmon/Makefile",
"chars": 440,
"preview": "#\n# Copyright (C) 2020 xxx <xxx@xxx.com>\n#\n# This is free software, licensed under the Apache License, Version 2.0 .\n#\n\n"
},
{
"path": "luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/config.js",
"chars": 986,
"preview": "'use strict';\n'require form';\n'require rpc';\n'require view';\n\nvar callChangeDatabasePath = rpc.declare({\n\tobject: 'luci."
},
{
"path": "luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/custom.js",
"chars": 997,
"preview": "'use strict';\n'require fs';\n'require ui';\n'require view';\n\nreturn view.extend({\n\tload: function() {\n\t\treturn fs.trimmed("
},
{
"path": "luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/details.js",
"chars": 19163,
"preview": "'use strict';\n'require dom';\n'require fs';\n'require poll';\n'require rpc';\n'require ui';\n'require validation';\n'require v"
},
{
"path": "luci-app-wrtbwmon/htdocs/luci-static/resources/view/wrtbwmon/wrtbwmon.css",
"chars": 1104,
"preview": ".th.sorted::after {\n\tcontent: '\\25bc';\n}\n.th.sorted.ascent::after {\n\tcontent: '\\25b2';\n}\n.hide {\n\tdisplay: none !importa"
},
{
"path": "luci-app-wrtbwmon/po/templates/wrtbwmon.pot",
"chars": 6958,
"preview": "msgid \"\"\nmsgstr \"Content-Type: text/plain; charset=UTF-8\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:177\n"
},
{
"path": "luci-app-wrtbwmon/po/zh_Hans/wrtbwmon.po",
"chars": 7434,
"preview": "msgid \"\"\nmsgstr \"Content-Type: text/plain; charset=UTF-8\\n\"\n\n#: htdocs/luci-static/resources/view/wrtbwmon/details.js:17"
},
{
"path": "luci-app-wrtbwmon/po/zh_Hant/wrtbwmon.po",
"chars": 7796,
"preview": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: \\n\"\n\"POT-Creation-Date: \\n\"\n\"PO-Revision-Date: 2023-04-26 18:31+0800\\n\"\n\"Last-Tr"
},
{
"path": "luci-app-wrtbwmon/root/etc/luci-wrtbwmon.conf",
"chars": 201,
"preview": "{\n\t\"protocol\": \"ipv4\",\n\t\"interval\": \"5\",\n\t\"showMore\": false,\n\t\"showZero\": true,\n\t\"useBits\": false,\n\t\"useMultiple\": \"1000"
},
{
"path": "luci-app-wrtbwmon/root/usr/libexec/rpcd/luci.wrtbwmon",
"chars": 2680,
"preview": "#!/bin/sh\n\n. \"$IPKG_INSTROOT/usr/share/libubox/jshn.sh\"\n\nrenamefile() {\n\tlocal base=$(basename -- \"$1\")\n\tlocal ext=$([ -"
},
{
"path": "luci-app-wrtbwmon/root/usr/share/luci/menu.d/luci-app-wrtbwmon.json",
"chars": 675,
"preview": "{\n\t\"admin/network/usage\": {\n\t\t\"title\": \"Traffic Status\",\n\t\t\"order\": 60,\n\t\t\"action\": {\n\t\t\t\"type\": \"alias\",\n\t\t\t\"path\": \"ad"
},
{
"path": "luci-app-wrtbwmon/root/usr/share/rpcd/acl.d/luci-app-wrtbwmon.json",
"chars": 648,
"preview": "{\n\t\"luci-app-wrtbwmon\": {\n\t\t\"description\": \"Grant access to LuCI app wrtbwmon\",\n\t\t\"read\": {\n\t\t\t\"ubus\": {\n\t\t\t\t\"luci.wrtbw"
}
]
About this extraction
This page contains the full source code of the brvphoenix/luci-app-wrtbwmon GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (55.3 KB), approximately 18.1k tokens, and a symbol index with 26 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.