summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2017-01-27 01:12:45 +0100
committerGiampaolo Rodola <g.rodola@gmail.com>2017-01-27 01:12:45 +0100
commit90b261aef4c94e364596f07711565ea250f3a10e (patch)
tree22c31d3666490ebce9c214312c660f0dc692cb60
parent78f8a41eb902c144dd31a9e4bafe63c25fea3c76 (diff)
downloadpsutil-90b261aef4c94e364596f07711565ea250f3a10e.tar.gz
#955: add power_plugged info
-rw-r--r--docs/index.rst16
-rw-r--r--psutil/_common.py6
-rw-r--r--psutil/_pslinux.py27
-rw-r--r--psutil/_pswindows.py3
-rwxr-xr-xpsutil/tests/test_linux.py57
-rwxr-xr-xpsutil/tests/test_system.py9
6 files changed, 95 insertions, 23 deletions
diff --git a/docs/index.rst b/docs/index.rst
index af44c516..f9ee7ee4 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -627,10 +627,11 @@ Sensors
- **percent**: battery power left as a percentage.
- **secsleft**: (rough approximation) number of seconds left before the
- battery run out of power; this may be set to
- :data:`psutil.POWER_TIME_UNKNOWN <psutil.POWER_TIME_UNKNOWN>`
- or :data:`psutil.POWER_TIME_UNLIMITED <psutil.POWER_TIME_UNLIMITED>` in
- case the remaining time cannot be determined or is unlimited.
+ battery run out of power. If the AC power cable is connected this will be
+ set to :data:`psutil.POWER_TIME_UNLIMITED <psutil.POWER_TIME_UNLIMITED>`.
+ If it can't be determined it will be set to
+ :data:`psutil.POWER_TIME_UNKNOWN <psutil.POWER_TIME_UNKNOWN>`.
+ - **power_plugged**: ``True`` if the AC power cable is connected.
If no battery is installed this function will return ``None``. Example::
@@ -641,10 +642,15 @@ Sensors
...
>>> batt = psutil.sensors_battery()
>>> batt
- sbattery(percent=93, secsleft=16628)
+ sbattery(percent=93, secsleft=16628, power_plugged=False)
>>> print("charge = %s%%, time left = %s" % (batt.percent, secs2hours(batt.secsleft)))
charge = 93%, time left = 4:37:08
+ .. warning::
+
+ This API is experimental. Backward incompatible changes may occur if
+ deemed necessary.
+
Availability: Linux, Windows
.. versionadded:: 5.1.0
diff --git a/psutil/_common.py b/psutil/_common.py
index b28af999..89dc1617 100644
--- a/psutil/_common.py
+++ b/psutil/_common.py
@@ -126,11 +126,11 @@ if enum is None:
POWER_TIME_UNKNOWN = -1
POWER_TIME_UNLIMITED = -2
else:
- class Power(enum.IntEnum):
+ class BatteryTime(enum.IntEnum):
POWER_TIME_UNKNOWN = -1
POWER_TIME_UNLIMITED = -2
- globals().update(NicDuplex.__members__)
+ globals().update(BatteryTime.__members__)
# ===================================================================
@@ -170,7 +170,7 @@ scpustats = namedtuple(
# psutil.cpu_freq()
scpufreq = namedtuple('scpufreq', ['current', 'min', 'max'])
# psutil.sensors_battery()
-sbattery = namedtuple('sbattery', ['percent', 'secsleft'])
+sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged'])
# --- for Process methods
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index 5be7f759..fec47be5 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -1065,18 +1065,25 @@ def disk_partitions(all=False):
def sensors_battery():
- root = "/sys/class/power_supply/BAT0/"
- if not os.path.exists(root.rstrip('/')):
+ root = "/sys/class/power_supply/BAT0"
+ if not os.path.exists(root):
return None
- energy_now = int(cat(root + "energy_now"))
- power_now = int(cat(root + "power_now"))
- percent = int(cat(root + "capacity"))
- try:
- secsleft = int(energy_now / power_now * 3600)
- except ZeroDivisionError:
- secsleft = _common.POWER_TIME_UNKNOWN
- return _common.sbattery(percent, secsleft)
+ power_plugged = \
+ cat("/sys/class/power_supply/AC0/online", fallback=b"0") == b"1"
+ energy_now = int(cat(root + "/energy_now"))
+ power_now = int(cat(root + "/power_now"))
+ percent = int(cat(root + "/capacity"))
+
+ if power_plugged:
+ secsleft = _common.POWER_TIME_UNLIMITED
+ else:
+ try:
+ secsleft = int(energy_now / power_now * 3600)
+ except ZeroDivisionError:
+ secsleft = _common.POWER_TIME_UNKNOWN
+
+ return _common.sbattery(percent, secsleft, power_plugged)
# =====================================================================
diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py
index 80faec91..51e2efab 100644
--- a/psutil/_pswindows.py
+++ b/psutil/_pswindows.py
@@ -384,7 +384,8 @@ def sensors_battery():
elif secsleft == -1:
secsleft = _common.POWER_TIME_UNKNOWN
- return _common.sbattery(percent, secsleft)
+ # TODO: implement power_plugged
+ return _common.sbattery(percent, secsleft, False)
# =====================================================================
diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py
index 54d5e251..a00bdae6 100755
--- a/psutil/tests/test_linux.py
+++ b/psutil/tests/test_linux.py
@@ -1024,12 +1024,67 @@ class TestMisc(unittest.TestCase):
finally:
t.stop()
- def test_sensors_battery_percent(self):
+
+class TestSensorsBattery(unittest.TestCase):
+
+ def test_percent(self):
out = sh("acpi -b")
acpi_value = int(out.split(",")[1].strip().replace('%', ''))
psutil_value = psutil.sensors_battery().percent
self.assertAlmostEqual(acpi_value, psutil_value, delta=1)
+ def test_power_plugged(self):
+ out = sh("acpi -b")
+ plugged = "Charging" in out.split('\n')[0]
+ self.assertEqual(psutil.sensors_battery().power_plugged, plugged)
+
+ def test_emulate_power_plugged(self):
+ # Pretend the AC power cable is connected.
+ def open_mock(name, *args, **kwargs):
+ if name.startswith("/sys/class/power_supply/AC0/online"):
+ return io.BytesIO(b"1")
+ else:
+ return orig_open(name, *args, **kwargs)
+
+ orig_open = open
+ patch_point = 'builtins.open' if PY3 else '__builtin__.open'
+ with mock.patch(patch_point, side_effect=open_mock) as m:
+ self.assertEqual(psutil.sensors_battery().power_plugged, True)
+ self.assertEqual(
+ psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNLIMITED)
+ assert m.called
+
+ def test_emulate_power_not_plugged(self):
+ # Pretend the AC power cable is not connected.
+ def open_mock(name, *args, **kwargs):
+ if name.startswith("/sys/class/power_supply/AC0/online"):
+ return io.BytesIO(b"0")
+ else:
+ return orig_open(name, *args, **kwargs)
+
+ orig_open = open
+ patch_point = 'builtins.open' if PY3 else '__builtin__.open'
+ with mock.patch(patch_point, side_effect=open_mock) as m:
+ self.assertEqual(psutil.sensors_battery().power_plugged, False)
+ self.assertGreaterEqual(psutil.sensors_battery().secsleft, 0)
+ assert m.called
+
+ def test_emulate_power_undetermined(self):
+ # Pretend we can't know whether the AC power cable not
+ # connected (assert fallback to False).
+ def open_mock(name, *args, **kwargs):
+ if name.startswith("/sys/class/power_supply/AC0/online"):
+ raise IOError(errno.ENOENT, "")
+ else:
+ return orig_open(name, *args, **kwargs)
+
+ orig_open = open
+ patch_point = 'builtins.open' if PY3 else '__builtin__.open'
+ with mock.patch(patch_point, side_effect=open_mock) as m:
+ self.assertEqual(psutil.sensors_battery().power_plugged, False)
+ self.assertGreaterEqual(psutil.sensors_battery().secsleft, 0)
+ assert m.called
+
# =====================================================================
# test process
diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py
index cf33d140..36b1b11c 100755
--- a/psutil/tests/test_system.py
+++ b/psutil/tests/test_system.py
@@ -761,12 +761,15 @@ class TestSystemAPIs(unittest.TestCase):
ret = psutil.sensors_battery()
if ret is None:
return # no battery
- if ret.percent is not None:
- self.assertGreaterEqual(ret.percent, 0)
- self.assertLessEqual(ret.percent, 100)
+ self.assertGreaterEqual(ret.percent, 0)
+ self.assertLessEqual(ret.percent, 100)
if ret.secsleft not in (psutil.POWER_TIME_UNKNOWN,
psutil.POWER_TIME_UNLIMITED):
self.assertGreaterEqual(ret.secsleft, 0)
+ else:
+ if ret.secsleft == psutil.POWER_TIME_UNLIMITED:
+ self.assertTrue(ret.power_plugged)
+ self.assertIsInstance(ret.power_plugged, bool)
if __name__ == '__main__':