1
2
3
4
5 package oshi.hardware.platform.windows;
6
7 import java.lang.reflect.Method;
8 import java.util.Collections;
9 import java.util.List;
10 import java.util.Locale;
11 import java.util.Objects;
12 import java.util.function.BiFunction;
13
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 import com.sun.jna.platform.win32.COM.COMException;
18 import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
19
20 import oshi.annotation.concurrent.ThreadSafe;
21 import oshi.driver.windows.wmi.MSAcpiThermalZoneTemperature;
22 import oshi.driver.windows.wmi.MSAcpiThermalZoneTemperature.TemperatureProperty;
23 import oshi.driver.windows.wmi.OhmHardware;
24 import oshi.driver.windows.wmi.OhmHardware.IdentifierProperty;
25 import oshi.driver.windows.wmi.OhmSensor;
26 import oshi.driver.windows.wmi.OhmSensor.ValueProperty;
27 import oshi.driver.windows.wmi.Win32Fan;
28 import oshi.driver.windows.wmi.Win32Fan.SpeedProperty;
29 import oshi.driver.windows.wmi.Win32Processor;
30 import oshi.driver.windows.wmi.Win32Processor.VoltProperty;
31 import oshi.hardware.common.AbstractSensors;
32 import oshi.util.platform.windows.WmiQueryHandler;
33 import oshi.util.platform.windows.WmiUtil;
34
35
36
37
38 @ThreadSafe
39 final class WindowsSensors extends AbstractSensors {
40
41 private static final Logger LOG = LoggerFactory.getLogger(WindowsSensors.class);
42
43 private static final String COM_EXCEPTION_MSG = "COM exception: {}";
44
45 private static final String REFLECT_EXCEPTION_MSG = "Reflect exception: {}";
46
47 private static final String JLIBREHARDWAREMONITOR_PACKAGE = "io.github.pandalxb.jlibrehardwaremonitor";
48
49 @Override
50 public double queryCpuTemperature() {
51
52
53
54 double tempC = getTempFromOHM();
55 if (tempC > 0d) {
56 return tempC;
57 }
58
59
60
61
62 tempC = getTempFromLHM();
63 if (tempC > 0d) {
64 return tempC;
65 }
66
67
68 tempC = getTempFromWMI();
69
70
71
72
73 return tempC;
74 }
75
76 private static double getTempFromOHM() {
77 WmiResult<ValueProperty> ohmSensors = getOhmSensors("Hardware", "CPU", "Temperature", (h, ohmHardware) -> {
78 String cpuIdentifier = WmiUtil.getString(ohmHardware, IdentifierProperty.IDENTIFIER, 0);
79 if (!cpuIdentifier.isEmpty()) {
80 return OhmSensor.querySensorValue(h, cpuIdentifier, "Temperature");
81 }
82 return null;
83 });
84 if (ohmSensors != null && ohmSensors.getResultCount() > 0) {
85 double sum = 0;
86 for (int i = 0; i < ohmSensors.getResultCount(); i++) {
87 sum += WmiUtil.getFloat(ohmSensors, ValueProperty.VALUE, i);
88 }
89 return sum / ohmSensors.getResultCount();
90 }
91 return 0;
92 }
93
94 private static double getTempFromLHM() {
95 return getAverageValueFromLHM("CPU", "Temperature",
96 (name, value) -> !name.contains("Max") && !name.contains("Average") && value > 0);
97 }
98
99 private static double getTempFromWMI() {
100 double tempC = 0d;
101 long tempK = 0L;
102 WmiResult<TemperatureProperty> result = MSAcpiThermalZoneTemperature.queryCurrentTemperature();
103 if (result.getResultCount() > 0) {
104 LOG.debug("Found Temperature data in WMI");
105 tempK = WmiUtil.getUint32asLong(result, TemperatureProperty.CURRENTTEMPERATURE, 0);
106 }
107 if (tempK > 2732L) {
108 tempC = tempK / 10d - 273.15;
109 } else if (tempK > 274L) {
110 tempC = tempK - 273d;
111 }
112 return Math.max(tempC, +0.0);
113 }
114
115 @Override
116 public int[] queryFanSpeeds() {
117
118 int[] fanSpeeds = getFansFromOHM();
119 if (fanSpeeds.length > 0) {
120 return fanSpeeds;
121 }
122
123
124
125
126 fanSpeeds = getFansFromLHM();
127 if (fanSpeeds.length > 0) {
128 return fanSpeeds;
129 }
130
131
132
133 fanSpeeds = getFansFromWMI();
134 if (fanSpeeds.length > 0) {
135 return fanSpeeds;
136 }
137
138
139 return new int[0];
140 }
141
142 private static int[] getFansFromOHM() {
143 WmiResult<ValueProperty> ohmSensors = getOhmSensors("Hardware", "CPU", "Fan", (h, ohmHardware) -> {
144 String cpuIdentifier = WmiUtil.getString(ohmHardware, IdentifierProperty.IDENTIFIER, 0);
145 if (!cpuIdentifier.isEmpty()) {
146 return OhmSensor.querySensorValue(h, cpuIdentifier, "Fan");
147 }
148 return null;
149 });
150 if (ohmSensors != null && ohmSensors.getResultCount() > 0) {
151 int[] fanSpeeds = new int[ohmSensors.getResultCount()];
152 for (int i = 0; i < ohmSensors.getResultCount(); i++) {
153 fanSpeeds[i] = (int) WmiUtil.getFloat(ohmSensors, ValueProperty.VALUE, i);
154 }
155 return fanSpeeds;
156 }
157 return new int[0];
158 }
159
160 private static int[] getFansFromLHM() {
161 List<?> sensors = getLhmSensors("SuperIO", "Fan");
162 if (sensors == null || sensors.isEmpty()) {
163 return new int[0];
164 }
165
166 try {
167
168 Class<?> sensorClass = Class.forName(JLIBREHARDWAREMONITOR_PACKAGE + ".model.Sensor");
169 Method getValueMethod = sensorClass.getMethod("getValue");
170
171 return sensors.stream().filter(sensor -> {
172 try {
173 double value = (double) getValueMethod.invoke(sensor);
174 return value > 0;
175 } catch (Exception e) {
176 LOG.warn(REFLECT_EXCEPTION_MSG, e.getMessage());
177 return false;
178 }
179 }).mapToInt(sensor -> {
180 try {
181 return (int) (double) getValueMethod.invoke(sensor);
182 } catch (Exception e) {
183 LOG.warn(REFLECT_EXCEPTION_MSG, e.getMessage());
184 return 0;
185 }
186 }).toArray();
187 } catch (Exception e) {
188 LOG.warn(REFLECT_EXCEPTION_MSG, e.getMessage());
189 }
190 return new int[0];
191 }
192
193 private static int[] getFansFromWMI() {
194 WmiResult<SpeedProperty> fan = Win32Fan.querySpeed();
195 if (fan.getResultCount() > 1) {
196 LOG.debug("Found Fan data in WMI");
197 int[] fanSpeeds = new int[fan.getResultCount()];
198 for (int i = 0; i < fan.getResultCount(); i++) {
199 fanSpeeds[i] = (int) WmiUtil.getUint64(fan, SpeedProperty.DESIREDSPEED, i);
200 }
201 return fanSpeeds;
202 }
203 return new int[0];
204 }
205
206 @Override
207 public double queryCpuVoltage() {
208
209 double volts = getVoltsFromOHM();
210 if (volts > 0d) {
211 return volts;
212 }
213
214
215
216
217 volts = getVoltsFromLHM();
218 if (volts > 0d) {
219 return volts;
220 }
221
222
223
224 volts = getVoltsFromWMI();
225
226 return volts;
227 }
228
229 private static double getVoltsFromOHM() {
230 WmiResult<ValueProperty> ohmSensors = getOhmSensors("Sensor", "Voltage", "Voltage", (h, ohmHardware) -> {
231
232 String cpuIdentifier = null;
233 for (int i = 0; i < ohmHardware.getResultCount(); i++) {
234 String id = WmiUtil.getString(ohmHardware, IdentifierProperty.IDENTIFIER, i);
235 if (id.toLowerCase(Locale.ROOT).contains("cpu")) {
236 cpuIdentifier = id;
237 break;
238 }
239 }
240
241 if (cpuIdentifier == null) {
242 cpuIdentifier = WmiUtil.getString(ohmHardware, IdentifierProperty.IDENTIFIER, 0);
243 }
244
245 return OhmSensor.querySensorValue(h, cpuIdentifier, "Voltage");
246 });
247 if (ohmSensors != null && ohmSensors.getResultCount() > 0) {
248 return WmiUtil.getFloat(ohmSensors, ValueProperty.VALUE, 0);
249 }
250 return 0d;
251 }
252
253 private static double getVoltsFromLHM() {
254 return getAverageValueFromLHM("SuperIO", "Voltage",
255 (name, value) -> name.toLowerCase(Locale.ROOT).contains("vcore") && value > 0);
256 }
257
258 private static double getVoltsFromWMI() {
259 WmiResult<VoltProperty> voltage = Win32Processor.queryVoltage();
260 if (voltage.getResultCount() > 1) {
261 LOG.debug("Found Voltage data in WMI");
262 int decivolts = WmiUtil.getUint16(voltage, VoltProperty.CURRENTVOLTAGE, 0);
263
264
265
266 if (decivolts > 0) {
267 if ((decivolts & 0x80) == 0) {
268 decivolts = WmiUtil.getUint32(voltage, VoltProperty.VOLTAGECAPS, 0);
269
270 if ((decivolts & 0x1) > 0) {
271 return 5.0;
272 } else if ((decivolts & 0x2) > 0) {
273 return 3.3;
274 } else if ((decivolts & 0x4) > 0) {
275 return 2.9;
276 }
277 } else {
278
279 return (decivolts & 0x7F) / 10d;
280 }
281 }
282 }
283 return 0d;
284 }
285
286 private static WmiResult<ValueProperty> getOhmSensors(String typeToQuery, String typeName, String sensorType,
287 BiFunction<WmiQueryHandler, WmiResult<IdentifierProperty>, WmiResult<ValueProperty>> querySensorFunction) {
288 WmiQueryHandler h = Objects.requireNonNull(WmiQueryHandler.createInstance());
289 boolean comInit = false;
290 WmiResult<ValueProperty> ohmSensors = null;
291 try {
292 comInit = h.initCOM();
293 WmiResult<IdentifierProperty> ohmHardware = OhmHardware.queryHwIdentifier(h, typeToQuery, typeName);
294 if (ohmHardware.getResultCount() > 0) {
295 LOG.debug("Found {} data in Open Hardware Monitor", sensorType);
296 ohmSensors = querySensorFunction.apply(h, ohmHardware);
297 }
298 } catch (COMException e) {
299 LOG.warn(COM_EXCEPTION_MSG, e.getMessage());
300 } finally {
301 if (comInit) {
302 h.unInitCOM();
303 }
304 }
305 return ohmSensors;
306 }
307
308 private static double getAverageValueFromLHM(String hardwareType, String sensorType,
309 BiFunction<String, Double, Boolean> sensorValidFunction) {
310 List<?> sensors = getLhmSensors(hardwareType, sensorType);
311 if (sensors == null || sensors.isEmpty()) {
312 return 0;
313 }
314
315 try {
316
317 Class<?> sensorClass = Class.forName(JLIBREHARDWAREMONITOR_PACKAGE + ".model.Sensor");
318 Method getNameMethod = sensorClass.getMethod("getName");
319 Method getValueMethod = sensorClass.getMethod("getValue");
320
321 double sum = 0;
322 int validCount = 0;
323 for (Object sensor : sensors) {
324 String name = (String) getNameMethod.invoke(sensor);
325 double value = (double) getValueMethod.invoke(sensor);
326 if (sensorValidFunction.apply(name, value)) {
327 sum += value;
328 validCount++;
329 }
330 }
331 return validCount > 0 ? sum / validCount : 0;
332 } catch (Exception e) {
333 LOG.warn(REFLECT_EXCEPTION_MSG, e.getMessage());
334 }
335 return 0;
336 }
337
338 private static List<?> getLhmSensors(String hardwareType, String sensorType) {
339 try {
340 Class<?> computerConfigClass = Class.forName(JLIBREHARDWAREMONITOR_PACKAGE + ".config.ComputerConfig");
341 Class<?> libreHardwareManagerClass = Class
342 .forName(JLIBREHARDWAREMONITOR_PACKAGE + ".manager.LibreHardwareManager");
343
344 Method computerConfigGetInstanceMethod = computerConfigClass.getMethod("getInstance");
345 Object computerConfigInstance = computerConfigGetInstanceMethod.invoke(null);
346
347 Method setEnabledMethod = computerConfigClass.getMethod("setCpuEnabled", boolean.class);
348 setEnabledMethod.invoke(computerConfigInstance, true);
349 setEnabledMethod = computerConfigClass.getMethod("setMotherboardEnabled", boolean.class);
350 setEnabledMethod.invoke(computerConfigInstance, true);
351
352 Method libreHardwareManagerGetInstanceMethod = libreHardwareManagerClass.getMethod("getInstance",
353 computerConfigClass);
354
355 Object instance = libreHardwareManagerGetInstanceMethod.invoke(null, computerConfigInstance);
356
357 Method querySensorsMethod = libreHardwareManagerClass.getMethod("querySensors", String.class, String.class);
358 return (List<?>) querySensorsMethod.invoke(instance, hardwareType, sensorType);
359 } catch (Exception e) {
360 LOG.warn(REFLECT_EXCEPTION_MSG, e.getMessage());
361 }
362 return Collections.emptyList();
363 }
364 }