system-resource-monitor (dsh-hardware-monitor)
tyvtjhj/system-resource-monitor/plugins/dsh-hardware-monitor
DSH 本机硬件监控插件:侧边栏完整面板、输入框下方的 CPU/内存/GPU/温度挂件,以及 hardware_monitor_status 工具。数据来自另装的本地 Python 服务:需在仓库根用 install-dsh.ps1 安装(Windows、Python 3.10);只装 DSH 插件时面板与工具均不可用。
安装
dsh plugin --profile web add github:tyvtjhj/system-resource-monitor该插件位于仓库的 plugins/dsh-hardware-monitor 子路径下。
README
硬件资源监控
Windows 本机的轻量硬件看板:Flask 采集 → JSON → 单页 HTML 仪表盘,无前端框架、无构建步骤、无数据库。
目录结构
server.py 兼容入口(转发到 hardware_monitor.__main__)
hardware_monitor/ 后端包:config/state/sampler/api + collectors/ + platform/
tests/ pytest 测试(状态层 / API / 采集工具 / 平台能力)
monitor-mobile.html 主页面(服务根路径 / 返回它),带 GPU 引擎侧栏、手机自适应
monitor.html 桌面版页面(/monitor.html),无 GPU 侧栏
lhm_temp.ps1 子进程:加载 LibreHardwareMonitor 读 CPU 内核级温度
vendor/lhm/ 自带的 LibreHardwareMonitorLib(官方 0.9.6)+ 依赖 + 许可证说明
pyproject.toml 项目元数据与依赖声明
requirements.in 直接依赖(锁文件输入)
requirements.txt 跨平台锁文件(uv pip compile --universal 生成)
requirements-dev.in/.txt 开发依赖(pytest / pytest-cov / ruff / pip-tools)
scripts/ bootstrap、lock-deps、doctor 等维护脚本
.env.example 环境变量示例(不会自动加载)
run.bat / run-admin.bat Windows 启动脚本(优先 .venv)
run-hidden.vbs Windows 无控制台启动(pythonw,后台静默)
run.sh Linux / macOS 启动脚本
docs/ Code Wiki 与开发文档
plugin/ 插件与缩略组件(embed.js / widget / 集成中心 / 适配器)
plugins/dsh-hardware-monitor/ DSH 原生插件包(可用 install-dsh.ps1 安装;awesome-dsh-plugin 投稿入口)
T2 起后端拆分为
hardware_monitor包:platform/windows.py与platform/linux.py提供统一接口(提权、物理盘、网卡详情、温度来源、核显、能力表),python server.py/run.bat等旧启动方式保持不变。
运行
py -3.10 -m pip install -r requirements.txt # 首次
py -3.10 server.py # 默认自动申请管理员权限(UAC)
server.py 默认以管理员身份启动:检测到没有提权时,会用 UAC 重新拉起一个管理员实例并关闭当前窗口(CPU 内核级温度需要 PawnIO 驱动)。
不想每次弹 UAC(例如在 PyCharm 里调试):
py -3.10 server.py --no-elevate
# 或设环境变量
$env:MONITOR_NO_ELEVATE='1'; py -3.10 server.py
普通权限下功能照常,只是 CPU 温度降级为 ACPI 平台温度(标签显示 CPU ACPI)。run-admin.bat 保留,效果等价(先在批处理里提权,再启动,省一次 UAC 确认差异)。
同一端口只会跑一个实例:若 5678 已有监控在运行,新进程会提示占用它的 PID 并退出(Windows 允许重复绑端口,不检测的话会出现两个实例抢答)。
浏览器打开 http://127.0.0.1:5678/;手机/局域网访问 http://<本机IP>:5678/。
注意:PATH 上的
python可能不是装了依赖的那个解释器,用py -3.10或run.bat最稳。
run.bat / run-admin.bat 会把命令行参数透传给 server.py(例如 run-admin.bat --no-elevate);run-admin.bat 提权重启时同样会带上这些参数。
隐藏 Python 控制台(Windows)
后台常驻时不想看到 Python 控制台窗口:
- 双击
run-hidden.vbs:用.venv\Scripts\pythonw.exe启动,完全无窗口(默认--no-elevate,温度走 ACPI); - DSH 插件自动拉起:插件已优先使用
pythonw.exe,不再弹出 Python 窗口; - 需要内核级 CPU 温度:改用
run.bat/run-admin.bat提权启动(会保留控制台用于日志); - 已经有一个可见的 Python 窗口时,结束该
python.exe进程即可;若插件在运行,它会自动用隐藏方式重新拉起。
可复制环境(T1)
推荐用脚本一键创建项目内虚拟环境并安装锁定依赖:
# Windows
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\bootstrap.ps1
.\.venv\Scripts\python.exe scripts\doctor.py # 环境自检
# Linux / macOS
bash scripts/bootstrap.sh
.venv/bin/python scripts/doctor.py
run.bat/run-admin.bat/run.sh会优先使用.venv,找不到时回退py -3.10、py -3或python3;原有的py -3.10 server.py用法仍然可用。bootstrap默认升级 pip(部分代理环境会卡住),可加-SkipPipUpgrade(Windows)或SKIP_PIP_UPGRADE=1(Linux/macOS)跳过;-Recreate/RECREATE=1可重建.venv。scripts/doctor.py要求解释器属于项目.venv(用系统解释器会以退出码 1 提示);只诊断系统解释器时加--allow-system。
依赖锁文件维护
- 直接依赖写在
requirements.in/requirements-dev.in(保持 ASCII,避免中文 Windows 的 GBK 解码问题)。 - 锁文件由
scripts/lock-deps.ps1(Windows)或scripts/lock-deps.sh(Linux/macOS)生成:uv pip compile --universal --python-version 3.10 ...,保留sys_platform平台 marker,Windows/Linux 共用同一份锁。 - 说明:pip-tools 7.6.x 的
pip-compile没有--universal选项(任务书假设与实际不符),因此锁脚本要求安装 uv(pip install uv);缺少 uv 时脚本会拒绝执行,避免用平台锁覆盖跨平台锁、丢失sys_platformmarker。生成时先写临时文件并校验 uv 头与sys_platformmarker,通过后才替换已提交的锁文件。 - 升级依赖的流程:改
.in→ 运行锁脚本 → 检查git diff→ 提交requirements*.txt。 - 环境变量示例见
.env.example(程序不会自动加载)。 - 沙箱/代理受限环境若 pip 卡住,可先设
PIP_NO_CACHE_DIR=1、PIP_DISABLE_PIP_VERSION_CHECK=1再执行。 - 锁脚本的临时文件(
requirements*.txt.tmp*)已加入.gitignore;脚本正常结束会清理,中断残留会在下次运行或下次手工执行时自动移除。
插件接入
插件与缩略组件已实现(T4/T5),入口如下:
GET /plugin:集成中心(服务状态、缩略预览、配置片段生成、宿主能力矩阵)。GET /widget:可 iframe 嵌入的缩略监控页;GET /embed.js:宿主注入脚本(Shadow DOM + iframe +postMessage握手,token 不进 URL)。GET /api/plugin/manifest:插件元数据(版本、能力表、鉴权方式)。- 宿主适配器在
plugin/adapters/:Web/DSH 注入、用户脚本、VS Code 系扩展、Codex 状态行,见 plugin/README.md。
DSH 原生插件(推荐):plugins/dsh-hardware-monitor/ 提供输入框下方(composer dock)挂件、侧边栏完整面板与 hardware_monitor_status 工具。
一键安装(Windows)
在仓库目录直接运行:
powershell -ExecutionPolicy Bypass -File install-dsh.ps1
或双击 install-dsh.cmd。不在仓库目录时,脚本会自动克隆/下载到 %LOCALAPPDATA%\HardwareMonitor:
powershell -ExecutionPolicy Bypass -Command "$u='https://raw.githubusercontent.com/tyvtjhj/system-resource-monitor/main/install-dsh.ps1'; $p=Join-Path $env:TEMP 'install-dsh.ps1'; irm $u -OutFile $p; & $p"
GitHub 不可用时把 raw 地址换成 Gitee:
https://gitee.com/tyvtjhj/system-resource-monitor/raw/main/install-dsh.ps1。
前提:已安装 DSH 并至少启动过一次;Windows PowerShell 5.1+。脚本会复用 scripts/bootstrap.ps1 创建 .venv 并安装依赖,然后安装 DSH 插件、启动 5678 服务。
手动安装:powershell -ExecutionPolicy Bypass -File .\plugins\dsh-hardware-monitor\install.ps1;
刷新 DSH 页面(Ctrl+R)后输入框下方出现性能挂件,侧边栏出现「硬件监控」图标,点击打开原生完整面板。服务地址/Token 通过启动 DSH 前的
HARDWARE_MONITOR_URL / HARDWARE_MONITOR_TOKEN 配置。
页面脚本支持 ?api=<base-url> 指定后端,便于反向代理/远程开发。
如果不装插件,最稳妥的集成方式仍是直接打开本机 http://127.0.0.1:5678/。
配置(环境变量)
| 变量 | 默认 | 说明 |
|---|---|---|
MONITOR_HOST | 0.0.0.0 | 监听地址;改为 127.0.0.1 则只允许本机访问 |
MONITOR_PORT | 5678 | 端口 |
MONITOR_TOKEN | 空 | 设置后所有 /api/* 需带 X-Monitor-Token 头;页面用 ?token=xxx 打开即可 |
MONITOR_CORS_ORIGIN | * | 允许的跨域来源 |
MONITOR_LHM_DLL | 自动查找 | LibreHardwareMonitorLib.dll 路径(默认找 FanControl / LibreHardwareMonitor 安装目录) |
接口
| 路由 | 说明 |
|---|---|
GET / /monitor-mobile.html | 主页面 |
GET /monitor.html | 桌面版页面 |
GET /api/all | 一次返回全部指标(CPU/内存/磁盘/网络/温度/GPU/引擎/系统信息) |
GET /api/cpu /api/memory /api/disk | 单指标 |
GET /api/network-speed /api/temperature /api/gpu /api/gpu-engines /api/system | 单指标 |
GET /api/igpu | 核显:利用率、共享内存、各引擎占比 |
GET /api/processes | 进程内存排行(RSS,供内存侧栏使用,1.5 秒缓存) |
GET /api/network | 网卡详情(ipconfig):地址/掩码/网关/DNS/MAC/速率/MTU/DHCP(5 秒缓存) |
GET /api/start | 健康检查(只回报状态,不再拉起进程) |
单项采集失败时,/api/all 里会出现 <指标>_error 字段,其余指标照常返回。
数据来源
-
psutil:CPU 占用/频率/核心数、内存、分区用量、网卡计数、开机时间、进程与线程数 -
pynvml(NVIDIA):独立显卡型号、利用率、显存、温度 -
核显(所有厂商通用):Windows 性能计数器
GPU Engine(*)\Utilization Percentage按 LUID 汇总 3D/Compute/视频引擎,GPU Adapter Memory提供共享/专用内存;核显通过「共享内存最多的那块适配器」识别(核显没有独立显存、用的是系统内存,独显的共享用量通常接近 0),型号名优先取 WMIWin32_VideoController,取不到时只在候选唯一的情况下用注册表DriverDesc,否则显示「集成显卡」 -
nvidia-smi dmon -s u:CUDA/3D/编码/解码引擎利用率(-q为兜底解析) -
WMI:物理硬盘型号/接口/序列号 -
CPU 温度(四条来源依次尝试,命中哪个就把来源写进显示名与页面标签):
vendor/lhm/LibreHardwareMonitorLib.dll(内核级,需管理员) —— 项目自带官方 LHM 0.9.6 及其依赖,由lhm_temp.ps1子进程加载读CPU Package/Core Max,与 FanControl、HWiNFO 显示的数值一致;不再依赖 FanControl 安装目录(找不到自带库时才回退到 FanControl / LibreHardwareMonitor 的安装位置,或用MONITOR_LHM_DLL指定);root\LibreHardwareMonitor/root\OpenHardwareMonitorWMI 命名空间(若这些程序正在运行);- Windows 性能计数器
\Thermal Zone Information(*)\Temperature(ACPI 热区,不需要管理员,但它是「平台温度」,比核心温度低不少); Win32_TemperatureSensor—— 多数机器上该类为空。
页面上的 CPU 标签会显示来源:
CPU PKG(内核级)、CPU ACPI(平台热区)、CPU OHM/CPU WMI,鼠标悬停可看完整来源名。
设计要点
采样在单个后台线程里按固定节奏进行,结果写入内存快照;HTTP 请求只读快照,
所以接口响应恒定在毫秒级,不会因为 sleep 采样或 nvidia-smi 子进程而变慢:
| 频率 | 项目 |
|---|---|
| 1s | CPU、内存、网络速率、GPU、温度 |
| 3s | 磁盘、系统信息(WMI/进程枚举)、GPU 引擎(子进程较重) |
| 2s | 前端轮询(上一轮结束后再计时,避免请求叠加;页面隐藏时暂停) |
网络速率由相邻两次采样求差分得出,不再在请求里 sleep(0.5)。
已知限制
- WMI 在受限账号/沙箱下会返回「拒绝访问」,此时物理硬盘列表为空;其余指标不受影响。
- CPU 温度:Windows 没有统一的用户态接口。真正的核心温度(
CPU Package)只能由内核驱动读 MSR 得到,必须管理员权限;以普通权限运行时程序会自动降级到 ACPI 热区(CPU ACPI),那是「平台热区」温度,通常比核心温度低 10 ℃ 左右、粒度 1 ℃。要拿到与 FanControl/HWiNFO 一致的读数,请用run-admin.bat启动;启动日志会打印当前是「管理员」还是「普通用户」,/api/start里也有elevated字段。- FanControl 那段
LibreHardwareMonitorLib走的是 PawnIO 驱动(新版 LHM 已从 WinRing0 切换过来)。LHM 通过 P/Invoke 加载PawnIOLib.dll,而该文件只在C:\Program Files\PawnIO、不在 PATH 上,所以lhm_temp.ps1会先把 PawnIO 目录补进 PATH,否则即使提权也会读到空值。 - 若提权后仍显示
CPU ACPI,说明该驱动被系统拦(HVCI/易受攻击驱动黑名单)或 PawnIO 版本不匹配,此时可改用 HWiNFO 共享内存方案。
- FanControl 那段
- 只有 NVIDIA 显卡有利用率与引擎数据;AMD/Intel 只能显示型号。
- Windows 上
psutil把存活进程都报为 RUNNING,因此「运行中进程数」与「进程总数」通常相等。
本轮修复
len(psutil.process_iter())崩溃(生成器无len())→ 进程/线程统计恢复正常。- 前端引用了 HTML 中不存在的
uptimeDisplay/bootTimeDisplay/procRunning,会在刷新时抛异常并冻结仪表盘 → 补齐系统信息条,取值改为安全写入。 /api/start调用不存在的subprocess.is_alive()必然 500,且允许局域网触发拉起进程 → 改为健康检查,按钮改为「重新连接」。/monitor.html无路由(404)且写死过期 IP → 加路由并改为自动推导地址。- 请求内
sleep采样(/api/all约 730ms、/api/network-speed约 515ms)→ 后台采样 + 快照,实测 8ms。 - 前端 800ms 轮询掩盖不了 >800ms 的响应 → 改 2s 且串行调度,标签页隐藏时暂停。
nvidia-smi dmon列错位(把 mem% 当编码、enc% 当解码)→ 按gpu sm mem enc dec修正。- 每次请求新建 WMI 连接 → 线程内单例,只初始化一次。
- 安全:新增可选 token、可绑定 127.0.0.1;移除可被局域网利用的进程拉起入口。
- 补充
requirements.txt、run.bat、本说明;删除重复 CSS、无用隐藏 canvas、未被try覆盖的import wmi。 createGauge()收了color参数却没把它放进返回对象,setGauge()里gauge.color恒为undefined,占用率 ≤60% 时描边变成非法值被丢弃(CSS 也没有兜底 stroke),整圈弧线消失 —— 这就是「CPU 圆环有时不显示」的原因;已改为返回color、创建时预置合法描边,并对NaN/越界值做钳制。- 温度卡片新增 CPU 温度:
/api/temperature增加cpu/gpu两个已分类字段(sensors保持兼容),CPU 温度按「LibreHardwareMonitor → ACPI 热区性能计数器 → WMI 传感器」依次尝试,读不到时显示--;无值不缓存、每轮重试,枚举失败则 60 秒后再试,避免开机初期一直空着。 - 温度趋势拆成两条曲线:
Sparkline支持多序列(共用一条纵轴便于比较),温度卡片同时画 CPU(蓝)与 GPU(红),数值标签前加了同色图例点;绘制改为「先画全部渐变面积、再画全部折线」,避免两条曲线接近时后画的面积盖住先画的线。 - CPU 温度改用内核级来源:新增
lhm_temp.ps1+LhmReader,以子进程加载LibreHardwareMonitorLib.dll读CPU Package(与 FanControl 同源,需管理员);普通权限下子进程会明确回报「读数全空、需要管理员」,上层只提示一次并回退 ACPI,不会每轮重试。页面标签显示来源(CPU PKG/CPU ACPI),悬停看完整名称;新增run-admin.bat一键提权启动。 - 默认自动提权:
server.py启动时若未提权,用ShellExecuteW("runas")重新拉起自己(带--elevated防递归),失败则在原进程提示并继续以普通权限运行;可用--no-elevate或MONITOR_NO_ELEVATE=1关闭。子进程还会把 LHM 依赖的PawnIOLib.dll所在目录补进PATH(它不在 PATH 上,否则提权了也读不到)。 - 单实例检测:启动前探测端口上是否已有本服务(Windows 允许两个进程绑同一端口),有则打印占用者 PID 并退出;
/api/start增加elevated字段,启动日志打印「运行权限: 管理员/普通用户」。 - 新增核显(iGPU)卡片:
GpuPerfReader用 Windows GPU 性能计数器读出集成显卡的利用率和共享内存(不依赖 NVML,AMD/Intel 通用),/api/all增加igpu字段与/api/igpu路由,两页都新增青色仪表卡片。核显靠「共享内存最多的适配器」识别;首次建查询约 250ms,之后每次约 1ms,每 20 秒重建一次以跟上进程变化。 - 新增内存侧栏:点击 MEMORY 卡片从右侧滑出,列出进程内存排行,支持「按占用 / 按名称」排序,以及「专用 / 工作集」两个口径切换;侧栏打开时每 2 秒刷新,关闭即停止轮询。后端
/api/processes(1.5 秒缓存)按进程名合并同名进程(Edge/Chrome 的多个渲染进程会合并成一行并标注 ×N),并同时返回两个口径:private_mb(USS,≈ 任务管理器「内存」列的专用工作集)与working_set_mb(工作集,含共享页,数值偏大)。另外四/三个侧栏都支持#memory/#disk/#cores/#gpu锚点直接展开。 - 新增网络侧栏(ipconfig):点击 NETWORK 卡片滑出,按网卡分卡片显示 IPv4/IPv6、子网掩码、默认网关、DNS、MAC、链路速率、MTU、DHCP 与设备名。数据由
Win32_NetworkAdapterConfiguration(网关/DNS/DHCP)与 psutil(友好名称/速率/MTU)按 MAC + IP 关联合并,WMI 不可用时退回纯 psutil(此时无网关/DNS)。/api/network带 5 秒缓存,打开时拉一次;锚点#network。 - CPU 温度不再依赖 FanControl:把官方 LibreHardwareMonitor 0.9.6 的
LibreHardwareMonitorLib.dll及依赖(HidSharp、System.Memory 等)放进vendor/lhm/,find_dll()优先用它,其次才找 FanControl / LibreHardwareMonitor 安装目录;附NOTICE.txt说明来源与 MPL-2.0 许可。同时修掉一个真实坑:把 DLL 复制到项目目录后,Assembly.LoadFrom会报0x80131515(Operation is not supported),改为按字节加载Assembly.Load(File.ReadAllBytes(...))并注册AssemblyResolve解析同目录依赖,两个位置都验证可用。 - 暗色模式:页头右上角新增主题开关(亮色显示「🌙 暗色」,暗色显示「☀️ 亮色」),选择存
localStorage,首次访问跟随系统prefers-color-scheme;<head>里有一段极小脚本在首次绘制前应用主题,避免闪白。实现上把散落的写死浅色收敛成语义变量(--track/--track-strong/--chip-good-*/--error-bg),暗色只需覆盖一组变量即可同时作用于卡片、仪表底环、进度条、徽标与侧栏;另支持?theme=dark|light直接固定主题。
许可证
本项目原创代码采用 MIT 许可证,见 LICENSE。
vendor/lhm 为 LibreHardwareMonitor 0.9.6(MPL-2.0)及其依赖,按各自上游许可分发,
不属于 MIT 范围;详见 vendor/lhm/NOTICE.txt 与 NOTICE。