Skip to content

Commit

Permalink
OSX: implement sensors_battery
Browse files Browse the repository at this point in the history
  • Loading branch information
Arnon Yaari committed Nov 18, 2017
1 parent e0df5da commit a41457b
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 2 deletions.
2 changes: 1 addition & 1 deletion psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2210,7 +2210,7 @@ def sensors_fans():
__all__.append("sensors_fans")


# Linux, Windows, FreeBSD
# Linux, Windows, FreeBSD, OSX
if hasattr(_psplatform, "sensors_battery"):

def sensors_battery():
Expand Down
19 changes: 19 additions & 0 deletions psutil/_psosx.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,25 @@ def disk_partitions(all=False):
return retlist


# =====================================================================
# --- sensors
# =====================================================================


def sensors_battery():
"""Return battery information.
"""
percent, minsleft, power_plugged = cext.sensors_battery()
power_plugged = power_plugged == 1
if power_plugged:
secsleft = _common.POWER_TIME_UNLIMITED
elif minsleft == -1:
secsleft = _common.POWER_TIME_UNKNOWN
else:
secsleft = minsleft * 60
return _common.sbattery(percent, secsleft, power_plugged)


# =====================================================================
# --- network
# =====================================================================
Expand Down
91 changes: 91 additions & 0 deletions psutil/_psutil_osx.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/IOBSD.h>
#include <IOKit/ps/IOPowerSources.h>
#include <IOKit/ps/IOPSKeys.h>

#include "_psutil_common.h"
#include "_psutil_posix.h"
Expand Down Expand Up @@ -1788,6 +1790,93 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
}


/*
* Return battery information.
*/
static PyObject *
psutil_sensors_battery(PyObject *self, PyObject *args) {
PyObject *py_retlist = NULL;
CFTypeRef power_info = NULL;
CFArrayRef power_sources_list = NULL;
CFDictionaryRef power_sources_infomation = NULL;
CFNumberRef capacity_ref = NULL;
CFNumberRef time_to_empty_ref = NULL;
CFStringRef ps_state_ref = NULL;
uint32_t capacity; /* units are percent */
int time_to_empty; /* units are minutes */
int is_power_plugged;

power_info = IOPSCopyPowerSourcesInfo();

if (!power_info) {
PyErr_SetString(PyExc_RuntimeError,
"Failed to get power sources info");
goto error;
}

power_sources_list = IOPSCopyPowerSourcesList(power_info);
if (!power_sources_list) {
PyErr_SetString(PyExc_RuntimeError,
"Failed to get power sources list");
goto error;
}

/* Should only get one source. But in practice, check for > 0 sources */
if (!CFArrayGetCount(power_sources_list)) {
PyErr_SetString(PyExc_RuntimeError,
"Power sources list is empty");
goto error;
}

power_sources_infomation = IOPSGetPowerSourceDescription(
power_info, CFArrayGetValueAtIndex(power_sources_list, 0));

capacity_ref = (CFNumberRef) CFDictionaryGetValue(
power_sources_infomation, CFSTR(kIOPSCurrentCapacityKey));
if (!CFNumberGetValue(capacity_ref, kCFNumberSInt32Type, &capacity)) {
PyErr_SetString(PyExc_RuntimeError,
"No battery capacity infomration in power sources info");
goto error;
}

ps_state_ref = (CFStringRef) CFDictionaryGetValue(
power_sources_infomation, CFSTR(kIOPSPowerSourceStateKey));
is_power_plugged = CFStringCompare(
ps_state_ref, CFSTR(kIOPSACPowerValue), 0)
== kCFCompareEqualTo;

time_to_empty_ref = (CFNumberRef) CFDictionaryGetValue(
power_sources_infomation, CFSTR(kIOPSTimeToEmptyKey));
if (!CFNumberGetValue(time_to_empty_ref,
kCFNumberIntType, &time_to_empty)) {
/* This value is recommended for non-Apple power sources, so it's not
* an error if it doesn't exist. We'll return -1 for "unknown" */
/* A value of -1 indicates "Still Calculating the Time" also for
* apple power source */
time_to_empty = -1;
}

py_retlist = Py_BuildValue("Iii",
capacity, time_to_empty, is_power_plugged);
if (!py_retlist) {
goto error;
}

CFRelease(power_info);
CFRelease(power_sources_list);
/* Caller should NOT release power_sources_infomation */

return py_retlist;

error:
if (power_info)
CFRelease(power_info);
if (power_sources_list)
CFRelease(power_sources_list);
Py_XDECREF(py_retlist);
return NULL;
}


/*
* define the psutil C module methods and initialize the module.
Expand Down Expand Up @@ -1854,6 +1943,8 @@ PsutilMethods[] = {
"Return currently connected users as a list of tuples"},
{"cpu_stats", psutil_cpu_stats, METH_VARARGS,
"Return CPU statistics"},
{"sensors_battery", psutil_sensors_battery, METH_VARARGS,
"Return battery information."},

// --- others
{"set_testing", psutil_set_testing, METH_NOARGS,
Expand Down
2 changes: 1 addition & 1 deletion psutil/tests/test_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def test_sensors_fans(self):

def test_battery(self):
self.assertEqual(hasattr(psutil, "sensors_battery"),
LINUX or WINDOWS or FREEBSD)
LINUX or WINDOWS or FREEBSD or OSX)

def test_proc_environ(self):
self.assertEqual(hasattr(psutil.Process, "environ"),
Expand Down

0 comments on commit a41457b

Please sign in to comment.