Benden.us
📓

OS X Power Management: No Sleep HOWTO


Beginning with Mountain Lion (10.6), Apple has introduced aggressive power management technology to their operating system. This technology helps the system to power-down even when applications are running and consuming CPU, disk, and other resources. While this is normally an acceptable behavior there are edge-cases in which this becomes a problem.

So how do we solve this problem? We use IOPMAssertion family of functions to solve this problem (Thread at StackOverflow.com.)

Example C source code may be found here in QA1340 in Apple’s Developer documentation, under listing 2.

Example Python source code may be found below:

#!/usr/bin/python2.7

import ctypes
import CoreFoundation
import objc
import subprocess
import time

def SetUpIOFramework():
  # load the IOKit library
  framework = ctypes.cdll.LoadLibrary(
    '/System/Library/Frameworks/IOKit.framework/IOKit')

  # declare parameters as described in IOPMLib.h
  framework.IOPMAssertionCreateWithName.argtypes = [
    ctypes.c_void_p,  # CFStringRef
    ctypes.c_uint32,  # IOPMAssertionLevel
    ctypes.c_void_p,  # CFStringRef
    ctypes.POINTER(ctypes.c_uint32)]  # IOPMAssertionID
  framework.IOPMAssertionRelease.argtypes = [
    ctypes.c_uint32]  # IOPMAssertionID
  return framework

def StringToCFString(string):
  # we'll need to convert our strings before use
  return objc.pyobjc_id(
    CoreFoundation.CFStringCreateWithCString(None, string,
      CoreFoundation.kCFStringEncodingASCII).nsstring())

def AssertionCreateWithName(framework, a_type,
                            a_level, a_reason):
  # this method will create an assertion using the IOKit library
  # several parameters
  a_id = ctypes.c_uint32(0)
  a_type = StringToCFString(a_type)
  a_reason = StringToCFString(a_reason)
  a_error = framework.IOPMAssertionCreateWithName(
    a_type, a_level, a_reason, ctypes.byref(a_id))

  # we get back a 0 or stderr, along with a unique c_uint
  # representing the assertion ID so we can release it later
  return a_error, a_id

def AssertionRelease(framework, assertion_id):
  # releasing the assertion is easy, and also returns a 0 on
  # success, or stderr otherwise
  return framework.IOPMAssertionRelease(assertion_id)

def main():
  # let's create a no idle assertion for 30 seconds
  no_idle = 'NoIdleSleepAssertion'
  reason = 'Test of Pythonic power assertions'

  # first, we'll need the IOKit framework
  framework = SetUpIOFramework()

  # next, create the assertion and save the ID!
  ret, a_id = AssertionCreateWithName(framework, no_idle, 255, reason)
  print '\n\nCreating power assertion: status %s, id %s\n\n' % (ret, a_id)

  # subprocess a call to pmset to verify the assertion worked
  subprocess.call(['pmset', '-g', 'assertions'])
  time.sleep(5)

  # finally, release the assertion of the ID we saved earlier
  AssertionRelease(framework, a_id)
  print '\n\nReleasing power assertion: id %s\n\n' % a_id

  # verify the assertion has been removed
  subprocess.call(['pmset', '-g', 'assertions'])

if __name__ == '__main__':
  main()

Tip: Use a wrapper Python script to call out to programs that do not support power management and cannot easily be made to support it. (A C wrapper program would be acceptable too. ;)

Information about calling native C/Objective-C functions from Python are explained on StackOverflow.com.

Node.js

Update for Aug 17, 2014: I have created a Node.js module to control power management on OS X. The project is located on GitHub.com.

comments powered by Disqus