22

I'm playing around with pygame, and one thing I'd like to do is reduce the number of frames per second when the computer is on battery power (to lower the CPU usage and extend battery life).

How can I detect, from Python, whether the computer is currently on battery power?

I'm using Python 3.1 on Windows.

Abizern
  • 146,289
  • 39
  • 203
  • 257
Joe White
  • 94,807
  • 60
  • 220
  • 330
  • Or you could use a bool, that F1 toggles. When on call with 30, else 0. http://www.pygame.org/docs/ref/time.html#Clock.tick – ninMonkey Sep 22 '11 at 04:17

6 Answers6

19

If you want to do it without win32api, you can use the built-in ctypes module. I usually run CPython without win32api, so I kinda like these solutions.

It's a tiny bit more work for GetSystemPowerStatus() because you have to define the SYSTEM_POWER_STATUS structure, but not bad.

# Get power status of the system using ctypes to call GetSystemPowerStatus

import ctypes
from ctypes import wintypes

class SYSTEM_POWER_STATUS(ctypes.Structure):
    _fields_ = [
        ('ACLineStatus', wintypes.BYTE),
        ('BatteryFlag', wintypes.BYTE),
        ('BatteryLifePercent', wintypes.BYTE),
        ('Reserved1', wintypes.BYTE),
        ('BatteryLifeTime', wintypes.DWORD),
        ('BatteryFullLifeTime', wintypes.DWORD),
    ]

SYSTEM_POWER_STATUS_P = ctypes.POINTER(SYSTEM_POWER_STATUS)

GetSystemPowerStatus = ctypes.windll.kernel32.GetSystemPowerStatus
GetSystemPowerStatus.argtypes = [SYSTEM_POWER_STATUS_P]
GetSystemPowerStatus.restype = wintypes.BOOL

status = SYSTEM_POWER_STATUS()
if not GetSystemPowerStatus(ctypes.pointer(status)):
    raise ctypes.WinError()
print('ACLineStatus', status.ACLineStatus)
print('BatteryFlag', status.BatteryFlag)
print('BatteryLifePercent', status.BatteryLifePercent)
print('BatteryLifeTime', status.BatteryLifeTime)
print('BatteryFullLifeTime', status.BatteryFullLifeTime)

On my system that prints this (basically meaning "desktop, plugged in"):

ACLineStatus 1
BatteryFlag -128
BatteryLifePercent -1
BatteryLifeTime 4294967295
BatteryFullLifeTime 4294967295
Kevin
  • 74,910
  • 12
  • 133
  • 166
Ben Hoyt
  • 10,694
  • 5
  • 60
  • 84
  • 3
    There is a bug in wintypes (http://bugs.python.org/issue16376) when BYTE is defined as c_byte, not c_ubyte. Therefore values in SYSTEM_POWER_STATUS won't match those defined in documentation. Use c_ubyte instead of BYTE. – Kentzo Nov 07 '12 at 11:06
  • 1
    It's also worth noting that `Reserved1` is now `SystemStatusFlag` as explained [here](https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-system_power_status#:~:text=Note%C2%A0%C2%A0This%20flag%20and%20the%20GUID_POWER_SAVING_STATUS%20GUID%20were%20introduced%20in%20Windows%C2%A010.%20This%20flag%20was%20previously%20reserved%2C%20named%20Reserved1%2C%20and%20had%20a%20value%20of%200.) – Jacob Dec 29 '21 at 02:55
  • It was finally fixed: [\[GitHub\]: python/cpython - gh-60580: Fix a wrong type of ctypes.wintypes.BYTE](https://github.com/python/cpython/pull/97579). – CristiFati Feb 04 '23 at 13:03
11

The most reliable way to retrieve this information in C is by using GetSystemPowerStatus. If no battery is present ACLineStatus will be set to 128. psutil exposes this information under Linux, Windows and FreeBSD, so to check if battery is present you can do this

>>> import psutil
>>> has_battery = psutil.sensors_battery() is not None

If a battery is present and you want to know whether the power cable is plugged in you can do this:

>>> import psutil
>>> psutil.sensors_battery()
sbattery(percent=99, secsleft=20308, power_plugged=True)
>>> psutil.sensors_battery().power_plugged
True
>>> 
Giampaolo Rodolà
  • 12,488
  • 6
  • 68
  • 60
4

It is easy, all you have to do is to call Windows API function GetSystemPowerStatus from Python, probably by importing win32api module.

EDIT: GetSystemPowerStatus() is not yet implemented in win32api as of build 219 (2014-05-04).

patricktokeeffe
  • 1,058
  • 1
  • 11
  • 21
sorin
  • 161,544
  • 178
  • 535
  • 806
  • @swati if you install ActiveState Python you will not need to install pywin32 anymore, it is included. – sorin May 27 '11 at 14:58
  • 1
    Alas ActiveState python is some kind of proprietary software, and not opensource á la vanilla Python. – joar May 27 '11 at 15:00
  • 3
    It is a commercial product, still open source and you do not have to pay (free). Usually I would prefer to use Python.org version but due to the missing modules, that are not so easy to compile on Windows due to the big number of compilers it makes a lot of sense to use ActivePython. I recommend this from my experience. – sorin May 27 '11 at 15:09
  • 1
    Generally, ActiveState distributions are just batteries included distos of the language in question, and you don't need to pay for them. They have software you can pay for (Komodo), but I believe all the language packages they offer are free to download. – RHSeeger May 27 '11 at 15:46
  • @Sorin, it'd be nice if you could provide a working example of the win32api version. – Ben Hoyt May 27 '11 at 21:51
  • Kindly check [\[SO\]: In Python, how can I detect whether the computer is on battery power? (@CristiFati's answer)](https://stackoverflow.com/a/75347540/4788546) :) – CristiFati Feb 04 '23 at 19:33
1

A simple method for cross platform power status indication is the 'power' module which you can install with pip

    import power
    ans = power.PowerManagement().get_providing_power_source_type()
    if not ans:
        print "plugged into wall socket"
    else:
        print "on battery"
Wayne DSouza
  • 151
  • 1
  • 4
0

You can install acpi.From wikipedia

In a computer, the Advanced Configuration and Power Interface provides an open standard that operating systems can use to discover and configure computer hardware components, to perform power management by putting unused components to sleep, and to perform status monitoring.

Then use the subprocess module in python

import subprocess
cmd = 'acpi -b'

# for python 3.7+
p = subprocess.run(cmd.split(), shell=True, capture_output=True)
battery_info, error = p.stdout.decode(), p.stderr.decode()

# for python3.x (x<6)
battery_info = subprocess.check_output(cmd.split(), shell=True).decode('utf-8')

print (battery_info) 
Abhyudai
  • 826
  • 7
  • 16
0

[SO]: In Python, how can I detect whether the computer is on battery power? (@BenHoyt's answer) is portable and doesn't require extra packages, but it's negatively impacted (until Python v3.12) by a CTypes (WinTypes) bug.
More details about the bug (and fix, workaround): [SO]: Why ctypes.wintypes.BYTE is signed, but native windows BYTE is unsigned? (@CristiFati's answer).

Anyway, I submitted [GitHub]: mhammond/pywin32 - Add GetSystemPowerStatus wrapper for GetSystemPowerStatus function to be available in Win32API.

Building win32api.pyd locally and overwriting the one from site-packages directory (as I mentioned in the Test section), yields:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q006153860]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[prompt]>
[prompt]> :: Power cable unplugged
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test1_pw32\Scripts\python.exe" -c "import win32api as wapi;from pprint import pprint as pp;pp(wapi.GetSystemPowerStatus(), sort_dicts=0);print(\"\nDone.\n\")"
{'ACLineStatus': 0,
 'BatteryFlag': 1,
 'BatteryLifePercent': 99,
 'SystemStatusFlag': 0,
 'BatteryLifeTime': 13094,
 'BatteryFullLifeTime': 4294967295}

Done.


[prompt]>
[prompt]> :: Plug in power cable
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test1_pw32\Scripts\python.exe" -c "import win32api as wapi;from pprint import pprint as pp;pp(wapi.GetSystemPowerStatus(), sort_dicts=0);print(\"\nDone.\n\")"
{'ACLineStatus': 1,
 'BatteryFlag': 1,
 'BatteryLifePercent': 100,
 'SystemStatusFlag': 0,
 'BatteryLifeTime': 4294967295,
 'BatteryFullLifeTime': 4294967295}

Done.

Check [SO]: How to change username of job in print queue using python & win32print (@CristiFati's answer) (at the end) for possible ways to benefit from the (above) patch.

Worth mentioning (if [SO]: In Python, how can I detect whether the computer is on battery power? (@GiampaoloRodolà's answer) is not clear enough about it) that [PyPI]: psutil also uses GetSystemPowerStatus in order to retrieve battery information.

CristiFati
  • 38,250
  • 9
  • 50
  • 87