10.14. Laptop Power (ACPI)

I took a radically different route when I started using a laptop and a newer distribution that used ACPI instead of APM. This works well for me under Ubuntu but doesn't go directly into a prompt: instead it stores fragments of text in files that can be pulled by the prompt or applications like the wmii, dwm, or dzen2 status bars. It also makes warning noises when the computer is on battery and has a very low change.

Please note that different versions of ACPI use different applications to give you status messages, and they have different output formats - this script will not work "out of the box" for you. In fact, acpi seems to change output format at least once a year. If nothing else, you'll have to change the directory where the data is stored. As written, this script detects the use of acpitool rather than acpi (which it was written for), but then does nothing to accomodate the different output format.

Note

This code is for ideas only: it worked on my computer at one point, but will not work on yours without considerable modification. Don't cut and paste without thought: that will only lead to tears.

#!/bin/bash
#
# Do frequent checks of battery status and record to files that other apps 
# can pull from.
#
# This is run once a minute from cron.  Internally it will
# do the check twice (sleeping between) and then quit.
#
# 20060611: FC5 uses "acpitool -b" instead of "acpi -b", I'm going to
# abstract it with a test to find the executable and assign ACPICMD= ...
# Doesn't work because output format is different!  
#   While discharging:
#      Battery #1     : discharging, 10.00%, 00:30:00
#   acpitool -b | awk '{print $4}' ("discharging," w/ comma on the end)
#   $5 = 9.00%,
#   $6 = 00:24:00
#
# 20060602: Play warning sound if battery too low.

saveDir="/home/giles/bin/"
let lowBatt="3" # The percentage at which to start making noises
noiseFile="${saveDir}batteryWarning.wav"

ACFile="${saveDir}batteryAC.txt"
percentFile="${saveDir}batteryPercent.txt"
timeFile="${saveDir}batteryTime.txt"
warningFile="${saveDir}batteryWarning.txt"

if which acpitool
then
  ACPICMD=$(which acpitool) # Fedora Core 5
elif which acpi
then
  ACPICMD=$(which acpi) # Ubuntu
else
  echo "Failed to find acpi or acpitool, exiting."
  exit 1
fi

function checkCharge {
  # Track the charge against the charge from the last time it was called.
  # Every time the charge drops a percentage point, make noise.
  let percent=${1}
  if [ ${percent} -gt ${lowBatt} ]
  then
    rm $warningFile 2> /dev/null
  fi
  if [ ${percent} -le ${lowBatt} ]
  then
    if [ -f ${warningFile} ]
    then
      read oldpc < ${warningFile}
    else
      # a slight (possibly troublesome) hack:
      let oldpc=${percent}+1
    fi
    echo ${percent} > ${warningFile}
    if [ ${percent} -lt ${oldpc} ]
    then 
      cat ${noiseFile} > /dev/dsp
    fi
  fi
}

function parseACPI {
  #  Check that these are the same for you or modify the regular
  #  expressions appropriately!:
  #
  #  Format while charging:
  #    Battery 1: charging, 3%, 04:51:00 until charged
  #  Format while unplugged:
  #    Battery 1: discharging, 30%, 01:30:00 remaining
  #  Format while fully charged:
  #    Battery 1: charged, 100%

  acpiOut="$(${ACPICMD} -b)"
  #  Get power state ("charging," "discharging," "charged,")
  powerState="$(echo "${acpiOut}" | awk '{print $3}')"
  case ${powerState} in
    Charging,|charging,)
      percent=$(echo "${acpiOut}" | awk '{print $4}' | tr -d , | tr -d %)
      time=$(echo "${acpiOut}" | awk '{print $5}' | sed -e "s/\([0-9][0-9]\):\([0-9][0-9]\).*/\1h\2m/")
      time=${time##0}
      charging="^"
      ;;
    Discharging,|discharging,)
      #  returns battery percent and remaining h:m :
      percent=$(echo "${acpiOut}" | awk '{print $4}' | tr -d , | tr -d %)
      time=$(echo "${acpiOut}" | awk '{print $5}' | sed -e "s/\([0-9][0-9]\):\([0-9][0-9]\).*/\1h\2m/")
      time=${time##0}
      charging="v"
      checkCharge ${percent}
      ;;
    Full,|Charged,|charged,)
      percent="100"
      charging="^"
      time=""
      ;;
    *)
      percent="?"
      charging="?"
      time="?"
      ;;
  esac
  
  echo "${percent}" > ${percentFile}
  echo "${time}" > ${timeFile}
  echo "${charging}" > ${ACFile}

}

parseACPI
sleep 30
parseACPI