| 
									
										
										
										
											2021-10-28 01:22:24 -07:00
										 |  |  | // 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 | 
					
						
							| 
									
										
										
										
											2022-03-23 19:35:15 -07:00
										 |  |  | #include <CoreFoundation/CFNumber.h> | 
					
						
							|  |  |  | #include <CoreFoundation/CFRunLoop.h> | 
					
						
							|  |  |  | #include <CoreFoundation/CFString.h> | 
					
						
							| 
									
										
										
										
											2021-10-28 01:22:24 -07:00
										 |  |  | #include <IOKit/ps/IOPowerSources.h> | 
					
						
							|  |  |  | #include <IOKit/ps/IOPSKeys.h> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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 <power_supply>.", 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 <power_supply>.", | 
					
						
							|  |  |  | 			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 <power_supply>.", name), | 
					
						
							|  |  |  | 				[]string{"power_supply", "state"}, nil, | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			prometheus.GaugeValue, value, powerSupply, state, | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |