- Writing a power consumption applet
- Using Bonobo (sorry)
- Very quick intro to Python
- Reading the battery info from a pseudofile interface
- Writing a function to calculate the power
So that I can see the power consumption of my shiny new netbook second by second, I’m going to write a Gnome panel applet which will display it constantly.
Now, this may not work in Unity, the new desktop environment for Ubuntu, but I’m not using it (yet) and a lot of people aren’t happy with it, so I’m ignoring it.
First, a confession. This applet uses Bonobo, which is deprecated. It’s also, it seems, currently the only way to write Python applets using standard repository packages, until the new GObject Introspection API is stable. When that happens, I’ll write a post porting this code.
Another alternative might be a rewrite in C, using this version as a prototype – and that’s something I’m seriously considering.
I’m going to use Python – it’s a good language for doing this sort of task, with lots of helpful text processing stuff and not too many barriers to getting something up quickly. For me, Python sits neatly between a scripting language and a software engineer’s language. If you don’t know Python, hopefully you’ll at least be able to follow along, but I’ll get you started by quickly introducing the oddest thing about the language.
Python doesn’t use curly brackets like Java and C to delineate blocks of code. Instead, it uses a colon on the line before, and an increase in indentation. When the block is finished, the indent decreases again. For example:
def foo(a,b): if a>b: print "A is greater than B" print "so I'll call bar()" bar(a,b) else: print "Other way round, so I'll call baz()" baz(a,b) def anotherfunction(): ...
You see that there’s no keyword or symbol saying we’re at the end of an “if” statement, or even at the end of a function definition – it’s all in the indenting.
First we need to write the code to get the power drain in watts, and just print it on the console. We’ll write this as a function called
getpower() which returns a floating point value.
To do this, we’re going to need to look at the battery state, which is in a special file called
This isn’t actually a file – it’s a pseudofile. It looks like a file, but reading it runs some special code which outputs some text describing the state of the battery. This sort of thing happens a lot in UNIX-style operating systems – the pattern is called “Everything is a File.” If you look around, you’ll find pseudofiles which represent devices (in
/dev), files which represent processes (in the top level of
/proc) and many files which represent system state (such as our battery file.)
Note: Your machine may use a different name for the battery – please check. If I were writing this properly, I’d probably make the code find the battery file itself by scanning the directory, and I’d have to make it deal with multiple batteries.
The contents of this fake file look something like this:
present: yes capacity state: ok charging state: discharging present rate: 773 mA remaining capacity: 3145 mAh present voltage: 12067 mV
We’re going to need to
- extract the “present rate” and “present voltage” values
- convert them from milliamps and millivolts into amps and volts
- and multiply them together to give the current power drain in watts
Luckily this is very easy in Python!
We start by opening the battery state file for reading:
def getpower(): f = open('/proc/acpi/battery/BAT0/state')
If you’re not familiar with Python,
def is used to define a new function, which in this case takes no parameter. Blocks of code are marked by a colon, a new line, and a change of indentation – this is the weirdest thing about Python to a (say) Java or C programmer.
Now we scan the file line by line, using the
foreach construct, which repeats a block of code for each element of a list, and the file method
readlines() which returns a list of lines in a file:
for x in f.readlines():
First we see if the current line has “present rate” in it. If it has, we split the line into two strings – the part before the colon, and the part after. Here we make use of the handy ability of Python to return lists of things, making it look like we’re returning two values:
if(x.find("present rate")>=0): dummy,data = x.split(':')
We throw away the string before the colon, and just use
data. This string looks something like this: ” 654 mA”
We need to remove the leading space, and split the “654 mA” by the space in the middle, giving us just the “654″ part (or whatever the amperage actually is.) We can do this by chaining a couple of methods together:
ma,dummy = data.strip().split()
strip() returns a string without leading or trailing spaces, and the
split() will split that string into two using the whitespace, leaving “654″ in
ma and the “mA” in
dummy. Finally, we need to turn this string into a floating point number:
amps = float(ma) / 1000.0
We now have the current in amps, as a floating point value.
We do exactly the same thing to get the volts:
if x.find("present voltage")>=0: dummy,data = x.split(':') mv,dummy = data.strip().split() volts = float(mv) / 1000.0
We scan the whole file, doing these two checks on every line. Finally, we get to the end of the loop. We can now close the file, and return our power drain – calculated by multiplying the current and the voltage:
f.close() return amps*volts
And that’s that. If we put this altogether in a test program, we can print out the current:
#!/bin/python def getpower(): f = open('/proc/acpi/battery/BAT0/state') for x in f.readlines(): if(x.find("present rate")>=0): dummy,data = x.split(':') ma,dummy = data.strip().split() amps = float(ma) / 1000.0 if x.find("present voltage")>=0: dummy,data = x.split(':') mv,dummy = data.strip().split() volts = float(mv) / 1000.0 f.close() return amps*volts print getpower()
That function is the core of our applet, which we can now build around it.
Next, we’ll start looking at building the applet itself.