🎁 Sync 2026-06-01 20:30:44

This commit is contained in:
github-actions[bot] committed 2026-06-01 20:30:44 +08:00
1 parent d01c61d2c9
commit f24353a35e
26 files changed
+606 -140

No files matched your search

+55
View File
@@ -0,0 +1,55 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gecoosac
PKG_VERSION:=2.2.20251015
PKG_RELEASE:=1
ifeq ($(ARCH),aarch64)
PKG_ARCH:=ac_linux_arm64
PKG_HASH:=skip
else ifeq ($(ARCH),arm)
PKG_ARCH:=ac_linux_arm
PKG_HASH:=skip
else ifeq ($(ARCH),mips)
PKG_ARCH:=ac_linux_mips
PKG_HASH:=skip
else ifeq ($(ARCH),mipsel)
PKG_ARCH:=ac_linux_mipsle
PKG_HASH:=skip
else ifeq ($(ARCH),i386)
PKG_ARCH:=ac_linux_386
PKG_HASH:=skip
else ifeq ($(ARCH),x86_64)
PKG_ARCH:=ac_linux_amd64
PKG_HASH:=skip
else
PKG_HASH:=skip
endif
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(ARCH_PACKAGES)
PKG_SOURCE_URL:=https://github.com/laipeng668/luci-app-gecoosac/releases/download/V$(PKG_VERSION)/$(PKG_ARCH)?
UNPACK_CMD=$(CP) $(DL_DIR)/$(PKG_SOURCE) $(PKG_BUILD_DIR)/$(PKG_NAME)
PKG_LICENSE:=AGPL-3.0-only
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=lwb1978 <lwb1978@gmail.com>
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=net
CATEGORY:=Network
TITLE:=gecoosac server (version $(PKG_VERSION))
URL:=http://www.cnrouter.com/
DEPENDS:=@(aarch64||arm||i386||mips||mipsel||x86_64)
endef
define Build/Compile
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/$(PKG_NAME) $(1)/usr/bin
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
+22
View File
@@ -0,0 +1,22 @@
# Copyright (C) 2024 Openwrt.org
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-gecoosac
PKG_VERSION:=2.2
PKG_RELEASE:=1
LUCI_TITLE:=LuCI Support for gecoosac
LUCI_DEPENDS:=+luci-compat +gecoosac
LUCI_PKGARCH:=all
define Package/$(PKG_NAME)/conffiles
/etc/gecoosac
endef
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -0,0 +1,29 @@
module("luci.controller.gecoosac", package.seeall)
function index()
if not nixio.fs.access("/etc/config/gecoosac") then
return
end
local page
page = entry({"admin", "services", "gecoosac"}, cbi("gecoosac"), _("Gecoos AC"), 100)
page.dependent = true
page = entry({"admin", "services", "gecoosac", "status"}, call("act_status"))
page.leaf = true
end
function act_status()
local e = {}
local binpath = "/usr/bin/gecoosac"
e = {
running = luci.sys.call("pgrep " .. binpath .. " >/dev/null") == 0
}
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
function clear_upload()
local path = "/tmp/gecoosac/upload/"
luci.sys.call("rm -rf " .. path .. "/*")
luci.http.status(200, "OK")
end
@@ -0,0 +1,74 @@
function program_exists(program)
local handle = io.popen('which ' .. program)
local result = handle:read('*a')
handle:close()
return result ~= ''
end
if program_exists("/usr/bin/gecoosac") then
m = Map("gecoosac", translate("Gecoos AC"), translate("Batch management Gecoos AP, Default password: admin") .. "<br>" .. translatef("The current AC version %s, only supports AP 7.6 and above.","2.2"))
else
m = Map("gecoosac", translate("Gecoos AC"), translate("Batch management Gecoos AP, Default password: admin") .. "<br>" .. translate("The AC program does not exist, please check."))
end
m:section(SimpleSection).template = "gecoosac/gecoosac_status"
s = m:section(TypedSection, "gecoosac", translate("Global Settings"))
s.addremove = false
s.anonymous = true
enable = s:option(Flag, "enabled", translate("Enabled AC"))
enable.rmempty = false
o = s:option(Value, "port", translate("Set interface port"))
o.placeholder = 60650
o.default = 60650
o.datatype = "port"
o.rmempty = false
o = s:option(Flag, "isonlyoneprot", translate("Single Port Mode"), translate("Do not enable the independent management port, only use one port for management."))
o.default = 1
o.rmempty = false
o = s:option(Value, "m_port", translate("Set management port"))
o.placeholder = 8080
o.default = 8080
o.datatype = "port"
o:depends("isonlyoneprot", false)
o = s:option(Flag, "https", translate("Enable HTTPS service"), translate("A certificate file must be specified, otherwise it will fail to start."))
o.default = 0
o:depends("isonlyoneprot", false)
o = s:option(Value, "crt_file", translate("Specify crt certificate file"))
o.placeholder = "/etc/gecoosac/tls/1.crt"
o.default = "/etc/gecoosac/tls/1.crt"
o:depends("https", true)
o = s:option(Value, "key_file", translate("Specify key certificate file"))
o.placeholder = "/etc/gecoosac/tls/1.key"
o.default = "/etc/gecoosac/tls/1.key"
o:depends("https", true)
upload_dir = s:option(Value, "upload_dir", translate("Upload dir path"), translate("The path to upload AP upgrade firmware"))
upload_dir.placeholder = "/tmp/gecoosac/upload/"
upload_dir.default = "/tmp/gecoosac/upload/"
upload_dir.rmempty = false
db_dir = s:option(Value, "db_dir", translate("Database dir path"), translate("The path to store the config database"))
db_dir.placeholder = "/etc/gecoosac/"
db_dir.default = "/etc/gecoosac/"
db_dir.rmempty = false
log = s:option(Flag, "log", translate("Enable Log"))
log.default = 1
log.rmempty = false
clear_upload = s:option(Button, "clear_upload", translate("Clear Upload Directory"))
clear_upload.inputstyle = "remove"
clear_upload.write = function(self, section)
local path = upload_dir:formvalue(section) or "/tmp/gecoosac/upload/"
luci.sys.call("rm -rf " .. path .. "/*")
end
return m
@@ -0,0 +1,44 @@
<%
local uci=require"luci.model.uci".cursor()
local isonlyoneprot = uci:get("gecoosac", "config", "isonlyoneprot") or "1"
local https = uci:get("gecoosac", "config", "https") or "0"
local port = uci:get("gecoosac", "config", "port") or "60650"
local m_port = uci:get("gecoosac", "config", "m_port") or "8080"
local http = "http://"
if isonlyoneprot == "0" then
port = m_port
if https == "1" then
http = "https://"
end
end
-%>
<script type="text/javascript">//<![CDATA[
var Port = "<%=port%>"
var Http = "<%=http%>"
XHR.poll(3, '<%=url([[admin]], [[services]], [[gecoosac]], [[status]])%>', null,
function(x, data) {
var tb = document.getElementById('gecoosac_status');
if (data && tb) {
if (data.running) {
var links = '<font color=green><%:The GecoosAC service is running.%></font>&nbsp;&nbsp;<input class="cbi-button cbi-button-reload " type="button" value="<%:Open the mgmt page%>" onclick="openClient();" />';
tb.innerHTML = links;
} else {
tb.innerHTML = '<font color=red><%:The GecoosAC service is not running.%></font>';
}
}
}
);
function openClient() {
var url = Http + window.location.hostname + ":" + Port;
window.open(url);
}
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="gecoosac_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>
+74
View File
@@ -0,0 +1,74 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Gecoos AC"
msgstr "集客AC控制器"
msgid "Batch management Gecoos AP, Default password: admin"
msgstr "批量集中管理集客 AP,默认密码:admin"
msgid "The current AC version %s, only supports AP 7.6 and above."
msgstr "当前 AC 版本 %s ,仅支持 AP 7.6 及以上版本。"
msgid "The AC program does not exist, please check."
msgstr "AC 程序不存在,请检查。"
msgid "Global Settings"
msgstr "全局设置"
msgid "Enabled AC"
msgstr "启用集客AC"
msgid "Set interface port"
msgstr "设置接口端口"
msgid "Single Port Mode"
msgstr "单端口模式"
msgid "Do not enable the independent management port, only use one port for management."
msgstr "不开启独立管理端口,只使用一个端口进行管理。"
msgid "Set management port"
msgstr "设置管理端口"
msgid "Enable HTTPS service"
msgstr "开启 HTTPS 服务"
msgid "A certificate file must be specified, otherwise it will fail to start."
msgstr "需要指定证书文件,否则开启失败。"
msgid "Specify crt certificate file"
msgstr "指定 crt 证书文件"
msgid "Specify key certificate file"
msgstr "指定 key 证书文件"
msgid "Upload dir path"
msgstr "上传目录"
msgid "The path to upload AP upgrade firmware"
msgstr "存放AP升级固件的路径"
msgid "Database dir path"
msgstr "数据库目录"
msgid "The path to store the config database"
msgstr "存放配置数据库的路径"
msgid "Enable Log"
msgstr "启用日志"
msgid "The GecoosAC service is running."
msgstr "集客AC控制器 运行中"
msgid "The GecoosAC service is not running."
msgstr "集客AC控制器 未运行"
msgid "Open the mgmt page"
msgstr "打开管理页面"
msgid "Collecting data..."
msgstr "收集数据..."
msgid "Clear Upload Directory"
msgstr "清理上传目录"
+1
View File
@@ -0,0 +1 @@
zh-cn
@@ -0,0 +1,8 @@
config gecoosac 'config'
option enabled '0'
option port '60650'
option isonlyoneprot '1'
option upload_dir '/tmp/gecoosac/upload/'
option db_dir '/etc/gecoosac/'
option log '0'
@@ -0,0 +1,45 @@
-----BEGIN CERTIFICATE-----
MIIDljCCAn6gAwIBAgIIeEgcfPZbDC0wDQYJKoZIhvcNAQELBQAwejELMAkGA1UE
BhMCQ04xFzAVBgNVBAoTDktleU1hbmFnZXIub3JnMTEwLwYDVQQLEyhLZXlNYW5h
Z2VyIFRlc3QgUm9vdCAtIEZvciBUZXN0IFVzZSBPbmx5MR8wHQYDVQQDExZLZXlN
YW5hZ2VyIFRlc3QgUlNBIENBMCAXDTI0MTIxMTA0MDUyNloYDzIxMjQxMjExMDQw
NTI2WjAZMQswCQYDVQQGEwJDTjEKMAgGA1UEAwwBKjCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBALM3K/5+yX6H9Dvq8TrKuk0KtFBqmDDP3lKXMpMkWjDn
4OtKGq4QtaPPJfGBjitYecyHLWbqGZlWU2H8ug/kL/PtAFgtTgiFvObePbP4VUVO
lICitAWCSMo7xsvt/XSGLOF3dV1tCoEcjK9GtJEMOyoqHMOgbuZ986wIR4LkgULz
3A2uC5abXP5Exa6phMCv3MojjaVgOFEIuC4371ZeLU/YXja9ssQ0Bs8PzOIjCP7i
pce94hYYSgAH3Ua1ZcWVZVEbCytoaOGWXkrhNTVZ+nAarlFY4fgN3yCi+YgFipKU
1H9oNr4PgLsxhtM3f19wwu9XNY1RF72cfRPS/mPy86sCAwEAAaN/MH0wDgYDVR0P
AQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4E
FgQUOW6TXeYQEZs1tMI05Vx2aSb9dk0wHwYDVR0jBBgwFoAUfzXMx0QcDHIhuynW
4rB+75TRqw8wDAYDVR0RBAUwA4IBKjANBgkqhkiG9w0BAQsFAAOCAQEAl63ixtfN
azLCIvIKGhjhqk2aU9NZtnsjYO729bNSk8SGtqVvCyXO8FJwdhoybtBlSkxjyvrb
nsR+QbeYGu0ZPsSsJJKuwlsM+ZoTOEGRQrhT7AkkXsNQHSalDCWczPW45xWHkg/s
P9xIGeySWMu4CJ1/eZYczT6bzS0eLd05camVCeU2E5N/M7hAO4SnK0agFZnaUEjp
13KZA3MfFNXZ2Q7na9CRjTbpqDCOpsLteP22fwUq7zutwvkQsQQYnQuVlpiUjSsV
MvY7oxkTi9M5BtowdEtVpWkVO2WOMs5dy2kxfgcIJ5KRZLmU+cPsWgsE76fErIYs
FU2leWlNpSvuMw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID2jCCAsKgAwIBAgIILz9r/y+4/ZgwDQYJKoZIhvcNAQELBQAwezELMAkGA1UE
BhMCQ04xFzAVBgNVBAoTDktleU1hbmFnZXIub3JnMTEwLwYDVQQLEyhLZXlNYW5h
Z2VyIFRlc3QgUm9vdCAtIEZvciBUZXN0IFVzZSBPbmx5MSAwHgYDVQQDExdLZXlN
YW5hZ2VyIFRlc3QgUm9vdCBDQTAeFw0yNDEyMTEwMzI3MzJaFw0zNDEyMTEwMzI3
MzJaMHoxCzAJBgNVBAYTAkNOMRcwFQYDVQQKEw5LZXlNYW5hZ2VyLm9yZzExMC8G
A1UECxMoS2V5TWFuYWdlciBUZXN0IFJvb3QgLSBGb3IgVGVzdCBVc2UgT25seTEf
MB0GA1UEAxMWS2V5TWFuYWdlciBUZXN0IFJTQSBDQTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMa7Tdag8tcvEwqRMD+gVEd4G3XeQyYzSYGLAa9DGji4
qUDUcBi0Ml2AndHtNNPcAk4vBUsZ10jjpNtOWtsy+r8VivBIlM5PE38JLhlZWao9
JvT1duxfxss+Q7xdT1oO++CDN/6avPXmDD1dmbTD09/cxcPW8M/3aaHfbI5GWxlr
ZLu8N50yQQxiXLEJad3n7fPjAosGroqa5FcsJ2vDQHuK13mpZG1eBoNZe6/OAzAz
yYkUKyixpS4xc8Agb9BCl7M9Tvkr8VkELGoFPWBMwBsa4BD0OYE7Vc+ZFEqhyzva
mOpicU1xl8mMkNIhiIaSU52fzRgYryZKmxPfYo8JaRUCAwEAAaNjMGEwDgYDVR0P
AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH81zMdEHAxyIbsp
1uKwfu+U0asPMB8GA1UdIwQYMBaAFElv+F66JtNinfmGM1cttCjILv/hMA0GCSqG
SIb3DQEBCwUAA4IBAQDOrqBCHRfVrTC/mfIIXcFn3lB+3kGmAmBVxH5UILljPdl9
ADjW9iiJtaEYxCOjrnFzpcOvuQnWPC7dbb+cy+DntLy/g6x95kBlAPdp02nYoS42
mDHmYUx+rIvnAceSTr72R+Vu9LNVzdbvnULT1o/4kW6uwRnrskisoFkAzeMd/4DT
tY71Dkz9faJt4VIE0wF3/jYXbqVhffyu6xXl+mS9mmCw/u9pFXMys2i/5tF90qIx
hpcVLhqFq4+dTnXaohC2DRpd+sCH4wRHMIRpi7gWI+6nXwLg95VSSwRNd+C+tyyh
GXlSB7/9WOGRGv74M5W9im3VFn5Ot3zMruEtl/WA
-----END CERTIFICATE-----
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAszcr/n7Jfof0O+rxOsq6TQq0UGqYMM/eUpcykyRaMOfg60oa
rhC1o88l8YGOK1h5zIctZuoZmVZTYfy6D+Qv8+0AWC1OCIW85t49s/hVRU6UgKK0
BYJIyjvGy+39dIYs4Xd1XW0KgRyMr0a0kQw7Kiocw6Bu5n3zrAhHguSBQvPcDa4L
lptc/kTFrqmEwK/cyiONpWA4UQi4LjfvVl4tT9heNr2yxDQGzw/M4iMI/uKlx73i
FhhKAAfdRrVlxZVlURsLK2ho4ZZeSuE1NVn6cBquUVjh+A3fIKL5iAWKkpTUf2g2
vg+AuzGG0zd/X3DC71c1jVEXvZx9E9L+Y/LzqwIDAQABAoIBAELDuAkMdMZFodA8
m3e6lKMk7L3UqDPvDLUaDMZSUaNMjzc4mS3lnbWT81/AUxWE1YNDjL99UOIWDeRA
QZSv/slRrsIqxppYMGKVwsjsJ+A1AQ/XDVfMDJACqvHIDJc/cTLhzQBsIfCtkR+v
KI+szdHMdIDQoOAPZhrdzr5NoHYvQM7B8HYe5G9dyw5GinRnTNqNUIhFM1HW5Gki
dhLAiqUGj2drSsbRoIWIOIzBbM3+hWZK1mO3q+QhdBEPcwzrOgMaj2722Dea57Hb
duCpdpKH43JfSmQ+f0V8OS/zYNUcyQikQg+Euak6dOuMhFN1ma7bNEug3slJMQ0M
JVG55IECgYEAxNgUXU+ORxCVZjMLxq2Uk2qJxbZspRIQ0ElsvsUpVpw0q0KV7V8K
qnxL4c6khOqWZQ9M7PWG4nJgjNXhYW9PpYdTXkmOZzAq+XU8nUYGHR0ZAlDj0oAd
lC9m9hBZPovxRozjMcRpnJinkZRLB0KEuTBECB6ZKZscLqbMIH81kWsCgYEA6RLd
b1Kk6xUjmUcGnq6bZN4a0spEGLayzgFOA2o/Ma1d94c3NsWsz97KGpCISExBOoPd
e/vryCrgvY50U4989z3mOVlcW9IJxaX5jyLmyG1POMkzwpWSST5u1DqnHpZ1xhzo
qjdd+F9E05PG8YM/2JYUDoCgL+BWvSYUsW/bdsECgYA2LZr9S8ZHPQNBy87z8haw
tGQTLPCeRmimS29I2o9ULU45zOvjF4OiBPAFyJBznPWj/yAXIETt0rlMShnsbWyo
jKSfkPNG53UMj/4M85b/MGEPyB/MckKx5JIcK8fEdVQFLQXjTsIavdFxOjWkW+SX
6/btnS5eyzhohrQsRZcC3QKBgEuzauSLbTFFjmhsX0QO5j3evcuVEoUMYgry9Ym6
aogSDSEmc9KAZd6mN/Jk2z7QNadxyPBKJIzBEHnA2RuC0U3kdvBNi+fu0mm1vNa2
uUqD5mVFRGCBACB1/L1Fx53xtJMCNLeEgCr5lns51jX/MS+Bn0YOlPi33rRUuAfh
kuCBAoGAPvxUmDXFnYyt8/3t5dpKt1GEpqDdQfhUDqE6zOPulsv0IkMKCvbyHdKI
iR1EbSvCMP9aVO7BeVRnr9FSrdL10TWNLP4JveSI4Ee1bzy0vvTj5l8CC4MwSdhN
04I8F7UqPhSbdVjsTxw16XrHSkvB5JRZsIAz/K0jVMwZs7fS8Lk=
-----END RSA PRIVATE KEY-----
+70
View File
@@ -0,0 +1,70 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2024 ImmortalWrt
. /lib/functions.sh
. /lib/functions/procd.sh
USE_PROCD=1
START=90
STOP=10
init_conf() {
config_load "gecoosac"
config_get "db_dir" "config" "db_dir" "/etc/gecoosac/"
config_get "upload_dir" "config" "upload_dir" "/tmp/gecoosac/upload/"
config_get "enabled" "config" "enabled" "0"
config_get "port" "config" "port" "60650"
config_get "isonlyoneprot" "config" "isonlyoneprot" "1"
config_get "m_port" "config" "m_port" "8080"
config_get "https" "config" "https" "0"
config_get "crt_file" "config" "crt_file" "/etc/gecoosac/tls/1.crt"
config_get "key_file" "config" "key_file" "/etc/gecoosac/tls/1.key"
config_get "log" "config" "log" "0"
}
start_service() {
init_conf
[ "${enabled}" = "1" ] || return 0
procd_open_instance gecoosac
procd_set_param command "/usr/bin/gecoosac"
procd_append_param command -f "${upload_dir}"
procd_append_param command -p "${port}"
procd_append_param command -debug "0"
procd_append_param command -dbpath "${db_dir}"
procd_append_param command -token "1"
if [ "${isonlyoneprot}" = "0" ]; then
procd_append_param command -isonlyoneprot "0"
procd_append_param command -mp "${m_port}"
if [ "${https}" = "1" ]; then
procd_append_param command -https "1"
procd_append_param command -crt "${crt_file}"
procd_append_param command -key "${key_file}"
else
procd_append_param command -https "0"
fi
else
procd_append_param command -isonlyoneprot "1"
fi
procd_append_param command -showtip "0"
procd_set_param env GIN_MODE=release
procd_set_param pidfile /var/run/gecoosac.pid
procd_set_param stdout $log
procd_set_param stderr $log
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
procd_close_instance
}
stop_service(){
init_conf
}
reload_service()
{
stop
start
}
service_triggers() {
procd_add_reload_trigger "gecoosac"
}
@@ -0,0 +1,19 @@
#!/bin/sh
[ ! -f "/usr/share/ucitrack/luci-app-gecoosac.json" ] && {
cat > /usr/share/ucitrack/luci-app-gecoosac.json << EEOF
{
"config": "gecoosac",
"init": "gecoosac"
}
EEOF
}
uci -q batch <<-EOF >/dev/null
delete ucitrack.@gecoosac[-1]
add ucitrack gecoosac
set ucitrack.@gecoosac[-1].init=gecoosac
commit ucitrack
EOF
rm -rf /tmp/luci-*
exit 0
@@ -0,0 +1,11 @@
{
"luci-app-gecoosac": {
"description": "Grant UCI access for luci-app-gecoosac",
"read": {
"uci": [ "gecoosac" ]
},
"write": {
"uci": [ "gecoosac" ]
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ LUCI_TITLE:=Information dashboard for 3G/LTE dongle
LUCI_DEPENDS:=+modeminfo
PKG_LICENSE:=GPLv3
PKG_VERSION:=0.4.7
PKG_RELEASE:=7
PKG_RELEASE:=8
include $(TOPDIR)/feeds/luci/luci.mk
@@ -15,6 +15,7 @@
Licensed to the GNU General Public License v3.0.
Refactored: bug fixes, performance improvements, code deduplication.
Modified: 5G NR support with combined band display
*/
const REGISTERED_STATUSES = [1, 6, 9];
@@ -39,37 +40,30 @@ const REG_STATUSES = new Map([
]);
// Progress bar configuration
// calc(vn, mn): vn = clamped signal value, mn = min boundary
// All formulas normalise the value to 0–100% range for the progress bar.
const PROGRESS_CONFIG = {
rssi: {
selector: '#rssi',
min: -110, max: -50,
// Linear map from [-110..-50] → [0..100]
calc: (vn, mn) => Math.floor(100 * (1 - (-50 - vn) / (-50 - mn))),
},
rsrp: {
selector: '#rsrp',
min: -140, max: -50,
// Slightly expanded scale (×1.2) to better use bar width
calc: (vn, mn) => Math.floor(120 * (1 - (-50 - vn) / (-70 - mn))),
},
sinr: {
selector: '#sinr',
min: -20, max: 30,
// Linear map from [-20..30] → [0..100]
calc: (vn, mn) => Math.floor(100 - (100 * (1 - ((mn - vn) / (mn - 30))))),
},
rsrq: {
selector: '#rsrq',
min: -20, max: 0,
// Proportional map from [-20..0] → [0..100]
calc: (vn, mn) => Math.floor(115 - (100 / mn) * vn),
},
ecio: {
selector: '#sinr',
min: -24, max: 0,
// Same shape as rsrq but for EC/IO range [-24..0]
calc: (vn, mn) => Math.floor(100 - (100 / mn) * vn),
},
};
@@ -91,6 +85,20 @@ const LTE_BANDS = [
{ min: 39650, max: 41589, frdl: 2496, frul: 2496, offset: 39650, band: '41' },
];
// 5G NR bands for FR1 (3GPP TS 38.104)
const NR_BANDS = [
{ min: 422000, max: 434000, name: '1', dlLow: 2110, offset: 422000 },
{ min: 361000, max: 376000, name: '3', dlLow: 1805, offset: 361000 },
{ min: 173800, max: 178800, name: '5', dlLow: 869, offset: 173800 },
{ min: 524000, max: 538000, name: '7', dlLow: 2620, offset: 524000 },
{ min: 185000, max: 192000, name: '8', dlLow: 925, offset: 185000 },
{ min: 158200, max: 164200, name: '20', dlLow: 791, offset: 158200 },
{ min: 499200, max: 538000, name: '41', dlLow: 2496, offset: 499200, tdd: true },
{ min: 653333, max: 680000, name: '77', dlLow: 3300, offset: 620000, tdd: true },
{ min: 620000, max: 653332, name: '78', dlLow: 3300, offset: 620000, tdd: true },
{ min: 693334, max: 733333, name: '79', dlLow: 4400, offset: 693334, tdd: true },
];
// Non-LTE (UMTS/GSM) ARFCN band/frequency table
const NON_LTE_BANDS = [
{
@@ -117,34 +125,16 @@ const NON_LTE_BANDS = [
const UMTS_MODES = /(HS|3G|UMTS|WCDMA)/i;
// Helpers
/**
* Safe getElementById wrapper.
* @param {string} id
* @returns {HTMLElement|null}
*/
// Helper functions
function getEl(id) {
return document.getElementById(id);
}
/**
* Show or hide the grandparent row of a signal element.
* @param {HTMLElement} el
* @param {boolean} show
*/
function setRowVisible(el, show) {
const row = el && el.parentElement && el.parentElement.parentElement;
if (row) row.style.display = show ? '' : 'none';
}
/**
* Update a cbi-progressbar element.
* @param {string} type - Key in PROGRESS_CONFIG
* @param {string} value - Raw signal value string (e.g. "-85 dBm")
* @param {number} max - Boundary value (passed as second calc argument)
* @param {number} idx - Modem index suffix for the element id
*/
function updateProgressBar(type, value, max, idx) {
const config = PROGRESS_CONFIG[type];
if (!config) return;
@@ -156,28 +146,16 @@ function updateProgressBar(type, value, max, idx) {
const mn = parseInt(max) || 100;
const pc = Math.min(100, Math.max(0, config.calc(vn, mn)));
pg.firstElementChild.style.width = `${pc}%`;
pg.firstElementChild.style.width = `${pc}%`;
pg.firstElementChild.style.animationDirection = 'reverse';
pg.setAttribute('title', String(value));
}
/**
* Format distance string, returns empty string when unavailable.
* @param {string|number} dist
* @returns {string}
*/
function formatDistance(dist) {
if (!dist || dist === '--' || dist === '' || dist === '0.00') return '';
return ' ~' + dist + ' km';
}
/**
* Build the operator status badge HTML safely using LuCI E() helper.
* @param {Object} modem - Single modem entry from JSON
* @param {string} icon - URL to signal icon
* @param {string} reg - Human-readable registration status
* @returns {string} - Safe innerHTML string via dom.content
*/
function formatModemStatus(modem, icon, reg) {
const rg = parseInt(modem.reg) || 0;
const p = modem.csq_per || 0;
@@ -201,44 +179,31 @@ function formatModemStatus(modem, icon, reg) {
}
}
/**
* Derive signal icon URL from signal percentage.
* @param {number} pct
* @returns {string}
*/
function resolveSignalIcon(pct) {
const { icn } = SIGNAL_ICONS.find(({ max }) => pct <= max) || SIGNAL_ICONS[SIGNAL_ICONS.length - 1];
return L.resource(`view/modem/icons/${icn}`);
}
/**
* Calculate DL/UL frequencies and band name for LTE mode.
* @param {number} rfcn
* @returns {{ dlfreq: number, ulfreq: number, band: string, frdl: number, frul: number, offset: number }}
*/
function calcLteBand(rfcn) {
const b = LTE_BANDS.find(b => rfcn >= b.min && rfcn <= b.max);
if (!b) return { frdl: 0, frul: 0, offset: 0, band: String(rfcn), dlfreq: 0, ulfreq: 0 };
if (!b) return { band: String(rfcn), dlfreq: 0, ulfreq: 0 };
const dlfreq = b.frdl + (rfcn - b.offset) / 10;
const ulfreq = b.frul + (rfcn - b.offset) / 10;
return { ...b, dlfreq, ulfreq };
return { band: b.band, dlfreq, ulfreq };
}
function calcNrBand(nrarfcn) {
const b = NR_BANDS.find(b => nrarfcn >= b.min && nrarfcn <= b.max);
if (!b) return { band: String(nrarfcn), dlfreq: 0 };
const dlfreq = b.dlLow + (nrarfcn - b.offset) / 1000;
return { band: b.name, dlfreq };
}
/**
* Calculate DL/UL frequencies and band name for non-LTE modes.
* @param {number} rfcn
* @returns {{ dlfreq: number, ulfreq: number, band: string }}
*/
function calcNonLteBand(rfcn) {
const match = NON_LTE_BANDS.find(b => b.condition(rfcn));
return match ? match.calc(rfcn) : { ulfreq: 0, dlfreq: 0, band: String(rfcn) };
return match ? match.calc(rfcn) : { band: String(rfcn), dlfreq: 0, ulfreq: 0 };
}
/**
* Build a LAC/CID/eNB/Cell/PCI label and value string.
* @param {Object} modem - Single modem entry
* @returns {{ namecid: string, lactac: string }}
*/
function buildCellId(modem) {
const { enbid, cell, pci, lac, cid } = modem;
const parts = [lac, cid];
@@ -263,16 +228,8 @@ function buildCellId(modem) {
return { namecid, lactac };
}
/**
* Derive mode-specific labels and carrier aggregation info.
* @param {Object} modem - Single modem entry
* @param {string} netmode
* @param {string} band
* @param {number} bw
* @returns {{ carrier, bcc, bca, bwDisplay, namech, namesnr, namecid, lactac, namebnd }}
*/
function buildModeInfo(modem, netmode, band, bw) {
let carrier = '';
let carrier = '';
let bcc, bca, bwDisplay, namech, namesnr, namecid, lactac;
if (netmode === 'LTE' || netmode === 'LTE+NR') {
@@ -280,15 +237,29 @@ function buildModeInfo(modem, netmode, band, bw) {
carrier = (netmode === 'LTE' && calte > 0) ? '+' : '';
namech = 'EARFCN';
namesnr = 'SINR';
if (calte > 0) {
bwDisplay = modem.bwca;
bcc = ` B${band}${modem.scc}`;
bca = bwDisplay ? ` / ${bwDisplay} MHz` : '';
let sccFormatted = modem.scc || '';
if (netmode === '5GNR' && sccFormatted) {
sccFormatted = sccFormatted.replace(/\+(\d+)/g, '+n$1');
if (sccFormatted.startsWith('+n')) {
sccFormatted = sccFormatted.substring(1);
}
if (sccFormatted) sccFormatted = '+' + sccFormatted;
}
bcc = ` ${band}${sccFormatted}`;
bca = bwDisplay ? ` / ${bwDisplay} MHz` : '';
} else {
bwDisplay = bw;
bcc = ` B${band}`;
bca = bw ? ` / ${bw} MHz` : '';
bcc = ` ${band}`;
if (netmode === 'LTE+NR' && bw) {
bca = ` / ${bw} MHz`;
} else if (bw) {
bca = ` / ${bw} MHz`;
} else {
bca = '';
}
}
const cellInfo = buildCellId(modem);
@@ -317,35 +288,16 @@ function buildModeInfo(modem, netmode, band, bw) {
return { carrier, bcc, bca: bca || '', bwDisplay, namech, namesnr, namecid, lactac, namebnd };
}
/**
* Set textContent of element by id (if it exists).
* @param {string} id
* @param {string} text
*/
function setText(id, text) {
const el = getEl(id);
if (el) el.textContent = text;
}
/**
* Set innerHTML of element by id (if it exists).
* @param {string} id
* @param {string} html
*/
function setHtml(id, html) {
const el = getEl(id);
if (el) el.innerHTML = html;
}
/**
* Update a signal progress bar, hiding its parent row if value is missing.
* @param {string} elId - Element id (without modem index)
* @param {number} idx - Modem index
* @param {*} rawVal - Raw value from modem JSON
* @param {string} unit - Unit suffix, e.g. ' dBm'
* @param {string} type - Key in PROGRESS_CONFIG
* @param {number} boundary
*/
function updateSignalBar(elId, idx, rawVal, unit, type, boundary) {
const el = getEl(elId + idx);
if (!el) return;
@@ -357,7 +309,6 @@ function updateSignalBar(elId, idx, rawVal, unit, type, boundary) {
}
// Main view
return view.extend({
load: function() {
@@ -381,20 +332,66 @@ return view.extend({
const modem = json.modem[i];
const netmode = modem.mode || '';
const rfcn = modem.arfcn || 0;
const nrArfcn = modem.nr_arfcn || 0;
// Band / Frequency
let dlfreq, ulfreq, band, bw;
let lteBand = '', nrBand = '';
let lteDlfreq = 0, nrDlfreq = 0;
let band = '', bw = '';
// LTE band and frequency (for LTE and LTE+NR)
if (netmode === 'LTE' || netmode === 'LTE+NR') {
({ dlfreq, ulfreq, band } = calcLteBand(rfcn));
const bandwidths = [1.4, 3, 5, 10, 15, 20];
bw = bandwidths[modem.bwdl] || '';
} else {
({ dlfreq, ulfreq, band } = calcNonLteBand(rfcn));
bw = '';
const lte = calcLteBand(rfcn);
lteBand = `B${lte.band}`;
lteDlfreq = lte.dlfreq;
}
const arfcnStr = `${rfcn} (${dlfreq} / ${ulfreq} MHz)`;
// NR band (for LTE+NR and 5G NR)
if (netmode === '5GNR' && nrArfcn) {
const nr = calcNrBand(nrArfcn);
nrBand = `n${nr.band}`;
nrDlfreq = nr.dlfreq;
}
// Combined band display
let bandDisplay = '';
if (lteBand && nrBand) {
bandDisplay = `${lteBand} + ${nrBand}`;
} else if (lteBand) {
bandDisplay = lteBand;
} else if (nrBand) {
bandDisplay = nrBand;
} else {
// Fallback for UMTS/GSM
const nonLte = calcNonLteBand(rfcn);
bandDisplay = nonLte.band;
lteDlfreq = nonLte.dlfreq;
}
// Bandwidth handling
const lteBandwidths = [1.4, 3, 5, 10, 15, 20];
const nrBandwidths = [5, 10, 15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 100];
if (netmode === 'LTE' || netmode === 'LTE+NR') {
if (modem.bwca && modem.bwca > 0) {
bw = modem.bwca;
} else {
bw = lteBandwidths[modem.bwdl] || '';
}
} else if (netmode === '5GNR') {
if (modem.bwca && modem.bwca > 0) {
bw = modem.bwca;
} else if (modem.bwdl) {
bw = nrBandwidths[modem.bwdl] || '';
}
}
// Frequency display - only primary (LTE or NR)
let arfcnDisplay = '';
if (lteDlfreq) {
arfcnDisplay = `${rfcn} (${lteDlfreq.toFixed(1)} MHz)`;
} else if (nrDlfreq && nrArfcn) {
arfcnDisplay = `${nrArfcn} (${nrDlfreq.toFixed(1)} MHz)`;
}
// Registration
const rg = Number(modem.reg);
@@ -403,16 +400,15 @@ return view.extend({
// Signal icon
const icon = resolveSignalIcon(modem.csq_per || 0);
// Mode-specific labels
// Build mode info for CA and other labels
const { carrier, bcc, bca, namech, namesnr, namecid, lactac, namebnd } =
buildModeInfo(modem, netmode, band, bw);
buildModeInfo(modem, netmode, bandDisplay, bw);
// DOM updates
setHtml('status' + i, formatModemStatus(modem, icon, reg));
setHtml('status' + i, formatModemStatus(modem, icon, reg));
const modeEl = getEl('mode' + i);
if (modeEl) {
// FIX: was `signal = 0` (assignment), must be `=== 0`
if (modem.signal === 0 || modem.signal === '' || !netmode) {
modeEl.textContent = '--';
} else {
@@ -424,11 +420,10 @@ return view.extend({
setText('chname' + i, namech);
setText('namecid' + i, namecid);
setText('snrname' + i, namesnr);
setHtml('arfcn' + i, arfcnStr);
setHtml('arfcn' + i, arfcnDisplay);
setHtml('lac' + i, lactac);
// Progress bars
// RSSI always visible if element exists
if (getEl('rssi' + i)) {
if (!modem.rssi || modem.rssi === '') {
setText('rssi' + i, '--');
@@ -437,17 +432,13 @@ return view.extend({
}
}
// SINR / ECIO (shared element, hidden when unavailable)
updateSignalBar(
'sinr', i, modem.sinr, ' dB',
netmode === 'LTE' ? 'sinr' : 'ecio',
netmode === 'LTE' ? -20 : -24
);
// RSRP (hidden when unavailable)
updateSignalBar('rsrp', i, modem.rsrp, ' dBm', 'rsrp', -140);
// RSRQ (hidden when unavailable)
updateSignalBar('rsrq', i, modem.rsrq, ' dB', 'rsrq', -20);
}
});
+1 -1
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.5.20
PKG_RELEASE:=144
PKG_RELEASE:=145
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
@@ -1804,13 +1804,14 @@ function gen_config(var)
-- remote dns outbound rules
if value.outboundTag == "blackhole" then
table.insert(remote_dns_out_rules, {
action = "reject",
action = "return",
rCode = 0,
domain = api.clone(value.domain)
})
else
table.insert(remote_dns_out_rules, {
action = "hijack",
qtype = "1,28",
qType = "1,28",
domain = api.clone(value.domain)
})
end
@@ -1877,7 +1878,7 @@ function gen_config(var)
if remote_dns_outbound.settings.rules then
table.insert(remote_dns_out_rules, {
action = "hijack",
qtype = "1,28"
qType = "1,28"
})
table.insert(remote_dns_out_rules, {
action = "direct"
@@ -734,7 +734,7 @@ filter_haproxy() {
}
filter_vpsip() {
local EXCLUDE_VPSIP="^(127\.0\.0\.1|1\.1\.1\.1|1\.1\.1\.2|8\.8\.8\.8|8\.8\.4\.4|9\.9\.9\.9)$"
local EXCLUDE_VPSIP="^(0\.0\.0\.0|127\.0\.0\.1|1\.1\.1\.1|1\.1\.1\.2|8\.8\.8\.8|8\.8\.4\.4|9\.9\.9\.9)$"
uci show $CONFIG | grep -E "(\.address=|\.download_address=)" | cut -d "'" -f 2 | grep -E "^([0-9]{1,3}\.){3}[0-9]{1,3}$" | grep -Ev "$EXCLUDE_VPSIP" | sed "s/^/add $IPSET_VPS /" | awk '1; END{print "COMMIT"}' | ipset -! -R
echolog " - [$?]加入所有IPv4节点到ipset[$IPSET_VPS]直连完成"
uci show $CONFIG | grep -E "(\.address=|\.download_address=)" | cut -d "'" -f 2 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed "s/^/add $IPSET_VPS6 /" | awk '1; END{print "COMMIT"}' | ipset -! -R
@@ -785,7 +785,7 @@ filter_vps_addr() {
}
filter_vpsip() {
local EXCLUDE_VPSIP="^(127\.0\.0\.1|1\.1\.1\.1|1\.1\.1\.2|8\.8\.8\.8|8\.8\.4\.4|9\.9\.9\.9)$"
local EXCLUDE_VPSIP="^(0\.0\.0\.0|127\.0\.0\.1|1\.1\.1\.1|1\.1\.1\.2|8\.8\.8\.8|8\.8\.4\.4|9\.9\.9\.9)$"
uci show $CONFIG | grep -E "(\.address=|\.download_address=)" | cut -d "'" -f 2 | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}" | grep -Ev "$EXCLUDE_VPSIP" | insert_nftset $NFTSET_VPS "-1"
echolog " - [$?]加入所有IPv4节点到nftset[$NFTSET_VPS]直连完成"
uci show $CONFIG | grep -E "(\.address=|\.download_address=)" | cut -d "'" -f 2 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_VPS6 "-1"
+1 -1
View File
@@ -7,7 +7,7 @@ LUCI_DEPENDS:=+luci-nginx +quickfile
PKG_LICENSE:=Apache-2.0
PKG_VERSION:=1.0.0
PKG_RELEASE:=4
PKG_RELEASE:=5
PKG_MAINTAINER:=sbwml <admin@cooluc.com>
include $(TOPDIR)/feeds/luci/luci.mk
@@ -10,8 +10,6 @@ return view.extend({
s.anonymous = true;
s.render = function () {
const host = window.location.origin;
const iframeContainer = E('div', {
'class': 'iframe-container',
'style': `
@@ -26,7 +24,7 @@ return view.extend({
`
}, [
E('iframe', {
'src': `/cgi-bin/luci/quickfile?host=${encodeURIComponent(host)}`,
'src': `/cgi-bin/luci/quickfile`,
'style': `
width: 100%;
height: 100%;
@@ -0,0 +1,6 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
#: luci-app-quickfile/root/usr/share/luci/menu.d/luci-app-quickfile.json:3
msgid "Quick File Manager"
msgstr ""
+2 -1
View File
@@ -1,5 +1,6 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgstr "Content-Type: text/plain; charset=UTF-8\n"
#: luci-app-quickfile/root/usr/share/luci/menu.d/luci-app-quickfile.json:3
msgid "Quick File Manager"
msgstr "文件管理"
@@ -1,10 +0,0 @@
{
"luci-app-quickfile": {
"description": "Grant UCI access for luci-app-openlist",
"read": {
"file": {
"/usr/bin/quickfile": [ "exec" ]
}
}
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=qminfo
PKG_VERSION=0.0.3
PKG_RELEASE:=6
PKG_RELEASE:=7
include $(INCLUDE_DIR)/package.mk
+3 -3
View File
@@ -299,8 +299,8 @@ static void emit_results(void)
printf(" NR SNR : %d dB\n", (int)roundf(info.nr_snr / 10.0f));
}
if (info.has_ca) {
printf(" LTE-A SCC : %d — %s\n", info.lte_ca, info.scc_bands);
printf(" BW CA : %.1f MHz\n", info.bw_ca_total / 1000.0f);
printf(" SCC : %dxCA — %s\n", info.lte_ca, info.scc_bands);
printf(" BW CA : %.1f MHz\n", (info.bw_ca_total - bw_index_to_khz(info.bw_dl)) / 1000.0f);
}
printf("=================================================\n");
return;
@@ -348,7 +348,7 @@ static void emit_results(void)
if (info.nr_nsa && !info.dcnr_restricted)
json_mode_str = "LTE+NR";
else if (info.nr_sa)
json_mode_str = "5G NR";
json_mode_str = "5GNR";
else
json_mode_str = info.mode[0] ? info.mode : "";