🐶 Sync 2026-03-30 14:49:16

This commit is contained in:
github-actions[bot] committed 2026-03-30 14:49:17 +08:00
1 parent f00793a73b
commit f28e441b8e
9 files changed
+110 -187

No files matched your search

+8 -10
View File
@@ -1,5 +1,4 @@
# Copyright (C) 2016 Openwrt.org # Copyright (C) 2025 Openwrt.org
# Copyright (C) 2024 iv7777 <hongba@rocketmail.com>
# #
# This is free software, licensed under the Apache License, Version 2.0 . # This is free software, licensed under the Apache License, Version 2.0 .
# #
@@ -7,15 +6,14 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for Timewol LUCI_TITLE:=LuCI support for Timewol
LUCI_PKGARCH:=all PKG_VERSION:=20250407
LUCI_DEPENDS:=+etherwake PKG_RELEASE:=2
define Package/luci-app-timewol/postinst LUCI_DEPENDS:=+luci-base +etherwake
#!/bin/sh LUCI_PKGARCH:=all
if [ -z "$${IPKG_INSTROOT}" ]; then
chmod +x /etc/init.d/timewol define Package/luci-app-timewol/conffiles
fi /etc/config/timewol
exit 0
endef endef
include $(TOPDIR)/feeds/luci/luci.mk include $(TOPDIR)/feeds/luci/luci.mk
+10 -35
View File
@@ -1,46 +1,21 @@
module("luci.controller.timewol", package.seeall) module("luci.controller.timewol", package.seeall)
local x = luci.model.uci.cursor()
function index() function index()
if not nixio.fs.access("/etc/config/timewol") then return end if not nixio.fs.access("/etc/config/timewol") then return end
entry({"admin", "control"}, firstchild(), "Control", 44).dependent = false entry({"admin", "control"}, firstchild(), "Control", 44).dependent = false
local page = entry({"admin", "control", "timewol"}, cbi("timewol"), _("Timed WOL"))
local page = entry({"admin", "control", "timewol"}, cbi("timewol"), _("Timed WOL"))
page.order = 95 page.order = 95
page.dependent = true page.dependent = true
page.acl_depends = { "luci-app-timewol" } page.acl_depends = { "luci-app-timewol" }
entry({"admin", "control", "timewol", "status"}, call("status")).leaf = true
entry( {"admin", "control", "timewol", "awake"}, call("awake") ).leaf = true entry({"admin", "control", "timewol", "status"}, call("status")).leaf = true
end end
function status() function status()
local e = {} local e = {}
e.status = luci.sys.call("cat /etc/crontabs/root |grep etherwake >/dev/null") == 0 e.status = luci.sys.call("cat /etc/crontabs/root | grep -v '^[ \t]*#' | grep etherwake >/dev/null") == 0
luci.http.prepare_content("application/json") luci.http.prepare_content("application/json")
luci.http.write_json(e) luci.http.write_json(e)
end
function awake(sections)
lan = x:get("timewol",sections,"maceth")
mac = x:get("timewol",sections,"macaddr")
local e = {}
cmd = "/usr/bin/etherwake -D -i " .. lan .. " -b " .. mac .. " 2>&1"
local p = io.popen(cmd)
local msg = ""
if p then
while true do
local l = p:read("*l")
if l then
if #l > 100 then l = l:sub(1, 100) .. "..." end
msg = msg .. l
else
break
end
end
p:close()
end
e["data"] = msg
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end end
+56 -59
View File
@@ -1,93 +1,90 @@
local sys = require "luci.sys" local sys = require "luci.sys"
local m, s, o
-- Create the main map object -- Create the main map object
local map = Map("timewol", translate("Timed Wake on LAN"), m = Map("timewol", translate("Timed Wake on LAN"),
translate("Wake up your local area network devices on schedule")) translate("Wake up your local area network devices on schedule"))
map.template = "timewol/index" m.template = "timewol/index"
-- Running Status Section -- Running Status Section
local status_section = map:section(TypedSection, "basic", translate("Running Status")) s = m:section(TypedSection, "basic", translate("Running Status"))
status_section.anonymous = true s.anonymous = true
local status = status_section:option(DummyValue, "timewol_status", translate("Current Status")) o = s:option(DummyValue, "timewol_status", translate("Current Status"))
status.template = "timewol/timewol" o.template = "timewol/timewol"
status.value = translate("Collecting data...") o.value = translate("Collecting data...")
-- Basic Settings Section -- Basic Settings Section
local basic_section = map:section(TypedSection, "basic", translate("Basic Settings")) s = m:section(TypedSection, "basic", translate("Basic Settings"))
basic_section.anonymous = true s.anonymous = true
local enable = basic_section:option(Flag, "enable", translate("Enable")) o = s:option(Flag, "enable", translate("Enable"))
enable.rmempty = false o.rmempty = false
-- Client Settings Section -- Client Settings Section
local client_section = map:section(TypedSection, "macclient", translate("Client Settings")) s = m:section(TypedSection, "macclient", translate("Client Settings"))
client_section.template = "cbi/tblsection" s.template = "cbi/tblsection"
client_section.anonymous = true s.anonymous = true
client_section.addremove = true s.addremove = true
o = s:option(Flag, "enable", translate("Enable"))
o.default = 1
o.rmempty = false
-- Client MAC Address -- Client MAC Address
local mac_addr = client_section:option(Value, "macaddr", translate("Client MAC")) o = s:option(Value, "macaddr", translate("Client MAC"))
mac_addr.rmempty = false o.rmempty = false
sys.net.mac_hints(function(mac, hint) sys.net.mac_hints(function(mac, hint)
mac_addr:value(mac, string.format("%s (%s)", mac, hint)) o:value(mac, string.format("%s (%s)", mac, hint))
end) end)
-- Network Interface -- Network Interface
local net_iface = client_section:option(Value, "maceth", translate("Network Interface")) o = s:option(Value, "maceth", translate("Network Interface"))
net_iface.rmempty = false o.rmempty = false
net_iface.default = "br-lan" o.default = "br-lan"
for _, device in ipairs(sys.net.devices()) do for _, device in ipairs(sys.net.devices()) do
if device ~= "lo" then if device ~= "lo" then
net_iface:value(device) o:value(device)
end end
end end
-- wake device
local btn = client_section:option(Button, "_awake",translate("Wake Up Host"))
btn.inputtitle = translate("Awake")
btn.inputstyle = "apply"
btn.disabled = false
btn.template = "timewol/awake"
-- Function to validate cron field values -- Function to validate cron field values
local function validate_cron_field(option_name, value, min, max, default) local function validate_cron_field(option_name, value, min, max, default)
if value == "" then if value == "" then
return default return default
elseif value == "*" then elseif value == "*" then
return value return value
end end
local num = tonumber(value) local num = tonumber(value)
if num and num >= min and num <= max then if num and num >= min and num <= max then
return value return value
else else
return nil, translatef("Invalid value for %s: %s. Must be between %d and %d or '*'", option_name, value, min, max) return nil, translatef("Invalid value for %s: %s. Must be between %d and %d or '*'", option_name, value, min, max)
end end
end end
-- Scheduling Options with Default Values and Range Checks -- Scheduling Options with Default Values and Range Checks
local schedule_options = { local schedule_options = {
{ "minute", translate("Minute"), 0, 59, "0" }, { "minute", translate("Minute"), 0, 59, "0" },
{ "hour", translate("Hour"), 0, 23, "0" }, { "hour", translate("Hour"), 0, 23, "0" },
{ "day", translate("Day"), 1, 31, "*" }, { "day", translate("Day"), 1, 31, "*" },
{ "month", translate("Month"), 1, 12, "*" }, { "month", translate("Month"), 1, 12, "*" },
{ "weeks", translate("Week"), 0, 6, "*" } -- 0 for Sunday, 6 for Saturday { "weeks", translate("Week"), 0, 6, "*" } -- 0 for Sunday, 6 for Saturday
} }
for _, opt in ipairs(schedule_options) do for _, opt in ipairs(schedule_options) do
local field = client_section:option(Value, opt[1], opt[2]) o = s:option(Value, opt[1], opt[2])
field.default = opt[5] or opt[4] -- Use default value if present, otherwise use maximum value o.default = opt[5] or opt[4] -- Use default value if present, otherwise use maximum value
field.optional = false o.optional = false
field.validate = function(self, value) o.validate = function(self, value)
return validate_cron_field(opt[2], value, opt[3], opt[4], field.default) return validate_cron_field(opt[2], value, opt[3], opt[4], o.default)
end end
end end
-- Apply the configuration changes -- Apply the configuration changes
map.apply_on_parse = true m.apply_on_parse = true
function map.on_apply(self) function m.on_apply(self)
sys.exec("/etc/init.d/timewol restart") sys.exec("/etc/init.d/timewol restart")
end end
return map return m
@@ -1,3 +0,0 @@
<%+cbi/valueheader%>
<input class="cbi-button cbi-input-<%=self.inputstyle or "button" %>" style="font-size: 100%;" type="button" onclick="onclick_awake(this.id)" <%=attr("name", section) .. attr("id", cbid) .. attr("value", self.inputtitle)%> />
<%+cbi/valuefooter%>
@@ -14,23 +14,5 @@
status.innerHTML = result.status?'<%=translate("RUNNING")%>':'<%=translate("NOT RUNNING")%>'; status.innerHTML = result.status?'<%=translate("RUNNING")%>':'<%=translate("NOT RUNNING")%>';
} }
) )
function _id2section(id) {
var x = id.split(".");
return x[2];
}
function onclick_awake(id) {
var section = _id2section(id);
var btnXHR = new XHR();
btnXHR.post('<%=url([[admin]], [[control]], [[timewol]], [[awake]])%>/' + section, { token: '<%=token%>' },
function(x, data) {
if (x.responseText == "_uncommitted_") {
txt="<%:Please [Save & Apply] your changes first%>";
alert( txt.replace(new RegExp("<%:&%>", "g"), "&") );
} else {
alert( JSON.parse(x.response).data );
}
}
);
}
//]]> //]]>
</script> </script>
-1
View File
@@ -1 +0,0 @@
zh_Hans
@@ -34,9 +34,6 @@ msgstr "客户端MAC"
msgid "Network Interface" msgid "Network Interface"
msgstr "网络接口" msgstr "网络接口"
msgid "Awake"
msgstr "立即唤醒"
msgid "Minute" msgid "Minute"
msgstr "分钟" msgstr "分钟"
+1
View File
@@ -0,0 +1 @@
zh-cn
+35 -58
View File
@@ -1,8 +1,6 @@
#!/bin/sh /etc/rc.common #!/bin/sh /etc/rc.common
# #
# Copyright (C) 2015 OpenWrt-dist # Copyright (C) 2025 OpenWrt.org
# Copyright (C) 2016 fw867 <ffkykzs@gmail.com>
# Copyright (C) 2024 iv7777 <hongba@rocketmail.com>
# #
# This is free software, licensed under the GNU General Public License v3. # This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information. # See /LICENSE for more information.
@@ -13,75 +11,54 @@ CONFIG=timewol
CRONTAB_FILE="/etc/crontabs/root" CRONTAB_FILE="/etc/crontabs/root"
ETHERWAKE_CMD="/usr/bin/etherwake" ETHERWAKE_CMD="/usr/bin/etherwake"
# Function to get UCI configuration values with defaults config_n_get() {
uci_get_by_type() { local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null)
local type=$1 echo "${ret:=$3}"
local option=$2
local default=$3
local index=${4:-0} # Use 0 if $4 is not provided
local value
value=$(uci get "$CONFIG.@$type[$index].$option" 2>/dev/null) || value=$default
echo "$value"
} }
# Function to check if a value represents a true boolean
is_true() { is_true() {
case "$1" in case "$1" in
1|on|true|yes|enabled) return 0 ;; 1|on|true|yes|enabled) return 0 ;;
*) return 1 ;; *) return 1 ;;
esac esac
} }
# Function to load configuration and check if enabled
load_config() { load_config() {
local enabled local enabled=$(config_n_get @basic[0] enable 0)
enabled=$(uci_get_by_type basic enable "0") is_true "$enabled"
is_true "$enabled"
} }
# Function to add WoL rules to the crontab
add_rule() { add_rule() {
# Remove existing etherwake entries sed -i '/etherwake/d' "$CRONTAB_FILE"
sed -i '/etherwake/d' "$CRONTAB_FILE"
for i in $(seq 0 100); do local ids=$(uci show $CONFIG | grep "=macclient" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}')
local macaddr [ -n "$ids" ] && {
local maceth for id in $ids; do
local minute local enabled=$(config_n_get $id enable 0)
local hour local macaddr=$(config_n_get $id macaddr)
local day local maceth=$(config_n_get $id maceth)
local month [ $enabled -eq 1 ] && [ -n "$macaddr" ] && [ -n "$maceth" ] && {
local weeks local minute=$(config_n_get $id minute "0")
local hour=$(config_n_get $id hour "*")
macaddr=$(uci_get_by_type macclient macaddr "" "$i") local day=$(config_n_get $id day "*")
maceth=$(uci_get_by_type macclient maceth "" "$i") local month=$(config_n_get $id month "*")
local weeks=$(config_n_get $id weeks "*")
# Stop if no more macaddr entries echo "$minute $hour $day $month $weeks $ETHERWAKE_CMD -D -i $maceth $macaddr" >> "$CRONTAB_FILE"
[ -z "$macaddr" ] && break }
[ -z "$maceth" ] && break done
}
minute=$(uci_get_by_type macclient minute "0" "$i")
hour=$(uci_get_by_type macclient hour "*" "$i")
day=$(uci_get_by_type macclient day "*" "$i")
month=$(uci_get_by_type macclient month "*" "$i")
weeks=$(uci_get_by_type macclient weeks "*" "$i")
echo "$minute $hour $day $month $weeks $ETHERWAKE_CMD -D -i $maceth $macaddr" >> "$CRONTAB_FILE"
done
} }
# Function to start the service
start() { start() {
if load_config; then if load_config; then
add_rule add_rule
else /etc/init.d/cron restart
exit 0 else
fi exit 0
fi
} }
# Function to stop the service
stop() { stop() {
# Remove etherwake entries from crontab sed -i '/etherwake/d' "$CRONTAB_FILE"
sed -i '/etherwake/d' "$CRONTAB_FILE" /etc/init.d/cron restart
} }