Standalone power management in Linux

I use the wonderful i3 window manager. Since I run it on my laptop, I need some sort of power management: suspend when lid is closed, adjust screen brightness and CPU performance when running on battery power, etc. My first thought was to use gnome-power-manager, but that seems to have disappeared in Fedora 17. I then tried xfce4-power-manager (yum install xfce4-power-manager, then start by running xfce4-power-manager and then xfce4-power-manager-settings to configure), which mostly of worked: I could get it to suspend and adjust brightness, but suspend-and-LOCK wouldn’t work for whatever reason.

So I decided to do it a different, and more customizable way. (Most of the following is probably a waste of time if you’re using something like Unity, Gnome, KDE, Xfce, etc., which all take care of these things for you.)

ACPI events for suspend

Let’s start with suspending and locking. First, install acpid if you don’t already have it and start it (I’m using Fedora here, but the instructions should be pretty much the same for Ubuntu):

yum install acpid
acpid

Now we can listen to APCI events and react to them. To check that it works, run acpi_listen and try things like adjusting screen brightness and closing/opening the lid. This should generate events like this:

$ acpi_listen
video/brightnessdown BRTDN 00000087 00000000
video/brightnessup BRTUP 00000086 00000000
video/brightnessup BRTUP 00000086 00000000
button/lid LID close
button/lid LID open

Next, in /etc/acpi/events, I created the file lidbtn (you can call it anything you want) with the following content:

event=button[ /]lid
action=/etc/acpi/lid.sh

The above simply specifies that on any button/lid event, /etc/acpi/lid.sh should be executed. In /etc/acpi/lid.sh, I have the following:

#!/bin/sh
    
grep -q closed /proc/acpi/button/lid/*/state
if [ $? = 0 ]; then
  /usr/local/sbin/physlock -d -u fooninja
  pm-suspend
fi

If the lid is closed, physlock and pm-suspend will be executed. physlock is a neat, lightweight locking tool, written because vlock had issues with suspend/hibernate.

Performance settings depending on AC/battery power

To adjust things depending on whether the laptop is running on AC or battery, I created /etc/pm/power.d/performance with the following. Note that for Ubuntu, at least in 12.04.1, change cpupower -c $cpu frequency-set to cpufreq-set -c $cpu—and don’t forget to install cpufrequtils first.

#!/bin/sh

if [ "$1" = "true" ]; then
    # Battery
    # Normal-(ish?) power management for HDD
    hdparm -B 192 /dev/sda
    # Turn down brightness
    echo 10 > /sys/class/backlight/acpi_video0/brightness
    # 60 seconds before X goes black
    xset dpms 0 0 60
    # Slower CPU performance (less power usage)
    for cpu in 0 1 2 3; do cpupower -c $cpu frequency-set -g powersave; done
else
    # AC
    # Least aggressive power management, keeps Load_Cycle_Count from increasing too much
    hdparm -B 254 /dev/sda
    # Turn up brightness
    echo 13 > /sys/class/backlight/acpi_video0/brightness
    # 600 seconds before X goes black
    xset dpms 0 0 600
    # Maximum CPU performance
    for cpu in 0 1 2 3; do cpupower -c $cpu frequency-set -g performance; done
fi

Don’t copy the above code blindly (unless you happen to have an X220, and the same preferences…)! And don’t forget to make it executable: chmod +x /etc/pm/power.d/performance.

Screen brightness

Next step was getting the laptop’s hotkeys for adjusting brightness to work. I could use ACPI events (video/brightnessdown, video/brightnessup) to trigger something, or just bind the X keycodes. I opted for the latter.

You should be able to set brightness manually, with e.g. echo 10 > /sys/class/backlight/acpi_video0/brightness. To make life easier I installed xbacklight, which lets you set or increase/decrease by a specificed amount.

Next, I used xev to grab the keycodes for the brightness up/down buttons and put the following in my ~/.i3/config:

bindcode 232 exec xbacklight -dec 10
bindcode 233 exec xbacklight -inc 10

Sound

To get the hotkeys for sound to work, I again used xev to get the keycodes, and then bound them to amixer commands:

bindcode 121 exec amixer set Master toggle -q
bindcode 122 exec amixer set Master 1%- unmute -q
bindcode 123 exec amixer set Master 1%+ unmute -q