Skip to content

Commit

Permalink
linux,darwin: introduce battery info API
Browse files Browse the repository at this point in the history
Refs: #4172
Signed-off-by: Juan José Arboleda <[email protected]>
  • Loading branch information
juanarbol committed Feb 22, 2024
1 parent 507f304 commit 511125e
Show file tree
Hide file tree
Showing 8 changed files with 352 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ if(WIN32)
shell32)
list(APPEND uv_sources
src/win/async.c
src/win/battery.c
src/win/core.c
src/win/detect-wakeup.c
src/win/dl.c
Expand Down Expand Up @@ -230,6 +231,7 @@ else()
endif()
list(APPEND uv_sources
src/unix/async.c
src/unix/battery.c
src/unix/core.c
src/unix/dl.c
src/unix/fs.c
Expand Down
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/win \
-D_WIN32_WINNT=0x0602
libuv_la_SOURCES += src/win/async.c \
src/win/atomicops-inl.h \
src/win/battery.c \
src/win/core.c \
src/win/detect-wakeup.c \
src/win/dl.c \
Expand Down Expand Up @@ -97,6 +98,7 @@ else # WINNT
uvinclude_HEADERS += include/uv/unix.h
AM_CPPFLAGS += -I$(top_srcdir)/src/unix
libuv_la_SOURCES += src/unix/async.c \
src/unix/battery.c \
src/unix/core.c \
src/unix/dl.c \
src/unix/fs.c \
Expand Down
30 changes: 30 additions & 0 deletions docs/src/misc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,20 @@ Data types
Random data request type.

.. c:type:: uv_battery_info_t
Data type for battery information storage.

::

typedef struct uv_battery_info_s {
int is_charging;
uint64_t level;
uint64_t discharge_time_in_secs;
uint64_t charge_time_in_secs;
void* reserved[7];
} uv_battery_info_t;

API
---

Expand Down Expand Up @@ -376,6 +390,22 @@ API
.. versionadded:: 1.44.0
.. c:function:: int uv_battery_info(uv_battery_info_t* battery_info)
Gets information about the current power source and battery status of the
system. The gathered information is stored in the provided `battery_info_t`
structure.
In Linux, it will iterate over from `/sys/class/power_supply/BAT0` up to
`/sys/class/power_supply/BAT3` and merge the results in the provided
`uv_battery_info_t` struct.
In macOS, it will use IOKit as the source of information.
:returns: 0 on success, or an error code < 0 on failure.
It will return `UV_ENOSYS` for desktop or non-detected battery sources.
On macOS it could return a `UV_ENOENT` in case of IOKit linking failure.
.. c:function:: int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count)
Gets information about the CPUs on the system. The `cpu_infos` array will
Expand Down
11 changes: 11 additions & 0 deletions include/uv.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ typedef struct uv_passwd_s uv_passwd_t;
typedef struct uv_group_s uv_group_t;
typedef struct uv_utsname_s uv_utsname_t;
typedef struct uv_statfs_s uv_statfs_t;
typedef struct uv_battery_info_s uv_battery_info_t;


typedef struct uv_metrics_s uv_metrics_t;

Expand Down Expand Up @@ -1167,6 +1169,14 @@ struct uv_cpu_info_s {
struct uv_cpu_times_s cpu_times;
};

struct uv_battery_info_s {
int is_charging;
uint64_t level;
uint64_t discharge_time_in_secs;
uint64_t charge_time_in_secs;
void* reserved[7];
};

struct uv_interface_address_s {
char* name;
char phys_addr[6];
Expand Down Expand Up @@ -1302,6 +1312,7 @@ enum {
UV_EXTERN int uv_thread_getpriority(uv_thread_t tid, int* priority);
UV_EXTERN int uv_thread_setpriority(uv_thread_t tid, int priority);

UV_EXTERN int uv_battery_info(uv_battery_info_t* battery_info);
UV_EXTERN unsigned int uv_available_parallelism(void);
UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
Expand Down
265 changes: 265 additions & 0 deletions src/unix/battery.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
/* Copyright libuv project contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/

#include "uv.h"

#if defined(__APPLE__) || TARGET_OS_IPHONE
#include <dlfcn.h>
#include "darwin-stub.h"
#endif /* defined(__APPLE__) || TARGET_OS_IPHONE */

/* macOS won't need any of this */
#if defined(__linux__)
#include <stdlib.h>
#include "internal.h"
#endif

int uv_battery_info(uv_battery_info_t* info) {
int err;
err = UV_ENOSYS;
#if defined(__APPLE__) || TARGET_OS_IPHONE
CFTypeRef (*pIOPSCopyPowerSourcesInfo)(void);
CFArrayRef (*pIOPSCopyPowerSourcesList)(CFTypeRef);
CFIndex (*pCFArrayGetCount)(CFArrayRef);
CFArrayRef (*pIOPSGetPowerSourceDescription)(CFTypeRef, CFTypeRef);
void (*pCFRelease)(CFTypeRef);
void (*pCFNumberGetValue)(CFNumberRef, int, void *);
void* (*pCFArrayGetValueAtIndex)(CFArrayRef, CFIndex);
void* (*pCFDictionaryGetValue)(CFDictionaryRef, const void*);
int (*pCFBooleanGetValue)(CFBooleanRef);
CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
const char*,
CFStringEncoding);

CFBooleanRef is_charging_value;
CFDictionaryRef power_source;
CFNumberRef current_capacity;
CFNumberRef time_remaining;
CFArrayRef sources = NULL;
CFTypeRef blob = NULL;
int h = 0;
int is_charging = 0;
void* core_foundation_handle;
void* iokit_framekwork_handle;

err = UV_ENOENT;
iokit_framekwork_handle = dlopen("/System/Library/Frameworks/"
"IOKit.framework/IOKit",
RTLD_LAZY | RTLD_LOCAL);

core_foundation_handle = dlopen("/System/Library/Frameworks/"
"CoreFoundation.framework/CoreFoundation",
RTLD_LAZY | RTLD_LOCAL);

if (iokit_framekwork_handle == NULL || core_foundation_handle == NULL)
goto out;

*(void **)(&pCFRelease) = dlsym(core_foundation_handle, "CFRelease");
*(void **)(&pCFStringCreateWithCString) =
dlsym(core_foundation_handle, "CFStringCreateWithCString");
*(void **)(&pCFArrayGetCount) =
dlsym(core_foundation_handle, "CFArrayGetCount");
*(void **)(&pCFArrayGetValueAtIndex) =
dlsym(core_foundation_handle, "CFArrayGetValueAtIndex");
*(void **)(&pCFNumberGetValue) =
dlsym(core_foundation_handle, "CFNumberGetValue");
*(void **)(&pCFBooleanGetValue) =
dlsym(core_foundation_handle, "CFBooleanGetValue");
*(void **)(&pCFDictionaryGetValue) =
dlsym(core_foundation_handle, "CFDictionaryGetValue");
*(void **)(&pIOPSCopyPowerSourcesInfo) =
dlsym(iokit_framekwork_handle, "IOPSCopyPowerSourcesInfo");
*(void **)(&pIOPSCopyPowerSourcesList) =
dlsym(iokit_framekwork_handle, "IOPSCopyPowerSourcesList");
*(void **)(&pIOPSGetPowerSourceDescription) =
dlsym(iokit_framekwork_handle, "IOPSGetPowerSourceDescription");

if (pCFRelease == NULL ||
pCFStringCreateWithCString == NULL ||
pCFArrayGetCount == NULL ||
pCFArrayGetValueAtIndex == NULL ||
pCFNumberGetValue == NULL ||
pCFDictionaryGetValue == NULL ||
pIOPSCopyPowerSourcesInfo == NULL ||
pIOPSCopyPowerSourcesList == NULL ||
pIOPSGetPowerSourceDescription == NULL) {
goto out;
}

#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)

blob = pIOPSCopyPowerSourcesInfo();
if (blob == NULL)
goto out;

sources = pIOPSCopyPowerSourcesList(blob);
if (sources == NULL)
goto out;

/* If there are no sources, we're on a desktop or server, nothing to do */
if (pCFArrayGetCount(sources) == 0) {
err = UV_ENOSYS;
goto out;
}

power_source =
pIOPSGetPowerSourceDescription(blob, pCFArrayGetValueAtIndex(sources, 0));
if (power_source == NULL)
goto out;

err = 0;

/* kIOPSCurrentCapacityKey */
current_capacity = pCFDictionaryGetValue(power_source, S("Current Capacity"));
if (current_capacity != NULL) {
pCFNumberGetValue(current_capacity, pkCFNumberIntType, &h);
info->level = h;
}

/* Retrieve is_charging status kIOPSIsChargingKey */
is_charging_value = pCFDictionaryGetValue(power_source, S("Is Charging"));
if (is_charging_value != NULL) {
is_charging = pCFBooleanGetValue(is_charging_value);
info->is_charging = is_charging;
}

/* kIOPSTimeToFullChargeKey and kIOPSTimeToEmptyKey */
time_remaining =
pCFDictionaryGetValue(power_source,
is_charging ? S("Time to Full Charge") : S("Time to Empty"));
if (time_remaining != NULL) {
pCFNumberGetValue(time_remaining, pkCFNumberIntType, &h);
}

/* A value of -1 indicates "Still Calculating the Time" */
if (h == -1)
h = 0;

/* Default unit is minutes, convert to seconds */
h = h * 60;
if (is_charging == 1) {
info->charge_time_in_secs = h;
info->discharge_time_in_secs = 0;
} else {
info->discharge_time_in_secs = h;
info->charge_time_in_secs = 0;
}

out:
if (iokit_framekwork_handle != NULL)
dlclose(iokit_framekwork_handle);

if (core_foundation_handle != NULL)
dlclose(core_foundation_handle);

if (blob != NULL)
pCFRelease(blob);

if (sources != NULL)
pCFRelease(sources);

#elif defined(__linux__)
char buf[1024];
char path[1024];
int r;
int i;
int battery_count;
double energy_full;
double energy_now;
double power_now;
struct stat statbuf;

battery_count = 0;

// Iterate over all the supported batteries
for (i = 0; i <= 3; i++) {
snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%d", i);
r = uv__stat(path, &statbuf);
// The battery doesn't exist, continue
if (r < 0)
continue;

snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%d/power_now", i);
r = uv__slurp(path, buf, sizeof(buf));
if (r != 0)
goto out;

power_now = atof(buf);

snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%d/energy_now", i);
r = uv__slurp(path, buf, sizeof(buf));
if (r != 0)
goto out;

energy_now = atof(buf);

snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%d/status", i);
r = uv__slurp(path, buf, sizeof(buf));
if (r != 0)
goto out;

if (strstr(buf, "Charging") != NULL) {
info->is_charging = 1;
snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%d/energy_full", i);
r = uv__slurp(path, buf, sizeof(buf));
if (r != 0)
goto out;

energy_full = atof(buf);

info->charge_time_in_secs = ((energy_full - energy_now) / power_now) * 3600;
info->discharge_time_in_secs = 0;
} else if (strstr(buf, "Discharging") != NULL) {
info->is_charging = 0;
info->charge_time_in_secs = 0;
info->discharge_time_in_secs = (energy_now / power_now) * 3600;
}

snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%d/capacity", i);
r = uv__slurp(path, buf, sizeof(buf));
if (r != 0)
goto out;

info->level = atoi(buf);
battery_count++;
}

if (battery_count == 0) {
err = UV_ENOSYS;
goto out2;
}

// info->charge_time_in_secs /= battery_count;
// info->discharge_time_in_secs /= battery_count;

err = 0;
out:
/* In case of error, fill with 0 (sum for merge results later) */
// info->charge_time_in_secs = 0;
// info->discharge_time_in_secs = 0;
// info->is_charging = 0;
// info->level = 0;

/* In case of no batteries, leave struct untouched */
out2:
#endif /* defined(__APPLE__) || TARGET_OS_IPHONE */
return err;
}
5 changes: 5 additions & 0 deletions src/unix/darwin-stub.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ typedef uint64_t FSEventStreamEventId;
typedef unsigned CFStringEncoding;
typedef void* CFAllocatorRef;
typedef void* CFArrayRef;
typedef void* CFBooleanRef;
typedef void* CFBundleRef;
typedef void* CFDictionaryRef;
typedef void* CFNumberRef;
typedef void* CFNumberType;
typedef void* CFRunLoopRef;
typedef void* CFRunLoopSourceRef;
typedef void* CFStringRef;
Expand Down Expand Up @@ -72,6 +75,8 @@ struct FSEventStreamContext {
static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100;
static const OSStatus noErr = 0;

static const int pkCFNumberIntType = 9;

static const FSEventStreamEventId kFSEventStreamEventIdSinceNow = -1;

static const int kFSEventStreamCreateFlagNoDefer = 2;
Expand Down

0 comments on commit 511125e

Please sign in to comment.