⛄ Sync 2026-10-25 10:56:54

This commit is contained in:
github-actions[bot] committed 2026-10-25 10:56:54 +08:00
1 parent 37e1475e87
commit 816dd89fe9
68 files changed
+55036 -377

No files matched your search

+6 -3
View File
@@ -8,8 +8,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=thermal-guard
PKG_VERSION:=2.0.0
PKG_RELEASE:=1
PKG_VERSION:=2.1.1.0
PKG_RELEASE:=2
PKG_LICENSE:=GPL-2.0-only
PKG_LICENSE_FILES:=LICENSE
@@ -29,7 +29,9 @@ define Package/thermal-guard/description
Watches SoC and modem temperature and escalates in stages when a router runs
hot, typically after a fan failure: fan to full speed, modem radio off, Wi-Fi
off. Stages do not roll back on their own, clearing them is an operator
decision, and they survive a reboot. Fan control works either through the
decision, and they survive a reboot. One optional exception, off by default:
the second stage clears itself when the modem alone caused it and has read
cool for a while. Fan control works either through the
kernel thermal zone or through sysfs PWM, modem temperature through Quectel or
Fibocom AT commands, a file or a hook script. Warnings are posted to a URL.
Where the kernel drives the fan, it can lower the fan's trip points while the
@@ -59,6 +61,7 @@ define Package/thermal-guard/install
$(INSTALL_BIN) ./files/usr/sbin/thermal-guard $(1)/usr/sbin/thermal-guard
$(INSTALL_DATA) ./files/usr/share/thermal-guard/backends.sh $(1)/usr/share/thermal-guard/backends.sh
$(INSTALL_DATA) ./files/usr/share/thermal-guard/trips.sh $(1)/usr/share/thermal-guard/trips.sh
$(INSTALL_DATA) ./files/usr/share/thermal-guard/modems $(1)/usr/share/thermal-guard/modems
$(INSTALL_CONF) ./files/etc/config/thermal-guard $(1)/etc/config/thermal-guard
$(INSTALL_BIN) ./files/etc/init.d/thermal-guard $(1)/etc/init.d/thermal-guard
$(INSTALL_BIN) ./files/etc/uci-defaults/90-thermal-guard-trip-base $(1)/etc/uci-defaults/
+21 -4
View File
@@ -1,20 +1,31 @@
config thermal-guard 'main'
option enabled '1'
# seconds between readings, minimum 5
# seconds between readings, 5 to 300, values outside are set to the nearest limit
option interval '20'
# stage thresholds in degrees celsius, must be ordered warn < crit < emergency
option cpu_warn '70'
option cpu_crit '80'
option cpu_emergency '90'
option modem_warn '80'
option modem_crit '88'
# Unset takes the profile of the detected modem from
# /usr/share/thermal-guard/modems, otherwise 80 and 88. Set either to
# override it, warn below crit
#option modem_warn '80'
#option modem_crit '88'
# minutes a stage has to persist before the next one can trigger on time alone
option hold_minutes '3'
# degrees below the warn thresholds that count as cooled down
option hysteresis '10'
# Clear daemon stage 1 (stage 2 of 3 on the status page) on its own once
# the modem, which alone caused it, has read cool for recover_minutes
# (5 to 120), at most recover_max times in 24 hours (1 to 10). Stage 2, an
# incident where the processor was warm at any point and the 'command'
# action wait for a reset. Unset or 0 is off
#option auto_recover '1'
#option recover_minutes '10'
#option recover_max '3'
option cpu_temp_path '/sys/class/thermal/thermal_zone0/temp'
# also report the other temperature sensors of the board, Wi-Fi chips and so on
@@ -23,6 +34,10 @@ config thermal-guard 'main'
# none, cooling_device (kernel thermal zone), pwm (sysfs) or auto.
# none leaves the fan to whatever regulates it already. auto takes the cooling
# device if there is one and otherwise stays off the fan; it never picks pwm.
# The fan options below are read when a stage takes the fan over. Changed
# during a stage, they apply from the next takeover: the fan taken at stage
# entry stays in use (forced, checked, handed back) until it is released,
# also under none.
option fan_mode 'none'
# type of the fan's cooling device, for cooling_device, auto and trip
# management; empty means pwm-fan
@@ -72,7 +87,9 @@ config thermal-guard 'main'
# the message is posted here. Works with ntfy, Gotify, Matrix, Slack and
# anything else that takes a plain POST body. For a delivery HTTP cannot
# express, put an executable at /etc/thermal-guard/hooks/notify instead
# express, put an executable at /etc/thermal-guard/hooks/notify instead.
# Without curl the address is visible in the process list: it must not carry
# a secret in its path, and one with a query string or user info is refused
#option notify_url 'https://ntfy.sh/my-topic'
# only sent when curl is installed, uclient-fetch cannot set headers
#option notify_header 'Authorization: Bearer secret'
@@ -22,12 +22,22 @@ delivery that way, and a missing modem-temp hook means no modem reading.
modem-temp Runs when modem_source is 'command'. Has to print the modem
temperature in whole degrees Celsius on stdout, nothing else.
A number of more than seven significant digits counts as no
reading (leading zeros are dropped first, a minus sign counts).
modem-model Runs at start, in cycles with a modem reading, up to three times
until it prints a valid model, when modem_source is 'command'
or 'file'. Prints the modem model on stdout, for the modem
table in /usr/share/thermal-guard/modems. With ModemManager:
mmcli -m any -K | sed -n 's/^modem\.generic\.model *: *//p'
Letters, digits, dot, underscore and dash, up to 32 characters.
Every hook runs under a time limit: 15 seconds for notify, stage1 and stage2,
5 seconds for modem-temp, which runs every cycle. A hook still running then
gets TERM, and KILL 5 seconds later. It counts as failed: a stage action is
reported as not carried out, a modem-temp hook as no reading. Keep hooks short;
a mail or HTTP call inside one needs its own, shorter time limit.
5 seconds for modem-temp, which runs every cycle, and for modem-model. A hook
still running then gets TERM, and KILL 5 seconds later. It counts as failed: a
stage action is reported as not carried out, a modem-temp hook as no reading.
Keep hooks short; a mail or HTTP call inside one needs its own, shorter time
limit.
Example, a notify hook for a local mailer:
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,17 @@
# Modem profiles for thermal-guard: default modem limits per model.
#
# One line per profile: pattern, warn, crit, source, separated by blanks.
# pattern shell glob, * and ? only, against the model the modem reports
# (AT+CGMM or the modem-model hook); case does not matter
# warn modem_warn in whole degrees, 40 to 120
# crit modem_crit, above warn, at most 120
# source the rest of the line: the measurement or published limit the
# numbers come from. Required, a line without one is skipped.
#
# The first matching line wins, so specific patterns go above generic ones.
# modem_warn and modem_crit set in /etc/config/thermal-guard override these
# values one by one. thermal-guard reads the hottest sensor the modem
# reports, which is a die or amplifier sensor, not the module case.
#
RM520N-GL* 85 95 GL-X3000 2026-10-19: 80 C after 90 min of 5G load, GL.iNet firmware sets no modem limit
FM350-GL* 80 88 Banana Pi R3 Mini: plateau 73 to 74 C under sustained load (2026-09-19), 59 to 63 C with heatsink (2026-09-21)
@@ -233,7 +233,7 @@ tg_trip_base_save() { # $1 whole degrees, space separated
mkdir -p "$PERSIST_DIR" 2>/dev/null &&
echo "$1" > "$f.tmp" 2>/dev/null && chmod 644 "$f.tmp" &&
mv "$f.tmp" "$f" 2>/dev/null || return 1
sync
tg_sync
}
tg_trip_base_read() {
@@ -336,7 +336,8 @@ tg_fan_levels() {
local f
for f in "$DT_ROOT"/*/cooling-levels "$DT_ROOT"/*/*/cooling-levels; do
[ -r "$f" ] || continue
tr '\0' '\n' < "${f%/*}/compatible" 2>/dev/null | grep -qx pwm-fan || continue
# The braces take the open error too, a node may have no compatible.
{ tr '\0' '\n' < "${f%/*}/compatible"; } 2>/dev/null | grep -qx pwm-fan || continue
hexdump -v -e '4/1 "%u " "\n"' "$f" 2>/dev/null |
awk 'NF == 4 { printf "%s%d", s, $1 * 16777216 + $2 * 65536 + $3 * 256 + $4; s = " " }'
return 0