@@ -6,6 +6,7 @@ package system
66import (
77 "os"
88 "path/filepath"
9+ "strconv"
910 "strings"
1011
1112 "log/slog"
@@ -96,3 +97,67 @@ func GetPendingReboot() *bool {
9697 }
9798 return nil
9899}
100+
101+ // GetPSUPowerUsage attempts to read PSU power usage from hwmon interfaces on Linux.
102+ // This function specifically looks for Corsair PSU sensors via the corsair_psu driver.
103+ // Returns power usage in watts, or nil if not available.
104+ func GetPSUPowerUsage () * float64 {
105+ // Look for hwmon directories that might contain PSU power sensors
106+ hwmonDirs , err := filepath .Glob ("/sys/class/hwmon/hwmon*" )
107+ if err != nil {
108+ slog .Debug ("Failed to glob hwmon directories" , "error" , err )
109+ return nil
110+ }
111+
112+ for _ , hwmonDir := range hwmonDirs {
113+ // Check if this is a Corsair PSU by looking at the name file
114+ nameFile := filepath .Join (hwmonDir , "name" )
115+ nameData , err := os .ReadFile (nameFile )
116+ if err != nil {
117+ continue
118+ }
119+ name := strings .TrimSpace (string (nameData ))
120+
121+ // Look for corsair_psu driver or similar PSU-related names
122+ if strings .Contains (strings .ToLower (name ), "corsair" ) ||
123+ strings .Contains (strings .ToLower (name ), "psu" ) ||
124+ strings .Contains (strings .ToLower (name ), "rmi" ) {
125+
126+ // Try to read power1_input (total power consumption)
127+ powerFile := filepath .Join (hwmonDir , "power1_input" )
128+ if powerData , err := os .ReadFile (powerFile ); err == nil {
129+ valueStr := strings .TrimSpace (string (powerData ))
130+ if value , err := strconv .ParseFloat (valueStr , 64 ); err == nil {
131+ // Convert from microwatts to watts
132+ powerWatts := value / 1_000_000
133+ slog .Info ("Found PSU power usage" , "hwmon" , hwmonDir , "name" , name , "power_watts" , powerWatts )
134+ return & powerWatts
135+ }
136+ }
137+ }
138+ }
139+
140+ // Fallback: try to find any power1_input file in hwmon directories
141+ // This covers cases where the PSU might not be properly identified by name
142+ for _ , hwmonDir := range hwmonDirs {
143+ powerFile := filepath .Join (hwmonDir , "power1_input" )
144+ if powerData , err := os .ReadFile (powerFile ); err == nil {
145+ // Check if there's a corresponding power1_label to see if it's PSU-related
146+ labelFile := filepath .Join (hwmonDir , "power1_label" )
147+ if labelData , err := os .ReadFile (labelFile ); err == nil {
148+ label := strings .ToLower (strings .TrimSpace (string (labelData )))
149+ if strings .Contains (label , "psu" ) || strings .Contains (label , "power" ) {
150+ valueStr := strings .TrimSpace (string (powerData ))
151+ if value , err := strconv .ParseFloat (valueStr , 64 ); err == nil {
152+ powerWatts := value / 1_000_000
153+ slog .Debug ("Found PSU power usage via label" , "hwmon" , hwmonDir , "label" , label , "power_watts" , powerWatts )
154+ return & powerWatts
155+ }
156+ }
157+ }
158+ }
159+ }
160+
161+ slog .Debug ("No PSU power usage sensors found" )
162+ return nil
163+ }
0 commit comments