diff --git a/collector/powersupplyclass.go b/collector/powersupplyclass.go index 87ac9812..ffa4d4e6 100644 --- a/collector/powersupplyclass.go +++ b/collector/powersupplyclass.go @@ -11,21 +11,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !nopowersupplyclass && linux -// +build !nopowersupplyclass,linux +//go:build !nopowersupplyclass && (linux || darwin) +// +build !nopowersupplyclass +// +build linux darwin package collector import ( - "errors" - "fmt" - "os" "regexp" - "strings" "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/procfs/sysfs" "gopkg.in/alecthomas/kingpin.v2" ) @@ -53,151 +49,3 @@ func NewPowerSupplyClassCollector(logger log.Logger) (Collector, error) { logger: logger, }, nil } - -func (c *powerSupplyClassCollector) Update(ch chan<- prometheus.Metric) error { - powerSupplyClass, err := getPowerSupplyClassInfo(c.ignoredPattern) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - return ErrNoData - } - return fmt.Errorf("could not get power_supply class info: %w", err) - } - for _, powerSupply := range powerSupplyClass { - - for name, value := range map[string]*int64{ - "authentic": powerSupply.Authentic, - "calibrate": powerSupply.Calibrate, - "capacity": powerSupply.Capacity, - "capacity_alert_max": powerSupply.CapacityAlertMax, - "capacity_alert_min": powerSupply.CapacityAlertMin, - "cyclecount": powerSupply.CycleCount, - "online": powerSupply.Online, - "present": powerSupply.Present, - "time_to_empty_seconds": powerSupply.TimeToEmptyNow, - "time_to_full_seconds": powerSupply.TimeToFullNow, - } { - if value != nil { - pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value), powerSupply.Name, prometheus.GaugeValue) - } - } - - for name, value := range map[string]*int64{ - "current_boot": powerSupply.CurrentBoot, - "current_max": powerSupply.CurrentMax, - "current_ampere": powerSupply.CurrentNow, - "energy_empty": powerSupply.EnergyEmpty, - "energy_empty_design": powerSupply.EnergyEmptyDesign, - "energy_full": powerSupply.EnergyFull, - "energy_full_design": powerSupply.EnergyFullDesign, - "energy_watthour": powerSupply.EnergyNow, - "voltage_boot": powerSupply.VoltageBoot, - "voltage_max": powerSupply.VoltageMax, - "voltage_max_design": powerSupply.VoltageMaxDesign, - "voltage_min": powerSupply.VoltageMin, - "voltage_min_design": powerSupply.VoltageMinDesign, - "voltage_volt": powerSupply.VoltageNow, - "voltage_ocv": powerSupply.VoltageOCV, - "charge_control_limit": powerSupply.ChargeControlLimit, - "charge_control_limit_max": powerSupply.ChargeControlLimitMax, - "charge_counter": powerSupply.ChargeCounter, - "charge_empty": powerSupply.ChargeEmpty, - "charge_empty_design": powerSupply.ChargeEmptyDesign, - "charge_full": powerSupply.ChargeFull, - "charge_full_design": powerSupply.ChargeFullDesign, - "charge_ampere": powerSupply.ChargeNow, - "charge_term_current": powerSupply.ChargeTermCurrent, - "constant_charge_current": powerSupply.ConstantChargeCurrent, - "constant_charge_current_max": powerSupply.ConstantChargeCurrentMax, - "constant_charge_voltage": powerSupply.ConstantChargeVoltage, - "constant_charge_voltage_max": powerSupply.ConstantChargeVoltageMax, - "precharge_current": powerSupply.PrechargeCurrent, - "input_current_limit": powerSupply.InputCurrentLimit, - "power_watt": powerSupply.PowerNow, - } { - if value != nil { - pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value)/1e6, powerSupply.Name, prometheus.GaugeValue) - } - } - - for name, value := range map[string]*int64{ - "temp_celsius": powerSupply.Temp, - "temp_alert_max_celsius": powerSupply.TempAlertMax, - "temp_alert_min_celsius": powerSupply.TempAlertMin, - "temp_ambient_celsius": powerSupply.TempAmbient, - "temp_ambient_max_celsius": powerSupply.TempAmbientMax, - "temp_ambient_min_celsius": powerSupply.TempAmbientMin, - "temp_max_celsius": powerSupply.TempMax, - "temp_min_celsius": powerSupply.TempMin, - } { - if value != nil { - pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value)/10.0, powerSupply.Name, prometheus.GaugeValue) - } - } - - var ( - keys []string - values []string - ) - for name, value := range map[string]string{ - "power_supply": powerSupply.Name, - "capacity_level": powerSupply.CapacityLevel, - "charge_type": powerSupply.ChargeType, - "health": powerSupply.Health, - "manufacturer": powerSupply.Manufacturer, - "model_name": powerSupply.ModelName, - "serial_number": powerSupply.SerialNumber, - "status": powerSupply.Status, - "technology": powerSupply.Technology, - "type": powerSupply.Type, - "usb_type": powerSupply.UsbType, - "scope": powerSupply.Scope, - } { - if value != "" { - keys = append(keys, name) - values = append(values, strings.ToValidUTF8(value, "�")) - } - } - - fieldDesc := prometheus.NewDesc( - prometheus.BuildFQName(namespace, c.subsystem, "info"), - "info of /sys/class/power_supply/.", - keys, - nil, - ) - ch <- prometheus.MustNewConstMetric(fieldDesc, prometheus.GaugeValue, 1.0, values...) - - } - - return nil -} - -func pushPowerSupplyMetric(ch chan<- prometheus.Metric, subsystem string, name string, value float64, powerSupplyName string, valueType prometheus.ValueType) { - fieldDesc := prometheus.NewDesc( - prometheus.BuildFQName(namespace, subsystem, name), - fmt.Sprintf("%s value of /sys/class/power_supply/.", name), - []string{"power_supply"}, - nil, - ) - - ch <- prometheus.MustNewConstMetric(fieldDesc, valueType, value, powerSupplyName) -} - -func getPowerSupplyClassInfo(ignore *regexp.Regexp) (sysfs.PowerSupplyClass, error) { - fs, err := sysfs.NewFS(*sysPath) - if err != nil { - return nil, err - } - powerSupplyClass, err := fs.PowerSupplyClass() - - if err != nil { - return powerSupplyClass, fmt.Errorf("error obtaining power_supply class info: %w", err) - } - - for device := range powerSupplyClass { - if ignore.MatchString(device) { - delete(powerSupplyClass, device) - } - } - - return powerSupplyClass, nil -} diff --git a/collector/powersupplyclass_darwin.go b/collector/powersupplyclass_darwin.go new file mode 100644 index 00000000..a070f64d --- /dev/null +++ b/collector/powersupplyclass_darwin.go @@ -0,0 +1,418 @@ +// Copyright 2019 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !nopowersupplyclass +// +build !nopowersupplyclass + +package collector + +/* +#cgo LDFLAGS: -framework IOKit -framework CoreFoundation +#include +#include +#include + +// values collected from IOKit Power Source APIs +// Functions documentation available at +// https://developer.apple.com/documentation/iokit/iopowersources_h +// CFDictionary keys definition +// https://developer.apple.com/documentation/iokit/iopskeys_h/defines +struct macos_powersupply { + char *Name; + char *PowerSourceState; + char *Type; + char *TransportType; + char *BatteryHealth; + char *HardwareSerialNumber; + + int *PowerSourceID; + int *CurrentCapacity; + int *MaxCapacity; + int *DesignCapacity; + int *NominalCapacity; + + int *TimeToEmpty; + int *TimeToFullCharge; + + int *Voltage; + int *Current; + + int *Temperature; + + // boolean values + int *IsCharged; + int *IsCharging; + int *InternalFailure; + int *IsPresent; +}; + +int *CFDictionaryGetInt(CFDictionaryRef theDict, const void *key) { + CFNumberRef tmp; + int *value; + + tmp = CFDictionaryGetValue(theDict, key); + + if (tmp == NULL) + return NULL; + + value = (int*)malloc(sizeof(int)); + if (CFNumberGetValue(tmp, kCFNumberIntType, value)) { + return value; + } + + free(value); + return NULL; +} + +int *CFDictionaryGetBoolean(CFDictionaryRef theDict, const void *key) { + CFBooleanRef tmp; + int *value; + + tmp = CFDictionaryGetValue(theDict, key); + + if (tmp == NULL) + return NULL; + + value = (int*)malloc(sizeof(int)); + if (CFBooleanGetValue(tmp)) { + *value = 1; + } else { + *value = 0; + } + + return value; +} + +char *CFDictionaryGetSring(CFDictionaryRef theDict, const void *key) { + CFStringRef tmp; + CFIndex size; + char *value; + + tmp = CFDictionaryGetValue(theDict, key); + + if (tmp == NULL) + return NULL; + + size = CFStringGetLength(tmp) + 1; + value = (char*)malloc(size); + + if(CFStringGetCString(tmp, value, size, kCFStringEncodingUTF8)) { + return value; + } + + free(value); + return NULL; +} + +struct macos_powersupply* getPowerSupplyInfo(CFDictionaryRef powerSourceInformation) { + struct macos_powersupply *ret; + + if (powerSourceInformation == NULL) + return NULL; + + ret = (struct macos_powersupply*)malloc(sizeof(struct macos_powersupply)); + + ret->PowerSourceID = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSPowerSourceIDKey)); + ret->CurrentCapacity = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSCurrentCapacityKey)); + ret->MaxCapacity = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSMaxCapacityKey)); + ret->DesignCapacity = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSDesignCapacityKey)); + ret->NominalCapacity = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSNominalCapacityKey)); + ret->TimeToEmpty = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSTimeToEmptyKey)); + ret->TimeToFullCharge = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSTimeToFullChargeKey)); + ret->Voltage = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSVoltageKey)); + ret->Current = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSCurrentKey)); + ret->Temperature = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSTemperatureKey)); + + ret->Name = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSNameKey)); + ret->PowerSourceState = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSPowerSourceStateKey)); + ret->Type = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSTypeKey)); + ret->TransportType = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSTransportTypeKey)); + ret->BatteryHealth = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSBatteryHealthKey)); + ret->HardwareSerialNumber = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSHardwareSerialNumberKey)); + + ret->IsCharged = CFDictionaryGetBoolean(powerSourceInformation, CFSTR(kIOPSIsChargedKey)); + ret->IsCharging = CFDictionaryGetBoolean(powerSourceInformation, CFSTR(kIOPSIsChargingKey)); + ret->InternalFailure = CFDictionaryGetBoolean(powerSourceInformation, CFSTR(kIOPSInternalFailureKey)); + ret->IsPresent = CFDictionaryGetBoolean(powerSourceInformation, CFSTR(kIOPSIsPresentKey)); + + return ret; +} + + + +void releasePowerSupply(struct macos_powersupply *ps) { + free(ps->Name); + free(ps->PowerSourceState); + free(ps->Type); + free(ps->TransportType); + free(ps->BatteryHealth); + free(ps->HardwareSerialNumber); + + free(ps->PowerSourceID); + free(ps->CurrentCapacity); + free(ps->MaxCapacity); + free(ps->DesignCapacity); + free(ps->NominalCapacity); + free(ps->TimeToEmpty); + free(ps->TimeToFullCharge); + free(ps->Voltage); + free(ps->Current); + free(ps->Temperature); + + free(ps->IsCharged); + free(ps->IsCharging); + free(ps->InternalFailure); + free(ps->IsPresent); + + free(ps); +} +*/ +import "C" + +import ( + "fmt" + "strconv" + + "github.com/prometheus/client_golang/prometheus" +) + +func (c *powerSupplyClassCollector) Update(ch chan<- prometheus.Metric) error { + psList, err := getPowerSourceList() + if err != nil { + return fmt.Errorf("couldn't get IOPPowerSourcesList: %w", err) + } + + for _, info := range psList { + labels := getPowerSourceDescriptorLabels(info) + powerSupplyName := labels["power_supply"] + + if c.ignoredPattern.MatchString(powerSupplyName) { + continue + } + + for name, value := range getPowerSourceDescriptorMap(info) { + if value == nil { + continue + } + + ch <- prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, c.subsystem, name), + fmt.Sprintf("IOKit Power Source information field %s for .", name), + []string{"power_supply"}, nil, + ), + prometheus.GaugeValue, *value, powerSupplyName, + ) + } + + pushEnumMetric( + ch, + getPowerSourceDescriptorState(info), + "power_source_state", + c.subsystem, + powerSupplyName, + ) + + pushEnumMetric( + ch, + getPowerSourceDescriptorBatteryHealth(info), + "battery_health", + c.subsystem, + powerSupplyName, + ) + + var ( + keys []string + values []string + ) + for name, value := range labels { + if value != "" { + keys = append(keys, name) + values = append(values, value) + } + } + fieldDesc := prometheus.NewDesc( + prometheus.BuildFQName(namespace, c.subsystem, "info"), + "IOKit Power Source information for .", + keys, + nil, + ) + ch <- prometheus.MustNewConstMetric(fieldDesc, prometheus.GaugeValue, 1.0, values...) + + C.releasePowerSupply(info) + } + + return nil +} + +// getPowerSourceList fetches information from IOKit APIs +// +// Data is provided as opaque CoreFoundation references +// C.getPowerSupplyInfo will convert those objects in something +// easily manageable in Go. +// https://developer.apple.com/documentation/iokit/iopowersources_h +func getPowerSourceList() ([]*C.struct_macos_powersupply, error) { + infos, err := C.IOPSCopyPowerSourcesInfo() + if err != nil { + return nil, err + } + defer C.CFRelease(infos) + + psList, err := C.IOPSCopyPowerSourcesList(infos) + if err != nil { + return nil, err + } + + if psList == C.CFArrayRef(0) { + return nil, nil + } + defer C.CFRelease(C.CFTypeRef(psList)) + + size, err := C.CFArrayGetCount(psList) + if err != nil { + return nil, err + } + + ret := make([]*C.struct_macos_powersupply, size) + for i := C.CFIndex(0); i < size; i++ { + ps, err := C.CFArrayGetValueAtIndex(psList, i) + if err != nil { + return nil, err + } + + dict, err := C.IOPSGetPowerSourceDescription(infos, (C.CFTypeRef)(ps)) + if err != nil { + return nil, err + } + + info, err := C.getPowerSupplyInfo(dict) + if err != nil { + return nil, err + } + + ret[int(i)] = info + } + + return ret, nil +} + +func getPowerSourceDescriptorMap(info *C.struct_macos_powersupply) map[string]*float64 { + return map[string]*float64{ + "current_capacity": convertValue(info.CurrentCapacity), + "max_capacity": convertValue(info.MaxCapacity), + "design_capacity": convertValue(info.DesignCapacity), + "nominal_capacity": convertValue(info.NominalCapacity), + "time_to_empty_seconds": minutesToSeconds(info.TimeToEmpty), + "time_to_full_seconds": minutesToSeconds(info.TimeToFullCharge), + "voltage_volt": scaleValue(info.Voltage, 1e3), + "current_ampere": scaleValue(info.Current, 1e3), + "temp_celsius": convertValue(info.Temperature), + "present": convertValue(info.IsPresent), + "charging": convertValue(info.IsCharging), + "charged": convertValue(info.IsCharged), + "internal_failure": convertValue(info.InternalFailure), + } +} + +func getPowerSourceDescriptorLabels(info *C.struct_macos_powersupply) map[string]string { + return map[string]string{ + "id": strconv.FormatInt(int64(*info.PowerSourceID), 10), + "power_supply": C.GoString(info.Name), + "type": C.GoString(info.Type), + "transport_type": C.GoString(info.TransportType), + "serial_number": C.GoString(info.HardwareSerialNumber), + } +} + +func getPowerSourceDescriptorState(info *C.struct_macos_powersupply) map[string]float64 { + stateMap := map[string]float64{ + "Off Line": 0, + "AC Power": 0, + "Battery Power": 0, + } + + // This field is always present + // https://developer.apple.com/documentation/iokit/kiopspowersourcestatekey + stateMap[C.GoString(info.PowerSourceState)] = 1 + + return stateMap +} + +func getPowerSourceDescriptorBatteryHealth(info *C.struct_macos_powersupply) map[string]float64 { + // This field is optional + // https://developer.apple.com/documentation/iokit/kiopsBatteryHealthkey + if info.BatteryHealth == nil { + return nil + } + + stateMap := map[string]float64{ + "Good": 0, + "Fair": 0, + "Poor": 0, + } + + stateMap[C.GoString(info.BatteryHealth)] = 1 + + return stateMap +} + +func convertValue(value *C.int) *float64 { + if value == nil { + return nil + } + + ret := new(float64) + *ret = (float64)(*value) + return ret +} + +func scaleValue(value *C.int, scale float64) *float64 { + ret := convertValue(value) + if ret == nil { + return nil + } + + *ret /= scale + + return ret +} + +// minutesToSeconds converts *C.int minutes into *float64 seconds. +// +// Only positive values will be scaled to seconds, because negative ones +// have special meanings. I.e. -1 indicates "Still Calculating the Time" +func minutesToSeconds(minutes *C.int) *float64 { + ret := convertValue(minutes) + if ret == nil { + return nil + } + + if *ret > 0 { + *ret *= 60 + } + + return ret +} + +func pushEnumMetric(ch chan<- prometheus.Metric, values map[string]float64, name, subsystem, powerSupply string) { + for state, value := range values { + ch <- prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, name), + fmt.Sprintf("IOKit Power Source information field %s for .", name), + []string{"power_supply", "state"}, nil, + ), + prometheus.GaugeValue, value, powerSupply, state, + ) + } +} diff --git a/collector/powersupplyclass_linux.go b/collector/powersupplyclass_linux.go new file mode 100644 index 00000000..86e81fb6 --- /dev/null +++ b/collector/powersupplyclass_linux.go @@ -0,0 +1,176 @@ +// Copyright 2019 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !nopowersupplyclass +// +build !nopowersupplyclass + +package collector + +import ( + "errors" + "fmt" + "os" + "regexp" + "strings" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/procfs/sysfs" +) + +func (c *powerSupplyClassCollector) Update(ch chan<- prometheus.Metric) error { + powerSupplyClass, err := getPowerSupplyClassInfo(c.ignoredPattern) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return ErrNoData + } + return fmt.Errorf("could not get power_supply class info: %w", err) + } + for _, powerSupply := range powerSupplyClass { + + for name, value := range map[string]*int64{ + "authentic": powerSupply.Authentic, + "calibrate": powerSupply.Calibrate, + "capacity": powerSupply.Capacity, + "capacity_alert_max": powerSupply.CapacityAlertMax, + "capacity_alert_min": powerSupply.CapacityAlertMin, + "cyclecount": powerSupply.CycleCount, + "online": powerSupply.Online, + "present": powerSupply.Present, + "time_to_empty_seconds": powerSupply.TimeToEmptyNow, + "time_to_full_seconds": powerSupply.TimeToFullNow, + } { + if value != nil { + pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value), powerSupply.Name, prometheus.GaugeValue) + } + } + + for name, value := range map[string]*int64{ + "current_boot": powerSupply.CurrentBoot, + "current_max": powerSupply.CurrentMax, + "current_ampere": powerSupply.CurrentNow, + "energy_empty": powerSupply.EnergyEmpty, + "energy_empty_design": powerSupply.EnergyEmptyDesign, + "energy_full": powerSupply.EnergyFull, + "energy_full_design": powerSupply.EnergyFullDesign, + "energy_watthour": powerSupply.EnergyNow, + "voltage_boot": powerSupply.VoltageBoot, + "voltage_max": powerSupply.VoltageMax, + "voltage_max_design": powerSupply.VoltageMaxDesign, + "voltage_min": powerSupply.VoltageMin, + "voltage_min_design": powerSupply.VoltageMinDesign, + "voltage_volt": powerSupply.VoltageNow, + "voltage_ocv": powerSupply.VoltageOCV, + "charge_control_limit": powerSupply.ChargeControlLimit, + "charge_control_limit_max": powerSupply.ChargeControlLimitMax, + "charge_counter": powerSupply.ChargeCounter, + "charge_empty": powerSupply.ChargeEmpty, + "charge_empty_design": powerSupply.ChargeEmptyDesign, + "charge_full": powerSupply.ChargeFull, + "charge_full_design": powerSupply.ChargeFullDesign, + "charge_ampere": powerSupply.ChargeNow, + "charge_term_current": powerSupply.ChargeTermCurrent, + "constant_charge_current": powerSupply.ConstantChargeCurrent, + "constant_charge_current_max": powerSupply.ConstantChargeCurrentMax, + "constant_charge_voltage": powerSupply.ConstantChargeVoltage, + "constant_charge_voltage_max": powerSupply.ConstantChargeVoltageMax, + "precharge_current": powerSupply.PrechargeCurrent, + "input_current_limit": powerSupply.InputCurrentLimit, + "power_watt": powerSupply.PowerNow, + } { + if value != nil { + pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value)/1e6, powerSupply.Name, prometheus.GaugeValue) + } + } + + for name, value := range map[string]*int64{ + "temp_celsius": powerSupply.Temp, + "temp_alert_max_celsius": powerSupply.TempAlertMax, + "temp_alert_min_celsius": powerSupply.TempAlertMin, + "temp_ambient_celsius": powerSupply.TempAmbient, + "temp_ambient_max_celsius": powerSupply.TempAmbientMax, + "temp_ambient_min_celsius": powerSupply.TempAmbientMin, + "temp_max_celsius": powerSupply.TempMax, + "temp_min_celsius": powerSupply.TempMin, + } { + if value != nil { + pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value)/10.0, powerSupply.Name, prometheus.GaugeValue) + } + } + + var ( + keys []string + values []string + ) + for name, value := range map[string]string{ + "power_supply": powerSupply.Name, + "capacity_level": powerSupply.CapacityLevel, + "charge_type": powerSupply.ChargeType, + "health": powerSupply.Health, + "manufacturer": powerSupply.Manufacturer, + "model_name": powerSupply.ModelName, + "serial_number": powerSupply.SerialNumber, + "status": powerSupply.Status, + "technology": powerSupply.Technology, + "type": powerSupply.Type, + "usb_type": powerSupply.UsbType, + "scope": powerSupply.Scope, + } { + if value != "" { + keys = append(keys, name) + values = append(values, strings.ToValidUTF8(value, "�")) + } + } + + fieldDesc := prometheus.NewDesc( + prometheus.BuildFQName(namespace, c.subsystem, "info"), + "info of /sys/class/power_supply/.", + keys, + nil, + ) + ch <- prometheus.MustNewConstMetric(fieldDesc, prometheus.GaugeValue, 1.0, values...) + + } + + return nil +} + +func pushPowerSupplyMetric(ch chan<- prometheus.Metric, subsystem string, name string, value float64, powerSupplyName string, valueType prometheus.ValueType) { + fieldDesc := prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, name), + fmt.Sprintf("%s value of /sys/class/power_supply/.", name), + []string{"power_supply"}, + nil, + ) + + ch <- prometheus.MustNewConstMetric(fieldDesc, valueType, value, powerSupplyName) +} + +func getPowerSupplyClassInfo(ignore *regexp.Regexp) (sysfs.PowerSupplyClass, error) { + fs, err := sysfs.NewFS(*sysPath) + if err != nil { + return nil, err + } + powerSupplyClass, err := fs.PowerSupplyClass() + + if err != nil { + return powerSupplyClass, fmt.Errorf("error obtaining power_supply class info: %w", err) + } + + for device := range powerSupplyClass { + if ignore.MatchString(device) { + delete(powerSupplyClass, device) + } + } + + return powerSupplyClass, nil +}