2727class ThermalsPage (Gtk .Box ):
2828 __gtype_name__ = 'ThermalsPage'
2929
30+ first_run = True
31+
3032 fan_rpm = Gtk .Template .Child ()
3133 fan_mode = Gtk .Template .Child ()
3234 fan_set_rpm = Gtk .Template .Child ()
3335 fan_set_percent = Gtk .Template .Child ()
3436 fan_percent_scale = Gtk .Template .Child ()
37+ fan_set_points = Gtk .Template .Child ()
38+ set_points = []
39+ ec_set_points_supported = False
40+ ec_set_points = []
3541
3642 temperatures = Gtk .Template .Child ()
3743 temp_items = []
@@ -40,24 +46,59 @@ def __init__(self, **kwargs):
4046 super ().__init__ (** kwargs )
4147
4248 def setup (self , app ):
49+ # Temperature sensors
50+ while temp_child := self .temperatures .get_last_child ():
51+ self .temperatures .remove (temp_child )
52+ self .temp_items .clear ()
53+
54+ try :
55+ ec_temp_sensors = ec_commands .thermal .get_temp_sensors (app .cros_ec )
56+ except ec_exceptions .ECError as e :
57+ if e .ec_status == ec_exceptions .EcStatus .EC_RES_INVALID_COMMAND :
58+ # Generate some labels if the command is not supported
59+ ec_temp_sensors = {}
60+ temps = ec_commands .memmap .get_temps (app .cros_ec )
61+ for i , temp in enumerate (temps ):
62+ ec_temp_sensors [f"Sensor { i } " ] = (temp , None )
63+ else :
64+ raise e
65+
66+ for key , value in ec_temp_sensors .items ():
67+ off_row = Adw .ActionRow (title = key , subtitle = f"{ value [0 ]} °C" )
68+ off_row .add_css_class ("property" )
69+ self .temperatures .append (off_row )
70+ self .temp_items .append (off_row )
71+
72+ self ._update_thermals (app )
73+
4374 # Don't let the user change the fans if they can't get back to auto
4475 if ec_commands .general .get_cmd_versions (
4576 app .cros_ec , ec_commands .thermal .EC_CMD_THERMAL_AUTO_FAN_CTRL
4677 ):
78+ self .ec_set_points_supported = ec_commands .general .check_cmd_version (
79+ app .cros_ec , ec_commands .thermal .EC_CMD_THERMAL_GET_THRESHOLD , 1
80+ ) and ec_commands .general .check_cmd_version (
81+ app .cros_ec , ec_commands .thermal .EC_CMD_THERMAL_SET_THRESHOLD , 1
82+ )
4783
4884 def handle_fan_mode (mode ):
4985 match mode :
5086 case 0 : # Auto
5187 self .fan_set_rpm .set_visible (False )
5288 self .fan_set_percent .set_visible (False )
5389 ec_commands .thermal .thermal_auto_fan_ctrl (app .cros_ec )
90+ self .fan_set_points .set_visible (self .ec_set_points_supported )
5491 case 1 : # Percent
92+ self .fan_set_points .set_visible (False )
5593 self .fan_set_rpm .set_visible (False )
5694 self .fan_set_percent .set_visible (True )
5795 case 2 : # RPM
96+ self .fan_set_points .set_visible (False )
5897 self .fan_set_rpm .set_visible (True )
5998 self .fan_set_percent .set_visible (False )
6099
100+ handle_fan_mode (self .fan_mode .get_selected ())
101+
61102 self .fan_mode .connect (
62103 "notify::selected" ,
63104 lambda combo , _ : handle_fan_mode (combo .get_selected ()),
@@ -81,41 +122,77 @@ def handle_fan_percent(scale):
81122 ):
82123
83124 def handle_fan_rpm (entry ):
84- rpm = int (entry .get_text ())
125+ rpm = int (entry .get_value ())
85126 ec_commands .pwm .pwm_set_fan_rpm (app .cros_ec , rpm )
86127
87128 self .fan_set_rpm .connect (
88- "notify::text " , lambda entry , _ : handle_fan_rpm (entry )
129+ "notify::value " , lambda entry , _ : handle_fan_rpm (entry )
89130 )
90131 else :
91132 self .fan_set_rpm .set_sensitive (False )
92133 else :
93134 self .fan_mode .set_sensitive (False )
94135
95- # Temperature sensors
96- while temp_child := self .temperatures .get_last_child ():
97- self .temperatures .remove (temp_child )
98- self .temp_items .clear ()
99-
100- try :
101- ec_temp_sensors = ec_commands .thermal .get_temp_sensors (app .cros_ec )
102- except ec_exceptions .ECError as e :
103- if e .ec_status == ec_exceptions .EcStatus .EC_RES_INVALID_COMMAND :
104- # Generate some labels if the command is not supported
105- ec_temp_sensors = {}
106- temps = ec_commands .memmap .get_temps (app .cros_ec )
107- for i , temp in enumerate (temps ):
108- ec_temp_sensors [f"Sensor { i } " ] = (temp , None )
109- else :
110- raise e
136+ # Set points
137+ if self .ec_set_points_supported and self .first_run :
138+ def handle_set_point (entry , key ):
139+ index = entry .ec_index
140+ temp = int (entry .get_value ())
141+ # Don't allow an off temp higher than max temp and vice versa
142+ match key :
143+ case "temp_fan_off" :
144+ if temp > self .ec_set_points [index ]["temp_fan_max" ]:
145+ entry .set_value (self .ec_set_points [index ]["temp_fan_off" ])
146+ return
147+ case "temp_fan_max" :
148+ if temp < self .ec_set_points [index ]["temp_fan_off" ]:
149+ entry .set_value (self .ec_set_points [index ]["temp_fan_max" ])
150+ return
151+ self .ec_set_points [entry .ec_index ][key ] = temp
152+ ec_commands .thermal .thermal_set_thresholds (
153+ app .cros_ec , index ,
154+ self .ec_set_points [index ]
155+ )
111156
112- for key , value in ec_temp_sensors .items ():
113- new_row = Adw .ActionRow (title = key , subtitle = f"{ value [0 ]} °C" )
114- new_row .add_css_class ("property" )
115- self .temperatures .append (new_row )
116- self .temp_items .append (new_row )
157+ for i , sensor in enumerate (ec_temp_sensors ):
158+ ec_set_point = ec_commands .thermal .thermal_get_thresholds (app .cros_ec , i )
159+ self .ec_set_points .append (ec_set_point )
160+ off_row = Adw .SpinRow (
161+ title = f"Fan On - { sensor } " ,
162+ subtitle = f"Turn fan on when above temp (°C)" ,
163+ )
164+ off_row .ec_index = i
165+ # 0K to 65535K for 16bit unsigned range
166+ # Actually the EC takes 32bits, but let's keep it like this for sanity
167+ off_row .set_adjustment (Gtk .Adjustment (
168+ lower = - 273 ,
169+ upper = 65_262 ,
170+ page_increment = 10 ,
171+ step_increment = 1 ,
172+ value = ec_set_point ["temp_fan_off" ],
173+ ))
174+ off_row .connect (
175+ "notify::value" , lambda entry , _ : handle_set_point (entry , "temp_fan_off" )
176+ )
177+ max_row = Adw .SpinRow (
178+ title = f"Fan Max - { sensor } " ,
179+ subtitle = f"Max fan speed when above temp (°C)" ,
180+ )
181+ max_row .ec_index = i
182+ max_row .set_adjustment (Gtk .Adjustment (
183+ lower = - 273 ,
184+ upper = 65_262 ,
185+ page_increment = 10 ,
186+ step_increment = 1 ,
187+ value = ec_set_point ["temp_fan_max" ],
188+ ))
189+ max_row .connect (
190+ "notify::value" , lambda entry , _ : handle_set_point (entry , "temp_fan_max" )
191+ )
192+ self .fan_set_points .add_row (off_row )
193+ self .fan_set_points .add_row (max_row )
117194
118- self ._update_thermals ( app )
195+ self .first_run = False
119196
120197 # Schedule _update_thermals to run every second
121198 GLib .timeout_add_seconds (1 , self ._update_thermals , app )
0 commit comments