Mac OS X – report time on battery since last AC unplug but exclude sleep time

batterysleep-wake

Is there an app or existing command to report the battery time since the laptop was last unplugged from AC but exlude the time spend during sleep on battery?

This can be inferred from pmset -g log so I'm looking for a way to do this without using an app that stays resident to detect plug/unplug events.

Best Answer

Well, I'll provide an answer to my own question, tested on Yosemite but not extensively. This is bash code:

#!/bin/bash
humantime () { printf -v $1 "%dh%02dm%02ds" $(($2/3600)) $((($2/60)%60)) $(($2%60)); }
#unplugged=`pmset -g log | grep 'Summary- \[System: DeclUser.*\] Using Batt' | tail -1 | cut -f1,2 -d' '`
unplugged=`syslog -u -k Sender loginwindow | grep 'magsafeStateChanged state changed old 1 new 2' | tail -n 1 | cut -f1 -dZ`
onbatt=$((`date +%s` - `date -j -f "%Y-%m-%d %H:%M:%S" "$unplugged" +%s`))
slept=`pmset -g log | sed -n "/^$unplugged/,//p" | awk '/Entering Sleep.*[0-9]+ secs/ {n=split($0,a," ");sum+=a[n-1]} END {print sum}'`
[[ -z $slept ]] && slept=0
awake=$((onbatt-slept))
humantime hawake $awake
humantime hslept $slept
printf "Awake on battery for $hawake (%.1f%%)\n" `echo "$awake*100/$onbatt" | bc -l`
printf "Slept on battery for $hslept (%.1f%%)\n" `echo "$slept*100/$onbatt" | bc -l`

I won't vouch for its accuracy but it seems to work. Since I don't know Apple's internals (I'm new to Mac OS X) I reversed engineered it with a few trial and errors plugging and unplugging the adapter, both while asleep and also while awake, to see which log entries are reliable.

You will notice that I tried using pmset -g log to detect when the power was unplugged but it's unreliable. Upon unplugging, the log entry is Summary- [System: DeclUser kDisp] Using Batt but unfortunately the same log entry occurs during sleep while unplugged so it's unreliable. I resorted to syslog instead.

NOTE: This reports the time since the last unplugged event. This may not be when the battery was 100 %.

Potential issue: The above code takes the timestamp from syslog and matches against pmset. If pmset doesn't have an entry (any entry) at the same timestamp then it won't work. That shouldn't happen but if it does, then you could convert the timestamps to epoch seconds and detecting the closest timestamp in pmset that way.