Environment
| Item |
Value |
| Device |
BeeconMini SEED AC2 |
| SoC |
MediaTek MT7981B (Filogic 820) |
| OS |
ImmortalWrt 24.10.1 r33048-cc720ea55a71 |
| Kernel |
6.6.86 |
| quickstart binary |
/usr/sbin/quickstart (served at 127.0.0.1:3038) |
| autocore |
version 42 |
Bug description
The CPU temperature widget on the QuickStart homepage always shows 0 (or blank).
The temperature API endpoint returns an empty result object:
# From browser console (authenticated session)
fetch('/cgi-bin/luci/istore/system/cpu/temperature/')
.then(r => r.json()).then(d => console.log(JSON.stringify(d)))
# Response:
{"result":{}}
Root cause analysis
The quickstart binary implements three temperature strategies, tried in order. All three fail on this platform.
Strategy 1 — thermalZoneTemperature
The binary reads /sys/class/thermal/thermal_zone*/type to find a zone whose type contains "cpu", then constructs the temperature path using the type value (not the directory name):
Step 1: read /sys/class/thermal/thermal_zone0/type → "cpu-thermal" ✓
Step 2: read /sys/class/thermal/cpu-thermal/temp → does not exist ✗
On this device, /sys/class/thermal/ only contains thermal_zone0 as a directory entry. There is no symlink or directory named cpu-thermal, so step 2 fails:
$ ls -la /sys/class/thermal/
lrwxrwxrwx thermal_zone0 -> ../../devices/virtual/thermal/thermal_zone0
$ cat /sys/class/thermal/thermal_zone0/type
cpu-thermal
$ cat /sys/class/thermal/cpu-thermal/temp
cat: can't open '/sys/class/thermal/cpu-thermal/temp': No such file or directory
The actual temperature is readable at:
$ cat /sys/class/thermal/thermal_zone0/temp
62767 # millidegrees Celsius → 62.7°C
sysfs does not permit creating symlinks or directories inside /sys/class/thermal/:
$ ln -sf /sys/class/thermal/thermal_zone0 /sys/class/thermal/cpu-thermal
ln: /sys/class/thermal/cpu-thermal: Operation not permitted
Strategy 2 — hwmonTemperature
Same pattern: the binary reads /sys/class/hwmon/hwmon*/name, finds a match, then constructs the path using the name value:
$ cat /sys/class/hwmon/hwmon1/name
cpu_thermal
$ cat /sys/class/hwmon/hwmon1/temp1_input
62918 # correct value ✓
$ cat /sys/class/hwmon/cpu_thermal/temp1_input
# does not exist ✗
Again, no cpu_thermal directory exists under /sys/class/hwmon/, and sysfs rejects any attempt to create one:
$ ln -sf /sys/class/hwmon/hwmon1 /sys/class/hwmon/cpu_thermal
ln: /sys/class/hwmon/cpu_thermal: Operation not permitted
Strategy 3 — autocoreTemperature
The binary calls ubus call luci getTempInfo, which returns a human-readable string:
$ ubus call luci getTempInfo
{ "tempinfo": "CPU: 63.1°C" }
The binary apparently cannot parse a numeric temperature from this string format, so this strategy also returns empty.
Verification
The ubus interface itself and the underlying /sbin/tempinfo script work correctly:
$ /sbin/tempinfo
CPU: 62.9°C
$ ubus call luci getTempInfo
{ "tempinfo": "CPU: 63.1°C" }
$ ubus call luci getCPUInfo
{ "cpuinfo": "ARMv8 Processor rev 4 (v8l) x 2" }
Note: /sbin/cpuinfo intentionally skips temperature for mediatek/* targets:
# from /sbin/cpuinfo:
case "$DISTRIB_TARGET" in
"ipq40xx"/*|"ipq806x"/*|"mediatek"/*|"qualcommax"/*) ;; # no cpu_temp set
Suggested fix
The binary's temperature discovery logic assumes that after finding a thermal zone or hwmon node by name, the path can be constructed from that name string. On platforms where the kernel only exposes numbered entries (thermal_zone0, hwmon1) without corresponding named symlinks, this breaks.
Option A — Use the directory path directly, not the name value
When iterating /sys/class/thermal/thermal_zone*/type, once a matching zone is found, read the temperature from the same directory (thermal_zone0/temp), not from a path derived from the type string.
Similarly for hwmon: once a matching hwmon*/name is found, read hwmon*/temp1_input from the same hwmon* directory.
Option B — Fall back to thermal_zone0/temp when named path fails
If the constructed path (e.g., /sys/class/thermal/cpu-thermal/temp) does not exist, fall back to reading directly from the zone that had the matching type.
Option C — Parse temperature from ubus call luci getTempInfo output
The autocoreTemperature strategy already has the correct data ("CPU: 63.1°C"). Extracting the numeric value with a simple regex (e.g., (\d+\.?\d*)°C) would make this strategy work on any platform supported by autocore.
Option C is the most robust since it delegates platform-specific logic to autocore, which already handles the per-target differences correctly.
Additional info
Confirmed via strings /usr/sbin/quickstart:
/sys/class/thermal/thermal_zone%v/type
/sys/class/thermal/%v/temp
/sys/class/hwmon/hwmon%d/name
/sys/class/hwmon/hwmon%d/temp%d_input
thermalZoneTemperature default
hwmonTemperature default
hwmonTemperature candidate
The %v in /sys/class/thermal/%v/temp is filled with the type value (cpu-thermal), not the zone index — this is the source of the mismatch.
Environment
/usr/sbin/quickstart(served at127.0.0.1:3038)Bug description
The CPU temperature widget on the QuickStart homepage always shows 0 (or blank).
The temperature API endpoint returns an empty result object:
Root cause analysis
The quickstart binary implements three temperature strategies, tried in order. All three fail on this platform.
Strategy 1 —
thermalZoneTemperatureThe binary reads
/sys/class/thermal/thermal_zone*/typeto find a zone whose type contains"cpu", then constructs the temperature path using the type value (not the directory name):On this device,
/sys/class/thermal/only containsthermal_zone0as a directory entry. There is no symlink or directory namedcpu-thermal, so step 2 fails:The actual temperature is readable at:
$ cat /sys/class/thermal/thermal_zone0/temp 62767 # millidegrees Celsius → 62.7°Csysfs does not permit creating symlinks or directories inside
/sys/class/thermal/:Strategy 2 —
hwmonTemperatureSame pattern: the binary reads
/sys/class/hwmon/hwmon*/name, finds a match, then constructs the path using the name value:Again, no
cpu_thermaldirectory exists under/sys/class/hwmon/, and sysfs rejects any attempt to create one:Strategy 3 —
autocoreTemperatureThe binary calls
ubus call luci getTempInfo, which returns a human-readable string:$ ubus call luci getTempInfo { "tempinfo": "CPU: 63.1°C" }The binary apparently cannot parse a numeric temperature from this string format, so this strategy also returns empty.
Verification
The
ubusinterface itself and the underlying/sbin/tempinfoscript work correctly:$ /sbin/tempinfo CPU: 62.9°C $ ubus call luci getTempInfo { "tempinfo": "CPU: 63.1°C" } $ ubus call luci getCPUInfo { "cpuinfo": "ARMv8 Processor rev 4 (v8l) x 2" }Note:
/sbin/cpuinfointentionally skips temperature formediatek/*targets:Suggested fix
The binary's temperature discovery logic assumes that after finding a thermal zone or hwmon node by name, the path can be constructed from that name string. On platforms where the kernel only exposes numbered entries (
thermal_zone0,hwmon1) without corresponding named symlinks, this breaks.Option A — Use the directory path directly, not the name value
When iterating
/sys/class/thermal/thermal_zone*/type, once a matching zone is found, read the temperature from the same directory (thermal_zone0/temp), not from a path derived from the type string.Similarly for hwmon: once a matching
hwmon*/nameis found, readhwmon*/temp1_inputfrom the samehwmon*directory.Option B — Fall back to
thermal_zone0/tempwhen named path failsIf the constructed path (e.g.,
/sys/class/thermal/cpu-thermal/temp) does not exist, fall back to reading directly from the zone that had the matching type.Option C — Parse temperature from
ubus call luci getTempInfooutputThe
autocoreTemperaturestrategy already has the correct data ("CPU: 63.1°C"). Extracting the numeric value with a simple regex (e.g.,(\d+\.?\d*)°C) would make this strategy work on any platform supported byautocore.Option C is the most robust since it delegates platform-specific logic to
autocore, which already handles the per-target differences correctly.Additional info
Confirmed via
strings /usr/sbin/quickstart:The
%vin/sys/class/thermal/%v/tempis filled with the type value (cpu-thermal), not the zone index — this is the source of the mismatch.