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
|
1. Definitions.
|
||||||
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:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
copies or substantial portions of the Software.
|
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
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
the copyright owner that is granting the License.
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
other entities that control, are controlled by, or are under common
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
control with that entity. For the purposes of this definition,
|
||||||
SOFTWARE.
|
"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 OpenWrt.
|
||||||
Internet detector for the LuCI status page (OpenWrt webUI).
|
Checking Internet availability.
|
||||||
|
|
||||||
OpenWrt >= 19.07.
|
OpenWrt >= 19.07.
|
||||||
|
|
||||||
|
Dependences: lua, luci-lib-nixio, libuci-lua
|
||||||
|
|
||||||
**Installation notes:**
|
**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
|
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/luci-app-internet-detector_0.2-1_all.ipk
|
opkg install /tmp/internet-detector_0.3.0-1_all.ipk
|
||||||
rm /tmp/luci-app-internet-detector_0.2-1_all.ipk
|
rm /tmp/internet-detector_0.3.0-1_all.ipk
|
||||||
/etc/init.d/rpcd restart
|
|
||||||
|
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:**
|
**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
|
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.2-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.2-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