Sunday, March 20, 2011

OSX, Login Screens, and Battery Information

I know it's probably weird, but I really don't like it when I boot up my Macbook and don't know if I should plug it in or not.  Sometimes, if it's very low on battery power, I'll just turn it back off again and get on my PC (I do a lot of coding from the couch :).

So, I asked myself, why not try and find a way to dynamically update my login screen with battery information?  There are a number of posts for updating the login text.  I took this basic idea and combined it with a Python script I wrote for parsing out the battery status.

Some of the content in the battery script was taken from a recent post on Lifehacker, showing how you can build a script which will vocally alert you when your battery status is getting critical.

Anyway, the first step was to build a script (I prefer Python) which will gather battery stats from ioreg and parse it for the current battery capacity, maximum battery capacity, and external charge capability.  The last option (yes/no) lets us know if the laptop is plugged into the wall.

Here's the script I came up with

#!/usr/bin/python
import subprocess
import os, sys

BATTERY_CMD = ["/usr/sbin/ioreg", "-l"]
GREP_CMD    = ["/usr/bin/egrep", "Capacity|ExternalChargeCapable"]

def main(argv):
    content      = getBatteryStatus()
    print(content)

    return 0

def getBatteryStatus():
    process = subprocess.Popen(BATTERY_CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    grep    = subprocess.Popen(GREP_CMD, stdin=process.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    grep.wait()
    output  = grep.communicate()[0]
    batteryStatus = output.split("\n")
    if len(batteryStatus) < 3:
        print(batteryStatus)
        return "Could not get battery status"
   
    if "Yes" in batteryStatus[0]:
        return "Laptop is plugged into the wall"
    else:
        maxCapacity = float(batteryStatus[1].split("=")[1].lstrip())
        curCapacity = float(batteryStatus[2].split("=")[1].lstrip())
        remaining   = 100 * (curCapacity / maxCapacity)
        return "Battery Remaining: %.02f%s" % (remaining, "%")

if __name__ == "__main__":
    sys.exit(main(sys.argv))


 This script basically runs the following command: ioreg -l | egrep Capacity|ExternalChargeCapable.  This bundles the content into a large string, delimited by "\n" characters.  Parsing this is a simple value of extracting everything after the "=" sign.

Anyway, this script prints either the percentage or the status of the wall plug.

The second part of this exercise was to create a StartupItem for my Macbook.  This was actually very easy (as the upper link shows).  Create a new directory in "/Library/StartupItems" called logintext.  Inside this directory, you'll need to copy the above script, and add 2 new scripts:

Call the first one, StartupParameters.plist, and add the following content:

{
Description = "Sets LoginwindowText";
Provides = ("logintext");
}


This script informs the startup process of which script to execute when starting up. This guarantees all initializations of the Macbook will have up-to-date knowledge of the battery life.

The last script is called "logintext". In this script, put the following content:

#!/bin/sh
batteryText=`/Library/StartupItems/logintext/batteryScript.py`
defaults write /Library/Preferences/com.apple.loginwindow LoginwindowText "$batteryText"


This script executes the Python script and sets the loginwindow's preference (LoginwindowText) to the output from the script.

Log in as root (sudo su -) and add a new cronjob to execute this script every 2 minutes:

*/2 * * * * /Library/StartupItems/logintext/logintext


And it should keep your system relatively up-to-date!