1
2
3
4
5 package oshi.hardware.platform.linux;
6
7 import java.io.File;
8 import java.io.FileFilter;
9 import java.io.IOException;
10 import java.nio.file.Paths;
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Locale;
15 import java.util.Map;
16
17 import oshi.annotation.concurrent.ThreadSafe;
18 import oshi.hardware.common.AbstractSensors;
19 import oshi.util.ExecutingCommand;
20 import oshi.util.FileUtil;
21 import oshi.util.ParseUtil;
22 import oshi.util.platform.linux.SysPath;
23
24
25
26
27 @ThreadSafe
28 final class LinuxSensors extends AbstractSensors {
29
30
31 private static final String TEMP = "temp";
32 private static final String FAN = "fan";
33 private static final String VOLTAGE = "in";
34 private static final String[] SENSORS = { TEMP, FAN, VOLTAGE };
35
36
37 private static final String HWMON = "hwmon";
38 private static final String HWMON_PATH = SysPath.HWMON + HWMON;
39
40 private static final String THERMAL_ZONE = "thermal_zone";
41 private static final String THERMAL_ZONE_PATH = SysPath.THERMAL + THERMAL_ZONE;
42
43
44 private static final boolean IS_PI = queryCpuTemperatureFromVcGenCmd() > 0;
45
46
47 private final Map<String, String> sensorsMap = new HashMap<>();
48
49
50
51
52
53
54 LinuxSensors() {
55 if (!IS_PI) {
56 populateSensorsMapFromHwmon();
57
58 if (!this.sensorsMap.containsKey(TEMP)) {
59 populateSensorsMapFromThermalZone();
60 }
61 }
62 }
63
64
65
66
67 private void populateSensorsMapFromHwmon() {
68 for (String sensor : SENSORS) {
69
70 final String sensorPrefix = sensor;
71
72 getSensorFilesFromPath(HWMON_PATH, sensor, f -> {
73 try {
74 return f.getName().startsWith(sensorPrefix) && f.getName().endsWith("_input")
75 && FileUtil.getIntFromFile(f.getCanonicalPath()) > 0;
76 } catch (IOException e) {
77 return false;
78 }
79 });
80 }
81 }
82
83
84
85
86 private void populateSensorsMapFromThermalZone() {
87 getSensorFilesFromPath(THERMAL_ZONE_PATH, TEMP, f -> f.getName().equals(TEMP));
88 }
89
90
91
92
93
94
95
96
97 private void getSensorFilesFromPath(String sensorPath, String sensor, FileFilter sensorFileFilter) {
98 int i = 0;
99 while (Paths.get(sensorPath + i).toFile().isDirectory()) {
100 String path = sensorPath + i;
101 File dir = new File(path);
102 File[] matchingFiles = dir.listFiles(sensorFileFilter);
103 if (matchingFiles != null && matchingFiles.length > 0) {
104 this.sensorsMap.put(sensor, String.format(Locale.ROOT, "%s/%s", path, sensor));
105 }
106 i++;
107 }
108 }
109
110 @Override
111 public double queryCpuTemperature() {
112 if (IS_PI) {
113 return queryCpuTemperatureFromVcGenCmd();
114 }
115 String tempStr = this.sensorsMap.get(TEMP);
116 if (tempStr != null) {
117 long millidegrees = 0;
118 if (tempStr.contains(HWMON)) {
119
120 millidegrees = FileUtil.getLongFromFile(String.format(Locale.ROOT, "%s1_input", tempStr));
121
122 if (millidegrees > 0) {
123 return millidegrees / 1000d;
124 }
125
126
127 long sum = 0;
128 int count = 0;
129 for (int i = 2; i <= 6; i++) {
130 millidegrees = FileUtil.getLongFromFile(String.format(Locale.ROOT, "%s%d_input", tempStr, i));
131 if (millidegrees > 0) {
132 sum += millidegrees;
133 count++;
134 }
135 }
136 if (count > 0) {
137 return sum / (count * 1000d);
138 }
139 } else if (tempStr.contains(THERMAL_ZONE)) {
140
141 millidegrees = FileUtil.getLongFromFile(tempStr);
142
143 if (millidegrees > 0) {
144 return millidegrees / 1000d;
145 }
146 }
147 }
148 return 0d;
149 }
150
151
152
153
154
155
156 private static double queryCpuTemperatureFromVcGenCmd() {
157 String tempStr = ExecutingCommand.getFirstAnswer("vcgencmd measure_temp");
158
159 if (tempStr.startsWith("temp=")) {
160 return ParseUtil.parseDoubleOrDefault(tempStr.replaceAll("[^\\d|\\.]+", ""), 0d);
161 }
162 return 0d;
163 }
164
165 @Override
166 public int[] queryFanSpeeds() {
167 if (!IS_PI) {
168 String fanStr = this.sensorsMap.get(FAN);
169 if (fanStr != null) {
170 List<Integer> speeds = new ArrayList<>();
171 int fan = 1;
172 for (;;) {
173 String fanPath = String.format(Locale.ROOT, "%s%d_input", fanStr, fan);
174 if (!new File(fanPath).exists()) {
175
176 break;
177 }
178
179 speeds.add(FileUtil.getIntFromFile(fanPath));
180
181 fan++;
182 }
183 int[] fanSpeeds = new int[speeds.size()];
184 for (int i = 0; i < speeds.size(); i++) {
185 fanSpeeds[i] = speeds.get(i);
186 }
187 return fanSpeeds;
188 }
189 }
190 return new int[0];
191 }
192
193 @Override
194 public double queryCpuVoltage() {
195 if (IS_PI) {
196 return queryCpuVoltageFromVcGenCmd();
197 }
198 String voltageStr = this.sensorsMap.get(VOLTAGE);
199 if (voltageStr != null) {
200
201 return FileUtil.getIntFromFile(String.format(Locale.ROOT, "%s1_input", voltageStr)) / 1000d;
202 }
203 return 0d;
204 }
205
206
207
208
209
210
211 private static double queryCpuVoltageFromVcGenCmd() {
212
213 String voltageStr = ExecutingCommand.getFirstAnswer("vcgencmd measure_volts core");
214
215 if (voltageStr.startsWith("volt=")) {
216 return ParseUtil.parseDoubleOrDefault(voltageStr.replaceAll("[^\\d|\\.]+", ""), 0d);
217 }
218 return 0d;
219 }
220 }