5151 import sv_ttk
5252 from PIL import Image
5353 from serial .tools .list_ports import comports
54+ from tktooltip import ToolTip
5455except :
5556 print (
5657 "[ERROR] Python dependencies not installed. Please follow start guide: https://github.com/mathoudebine/turing-smart-screen-python/wiki/System-monitor-:-how-to-start" )
5960 except :
6061 os ._exit (0 )
6162
63+ from library .sensors .sensors_python import sensors_fans , is_cpu_fan
64+
6265TURING_MODEL = "Turing Smart Screen"
6366USBPCMONITOR_MODEL = "UsbPCMonitor"
6467XUANFANG_MODEL = "XuanFang rev. B & flagship"
@@ -141,14 +144,28 @@ def get_net_if():
141144 return if_list
142145
143146
147+ def get_fans ():
148+ fan_list = list ()
149+ auto_detected_cpu_fan = "None"
150+ for name , entries in sensors_fans ().items ():
151+ for entry in entries :
152+ fan_list .append ("%s/%s (%d%% - %d RPM)" % (name , entry .label , entry .percent , entry .current ))
153+ if (is_cpu_fan (entry .label ) or is_cpu_fan (name )) and auto_detected_cpu_fan == "None" :
154+ auto_detected_cpu_fan = "Auto-detected: %s/%s" % (name , entry .label )
155+
156+ fan_list .insert (0 , auto_detected_cpu_fan ) # Add manual entry on top if auto-detection succeeded
157+ return fan_list
158+
159+
144160class TuringConfigWindow :
145161 def __init__ (self ):
146162 self .window = Tk ()
147163 self .window .title ('Turing System Monitor configuration' )
148- self .window .geometry ("770x550 " )
164+ self .window .geometry ("770x570 " )
149165 self .window .iconphoto (True , PhotoImage (file = "res/icons/monitor-icon-17865/64.png" ))
150166 # When window gets focus again, reload theme preview in case it has been updated by theme editor
151167 self .window .bind ("<FocusIn>" , self .on_theme_change )
168+ self .window .after (0 , self .on_fan_speed_update )
152169
153170 # Make TK look better with Sun Valley ttk theme
154171 sv_ttk .set_theme ("light" )
@@ -224,18 +241,29 @@ def __init__(self):
224241 self .wl_cb = ttk .Combobox (self .window , values = get_net_if (), state = 'readonly' )
225242 self .wl_cb .place (x = 500 , y = 415 , width = 250 )
226243
244+ # For Windows platform only
227245 self .lhm_admin_warning = ttk .Label (self .window ,
228246 text = "❌ Restart as admin. or select another Hardware monitoring" ,
229247 foreground = '#f00' )
248+ # For platform != Windows
249+ self .cpu_fan_label = ttk .Label (self .window , text = 'CPU fan (?)' )
250+ self .cpu_fan_label .config (foreground = "#a3a3ff" , cursor = "hand2" )
251+ self .cpu_fan_cb = ttk .Combobox (self .window , values = get_fans (), state = 'readonly' )
252+
253+ self .tooltip = ToolTip (self .cpu_fan_label ,
254+ msg = "If \" None\" is selected, CPU fan was not auto-detected.\n "
255+ "Manually select your CPU fan from the list.\n \n "
256+ "Fans missing from the list? Install lm-sensors package\n "
257+ "and run 'sudo sensors-detect' command, then reboot." )
230258
231259 self .edit_theme_btn = ttk .Button (self .window , text = "Edit theme" , command = lambda : self .on_theme_editor_click ())
232- self .edit_theme_btn .place (x = 310 , y = 490 , height = 50 , width = 130 )
260+ self .edit_theme_btn .place (x = 310 , y = 510 , height = 50 , width = 130 )
233261
234262 self .save_btn = ttk .Button (self .window , text = "Save settings" , command = lambda : self .on_save_click ())
235- self .save_btn .place (x = 450 , y = 490 , height = 50 , width = 130 )
263+ self .save_btn .place (x = 450 , y = 510 , height = 50 , width = 130 )
236264
237265 self .save_run_btn = ttk .Button (self .window , text = "Save and run" , command = lambda : self .on_saverun_click ())
238- self .save_run_btn .place (x = 590 , y = 490 , height = 50 , width = 130 )
266+ self .save_run_btn .place (x = 590 , y = 510 , height = 50 , width = 130 )
239267
240268 self .config = None
241269 self .load_config_values ()
@@ -261,7 +289,8 @@ def load_theme_preview(self):
261289 self .theme_author .config (text = "Author: " + author_name )
262290 if author_name .startswith ("@" ):
263291 self .theme_author .config (foreground = "#a3a3ff" , cursor = "hand2" )
264- self .theme_author .bind ("<Button-1>" , lambda e : webbrowser .open_new_tab ("https://github.com/" + author_name [1 :]))
292+ self .theme_author .bind ("<Button-1>" ,
293+ lambda e : webbrowser .open_new_tab ("https://github.com/" + author_name [1 :]))
265294 else :
266295 self .theme_author .config (foreground = "#a3a3a3" , cursor = "" )
267296 self .theme_author .unbind ("<Button-1>" )
@@ -336,6 +365,14 @@ def load_config_values(self):
336365 except :
337366 self .brightness_slider .set (50 )
338367
368+ try :
369+ if self .config ['config' ]['CPU_FAN' ] == "AUTO" :
370+ self .cpu_fan_cb .current (0 )
371+ else :
372+ self .cpu_fan_cb .set (self .config ['config' ]['CPU_FAN' ])
373+ except :
374+ self .cpu_fan_cb .current (0 )
375+
339376 # Reload content on screen
340377 self .on_model_change ()
341378 self .on_size_change ()
@@ -358,6 +395,10 @@ def save_config_values(self):
358395 self .config ['config' ]['COM_PORT' ] = "AUTO"
359396 else :
360397 self .config ['config' ]['COM_PORT' ] = self .com_cb .get ()
398+ if self .cpu_fan_cb .current () == 0 :
399+ self .config ['config' ]['CPU_FAN' ] = "AUTO"
400+ else :
401+ self .config ['config' ]['CPU_FAN' ] = self .cpu_fan_cb .get ().split (' ' )[0 ]
361402 self .config ['display' ]['REVISION' ] = model_and_size_to_revision_map [(self .model_cb .get (), self .size_cb .get ())]
362403 self .config ['display' ]['DISPLAY_REVERSE' ] = [k for k , v in reverse_map .items () if v == self .orient_cb .get ()][0 ]
363404 self .config ['display' ]['BRIGHTNESS' ] = int (self .brightness_slider .get ())
@@ -421,11 +462,18 @@ def on_hwlib_change(self, e=None):
421462 import ctypes
422463 is_admin = ctypes .windll .shell32 .IsUserAnAdmin () != 0
423464 if (hwlib == "LHM" or hwlib == "AUTO" ) and not is_admin :
424- self .lhm_admin_warning .place (x = 320 , y = 455 )
465+ self .lhm_admin_warning .place (x = 320 , y = 460 )
425466 self .save_run_btn .state (["disabled" ])
426467 else :
427468 self .lhm_admin_warning .place_forget ()
428469 self .save_run_btn .state (["!disabled" ])
470+ else :
471+ if hwlib == "PYTHON" or hwlib == "AUTO" :
472+ self .cpu_fan_label .place (x = 320 , y = 460 )
473+ self .cpu_fan_cb .place (x = 500 , y = 455 , width = 250 )
474+ else :
475+ self .cpu_fan_label .place_forget ()
476+ self .cpu_fan_cb .place_forget ()
429477
430478 def show_hide_brightness_warning (self , e = None ):
431479 if int (self .brightness_slider .get ()) > 50 and self .model_cb .get () == TURING_MODEL and self .size_cb .get () == SIZE_3_5_INCH :
@@ -434,6 +482,14 @@ def show_hide_brightness_warning(self, e=None):
434482 else :
435483 self .brightness_warning_label .place_forget ()
436484
485+ def on_fan_speed_update (self ):
486+ # Update fan speed periodically
487+ prev_value = self .cpu_fan_cb .current () # Save currently selected index
488+ self .cpu_fan_cb .config (values = get_fans ())
489+ if prev_value != - 1 :
490+ self .cpu_fan_cb .current (prev_value ) # Force select same index to refresh displayed value
491+ self .window .after (500 , self .on_fan_speed_update )
492+
437493
438494if __name__ == "__main__" :
439495 configurator = TuringConfigWindow ()
0 commit comments