@daimazun/hardware-info 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +907 -0
- package/bin/OpenHardwareMonitorLib.dll +0 -0
- package/index.js +192 -0
- package/lib/allhardware.js +35 -0
- package/lib/backend-usage.js +103 -0
- package/lib/battery.js +76 -0
- package/lib/disk-watcher.js +156 -0
- package/lib/disk.js +77 -0
- package/lib/gpu.js +70 -0
- package/lib/hardware-service.js +284 -0
- package/lib/memory.js +59 -0
- package/lib/monitor.js +176 -0
- package/lib/network.js +75 -0
- package/lib/ohm-daemon.js +208 -0
- package/lib/ohm.js +41 -0
- package/lib/perfctr.js +43 -0
- package/lib/process-icon.js +76 -0
- package/lib/process-ops.js +218 -0
- package/lib/processes.js +232 -0
- package/lib/ps.js +93 -0
- package/lib/public-ip.js +124 -0
- package/lib/services.js +43 -0
- package/lib/sysinfo-daemon.js +188 -0
- package/lib/system.js +77 -0
- package/lib/usb.js +49 -0
- package/lib/win32-procs.js +241 -0
- package/package.json +50 -0
- package/scripts/get-all-hardware.ps1 +98 -0
- package/scripts/get-lhm-temp.ps1 +119 -0
- package/scripts/ohm-daemon.ps1 +141 -0
- package/scripts/sysinfo-daemon.ps1 +386 -0
- package/test/check-bugs.js +182 -0
- package/test/public/gj.pay.ali.jpg +0 -0
- package/test/public/gj.pay.wx.jpg +0 -0
- package/test/public/index.html +996 -0
- package/test/public/zdl.pay.ali.jpg +0 -0
- package/test/public/zdl.pay.wx.jpg +0 -0
- package/test/scan-encoding.js +77 -0
- package/test/server.js +255 -0
- package/test/test-all.js +180 -0
- package/test/test-daemon.js +51 -0
- package/test/test-kill-name.js +30 -0
- package/test/test-monitor.js +78 -0
- package/test/test-new-features.js +93 -0
- package/test/test-service.js +91 -0
- package/test/test-sysinfo-daemon.js +95 -0
- package/test/test.js +63 -0
package/lib/system.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const { runPsJson } = require('./ps');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 获取系统信息:OS、计算机、BIOS、主板、CPU、开机时间、电源计划
|
|
5
|
+
*/
|
|
6
|
+
async function getSystemInfo() {
|
|
7
|
+
const cmd = `
|
|
8
|
+
$os = Get-CimInstance Win32_OperatingSystem
|
|
9
|
+
$cs = Get-CimInstance Win32_ComputerSystem
|
|
10
|
+
$bios = Get-CimInstance Win32_BIOS
|
|
11
|
+
$board = Get-CimInstance Win32_BaseBoard
|
|
12
|
+
$cpu = Get-CimInstance Win32_Processor | Select-Object -First 1
|
|
13
|
+
$powerPlan = powercfg /getactivescheme | ForEach-Object { ($_ -replace '.*\\(|\\).*', '') }
|
|
14
|
+
$bootTime = $os.LastBootUpTime
|
|
15
|
+
$uptime = (Get-Date) - $bootTime
|
|
16
|
+
|
|
17
|
+
[PSCustomObject]@{
|
|
18
|
+
os = [PSCustomObject]@{
|
|
19
|
+
caption = $os.Caption
|
|
20
|
+
version = $os.Version
|
|
21
|
+
buildNumber = $os.BuildNumber
|
|
22
|
+
architecture = $os.OSArchitecture
|
|
23
|
+
installDate = $os.InstallDate.ToString('o')
|
|
24
|
+
lastBootUpTime = $bootTime.ToString('o')
|
|
25
|
+
uptimeSeconds = [math]::Round($uptime.TotalSeconds)
|
|
26
|
+
uptimeDays = [math]::Round($uptime.TotalDays, 2)
|
|
27
|
+
systemDrive = $os.SystemDrive
|
|
28
|
+
windowsDirectory = $os.WindowsDirectory
|
|
29
|
+
}
|
|
30
|
+
computer = [PSCustomObject]@{
|
|
31
|
+
manufacturer = $cs.Manufacturer
|
|
32
|
+
model = $cs.Model
|
|
33
|
+
name = $cs.Name
|
|
34
|
+
domain = $cs.Domain
|
|
35
|
+
workgroup = $cs.Workgroup
|
|
36
|
+
totalPhysicalMemoryGB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 2)
|
|
37
|
+
numberOfProcessors = $cs.NumberOfProcessors
|
|
38
|
+
numberOfLogicalProcessors = $cs.NumberOfLogicalProcessors
|
|
39
|
+
systemType = $cs.SystemType
|
|
40
|
+
pcSystemType = $cs.PCSystemType
|
|
41
|
+
}
|
|
42
|
+
bios = [PSCustomObject]@{
|
|
43
|
+
manufacturer = $bios.Manufacturer
|
|
44
|
+
version = $bios.SMBIOSBIOSVersion
|
|
45
|
+
releaseDate = if ($bios.ReleaseDate) { $bios.ReleaseDate.ToString('o') } else { $null }
|
|
46
|
+
serialNumber = $bios.SerialNumber
|
|
47
|
+
}
|
|
48
|
+
motherboard = [PSCustomObject]@{
|
|
49
|
+
manufacturer = $board.Manufacturer
|
|
50
|
+
product = $board.Product
|
|
51
|
+
version = $board.Version
|
|
52
|
+
serialNumber = $board.SerialNumber
|
|
53
|
+
}
|
|
54
|
+
cpu = [PSCustomObject]@{
|
|
55
|
+
name = $cpu.Name
|
|
56
|
+
manufacturer = $cpu.Manufacturer
|
|
57
|
+
description = $cpu.Description
|
|
58
|
+
architecture = $cpu.Architecture
|
|
59
|
+
numberOfCores = $cpu.NumberOfCores
|
|
60
|
+
numberOfLogicalProcessors = $cpu.NumberOfLogicalProcessors
|
|
61
|
+
maxClockSpeedMHz = $cpu.MaxClockSpeed
|
|
62
|
+
currentClockSpeedMHz = $cpu.CurrentClockSpeed
|
|
63
|
+
l2CacheKB = $cpu.L2CacheSize
|
|
64
|
+
l3CacheKB = $cpu.L3CacheSize
|
|
65
|
+
socketDesignation = $cpu.SocketDesignation
|
|
66
|
+
processorId = $cpu.ProcessorId
|
|
67
|
+
virtualizationEnabled = $cpu.VirtualizationFirmwareEnabled
|
|
68
|
+
}
|
|
69
|
+
powerPlan = $powerPlan
|
|
70
|
+
timezone = (Get-TimeZone).Id
|
|
71
|
+
currentTime = (Get-Date).ToString('o')
|
|
72
|
+
} | ConvertTo-Json -Depth 6 -Compress
|
|
73
|
+
`;
|
|
74
|
+
return runPsJson(cmd);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = { getSystemInfo };
|
package/lib/usb.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { runPsJson } = require('./ps');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 获取 USB 设备列表
|
|
5
|
+
*/
|
|
6
|
+
async function getUsbInfo() {
|
|
7
|
+
const cmd = `
|
|
8
|
+
$controllers = Get-CimInstance Win32_USBController
|
|
9
|
+
$devices = Get-CimInstance Win32_PnPEntity | Where-Object {
|
|
10
|
+
$_.PNPDeviceID -like 'USB\\*' -and $_.Name -notlike 'USB Root Hub*' -and $_.Name -notlike 'USB Composite Device*'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
$controllerList = @()
|
|
14
|
+
foreach ($c in $controllers) {
|
|
15
|
+
$controllerList += [PSCustomObject]@{
|
|
16
|
+
name = $c.Name
|
|
17
|
+
manufacturer = $c.Manufacturer
|
|
18
|
+
status = $c.Status
|
|
19
|
+
deviceId = $c.DeviceID
|
|
20
|
+
pnpDeviceId = $c.PNPDeviceID
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
$deviceList = @()
|
|
25
|
+
foreach ($d in $devices) {
|
|
26
|
+
$deviceList += [PSCustomObject]@{
|
|
27
|
+
name = $d.Name
|
|
28
|
+
description = $d.Description
|
|
29
|
+
manufacturer = $d.Manufacturer
|
|
30
|
+
status = $d.Status
|
|
31
|
+
service = $d.Service
|
|
32
|
+
deviceId = $d.DeviceID
|
|
33
|
+
pnpDeviceId = $d.PNPDeviceID
|
|
34
|
+
classGuid = $d.ClassGuid
|
|
35
|
+
hardwareId = if ($d.HardwareID) { $d.HardwareID[0] } else { $null }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
[PSCustomObject]@{
|
|
40
|
+
controllers = $controllerList
|
|
41
|
+
controllerCount = $controllerList.Count
|
|
42
|
+
devices = $deviceList
|
|
43
|
+
deviceCount = $deviceList.Count
|
|
44
|
+
} | ConvertTo-Json -Depth 4 -Compress
|
|
45
|
+
`;
|
|
46
|
+
return runPsJson(cmd, { timeout: 15000 });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { getUsbInfo };
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* win32-procs.js — koffi 直调 Win32 API 枚举进程
|
|
3
|
+
*
|
|
4
|
+
* 性能:264 进程 ~16ms(vs PowerShell WMI ~590ms / 常驻 WMI ~115ms)
|
|
5
|
+
* 高频调用友好:进程内调用,无子进程开销
|
|
6
|
+
*
|
|
7
|
+
* 涉及 API:
|
|
8
|
+
* kernel32: CreateToolhelp32Snapshot / Process32FirstW / Process32NextW
|
|
9
|
+
* OpenProcess / CloseHandle
|
|
10
|
+
* QueryFullProcessImageNameW
|
|
11
|
+
* GetProcessTimes
|
|
12
|
+
* psapi: GetProcessMemoryInfo
|
|
13
|
+
*
|
|
14
|
+
* CPU% 口径:与任务管理器一致
|
|
15
|
+
* (Δkernel+Δuser 100ns ticks) / (Δwall 100ns ticks) × 100
|
|
16
|
+
* 单进程 CPU% 上限 = 核心数 × 100%
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const koffi = require('koffi');
|
|
20
|
+
const { existsSync, statSync } = require('fs');
|
|
21
|
+
|
|
22
|
+
const TH32CS_SNAPPROCESS = 0x00000002;
|
|
23
|
+
const PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
|
|
24
|
+
const PROCESS_VM_READ = 0x0010;
|
|
25
|
+
const MAX_PATH = 260;
|
|
26
|
+
|
|
27
|
+
// ---- Win32 结构体 ----
|
|
28
|
+
const PROCESSENTRY32W = koffi.struct('PROCESSENTRY32W', {
|
|
29
|
+
dwSize: 'uint32',
|
|
30
|
+
cntUsage: 'uint32',
|
|
31
|
+
th32ProcessID: 'uint32',
|
|
32
|
+
th32DefaultHeapID: 'uint64',
|
|
33
|
+
th32ModuleID: 'uint32',
|
|
34
|
+
cntThreads: 'uint32',
|
|
35
|
+
th32ParentProcessID: 'uint32',
|
|
36
|
+
pcPriClassBase: 'int32',
|
|
37
|
+
dwFlags: 'uint32',
|
|
38
|
+
szExeFile: koffi.array('uint16', MAX_PATH)
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const FILETIME = koffi.struct('FILETIME', {
|
|
42
|
+
dwLowDateTime: 'uint32',
|
|
43
|
+
dwHighDateTime: 'uint32'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const PROCESS_MEMORY_COUNTERS = koffi.struct('PROCESS_MEMORY_COUNTERS', {
|
|
47
|
+
cb: 'uint32',
|
|
48
|
+
PageFaultCount: 'uint32',
|
|
49
|
+
PeakWorkingSetSize: 'uint64',
|
|
50
|
+
WorkingSetSize: 'uint64',
|
|
51
|
+
QuotaPeakPagedPoolUsage: 'uint64',
|
|
52
|
+
QuotaPagedPoolUsage: 'uint64',
|
|
53
|
+
QuotaPeakNonPagedPoolUsage: 'uint64',
|
|
54
|
+
QuotaNonPagedPoolUsage: 'uint64',
|
|
55
|
+
PagefileUsage: 'uint64',
|
|
56
|
+
PeakPagefileUsage: 'uint64'
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// ---- 加载 DLL 并声明函数 ----
|
|
60
|
+
const kernel32 = koffi.load('kernel32.dll');
|
|
61
|
+
const psapi = koffi.load('psapi.dll');
|
|
62
|
+
|
|
63
|
+
const PE32_PTR = koffi.inout(koffi.pointer(PROCESSENTRY32W));
|
|
64
|
+
const CreateToolhelp32Snapshot = kernel32.func('CreateToolhelp32Snapshot', 'void *', ['uint32', 'uint32']);
|
|
65
|
+
const Process32FirstW = kernel32.func('Process32FirstW', 'int', ['void *', PE32_PTR]);
|
|
66
|
+
const Process32NextW = kernel32.func('Process32NextW', 'int', ['void *', PE32_PTR]);
|
|
67
|
+
const CloseHandle = kernel32.func('CloseHandle', 'int', ['void *']);
|
|
68
|
+
const OpenProcess = kernel32.func('OpenProcess', 'void *', ['uint32', 'int', 'uint32']);
|
|
69
|
+
const QueryFullProcessImageNameW = kernel32.func(
|
|
70
|
+
'QueryFullProcessImageNameW',
|
|
71
|
+
'int',
|
|
72
|
+
['void *', 'uint32', koffi.out(koffi.pointer('uint16')), koffi.inout(koffi.pointer('uint32'))]
|
|
73
|
+
);
|
|
74
|
+
const FT_PTR = koffi.out(koffi.pointer(FILETIME));
|
|
75
|
+
const GetProcessTimes = kernel32.func('GetProcessTimes', 'int', ['void *', FT_PTR, FT_PTR, FT_PTR, FT_PTR]);
|
|
76
|
+
const PMC_PTR = koffi.inout(koffi.pointer(PROCESS_MEMORY_COUNTERS));
|
|
77
|
+
const GetProcessMemoryInfo = psapi.func('GetProcessMemoryInfo', 'int', ['void *', PMC_PTR, 'uint32']);
|
|
78
|
+
|
|
79
|
+
// ---- 辅助函数 ----
|
|
80
|
+
function wcharToStr(arr) {
|
|
81
|
+
let end = 0;
|
|
82
|
+
while (end < arr.length && arr[end] !== 0) end++;
|
|
83
|
+
if (end === 0) return '';
|
|
84
|
+
return Buffer.from(arr.slice(0, end).buffer).toString('utf16le');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function filetimeToNumber(ft) {
|
|
88
|
+
return (ft.dwHighDateTime * 0x100000000) + ft.dwLowDateTime;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---- CPU% 基线缓存 ----
|
|
92
|
+
let cpuBaseline = null; // Map<pid, { kernel, user, wallTime }>
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 枚举所有进程(原始数据)
|
|
96
|
+
* @returns {Array<{pid, name, threads, parentPid, workingSet, privateUsage, exePath, kernelTime, userTime}>}
|
|
97
|
+
*/
|
|
98
|
+
function sampleProcesses() {
|
|
99
|
+
const snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
100
|
+
if (!snapshot || snapshot === 0) {
|
|
101
|
+
throw new Error('CreateToolhelp32Snapshot 失败');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const entry = { dwSize: PROCESSENTRY32W.size };
|
|
105
|
+
const processes = [];
|
|
106
|
+
|
|
107
|
+
if (Process32FirstW(snapshot, entry)) {
|
|
108
|
+
do {
|
|
109
|
+
const pid = entry.th32ProcessID;
|
|
110
|
+
const name = wcharToStr(entry.szExeFile);
|
|
111
|
+
const threads = entry.cntThreads;
|
|
112
|
+
const parentPid = entry.th32ParentProcessID;
|
|
113
|
+
|
|
114
|
+
let workingSet = 0;
|
|
115
|
+
let privateUsage = 0;
|
|
116
|
+
let exePath = '';
|
|
117
|
+
let kernelTime = 0;
|
|
118
|
+
let userTime = 0;
|
|
119
|
+
|
|
120
|
+
// 打开进程获取详细信息(系统进程可能失败,跳过即可)
|
|
121
|
+
const handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, 0, pid);
|
|
122
|
+
if (handle && handle !== 0) {
|
|
123
|
+
try {
|
|
124
|
+
// exe 路径
|
|
125
|
+
const buf = Buffer.alloc(MAX_PATH * 2);
|
|
126
|
+
const sizePtr = [MAX_PATH];
|
|
127
|
+
if (QueryFullProcessImageNameW(handle, 0, buf, sizePtr)) {
|
|
128
|
+
exePath = buf.toString('utf16le', 0, sizePtr[0] * 2).replace(/\0+$/, '');
|
|
129
|
+
}
|
|
130
|
+
} catch (e) { /* 忽略 */ }
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
// 进程时间(用于 CPU%)
|
|
134
|
+
const ct = {};
|
|
135
|
+
const et = {};
|
|
136
|
+
const kt = {};
|
|
137
|
+
const ut = {};
|
|
138
|
+
if (GetProcessTimes(handle, ct, et, kt, ut)) {
|
|
139
|
+
kernelTime = filetimeToNumber(kt);
|
|
140
|
+
userTime = filetimeToNumber(ut);
|
|
141
|
+
}
|
|
142
|
+
} catch (e) { /* 忽略 */ }
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// 内存信息
|
|
146
|
+
const pmc = { cb: PROCESS_MEMORY_COUNTERS.size };
|
|
147
|
+
if (GetProcessMemoryInfo(handle, pmc, PROCESS_MEMORY_COUNTERS.size)) {
|
|
148
|
+
workingSet = pmc.WorkingSetSize;
|
|
149
|
+
privateUsage = pmc.PagefileUsage;
|
|
150
|
+
}
|
|
151
|
+
} catch (e) { /* 忽略 */ }
|
|
152
|
+
|
|
153
|
+
CloseHandle(handle);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
processes.push({
|
|
157
|
+
pid,
|
|
158
|
+
name,
|
|
159
|
+
threads,
|
|
160
|
+
parentPid,
|
|
161
|
+
workingSet,
|
|
162
|
+
privateUsage,
|
|
163
|
+
exePath,
|
|
164
|
+
kernelTime,
|
|
165
|
+
userTime
|
|
166
|
+
});
|
|
167
|
+
} while (Process32NextW(snapshot, entry));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
CloseHandle(snapshot);
|
|
171
|
+
return processes;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 计算每个进程的 CPU%(基于两次采样差值)
|
|
176
|
+
* 第一次调用时基线为空,返回全 0;第二次起正常
|
|
177
|
+
* @param {Array} raw - sampleProcesses() 的返回值
|
|
178
|
+
* @returns {Map<pid, cpuPercent>}
|
|
179
|
+
*/
|
|
180
|
+
function computeCpuPercent(raw) {
|
|
181
|
+
const now = Date.now();
|
|
182
|
+
const cpuMap = new Map();
|
|
183
|
+
|
|
184
|
+
if (!cpuBaseline) {
|
|
185
|
+
// 第一次采样,建立基线
|
|
186
|
+
cpuBaseline = new Map();
|
|
187
|
+
for (const p of raw) {
|
|
188
|
+
cpuBaseline.set(p.pid, { kernel: p.kernelTime, user: p.userTime, wallTime: now });
|
|
189
|
+
cpuMap.set(p.pid, 0);
|
|
190
|
+
}
|
|
191
|
+
return cpuMap;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const newBaseline = new Map();
|
|
195
|
+
for (const p of raw) {
|
|
196
|
+
const prev = cpuBaseline.get(p.pid);
|
|
197
|
+
let cpuPercent = 0;
|
|
198
|
+
if (prev) {
|
|
199
|
+
const dKernel = p.kernelTime - prev.kernel;
|
|
200
|
+
const dUser = p.userTime - prev.user;
|
|
201
|
+
const dWall = (now - prev.wallTime) * 10000; // ms → 100ns ticks
|
|
202
|
+
if (dWall > 0) {
|
|
203
|
+
cpuPercent = ((dKernel + dUser) / dWall) * 100;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
cpuMap.set(p.pid, Math.max(0, cpuPercent));
|
|
207
|
+
newBaseline.set(p.pid, { kernel: p.kernelTime, user: p.userTime, wallTime: now });
|
|
208
|
+
}
|
|
209
|
+
cpuBaseline = newBaseline;
|
|
210
|
+
return cpuMap;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 重置 CPU% 基线(下次采样重新建立)
|
|
215
|
+
*/
|
|
216
|
+
function resetCpuBaseline() {
|
|
217
|
+
cpuBaseline = null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 获取 exe 文件的安装时间(创建时间)
|
|
222
|
+
* @param {string} exePath
|
|
223
|
+
* @returns {string} 'YYYY-MM-DD' 或空串
|
|
224
|
+
*/
|
|
225
|
+
function getInstallTime(exePath) {
|
|
226
|
+
if (!exePath || !existsSync(exePath)) return '';
|
|
227
|
+
try {
|
|
228
|
+
const stat = statSync(exePath);
|
|
229
|
+
const d = stat.birthtime;
|
|
230
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
|
231
|
+
} catch {
|
|
232
|
+
return '';
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = {
|
|
237
|
+
sampleProcesses,
|
|
238
|
+
computeCpuPercent,
|
|
239
|
+
resetCpuBaseline,
|
|
240
|
+
getInstallTime
|
|
241
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@daimazun/hardware-info",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Cross-platform hardware information & monitor library — CPU temperature/load/clock/power, memory, disk, motherboard, GPU via OpenHardwareMonitor (Windows) with Linux/macOS support planned",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"os": [
|
|
8
|
+
"win32"
|
|
9
|
+
],
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=14.0.0"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cpu",
|
|
15
|
+
"temperature",
|
|
16
|
+
"windows",
|
|
17
|
+
"openhardwaremonitor",
|
|
18
|
+
"hardware",
|
|
19
|
+
"monitor",
|
|
20
|
+
"sensor",
|
|
21
|
+
"daemon",
|
|
22
|
+
"msr",
|
|
23
|
+
"dts"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"author": "高健 <gaojianstyle@163.com>",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "node test/test.js",
|
|
29
|
+
"test:daemon": "node test/test-daemon.js",
|
|
30
|
+
"test:cpu-monitor": "node test/test-monitor.js",
|
|
31
|
+
"test:all": "node test/test-all.js",
|
|
32
|
+
"test:new": "node test/test-new-features.js",
|
|
33
|
+
"test:monitor": "node test/test-service.js",
|
|
34
|
+
"server": "node test/server.js"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"index.js",
|
|
38
|
+
"lib/",
|
|
39
|
+
"scripts/",
|
|
40
|
+
"bin/",
|
|
41
|
+
"README.md",
|
|
42
|
+
"!**/*.png",
|
|
43
|
+
"test/"
|
|
44
|
+
],
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"express": "^5.2.1",
|
|
47
|
+
"fkill": "^10.0.3",
|
|
48
|
+
"koffi": "^3.2.1"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[string]$DllPath = ""
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
# UTF-8 输出,解决中文乱码
|
|
6
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
7
|
+
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
8
|
+
chcp 65001 > $null
|
|
9
|
+
|
|
10
|
+
$ErrorActionPreference = "Stop"
|
|
11
|
+
|
|
12
|
+
if (-not $DllPath) {
|
|
13
|
+
$DllPath = Join-Path $PSScriptRoot "..\bin\OpenHardwareMonitorLib.dll"
|
|
14
|
+
}
|
|
15
|
+
$DllPath = (Resolve-Path $DllPath).Path
|
|
16
|
+
|
|
17
|
+
if (-not (Test-Path $DllPath)) {
|
|
18
|
+
Write-Error "DLL not found: $DllPath"
|
|
19
|
+
exit 1
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
Unblock-File -LiteralPath $DllPath -ErrorAction SilentlyContinue
|
|
24
|
+
Add-Type -LiteralPath $DllPath
|
|
25
|
+
} catch {
|
|
26
|
+
Write-Error "Failed to load DLL: $_"
|
|
27
|
+
exit 1
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
$monitor = New-Object OpenHardwareMonitor.Hardware.Computer
|
|
31
|
+
$monitor.CPUEnabled = $true
|
|
32
|
+
$monitor.MainboardEnabled = $true
|
|
33
|
+
$monitor.GPUEnabled = $true
|
|
34
|
+
$monitor.HDDEnabled = $true
|
|
35
|
+
$monitor.RAMEnabled = $true
|
|
36
|
+
$monitor.FanControllerEnabled = $true
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
$monitor.Open()
|
|
40
|
+
} catch {
|
|
41
|
+
Write-Error "Failed to open monitor (needs admin?): $_"
|
|
42
|
+
exit 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
$hardwareList = @()
|
|
46
|
+
|
|
47
|
+
function Get-SensorValue {
|
|
48
|
+
param($sensor)
|
|
49
|
+
$val = $sensor.Value
|
|
50
|
+
if ($null -eq $val) { return $null }
|
|
51
|
+
return [math]::Round([double]$val, 2)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function Walk-Hardware {
|
|
55
|
+
param($hw, $parentPath = "")
|
|
56
|
+
$hw.Update()
|
|
57
|
+
|
|
58
|
+
$sensors = @()
|
|
59
|
+
foreach ($sensor in $hw.Sensors) {
|
|
60
|
+
$val = Get-SensorValue $sensor
|
|
61
|
+
if ($null -ne $val) {
|
|
62
|
+
$sensors += [PSCustomObject]@{
|
|
63
|
+
name = $sensor.Name
|
|
64
|
+
type = $sensor.SensorType.ToString()
|
|
65
|
+
value = $val
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
$hwInfo = [PSCustomObject]@{
|
|
71
|
+
hardwareType = $hw.HardwareType.ToString()
|
|
72
|
+
name = $hw.Name
|
|
73
|
+
identifier = $hw.Identifier.ToString()
|
|
74
|
+
sensors = $sensors
|
|
75
|
+
subHardware = @()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
foreach ($sub in $hw.SubHardware) {
|
|
79
|
+
$subInfo = Walk-Hardware $sub
|
|
80
|
+
$hwInfo.subHardware += $subInfo
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return $hwInfo
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
foreach ($hw in $monitor.Hardware) {
|
|
87
|
+
$hardwareList += Walk-Hardware $hw
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
$monitor.Close()
|
|
91
|
+
|
|
92
|
+
$result = [PSCustomObject]@{
|
|
93
|
+
source = "openhardwaremonitor"
|
|
94
|
+
timestamp = (Get-Date).ToString("o")
|
|
95
|
+
hardware = $hardwareList
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
$result | ConvertTo-Json -Depth 10 -Compress
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[string]$DllPath = ""
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# UTF-8 输出,解决中文乱码
|
|
7
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
8
|
+
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
9
|
+
chcp 65001 > $null
|
|
10
|
+
|
|
11
|
+
$ErrorActionPreference = "Stop"
|
|
12
|
+
|
|
13
|
+
if (-not $DllPath) {
|
|
14
|
+
$DllPath = Join-Path $PSScriptRoot "..\bin\OpenHardwareMonitorLib.dll"
|
|
15
|
+
}
|
|
16
|
+
$DllPath = (Resolve-Path $DllPath).Path
|
|
17
|
+
|
|
18
|
+
if (-not (Test-Path $DllPath)) {
|
|
19
|
+
Write-Error "DLL not found: $DllPath"
|
|
20
|
+
exit 1
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
Unblock-File -LiteralPath $DllPath -ErrorAction SilentlyContinue
|
|
25
|
+
Add-Type -LiteralPath $DllPath
|
|
26
|
+
} catch {
|
|
27
|
+
Write-Error "Failed to load DLL: $_"
|
|
28
|
+
exit 1
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
$monitor = New-Object OpenHardwareMonitor.Hardware.Computer
|
|
32
|
+
$monitor.CPUEnabled = $true
|
|
33
|
+
$monitor.MainboardEnabled = $false
|
|
34
|
+
$monitor.GPUEnabled = $false
|
|
35
|
+
$monitor.HDDEnabled = $false
|
|
36
|
+
$monitor.RAMEnabled = $false
|
|
37
|
+
$monitor.FanControllerEnabled = $false
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
$monitor.Open()
|
|
41
|
+
} catch {
|
|
42
|
+
Write-Error "Failed to open monitor (needs admin?): $_"
|
|
43
|
+
exit 1
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
$cpuName = ""
|
|
47
|
+
$allSensors = @()
|
|
48
|
+
$subSensors = @()
|
|
49
|
+
|
|
50
|
+
function Walk-Hardware {
|
|
51
|
+
param($hw)
|
|
52
|
+
$hw.Update()
|
|
53
|
+
foreach ($sensor in $hw.Sensors) {
|
|
54
|
+
$val = $sensor.Value
|
|
55
|
+
if ($null -ne $val) {
|
|
56
|
+
$script:allSensors += [PSCustomObject]@{
|
|
57
|
+
name = $sensor.Name
|
|
58
|
+
type = $sensor.SensorType.ToString()
|
|
59
|
+
value = [math]::Round([double]$val, 2)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
foreach ($sub in $hw.SubHardware) {
|
|
64
|
+
Walk-Hardware $sub
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
foreach ($hw in $monitor.Hardware) {
|
|
69
|
+
if ($hw.HardwareType -eq [OpenHardwareMonitor.Hardware.HardwareType]::CPU) {
|
|
70
|
+
$cpuName = $hw.Name
|
|
71
|
+
Walk-Hardware $hw
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
$monitor.Close()
|
|
76
|
+
|
|
77
|
+
if ($allSensors.Count -eq 0) {
|
|
78
|
+
Write-Error "OHM: no sensors (likely no admin / driver failed)"
|
|
79
|
+
exit 1
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function Get-SensorsByType {
|
|
83
|
+
param($type)
|
|
84
|
+
return @($allSensors | Where-Object { $_.type -eq $type } | ForEach-Object {
|
|
85
|
+
[PSCustomObject]@{ name = $_.name; value = $_.value }
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
$tempSensors = Get-SensorsByType "Temperature"
|
|
90
|
+
$loadSensors = Get-SensorsByType "Load"
|
|
91
|
+
$clockSensors = Get-SensorsByType "Clock"
|
|
92
|
+
$powerSensors = Get-SensorsByType "Power"
|
|
93
|
+
|
|
94
|
+
# 没有温度传感器说明驱动没加载成功(大概率非管理员),报错让上层降级
|
|
95
|
+
if ($tempSensors.Count -eq 0) {
|
|
96
|
+
Write-Error "OHM: no temperature sensors (likely no admin privileges, WinRing0 driver not loaded)"
|
|
97
|
+
exit 1
|
|
98
|
+
}
|
|
99
|
+
$coreTemps = @($tempSensors | Where-Object { $_.name -match "Core" })
|
|
100
|
+
$packageTemp = ($tempSensors | Where-Object { $_.name -match "Package" } | Select-Object -First 1).value
|
|
101
|
+
$maxCoreTemp = if ($coreTemps.Count -gt 0) { ($coreTemps | Measure-Object -Property value -Maximum).Maximum } else { $null }
|
|
102
|
+
|
|
103
|
+
$result = [PSCustomObject]@{
|
|
104
|
+
source = "openhardwaremonitor"
|
|
105
|
+
isCoreTemp = $true
|
|
106
|
+
cpuName = $cpuName
|
|
107
|
+
temperature = [PSCustomObject]@{
|
|
108
|
+
cores = $coreTemps
|
|
109
|
+
package = $packageTemp
|
|
110
|
+
maxCore = $maxCoreTemp
|
|
111
|
+
}
|
|
112
|
+
load = $loadSensors
|
|
113
|
+
clock = $clockSensors
|
|
114
|
+
power = $powerSensors
|
|
115
|
+
allSensors = $allSensors
|
|
116
|
+
timestamp = (Get-Date).ToString("o")
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
$result | ConvertTo-Json -Depth 6 -Compress
|