Skip to content

Commit 6ed1f7e

Browse files
Merge pull request #117 from shaardie/power_supply_class
Add Support for /sys/class/power_supply
2 parents e4d4a22 + 18fdf56 commit 6ed1f7e

File tree

3 files changed

+408
-0
lines changed

3 files changed

+408
-0
lines changed

sysfs/class_power_supply.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// Copyright 2018 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
// +build !windows
15+
16+
package sysfs
17+
18+
import (
19+
"fmt"
20+
"io/ioutil"
21+
"os"
22+
"reflect"
23+
"strconv"
24+
"strings"
25+
26+
"github.com/prometheus/procfs/internal/util"
27+
)
28+
29+
// PowerSupply contains info from files in /sys/class/power_supply for a single power supply.
30+
type PowerSupply struct {
31+
Name string // Power Supply Name
32+
Authentic *int64 `fileName:"authentic"` // /sys/class/power_suppy/<Name>/authentic
33+
Calibrate *int64 `fileName:"calibrate"` // /sys/class/power_suppy/<Name>/calibrate
34+
Capacity *int64 `fileName:"capacity"` // /sys/class/power_suppy/<Name>/capacity
35+
CapacityAlertMax *int64 `fileName:"capacity_alert_max"` // /sys/class/power_suppy/<Name>/capacity_alert_max
36+
CapacityAlertMin *int64 `fileName:"capacity_alert_min"` // /sys/class/power_suppy/<Name>/capacity_alert_min
37+
CapacityLevel string `fileName:"capacity_level"` // /sys/class/power_suppy/<Name>/capacity_level
38+
ChargeAvg *int64 `fileName:"charge_avg"` // /sys/class/power_suppy/<Name>/charge_avg
39+
ChargeControlLimit *int64 `fileName:"charge_control_limit"` // /sys/class/power_suppy/<Name>/charge_control_limit
40+
ChargeControlLimitMax *int64 `fileName:"charge_control_limit_max"` // /sys/class/power_suppy/<Name>/charge_control_limit_max
41+
ChargeCounter *int64 `fileName:"charge_counter"` // /sys/class/power_suppy/<Name>/charge_counter
42+
ChargeEmpty *int64 `fileName:"charge_empty"` // /sys/class/power_suppy/<Name>/charge_empty
43+
ChargeEmptyDesign *int64 `fileName:"charge_empty_design"` // /sys/class/power_suppy/<Name>/charge_empty_design
44+
ChargeFull *int64 `fileName:"charge_full"` // /sys/class/power_suppy/<Name>/charge_full
45+
ChargeFullDesign *int64 `fileName:"charge_full_design"` // /sys/class/power_suppy/<Name>/charge_full_design
46+
ChargeNow *int64 `fileName:"charge_now"` // /sys/class/power_suppy/<Name>/charge_now
47+
ChargeTermCurrent *int64 `fileName:"charge_term_current"` // /sys/class/power_suppy/<Name>/charge_term_current
48+
ChargeType string `fileName:"charge_type"` // /sys/class/power_supply/<Name>/charge_type
49+
ConstantChargeCurrent *int64 `fileName:"constant_charge_current"` // /sys/class/power_suppy/<Name>/constant_charge_current
50+
ConstantChargeCurrentMax *int64 `fileName:"constant_charge_current_max"` // /sys/class/power_suppy/<Name>/constant_charge_current_max
51+
ConstantChargeVoltage *int64 `fileName:"constant_charge_voltage"` // /sys/class/power_suppy/<Name>/constant_charge_voltage
52+
ConstantChargeVoltageMax *int64 `fileName:"constant_charge_voltage_max"` // /sys/class/power_suppy/<Name>/constant_charge_voltage_max
53+
CurrentAvg *int64 `fileName:"current_avg"` // /sys/class/power_suppy/<Name>/current_avg
54+
CurrentBoot *int64 `fileName:"current_boot"` // /sys/class/power_suppy/<Name>/current_boot
55+
CurrentMax *int64 `fileName:"current_max"` // /sys/class/power_suppy/<Name>/current_max
56+
CurrentNow *int64 `fileName:"current_now"` // /sys/class/power_suppy/<Name>/current_now
57+
CycleCount *int64 `fileName:"cycle_count"` // /sys/class/power_suppy/<Name>/cycle_count
58+
EnergyAvg *int64 `fileName:"energy_avg"` // /sys/class/power_supply/<Name>/energy_avg
59+
EnergyEmpty *int64 `fileName:"energy_empty"` // /sys/class/power_suppy/<Name>/energy_empty
60+
EnergyEmptyDesign *int64 `fileName:"energy_empty_design"` // /sys/class/power_suppy/<Name>/energy_empty_design
61+
EnergyFull *int64 `fileName:"energy_full"` // /sys/class/power_suppy/<Name>/energy_full
62+
EnergyFullDesign *int64 `fileName:"energy_full_design"` // /sys/class/power_suppy/<Name>/energy_full_design
63+
EnergyNow *int64 `fileName:"energy_now"` // /sys/class/power_supply/<Name>/energy_now
64+
Health string `fileName:"health"` // /sys/class/power_suppy/<Name>/health
65+
InputCurrentLimit *int64 `fileName:"input_current_limit"` // /sys/class/power_suppy/<Name>/input_current_limit
66+
Manufacturer string `fileName:"manufacturer"` // /sys/class/power_suppy/<Name>/manufacturer
67+
ModelName string `fileName:"model_name"` // /sys/class/power_suppy/<Name>/model_name
68+
Online *int64 `fileName:"online"` // /sys/class/power_suppy/<Name>/online
69+
PowerAvg *int64 `fileName:"power_avg"` // /sys/class/power_suppy/<Name>/power_avg
70+
PowerNow *int64 `fileName:"power_now"` // /sys/class/power_suppy/<Name>/power_now
71+
PrechargeCurrent *int64 `fileName:"precharge_current"` // /sys/class/power_suppy/<Name>/precharge_current
72+
Present *int64 `fileName:"present"` // /sys/class/power_suppy/<Name>/present
73+
Scope *int64 `fileName:"scope"` // /sys/class/power_suppy/<Name>/scope
74+
SerialNumber string `fileName:"serial_number"` // /sys/class/power_suppy/<Name>/serial_number
75+
Status string `fileName:"status"` // /sys/class/power_supply/<Name>/status
76+
Technology string `fileName:"technology"` // /sys/class/power_suppy/<Name>/technology
77+
Temp *int64 `fileName:"temp"` // /sys/class/power_suppy/<Name>/temp
78+
TempAlertMax *int64 `fileName:"temp_alert_max"` // /sys/class/power_suppy/<Name>/temp_alert_max
79+
TempAlertMin *int64 `fileName:"temp_alert_min"` // /sys/class/power_suppy/<Name>/temp_alert_min
80+
TempAmbient *int64 `fileName:"temp_ambient"` // /sys/class/power_suppy/<Name>/temp_ambient
81+
TempAmbientMax *int64 `fileName:"temp_ambient_max"` // /sys/class/power_suppy/<Name>/temp_ambient_max
82+
TempAmbientMin *int64 `fileName:"temp_ambient_min"` // /sys/class/power_suppy/<Name>/temp_ambient_min
83+
TempMax *int64 `fileName:"temp_max"` // /sys/class/power_suppy/<Name>/temp_max
84+
TempMin *int64 `fileName:"temp_min"` // /sys/class/power_suppy/<Name>/temp_min
85+
TimeToEmptyAvg *int64 `fileName:"time_to_empty_avg"` // /sys/class/power_suppy/<Name>/time_to_empty_avg
86+
TimeToEmptyNow *int64 `fileName:"time_to_empty_now"` // /sys/class/power_suppy/<Name>/time_to_empty_now
87+
TimeToFullAvg *int64 `fileName:"time_to_full_avg"` // /sys/class/power_suppy/<Name>/time_to_full_avg
88+
TimeToFullNow *int64 `fileName:"time_to_full_now"` // /sys/class/power_suppy/<Name>/time_to_full_now
89+
Type string `fileName:"type"` // /sys/class/power_supply/<Name>/type
90+
UsbType string `fileName:"usb_type"` // /sys/class/power_supply/<Name>/usb_type
91+
VoltageAvg *int64 `fileName:"voltage_avg"` // /sys/class/power_supply/<Name>/voltage_avg
92+
VoltageBoot *int64 `fileName:"voltage_boot"` // /sys/class/power_suppy/<Name>/voltage_boot
93+
VoltageMax *int64 `fileName:"voltage_max"` // /sys/class/power_suppy/<Name>/voltage_max
94+
VoltageMaxDesign *int64 `fileName:"voltage_max_design"` // /sys/class/power_suppy/<Name>/voltage_max_design
95+
VoltageMin *int64 `fileName:"voltage_min"` // /sys/class/power_suppy/<Name>/voltage_min
96+
VoltageMinDesign *int64 `fileName:"voltage_min_design"` // /sys/class/power_suppy/<Name>/voltage_min_design
97+
VoltageNow *int64 `fileName:"voltage_now"` // /sys/class/power_supply/<Name>/voltage_now
98+
VoltageOCV *int64 `fileName:"voltage_ocv"` // /sys/class/power_suppy/<Name>/voltage_ocv
99+
}
100+
101+
// PowerSupplyClass is a collection of every power supply in /sys/class/power_supply/.
102+
// The map keys are the names of the power supplies.
103+
type PowerSupplyClass map[string]PowerSupply
104+
105+
// NewPowerSupplyClass returns info for all power supplies read from /sys/class/power_supply/.
106+
func NewPowerSupplyClass() (PowerSupplyClass, error) {
107+
fs, err := NewFS(DefaultMountPoint)
108+
if err != nil {
109+
return nil, err
110+
}
111+
112+
return fs.NewPowerSupplyClass()
113+
}
114+
115+
// NewPowerSupplyClass returns info for all power supplies read from /sys/class/power_supply/.
116+
func (fs FS) NewPowerSupplyClass() (PowerSupplyClass, error) {
117+
path := fs.Path("class/power_supply")
118+
119+
powerSupplyDirs, err := ioutil.ReadDir(path)
120+
if err != nil {
121+
return PowerSupplyClass{}, fmt.Errorf("cannot access %s dir %s", path, err)
122+
}
123+
124+
powerSupplyClass := PowerSupplyClass{}
125+
for _, powerSupplyDir := range powerSupplyDirs {
126+
powerSupply, err := powerSupplyClass.parsePowerSupply(path + "/" + powerSupplyDir.Name())
127+
if err != nil {
128+
return nil, err
129+
}
130+
powerSupply.Name = powerSupplyDir.Name()
131+
powerSupplyClass[powerSupplyDir.Name()] = *powerSupply
132+
}
133+
return powerSupplyClass, nil
134+
}
135+
136+
func (psc PowerSupplyClass) parsePowerSupply(powerSupplyPath string) (*PowerSupply, error) {
137+
powerSupply := PowerSupply{}
138+
powerSupplyElem := reflect.ValueOf(&powerSupply).Elem()
139+
powerSupplyType := reflect.TypeOf(powerSupply)
140+
141+
//start from 1 - skip the Name field
142+
for i := 1; i < powerSupplyElem.NumField(); i++ {
143+
fieldType := powerSupplyType.Field(i)
144+
fieldValue := powerSupplyElem.Field(i)
145+
146+
if fieldType.Tag.Get("fileName") == "" {
147+
panic(fmt.Errorf("field %s does not have a filename tag", fieldType.Name))
148+
}
149+
150+
value, err := util.SysReadFile(powerSupplyPath + "/" + fieldType.Tag.Get("fileName"))
151+
152+
if err != nil {
153+
if os.IsNotExist(err) || err.Error() == "operation not supported" || err.Error() == "invalid argument" {
154+
continue
155+
}
156+
return nil, fmt.Errorf("could not access file %s: %s", fieldType.Tag.Get("fileName"), err)
157+
}
158+
159+
switch fieldValue.Kind() {
160+
case reflect.String:
161+
fieldValue.SetString(value)
162+
case reflect.Ptr:
163+
var int64ptr *int64
164+
switch fieldValue.Type() {
165+
case reflect.TypeOf(int64ptr):
166+
var intValue int64
167+
if strings.HasPrefix(value, "0x") {
168+
intValue, err = strconv.ParseInt(value[2:], 16, 64)
169+
if err != nil {
170+
return nil, fmt.Errorf("expected hex value for %s, got: %s", fieldType.Name, value)
171+
}
172+
} else {
173+
intValue, err = strconv.ParseInt(value, 10, 64)
174+
if err != nil {
175+
return nil, fmt.Errorf("expected Uint64 value for %s, got: %s", fieldType.Name, value)
176+
}
177+
}
178+
fieldValue.Set(reflect.ValueOf(&intValue))
179+
default:
180+
return nil, fmt.Errorf("unhandled pointer type %q", fieldValue.Type())
181+
}
182+
default:
183+
return nil, fmt.Errorf("unhandled type %q", fieldValue.Kind())
184+
}
185+
}
186+
187+
return &powerSupply, nil
188+
}

sysfs/class_power_supply_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright 2018 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
// +build !windows
15+
16+
package sysfs
17+
18+
import (
19+
"encoding/json"
20+
"reflect"
21+
"testing"
22+
)
23+
24+
func TestNewPowerSupplyClass(t *testing.T) {
25+
fs, err := NewFS("fixtures")
26+
if err != nil {
27+
t.Fatal(err)
28+
}
29+
30+
psc, err := fs.NewPowerSupplyClass()
31+
if err != nil {
32+
t.Fatal(err)
33+
}
34+
35+
var (
36+
acOnline int64 = 0
37+
bat0Capacity int64 = 98
38+
bat0CycleCount int64 = 0
39+
bat0EnergyFull int64 = 50060000
40+
bat0EnergyFullDesign int64 = 47520000
41+
bat0EnergyNow int64 = 49450000
42+
bat0PowerNow int64 = 4830000
43+
bat0Present int64 = 1
44+
bat0VoltageMinDesign int64 = 10800000
45+
bat0VoltageNow int64 = 12229000
46+
)
47+
48+
powerSupplyClass := PowerSupplyClass{
49+
"AC": {
50+
Name: "AC",
51+
Type: "Mains",
52+
Online: &acOnline,
53+
},
54+
"BAT0": {
55+
Name: "BAT0",
56+
Capacity: &bat0Capacity,
57+
CapacityLevel: "Normal",
58+
CycleCount: &bat0CycleCount,
59+
EnergyFull: &bat0EnergyFull,
60+
EnergyFullDesign: &bat0EnergyFullDesign,
61+
EnergyNow: &bat0EnergyNow,
62+
Manufacturer: "LGC",
63+
ModelName: "LNV-45N1",
64+
PowerNow: &bat0PowerNow,
65+
Present: &bat0Present,
66+
SerialNumber: "38109",
67+
Status: "Discharging",
68+
Technology: "Li-ion",
69+
Type: "Battery",
70+
VoltageMinDesign: &bat0VoltageMinDesign,
71+
VoltageNow: &bat0VoltageNow,
72+
},
73+
}
74+
75+
if !reflect.DeepEqual(powerSupplyClass, psc) {
76+
want, _ := json.Marshal(powerSupplyClass)
77+
get, _ := json.Marshal(psc)
78+
t.Errorf("Result not correct: want %v, have %v.", string(want), string(get))
79+
}
80+
}

0 commit comments

Comments
 (0)