11import os
22import gi
33import xapp .util
4+ import re
5+ from enum import IntEnum
46
57gi .require_version ("Gtk" , "3.0" )
68from gi .repository import Gtk , GLib , Pango
1113
1214COL_NAME , COL_VALUE , COL_UNIT , COL_SENSITIVE , COL_ICON_NAME = range (5 )
1315
14- def format_sensor (filename , raw ):
15- raw = raw .strip ()
16-
17- if filename .startswith ("temp" ):
18- return f"{ int (raw )/ 1000 :.1f} " , "°C" , "xsi-temperature-symbolic"
19-
20- if filename .startswith ("fan" ):
21- return raw , _ ("RPM" ), "xsi-cog-symbolic"
22-
23- if filename .startswith ("pwm" ):
24- return f"{ int (raw )* 100 / 255 :.0f} " , "%" , "xsi-cog-symbolic"
25-
26- if filename .startswith ("in" ):
27- return f"{ int (raw )/ 1000 :.3f} " , "V" , "xsi-cog-symbolic"
28-
29- if filename .startswith ("curr" ):
30- return f"{ int (raw )/ 1000 :.3f} " , "A" , "xsi-cog-symbolic"
31-
32- if filename .startswith ("power" ):
33- return f"{ int (raw )/ 1_000_000 :.1f} " , "W" , "xsi-cog-symbolic"
34-
35- if filename .startswith ("freq" ):
36- return f"{ int (raw )/ 1_000_000_000 :.3f} " , "GHz" , "xsi-cog-symbolic"
37-
38- if filename .startswith ("energy" ):
39- return f"{ int (raw )/ 1_000_000 :.3f} " , "J" , "xsi-cog-symbolic"
40-
41- return raw , "" , "xsi-cpu-symbolic"
16+ class SensorType (IntEnum ):
17+ TEMP = 0
18+ FAN = 1
19+ PWM = 2
20+ FREQ = 3
21+ VOLTAGE = 4
22+ CURRENT = 5
23+ POWER = 6
24+ ENERGY = 7
25+ OTHER = 99
26+
27+
28+ SENSOR_SPECS = {
29+ SensorType .TEMP : {
30+ "prefix" :"temp" ,
31+ "format" :lambda raw : f"{ int (raw )/ 1000 :.1f} " ,
32+ "unit" :"°C" ,
33+ "icon" :"xsi-temperature-symbolic"
34+ },
35+ SensorType .FAN : {
36+ "prefix" :"fan" ,
37+ "format" :lambda raw : raw .strip (),
38+ "unit" :_ ("RPM" ),
39+ "icon" :"xsi-cog-symbolic"
40+ },
41+ SensorType .PWM : {
42+ "prefix" :"pwm" ,
43+ "format" :lambda raw : f"{ int (raw )* 100 / 255 :.0f} " ,
44+ "unit" :"%" ,
45+ "icon" :"xsi-cog-symbolic"
46+ },
47+ SensorType .FREQ : {
48+ "prefix" :"freq" ,
49+ "format" :lambda raw : f"{ int (raw )/ 1_000_000_000 :.3f} " ,
50+ "unit" :"GHz" ,
51+ "icon" :"xsi-cog-symbolic"
52+ },
53+ SensorType .VOLTAGE : {
54+ "prefix" :"in" ,
55+ "format" :lambda raw : f"{ int (raw )/ 1000 :.3f} " ,
56+ "unit" :"V" ,
57+ "icon" :"xsi-cog-symbolic"
58+ },
59+ SensorType .CURRENT : {
60+ "prefix" :"curr" ,
61+ "format" :lambda raw : f"{ int (raw )/ 1000 :.3f} " ,
62+ "unit" :"A" ,
63+ "icon" :"xsi-cog-symbolic"
64+ },
65+ SensorType .POWER : {
66+ "prefix" :"power" ,
67+ "format" :lambda raw : f"{ int (raw )/ 1_000_000 :.1f} " ,
68+ "unit" :"W" ,
69+ "icon" :"xsi-cog-symbolic"
70+ },
71+ SensorType .ENERGY : {
72+ "prefix" :"energy" ,
73+ "format" :lambda raw : f"{ int (raw )/ 1_000_000 :.3f} " ,
74+ "unit" :"J" ,
75+ "icon" :"xsi-cog-symbolic"
76+ },
77+ SensorType .OTHER : {
78+ "prefix" :"" ,
79+ "format" :lambda raw : raw .strip (),
80+ "unit" :"" ,
81+ "icon" :"xsi-cog-symbolic"
82+ },
83+ }
84+
85+ def sensor_spec_from_filename (filename ):
86+ for stype , spec in SENSOR_SPECS .items ():
87+ prefix = spec ["prefix" ]
88+ if prefix and filename .startswith (prefix ):
89+ return stype , spec
90+ return SensorType .OTHER , SENSOR_SPECS [SensorType .OTHER ]
91+
92+ # Helper funcs to sort sensors in correct numerical order (ex in10 after in9)
93+ def natural_key (label ):
94+ # Split around any digit sequence
95+ parts = re .split (r'(\d+)' , label )
96+ key = []
97+ for part in parts :
98+ if part .isdigit ():
99+ key .append (int (part ))
100+ else :
101+ key .append (part .lower ())
102+ return key
103+
104+ def sort_sensors (sensors ):
105+ # Natural sort within each sensor type
106+ sensors .sort (key = lambda s : natural_key (s ["label" ]))
107+ # Group by sensor type
108+ sensors .sort (key = lambda s : s ["type" ])
42109
43110class SensorsListWidget (Gtk .ScrolledWindow ):
44111
@@ -122,7 +189,7 @@ def build_tree(self):
122189 if not os .path .isdir (SYS_HWMON ):
123190 return
124191
125- for hwmon in sorted ( os .listdir (SYS_HWMON ) ):
192+ for hwmon in os .listdir (SYS_HWMON ):
126193 hwmon_path = os .path .join (SYS_HWMON , hwmon )
127194 device_path = os .path .join (hwmon_path , "device" )
128195
@@ -146,10 +213,11 @@ def build_tree(self):
146213 None , [name , "" , "" , True , "xsi-cpu-symbolic" ]
147214 )
148215
149- device_without_sensors = True ;
216+ device_without_sensors = True
150217
151218 # Process all *_input files in base_path
152- for fname in sorted (os .listdir (base_path )):
219+ sensors = []
220+ for fname in os .listdir (base_path ):
153221 if not fname .endswith ("_input" ):
154222 continue
155223
@@ -158,20 +226,35 @@ def build_tree(self):
158226 if raw is None :
159227 continue
160228
161- value , unit , icon_name = format_sensor (fname , raw )
229+ stype , spec = sensor_spec_from_filename (fname )
230+ value = spec ["format" ](raw )
162231
163232 # Label
164233 label_file = fpath .replace ("_input" , "_label" )
165234 label = self ._read_file (label_file )
166235 label = label .strip () if label else fname .replace ("_input" , "" )
167236
237+ sensors .append ({
238+ "label" : label ,
239+ "path" : fpath ,
240+ "value" : value ,
241+ "unit" : spec ["unit" ],
242+ "icon" : spec ["icon" ],
243+ "type" : stype ,
244+ })
245+
246+ sort_sensors (sensors )
247+
248+ # Add sorted sensors to treestore
249+ for s in sensors :
168250 itr = self .treestore .append (
169251 parent ,
170- [label , value , unit , True , icon_name ],
252+ [s [ " label" ], s [ " value" ], s [ " unit" ] , True , s [ "icon" ] ],
171253 )
172- self .sensor_rows [fpath ] = itr
173254
174- device_without_sensors = False ;
255+ # Store TreeStore itr and sensor type by path for refresh
256+ self .sensor_rows [s ["path" ]] = (itr , s ["type" ])
257+ device_without_sensors = False
175258
176259 if device_without_sensors :
177260 self .treestore .set_value (parent , COL_SENSITIVE , False )
@@ -180,15 +263,18 @@ def build_tree(self):
180263
181264 def refresh_values (self ):
182265 self .treestore .freeze_notify ()
183- for fpath , itr in self .sensor_rows .items ():
266+
267+ for fpath , (itr , stype ) in self .sensor_rows .items ():
184268 raw = self ._read_file (fpath )
185269 if raw is None :
186270 continue
187271
188- fname = os .path .basename (fpath )
189- value , _ , _ = format_sensor (fname , raw )
272+ spec = SENSOR_SPECS [stype ]
273+ value = spec ["format" ](raw )
274+
190275 if value != self .treestore .get_value (itr , COL_VALUE ):
191276 self .treestore .set_value (itr , COL_VALUE , value )
277+
192278 self .treestore .thaw_notify ()
193279 return True
194280
0 commit comments