pax_global_header00006660000000000000000000000064125104035470014513gustar00rootroot0000000000000052 comment=cbeb47eeb6fc3573937bbd97952d596facde9cb4 power-1.4+dfsg/000077500000000000000000000000001251040354700134325ustar00rootroot00000000000000power-1.4+dfsg/.gitignore000066400000000000000000000004571251040354700154300ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject power-1.4+dfsg/README.md000066400000000000000000000010261251040354700147100ustar00rootroot00000000000000Power ===== Crossplatform (Windows, Linux, Mac OS X, FreeBSD) python module to access power capabilities of the system. - Power source type: AC, Battery or UPS - Battery warning level: no warning (None), less than 22% of battery (Early), less than 10min (Final) - Time remaining estimate - Fault tolerant: if for some reason power capabilities cannot be extracted, falls back to AC - Support for multiple battries - Power changes can be observed (Mac OS X only for now) - Very easy to extand to support new features or new systems power-1.4+dfsg/power/000077500000000000000000000000001251040354700145665ustar00rootroot00000000000000power-1.4+dfsg/power/__init__.py000066400000000000000000000027301251040354700167010ustar00rootroot00000000000000# coding=utf-8 """ Provides crossplatform checking of current power source, battery warning level and battery time remaining estimate. Allows you to add observer for power notifications if platform supports it. Usage: from power import PowerManagement, PowerManagementObserver # Automatically imports platform-specific implementation class Observer(PowerManagementObserver): def on_power_sources_change(self, power_management): print("Power sources did change.") def on_time_remaining_change(self, power_management): print("Time remaining did change.") # class Observer(object): # ... # PowerManagementObserver.register(Observer) """ __version__ = '1.4' from sys import platform from power.common import * try: if platform.startswith('darwin'): from power.darwin import PowerManagement elif platform.startswith('freebsd'): from power.freebsd import PowerManagement elif platform.startswith('win32'): from power.win32 import PowerManagement elif platform.startswith('linux'): from power.linux import PowerManagement else: raise RuntimeError("{platform} is not supported.".format(platform=platform)) except RuntimeError as e: import warnings warnings.warn("Unable to load PowerManagement for {platform}. No-op PowerManagement class is used: {error}".format(error=str(e), platform=platform)) from power.common import PowerManagementNoop as PowerManagement power-1.4+dfsg/power/common.py000066400000000000000000000133051251040354700164320ustar00rootroot00000000000000# coding=utf-8 """ Represents common constants and classes for all platforms. @group Power Source Type: POWER_TYPE_AC, POWER_TYPE_BATTERY, POWER_TYPE_UPS @var POWER_TYPE_AC: The system is connected to the external power source. @var POWER_TYPE_BATTERY: The system is connected to the battery. @var POWER_TYPE_UPS: The system is connected to UPS. @type POWER_TYPE_BATTERY: int @type POWER_TYPE_AC: int @type POWER_TYPE_UPS: int @group Low Battery Warning Levels: LOW_BATTERY_WARNING_NONE, LOW_BATTERY_WARNING_EARLY, LOW_BATTERY_WARNING_FINAL @var LOW_BATTERY_WARNING_NONE: The system is connected to the unlimited power source. @var LOW_BATTERY_WARNING_EARLY: The battery has dropped below 22% remaining power. @var LOW_BATTERY_WARNING_FINAL: The battery can provide no more than 10 minutes of runtime. @type LOW_BATTERY_WARNING_EARLY: int @type LOW_BATTERY_WARNING_NONE: int @type LOW_BATTERY_WARNING_FINAL: int @group Special Values For Time Remaining: TIME_REMAINING_UNKNOWN, TIME_REMAINING_UNLIMITED @var TIME_REMAINING_UNKNOWN: Indicates the system is connected to a limited power source, but system is still calculating a time remaining estimate. @var TIME_REMAINING_UNLIMITED: Indicates that the system is connected to an external power source, without time limit. @type TIME_REMAINING_UNKNOWN: float @type TIME_REMAINING_UNLIMITED: float """ __author__ = 'kulakov.ilya@gmail.com' from abc import ABCMeta, abstractmethod import weakref __all__ = [ 'POWER_TYPE_AC', 'POWER_TYPE_BATTERY', 'POWER_TYPE_UPS', 'LOW_BATTERY_WARNING_NONE', 'LOW_BATTERY_WARNING_EARLY', 'LOW_BATTERY_WARNING_FINAL', 'TIME_REMAINING_UNKNOWN', 'TIME_REMAINING_UNLIMITED', 'PowerManagementObserver' ] POWER_TYPE_AC = 0 POWER_TYPE_BATTERY = 1 POWER_TYPE_UPS = 2 LOW_BATTERY_WARNING_NONE = 1 LOW_BATTERY_WARNING_EARLY = 2 LOW_BATTERY_WARNING_FINAL = 3 TIME_REMAINING_UNKNOWN = -1.0 TIME_REMAINING_UNLIMITED = -2.0 class PowerManagementBase(object): """ Base class for platform dependent PowerManagement functions. @ivar _weak_observers: List of weak reference to added observers @note: Platform's implementation may provide additional parameters for initialization """ __metaclass__ = ABCMeta def __init__(self): self._weak_observers = [] @abstractmethod def get_providing_power_source_type(self): """ Returns type of the providing power source. @return: Possible values: - POWER_TYPE_AC - POWER_TYPE_BATTERY - POWER_TYPE_UPS @rtype: int """ pass @abstractmethod def get_low_battery_warning_level(self): """ Returns the system battery warning level. @return: Possible values: - LOW_BATTERY_WARNING_NONE - LOW_BATTERY_WARNING_EARLY - LOW_BATTERY_WARNING_FINAL @rtype: int """ pass @abstractmethod def get_time_remaining_estimate(self): """ Returns the estimated minutes remaining until all power sources (battery and/or UPS) are empty. @return: Special values: - TIME_REMAINING_UNKNOWN - TIME_REMAINING_UNLIMITED @rtype: float """ pass @abstractmethod def add_observer(self, observer): """ Adds weak ref to an observer. @param observer: Instance of class registered with PowerManagementObserver @raise TypeError: If observer is not registered with PowerManagementObserver abstract class """ if not isinstance(observer, PowerManagementObserver): raise TypeError("observer MUST conform to power.PowerManagementObserver") self._weak_observers.append(weakref.ref(observer)) @abstractmethod def remove_observer(self, observer): """ Removes an observer. @param observer: Previously added observer """ self._weak_observers.remove(weakref.ref(observer)) def remove_all_observers(self): """ Removes all registered observers. """ for weak_observer in self._weak_observers: observer = weak_observer() if observer: self.remove_observer(observer) class PowerManagementObserver: """ Base class for PowerManagement observers. Do not make assumptions in what thread or event loop these methods are called. """ __metaclass__ = ABCMeta @abstractmethod def on_power_sources_change(self, power_management): """ @param power_management: Instance of PowerManagement posted notification """ pass @abstractmethod def on_time_remaining_change(self, power_management): """ @param power_management: Instance of PowerManagement posted notification """ pass class PowerManagementNoop(PowerManagementBase): """ No-op subclass of PowerManagement. It operates like AC is always attached and power sources are never changed. """ def get_providing_power_source_type(self): """ @return: Always POWER_TYPE_AC """ return POWER_TYPE_AC def get_low_battery_warning_level(self): """ @return: Always LOW_BATTERY_WARNING_NONE """ return LOW_BATTERY_WARNING_NONE def get_time_remaining_estimate(self): """ @return: Always TIME_REMAINING_UNLIMITED """ return TIME_REMAINING_UNLIMITED def add_observer(self, observer): """ Does nothing. """ pass def remove_observer(self, observer): """ Does nothing. """ pass def remove_all_observers(self): """ Does nothing. """ pass power-1.4+dfsg/power/darwin.py000066400000000000000000000374361251040354700164410ustar00rootroot00000000000000# coding=utf-8 """ Implements PowerManagement functions using IOPowerSources. Requires Mac OS X 10.6+ See doc/darwin for platform-specific details. """ __author__ = 'kulakov.ilya@gmail.com' import weakref import warnings import objc from objc import super from Foundation import * from power import common # Generated in Mac OS X 10.8.2 using the following command: # gen_bridge_metadata -c '-l/System/Library/Frameworks/IOKit.framework/IOKit -I/System/Library/Frameworks/IOKit.framework/Headers/ps/' /System/Library/Frameworks/IOKit.framework/Headers/ps/IOPowerSources.h /System/Library/Frameworks/IOKit.framework/Headers/ps/IOPSKeys.h # # Following keas are added manually, because public headers misses their definitions: # http://opensource.apple.com/source/IOKitUser/IOKitUser-514.16.50/pwr_mgt.subproj/IOPMLibPrivate.h # - kIOPMUPSPowerKey # - kIOPMBatteryPowerKey # - kIOPMACPowerKey # - kIOPSProvidesTimeRemaining IO_POWER_SOURCES_BRIDGESUPPORT = """ """ objc.parseBridgeSupport( IO_POWER_SOURCES_BRIDGESUPPORT, globals(), objc.pathForFramework("/System/Library/Frameworks/IOKit.framework") ) POWER_TYPE_MAP = { kIOPMACPowerKey: common.POWER_TYPE_AC, kIOPMBatteryPowerKey: common.POWER_TYPE_BATTERY, kIOPMUPSPowerKey: common.POWER_TYPE_UPS } WARNING_LEVEL_MAP = { kIOPSLowBatteryWarningNone: common.LOW_BATTERY_WARNING_NONE, kIOPSLowBatteryWarningEarly: common.LOW_BATTERY_WARNING_EARLY, kIOPSLowBatteryWarningFinal: common.LOW_BATTERY_WARNING_FINAL } class PowerSourcesNotificationsObserver(NSObject): """ Manages NSThread instance which is used to run NSRunLoop with only source - IOPSNotificationCreateRunLoopSource. Thread is automatically spawned when first observer is added and stopped when last observer is removed. Does not keep strong references to observers. @note: Method names break PEP8 convention to conform PyObjC naming conventions """ def init(self): self = super(PowerSourcesNotificationsObserver, self).init() if self is not None: self._weak_observers = [] self._thread = None self._lock = objc.object_lock(self) return self def startThread(self): """Spawns new NSThread to handle notifications.""" if self._thread is not None: return self._thread = NSThread.alloc().initWithTarget_selector_object_(self, 'runPowerNotificationsThread', None) self._thread.start() def stopThread(self): """Stops spawned NSThread.""" if self._thread is not None: self.performSelector_onThread_withObject_waitUntilDone_('stopPowerNotificationsThread', self._thread, None, objc.YES) self._thread = None def runPowerNotificationsThread(self): """Main method of the spawned NSThread. Registers run loop source and runs current NSRunLoop.""" pool = NSAutoreleasePool.alloc().init() @objc.callbackFor(IOPSNotificationCreateRunLoopSource) def on_power_source_notification(context): with self._lock: for weak_observer in self._weak_observers: observer = weak_observer() if observer: observer.on_power_source_notification() self._source = IOPSNotificationCreateRunLoopSource(on_power_source_notification, None) CFRunLoopAddSource(NSRunLoop.currentRunLoop().getCFRunLoop(), self._source, kCFRunLoopDefaultMode) while not NSThread.currentThread().isCancelled(): NSRunLoop.currentRunLoop().runMode_beforeDate_(NSDefaultRunLoopMode, NSDate.distantFuture()) del pool def stopPowerNotificationsThread(self): """Removes the only source from NSRunLoop and cancels thread.""" assert NSThread.currentThread() == self._thread CFRunLoopSourceInvalidate(self._source) self._source = None NSThread.currentThread().cancel() def addObserver(self, observer): """ Adds weak ref to an observer. @param observer: Instance of class that implements on_power_source_notification() """ with self._lock: self._weak_observers.append(weakref.ref(observer)) if len(self._weak_observers) == 1: self.startThread() def removeObserver(self, observer): """ Removes an observer. @param observer: Previously added observer """ with self._lock: self._weak_observers.remove(weakref.ref(observer)) if len(self._weak_observers) == 0: self.stopThread() class PowerManagement(common.PowerManagementBase): notifications_observer = PowerSourcesNotificationsObserver.alloc().init() def __init__(self, cf_run_loop=None): """ @param cf_run_loop: If provided, all notifications are posted within this loop """ super(PowerManagement, self).__init__() self._cf_run_loop = cf_run_loop def on_power_source_notification(self): """ Called in response to IOPSNotificationCreateRunLoopSource() event. """ for weak_observer in self._weak_observers: observer = weak_observer() if observer: observer.on_power_sources_change(self) observer.on_time_remaining_change(self) def get_providing_power_source_type(self): """ Uses IOPSCopyPowerSourcesInfo and IOPSGetProvidingPowerSourceType to get providing power source type. """ blob = IOPSCopyPowerSourcesInfo() type = IOPSGetProvidingPowerSourceType(blob) return POWER_TYPE_MAP[type] def get_low_battery_warning_level(self): """ Uses IOPSGetBatteryWarningLevel to get battery warning level. """ warning_level = IOPSGetBatteryWarningLevel() return WARNING_LEVEL_MAP[warning_level] def get_time_remaining_estimate(self): """ In Mac OS X 10.7+ Uses IOPSGetTimeRemainingEstimate to get time remaining estimate. In Mac OS X 10.6 IOPSGetTimeRemainingEstimate is not available. If providing power source type is AC, returns TIME_REMAINING_UNLIMITED. Otherwise looks through all power sources returned by IOPSGetProvidingPowerSourceType and returns total estimate. """ if IOPSGetTimeRemainingEstimate is not None: # Mac OS X 10.7+ estimate = float(IOPSGetTimeRemainingEstimate()) if estimate == -1.0: return common.TIME_REMAINING_UNKNOWN elif estimate == -2.0: return common.TIME_REMAINING_UNLIMITED else: return estimate / 60.0 else: # Mac OS X 10.6 warnings.warn("IOPSGetTimeRemainingEstimate is not preset", RuntimeWarning) blob = IOPSCopyPowerSourcesInfo() type = IOPSGetProvidingPowerSourceType(blob) if type == common.POWER_TYPE_AC: return common.TIME_REMAINING_UNLIMITED else: estimate = 0.0 for source in IOPSCopyPowerSourcesList(blob): description = IOPSGetPowerSourceDescription(blob, source) if kIOPSIsPresentKey in description and description[kIOPSIsPresentKey] and kIOPSTimeToEmptyKey in description and description[kIOPSTimeToEmptyKey] > 0.0: estimate += float(description[kIOPSTimeToEmptyKey]) if estimate > 0.0: return float(estimate) else: return common.TIME_REMAINING_UNKNOWN def add_observer(self, observer): """ Spawns thread or adds IOPSNotificationCreateRunLoopSource directly to provided cf_run_loop @see: __init__ """ super(PowerManagement, self).add_observer(observer) if len(self._weak_observers) == 1: if not self._cf_run_loop: PowerManagement.notifications_observer.addObserver(self) else: @objc.callbackFor(IOPSNotificationCreateRunLoopSource) def on_power_sources_change(context): self.on_power_source_notification() self._source = IOPSNotificationCreateRunLoopSource(on_power_sources_change, None) CFRunLoopAddSource(self._cf_run_loop, self._source, kCFRunLoopDefaultMode) def remove_observer(self, observer): """ Stops thread and invalidates source. """ super(PowerManagement, self).remove_observer(observer) if len(self._weak_observers) == 0: if not self._cf_run_loop: PowerManagement.notifications_observer.removeObserver(self) else: CFRunLoopSourceInvalidate(self._source) self._source = None power-1.4+dfsg/power/freebsd.py000066400000000000000000000142241251040354700165550ustar00rootroot00000000000000# coding=utf-8 """ Implements PowerManagement functions using FreeBSD SYSCTL mechanism. FreeBSD portion written by Tomasz CEDRO (http://www.tomek.cedro.info) """ __author__ = 'cederom@tlen.pl' import os import warnings from power import common import subprocess class PowerManagement(common.PowerManagementBase): @staticmethod def power_source_type(): """ FreeBSD use sysctl hw.acpi.acline to tell if Mains (1) is used or Battery (0). Beware, that on a Desktop machines this hw.acpi.acline oid may not exist. @return: One of common.POWER_TYPE_* @raise: Runtime error if type of power source is not supported """ try: supply=int(subprocess.check_output(["sysctl","-n","hw.acpi.acline"])) except: return common.POWER_TYPE_AC if supply == 1: return common.POWER_TYPE_AC elif supply == 0: return common.POWER_TYPE_BATTERY else: raise RuntimeError("Unknown power source type!") @staticmethod def is_ac_online(): """ @return: True if ac is online. Otherwise False """ try: supply=int(subprocess.check_output(["sysctl","-n","hw.acpi.acline"])) except: return True return supply == 1 @staticmethod def is_battery_present(): """ TODO @return: True if battery is present. Otherwise False """ return False @staticmethod def is_battery_discharging(): """ TODO @return: True if ac is online. Otherwise False """ return False @staticmethod def get_battery_state(): """ TODO @return: Tuple (energy_full, energy_now, power_now) """ energy_now = float(100.0) power_now = float(100.0) energy_full = float(100.0) return energy_full, energy_now, power_now def get_providing_power_source_type(self): """ Looks through all power supplies in POWER_SUPPLY_PATH. If there is an AC adapter online returns POWER_TYPE_AC. If there is a discharging battery, returns POWER_TYPE_BATTERY. Since the order of supplies is arbitrary, whatever found first is returned. """ type = self.power_source_type() if type == common.POWER_TYPE_AC: if self.is_ac_online(): return common.POWER_TYPE_AC elif type == common.POWER_TYPE_BATTERY: if self.is_battery_present() and self.is_battery_discharging(): return common.POWER_TYPE_BATTERY else: warnings.warn("UPS is not supported.") return common.POWER_TYPE_AC def get_low_battery_warning_level(self): """ Looks through all power supplies in POWER_SUPPLY_PATH. If there is an AC adapter online returns POWER_TYPE_AC returns LOW_BATTERY_WARNING_NONE. Otherwise determines total percentage and time remaining across all attached batteries. """ all_energy_full = [] all_energy_now = [] all_power_now = [] try: type = self.power_source_type() if type == common.POWER_TYPE_AC: if self.is_ac_online(): return common.LOW_BATTERY_WARNING_NONE elif type == common.POWER_TYPE_BATTERY: if self.is_battery_present() and self.is_battery_discharging(): energy_full, energy_now, power_now = self.get_battery_state() all_energy_full.append(energy_full) all_energy_now.append(energy_now) all_power_now.append(power_now) else: warnings.warn("UPS is not supported.") except (RuntimeError, IOError) as e: warnings.warn("Unable to read system power information!") try: total_percentage = sum(all_energy_full) / sum(all_energy_now) total_time = sum([energy_now / power_now * 60.0 for energy_now, power_now in zip(all_energy_now, all_power_now)]) if total_time <= 10.0: return common.LOW_BATTERY_WARNING_FINAL elif total_percentage <= 22.0: return common.LOW_BATTERY_WARNING_EARLY else: return common.LOW_BATTERY_WARNING_NONE except ZeroDivisionError as e: warnings.warn("Unable to calculate low battery level: {error}".format(error=str(e))) return common.LOW_BATTERY_WARNING_NONE def get_time_remaining_estimate(self): """ Looks through all power sources and returns total time remaining estimate or TIME_REMAINING_UNLIMITED if ac power supply is online. """ all_energy_now = [] all_power_now = [] try: type = self.power_source_type() if type == common.POWER_TYPE_AC: if self.is_ac_online(supply_path): return common.TIME_REMAINING_UNLIMITED elif type == common.POWER_TYPE_BATTERY: if self.is_battery_present() and self.is_battery_discharging(): energy_full, energy_now, power_now = self.get_battery_state() all_energy_now.append(energy_now) all_power_now.append(power_now) else: warnings.warn("UPS is not supported.") except (RuntimeError, IOError) as e: warnings.warn("Unable to read system power information!") if len(all_energy_now) > 0: try: return sum([energy_now / power_now * 60.0 for energy_now, power_now in zip(all_energy_now, all_power_now)]) except ZeroDivisionError as e: warnings.warn("Unable to calculate time remaining estimate: {error}".format(error=str(e))) return common.TIME_REMAINING_UNKNOWN else: return common.TIME_REMAINING_UNKNOWN def add_observer(self, observer): warnings.warn("Current system does not support observing.") pass def remove_observer(self, observer): warnings.warn("Current system does not support observing.") pass power-1.4+dfsg/power/linux.py000066400000000000000000000177641251040354700163160ustar00rootroot00000000000000# coding=utf-8 """ Implements PowerManagement functions using /sys/class/power_supply/* See doc/linux for platform-specific details. """ __author__ = 'kulakov.ilya@gmail.com' import os import warnings from power import common POWER_SUPPLY_PATH = '/sys/class/power_supply' if not os.access(POWER_SUPPLY_PATH, os.R_OK): raise RuntimeError("Unable to read {path}.".format(path=POWER_SUPPLY_PATH)) class PowerManagement(common.PowerManagementBase): @staticmethod def power_source_type(supply_path): """ @param supply_path: Path to power supply @return: One of common.POWER_TYPE_* @raise: Runtime error if type of power source is not supported """ with open(os.path.join(supply_path, 'type'), 'r') as type_file: type = type_file.readline().strip() if type == 'Mains': return common.POWER_TYPE_AC elif type == 'UPS': return common.POWER_TYPE_UPS elif type == 'Battery': return common.POWER_TYPE_BATTERY else: raise RuntimeError("Type of {path} ({type}) is not supported".format(path=supply_path, type=type)) @staticmethod def is_ac_online(supply_path): """ @param supply_path: Path to power supply @return: True if ac is online. Otherwise False """ with open(os.path.join(supply_path, 'online'), 'r') as online_file: return online_file.readline().strip() == '1' @staticmethod def is_battery_present(supply_path): """ @param supply_path: Path to power supply @return: True if battery is present. Otherwise False """ with open(os.path.join(supply_path, 'present'), 'r') as present_file: return present_file.readline().strip() == '1' @staticmethod def is_battery_discharging(supply_path): """ @param supply_path: Path to power supply @return: True if ac is online. Otherwise False """ with open(os.path.join(supply_path, 'status'), 'r') as status_file: return status_file.readline().strip() == 'Discharging' @staticmethod def get_battery_state(supply_path): """ @param supply_path: Path to power supply @return: Tuple (energy_full, energy_now, power_now) """ with open(os.path.join(supply_path, 'energy_now'), 'r') as energy_now_file: with open(os.path.join(supply_path, 'power_now'), 'r') as power_now_file: with open(os.path.join(supply_path, 'energy_full'), 'r') as energy_full_file: energy_now = float(energy_now_file.readline().strip()) power_now = float(power_now_file.readline().strip()) energy_full = float(energy_full_file.readline().strip()) return energy_full, energy_now, power_now def get_providing_power_source_type(self): """ Looks through all power supplies in POWER_SUPPLY_PATH. If there is an AC adapter online returns POWER_TYPE_AC. If there is a discharging battery, returns POWER_TYPE_BATTERY. Since the order of supplies is arbitrary, whatever found first is returned. """ for supply in os.listdir(POWER_SUPPLY_PATH): supply_path = os.path.join(POWER_SUPPLY_PATH, supply) try: type = self.power_source_type(supply_path) if type == common.POWER_TYPE_AC: if self.is_ac_online(supply_path): return common.POWER_TYPE_AC elif type == common.POWER_TYPE_BATTERY: if self.is_battery_present(supply_path) and self.is_battery_discharging(supply_path): return common.POWER_TYPE_BATTERY else: warnings.warn("UPS is not supported.") except (RuntimeError, IOError) as e: warnings.warn("Unable to read properties of {path}: {error}".format(path=supply_path, error=str(e))) return common.POWER_TYPE_AC def get_low_battery_warning_level(self): """ Looks through all power supplies in POWER_SUPPLY_PATH. If there is an AC adapter online returns POWER_TYPE_AC returns LOW_BATTERY_WARNING_NONE. Otherwise determines total percentage and time remaining across all attached batteries. """ all_energy_full = [] all_energy_now = [] all_power_now = [] for supply in os.listdir(POWER_SUPPLY_PATH): supply_path = os.path.join(POWER_SUPPLY_PATH, supply) try: type = self.power_source_type(supply_path) if type == common.POWER_TYPE_AC: if self.is_ac_online(supply_path): return common.LOW_BATTERY_WARNING_NONE elif type == common.POWER_TYPE_BATTERY: if self.is_battery_present(supply_path) and self.is_battery_discharging(supply_path): energy_full, energy_now, power_now = self.get_battery_state(supply_path) all_energy_full.append(energy_full) all_energy_now.append(energy_now) all_power_now.append(power_now) else: warnings.warn("UPS is not supported.") except (RuntimeError, IOError) as e: warnings.warn("Unable to read properties of {path}: {error}".format(path=supply_path, error=str(e))) try: total_percentage = sum(all_energy_full) / sum(all_energy_now) total_time = sum([energy_now / power_now * 60.0 for energy_now, power_now in zip(all_energy_now, all_power_now)]) if total_time <= 10.0: return common.LOW_BATTERY_WARNING_FINAL elif total_percentage <= 22.0: return common.LOW_BATTERY_WARNING_EARLY else: return common.LOW_BATTERY_WARNING_NONE except ZeroDivisionError as e: warnings.warn("Unable to calculate low battery level: {error}".format(error=str(e))) return common.LOW_BATTERY_WARNING_NONE def get_time_remaining_estimate(self): """ Looks through all power sources and returns total time remaining estimate or TIME_REMAINING_UNLIMITED if ac power supply is online. """ all_energy_now = [] all_power_now = [] for supply in os.listdir(POWER_SUPPLY_PATH): supply_path = os.path.join(POWER_SUPPLY_PATH, supply) try: type = self.power_source_type(supply_path) if type == common.POWER_TYPE_AC: if self.is_ac_online(supply_path): return common.TIME_REMAINING_UNLIMITED elif type == common.POWER_TYPE_BATTERY: if self.is_battery_present(supply_path) and self.is_battery_discharging(supply_path): energy_full, energy_now, power_now = self.get_battery_state(supply_path) all_energy_now.append(energy_now) all_power_now.append(power_now) else: warnings.warn("UPS is not supported.") except (RuntimeError, IOError) as e: warnings.warn("Unable to read properties of {path}: {error}".format(path=supply_path, error=str(e))) if len(all_energy_now) > 0: try: return sum([energy_now / power_now * 60.0 for energy_now, power_now in zip(all_energy_now, all_power_now)]) except ZeroDivisionError as e: warnings.warn("Unable to calculate time remaining estimate: {error}".format(error=str(e))) return common.TIME_REMAINING_UNKNOWN else: return common.TIME_REMAINING_UNKNOWN def add_observer(self, observer): warnings.warn("Current system does not support observing.") pass def remove_observer(self, observer): warnings.warn("Current system does not support observing.") pass power-1.4+dfsg/power/tests.py000066400000000000000000000031051251040354700163010ustar00rootroot00000000000000# coding=utf-8 from __future__ import print_function __author__ = 'kulakov.ilya@gmail.com' import unittest import power class TestPowerManagementCommon(unittest.TestCase): def testGetLowBatteryWarningLevel(self): level = power.PowerManagement().get_low_battery_warning_level() self.assertIsNotNone(level) self.assertIsInstance(level, int) self.assertIn(level, [power.LOW_BATTERY_WARNING_NONE, power.LOW_BATTERY_WARNING_EARLY, power.LOW_BATTERY_WARNING_FINAL]) def testGetRemainingEstimate(self): estimate = power.PowerManagement().get_time_remaining_estimate() self.assertIsNotNone(estimate) self.assertIsInstance(estimate, float) self.assertTrue(estimate == -1.0 or estimate == -2.0 or estimate >= 0.0) def testGetProvidingPowerSource(self): type = power.PowerManagement().get_providing_power_source_type() self.assertIsNotNone(type) self.assertIsInstance(type, int) self.assertIn(type, [power.POWER_TYPE_AC, power.POWER_TYPE_BATTERY, power.POWER_TYPE_UPS]) class TestObserver(power.PowerManagementObserver): def on_power_sources_change(self, power_management): print("on_power_sources_change") def on_time_remaining_change(self, power_management): print("on_time_remaining_change") if __name__ == "__main__": o = TestObserver() p = power.PowerManagement() p.add_observer(o) try: print("Power management observer is registered") import time while True: time.sleep(1) finally: p.remove_observer(o) power-1.4+dfsg/power/win32.py000066400000000000000000000065371251040354700161150ustar00rootroot00000000000000# coding=utf-8 """ Implements PowerManagement functions using GetSystemPowerStatus. Requires Windows XP+. Observing is not supported """ __author__ = 'kulakov.ilya@gmail.com' from ctypes import Structure, wintypes, POINTER, windll, WinError, pointer, WINFUNCTYPE import warnings from power import common # GetSystemPowerStatus # Returns brief description of current system power status. # Windows XP+ # REQUIRED. GetSystemPowerStatus = None try: GetSystemPowerStatus = windll.kernel32.GetSystemPowerStatus class SYSTEM_POWER_STATUS(Structure): _fields_ = [ ('ACLineStatus', wintypes.c_ubyte), ('BatteryFlag', wintypes.c_ubyte), ('BatteryLifePercent', wintypes.c_ubyte), ('Reserved1', wintypes.c_ubyte), ('BatteryLifeTime', wintypes.DWORD), ('BatteryFullLifeTime', wintypes.DWORD) ] GetSystemPowerStatus.argtypes = [POINTER(SYSTEM_POWER_STATUS)] GetSystemPowerStatus.restype = wintypes.BOOL except AttributeError as e: raise RuntimeError("Unable to load GetSystemPowerStatus." "The system does not provide it (Win XP is required) or kernel32.dll is damaged.") POWER_TYPE_MAP = { 0: common.POWER_TYPE_BATTERY, 1: common.POWER_TYPE_AC, 255: common.POWER_TYPE_AC } class PowerManagement(common.PowerManagementBase): def get_providing_power_source_type(self): """ Returns GetSystemPowerStatus().ACLineStatus @raise: WindowsError if any underlying error occures. """ power_status = SYSTEM_POWER_STATUS() if not GetSystemPowerStatus(pointer(power_status)): raise WinError() return POWER_TYPE_MAP[power_status.ACLineStatus] def get_low_battery_warning_level(self): """ Returns warning according to GetSystemPowerStatus().BatteryLifeTime/BatteryLifePercent @raise WindowsError if any underlying error occures. """ power_status = SYSTEM_POWER_STATUS() if not GetSystemPowerStatus(pointer(power_status)): raise WinError() if POWER_TYPE_MAP[power_status.ACLineStatus] == common.POWER_TYPE_AC: return common.LOW_BATTERY_WARNING_NONE else: if power_status.BatteryLifeTime != -1 and power_status.BatteryLifeTime <= 600: return common.LOW_BATTERY_WARNING_FINAL elif power_status.BatteryLifePercent <= 22: return common.LOW_BATTERY_WARNING_EARLY else: return common.LOW_BATTERY_WARNING_NONE def get_time_remaining_estimate(self): """ Returns time remaining estimate according to GetSystemPowerStatus().BatteryLifeTime """ power_status = SYSTEM_POWER_STATUS() if not GetSystemPowerStatus(pointer(power_status)): raise WinError() if POWER_TYPE_MAP[power_status.ACLineStatus] == common.POWER_TYPE_AC: return common.TIME_REMAINING_UNLIMITED elif power_status.BatteryLifeTime == -1: return common.TIME_REMAINING_UNKNOWN else: return float(power_status.BatteryLifeTime) / 60.0 def add_observer(self, observer): warnings.warn("Current system does not support observing.") pass def remove_observer(self, observer): warnings.warn("Current system does not support observing.") pass power-1.4+dfsg/setup.py000066400000000000000000000024471251040354700151530ustar00rootroot00000000000000#!/usr/bin/env python # coding=utf-8 __author__ = 'kulakov.ilya@gmail.com' from setuptools import setup from sys import platform REQUIREMENTS = [] if platform.startswith('darwin'): REQUIREMENTS.append('pyobjc >= 2.5') setup( name="power", version="1.4", description="Cross-platform system power status information.", long_description="Library that allows you get current power source type (AC, Battery or UPS), warning level (none, <22%, <10min) and remaining minutes. You can also observe changes of power source and remaining time.", author="Ilya Kulakov", author_email="kulakov.ilya@gmail.com", url="https://github.com/Kentzo/Power", platforms=["Mac OS X 10.6+", "Windows XP+", "Linux 2.6+", "FreeBSD"], packages=['power'], license="MIT License", classifiers=[ 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Topic :: System :: Monitoring', 'Topic :: System :: Power (UPS)', ], install_requires=REQUIREMENTS )