@@ -16,6 +16,9 @@ const GIB_TO_MIB = 1024; // 1 GiB = 1,042 MiB
1616const KB_TO_B = 1000 ; // 1 KB = 1,000 B
1717const 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+
1922const UUID = "system-monitor-graph@rcassani" ;
2023const 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} ;
0 commit comments