Skip to content

Commit 7197af8

Browse files
tidotuarcassani
andauthored
system-monitor-graph@rcassani: Add support for CPU and GPU temperatures (#1768)
--------- Co-authored-by: rcassani <raymundo.cassani@gmail.com>
1 parent daf531c commit 7197af8

18 files changed

Lines changed: 1121 additions & 362 deletions

File tree

system-monitor-graph@rcassani/README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@ This project has been inspired from other Desklets such as [Disk Space](https://
1515
| System variable | Description |
1616
| ----------- | ----------- |
1717
| CPU | CPU usage in % |
18+
| CPU Temperature | CPU temperature C or F |
1819
| RAM | Used RAM as % of total, and in GB |
1920
| Swap | Used Swap space as % of total, and in GB |
2021
| HDD | % of I/O activity, and free and total space in the filesystem (partition) indicated by the user |
2122
| GPU Usage | GPU usage in % |
2223
| GPU Memory | GPU memory usage in % |
24+
| GPU Temperature | GPU temperature C or F |
2325
| Network | Real-time upload and download speeds with dual-line graph, configurable interface monitoring |
26+
| Battery | Current capacity %, and Status |
2427

2528
Each variable is calculated every `Refresh interval` seconds (Min. 1 s, Max. 60 s.), and the graph shows the last `Duration of the graph` period (Min. 30 s, Max. 60 min).
29+
GPU graphs work for NVIDIA, discrete AMD and built-in AMD.
2630

2731
### Customizable visual elements
2832
<p align="center">
@@ -48,9 +52,16 @@ A simple screenshot.
4852
Another screenshot.
4953
</p>
5054

55+
<p align="center">
56+
<img src="https://cinnamon-spices.linuxmint.com/git/desklets/system-monitor-graph@rcassani/screenshot4.png" width="900" align="middle"><br>
57+
Screenshot with temperature and battery graphs.
58+
</p>
5159

5260
## TODO
53-
- [ ] Add other variables such as ~~network~~, CPU and GPU temperatures, battery levels for PC and peripherals.
61+
- [x] Add network
62+
- [x] Battery levels for main battery (laptop)
63+
- [x] CPU and GPU temperatures
64+
- [ ] Battery levels for main battery (laptop)
5465

5566
### Resources
5667
This is my first Desklet and the first time using JavaScript. Below, some resources that I used for the development of this Desklet.

system-monitor-graph@rcassani/files/system-monitor-graph@rcassani/desklet.js

Lines changed: 128 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ const GIB_TO_MIB = 1024; // 1 GiB = 1,042 MiB
1616
const KB_TO_B = 1000; // 1 KB = 1,000 B
1717
const KIB_TO_B = 1024; // 1 KiB = 1,024 B
1818

19+
const CPU_TEMP_MIN = 20; // Minimum CPU temperature
20+
const CPU_TEMP_MAX = 100; // Maximum CPU temperature
21+
1922
const UUID = "system-monitor-graph@rcassani";
2023
const DESKLET_PATH = imports.ui.deskletManager.deskletMeta[UUID].path;
2124

@@ -94,6 +97,8 @@ SystemMonitorGraph.prototype = {
9497
// initialize settings
9598
this.settings = new Settings.DeskletSettings(this, this.metadata["uuid"], desklet_id);
9699
this.settings.bindProperty(Settings.BindingDirection.IN, "type", "type", this.on_setting_changed);
100+
this.settings.bindProperty(Settings.BindingDirection.IN, "cpu-variable", "cpu_variable", this.on_setting_changed);
101+
this.settings.bindProperty(Settings.BindingDirection.IN, "temperature-units-cpu", "temperature_units_cpu", this.on_setting_changed);
97102
this.settings.bindProperty(Settings.BindingDirection.IN, "data-prefix-ram", "data_prefix_ram", this.on_setting_changed);
98103
this.settings.bindProperty(Settings.BindingDirection.IN, "data-prefix-swap", "data_prefix_swap", this.on_setting_changed);
99104
this.settings.bindProperty(Settings.BindingDirection.IN, "data-prefix-hdd", "data_prefix_hdd", this.on_setting_changed);
@@ -105,6 +110,7 @@ SystemMonitorGraph.prototype = {
105110
this.settings.bindProperty(Settings.BindingDirection.IN, "filesystem-label", "filesystem_label", this.on_setting_changed);
106111
this.settings.bindProperty(Settings.BindingDirection.IN, "gpu-manufacturer", "gpu_manufacturer", this.on_setting_changed);
107112
this.settings.bindProperty(Settings.BindingDirection.IN, "gpu-variable", "gpu_variable", this.on_setting_changed);
113+
this.settings.bindProperty(Settings.BindingDirection.IN, "temperature-units-gpu", "temperature_units_gpu", this.on_setting_changed);
108114
this.settings.bindProperty(Settings.BindingDirection.IN, "gpu-id", "gpu_id", this.on_setting_changed);
109115
this.settings.bindProperty(Settings.BindingDirection.IN, "refresh-interval", "refresh_interval", this.on_setting_changed);
110116
this.settings.bindProperty(Settings.BindingDirection.IN, "duration", "duration", this.on_setting_changed);
@@ -123,6 +129,7 @@ SystemMonitorGraph.prototype = {
123129
this.settings.bindProperty(Settings.BindingDirection.IN, "line-color-network-up", "line_color_network_up", this.on_setting_changed);
124130
this.settings.bindProperty(Settings.BindingDirection.IN, "line-color-battery", "line_color_battery", this.on_setting_changed);
125131

132+
126133
// initialize desklet GUI
127134
this.setupUI();
128135
},
@@ -178,9 +185,11 @@ SystemMonitorGraph.prototype = {
178185
this.net_max_scale = 1; // Auto-scaling for network graph
179186
// battery values
180187
this.battery_percent = NaN;
181-
this.battery_capacity = NaN;
182188
this.battery_status = "";
183189
this.battery_time = "";
190+
// temperature values
191+
this.cpu_temperature = NaN;
192+
this.gpu_temperature = NaN;
184193

185194
// set colors
186195
switch (this.type) {
@@ -208,6 +217,27 @@ SystemMonitorGraph.prototype = {
208217
this.line_color = this.line_color_battery;
209218
break;
210219
}
220+
221+
// set files
222+
// find file for overall CPU temperature
223+
let cpu_path_temp = "/sys/class/hwmon/";
224+
// test for Intel ("Package id 0")
225+
this.get_temperature_file_by_label(cpu_path_temp, 'Package id 0', (result) => {
226+
if (result !== "") {
227+
this.cpu_temperature_file = result;
228+
} else {
229+
// test for AMD ("Tct1")
230+
this.get_temperature_file_by_label(cpu_path_temp, 'Tctl', (result) => {
231+
this.cpu_temperature_file = result;
232+
});
233+
}
234+
});
235+
// find file for overall AMD GPU temperature
236+
let gpu_path_temp = "/sys/class/drm/card" + this.gpu_id + "/device/hwmon/";
237+
this.get_temperature_file_by_label(gpu_path_temp, 'edge', (result) => {
238+
this.gpu_amd_temperature_file = result;
239+
});
240+
211241
this.first_run = false;
212242
}
213243

@@ -240,10 +270,27 @@ SystemMonitorGraph.prototype = {
240270
// current values
241271
switch (this.type) {
242272
case "cpu":
243-
this.get_cpu_use();
244-
value = this.cpu_use / 100;
245-
text1 = _("CPU");
246-
text2 = Math.round(this.cpu_use).toString() + "%";
273+
switch (this.cpu_variable) {
274+
case "usage":
275+
this.get_cpu_use();
276+
value = this.cpu_use / 100;
277+
text1 = _("CPU");
278+
text2 = Math.round(this.cpu_use).toString() + "%";
279+
break;
280+
281+
case "temperature":
282+
this.get_cpu_temperature(this.cpu_temperature_file);
283+
// scale CPU temperature based on [CPU_TEMP_MIN, CPU_TEMP_MAX]
284+
value = 1.0 * (this.cpu_temperature - CPU_TEMP_MIN) / (CPU_TEMP_MAX - CPU_TEMP_MIN);
285+
value = value < 0 ? 0 : value > 1 ? 1 : value;
286+
text1 = _("CPU Temperature");
287+
if (this.temperature_units_cpu == "C") {
288+
text2 = this.cpu_temperature.toString() + "°C";
289+
} else if (this.temperature_units_cpu == "F") {
290+
text2 = Math.round(this.cpu_temperature * 9 / 5 + 32).toString() + "°F";
291+
}
292+
break;
293+
}
247294
break;
248295

249296
case "ram":
@@ -342,6 +389,24 @@ SystemMonitorGraph.prototype = {
342389
text3 = this.gpu_mem[1].toFixed(1) + " / "
343390
+ this.gpu_mem[0].toFixed(1) + " " + gpumem_prefix;
344391
break;
392+
case "temperature":
393+
switch (this.gpu_manufacturer) {
394+
case "nvidia":
395+
this.get_nvidia_gpu_temperature();
396+
break;
397+
case "amdgpu":
398+
this.get_amdgpu_gpu_temperature(this.gpu_amd_temperature_file);
399+
break;
400+
}
401+
value = 1.0 * (this.gpu_temperature - CPU_TEMP_MIN) / (CPU_TEMP_MAX - CPU_TEMP_MIN);
402+
value = value < 0 ? 0 : value > 1 ? 1 : value;
403+
text1 = _("GPU Temperature");
404+
if (this.temperature_units_gpu == "C") {
405+
text2 = this.gpu_temperature.toString() + "°C";
406+
} else if (this.temperature_units_gpu == "F") {
407+
text2 = Math.round(this.gpu_temperature * 9 / 5 + 32).toString() + "°F";
408+
}
409+
break;
345410
}
346411
break;
347412

@@ -361,7 +426,7 @@ SystemMonitorGraph.prototype = {
361426

362427
case "battery":
363428
this.get_battery_use();
364-
value = this.battery_capacity;
429+
value = this.battery_percent / 100.0;
365430
text1 = _("Battery");
366431
text2 = this.battery_percent + "%";
367432
let prefix = (this.battery_status == "Charging") ? "⚡ " : (this.battery_percent <= 20 ? "🪫 " : "🔋 ");
@@ -934,6 +999,19 @@ SystemMonitorGraph.prototype = {
934999
);
9351000
},
9361001

1002+
get_nvidia_gpu_temperature: function() {
1003+
spawnAsyncWithOutput(
1004+
['/usr/bin/nvidia-smi', '--query-gpu=temperature.gpu', '--format=csv', '--id='+ this.gpu_id],
1005+
(success, out_string) => {
1006+
if (!success || !out_string) {
1007+
this.gpu_temperature = 0;
1008+
return;
1009+
}
1010+
this.gpu_temperature = parseInt(out_string.match(/[^\r\n]+/g)[1]);
1011+
}
1012+
);
1013+
},
1014+
9371015
get_amdgpu_gpu_use: function() {
9381016
// Sysfs directory with files related to the chosen gpu
9391017
let gpu_dir = "/sys/class/drm/card" + this.gpu_id + "/device/";
@@ -982,6 +1060,21 @@ SystemMonitorGraph.prototype = {
9821060
});
9831061
},
9841062

1063+
get_amdgpu_gpu_temperature: function(temperature_file) {
1064+
if(temperature_file == null || temperature_file == "") return;
1065+
// File contains temperature, integer number in celsius * 1000
1066+
Gio.file_new_for_path(temperature_file).load_contents_async(null, (file, response) => {
1067+
try {
1068+
let [success, contents, tag] = file.load_contents_finish(response);
1069+
if (success) {
1070+
this.gpu_temperature = Math.round(parseInt(ByteArray.toString(contents)) / 1000);
1071+
}
1072+
GLib.free(contents);
1073+
} catch(error) {
1074+
global.log('GPU AMD temperature file read error: ' + error.toString());
1075+
}
1076+
});
1077+
},
9851078

9861079
get_battery_use: function() {
9871080
// Sysfs directory for battery info
@@ -996,7 +1089,6 @@ SystemMonitorGraph.prototype = {
9961089
if (success) {
9971090
let percent = parseInt(ByteArray.toString(contents));
9981091
this.battery_percent = percent >= 100 ? 100 : percent;
999-
this.battery_capacity = this.battery_percent / 100.0;
10001092
}
10011093
GLib.free(contents);
10021094
} catch(error) {
@@ -1032,6 +1124,34 @@ SystemMonitorGraph.prototype = {
10321124
let hours = String(Math.floor(totalMinutes / 60)).padStart(2, '0');
10331125
let result = `${hours}:${minutes}`;
10341126
return (result == "00:00") ? "--:--" : result;
1035-
}
1127+
},
10361128

1129+
get_cpu_temperature: function(temperature_file) {
1130+
if(temperature_file == null || temperature_file == "") return;
1131+
// File contains temperature, integer number in celsius * 1000
1132+
Gio.file_new_for_path(temperature_file).load_contents_async(null, (file, response) => {
1133+
try {
1134+
let [success, contents, tag] = file.load_contents_finish(response);
1135+
if (success) {
1136+
this.cpu_temperature = Math.round(parseInt(ByteArray.toString(contents)) / 1000);
1137+
}
1138+
GLib.free(contents);
1139+
} catch(error) {
1140+
global.log('CPU temperature file read error: ' + error.toString());
1141+
}
1142+
});
1143+
},
1144+
1145+
get_temperature_file_by_label: function(path, label, callback) {
1146+
// search for temperture file: 'path'/*/temp*_input associated to file path/*/temp*_label with content 'label'
1147+
let argv = ['/bin/sh', '-c', "grep -s -l -d skip '" + label + "' " + path + "*/temp*_label | sed 's/_label/_input/' | head -n 1"];
1148+
spawnAsyncWithOutput(argv, (success, output) => {
1149+
if (success && output && output.trim() !== "") {
1150+
callback(output.trim());
1151+
} else {
1152+
global.log('Temperature file search error for label: ' + label);
1153+
callback("");
1154+
}
1155+
});
1156+
},
10371157
};

system-monitor-graph@rcassani/files/system-monitor-graph@rcassani/metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
"uuid": "system-monitor-graph@rcassani",
44
"name": "System monitor graph",
55
"description": "Creates graphs for system variables.",
6-
"version": "2.0",
6+
"version": "2.1",
77
"prevent-decorations": true
88
}

0 commit comments

Comments
 (0)