mirror of
https://github.com/gSpotx2f/luci-app-internet-detector.git
synced 2025-12-06 03:26:50 +03:00
v0.3. Internet detector daemon
This commit is contained in:
214
LICENSE
214
LICENSE
@@ -1,21 +1,201 @@
|
||||
MIT License
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright (c) 2020 gSpotx2f
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
1. Definitions.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
18
Makefile
18
Makefile
@@ -1,18 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2020 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
#
|
||||
# This is free software, licensed under the MIT License.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_VERSION:=0.2
|
||||
PKG_RELEASE:=1
|
||||
LUCI_TITLE:=Internet detector for the LuCI status page
|
||||
LUCI_DEPENDS:=+luci-mod-admin-full
|
||||
LUCI_PKGARCH:=all
|
||||
PKG_LICENSE:=MIT
|
||||
|
||||
include ../../luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
42
README.md
42
README.md
@@ -1,21 +1,43 @@
|
||||
# luci-app-internet-detector
|
||||
Internet detector for the LuCI status page (OpenWrt webUI).
|
||||
# Internet detector for OpenWrt.
|
||||
Checking Internet availability.
|
||||
|
||||
OpenWrt >= 19.07.
|
||||
|
||||
Dependences: lua, luci-lib-nixio, libuci-lua
|
||||
|
||||
**Installation notes:**
|
||||
|
||||
wget --no-check-certificate -O /tmp/luci-app-internet-detector_0.2-1_all.ipk https://github.com/gSpotx2f/luci-app-internet-detector/raw/master/packages/19.07/luci-app-internet-detector_0.2-1_all.ipk
|
||||
opkg install /tmp/luci-app-internet-detector_0.2-1_all.ipk
|
||||
rm /tmp/luci-app-internet-detector_0.2-1_all.ipk
|
||||
/etc/init.d/rpcd restart
|
||||
wget --no-check-certificate -O /tmp/internet-detector_0.3.0-1_all.ipk https://github.com/gSpotx2f/luci-app-internet-detector/raw/master/packages/19.07/internet-detector_0.3.0-1_all.ipk
|
||||
opkg install /tmp/internet-detector_0.3.0-1_all.ipk
|
||||
rm /tmp/internet-detector_0.3.0-1_all.ipk
|
||||
|
||||
wget --no-check-certificate -O /tmp/luci-app-internet-detector_0.3.0-1_all.ipk https://github.com/gSpotx2f/luci-app-internet-detector/raw/master/packages/19.07/luci-app-internet-detector_0.3.0-1_all.ipk
|
||||
opkg install /tmp/luci-app-internet-detector_0.3.0-1_all.ipk
|
||||
rm /tmp/luci-app-internet-detector_0.3.0-1_all.ipk
|
||||
/etc/init.d/rpcd reload
|
||||
|
||||
**i18n-ru:**
|
||||
|
||||
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_0.2-1_all.ipk https://github.com/gSpotx2f/luci-app-internet-detector/raw/master/packages/19.07/luci-i18n-internet-detector-ru_0.2-1_all.ipk
|
||||
opkg install /tmp/luci-i18n-internet-detector-ru_0.2-1_all.ipk
|
||||
rm /tmp/luci-i18n-internet-detector-ru_0.2-1_all.ipk
|
||||
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_0.3.0-1_all.ipk https://github.com/gSpotx2f/luci-app-internet-detector/raw/master/packages/19.07/luci-i18n-internet-detector-ru_0.3.0-1_all.ipk
|
||||
opkg install /tmp/luci-i18n-internet-detector-ru_0.3.0-1_all.ipk
|
||||
rm /tmp/luci-i18n-internet-detector-ru_0.3.0-1_all.ipk
|
||||
|
||||
**Screenshots:**
|
||||
**Script for LED control:**
|
||||
|
||||

|
||||
|
||||
LED is on when Internet is available. A specific LED can be set in `/etc/internet-detector/run-script` (`LEDN`), either by number or by name from /sys/class/leds/*****. The list of available LEDs can be obtained using the command: `/usr/bin/internet-detector-led.sh list`.
|
||||
|
||||
wget --no-check-certificate -O /usr/bin/internet-detector-led.sh https://github.com/gSpotx2f/luci-app-internet-detector/raw/master/led/usr/bin/internet-detector-led.sh
|
||||
chmod +x /usr/bin/internet-detector-led.sh
|
||||
wget --no-check-certificate -O /etc/internet-detector/run-script https://github.com/gSpotx2f/luci-app-internet-detector/raw/master/led/etc/internet-detector/run-script
|
||||
chmod +x /etc/internet-detector/run-script
|
||||
uci set internet-detector.config.enable_run_script='1'
|
||||
uci commit
|
||||
/etc/init.d/internet-detector restart
|
||||
|
||||
### Screenshots:
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
|
||||
return L.Class.extend({
|
||||
title: _('Internet'),
|
||||
|
||||
hosts: [
|
||||
'8.8.8.8',
|
||||
'2a00:1450:4010:c05::71',
|
||||
'1.1.1.1',
|
||||
'2606:4700::6811:b055',
|
||||
//'8.8.4.4',
|
||||
//'2a00:1450:4010:c09::66',
|
||||
],
|
||||
|
||||
checkInterval: 6, // 5 x 6 = 30 sec.
|
||||
|
||||
load: async function() {
|
||||
window.internetDetectorCounter = ('internetDetectorCounter' in window) ?
|
||||
++window.internetDetectorCounter : 0;
|
||||
if(!('internetDetectorState' in window)) {
|
||||
window.internetDetectorState = 1;
|
||||
};
|
||||
|
||||
if(window.internetDetectorState === 0 &&
|
||||
window.internetDetectorCounter % this.checkInterval) {
|
||||
return;
|
||||
};
|
||||
|
||||
for(let host of this.hosts) {
|
||||
await fs.exec('/bin/ping', [ '-c', '1', '-W', '1', host ]).then(res => {
|
||||
window.internetDetectorState = res.code;
|
||||
}).catch(e => {});
|
||||
|
||||
if(window.internetDetectorState === 0) {
|
||||
break;
|
||||
};
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let internetStatus = E('span', { 'class': 'label' });
|
||||
|
||||
if(window.internetDetectorState === 0) {
|
||||
internetStatus.style.background = '#46a546';
|
||||
internetStatus.textContent = _('Internet connected');
|
||||
} else {
|
||||
internetStatus.textContent = _('Internet disconnected');
|
||||
};
|
||||
|
||||
return E('div', {
|
||||
'class': 'cbi-section',
|
||||
'style': 'margin-bottom:1em',
|
||||
}, internetStatus);
|
||||
},
|
||||
});
|
||||
52
internet-detector/Makefile
Normal file
52
internet-detector/Makefile
Normal file
@@ -0,0 +1,52 @@
|
||||
#
|
||||
# (с) 2021 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=internet-detector
|
||||
PKG_VERSION:=0.3.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Internet detector
|
||||
URL:=https://github.com/gSpotx2f/luci-app-internet-detector
|
||||
PKGARCH:=all
|
||||
DEPENDS:=+lua +luci-lib-nixio +libuci-lua
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
/etc/config/internet-detector
|
||||
/etc/internet-detector/down-script
|
||||
/etc/internet-detector/run-script
|
||||
/etc/internet-detector/up-script
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
$(CP) -r ./files $(PKG_BUILD_DIR)/files
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/files/usr/bin/internet-detector $(1)/usr/bin/internet-detector
|
||||
$(INSTALL_DIR) $(1)/etc/internet-detector
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/files/etc/internet-detector/down-script $(1)/etc/internet-detector/down-script
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/files/etc/internet-detector/run-script $(1)/etc/internet-detector/run-script
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/files/etc/internet-detector/up-script $(1)/etc/internet-detector/up-script
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) $(PKG_BUILD_DIR)/files/etc/config/internet-detector $(1)/etc/config/internet-detector
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/files/etc/init.d/internet-detector $(1)/etc/init.d/internet-detector
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
18
internet-detector/files/etc/config/internet-detector
Normal file
18
internet-detector/files/etc/config/internet-detector
Normal file
@@ -0,0 +1,18 @@
|
||||
config main 'config'
|
||||
option mode '2'
|
||||
option enable_logger '1'
|
||||
option enable_up_script '0'
|
||||
option enable_down_script '0'
|
||||
option enable_run_script '0'
|
||||
option interval_up '30'
|
||||
option interval_down '5'
|
||||
option ui_interval_up '6'
|
||||
option ui_interval_down '1'
|
||||
list hosts '8.8.8.8'
|
||||
list hosts '1.1.1.1'
|
||||
option check_type '0'
|
||||
option connection_attempts '2'
|
||||
option connection_timeout '2'
|
||||
option ui_connection_attempts '1'
|
||||
option ui_connection_timeout '1'
|
||||
option tcp_port '53'
|
||||
19
internet-detector/files/etc/init.d/internet-detector
Executable file
19
internet-detector/files/etc/init.d/internet-detector
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=99
|
||||
STOP=01
|
||||
|
||||
ID="/usr/bin/internet-detector"
|
||||
|
||||
start() {
|
||||
$ID
|
||||
}
|
||||
|
||||
stop() {
|
||||
$ID stop
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
1
internet-detector/files/etc/internet-detector/down-script
Executable file
1
internet-detector/files/etc/internet-detector/down-script
Executable file
@@ -0,0 +1 @@
|
||||
# Shell commands to run when disconnected from the Internet
|
||||
4
internet-detector/files/etc/internet-detector/run-script
Executable file
4
internet-detector/files/etc/internet-detector/run-script
Executable file
@@ -0,0 +1,4 @@
|
||||
# Shell commands that are executed every time the Internet is checked for availability
|
||||
#
|
||||
# $1 - (0|1) - internet status: 0 is up, 1 is down
|
||||
#
|
||||
1
internet-detector/files/etc/internet-detector/up-script
Executable file
1
internet-detector/files/etc/internet-detector/up-script
Executable file
@@ -0,0 +1 @@
|
||||
# Shell commands that run when connected to the Internet
|
||||
391
internet-detector/files/usr/bin/internet-detector
Executable file
391
internet-detector/files/usr/bin/internet-detector
Executable file
@@ -0,0 +1,391 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
--[[
|
||||
Internet detector daemon for OpenWrt.
|
||||
|
||||
Dependences:
|
||||
lua
|
||||
luci-lib-nixio
|
||||
libuci-lua
|
||||
|
||||
(с) 2021 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
--]]
|
||||
|
||||
-- Default settings
|
||||
|
||||
local Config = {
|
||||
["mode"] = 2,
|
||||
["enableLogger"] = 1,
|
||||
["enableUpScript"] = 0,
|
||||
["enableDownScript"] = 0,
|
||||
["enableRunScript"] = 0,
|
||||
["intervalUp"] = 30,
|
||||
["intervalDown"] = 5,
|
||||
["connectionAttempts"] = 1,
|
||||
["UIConnectionAttempts"] = 1,
|
||||
["hosts"] = {
|
||||
[1] = "8.8.8.8",
|
||||
[2] = "1.1.1.1",
|
||||
},
|
||||
["parsedHosts"] = {},
|
||||
["appName"] = "internet-detector",
|
||||
["commonDir"] = "/tmp/run",
|
||||
["pingCmd"] = "ping",
|
||||
["pingParams"] = "-c 1",
|
||||
["connectionTimeout"] = 3,
|
||||
["UIConnectionTimeout"] = 1,
|
||||
["tcpPort"] = 53,
|
||||
["checkType"] = 0, -- 0: ping, 1: TCP
|
||||
["loggerLevel"] = "info",
|
||||
--["loggerCmd"] = "logger",
|
||||
}
|
||||
Config.configDir = "/etc/" .. Config.appName
|
||||
Config.upScript = Config.configDir .. "/" .. "up-script"
|
||||
Config.downScript = Config.configDir .. "/" .. "down-script"
|
||||
Config.runScript = Config.configDir .. "/" .. "run-script"
|
||||
Config.pidFile = Config.commonDir .. "/" .. Config.appName .. ".pid"
|
||||
Config.statusFile = Config.commonDir .. "/" .. Config.appName .. ".status"
|
||||
|
||||
-- Import packages
|
||||
|
||||
local function prequire(package)
|
||||
local retVal, pkg = pcall(require, package)
|
||||
return retVal and pkg
|
||||
end
|
||||
|
||||
local nixio = prequire("nixio")
|
||||
if not nixio then
|
||||
error("You need to install nixio...")
|
||||
end
|
||||
|
||||
local uci = prequire("uci")
|
||||
if uci then
|
||||
|
||||
-- Load settings from UCI
|
||||
|
||||
local cursor = uci.cursor()
|
||||
Config.mode = cursor:get("internet-detector", "config", "mode")
|
||||
Config.enableLogger = cursor:get("internet-detector", "config", "enable_logger")
|
||||
Config.enableUpScript = cursor:get("internet-detector", "config", "enable_up_script")
|
||||
Config.enableDownScript = cursor:get("internet-detector", "config", "enable_down_script")
|
||||
Config.enableRunScript = cursor:get("internet-detector", "config", "enable_run_script")
|
||||
Config.intervalUp = tonumber(cursor:get("internet-detector", "config", "interval_up"))
|
||||
Config.intervalDown = tonumber(cursor:get("internet-detector", "config", "interval_down"))
|
||||
Config.hosts = cursor:get("internet-detector", "config", "hosts")
|
||||
Config.checkType = tonumber(cursor:get("internet-detector", "config", "check_type"))
|
||||
Config.connectionAttempts = tonumber(cursor:get("internet-detector", "config", "connection_attempts"))
|
||||
Config.connectionTimeout = tonumber(cursor:get("internet-detector", "config", "connection_timeout"))
|
||||
Config.UIConnectionAttempts = tonumber(cursor:get("internet-detector", "config", "ui_connection_attempts"))
|
||||
Config.UIConnectionTimeout = tonumber(cursor:get("internet-detector", "config", "ui_connection_timeout"))
|
||||
Config.tcpPort = tonumber(cursor:get("internet-detector", "config", "tcp_port"))
|
||||
|
||||
else
|
||||
io.stderr:write("libuci-lua does not exists! The default settings will be used...\n")
|
||||
end
|
||||
|
||||
local function writeValueToFile(filePath, str)
|
||||
local retValue = false
|
||||
local fh = io.open(filePath, "w")
|
||||
if fh then
|
||||
fh:setvbuf("no")
|
||||
fh:write(string.format("%s\n", str))
|
||||
fh:close()
|
||||
retValue = true
|
||||
end
|
||||
return retValue
|
||||
end
|
||||
|
||||
local function readValueFromFile(filePath)
|
||||
local retValue
|
||||
local fh = io.open(filePath, "r")
|
||||
if fh then
|
||||
retValue = fh:read("*l")
|
||||
fh:close()
|
||||
end
|
||||
return retValue
|
||||
end
|
||||
|
||||
local function writeLogMessage(msg)
|
||||
if Config.enableLogger == "1" then
|
||||
local pidValue = readValueFromFile(Config.pidFile)
|
||||
--local fh = io.popen(string.format('%s -t "%s[%d]" -p daemon.%s "%s"', Config.loggerCmd, Config.appName, (pidValue or ""), Config.loggerLevel, msg), 'r')
|
||||
--fh:close()
|
||||
nixio.syslog(Config.loggerLevel, string.format("%s[%d]: %s", Config.appName, (pidValue or ""), msg))
|
||||
end
|
||||
end
|
||||
|
||||
local function runExternalScript(scriptPath, inetStat)
|
||||
if inetStat == nil then
|
||||
inetStat = ""
|
||||
end
|
||||
|
||||
if nixio.fs.access(scriptPath, "x") then
|
||||
local fh = io.popen(string.format('/bin/sh -c "%s %s" &', scriptPath, inetStat), "r")
|
||||
fh:close()
|
||||
end
|
||||
end
|
||||
|
||||
local function parseHost(host)
|
||||
local port
|
||||
local addr = host:match("^[^:]+")
|
||||
if host:find(":") then
|
||||
port = host:match("[^:]+$")
|
||||
end
|
||||
return addr, port
|
||||
end
|
||||
|
||||
local function parseHosts()
|
||||
Config.parsedHosts = {}
|
||||
for k, v in ipairs(Config.hosts) do
|
||||
local addr, port = parseHost(v)
|
||||
Config.parsedHosts[k] = {[1] = addr, [2] = (tonumber(port) or false)}
|
||||
end
|
||||
end
|
||||
|
||||
local function pingHost(host)
|
||||
return os.execute(string.format("%s %s -W %d %s > /dev/null 2>&1",
|
||||
Config.pingCmd, Config.pingParams, Config.connectionTimeout, host))
|
||||
end
|
||||
|
||||
local function tcpConnectToHost(host, port)
|
||||
local retCode = 1
|
||||
local addrInfo = nixio.getaddrinfo(host, "any")
|
||||
if addrInfo then
|
||||
local family = addrInfo[1].family
|
||||
if family then
|
||||
local socket = nixio.socket(family, "stream")
|
||||
socket:setopt("socket", "sndtimeo", Config.connectionTimeout)
|
||||
socket:setopt("socket", "rcvtimeo", Config.connectionTimeout)
|
||||
local success = socket:connect(host, port or Config.tcpPort)
|
||||
socket:close()
|
||||
retCode = success and 0 or 1
|
||||
end
|
||||
end
|
||||
return retCode
|
||||
end
|
||||
|
||||
local function checkHosts()
|
||||
local checkFunc = (Config.checkType == 1) and tcpConnectToHost or pingHost
|
||||
local retCode = 1
|
||||
for k, v in ipairs(Config.parsedHosts) do
|
||||
for i = 1, Config.connectionAttempts do
|
||||
if checkFunc(v[1], v[2]) == 0 then
|
||||
retCode = 0
|
||||
break
|
||||
end
|
||||
end
|
||||
if retCode == 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
return retCode
|
||||
end
|
||||
|
||||
local function main()
|
||||
local last_status
|
||||
local current_status
|
||||
local interval = Config.intervalUp
|
||||
|
||||
while true do
|
||||
current_status = checkHosts()
|
||||
if not nixio.fs.access(Config.statusFile, "r") then
|
||||
writeValueToFile(Config.statusFile, current_status)
|
||||
end
|
||||
|
||||
if current_status == 0 then
|
||||
interval = Config.intervalUp
|
||||
if last_status ~= nil and current_status ~= last_status then
|
||||
writeValueToFile(Config.statusFile, current_status)
|
||||
writeLogMessage("internet connected")
|
||||
if Config.enableUpScript == "1" then
|
||||
runExternalScript(Config.upScript)
|
||||
end
|
||||
end
|
||||
else
|
||||
interval = Config.intervalDown
|
||||
if last_status ~= nil and current_status ~= last_status then
|
||||
writeValueToFile(Config.statusFile, current_status)
|
||||
writeLogMessage("internet disconnected")
|
||||
if Config.enableDownScript == "1" then
|
||||
runExternalScript(Config.downScript)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Config.enableRunScript == "1" then
|
||||
runExternalScript(Config.runScript, current_status)
|
||||
end
|
||||
|
||||
last_status = current_status
|
||||
nixio.nanosleep(interval)
|
||||
end
|
||||
end
|
||||
|
||||
local function removeProcessFiles()
|
||||
os.remove(Config.pidFile)
|
||||
os.remove(Config.statusFile)
|
||||
end
|
||||
|
||||
local function status()
|
||||
if nixio.fs.access(Config.pidFile, "r") then
|
||||
return "running"
|
||||
else
|
||||
return "stoped"
|
||||
end
|
||||
end
|
||||
|
||||
local function poll(attempts, timeout)
|
||||
if Config.mode == "1" then
|
||||
Config.connectionAttempts = Config.UIConnectionAttempts
|
||||
Config.connectionTimeout = Config.UIConnectionTimeout
|
||||
end
|
||||
if attempts then
|
||||
Config.connectionAttempts = attempts
|
||||
end
|
||||
if timeout then
|
||||
Config.connectionTimeout = timeout
|
||||
end
|
||||
if checkHosts() == 0 then
|
||||
return "up"
|
||||
else
|
||||
return "down"
|
||||
end
|
||||
end
|
||||
|
||||
local function inetStatus()
|
||||
local inetStat = "down"
|
||||
if nixio.fs.access(Config.statusFile, "r") then
|
||||
local inetStatVal = readValueFromFile(Config.statusFile)
|
||||
if inetStatVal ~= nil and tonumber(inetStatVal) == 0 then
|
||||
inetStat = "up"
|
||||
end
|
||||
elseif Config.mode == "1" then
|
||||
inetStat = poll()
|
||||
else
|
||||
os.exit(126)
|
||||
end
|
||||
return inetStat
|
||||
end
|
||||
|
||||
local function stop()
|
||||
local pidValue
|
||||
if nixio.fs.access(Config.pidFile, "r") then
|
||||
pidValue = readValueFromFile(Config.pidFile)
|
||||
if pidValue then
|
||||
local success
|
||||
for i = 0, 10 do
|
||||
success = nixio.kill(tonumber(pidValue), 15)
|
||||
if success then
|
||||
break
|
||||
end
|
||||
end
|
||||
if not success then
|
||||
io.stderr:write(string.format('No such process: "%s"\n', pidValue))
|
||||
end
|
||||
writeLogMessage('stoped')
|
||||
removeProcessFiles()
|
||||
end
|
||||
end
|
||||
|
||||
if not pidValue then
|
||||
io.stderr:write(
|
||||
string.format('PID file "%s" does not exist. %s not running?\n',
|
||||
Config.pidFile, Config.appName))
|
||||
end
|
||||
end
|
||||
|
||||
local function preRun()
|
||||
-- Exit if internet detector mode != 2(Service)
|
||||
if Config.mode ~= "2" then
|
||||
io.stderr:write(string.format('Start failed, mode != "2"\n', Config.appName))
|
||||
os.exit(0)
|
||||
end
|
||||
if nixio.fs.access(Config.pidFile, "r") then
|
||||
io.stderr:write(
|
||||
string.format('PID file "%s" already exist. %s already running?\n',
|
||||
Config.pidFile, Config.appName))
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function run()
|
||||
local pidValue = nixio.getpid()
|
||||
writeValueToFile(Config.pidFile, pidValue)
|
||||
writeLogMessage('started')
|
||||
main()
|
||||
end
|
||||
|
||||
local function noDaemon()
|
||||
if not preRun() then
|
||||
return
|
||||
end
|
||||
run()
|
||||
end
|
||||
|
||||
local function daemon()
|
||||
if not preRun() then
|
||||
return
|
||||
end
|
||||
-- UNIX double fork
|
||||
if nixio.fork() == 0 then
|
||||
nixio.setsid()
|
||||
if nixio.fork() == 0 then
|
||||
nixio.chdir("/")
|
||||
nixio.umask(0)
|
||||
local devnull = "/dev/null"
|
||||
io.stdout:flush()
|
||||
io.stderr:flush()
|
||||
nixio.dup(io.open(devnull, "r"), io.stdin)
|
||||
nixio.dup(io.open(devnull, "a+"), io.stdout)
|
||||
nixio.dup(io.open(devnull, "a+"), io.stderr)
|
||||
run()
|
||||
end
|
||||
os.exit(0)
|
||||
end
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
local function restart()
|
||||
stop()
|
||||
daemon()
|
||||
end
|
||||
|
||||
-- Main section
|
||||
|
||||
parseHosts()
|
||||
|
||||
local function help()
|
||||
return string.format("Usage: %s [start|no-daemon|stop|restart|status|inet-status|poll [<attempts num>] [<timeout sec>]|--help]", arg[0])
|
||||
end
|
||||
|
||||
local helpArgs = {["-h"] = true, ["--help"] = true, ["help"] = true}
|
||||
if arg[1] == "start" or #arg == 0 then
|
||||
daemon()
|
||||
elseif arg[1] == "no-daemon" then
|
||||
noDaemon()
|
||||
elseif arg[1] == "stop" then
|
||||
stop()
|
||||
elseif arg[1] == "restart" then
|
||||
restart()
|
||||
elseif arg[1] == "status" then
|
||||
print(status())
|
||||
elseif arg[1] == "inet-status" then
|
||||
print(inetStatus())
|
||||
elseif arg[1] == "poll" then
|
||||
local attempts, timeout
|
||||
if arg[2] and arg[2]:match("[0-9]+") then
|
||||
attempts = tonumber(arg[2])
|
||||
if arg[3] and arg[3]:match("[0-9]+") then
|
||||
timeout = tonumber(arg[3])
|
||||
end
|
||||
end
|
||||
print(poll(attempts, timeout))
|
||||
elseif helpArgs[arg[1]] then
|
||||
print(help())
|
||||
else
|
||||
print(help())
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
os.exit(0)
|
||||
14
led/etc/internet-detector/run-script
Executable file
14
led/etc/internet-detector/run-script
Executable file
@@ -0,0 +1,14 @@
|
||||
# Shell commands that are executed every time the Internet is checked for availability
|
||||
#
|
||||
# $1 : (0|1) - internet status: 0 is up, 1 is down
|
||||
#
|
||||
|
||||
LEDN=1
|
||||
LED_CMD="/usr/bin/internet-detector-led.sh"
|
||||
LED_STATE=`$LED_CMD state $LEDN`
|
||||
|
||||
if [ $LED_STATE -eq 0 -a $1 -eq 0 ]; then
|
||||
$LED_CMD on $LEDN
|
||||
elif [ $LED_STATE -ne 0 -a $1 -eq 1 ]; then
|
||||
$LED_CMD off $LEDN
|
||||
fi
|
||||
63
led/usr/bin/internet-detector-led.sh
Executable file
63
led/usr/bin/internet-detector-led.sh
Executable file
@@ -0,0 +1,63 @@
|
||||
#/bin/sh
|
||||
|
||||
#
|
||||
# Usage: internet-detector-led.sh on|off|state|list [<LED number>|<LED sysfs-name>]
|
||||
#
|
||||
# Examples:
|
||||
# internet-detector-led.sh list # list of available LEDs
|
||||
# internet-detector-led.sh on 2 # turn on LED 2
|
||||
# internet-detector-led.sh off 2 # turn off LED 2
|
||||
# internet-detector-led.sh on "nbg6817:white:internet" # turn on LED by sysfs-name (/sys/class/leds/nbg6817:white:internet)
|
||||
# internet-detector-led.sh off "nbg6817:white:internet" # turn off --"--
|
||||
# internet-detector-led.sh on # same as "internet-detector-led.sh on 1" (default <LED number> is 1)
|
||||
# internet-detector-led.sh off # turn off --"--
|
||||
# internet-detector-led.sh state 2 # current state (brightness) of LED 2
|
||||
# ...
|
||||
#
|
||||
|
||||
if [ -n $2 ]; then
|
||||
LEDN=$2
|
||||
else
|
||||
LEDN=1
|
||||
fi
|
||||
|
||||
SYSFS_LEDS="/sys/class/leds/"
|
||||
|
||||
LED="`ls -1 $SYSFS_LEDS | awk -v LEDN=$LEDN '{
|
||||
LEDN = (length(LEDN) == 0) ? 1 : LEDN;
|
||||
if($0 == LEDN || NR == LEDN) {
|
||||
print $0;
|
||||
exit;
|
||||
}
|
||||
}'`" 2> /dev/null
|
||||
|
||||
LED_BR_PATH="${SYSFS_LEDS}/${LED}/brightness"
|
||||
|
||||
[ -w $LED_BR_PATH ] || exit 1
|
||||
|
||||
MAX_BRIGHTNESS=`cat ${SYSFS_LEDS}/${LED}/max_brightness` 2> /dev/null
|
||||
|
||||
if [ -z $MAX_BRIGHTNESS ]; then
|
||||
MAX_BRIGHTNESS=1
|
||||
fi
|
||||
|
||||
case $1 in
|
||||
on)
|
||||
printf $MAX_BRIGHTNESS > $LED_BR_PATH
|
||||
;;
|
||||
off)
|
||||
printf 0 > $LED_BR_PATH
|
||||
;;
|
||||
state)
|
||||
cat $LED_BR_PATH 2> /dev/null
|
||||
;;
|
||||
list)
|
||||
ls -1 $SYSFS_LEDS | awk '{print NR ": " $0}'
|
||||
;;
|
||||
*)
|
||||
echo "Usage: `basename $0` on|off|state|list [<LED number>|<LED sysfs-name>]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0;
|
||||
16
luci-app-internet-detector/Makefile
Normal file
16
luci-app-internet-detector/Makefile
Normal file
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# (с) 2021 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_VERSION:=0.3.0
|
||||
PKG_RELEASE:=1
|
||||
LUCI_TITLE:=LuCI support for internet-detector
|
||||
LUCI_DEPENDS:=+internet-detector
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
#include ../../luci.mk
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@@ -0,0 +1,554 @@
|
||||
'use strict';
|
||||
'require form';
|
||||
'require fs';
|
||||
'require rpc';
|
||||
'require uci';
|
||||
'require ui';
|
||||
|
||||
const btnStyleEnabled = 'btn cbi-button-save';
|
||||
const btnStyleDisabled = 'btn cbi-button-reset';
|
||||
const btnStyleApply = 'btn cbi-button-apply';
|
||||
|
||||
return L.view.extend({
|
||||
execPath : '/usr/bin/internet-detector',
|
||||
initPath : '/etc/init.d/internet-detector',
|
||||
upScriptPath : '/etc/internet-detector/up-script',
|
||||
downScriptPath : '/etc/internet-detector/down-script',
|
||||
runScriptPath : '/etc/internet-detector/run-script',
|
||||
pollInterval : L.env.pollinterval,
|
||||
appStatus : 'stoped',
|
||||
initStatus : null,
|
||||
inetStatus : null,
|
||||
inetStatusLabel : E('span', { 'class': 'label' }),
|
||||
inetStatusSpinner : E('span', { 'class': 'spinning', 'style': 'margin-top:1em' }, ' '),
|
||||
serviceStatusLabel : E('em'),
|
||||
serviceButton : null,
|
||||
initButton : null,
|
||||
uiPollCounter : 0,
|
||||
uiPollState : null,
|
||||
uiCheckIntervalUp : null,
|
||||
uiCheckIntervalDown : null,
|
||||
currentAppMode : '0',
|
||||
|
||||
callInitAction: rpc.declare({
|
||||
object: 'luci',
|
||||
method: 'setInitAction',
|
||||
params: [ 'name', 'action' ],
|
||||
expect: { result: false }
|
||||
}),
|
||||
|
||||
handleServiceAction: function(action) {
|
||||
return this.callInitAction('internet-detector', action).then(success => {
|
||||
if(!success) {
|
||||
throw _('Command failed');
|
||||
};
|
||||
return true;
|
||||
}).catch(e => {
|
||||
ui.addNotification(null,
|
||||
E('p', _('Failed to execute "%s %s": %s').format(this.initPath, action, e)));
|
||||
});
|
||||
},
|
||||
|
||||
serviceRestart: function(ev) {
|
||||
L.Poll.stop();
|
||||
return this.handleServiceAction('restart').then(() => {
|
||||
this.servicePoll();
|
||||
L.Poll.start();
|
||||
});
|
||||
},
|
||||
|
||||
fileEditDialog: L.Class.extend({
|
||||
__init__: function(file, title, description, callback, fileExists=false) {
|
||||
this.file = file;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.callback = callback;
|
||||
this.fileExists = fileExists;
|
||||
},
|
||||
|
||||
load: function() {
|
||||
return fs.read(this.file);
|
||||
},
|
||||
|
||||
render: function(content) {
|
||||
ui.showModal(this.title, [
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('div', { 'class': 'cbi-section-descr' }, this.description),
|
||||
E('div', { 'class': 'cbi-section' },
|
||||
E('p', {},
|
||||
E('textarea', {
|
||||
'id': 'widget.modal_content',
|
||||
'class': 'cbi-input-textarea',
|
||||
'style': 'width:100% !important',
|
||||
'rows': 10,
|
||||
'wrap': 'off',
|
||||
'spellcheck': 'false',
|
||||
},
|
||||
content || '')
|
||||
)
|
||||
),
|
||||
]),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'click': ui.hideModal,
|
||||
}, _('Dismiss')),
|
||||
' ',
|
||||
E('button', {
|
||||
'id': 'btn_save',
|
||||
'class': 'btn cbi-button-positive important',
|
||||
'click': ui.createHandlerFn(this, this.handleSave),
|
||||
}, _('Save')),
|
||||
]),
|
||||
]);
|
||||
},
|
||||
|
||||
handleSave: function(ev) {
|
||||
let textarea = document.getElementById('widget.modal_content');
|
||||
let value = textarea.value.trim().replace(/\r\n/g, '\n') + '\n';
|
||||
|
||||
return fs.write(this.file, value).then(rc => {
|
||||
textarea.value = value;
|
||||
ui.addNotification(null, E('p', _('Contents have been saved.')),
|
||||
'info');
|
||||
if(this.callback) {
|
||||
return this.callback(rc);
|
||||
};
|
||||
}).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to save the contents')
|
||||
+ ': %s'.format(e.message)));
|
||||
}).finally(() => {
|
||||
ui.hideModal();
|
||||
});
|
||||
},
|
||||
|
||||
error: function(e) {
|
||||
if(!this.fileExists && e instanceof Error && e.name === 'NotFoundError') {
|
||||
return this.render();
|
||||
} else {
|
||||
ui.showModal(this.title, [
|
||||
E('div', { 'class': 'cbi-section' },
|
||||
E('p', {}, _('Unable to read the contents')
|
||||
+ ': %s'.format(e.message))
|
||||
),
|
||||
E('div', { 'class': 'right' },
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'click': ui.hideModal,
|
||||
}, _('Dismiss'))
|
||||
),
|
||||
]);
|
||||
};
|
||||
},
|
||||
|
||||
show: function() {
|
||||
ui.showModal(null,
|
||||
E('p', { 'class': 'spinning' }, _('Loading'))
|
||||
);
|
||||
this.load().then(content => {
|
||||
ui.hideModal();
|
||||
return this.render(content);
|
||||
}).catch(e => {
|
||||
ui.hideModal();
|
||||
return this.error(e);
|
||||
})
|
||||
},
|
||||
}),
|
||||
|
||||
setInternetStatus: function(initial=false) {
|
||||
if(this.inetStatus === 'up') {
|
||||
this.inetStatusLabel.style.background = '#46a546';
|
||||
this.inetStatusLabel.textContent = _('Connected');
|
||||
}
|
||||
else if(this.inetStatus === 'down') {
|
||||
this.inetStatusLabel.textContent = _('Disconnected');
|
||||
this.inetStatusLabel.style.background = '#ff6c74';
|
||||
}
|
||||
else {
|
||||
this.inetStatusLabel.textContent = _('Undefined');
|
||||
this.inetStatusLabel.style.background = '#cccccc';
|
||||
};
|
||||
|
||||
if(!initial && this.inetStatusSpinner) {
|
||||
this.inetStatusSpinner.remove();
|
||||
};
|
||||
|
||||
if(this.appStatus === 'running') {
|
||||
this.serviceStatusLabel.textContent = _('Running');
|
||||
} else {
|
||||
this.serviceStatusLabel.textContent = _('Stopped');
|
||||
};
|
||||
},
|
||||
|
||||
CBIBlockService: form.DummyValue.extend({
|
||||
ctx: null,
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
this.title = this.description = null;
|
||||
|
||||
this.ctx.serviceButton = E('button', {
|
||||
'class': btnStyleApply,
|
||||
'click': ui.createHandlerFn(this.ctx, this.ctx.serviceRestart),
|
||||
}, _('Restart'));
|
||||
this.ctx.initButton = E('button', {
|
||||
'class': (this.ctx.initStatus === 1) ? btnStyleDisabled : btnStyleEnabled,
|
||||
'click': ui.createHandlerFn(this, () => {
|
||||
return this.ctx.handleServiceAction(
|
||||
(this.ctx.initStatus === 1) ? 'enable' : 'disable'
|
||||
).then(success => {
|
||||
if(!success) {
|
||||
return;
|
||||
};
|
||||
if(this.ctx.initStatus === 1) {
|
||||
this.ctx.initButton.textContent = _('Enabled');
|
||||
this.ctx.initButton.className = btnStyleEnabled;
|
||||
this.ctx.initStatus = 0;
|
||||
}
|
||||
else {
|
||||
this.ctx.initButton.textContent = _('Disabled');
|
||||
this.ctx.initButton.className = btnStyleDisabled;
|
||||
this.ctx.initStatus = 1;
|
||||
};
|
||||
});
|
||||
}),
|
||||
}, (this.ctx.initStatus == 1) ? _('Disabled') : _('Enabled'));
|
||||
|
||||
this.ctx.setInternetStatus(true);
|
||||
|
||||
let serviceItems = '';
|
||||
if(this.ctx.currentAppMode === '2') {
|
||||
serviceItems = E([
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title' },
|
||||
_('Service')
|
||||
),
|
||||
E('div', { 'class': 'cbi-value-field' },
|
||||
this.ctx.serviceStatusLabel
|
||||
),
|
||||
]),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title' },
|
||||
_('Restart service')
|
||||
),
|
||||
E('div', { 'class': 'cbi-value-field' },
|
||||
this.ctx.serviceButton
|
||||
),
|
||||
]),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title' },
|
||||
_('Run service at startup')
|
||||
),
|
||||
E('div', { 'class': 'cbi-value-field' },
|
||||
this.ctx.initButton
|
||||
),
|
||||
]),
|
||||
]);
|
||||
};
|
||||
|
||||
let internetStatus = (this.ctx.currentAppMode !== '0') ?
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title' },
|
||||
_('Internet status')
|
||||
),
|
||||
E('div', { 'class': 'cbi-value-field' }, [
|
||||
this.ctx.inetStatusLabel,
|
||||
(!this.ctx.inetStatus) ? this.ctx.inetStatusSpinner : '',
|
||||
]),
|
||||
])
|
||||
: '';
|
||||
|
||||
return E('div', { 'class': 'cbi-section fade-in' },
|
||||
E('div', { 'class': 'cbi-section-node' }, [
|
||||
internetStatus,
|
||||
serviceItems,
|
||||
])
|
||||
);
|
||||
},
|
||||
}),
|
||||
|
||||
servicePoll: function() {
|
||||
return Promise.all([
|
||||
fs.exec(this.execPath, [ 'status' ]),
|
||||
fs.exec(this.execPath, [ 'inet-status' ]),
|
||||
]).then(stat => {
|
||||
let curAppStatus = (stat[0].code === 0) ? stat[0].stdout.trim() : null;
|
||||
let curInetStatus = (stat[1].code === 0) ? stat[1].stdout.trim() : null;
|
||||
|
||||
if(this.inetStatus === curInetStatus && this.appStatus === curAppStatus) {
|
||||
return;
|
||||
};
|
||||
this.appStatus = curAppStatus;
|
||||
this.inetStatus = curInetStatus;
|
||||
this.setInternetStatus();
|
||||
}).catch(e => {
|
||||
this.appStatus = 'stoped';
|
||||
this.inetStatus = null;
|
||||
});
|
||||
},
|
||||
|
||||
uiPoll: function() {
|
||||
let curInetStatus = null;
|
||||
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, [ 'inet-status' ]).then(res => {
|
||||
this.uiPollState = (res.code === 0 && res.stdout.trim() === 'up') ? 0 : 1;
|
||||
|
||||
if(this.uiPollState === 0) {
|
||||
curInetStatus = 'up';
|
||||
} else {
|
||||
curInetStatus = 'down';
|
||||
};
|
||||
|
||||
if(this.inetStatus !== curInetStatus) {
|
||||
this.inetStatus = (this.currentAppMode === '0') ? null : curInetStatus;
|
||||
this.setInternetStatus();
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
fs.exec(this.execPath, [ 'status' ]),
|
||||
fs.exec(this.initPath, [ 'enabled' ]),
|
||||
uci.load('internet-detector'),
|
||||
]).catch(e => {
|
||||
ui.addNotification(null, E('p', _('An error has occurred') + ': %s'.format(e.message)));
|
||||
});
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
if(!data) {
|
||||
return;
|
||||
};
|
||||
this.appStatus = (data[0].code === 0) ? data[0].stdout.trim() : null;
|
||||
this.initStatus = data[1].code;
|
||||
this.currentAppMode = uci.get('internet-detector', 'config', 'mode');
|
||||
this.uiCheckIntervalUp = Number(uci.get('internet-detector', 'config', 'ui_interval_up'));
|
||||
this.uiCheckIntervalDown = Number(uci.get('internet-detector', 'config', 'ui_interval_down'));
|
||||
|
||||
let upScriptEditDialog = new this.fileEditDialog(
|
||||
this.upScriptPath,
|
||||
_('up-script'),
|
||||
_('Shell commands that run when connected to the Internet'),
|
||||
);
|
||||
let downScriptEditDialog = new this.fileEditDialog(
|
||||
this.downScriptPath,
|
||||
_('down-script'),
|
||||
_('Shell commands to run when disconnected from the Internet'),
|
||||
);
|
||||
let runScriptEditDialog = new this.fileEditDialog(
|
||||
this.runScriptPath,
|
||||
_('run-script'),
|
||||
_("Shell commands that are executed every time the Internet is checked for availability"),
|
||||
);
|
||||
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('internet-detector', _('Internet detector'),
|
||||
_('Checking Internet availability.'));
|
||||
|
||||
s = m.section(form.NamedSection, 'config');
|
||||
s.anonymous = true;
|
||||
s.addremove = false;
|
||||
|
||||
s.tab('main_settings', _('Main settings'));
|
||||
|
||||
// service section
|
||||
o = s.taboption('main_settings', this.CBIBlockService, '_dummy_service');
|
||||
o.ctx = this;
|
||||
|
||||
// mode
|
||||
o = s.taboption('main_settings', form.ListValue,
|
||||
'mode', _('Internet detector mode'));
|
||||
o.value('0', _('Disabled'));
|
||||
o.value('1', _('Web UI only'));
|
||||
o.value('2', _('Service'));
|
||||
o.description = '%s;<br>%s;<br>%s;'.format(
|
||||
_('Disabled: detector is completely off'),
|
||||
_('Web UI only: detector works only when the Web UI is open (UI detector)'),
|
||||
_('Service: detector always runs as a system service')
|
||||
);
|
||||
|
||||
// hosts
|
||||
o = s.taboption('main_settings', form.DynamicList,
|
||||
'hosts', _('Hosts'));
|
||||
o.description = _('Hosts to check Internet availability. Hosts are polled (in list order) until at least one of them responds');
|
||||
o.datatype = 'or(host,hostport)';
|
||||
|
||||
// check_type
|
||||
o = s.taboption('main_settings', form.ListValue,
|
||||
'check_type', _('Check type'));
|
||||
o.description = _('Host availability check type');
|
||||
o.value(0, _('Ping host'));
|
||||
o.value(1, _('TCP port connection'));
|
||||
|
||||
// tcp_port
|
||||
o = s.taboption('main_settings', form.Value,
|
||||
'tcp_port', _('TCP port'));
|
||||
o.description = _('Default port value for TCP connections');
|
||||
o.rmempty = false;
|
||||
o.datatype = "port";
|
||||
|
||||
s.tab('ui_detector_configuration', _('UI detector configuration'));
|
||||
|
||||
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);
|
||||
|
||||
// ui_interval_up
|
||||
o = s.taboption('ui_detector_configuration', form.ListValue,
|
||||
'ui_interval_up', _('Alive interval'));
|
||||
o.description = _('Hosts polling interval when the Internet is up');
|
||||
makeUIIntervalOptions(o);
|
||||
|
||||
// ui_interval_down
|
||||
o = s.taboption('ui_detector_configuration', form.ListValue,
|
||||
'ui_interval_down', _('Dead interval'));
|
||||
o.description = _('Hosts polling interval when the Internet is down');
|
||||
makeUIIntervalOptions(o);
|
||||
|
||||
// ui_connection_attempts
|
||||
o = s.taboption('ui_detector_configuration', form.ListValue,
|
||||
'ui_connection_attempts', _('Connection attempts'));
|
||||
o.description = _('Maximum number of attempts to connect to each host');
|
||||
o.value(1);
|
||||
o.value(2);
|
||||
o.value(3);
|
||||
|
||||
// ui_connection_timeout
|
||||
o = s.taboption('ui_detector_configuration', form.ListValue,
|
||||
'ui_connection_timeout', _('Connection timeout'));
|
||||
o.description = _('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'));
|
||||
|
||||
s.tab('service_configuration', _('Service configuration'));
|
||||
|
||||
// enable_logger
|
||||
o = s.taboption('service_configuration', form.Flag,
|
||||
'enable_logger', _('Enable logging'));
|
||||
o.description = _('Write messages to the system log');
|
||||
o.rmempty = false;
|
||||
|
||||
// enable_up_script
|
||||
o = s.taboption('service_configuration', form.Flag,
|
||||
'enable_up_script', _('Enable up-script'));
|
||||
o.description = _('Execute commands when the Internet is connected');
|
||||
o.rmempty = false;
|
||||
|
||||
// up_script edit dialog
|
||||
o = s.taboption('service_configuration', form.Button,
|
||||
'_up_script_btn', _('Edit up-script'));
|
||||
o.onclick = () => upScriptEditDialog.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
|
||||
// enable_down_script
|
||||
o = s.taboption('service_configuration', form.Flag,
|
||||
'enable_down_script', _('Enable down-script'));
|
||||
o.description = _('Execute commands when the Internet is disconnected');
|
||||
o.rmempty = false;
|
||||
|
||||
// down_script edit dialog
|
||||
o = s.taboption('service_configuration', form.Button,
|
||||
'_down_script_btn', _('Edit down-script'));
|
||||
o.onclick = () => downScriptEditDialog.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
|
||||
// enable_run_script
|
||||
o = s.taboption('service_configuration', form.Flag,
|
||||
'enable_run_script', _('Enable run-script'));
|
||||
o.description = _('Execute commands every time the Internet is checked for availability');
|
||||
o.rmempty = false;
|
||||
|
||||
// run_script edit dialog
|
||||
o = s.taboption('service_configuration', form.Button,
|
||||
'_run_script_btn', _('Edit run-script'));
|
||||
o.onclick = () => runScriptEditDialog.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
|
||||
function makeIntervalOptions(list) {
|
||||
list.value(2, '2 ' + _('sec'));
|
||||
list.value(5, '5 ' + _('sec'));
|
||||
list.value(10, '10 ' + _('sec'));
|
||||
list.value(15, '15 ' + _('sec'));
|
||||
list.value(20, '20 ' + _('sec'));
|
||||
list.value(25, '25 ' + _('sec'));
|
||||
list.value(30, '30 ' + _('sec'));
|
||||
list.value(60, '1 ' + _('min'));
|
||||
list.value(120, '2 ' + _('min'));
|
||||
list.value(300, '5 ' + _('min'));
|
||||
list.value(600, '10 ' + _('min'));
|
||||
}
|
||||
|
||||
// interval_up
|
||||
o = s.taboption('service_configuration', form.ListValue,
|
||||
'interval_up', _('Alive interval'));
|
||||
o.description = _('Hosts polling interval when the Internet is up');
|
||||
makeIntervalOptions(o);
|
||||
|
||||
// interval_down
|
||||
o = s.taboption('service_configuration', form.ListValue,
|
||||
'interval_down', _('Dead interval'));
|
||||
o.description = _('Hosts polling interval when the Internet is down');
|
||||
makeIntervalOptions(o);
|
||||
|
||||
// connection_attempts
|
||||
o = s.taboption('service_configuration', form.ListValue,
|
||||
'connection_attempts', _('Connection attempts'));
|
||||
o.description = _('Maximum number of attempts to connect to each host');
|
||||
o.value(1);
|
||||
o.value(2);
|
||||
o.value(3);
|
||||
o.value(4);
|
||||
o.value(5);
|
||||
|
||||
// connection_timeout
|
||||
o = s.taboption('service_configuration', form.ListValue,
|
||||
'connection_timeout', _('Connection timeout'));
|
||||
o.description = _('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.value(4, "4 " + _('sec'));
|
||||
o.value(5, "5 " + _('sec'));
|
||||
o.value(6, "6 " + _('sec'));
|
||||
o.value(7, "7 " + _('sec'));
|
||||
o.value(8, "8 " + _('sec'));
|
||||
o.value(9, "9 " + _('sec'));
|
||||
o.value(10, "10 " + _('sec'));
|
||||
|
||||
if(this.currentAppMode !== '0') {
|
||||
L.Poll.add(
|
||||
L.bind((this.currentAppMode === '2') ? this.servicePoll : this.uiPoll, this),
|
||||
this.pollInterval
|
||||
);
|
||||
};
|
||||
|
||||
let mapPromise = m.render();
|
||||
mapPromise.then(node => node.classList.add('fade-in'));
|
||||
return mapPromise;
|
||||
},
|
||||
|
||||
handleSaveApply: function(ev, mode) {
|
||||
return this.handleSave(ev).then(() => {
|
||||
ui.changes.apply(mode == '0');
|
||||
window.setTimeout(() => this.serviceRestart(), 3000);
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require uci';
|
||||
|
||||
return L.Class.extend({
|
||||
title : _('Internet'),
|
||||
execPath : '/usr/bin/internet-detector',
|
||||
inetStatus : null,
|
||||
|
||||
load: async function() {
|
||||
if(!(
|
||||
'uiCheckIntervalUp' in window &&
|
||||
'uiCheckIntervalDown' in window &&
|
||||
'currentAppMode' in window
|
||||
)) {
|
||||
await uci.load('internet-detector').then(data => {
|
||||
window.uiCheckIntervalUp = Number(uci.get('internet-detector', 'config', 'ui_interval_up'));
|
||||
window.uiCheckIntervalDown = Number(uci.get('internet-detector', 'config', 'ui_interval_down'));
|
||||
window.currentAppMode = uci.get('internet-detector', 'config', 'mode');
|
||||
}).catch(e => {});
|
||||
};
|
||||
|
||||
if(window.currentAppMode === '1' || window.currentAppMode === '2') {
|
||||
window.internetDetectorCounter = ('internetDetectorCounter' in window) ?
|
||||
++window.internetDetectorCounter : 0;
|
||||
|
||||
if(!('internetDetectorState' in window)) {
|
||||
window.internetDetectorState = 2;
|
||||
};
|
||||
|
||||
if(window.currentAppMode === '1' && (
|
||||
(window.internetDetectorState === 0 && window.internetDetectorCounter % window.uiCheckIntervalUp) ||
|
||||
(window.internetDetectorState === 1 && window.internetDetectorCounter % window.uiCheckIntervalDown)
|
||||
)) {
|
||||
return;
|
||||
};
|
||||
|
||||
window.internetDetectorCounter = 0;
|
||||
|
||||
return L.resolveDefault(fs.exec(this.execPath, [ 'inet-status' ]), null);
|
||||
}
|
||||
else {
|
||||
window.internetDetectorState = 2;
|
||||
};
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
if(window.currentAppMode === '0') {
|
||||
return
|
||||
};
|
||||
|
||||
if(data) {
|
||||
this.inetStatus = (data.code === 0) ? data.stdout.trim() : null;
|
||||
if(this.inetStatus === 'up') {
|
||||
window.internetDetectorState = 0;
|
||||
}
|
||||
else if(this.inetStatus === 'down') {
|
||||
window.internetDetectorState = 1;
|
||||
}
|
||||
else {
|
||||
window.internetDetectorState = 2;
|
||||
};
|
||||
};
|
||||
|
||||
let internetStatus = E('span', { 'class': 'label' });
|
||||
|
||||
if(window.internetDetectorState === 0) {
|
||||
internetStatus.textContent = _('Connected');
|
||||
internetStatus.style.background = '#46a546';
|
||||
}
|
||||
else if(window.internetDetectorState === 1) {
|
||||
internetStatus.textContent = _('Disconnected');
|
||||
internetStatus.style.background = '#ff6c74';
|
||||
}
|
||||
else {
|
||||
internetStatus.textContent = _('Undefined');
|
||||
internetStatus.background = '#cccccc';
|
||||
};
|
||||
|
||||
return E('div', {
|
||||
'class': 'cbi-section',
|
||||
'style': 'margin-bottom:1em',
|
||||
}, internetStatus);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
module('luci.controller.internet-detector', package.seeall)
|
||||
|
||||
function index()
|
||||
if nixio.fs.access('/usr/bin/internet-detector', 'x') then
|
||||
entry({'admin', 'services', 'internet-detector'}, view('internet-detector'), _('Internet detector'), 10).acl_depends = { 'luci-app-internet-detector' }
|
||||
end
|
||||
end
|
||||
217
luci-app-internet-detector/po/ru/internet-detector.po
Normal file
217
luci-app-internet-detector/po/ru/internet-detector.po
Normal file
@@ -0,0 +1,217 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.4.2\n"
|
||||
"Last-Translator: \n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
|
||||
"Language: ru\n"
|
||||
|
||||
msgid "Alive interval"
|
||||
msgstr "Интервал при подключении"
|
||||
|
||||
msgid "An error has occurred"
|
||||
msgstr "Произошла ошибка"
|
||||
|
||||
msgid "Check type"
|
||||
msgstr "Тип проверки"
|
||||
|
||||
msgid "Checking Internet availability."
|
||||
msgstr "Проверка доступности Интернет."
|
||||
|
||||
msgid "Command failed"
|
||||
msgstr "Команда не выполнена"
|
||||
|
||||
msgid "Connected"
|
||||
msgstr "Подключен"
|
||||
|
||||
msgid "Connection timeout"
|
||||
msgstr "Таймаут соединения"
|
||||
|
||||
msgid "Connections"
|
||||
msgstr "Подключения"
|
||||
|
||||
msgid "Contents have been saved."
|
||||
msgstr "Содержимое сохранено."
|
||||
|
||||
msgid "Dead interval"
|
||||
msgstr "Интервал при отключении"
|
||||
|
||||
msgid "Default port value for TCP connections"
|
||||
msgstr "Стандартное значение порта для TCP-подключений"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "Отключен"
|
||||
|
||||
msgid "Disabled: detector is completely off"
|
||||
msgstr "Отключен: детектор полностью выключен"
|
||||
|
||||
msgid "Disconnected"
|
||||
msgstr "Отключен"
|
||||
|
||||
msgid "Dismiss"
|
||||
msgstr "Отмена"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "Изменить"
|
||||
|
||||
msgid "Edit down-script"
|
||||
msgstr "Изменить down-script"
|
||||
|
||||
msgid "Edit run-script"
|
||||
msgstr "Изменить run-script"
|
||||
|
||||
msgid "Edit up-script"
|
||||
msgstr "Изменить up-script"
|
||||
|
||||
msgid "Enable down-script"
|
||||
msgstr "Включить down-script"
|
||||
|
||||
msgid "Enable logging"
|
||||
msgstr "Включить логирование"
|
||||
|
||||
msgid "Enable run-script"
|
||||
msgstr "Включить run-script"
|
||||
|
||||
msgid "Enable up-script"
|
||||
msgstr "Включить up-script"
|
||||
|
||||
msgid "Enabled"
|
||||
msgstr "Включен"
|
||||
|
||||
msgid "Execute commands every time the Internet is checked for availability"
|
||||
msgstr "Выполнение команд при каждой проверке доступности Интернет"
|
||||
|
||||
msgid "Execute commands when the Internet is connected"
|
||||
msgstr "Выполнение команд при подключении к Интернет"
|
||||
|
||||
msgid "Execute commands when the Internet is disconnected"
|
||||
msgstr "Выполнение команд при отключении от Интернет"
|
||||
|
||||
msgid "Failed to execute \"%s %s\": %s"
|
||||
msgstr "Не удалось выполнить \"%s %s\": %s"
|
||||
|
||||
msgid "Host availability check type"
|
||||
msgstr "Тип проверки доступности хоста"
|
||||
|
||||
msgid "Hosts"
|
||||
msgstr "Хосты"
|
||||
|
||||
msgid "Hosts polling interval when the Internet is down"
|
||||
msgstr "Интервал опроса хостов если Интернет не доступен"
|
||||
|
||||
msgid "Hosts polling interval when the Internet is up"
|
||||
msgstr "Интервал опроса хостов если Интернет доступен"
|
||||
|
||||
msgid "Hosts to check Internet availability. Hosts are polled (in list order) until at least one of them responds"
|
||||
msgstr "Хосты для проверки доступности Интернет. Хосты опрашиваются (в порядке списка) до тех пор, пока хотя бы один из них не ответит"
|
||||
|
||||
msgid "Internet"
|
||||
msgstr "Интернет"
|
||||
|
||||
msgid "Internet detector"
|
||||
msgstr "Интернет-детектор"
|
||||
|
||||
msgid "Internet detector mode"
|
||||
msgstr "Режим интернет-детектора"
|
||||
|
||||
msgid "Internet status"
|
||||
msgstr "Статус Интернет"
|
||||
|
||||
msgid "Loading"
|
||||
msgstr "Загрузка"
|
||||
|
||||
msgid "Main settings"
|
||||
msgstr "Основные настройки"
|
||||
|
||||
msgid "Maximum timeout for waiting for a response from the host"
|
||||
msgstr "Маскимальный таймаут ожидания ответа от хоста"
|
||||
|
||||
msgid "Maximum number of attempts to connect to each host"
|
||||
msgstr "Максимальное количество попыток подключения к каждому хосту"
|
||||
|
||||
msgid "Ping host"
|
||||
msgstr "Пинг хоста"
|
||||
|
||||
msgid "Restart"
|
||||
msgstr "Перезапуск"
|
||||
|
||||
msgid "Restart service"
|
||||
msgstr "Перезапуск службы"
|
||||
|
||||
msgid "Run service at startup"
|
||||
msgstr "Запуск службы при старте"
|
||||
|
||||
msgid "Running"
|
||||
msgstr "Выполняется"
|
||||
|
||||
msgid "Save"
|
||||
msgstr "Сохранить"
|
||||
|
||||
msgid "Service"
|
||||
msgstr "Служба"
|
||||
|
||||
msgid "Service configuration"
|
||||
msgstr "Конфигурация службы"
|
||||
|
||||
msgid "Service: detector always runs as a system service"
|
||||
msgstr "Служба: детектор работает постоянно, как системная служба"
|
||||
|
||||
msgid "Shell commands that are executed every time the Internet is checked for availability"
|
||||
msgstr "Команды shell выполняемые при каждой проверке доступности Интернет"
|
||||
|
||||
msgid "Shell commands that run when connected to the Internet"
|
||||
msgstr "Команды shell выполняемые при подключении к Интернет"
|
||||
|
||||
msgid "Shell commands to run when disconnected from the Internet"
|
||||
msgstr "Команды shell выполняемые при отключении от Интернет"
|
||||
|
||||
msgid "Stopped"
|
||||
msgstr "Остановлена"
|
||||
|
||||
msgid "TCP port"
|
||||
msgstr "TCP-порт"
|
||||
|
||||
msgid "TCP port connection"
|
||||
msgstr "Подключение к TCP-порту"
|
||||
|
||||
msgid "UI detector configuration"
|
||||
msgstr "Конфигурация UI детектора"
|
||||
|
||||
msgid "Unable to read the contents"
|
||||
msgstr "Невозможно прочитать содержимое"
|
||||
|
||||
msgid "Unable to save the contents"
|
||||
msgstr "Невозможно сохранить содержимое"
|
||||
|
||||
msgid "Undefined"
|
||||
msgstr "Неопределён"
|
||||
|
||||
msgid "Web UI only"
|
||||
msgstr "Только web-интерфейс"
|
||||
|
||||
msgid "Web UI only: detector works only when the Web UI is open (UI detector)"
|
||||
msgstr "Только web-интерфейс: детектор работает только в web-интерфейсе (UI детектор)"
|
||||
|
||||
msgid "Write messages to the system log"
|
||||
msgstr "Записывать сообщения в системный журнал"
|
||||
|
||||
msgid "down-script"
|
||||
msgstr ""
|
||||
|
||||
msgid "min"
|
||||
msgstr "мин"
|
||||
|
||||
msgid "run-script"
|
||||
msgstr ""
|
||||
|
||||
msgid "sec"
|
||||
msgstr "сек"
|
||||
|
||||
msgid "up-script"
|
||||
msgstr ""
|
||||
203
luci-app-internet-detector/po/templates/internet-detector.pot
Normal file
203
luci-app-internet-detector/po/templates/internet-detector.pot
Normal file
@@ -0,0 +1,203 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid "Alive interval"
|
||||
msgstr ""
|
||||
|
||||
msgid "An error has occurred"
|
||||
msgstr ""
|
||||
|
||||
msgid "Check type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checking Internet availability."
|
||||
msgstr ""
|
||||
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Connected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Connection timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "Connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Contents have been saved."
|
||||
msgstr ""
|
||||
|
||||
msgid "Dead interval"
|
||||
msgstr ""
|
||||
|
||||
msgid "Default port value for TCP connections"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disabled: detector is completely off"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disconnected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dismiss"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit down-script"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit run-script"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit up-script"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable down-script"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable logging"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable run-script"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable up-script"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "Execute commands every time the Internet is checked for availability"
|
||||
msgstr ""
|
||||
|
||||
msgid "Execute commands when the Internet is connected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Execute commands when the Internet is disconnected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to execute \"%s %s\": %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Host availability check type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hosts"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hosts polling interval when the Internet is down"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hosts polling interval when the Internet is up"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hosts to check Internet availability. Hosts are polled (in list order) until at least one of them responds"
|
||||
msgstr ""
|
||||
|
||||
msgid "Internet"
|
||||
msgstr ""
|
||||
|
||||
msgid "Internet detector"
|
||||
msgstr ""
|
||||
|
||||
msgid "Internet detector mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Internet status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
msgid "Main settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum timeout for waiting for a response from the host"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum number of attempts to connect to each host"
|
||||
msgstr ""
|
||||
|
||||
msgid "Ping host"
|
||||
msgstr ""
|
||||
|
||||
msgid "Restart"
|
||||
msgstr ""
|
||||
|
||||
msgid "Restart service"
|
||||
msgstr ""
|
||||
|
||||
msgid "Run service at startup"
|
||||
msgstr ""
|
||||
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
msgid "Service"
|
||||
msgstr ""
|
||||
|
||||
msgid "Service configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Service: detector always runs as a system service"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shell commands that are executed every time the Internet is checked for availability"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shell commands that run when connected to the Internet"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shell commands to run when disconnected from the Internet"
|
||||
msgstr ""
|
||||
|
||||
msgid "Stopped"
|
||||
msgstr ""
|
||||
|
||||
msgid "TCP port"
|
||||
msgstr ""
|
||||
|
||||
msgid "TCP port connection"
|
||||
msgstr ""
|
||||
|
||||
msgid "UI detector configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to read the contents"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to save the contents"
|
||||
msgstr ""
|
||||
|
||||
msgid "Undefined"
|
||||
msgstr ""
|
||||
|
||||
msgid "Web UI only"
|
||||
msgstr ""
|
||||
|
||||
msgid "Web UI only: detector works only when the Web UI is open (UI detector)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Write messages to the system log"
|
||||
msgstr ""
|
||||
|
||||
msgid "down-script"
|
||||
msgstr ""
|
||||
|
||||
msgid "min"
|
||||
msgstr ""
|
||||
|
||||
msgid "run-script"
|
||||
msgstr ""
|
||||
|
||||
msgid "sec"
|
||||
msgstr ""
|
||||
|
||||
msgid "up-script"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"admin/services/internet-detector": {
|
||||
"title": "Internet detector",
|
||||
"order": 80,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "internet-detector"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-internet-detector" ],
|
||||
"fs": {
|
||||
"/usr/bin/internet-detector": "executable"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"luci-app-internet-detector": {
|
||||
"description": "Grant access to internet-detector procedures",
|
||||
"read": {
|
||||
"file": {
|
||||
"/etc/internet-detector/up-script": [ "read" ],
|
||||
"/etc/internet-detector/down-script": [ "read" ],
|
||||
"/etc/internet-detector/run-script": [ "read" ],
|
||||
"/etc/init.d/internet-detector": [ "exec" ],
|
||||
"/usr/bin/internet-detector*": [ "exec" ]
|
||||
},
|
||||
"uci": [ "internet-detector" ],
|
||||
"ubus": {
|
||||
"luci": [ "setInitAction" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"file": {
|
||||
"/etc/internet-detector/up-script": [ "write" ],
|
||||
"/etc/internet-detector/down-script": [ "write" ],
|
||||
"/etc/internet-detector/run-script": [ "write" ]
|
||||
},
|
||||
"uci": [ "internet-detector" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
packages/19.07/internet-detector_0.3.0-1_all.ipk
vendored
Normal file
BIN
packages/19.07/internet-detector_0.3.0-1_all.ipk
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
packages/19.07/luci-app-internet-detector_0.3.0-1_all.ipk
vendored
Normal file
BIN
packages/19.07/luci-app-internet-detector_0.3.0-1_all.ipk
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
packages/19.07/luci-i18n-internet-detector-ru_0.3.0-1_all.ipk
vendored
Normal file
BIN
packages/19.07/luci-i18n-internet-detector-ru_0.3.0-1_all.ipk
vendored
Normal file
Binary file not shown.
@@ -1,21 +0,0 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
|
||||
msgid "Internet"
|
||||
msgstr "Интернет"
|
||||
|
||||
msgid "Internet connected"
|
||||
msgstr "Интернет подключен"
|
||||
|
||||
msgid "Internet disconnected"
|
||||
msgstr "Интернет отключен"
|
||||
@@ -1,11 +0,0 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid "Internet"
|
||||
msgstr ""
|
||||
|
||||
msgid "Internet connected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Internet disconnected"
|
||||
msgstr ""
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"luci-app-internet-detector": {
|
||||
"description": "Grant access to internet-detector procedures",
|
||||
"read": {
|
||||
"file": {
|
||||
"/bin/ping -c 1 -W 1 [a-z0-9:.]*": [ "exec" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 180 KiB |
BIN
screenshots/02.jpg
Normal file
BIN
screenshots/02.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 134 KiB |
BIN
screenshots/03.jpg
Normal file
BIN
screenshots/03.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 199 KiB |
BIN
screenshots/04.jpg
Normal file
BIN
screenshots/04.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 196 KiB |
BIN
screenshots/internet-led.jpg
Normal file
BIN
screenshots/internet-led.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 170 KiB |
Reference in New Issue
Block a user