Sunday, April 22, 2012

Windows Power Options, meet Python

According to Mac users, the world is divided into two main groups: those who use Macs and everyone else.

Not much of a fan of Apple’s products, but even I can’t deny they’re doing a great job with keeping a high uptime laptops using a precise power consumption control. Using power options built into drivers, with an operating system built from ground-up optimized for long use and with breakthrough battery technology, Macs today have the best energy efficient systems out there.

Although Windows lack a high level of power consumption, it offers a good infrastructure for developers.

My initial approach was to allow Windows to recognize some of the uses I make of my computer, and adapt itself accordingly.

There are two ways to accomplish this:

  1. Monitor processes via Windows Task Manager. If a known process was detected, act accordingly and change the power scheme.
  2. Monitor the currently active window by interacting with Windows GUI. If a known process was detected, act accordingly and change the power scheme.

Method 1

Advantages:

  1. See the big picture, If a user minimize the window for several seconds you’ll still be able to determine what’s running in the background.

Disadvantages:

  1. If a user minimize a known process, the code needs to be adjusted to detect the process is not in use.
  2. Additional coding is required to avoid conflicts between processes. If a user minimizes two known processes, the code should adjust itself.
  3. To efficiently scan the process list, the code should either enumerate every process which can take up the very same resources we’re trying to save, or query only for known processes, which can take up some memory maintaining such process list.

Method 2

Advantages:

  1. The code is always relevant to what the user is currently doing
  2. No need to enumerate the entire process list, only fetching the active window from the GUI using Windows API.

Disadvantages:

  1. In case a user minimizes a program, it can greatly affect its performance.
  2. Since the code doesn’t know the user intention, and since the user can minimize a program just for a couple of seconds, it is possible that switching several power schemes in a short time may affect your hardware in the long run.

Combining method 2 with a short grace period between fetching the active window, can eliminate disadvantage #2. Assuming enough power schemes will be created, it is possible to avoid disadvantage #1.

Let’s get busy
  1: #-------------------------------------------------------------------------
  2: # Name:        ActiveWindowSniffer
  3: # Author:      at0m
  4: # Created:     22/04/2012
  5: # Copyright:   (c) at0m 2012
  6: #-------------------------------------------------------------------------
  7: 
  8: #!/usr/bin/env python
  9: 
 10: import win32gui
 11: from time import sleep
 12: from subprocess import call
 13: from re import findall
 14: 
 15: w=win32gui
 16: powerschemes = {"Balanced":"381b4222-f694-41f0-9685-ff5bb260df2e",
 17:                 "Adobe Audition": "7a601a49-8d15-46d2-8160-55163f627070",
 18:                 "Internet":"8b12c7c4-a5e9-4e27-9283-338dc2138c2e",
 19:                 "Internet-Media":"b0e87387-1585-4351-894c-267adb53e563",
 20:                 "High Performance":"8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c",
 21:                 "Power Saver":"a1841308-3541-4fab-bc81-f71556f20b4a",
 22:                 "Balanced_TB_ON":"b18511ef-e34a-4f4b-85e7-2ea5cd1ac989",
 23:                 "Shuteye":"d7925c9e-bfc0-47d7-98c3-028edd2acb81",
 24:                 "Reading":"7c2951ff-7d0e-4609-9f68-d6428869cf5e",
 25:                 "Video Optimized":"e21061ef-9975-4822-9af5-6215c973a982"}
 26: 
 27: while True:
 28:     try:
 29:         sleep(15)
 30:         active_window = w.GetWindowText (w.GetForegroundWindow())
 31:         print_string = str(active_window) + " was detected, adjusting"
 32:         print print_string
 33: 
 34:         if "Mozilla Firefox" in active_window:
 35:             if "YouTube" in active_window or "Facebook" in active_window:
 36:                 call(["POWERCFG","-SETACTIVE",powerschemes["Internet-Media"]])
 37: 
 38:             elif "application/pdf Object" in active_window:
 39:                 call(["POWERCFG","-SETACTIVE",powerschemes["Reading"]])
 40: 
 41:             else:
 42:                 call(["POWERCFG","-SETACTIVE",powerschemes["Internet"]])
 43: 
 44:         elif "Adobe Audition" in active_window or "Saving" in active_window:
 45:             call(["POWERCFG","-SETACTIVE",powerschemes["Adobe Audition"]])
 46: 
 47:         elif "Torrent" in active_window:
 48:             call(["POWERCFG","-SETACTIVE",powerschemes["Shuteye"]])
 49: 
 50:         elif findall("\.(avi|wmv|mp4|mkv|mpg|mpeg)$", active_window) != None:
 51:             call(["POWERCFG", "-SETACTIVE", powerschemes["Video Optimized"]])
 52: 
 53:         else:
 54:             call(["POWERCFG","-SETACTIVE",powerschemes["Power Saver"]])
 55: 
 56:     except KeyboardInterrupt:
 57:         raise SystemExit

At lines 10-13 we’re importing the modules we need, amongst them is win32gui which is the heart of this all program.


At line 15, we’re allocating the Windows GUI to an object.


At line 16, I’ve created a dictionary to allow me to fetch my power schemes quickly. You can see your power schemes by using launching POWERCFG –L from the command line.


At line 29, we’re adding the 15 seconds grace period, you can change it to suit your needs.


Line 30 is the heart of this program, and using the GetForegroundWindow() function, Windows will retrieve the currently active window from Windows GUI API


Line 34-55, if a pattern was detected, activate the power scheme corresponding to the pattern using POWERCFG –SETACTIVE <power_scheme_GUID>


Cheers,


at0m q[^_^]p

No comments:

Post a Comment