# coding=utf-8
"""
Implements PowerManagement functions using IOPowerSources.
Requires Mac OS X 10.6+
See doc/darwin for platform-specific details.
"""
import weakref
import warnings
import objc
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 = """<?xml version='1.0'?>
<!DOCTYPE signatures SYSTEM "file://localhost/System/Library/DTDs/BridgeSupport.dtd">
<signatures version='1.0'>
    <depends_on path="/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" />
    <string_constant name='kIOPSACPowerValue' value='AC Power'/>
    <string_constant name='kIOPSBatteryFailureModesKey' value='BatteryFailureModes'/>
    <string_constant name='kIOPSBatteryHealthConditionKey' value='BatteryHealthCondition'/>
    <string_constant name='kIOPSBatteryHealthKey' value='BatteryHealth'/>
    <string_constant name='kIOPSBatteryPowerValue' value='Battery Power'/>
    <string_constant name='kIOPSCheckBatteryValue' value='Check Battery'/>
    <string_constant name='kIOPSCommandDelayedRemovePowerKey' value='Delayed Remove Power'/>
    <string_constant name='kIOPSCommandEnableAudibleAlarmKey' value='Enable Audible Alarm'/>
    <string_constant name='kIOPSCommandStartupDelayKey' value='Startup Delay'/>
    <string_constant name='kIOPSCurrentCapacityKey' value='Current Capacity'/>
    <string_constant name='kIOPSCurrentKey' value='Current'/>
    <string_constant name='kIOPSDeadWarnLevelKey' value='Shutdown Level'/>
    <string_constant name='kIOPSDesignCapacityKey' value='DesignCapacity'/>
    <string_constant name='kIOPSDesignCycleCountKey' value='DesignCycleCount' />
    <string_constant name='kIOPSDynamicStorePath' value='/IOKit/PowerSources'/>
    <string_constant name='kIOPSFailureCellImbalance' value='Cell Imbalance'/>
    <string_constant name='kIOPSFailureChargeFET' value='Charge FET'/>
    <string_constant name='kIOPSFailureChargeOverCurrent' value='Charge Over-Current'/>
    <string_constant name='kIOPSFailureChargeOverTemp' value='Charge Over-Temperature'/>
    <string_constant name='kIOPSFailureDataFlushFault' value='Data Flush Fault'/>
    <string_constant name='kIOPSFailureDischargeFET' value='Discharge FET'/>
    <string_constant name='kIOPSFailureDischargeOverCurrent' value='Discharge Over-Current'/>
    <string_constant name='kIOPSFailureDischargeOverTemp' value='Discharge Over-Temperature'/>
    <string_constant name='kIOPSFailureExternalInput' value='Externally Indicated Failure'/>
    <string_constant name='kIOPSFailureFuseBlown' value='Fuse Blown'/>
    <string_constant name='kIOPSFailureOpenThermistor' value='Open Thermistor'/>
    <string_constant name='kIOPSFailurePeriodicAFEComms' value='Periodic AFE Comms'/>
    <string_constant name='kIOPSFailurePermanentAFEComms' value='Permanent AFE Comms'/>
    <string_constant name='kIOPSFailureSafetyOverVoltage' value='Safety Over-Voltage'/>
    <string_constant name='kIOPSFairValue' value='Fair'/>
    <string_constant name='kIOPSGoodValue' value='Good'/>
    <string_constant name='kIOPSHardwareSerialNumberKey' value='Hardware Serial Number'/>
    <string_constant name='kIOPSHealthConfidenceKey' value='HealthConfidence'/>
    <string_constant name='kIOPSInternalBatteryType' value='InternalBattery'/>
    <string_constant name='kIOPSInternalType' value='Internal'/>
    <string_constant name='kIOPSIsChargedKey' value='Is Charged'/>
    <string_constant name='kIOPSIsChargingKey' value='Is Charging'/>
    <string_constant name='kIOPSIsFinishingChargeKey' value='Is Finishing Charge'/>
    <string_constant name='kIOPSIsPresentKey' value='Is Present'/>
    <string_constant name='kIOPSLowWarnLevelKey' value='Low Warn Level'/>
    <string_constant name='kIOPSMaxCapacityKey' value='Max Capacity'/>
    <string_constant name='kIOPSMaxErrKey' value='MaxErr'/>
    <string_constant name='kIOPSNameKey' value='Name'/>
    <string_constant name='kIOPSNetworkTransportType' value='Ethernet'/>
    <string_constant name='kIOPSNotifyLowBattery' value='com.apple.system.powersources.lowbattery'/>
    <string_constant name='kIOPSOffLineValue' value='Off Line'/>
    <string_constant name='kIOPSPermanentFailureValue' value='Permanent Battery Failure'/>
    <string_constant name='kIOPSPoorValue' value='Poor'/>
    <string_constant name='kIOPSPowerAdapterCurrentKey' value='Current'/>
    <string_constant name='kIOPSPowerAdapterFamilyKey' value='FamilyCode'/>
    <string_constant name='kIOPSPowerAdapterIDKey' value='AdapterID'/>
    <string_constant name='kIOPSPowerAdapterRevisionKey' value='AdapterRevision'/>
    <string_constant name='kIOPSPowerAdapterSerialNumberKey' value='SerialNumber'/>
    <string_constant name='kIOPSPowerAdapterSourceKey' value='Source'/>
    <string_constant name='kIOPSPowerAdapterWattsKey' value='Watts'/>
    <string_constant name='kIOPSPowerSourceIDKey' value='Power Source ID'/>
    <string_constant name='kIOPSPowerSourceStateKey' value='Power Source State'/>
    <string_constant name='kIOPSProvidesTimeRemaining' value='Battery Provides Time Remaining' />
    <string_constant name='kIOPSSerialTransportType' value='Serial'/>
    <string_constant name='kIOPSTimeRemainingNotificationKey' value='com.apple.system.powersources.timeremaining'/>
    <string_constant name='kIOPSTimeToEmptyKey' value='Time to Empty'/>
    <string_constant name='kIOPSTimeToFullChargeKey' value='Time to Full Charge'/>
    <string_constant name='kIOPSTransportTypeKey' value='Transport Type'/>
    <string_constant name='kIOPSTypeKey' value='Type'/>
    <string_constant name='kIOPSUPSManagementClaimed' value='/IOKit/UPSPowerManagementClaimed'/>
    <string_constant name='kIOPSUPSType' value='UPS'/>
    <string_constant name='kIOPSUSBTransportType' value='USB'/>
    <string_constant name='kIOPSVendorDataKey' value='Vendor Specific Data'/>
    <string_constant name='kIOPSVoltageKey' value='Voltage'/>
    <string_constant name="kIOPMUPSPowerKey" value="UPS Power" />
    <string_constant name="kIOPMBatteryPowerKey" value="Battery Power" />
    <string_constant name="kIOPMACPowerKey" value="AC Power" />
    <enum name='kIOPSLowBatteryWarningEarly' value='2'/>
    <enum name='kIOPSLowBatteryWarningFinal' value='3'/>
    <enum name='kIOPSLowBatteryWarningNone' value='1'/>
    <enum name='kIOPSTimeRemainingUnknown' value='-1'/>
    <enum name='kIOPSTimeRemainingUnlimited' value='-2'/>
    <function name='IOPSCopyExternalPowerAdapterDetails'>
        <retval type='^{__CFDictionary=}' already_retained='true'/>
    </function>
    <function name='IOPSCopyPowerSourcesInfo'>
        <retval type='@' already_retained='true'/>
    </function>
    <function name='IOPSCopyPowerSourcesList'>
        <arg type='@'/>
        <retval type='^{__CFArray=}' already_retained='true'/>
    </function>
    <function name='IOPSGetBatteryWarningLevel'>
        <retval type='i'/>
    </function>
    <function name='IOPSGetPowerSourceDescription'>
        <arg type='@'/>
        <arg type='@'/>
        <retval type='^{__CFDictionary=}'/>
    </function>
    <function name='IOPSGetProvidingPowerSourceType'>
        <arg type='@'/>
        <retval type='^{__CFString=}'/>
    </function>
    <function name='IOPSGetTimeRemainingEstimate'>
        <retval type='d'/>
    </function>
    <function name='IOPSNotificationCreateRunLoopSource'>
        <arg type='^?' function_pointer='true'>
            <arg type='^v'/>
            <retval type='v'/>
        </arg>
        <arg type='^v'/>
        <retval type='^{__CFRunLoopSource=}' already_retained='true'/>
    </function>
</signatures>"""

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 = objc.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

# coding=utf-8
"""
Implements PowerManagement functions using IOPowerSources.
Requires Mac OS X 10.6+
See doc/darwin for platform-specific details.
"""
import weakref
import warnings
import objc
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 = """<?xml version='1.0'?>
<!DOCTYPE signatures SYSTEM "file://localhost/System/Library/DTDs/BridgeSupport.dtd">
<signatures version='1.0'>
    <depends_on path="/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" />
    <string_constant name='kIOPSACPowerValue' value='AC Power'/>
    <string_constant name='kIOPSBatteryFailureModesKey' value='BatteryFailureModes'/>
    <string_constant name='kIOPSBatteryHealthConditionKey' value='BatteryHealthCondition'/>
    <string_constant name='kIOPSBatteryHealthKey' value='BatteryHealth'/>
    <string_constant name='kIOPSBatteryPowerValue' value='Battery Power'/>
    <string_constant name='kIOPSCheckBatteryValue' value='Check Battery'/>
    <string_constant name='kIOPSCommandDelayedRemovePowerKey' value='Delayed Remove Power'/>
    <string_constant name='kIOPSCommandEnableAudibleAlarmKey' value='Enable Audible Alarm'/>
    <string_constant name='kIOPSCommandStartupDelayKey' value='Startup Delay'/>
    <string_constant name='kIOPSCurrentCapacityKey' value='Current Capacity'/>
    <string_constant name='kIOPSCurrentKey' value='Current'/>
    <string_constant name='kIOPSDeadWarnLevelKey' value='Shutdown Level'/>
    <string_constant name='kIOPSDesignCapacityKey' value='DesignCapacity'/>
    <string_constant name='kIOPSDesignCycleCountKey' value='DesignCycleCount' />
    <string_constant name='kIOPSDynamicStorePath' value='/IOKit/PowerSources'/>
    <string_constant name='kIOPSFailureCellImbalance' value='Cell Imbalance'/>
    <string_constant name='kIOPSFailureChargeFET' value='Charge FET'/>
    <string_constant name='kIOPSFailureChargeOverCurrent' value='Charge Over-Current'/>
    <string_constant name='kIOPSFailureChargeOverTemp' value='Charge Over-Temperature'/>
    <string_constant name='kIOPSFailureDataFlushFault' value='Data Flush Fault'/>
    <string_constant name='kIOPSFailureDischargeFET' value='Discharge FET'/>
    <string_constant name='kIOPSFailureDischargeOverCurrent' value='Discharge Over-Current'/>
    <string_constant name='kIOPSFailureDischargeOverTemp' value='Discharge Over-Temperature'/>
    <string_constant name='kIOPSFailureExternalInput' value='Externally Indicated Failure'/>
    <string_constant name='kIOPSFailureFuseBlown' value='Fuse Blown'/>
    <string_constant name='kIOPSFailureOpenThermistor' value='Open Thermistor'/>
    <string_constant name='kIOPSFailurePeriodicAFEComms' value='Periodic AFE Comms'/>
    <string_constant name='kIOPSFailurePermanentAFEComms' value='Permanent AFE Comms'/>
    <string_constant name='kIOPSFailureSafetyOverVoltage' value='Safety Over-Voltage'/>
    <string_constant name='kIOPSFairValue' value='Fair'/>
    <string_constant name='kIOPSGoodValue' value='Good'/>
    <string_constant name='kIOPSHardwareSerialNumberKey' value='Hardware Serial Number'/>
    <string_constant name='kIOPSHealthConfidenceKey' value='HealthConfidence'/>
    <string_constant name='kIOPSInternalBatteryType' value='InternalBattery'/>
    <string_constant name='kIOPSInternalType' value='Internal'/>
    <string_constant name='kIOPSIsChargedKey' value='Is Charged'/>
    <string_constant name='kIOPSIsChargingKey' value='Is Charging'/>
    <string_constant name='kIOPSIsFinishingChargeKey' value='Is Finishing Charge'/>
    <string_constant name='kIOPSIsPresentKey' value='Is Present'/>
    <string_constant name='kIOPSLowWarnLevelKey' value='Low Warn Level'/>
    <string_constant name='kIOPSMaxCapacityKey' value='Max Capacity'/>
    <string_constant name='kIOPSMaxErrKey' value='MaxErr'/>
    <string_constant name='kIOPSNameKey' value='Name'/>
    <string_constant name='kIOPSNetworkTransportType' value='Ethernet'/>
    <string_constant name='kIOPSNotifyLowBattery' value='com.apple.system.powersources.lowbattery'/>
    <string_constant name='kIOPSOffLineValue' value='Off Line'/>
    <string_constant name='kIOPSPermanentFailureValue' value='Permanent Battery Failure'/>
    <string_constant name='kIOPSPoorValue' value='Poor'/>
    <string_constant name='kIOPSPowerAdapterCurrentKey' value='Current'/>
    <string_constant name='kIOPSPowerAdapterFamilyKey' value='FamilyCode'/>
    <string_constant name='kIOPSPowerAdapterIDKey' value='AdapterID'/>
    <string_constant name='kIOPSPowerAdapterRevisionKey' value='AdapterRevision'/>
    <string_constant name='kIOPSPowerAdapterSerialNumberKey' value='SerialNumber'/>
    <string_constant name='kIOPSPowerAdapterSourceKey' value='Source'/>
    <string_constant name='kIOPSPowerAdapterWattsKey' value='Watts'/>
    <string_constant name='kIOPSPowerSourceIDKey' value='Power Source ID'/>
    <string_constant name='kIOPSPowerSourceStateKey' value='Power Source State'/>
    <string_constant name='kIOPSProvidesTimeRemaining' value='Battery Provides Time Remaining' />
    <string_constant name='kIOPSSerialTransportType' value='Serial'/>
    <string_constant name='kIOPSTimeRemainingNotificationKey' value='com.apple.system.powersources.timeremaining'/>
    <string_constant name='kIOPSTimeToEmptyKey' value='Time to Empty'/>
    <string_constant name='kIOPSTimeToFullChargeKey' value='Time to Full Charge'/>
    <string_constant name='kIOPSTransportTypeKey' value='Transport Type'/>
    <string_constant name='kIOPSTypeKey' value='Type'/>
    <string_constant name='kIOPSUPSManagementClaimed' value='/IOKit/UPSPowerManagementClaimed'/>
    <string_constant name='kIOPSUPSType' value='UPS'/>
    <string_constant name='kIOPSUSBTransportType' value='USB'/>
    <string_constant name='kIOPSVendorDataKey' value='Vendor Specific Data'/>
    <string_constant name='kIOPSVoltageKey' value='Voltage'/>
    <string_constant name="kIOPMUPSPowerKey" value="UPS Power" />
    <string_constant name="kIOPMBatteryPowerKey" value="Battery Power" />
    <string_constant name="kIOPMACPowerKey" value="AC Power" />
    <enum name='kIOPSLowBatteryWarningEarly' value='2'/>
    <enum name='kIOPSLowBatteryWarningFinal' value='3'/>
    <enum name='kIOPSLowBatteryWarningNone' value='1'/>
    <enum name='kIOPSTimeRemainingUnknown' value='-1'/>
    <enum name='kIOPSTimeRemainingUnlimited' value='-2'/>
    <function name='IOPSCopyExternalPowerAdapterDetails'>
        <retval type='^{__CFDictionary=}' already_retained='true'/>
    </function>
    <function name='IOPSCopyPowerSourcesInfo'>
        <retval type='@' already_retained='true'/>
    </function>
    <function name='IOPSCopyPowerSourcesList'>
        <arg type='@'/>
        <retval type='^{__CFArray=}' already_retained='true'/>
    </function>
    <function name='IOPSGetBatteryWarningLevel'>
        <retval type='i'/>
    </function>
    <function name='IOPSGetPowerSourceDescription'>
        <arg type='@'/>
        <arg type='@'/>
        <retval type='^{__CFDictionary=}'/>
    </function>
    <function name='IOPSGetProvidingPowerSourceType'>
        <arg type='@'/>
        <retval type='^{__CFString=}'/>
    </function>
    <function name='IOPSGetTimeRemainingEstimate'>
        <retval type='d'/>
    </function>
    <function name='IOPSNotificationCreateRunLoopSource'>
        <arg type='^?' function_pointer='true'>
            <arg type='^v'/>
            <retval type='v'/>
        </arg>
        <arg type='^v'/>
        <retval type='^{__CFRunLoopSource=}' already_retained='true'/>
    </function>
</signatures>"""

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 = objc.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

# coding=utf-8 Implements PowerManagement functions using IOPowerSources. Requires Mac OS X 10.6+ See doc/darwin for platform-specific details. # 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 <?xml version='1.0'?> <!DOCTYPE signatures SYSTEM "file://localhost/System/Library/DTDs/BridgeSupport.dtd"> <signatures version='1.0'>     <depends_on path="/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" />     <string_constant name='kIOPSACPowerValue' value='AC Power'/>     <string_constant name='kIOPSBatteryFailureModesKey' value='BatteryFailureModes'/>     <string_constant name='kIOPSBatteryHealthConditionKey' value='BatteryHealthCondition'/>     <string_constant name='kIOPSBatteryHealthKey' value='BatteryHealth'/>     <string_constant name='kIOPSBatteryPowerValue' value='Battery Power'/>     <string_constant name='kIOPSCheckBatteryValue' value='Check Battery'/>     <string_constant name='kIOPSCommandDelayedRemovePowerKey' value='Delayed Remove Power'/>     <string_constant name='kIOPSCommandEnableAudibleAlarmKey' value='Enable Audible Alarm'/>     <string_constant name='kIOPSCommandStartupDelayKey' value='Startup Delay'/>     <string_constant name='kIOPSCurrentCapacityKey' value='Current Capacity'/>     <string_constant name='kIOPSCurrentKey' value='Current'/>     <string_constant name='kIOPSDeadWarnLevelKey' value='Shutdown Level'/>     <string_constant name='kIOPSDesignCapacityKey' value='DesignCapacity'/>     <string_constant name='kIOPSDesignCycleCountKey' value='DesignCycleCount' />     <string_constant name='kIOPSDynamicStorePath' value='/IOKit/PowerSources'/>     <string_constant name='kIOPSFailureCellImbalance' value='Cell Imbalance'/>     <string_constant name='kIOPSFailureChargeFET' value='Charge FET'/>     <string_constant name='kIOPSFailureChargeOverCurrent' value='Charge Over-Current'/>     <string_constant name='kIOPSFailureChargeOverTemp' value='Charge Over-Temperature'/>     <string_constant name='kIOPSFailureDataFlushFault' value='Data Flush Fault'/>     <string_constant name='kIOPSFailureDischargeFET' value='Discharge FET'/>     <string_constant name='kIOPSFailureDischargeOverCurrent' value='Discharge Over-Current'/>     <string_constant name='kIOPSFailureDischargeOverTemp' value='Discharge Over-Temperature'/>     <string_constant name='kIOPSFailureExternalInput' value='Externally Indicated Failure'/>     <string_constant name='kIOPSFailureFuseBlown' value='Fuse Blown'/>     <string_constant name='kIOPSFailureOpenThermistor' value='Open Thermistor'/>     <string_constant name='kIOPSFailurePeriodicAFEComms' value='Periodic AFE Comms'/>     <string_constant name='kIOPSFailurePermanentAFEComms' value='Permanent AFE Comms'/>     <string_constant name='kIOPSFailureSafetyOverVoltage' value='Safety Over-Voltage'/>     <string_constant name='kIOPSFairValue' value='Fair'/>     <string_constant name='kIOPSGoodValue' value='Good'/>     <string_constant name='kIOPSHardwareSerialNumberKey' value='Hardware Serial Number'/>     <string_constant name='kIOPSHealthConfidenceKey' value='HealthConfidence'/>     <string_constant name='kIOPSInternalBatteryType' value='InternalBattery'/>     <string_constant name='kIOPSInternalType' value='Internal'/>     <string_constant name='kIOPSIsChargedKey' value='Is Charged'/>     <string_constant name='kIOPSIsChargingKey' value='Is Charging'/>     <string_constant name='kIOPSIsFinishingChargeKey' value='Is Finishing Charge'/>     <string_constant name='kIOPSIsPresentKey' value='Is Present'/>     <string_constant name='kIOPSLowWarnLevelKey' value='Low Warn Level'/>     <string_constant name='kIOPSMaxCapacityKey' value='Max Capacity'/>     <string_constant name='kIOPSMaxErrKey' value='MaxErr'/>     <string_constant name='kIOPSNameKey' value='Name'/>     <string_constant name='kIOPSNetworkTransportType' value='Ethernet'/>     <string_constant name='kIOPSNotifyLowBattery' value='com.apple.system.powersources.lowbattery'/>     <string_constant name='kIOPSOffLineValue' value='Off Line'/>     <string_constant name='kIOPSPermanentFailureValue' value='Permanent Battery Failure'/>     <string_constant name='kIOPSPoorValue' value='Poor'/>     <string_constant name='kIOPSPowerAdapterCurrentKey' value='Current'/>     <string_constant name='kIOPSPowerAdapterFamilyKey' value='FamilyCode'/>     <string_constant name='kIOPSPowerAdapterIDKey' value='AdapterID'/>     <string_constant name='kIOPSPowerAdapterRevisionKey' value='AdapterRevision'/>     <string_constant name='kIOPSPowerAdapterSerialNumberKey' value='SerialNumber'/>     <string_constant name='kIOPSPowerAdapterSourceKey' value='Source'/>     <string_constant name='kIOPSPowerAdapterWattsKey' value='Watts'/>     <string_constant name='kIOPSPowerSourceIDKey' value='Power Source ID'/>     <string_constant name='kIOPSPowerSourceStateKey' value='Power Source State'/>     <string_constant name='kIOPSProvidesTimeRemaining' value='Battery Provides Time Remaining' />     <string_constant name='kIOPSSerialTransportType' value='Serial'/>     <string_constant name='kIOPSTimeRemainingNotificationKey' value='com.apple.system.powersources.timeremaining'/>     <string_constant name='kIOPSTimeToEmptyKey' value='Time to Empty'/>     <string_constant name='kIOPSTimeToFullChargeKey' value='Time to Full Charge'/>     <string_constant name='kIOPSTransportTypeKey' value='Transport Type'/>     <string_constant name='kIOPSTypeKey' value='Type'/>     <string_constant name='kIOPSUPSManagementClaimed' value='/IOKit/UPSPowerManagementClaimed'/>     <string_constant name='kIOPSUPSType' value='UPS'/>     <string_constant name='kIOPSUSBTransportType' value='USB'/>     <string_constant name='kIOPSVendorDataKey' value='Vendor Specific Data'/>     <string_constant name='kIOPSVoltageKey' value='Voltage'/>     <string_constant name="kIOPMUPSPowerKey" value="UPS Power" />     <string_constant name="kIOPMBatteryPowerKey" value="Battery Power" />     <string_constant name="kIOPMACPowerKey" value="AC Power" />     <enum name='kIOPSLowBatteryWarningEarly' value='2'/>     <enum name='kIOPSLowBatteryWarningFinal' value='3'/>     <enum name='kIOPSLowBatteryWarningNone' value='1'/>     <enum name='kIOPSTimeRemainingUnknown' value='-1'/>     <enum name='kIOPSTimeRemainingUnlimited' value='-2'/>     <function name='IOPSCopyExternalPowerAdapterDetails'>         <retval type='^{__CFDictionary=}' already_retained='true'/>     </function>     <function name='IOPSCopyPowerSourcesInfo'>         <retval type='@' already_retained='true'/>     </function>     <function name='IOPSCopyPowerSourcesList'>         <arg type='@'/>         <retval type='^{__CFArray=}' already_retained='true'/>     </function>     <function name='IOPSGetBatteryWarningLevel'>         <retval type='i'/>     </function>     <function name='IOPSGetPowerSourceDescription'>         <arg type='@'/>         <arg type='@'/>         <retval type='^{__CFDictionary=}'/>     </function>     <function name='IOPSGetProvidingPowerSourceType'>         <arg type='@'/>         <retval type='^{__CFString=}'/>     </function>     <function name='IOPSGetTimeRemainingEstimate'>         <retval type='d'/>     </function>     <function name='IOPSNotificationCreateRunLoopSource'>         <arg type='^?' function_pointer='true'>             <arg type='^v'/>             <retval type='v'/>         </arg>         <arg type='^v'/>         <retval type='^{__CFRunLoopSource=}' already_retained='true'/>     </function> </signatures> 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 Spawns new NSThread to handle notifications. Stops spawned NSThread. Main method of the spawned NSThread. Registers run loop source and runs current NSRunLoop. Removes the only source from NSRunLoop and cancels thread. Adds weak ref to an observer.          @param observer: Instance of class that implements on_power_source_notification() Removes an observer.          @param observer: Previously added observer @param cf_run_loop: If provided, all notifications are posted within this loop Called in response to IOPSNotificationCreateRunLoopSource() event. Uses IOPSCopyPowerSourcesInfo and IOPSGetProvidingPowerSourceType to get providing power source type. Uses IOPSGetBatteryWarningLevel to get battery warning level. 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. # Mac OS X 10.7+ # Mac OS X 10.6 Spawns thread or adds IOPSNotificationCreateRunLoopSource directly to provided cf_run_loop         @see: __init__ Stops thread and invalidates source.