v1.2. UI detector refactoring
19
README.md
@@ -19,15 +19,15 @@ Internet-detector is an application for checking the availability of the Interne
|
||||
**OpenWrt >= 21.02:**
|
||||
|
||||
opkg update
|
||||
wget --no-check-certificate -O /tmp/internet-detector_1.1-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector_1.1-0_all.ipk
|
||||
opkg install /tmp/internet-detector_1.1-0_all.ipk
|
||||
rm /tmp/internet-detector_1.1-0_all.ipk
|
||||
wget --no-check-certificate -O /tmp/internet-detector_1.2-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector_1.2-0_all.ipk
|
||||
opkg install /tmp/internet-detector_1.2-0_all.ipk
|
||||
rm /tmp/internet-detector_1.2-0_all.ipk
|
||||
/etc/init.d/internet-detector start
|
||||
/etc/init.d/internet-detector enable
|
||||
|
||||
wget --no-check-certificate -O /tmp/luci-app-internet-detector_1.1-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-internet-detector_1.1-0_all.ipk
|
||||
opkg install /tmp/luci-app-internet-detector_1.1-0_all.ipk
|
||||
rm /tmp/luci-app-internet-detector_1.1-0_all.ipk
|
||||
wget --no-check-certificate -O /tmp/luci-app-internet-detector_1.2-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-internet-detector_1.2-0_all.ipk
|
||||
opkg install /tmp/luci-app-internet-detector_1.2-0_all.ipk
|
||||
rm /tmp/luci-app-internet-detector_1.2-0_all.ipk
|
||||
/etc/init.d/rpcd restart
|
||||
|
||||
Email notification:
|
||||
@@ -36,9 +36,9 @@ Email notification:
|
||||
|
||||
i18n-ru:
|
||||
|
||||
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_1.1-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-internet-detector-ru_1.1-0_all.ipk
|
||||
opkg install /tmp/luci-i18n-internet-detector-ru_1.1-0_all.ipk
|
||||
rm /tmp/luci-i18n-internet-detector-ru_1.1-0_all.ipk
|
||||
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_1.2-0_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-internet-detector-ru_1.2-0_all.ipk
|
||||
opkg install /tmp/luci-i18n-internet-detector-ru_1.2-0_all.ipk
|
||||
rm /tmp/luci-i18n-internet-detector-ru_1.2-0_all.ipk
|
||||
|
||||
**[OpenWrt 19.07](https://github.com/gSpotx2f/luci-app-internet-detector/tree/19.07)**
|
||||
|
||||
@@ -47,4 +47,3 @@ i18n-ru:
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#
|
||||
# (с) 2023 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
# (с) 2024 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=internet-detector
|
||||
PKG_VERSION:=1.1
|
||||
PKG_VERSION:=1.2
|
||||
PKG_RELEASE:=0
|
||||
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
|
||||
|
||||
|
||||
@@ -2,16 +2,6 @@ config main 'config'
|
||||
option mode '1'
|
||||
option enable_logger '1'
|
||||
|
||||
config ui 'ui'
|
||||
list hosts '8.8.8.8'
|
||||
list hosts '1.1.1.1'
|
||||
option check_type '0'
|
||||
option tcp_port '53'
|
||||
option interval_up '6'
|
||||
option interval_down '1'
|
||||
option connection_attempts '1'
|
||||
option connection_timeout '1'
|
||||
|
||||
config instance 'internet'
|
||||
option enabled '1'
|
||||
list hosts '8.8.8.8'
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
START=97
|
||||
STOP=01
|
||||
|
||||
PROG=/usr/bin/internet-detector
|
||||
PROG="/usr/bin/internet-detector"
|
||||
|
||||
config_app() {
|
||||
run_instance() {
|
||||
config_get enabled "$1" enabled "0"
|
||||
if [ $enabled = "1" ]; then
|
||||
$PROG service "$1"
|
||||
@@ -16,7 +16,7 @@ start() {
|
||||
config_load internet-detector
|
||||
config_get mode "config" mode "0"
|
||||
if [ $mode = "1" ]; then
|
||||
config_foreach config_app "instance"
|
||||
config_foreach run_instance "instance"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
luaposix
|
||||
libuci-lua
|
||||
|
||||
(с) 2023 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
(с) 2024 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
--]]
|
||||
|
||||
-- Importing packages
|
||||
@@ -26,13 +26,16 @@ local uci = require("uci")
|
||||
-- Default settings
|
||||
|
||||
local InternetDetector = {
|
||||
mode = 0,
|
||||
mode = 0, -- 0: disabled, 1: Service, 2: UI detector
|
||||
enableLogger = true,
|
||||
hostname = "OpenWrt",
|
||||
appName = "internet-detector",
|
||||
commonDir = "/tmp/run",
|
||||
pingCmd = "/bin/ping",
|
||||
pingParams = "-c 1",
|
||||
uiRunTime = 30,
|
||||
noModules = false,
|
||||
uiAvailModules = { mod_public_ip = true },
|
||||
debug = false,
|
||||
serviceConfig = {
|
||||
hosts = {
|
||||
@@ -51,11 +54,13 @@ local InternetDetector = {
|
||||
},
|
||||
modules = {},
|
||||
parsedHosts = {},
|
||||
uiCounter = 0,
|
||||
}
|
||||
InternetDetector.configDir = string.format("/etc/%s", InternetDetector.appName)
|
||||
InternetDetector.modulesDir = string.format("/usr/lib/%s", InternetDetector.appName)
|
||||
|
||||
-- Loading settings from UCI
|
||||
|
||||
local uciCursor = uci.cursor()
|
||||
InternetDetector.mode = tonumber(
|
||||
uciCursor:get(InternetDetector.appName, "config", "mode"))
|
||||
@@ -162,6 +167,8 @@ function InternetDetector:loadModules()
|
||||
for item in modulesDir do
|
||||
if item:match("^mod_") then
|
||||
local modName = item:gsub("%.lua$", "")
|
||||
if self.noModules and not self.uiAvailModules[modName] then
|
||||
else
|
||||
local modConfig = {}
|
||||
for k, v in pairs(self.serviceConfig) do
|
||||
if k:match("^" .. modName) then
|
||||
@@ -181,6 +188,7 @@ function InternetDetector:loadModules()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(self.modules, function(a, b) return a.runPrio < b.runPrio end)
|
||||
end
|
||||
end
|
||||
@@ -213,7 +221,6 @@ function InternetDetector:pingHost(host)
|
||||
)
|
||||
local retCode = os.execute(ping)
|
||||
|
||||
-- Debug
|
||||
if self.debug then
|
||||
io.stdout:write(string.format(
|
||||
"--- Ping ---\ntime = %s\n%s\nretCode = %s\n", os.time(), ping, retCode)
|
||||
@@ -329,12 +336,17 @@ function InternetDetector:breakMain(signo)
|
||||
RUNNING = false
|
||||
end
|
||||
|
||||
function InternetDetector:resetUiCounter(signo)
|
||||
self.uiCounter = 0
|
||||
end
|
||||
|
||||
function InternetDetector:main()
|
||||
signal.signal(signal.SIGTERM, function(signo) self:breakMain(signo) end)
|
||||
signal.signal(signal.SIGINT, function(signo) self:breakMain(signo) end)
|
||||
signal.signal(signal.SIGQUIT, function(signo) self:breakMain(signo) end)
|
||||
signal.signal(signal.SIGUSR1, function(signo) self:resetUiCounter(signo) end)
|
||||
|
||||
local lastStatus, currentStatus, timeNow, timeDiff, lastTime
|
||||
local lastStatus, currentStatus, mTimeNow, mTimeDiff, mLastTime, uiTimeNow, uiLastTime
|
||||
local interval = self.serviceConfig.interval_up
|
||||
local counter = 0
|
||||
local onStart = true
|
||||
@@ -367,16 +379,16 @@ function InternetDetector:main()
|
||||
counter = 0
|
||||
end
|
||||
|
||||
timeDiff = 0
|
||||
mTimeDiff = 0
|
||||
for _, e in ipairs(self.modules) do
|
||||
timeNow = time.clock_gettime(time.CLOCK_MONOTONIC).tv_sec
|
||||
if lastTime then
|
||||
timeDiff = timeDiff + timeNow - lastTime
|
||||
mTimeNow = time.clock_gettime(time.CLOCK_MONOTONIC).tv_sec
|
||||
if mLastTime then
|
||||
mTimeDiff = mTimeDiff + mTimeNow - mLastTime
|
||||
else
|
||||
timeDiff = 1
|
||||
mTimeDiff = 1
|
||||
end
|
||||
lastTime = timeNow
|
||||
e:run(currentStatus, lastStatus, timeDiff)
|
||||
mLastTime = mTimeNow
|
||||
e:run(currentStatus, lastStatus, mTimeDiff)
|
||||
end
|
||||
|
||||
local modulesStatus = {}
|
||||
@@ -393,6 +405,19 @@ function InternetDetector:main()
|
||||
lastStatus = currentStatus
|
||||
unistd.sleep(1)
|
||||
counter = counter + 1
|
||||
|
||||
if self.mode == 2 then
|
||||
uiTimeNow = time.clock_gettime(time.CLOCK_MONOTONIC).tv_sec
|
||||
if uiLastTime then
|
||||
self.uiCounter = self.uiCounter + uiTimeNow - uiLastTime
|
||||
else
|
||||
self.uiCounter = self.uiCounter + 1
|
||||
end
|
||||
uiLastTime = uiTimeNow
|
||||
if self.uiCounter >= self.uiRunTime then
|
||||
self:breakMain(signal.SIGTERM)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -490,13 +515,27 @@ function InternetDetector:stop()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function InternetDetector:setSIGUSR()
|
||||
local appName = self.appName:gsub("-", "%%-")
|
||||
local ok, commonDir = pcall(dirent.files, self.commonDir)
|
||||
if ok then
|
||||
for item in commonDir do
|
||||
if item:match("^" .. appName .. ".-%.pid$") then
|
||||
pidValue = self:readValueFromFile(string.format("%s/%s", self.commonDir, item))
|
||||
if pidValue then
|
||||
signal.kill(tonumber(pidValue), signal.SIGUSR1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function InternetDetector:preRun()
|
||||
-- Exit if internet-detector mode != 1(Service)
|
||||
if self.mode ~= 1 then
|
||||
io.stderr:write(string.format('Start failed, mode != 1\n', self.appName))
|
||||
-- Exit if internet-detector mode != (1 or 2)
|
||||
if self.mode ~= 1 and self.mode ~= 2 then
|
||||
io.stderr:write(string.format('Start failed, mode != (1 or 2)\n', self.appName))
|
||||
os.exit(0)
|
||||
end
|
||||
if stat.stat(self.pidFile) then
|
||||
@@ -528,7 +567,6 @@ function InternetDetector:run()
|
||||
)
|
||||
end
|
||||
|
||||
-- Debug
|
||||
if self.debug then
|
||||
local function inspectTable()
|
||||
local tables = {}, f
|
||||
@@ -597,6 +635,10 @@ end
|
||||
function InternetDetector:setServiceConfig(instance)
|
||||
if self:loadUCIConfig("instance", instance) then
|
||||
self:parseHosts()
|
||||
if self.mode == 2 then
|
||||
self.enableLogger = false
|
||||
self.noModules = true
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -605,12 +647,12 @@ end
|
||||
|
||||
local function help()
|
||||
return string.format(
|
||||
"Usage: %s service <UCI instance> | nodaemon <UCI instance> | debug <UCI instance> | stop | status | inet-status | poll [<attempts num>] [<timeout sec>] | --help",
|
||||
"Usage: %s service <UCI instance> | nodaemon <UCI instance> | debug <UCI instance> | stop | status | inet-status | uipoll | --help",
|
||||
arg[0]
|
||||
)
|
||||
end
|
||||
|
||||
local helpArgs = { ["-h"] = true, ["--help"] = true, ["help"] = true }
|
||||
local helpArgs = { ["-h"] = true, ["--help"] = true, help = true }
|
||||
if arg[1] == "service" then
|
||||
if arg[2] then
|
||||
if InternetDetector:setServiceConfig(arg[2]) then
|
||||
@@ -651,20 +693,12 @@ elseif arg[1] == "status" then
|
||||
print(InternetDetector:status())
|
||||
elseif arg[1] == "inet-status" then
|
||||
print(InternetDetector:inetStatus())
|
||||
elseif arg[1] == "poll" then
|
||||
if InternetDetector:loadUCIConfig("ui", "ui") then
|
||||
InternetDetector:parseHosts()
|
||||
|
||||
if arg[2] and arg[2]:match("[0-9]+") then
|
||||
InternetDetector.serviceConfig.connection_attempts = tonumber(arg[2])
|
||||
if arg[3] and arg[3]:match("[0-9]+") then
|
||||
InternetDetector.serviceConfig.connection_timeout = tonumber(arg[3])
|
||||
end
|
||||
end
|
||||
|
||||
print(InternetDetector:poll())
|
||||
else
|
||||
elseif arg[1] == "uipoll" then
|
||||
if InternetDetector:status() == "stoped" then
|
||||
os.exit(126)
|
||||
else
|
||||
InternetDetector:setSIGUSR()
|
||||
print(InternetDetector:inetStatus())
|
||||
end
|
||||
elseif helpArgs[arg[1]] then
|
||||
print(help())
|
||||
|
||||
@@ -12,8 +12,8 @@ local Module = {
|
||||
runInterval = 5,
|
||||
sysLedsDir = "/sys/class/leds",
|
||||
ledName = nil,
|
||||
ledAction1 = 2,
|
||||
ledAction2 = 1,
|
||||
ledAction1 = 2, -- 1: off, 2: on, 3: blink
|
||||
ledAction2 = 1, -- 1: off, 2: on, 3: blink
|
||||
status = nil,
|
||||
_enabled = false,
|
||||
_ledDir = nil,
|
||||
|
||||
@@ -7,6 +7,7 @@ local Module = {
|
||||
name = "mod_public_ip",
|
||||
runPrio = 50,
|
||||
config = {
|
||||
noModules = false,
|
||||
debug = false,
|
||||
serviceConfig = {
|
||||
iface = nil,
|
||||
@@ -65,7 +66,7 @@ local Module = {
|
||||
}
|
||||
|
||||
function Module:runIpScript()
|
||||
if self.enableIpScript and unistd.access(self.ipScript, "r") then
|
||||
if not self.config.noModules and self.enableIpScript and unistd.access(self.ipScript, "r") then
|
||||
stdlib.setenv("PUBLIC_IP", self.status)
|
||||
os.execute(string.format('/bin/sh "%s" &', self.ipScript))
|
||||
end
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#
|
||||
# (с) 2023 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
# (с) 2024 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_VERSION:=1.1-0
|
||||
PKG_VERSION:=1.2-0
|
||||
LUCI_TITLE:=LuCI support for internet-detector
|
||||
LUCI_DEPENDS:=+internet-detector
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
@@ -64,7 +64,7 @@ const btnStyleDisabled = 'btn cbi-button-reset';
|
||||
const btnStyleApply = 'btn cbi-button-apply';
|
||||
|
||||
var Timefield = ui.Textfield.extend({
|
||||
secToString: function(value) {
|
||||
secToString(value) {
|
||||
let string = '0';
|
||||
if(/^\d+$/.test(value)) {
|
||||
value = Number(value);
|
||||
@@ -81,7 +81,7 @@ var Timefield = ui.Textfield.extend({
|
||||
return string;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
let frameEl = E('div', { 'id': this.options.id }),
|
||||
inputEl = E('input', {
|
||||
'id' : this.options.id ? 'widget.' + this.options.id : null,
|
||||
@@ -98,7 +98,7 @@ var Timefield = ui.Textfield.extend({
|
||||
return this.bind(frameEl);
|
||||
},
|
||||
|
||||
getValue: function() {
|
||||
getValue() {
|
||||
let rawValue = this.node.querySelector('input').value,
|
||||
value = 0,
|
||||
res = rawValue.match(/^(\d+)([hms]?)$/);
|
||||
@@ -121,7 +121,7 @@ var Timefield = ui.Textfield.extend({
|
||||
return String(value);
|
||||
},
|
||||
|
||||
setValue: function(value) {
|
||||
setValue(value) {
|
||||
let inputEl = this.node.querySelector('input');
|
||||
inputEl.value = this.secToString(value);
|
||||
},
|
||||
@@ -140,10 +140,6 @@ return view.extend({
|
||||
inetStatusArea : E('div', { 'class': 'cbi-value-field', 'id': 'inetStatusArea' }),
|
||||
serviceStatusLabel : E('em', { 'id': 'serviceStatusLabel' }),
|
||||
initButton : null,
|
||||
uiPollCounter : 0,
|
||||
uiPollState : null,
|
||||
uiCheckIntervalUp : null,
|
||||
uiCheckIntervalDown : null,
|
||||
currentAppMode : '0',
|
||||
defaultHosts : [ '8.8.8.8', '1.1.1.1' ],
|
||||
leds : [],
|
||||
@@ -164,7 +160,7 @@ return view.extend({
|
||||
expect: { result: false }
|
||||
}),
|
||||
|
||||
getInitStatus: function() {
|
||||
getInitStatus() {
|
||||
return this.callInitStatus(this.appName).then(res => {
|
||||
if(res) {
|
||||
return res[this.appName].enabled;
|
||||
@@ -177,7 +173,7 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
handleServiceAction: function(action) {
|
||||
handleServiceAction(action) {
|
||||
return this.callInitAction(this.appName, action).then(success => {
|
||||
if(!success) {
|
||||
throw _('Command failed');
|
||||
@@ -189,12 +185,24 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
setInternetStatus: function() {
|
||||
callUIPoll: rpc.declare({
|
||||
object: 'luci.internet-detector',
|
||||
method: 'UIPoll',
|
||||
expect: { '': {} }
|
||||
}),
|
||||
|
||||
getUIPoll() {
|
||||
return this.callUIPoll().then(data => {
|
||||
return data;
|
||||
});
|
||||
},
|
||||
|
||||
setInternetStatus() {
|
||||
this.inetStatusArea.innerHTML = '';
|
||||
|
||||
if(!this.inetStatus || !this.inetStatus.instances || this.inetStatus.instances.length === 0) {
|
||||
let label = E('span', { 'class': 'id-label-status id-undefined' }, _('Undefined'))
|
||||
if(this.currentAppMode !== '0' && this.appStatus !== 'stoped') {
|
||||
let label = E('span', { 'class': 'id-label-status id-undefined' }, _('Undefined'));
|
||||
if((this.currentAppMode === '1' && this.appStatus !== 'stoped') || this.currentAppMode === '2') {
|
||||
label.classList.add('spinning');
|
||||
};
|
||||
this.inetStatusArea.append(label);
|
||||
@@ -219,8 +227,7 @@ return view.extend({
|
||||
|
||||
this.inetStatusArea.append(
|
||||
E('span', { 'class': className }, '%s%s%s'.format(
|
||||
(this.currentAppMode === '1') ? i.instance + ': ' : '',
|
||||
status, publicIp)
|
||||
i.instance + ': ', status, publicIp)
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -233,7 +240,7 @@ return view.extend({
|
||||
};
|
||||
},
|
||||
|
||||
inetStatusFromJson: function(res) {
|
||||
inetStatusFromJson(res) {
|
||||
let inetStatData = null;
|
||||
if(res.code === 0) {
|
||||
try {
|
||||
@@ -243,7 +250,7 @@ return view.extend({
|
||||
return inetStatData;
|
||||
},
|
||||
|
||||
servicePoll: function() {
|
||||
servicePoll() {
|
||||
return Promise.all([
|
||||
fs.exec(this.execPath, [ 'status' ]),
|
||||
fs.exec(this.execPath, [ 'inet-status' ]),
|
||||
@@ -259,31 +266,18 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
uiPoll: function() {
|
||||
this.uiPollCounter = ++this.uiPollCounter;
|
||||
|
||||
if((this.uiPollState === 0 && this.uiPollCounter % this.uiCheckIntervalUp) ||
|
||||
(this.uiPollState === 1 && this.uiPollCounter % this.uiCheckIntervalDown)) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.uiPollCounter = 0;
|
||||
|
||||
return fs.exec(this.execPath, [ 'poll' ]).then(res => {
|
||||
let inetStatData = this.inetStatusFromJson(res);
|
||||
if(inetStatData.instances[0]) {
|
||||
this.uiPollState = inetStatData.instances[0].inet;
|
||||
};
|
||||
this.inetStatus = inetStatData;
|
||||
uiPoll() {
|
||||
return this.getUIPoll().then(status => {
|
||||
this.inetStatus = status;
|
||||
this.setInternetStatus();
|
||||
});
|
||||
},
|
||||
|
||||
serviceRestart: function() {
|
||||
serviceRestart() {
|
||||
return this.handleServiceAction('restart');
|
||||
},
|
||||
|
||||
serviceRestartHandler: function() {
|
||||
serviceRestartHandler() {
|
||||
poll.stop();
|
||||
return this.serviceRestart().then(() => {
|
||||
window.setTimeout(() => this.servicePoll(), 1000);
|
||||
@@ -294,7 +288,7 @@ return view.extend({
|
||||
CBITimeInput: form.Value.extend({
|
||||
__name__ : 'CBI.TimeInput',
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
let value = (cfgvalue != null) ? cfgvalue : this.default,
|
||||
widget = new Timefield(value, {
|
||||
id : this.cbid(section_id),
|
||||
@@ -318,7 +312,7 @@ return view.extend({
|
||||
CBIBlockInetStatus: form.Value.extend({
|
||||
__name__ : 'CBI.BlockInetStatus',
|
||||
|
||||
__init__ : function(map, section, ctx) {
|
||||
__init__(map, section, ctx) {
|
||||
this.map = map;
|
||||
this.section = section;
|
||||
this.ctx = ctx;
|
||||
@@ -326,7 +320,7 @@ return view.extend({
|
||||
this.rmempty = true;
|
||||
},
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
this.ctx.setInternetStatus();
|
||||
|
||||
return E([
|
||||
@@ -340,7 +334,7 @@ return view.extend({
|
||||
CBIBlockServiceStatus: form.Value.extend({
|
||||
__name__ : 'CBI.BlockServiceStatus',
|
||||
|
||||
__init__ : function(map, section, ctx) {
|
||||
__init__(map, section, ctx) {
|
||||
this.map = map;
|
||||
this.section = section;
|
||||
this.ctx = ctx;
|
||||
@@ -348,7 +342,7 @@ return view.extend({
|
||||
this.rmempty = true;
|
||||
},
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
return E([
|
||||
E('label', { 'class': 'cbi-value-title', 'for': 'serviceStatusLabel' },
|
||||
_('Service')
|
||||
@@ -363,7 +357,7 @@ return view.extend({
|
||||
CBIBlockInitButton: form.Value.extend({
|
||||
__name__ : 'CBI.BlockInitButton',
|
||||
|
||||
__init__ : function(map, section, ctx) {
|
||||
__init__(map, section, ctx) {
|
||||
this.map = map;
|
||||
this.section = section;
|
||||
this.ctx = ctx;
|
||||
@@ -371,7 +365,7 @@ return view.extend({
|
||||
this.rmempty = true;
|
||||
},
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
this.ctx.initButton = E('button', {
|
||||
'class': (!this.ctx.initStatus) ? btnStyleDisabled : btnStyleEnabled,
|
||||
'click': ui.createHandlerFn(this, () => {
|
||||
@@ -413,7 +407,7 @@ return view.extend({
|
||||
CBIBlockFileEdit: form.Value.extend({
|
||||
__name__ : 'CBI.BlockFileEdit',
|
||||
|
||||
__init__ : function(map, section, ctx, id, file, title, description, callback) {
|
||||
__init__(map, section, ctx, id, file, title, description, callback) {
|
||||
this.map = map;
|
||||
this.section = section;
|
||||
this.ctx = ctx;
|
||||
@@ -427,11 +421,11 @@ return view.extend({
|
||||
this.content = '';
|
||||
},
|
||||
|
||||
cfgvalue: function(section_id, option) {
|
||||
cfgvalue(section_id, option) {
|
||||
return this.content;
|
||||
},
|
||||
|
||||
formvalue: function(section_id) {
|
||||
formvalue(section_id) {
|
||||
let value = this.content;
|
||||
let textarea = document.getElementById('widget.file_edit.content.' + this.id);
|
||||
if(textarea) {
|
||||
@@ -440,7 +434,7 @@ return view.extend({
|
||||
return value;
|
||||
},
|
||||
|
||||
write: function(section_id, formvalue) {
|
||||
write(section_id, formvalue) {
|
||||
return fs.write(this.file, formvalue).then(rc => {
|
||||
ui.addNotification(null, E('p', _('Contents have been saved.')),
|
||||
'info');
|
||||
@@ -453,13 +447,13 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
load: function() {
|
||||
load() {
|
||||
return L.resolveDefault(fs.read(this.file), '').then(c => {
|
||||
this.content = c;
|
||||
});
|
||||
},
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
return E('textarea', {
|
||||
'id' : 'widget.file_edit.content.' + this.id,
|
||||
'class' : 'cbi-input-textarea',
|
||||
@@ -471,7 +465,7 @@ return view.extend({
|
||||
},
|
||||
}),
|
||||
|
||||
load: function() {
|
||||
load() {
|
||||
return Promise.all([
|
||||
fs.exec(this.execPath, [ 'status' ]),
|
||||
this.getInitStatus(),
|
||||
@@ -484,7 +478,7 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
render(data) {
|
||||
if(!data) {
|
||||
return;
|
||||
};
|
||||
@@ -498,8 +492,6 @@ return view.extend({
|
||||
this.mta = true;
|
||||
};
|
||||
this.currentAppMode = uci.get(this.appName, 'config', 'mode');
|
||||
this.uiCheckIntervalUp = Number(uci.get(this.appName, 'ui', 'interval_up'));
|
||||
this.uiCheckIntervalDown = Number(uci.get(this.appName, 'ui', 'interval_down'));
|
||||
|
||||
let s, o, ss;
|
||||
let m = new form.Map(this.appName,
|
||||
@@ -549,32 +541,27 @@ return view.extend({
|
||||
);
|
||||
|
||||
|
||||
s = m.section(form.NamedSection, 'config', 'main');
|
||||
|
||||
|
||||
/* Service instances configuration */
|
||||
|
||||
s.tab('service', _('Service configuration'));
|
||||
if(this.currentAppMode !== '2') {
|
||||
|
||||
// enable_logger
|
||||
o = s.taboption('service', form.Flag, 'enable_logger',
|
||||
o = s.option(form.Flag, 'enable_logger',
|
||||
_('Enable logging'),
|
||||
_('Write messages to the system log.')
|
||||
);
|
||||
o.rmempty = false;
|
||||
};
|
||||
|
||||
o = s.taboption('service', form.SectionValue, 'instance', form.GridSection,
|
||||
'instance'
|
||||
);
|
||||
ss = o.subsection;
|
||||
s = m.section(form.GridSection, 'instance');
|
||||
|
||||
ss.title = _('Service instances');
|
||||
ss.addremove = true;
|
||||
ss.sortable = true;
|
||||
ss.nodescriptions = true;
|
||||
ss.addbtntitle = _('Add instance');
|
||||
s.title = _('Service instances');
|
||||
s.addremove = true;
|
||||
s.sortable = true;
|
||||
s.nodescriptions = true;
|
||||
s.addbtntitle = _('Add instance');
|
||||
|
||||
ss.tab('main', _('Main settings'));
|
||||
s.tab('main', _('Main settings'));
|
||||
|
||||
function makeIntervalOptions(list) {
|
||||
list.value(2, '2 ' + _('sec'));
|
||||
@@ -591,7 +578,7 @@ return view.extend({
|
||||
}
|
||||
|
||||
// enabled
|
||||
o = ss.taboption('main', form.Flag, 'enabled',
|
||||
o = s.taboption('main', form.Flag, 'enabled',
|
||||
_('Enabled'),
|
||||
);
|
||||
o.rmempty = false;
|
||||
@@ -600,16 +587,17 @@ return view.extend({
|
||||
o.modalonly = false;
|
||||
|
||||
// hosts
|
||||
o = ss.taboption('main', form.DynamicList,
|
||||
o = s.taboption('main', form.DynamicList,
|
||||
'hosts', _('Hosts'),
|
||||
_('Hosts to check Internet availability. Hosts are polled (in list order) until at least one of them responds.')
|
||||
);
|
||||
//o.datatype = 'or(host,hostport)';
|
||||
o.datatype = 'or(or(host,hostport),ipaddrport(1))';
|
||||
o.default = this.defaultHosts;
|
||||
o.rmempty = false;
|
||||
|
||||
// check_type
|
||||
o = ss.taboption('main', form.ListValue,
|
||||
o = s.taboption('main', form.ListValue,
|
||||
'check_type', _('Check type'),
|
||||
_('Host availability check type.')
|
||||
);
|
||||
@@ -619,7 +607,7 @@ return view.extend({
|
||||
o.modalonly = true;
|
||||
|
||||
// tcp_port
|
||||
o = ss.taboption('main', form.Value,
|
||||
o = s.taboption('main', form.Value,
|
||||
'tcp_port', _('TCP port'),
|
||||
_('Default port value for TCP connections.')
|
||||
);
|
||||
@@ -629,7 +617,7 @@ return view.extend({
|
||||
o.modalonly = true;
|
||||
|
||||
// icmp_packet_size
|
||||
o = ss.taboption('main', form.ListValue,
|
||||
o = s.taboption('main', form.ListValue,
|
||||
'icmp_packet_size', _('ICMP packet data size'));
|
||||
o.value(1, _('Small: 1 byte'));
|
||||
o.value(32, _('Windows: 32 bytes'));
|
||||
@@ -642,14 +630,14 @@ return view.extend({
|
||||
o.modalonly = true;
|
||||
|
||||
// iface
|
||||
o = ss.taboption('main', widgets.DeviceSelect,
|
||||
o = s.taboption('main', widgets.DeviceSelect,
|
||||
'iface', _('Interface'),
|
||||
_('Network interface for Internet access. If not specified, the default interface is used.')
|
||||
);
|
||||
o.noaliases = true;
|
||||
|
||||
// interval_up
|
||||
o = ss.taboption('main', form.ListValue,
|
||||
o = s.taboption('main', form.ListValue,
|
||||
'interval_up', _('Alive interval'),
|
||||
_('Hosts polling interval when the Internet is up.')
|
||||
);
|
||||
@@ -658,7 +646,7 @@ return view.extend({
|
||||
makeIntervalOptions(o);
|
||||
|
||||
// interval_down
|
||||
o = ss.taboption('main', form.ListValue,
|
||||
o = s.taboption('main', form.ListValue,
|
||||
'interval_down', _('Dead interval'),
|
||||
_('Hosts polling interval when the Internet is down.')
|
||||
);
|
||||
@@ -667,7 +655,7 @@ return view.extend({
|
||||
makeIntervalOptions(o);
|
||||
|
||||
// connection_attempts
|
||||
o = ss.taboption('main', form.ListValue,
|
||||
o = s.taboption('main', form.ListValue,
|
||||
'connection_attempts', _('Connection attempts'),
|
||||
_('Maximum number of attempts to connect to each host.')
|
||||
);
|
||||
@@ -680,7 +668,7 @@ return view.extend({
|
||||
o.default = '2';
|
||||
|
||||
// connection_timeout
|
||||
o = ss.taboption('main', form.ListValue,
|
||||
o = s.taboption('main', form.ListValue,
|
||||
'connection_timeout', _('Connection timeout'),
|
||||
_('Maximum timeout for waiting for a response from the host.')
|
||||
);
|
||||
@@ -700,15 +688,23 @@ return view.extend({
|
||||
|
||||
/* Modules */
|
||||
|
||||
ss.tab('led_control', _('LED control'));
|
||||
ss.tab('reboot_device', _('Reboot device'));
|
||||
ss.tab('restart_network', _('Restart network'));
|
||||
ss.tab('restart_modem', _('Restart modem'));
|
||||
ss.tab('public_ip', _('Public IP address'));
|
||||
ss.tab('email', _('Email notification'));
|
||||
ss.tab('user_scripts', _('User scripts'));
|
||||
if(this.currentAppMode !== '2') {
|
||||
s.tab('led_control', _('LED control'));
|
||||
s.tab('reboot_device', _('Reboot device'));
|
||||
s.tab('restart_network', _('Restart network'));
|
||||
s.tab('restart_modem', _('Restart modem'));
|
||||
};
|
||||
|
||||
ss.addModalOptions = (s, section_id, ev) => {
|
||||
s.tab('public_ip', _('Public IP address'));
|
||||
|
||||
if(this.currentAppMode !== '2') {
|
||||
s.tab('email', _('Email notification'));
|
||||
s.tab('user_scripts', _('User scripts'));
|
||||
};
|
||||
|
||||
s.addModalOptions = (s, section_id, ev) => {
|
||||
|
||||
if(this.currentAppMode !== '2') {
|
||||
|
||||
// LED control
|
||||
|
||||
@@ -740,7 +736,6 @@ return view.extend({
|
||||
_('Action when connected'));
|
||||
o.depends({ mod_led_control_enabled: '1' });
|
||||
o.modalonly = true;
|
||||
//o.value(0, _('Nothing'));
|
||||
o.value(1, _('Off'));
|
||||
o.value(2, _('On'));
|
||||
o.value(3, _('Blink'));
|
||||
@@ -919,6 +914,8 @@ return view.extend({
|
||||
o.modalonly = true;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
// Public IP address
|
||||
|
||||
o = s.taboption('public_ip', form.DummyValue, '_dummy');
|
||||
@@ -978,10 +975,12 @@ return view.extend({
|
||||
);
|
||||
o.default = '3'
|
||||
o.modalonly = true;
|
||||
for(let i=1; i<=5; i++) {
|
||||
for(let i = 1; i <= 5; i++) {
|
||||
o.value(i, i + ' ' + _('sec'));
|
||||
};
|
||||
|
||||
if(this.currentAppMode !== '2') {
|
||||
|
||||
// enable_ip_script
|
||||
o = s.taboption('public_ip', form.Flag, 'mod_public_ip_enable_ip_script',
|
||||
_('Enable public-ip-script'));
|
||||
@@ -997,6 +996,7 @@ return view.extend({
|
||||
);
|
||||
o.modalonly = true;
|
||||
|
||||
|
||||
// Email notification
|
||||
|
||||
o = s.taboption('email', form.DummyValue, '_dummy');
|
||||
@@ -1141,107 +1141,7 @@ return view.extend({
|
||||
o.modalonly = true;
|
||||
};
|
||||
|
||||
|
||||
/* UI detector configuration */
|
||||
|
||||
s.tab('ui_detector', _('UI detector configuration'));
|
||||
|
||||
o = s.taboption('ui_detector', form.SectionValue,
|
||||
'ui', form.NamedSection, 'ui'
|
||||
);
|
||||
ss = o.subsection;
|
||||
|
||||
let makeUIIntervalOptions = L.bind(function(list) {
|
||||
list.value(1, '%d %s'.format(this.pollInterval, _('sec')));
|
||||
list.value(2, '%d %s'.format(this.pollInterval * 2, _('sec')));
|
||||
list.value(3, '%d %s'.format(this.pollInterval * 3, _('sec')));
|
||||
list.value(4, '%d %s'.format(this.pollInterval * 4, _('sec')));
|
||||
list.value(5, '%d %s'.format(this.pollInterval * 5, _('sec')));
|
||||
list.value(6, '%d %s'.format(this.pollInterval * 6, _('sec')));
|
||||
}, this);
|
||||
|
||||
// hosts
|
||||
o = ss.option(form.DynamicList,
|
||||
'hosts', _('Hosts'),
|
||||
_('Hosts to check Internet availability. Hosts are polled (in list order) until at least one of them responds.')
|
||||
);
|
||||
o.datatype = 'or(or(host,hostport),ipaddrport(1))';
|
||||
o.default = this.defaultHosts;
|
||||
o.rmempty = false;
|
||||
|
||||
// check_type
|
||||
o = ss.option(form.ListValue,
|
||||
'check_type', _('Check type'),
|
||||
_('Host availability check type.')
|
||||
);
|
||||
o.value(0, _('TCP port connection'));
|
||||
o.value(1, _('ICMP-echo request (ping)'));
|
||||
o.default = '0';
|
||||
|
||||
// tcp_port
|
||||
o = ss.option(form.Value,
|
||||
'tcp_port', _('TCP port'),
|
||||
_('Default port value for TCP connections.')
|
||||
);
|
||||
o.datatype = 'port';
|
||||
o.default = '53';
|
||||
o.depends({ check_type: '0' });
|
||||
|
||||
// icmp_packet_size
|
||||
o = ss.option(form.ListValue,
|
||||
'icmp_packet_size', _('ICMP packet data size'));
|
||||
o.value(1, _('Small: 1 byte'));
|
||||
o.value(32, _('Windows: 32 bytes'));
|
||||
o.value(56, _('Standard: 56 bytes'));
|
||||
o.value(248, _('Big: 248 bytes'));
|
||||
o.value(1492, _('Huge: 1492 bytes'));
|
||||
o.value(9000, _('Jumbo: 9000 bytes'));
|
||||
o.default = '56';
|
||||
o.depends({ check_type: '1' });
|
||||
|
||||
// iface
|
||||
o = ss.option(widgets.DeviceSelect,
|
||||
'iface', _('Interface'),
|
||||
_('Network interface for Internet access. If not specified, the default interface is used.')
|
||||
);
|
||||
o.noaliases = true;
|
||||
|
||||
// interval_up
|
||||
o = ss.option(form.ListValue,
|
||||
'interval_up', _('Alive interval'),
|
||||
_('Hosts polling interval when the Internet is up.')
|
||||
);
|
||||
makeUIIntervalOptions(o);
|
||||
o.default = '6';
|
||||
|
||||
// interval_down
|
||||
o = ss.option(form.ListValue,
|
||||
'interval_down', _('Dead interval'),
|
||||
_('Hosts polling interval when the Internet is down.')
|
||||
);
|
||||
makeUIIntervalOptions(o);
|
||||
o.default = '1';
|
||||
|
||||
// connection_attempts
|
||||
o = ss.option(form.ListValue,
|
||||
'connection_attempts', _('Connection attempts'),
|
||||
_('Maximum number of attempts to connect to each host.')
|
||||
);
|
||||
o.value(1);
|
||||
o.value(2);
|
||||
o.value(3);
|
||||
o.default = '1';
|
||||
|
||||
// connection_timeout
|
||||
o = ss.option(form.ListValue,
|
||||
'connection_timeout', _('Connection timeout'),
|
||||
_('Maximum timeout for waiting for a response from the host.')
|
||||
);
|
||||
o.value(1, '1 ' + _('sec'));
|
||||
o.value(2, '2 ' + _('sec'));
|
||||
o.value(3, '3 ' + _('sec'));
|
||||
o.default = '1';
|
||||
|
||||
};
|
||||
|
||||
if(this.currentAppMode !== '0') {
|
||||
poll.add(
|
||||
@@ -1255,7 +1155,7 @@ return view.extend({
|
||||
return mapPromise;
|
||||
},
|
||||
|
||||
handleSaveApply: function(ev, mode) {
|
||||
handleSaveApply(ev, mode) {
|
||||
poll.stop();
|
||||
return this.handleSave(ev).then(() => {
|
||||
ui.changes.apply(mode == '0');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require fs';
|
||||
'require rpc';
|
||||
'require uci';
|
||||
|
||||
document.head.append(E('style', {'type': 'text/css'},
|
||||
@@ -57,14 +58,22 @@ return baseclass.extend({
|
||||
title : _('Internet'),
|
||||
appName : 'internet-detector',
|
||||
execPath : '/usr/bin/internet-detector',
|
||||
uiCheckIntervalUp : null,
|
||||
uiCheckIntervalDown : null,
|
||||
currentAppMode : null,
|
||||
inetStatus : null,
|
||||
uiState : null,
|
||||
counter : 0,
|
||||
|
||||
inetStatusFromJson : function(res) {
|
||||
callUIPoll: rpc.declare({
|
||||
object: 'luci.internet-detector',
|
||||
method: 'UIPoll',
|
||||
expect: { '': {} }
|
||||
}),
|
||||
|
||||
getUIPoll() {
|
||||
return this.callUIPoll().then(data => {
|
||||
return data;
|
||||
});
|
||||
},
|
||||
|
||||
inetStatusFromJson(res) {
|
||||
let inetStatData = null;
|
||||
if(res.code === 0) {
|
||||
try {
|
||||
@@ -74,50 +83,38 @@ return baseclass.extend({
|
||||
return inetStatData;
|
||||
},
|
||||
|
||||
load: async function() {
|
||||
if(!(this.uiCheckIntervalUp && this.uiCheckIntervalDown && this.currentAppMode)) {
|
||||
async load() {
|
||||
if(!this.currentAppMode) {
|
||||
await uci.load(this.appName).then(data => {
|
||||
this.uiCheckIntervalUp = Number(uci.get(this.appName, 'ui', 'interval_up'));
|
||||
this.uiCheckIntervalDown = Number(uci.get(this.appName, 'ui', 'interval_down'));
|
||||
this.currentAppMode = uci.get(this.appName, 'config', 'mode');
|
||||
}).catch(e => {});
|
||||
};
|
||||
|
||||
if(this.currentAppMode === '2') {
|
||||
this.counter++;
|
||||
|
||||
if((this.uiState === 0 && this.counter % this.uiCheckIntervalUp) ||
|
||||
(this.uiState === 1 && this.counter % this.uiCheckIntervalDown)
|
||||
) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.counter = 0;
|
||||
return L.resolveDefault(fs.exec(this.execPath, [ 'poll' ]), null);
|
||||
return this.getUIPoll();
|
||||
}
|
||||
else if(this.currentAppMode === '1') {
|
||||
return L.resolveDefault(fs.exec(this.execPath, [ 'inet-status' ]), null);
|
||||
};
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
render(data) {
|
||||
if(this.currentAppMode === '0') {
|
||||
return;
|
||||
}
|
||||
else if(this.currentAppMode === '1' && data) {
|
||||
data = this.inetStatusFromJson(data);
|
||||
};
|
||||
|
||||
if(data) {
|
||||
this.inetStatus = this.inetStatusFromJson(data);
|
||||
if(this.currentAppMode === '2') {
|
||||
this.uiState = this.inetStatus.instances[0].inet;
|
||||
};
|
||||
};
|
||||
this.inetStatus = data;
|
||||
|
||||
let inetStatusArea = E('div', {});
|
||||
|
||||
if(!this.inetStatus || !this.inetStatus.instances || this.inetStatus.instances.length === 0) {
|
||||
inetStatusArea.append(
|
||||
E('span', { 'class': 'id-label-status id-undefined' }, _('Undefined'))
|
||||
);
|
||||
let label = E('span', { 'class': 'id-label-status id-undefined' }, _('Undefined'));
|
||||
if(this.currentAppMode === '2') {
|
||||
label.classList.add('spinning');
|
||||
};
|
||||
inetStatusArea.append(label);
|
||||
} else {
|
||||
this.inetStatus.instances.sort((a, b) => a.num > b.num);
|
||||
|
||||
@@ -139,8 +136,7 @@ return baseclass.extend({
|
||||
|
||||
inetStatusArea.append(
|
||||
E('span', { 'class': className }, '%s%s%s'.format(
|
||||
(this.currentAppMode === '1') ? i.instance + ': ' : '',
|
||||
status, publicIp)
|
||||
i.instance + ': ', status, publicIp)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -434,9 +434,6 @@ msgstr "Таймаут между остановкой и запуском ин
|
||||
msgid "Type a time string"
|
||||
msgstr "Введите строку времени"
|
||||
|
||||
msgid "UI detector configuration"
|
||||
msgstr "Конфигурация UI детектора"
|
||||
|
||||
msgid "Unable to read the contents"
|
||||
msgstr "Невозможно прочитать содержимое"
|
||||
|
||||
|
||||
@@ -404,9 +404,6 @@ msgstr ""
|
||||
msgid "Type a time string"
|
||||
msgstr ""
|
||||
|
||||
msgid "UI detector configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to read the contents"
|
||||
msgstr ""
|
||||
|
||||
|
||||
46
luci-app-internet-detector/root/usr/libexec/rpcd/luci.internet-detector
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
. /lib/functions.sh
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
readonly ID_EXEC="/usr/bin/internet-detector"
|
||||
|
||||
run_instance() {
|
||||
config_get enabled "$1" enabled "0"
|
||||
if [ $enabled = "1" ]; then
|
||||
$ID_EXEC service "$1" > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
start_ui_instances() {
|
||||
config_load internet-detector
|
||||
config_get mode "config" mode "0"
|
||||
if [ $mode = "2" ]; then
|
||||
config_foreach run_instance "instance"
|
||||
fi
|
||||
}
|
||||
|
||||
ui_poll() {
|
||||
$ID_EXEC uipoll
|
||||
if [ $? -eq 126 ]; then
|
||||
start_ui_instances
|
||||
$ID_EXEC inet-status
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
list)
|
||||
json_init
|
||||
json_add_object "UIPoll"
|
||||
json_close_object
|
||||
json_dump
|
||||
json_cleanup
|
||||
;;
|
||||
call)
|
||||
case "$2" in
|
||||
UIPoll)
|
||||
ui_poll
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
@@ -12,7 +12,8 @@
|
||||
},
|
||||
"uci": [ "internet-detector" ],
|
||||
"ubus": {
|
||||
"luci": [ "getInitList", "setInitAction" ]
|
||||
"luci": [ "getInitList", "setInitAction" ],
|
||||
"luci.internet-detector": [ "UIPoll" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
|
||||
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 165 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |