当前位置:首页 > 其他

X-WRT 概览页显示 CPU 温度与 WiFi 温度

1个月前 (09-13)其他920

背景

X-WRT 26.04(Tenda BE12 Pro,mediatek/filogic,内核 6.18.44)的 LuCI 概览页默认不显示温度。底层数据其实一直存在:

  • CPU 温度:/sys/class/thermal/thermal_zone0/temp(毫摄氏度)
  • WiFi 温度:/sys/class/ieee80211/phy0/hwmon*/temp1_input(毫摄氏度,2.4G / 5G 各一个)

目标:在概览页显示一行温度信息,不破坏原有页面结构,固件升级后重新放回文件即可恢复。


方案

利用 X-WRT 新版 LuCI 概览页的模块化加载机制:

  • 主 index.ut(ucode 模板)渲染时会加载 admin_status/luaindex 子视图
  • luaindex.htm(Lua 视图)会扫描 /usr/lib/lua/luci/view/admin_status/index/ 目录,把所有 .htm 片段 include 进来

我们在这个目录里新建一个片段 99-temperature.htm,它负责:

  1. 读 CPU 和 WiFi 温度
  2. 把值写进 window.__cpuTemp / window.__wifiTemp
  3. 在 #view 容器外面创建一个横条,显示这两个温度

关键设计:横条插在 #view 外面,因为 LuCI 概览页每 5 秒轮询重绘 #view 内容,插在里面会被冲掉。


完整文件

/usr/lib/lua/luci/view/admin_status/index/99-temperature.htm

<%
-- CPU 温度
local f = io.open("/sys/class/thermal/thermal_zone0/temp", "r")
local craw = f and f:read("*l") or nil
if f then f:close() end
local ctemp = craw and string.format("%.1f", tonumber(craw) / 1000) or "N/A"

-- WiFi 温度:扫所有 /sys/class/ieee80211/phy*/hwmon*/temp1_input
local wlist = {}
local p = io.popen("ls -d /sys/class/ieee80211/phy*/hwmon*/temp1_input 2>/dev/null")
if p then
    for path in p:lines() do
        local wf = io.open(path, "r")
        local wraw = wf and wf:read("*l") or nil
        if wf then wf:close() end
        if wraw and tonumber(wraw) then
            wlist[#wlist + 1] = string.format("%.1f", tonumber(wraw) / 1000)
        end
    end
    p:close()
end
local wtemp = #wlist > 0 and table.concat(wlist, " / ") or "N/A"
%>
<script>
window.__cpuTemp = '<%=ctemp%> °C';
window.__wifiTemp = '<%=wtemp%> °C';

(function() {
    if (!document.body || document.body.getAttribute('data-page') !== 'admin-status-overview')
        return;

    function build() {
        var bar = document.getElementById('cpu-temp-bar');
        if (!bar) {
            var view = document.getElementById('view');
            if (!view || !view.parentNode) return;
            bar = document.createElement('div');
            bar.id = 'cpu-temp-bar';
            bar.style.cssText = 'margin:8px 0;padding:8px 12px;background:#fff3e0;border:1px solid #ffcc80;border-radius:6px;font-size:14px;';
            view.parentNode.insertBefore(bar, view);
        }
        bar.textContent = 'CPU 温度: ' + window.__cpuTemp + '    WiFi 温度: ' + window.__wifiTemp;
    }

    setInterval(build, 500);
    build();
})();
</script>

部署步骤

# 1. 写入片段文件
cat > /usr/lib/lua/luci/view/admin_status/index/99-temperature.htm <<'EOF'
(上面完整内容)
EOF

# 2. 清 LuCI 模板缓存
rm -rf /tmp/luci-*

# 3. 重启 web 服务
/etc/init.d/uwsgi restart 2>/dev/null
/etc/init.d/uhttpd restart 2>/dev/null

浏览器 Ctrl+F5 强刷。


效果

概览页“状态”标题下方、原来“系统”表格上方,出现一条橙色边框横条:

CPU 温度: 65.7 °C    WiFi 温度: 49.0 / 53.0 °C

其中 49.0 / 53.0 是两个频段(2.4G / 5G)的温度。

温度值在页面加载时读一次。要看最新值,刷新页面即可。


原理

1. 为什么用 admin_status/index/ 目录

X-WRT 26.04 的 LuCI 概览页用了模块化视图。/usr/lib/lua/luci/view/admin_status/luaindex.htm 内容是:

local incdir = util.libpath() .. "/view/admin_status/index/"
if fs.access(incdir) then
    for inc in fs.dir(incdir) do
        if inc:match("%.htm$") then
            includes[#includes + 1] = inc:gsub("%.htm$", "")
        end
    end
    for _, inc in luci.util.vspairs(includes) do
        include("admin_status/index/" .. inc)
    end
end

只要目录存在,里面的 .htm 就会被自动加载。文件名的字母序决定加载顺序,99- 表示排在最后。

2. 为什么插在 #view 外面

概览页的 <div id="view"> 每 5 秒被 LuCI 轮询重绘。任何插在它内部的元素,下一轮就会被抹掉。插在它前面或后面(即 #view 的兄弟节点),就不会被影响。

3. 为什么用 setInterval 而不是一次性插入

横条第一次创建后,setInterval 每 500ms 检查一次 document.getElementById('cpu-temp-bar'),不存在就重建。这样即使页面发生某种重绘,横条也能自动恢复。


踩坑记录

尝试 结果 原因
用 sed 往 index.htm 插 <tr> 失败 index.htm 只是一个“加载器”,实际内容是异步渲染的
往 #view 里的表格插 <tr> 显示后被冲掉 5 秒轮询会重绘整个 #view
用 MutationObserver + 轮询移动 <tr> 失败 .includes 里的源元素被 LuCI 的 DOM 渲染逻辑清掉,getElementById 返回 null
插在 #view 外面的横条 ✅ 成功 不受 #view 重绘影响

排查过程中最有用的三条 Console 命令:

// 1. 看视图容器是否存在
document.getElementById('view') ? 'ok' : 'missing'

// 2. 看概览页表格结构
document.querySelectorAll('#view tr').length

// 3. 看温度行是否被保留
document.getElementById('cpu-temp-src') ? 'found' : 'not found'

hwmon 路径确认:

cat /sys/class/ieee80211/phy*/hwmon*/temp1_input
# 输出示例:
# 49000
# 53000
# 表示 49.0 °C / 53.0 °C,两个频段

备份与恢复

备份(修改前做):

cp /usr/lib/lua/luci/view/admin_status/index/99-temperature.htm /root/99-temperature.htm.bak 2>/dev/null

卸载(不想要了):

rm /usr/lib/lua/luci/view/admin_status/index/99-temperature.htm
rm -rf /tmp/luci-*
/etc/init.d/uwsgi restart 2>/dev/null
/etc/init.d/uhttpd restart 2>/dev/null

升级固件后:/usr/lib/lua/luci/view/admin_status/index/ 目录会被重置,把备份的 99-temperature.htm 放回去,清缓存重启即可。


可扩展方向

  • 动态刷新:当前是页面加载时读一次。若需不刷新页面就自动更新,可以加一个小 cgi 接口(如 /www/cgi-bin/temp),前端用 fetch 每几秒请求一次。
  • 区分频段:如果 hwmon 顺序固定(如 hwmon0 对应 2.4G、hwmon1 对应 5G),可以把 49.0 / 53.0 改成 2.4G 49.0 / 5G 53.0。
  • 显示风扇转速 / 电压:如果硬件支持,同样可以读 /sys/class/hwmon/ 下的对应文件。

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。