Friday, May 02, 2008

Bluetooth Proximity Monitor (improved)

About a month ago, a friend of mine pointed out a bluetooth proximity program for windows which would lock your desktop when you walked away by polling your mobile phone's proximity to your computer. I thought that sounded fun to try as I have a bluetooth enabled phone and laptop, however, I'm running Linux, not Windows. After a quick search on Google I found a Bluetooth Proximity Monitor script for Linux. It worked pretty well as it was, but I've made a few adjustments to it to improve it for my purposes.


  1. The original script is written for KDE or other window managers that use xscreensaver. Since I'm currently running in Gnome, I had to switch the commands to use gnome-screensaver instead.

  2. While changing the screensaver commands, I also added the ability to toggle my instant messenger (Pidgin) from 'available' to 'away'

  3. The original script has a single THRESHOLD value to toggle between being near and far. I decided I needed separate NEAR and FAR thresholds. This is due to the wide variance of proximity I can have while I'm sitting at my desk. Just turning in my chair so that my body was between the phone and the laptop could change my RSSI (proximity value) from -1 to -18, so I need a fairly high threshold to prevent that. On the other hand, setting the threshold high could allow my system to unlock when I'm still 30 feet away. So making separate thresholds allows the proximity monitor not to trigger just because I turned in my chair, but also not unlock until I've actually returned to my desk.

  4. With the higher away threshold, it's possible (though uncommon) to totally leave the bluetooth range before it triggers that you've gone away. So, I also added a little logic to trigger the change in proximity if you were previously in near proximity, but now your bluetooth can no longer be pinged (out of range, turned off, etc).

  5. Finally, I alter the proximity check interval depending on if you're near or far. The motivation for this was an attempt to reduce power demands a little in order to prolong the battery life. I haven't done any actual tests to determine if it helped or not. Basically, if you're at your desk, it only checks every 5 seconds to make sure you're still there. If you've walked away it switches to check every 2 seconds in order to be more responsive to when you return.



In the end, my version of the bluetooth proximity monitor script looks like:

#!/bin/sh

DEVICE="00:1A:8A:61:6C:FE"
CHECK_INTERVAL=5
NEAR_THRESHOLD=-1
FAR_THRESHOLD=-22
PID=0
START_CMD='/usr/bin/gnome-screensaver'
FAR_CMD='/usr/bin/gnome-screensaver-command -l && purple-remote setstatus?status=away '
NEAR_CMD='/usr/bin/gnome-screensaver-command -d && purple-remote setstatus?status=available'
HCITOOL="/usr/bin/hcitool"
DEBUG="/var/log/btproximity.log"

connected=0

function msg {
echo "$1" >> $DEBUG
}

function msgn {
echo -n "$1" >> $DEBUG
}

function check_connection {
connected=0;
found=0
for s in `$HCITOOL con`; do
if [[ "$s" == "$DEVICE" ]]; then
found=1;
fi
done
if [[ $found == 1 ]]; then
connected=1;
else
# msgn 'Attempting connection...'
if [ -z "`$HCITOOL cc $DEVICE 2>&1`" ]; then
# msg 'Connected.'
connected=1;
else
msg "ERROR: Could not connect to device $DEVICE."
fi
fi
}

function check_xscreensaver {
PID=`ps -C gnome-screensaver --no-heading | awk '{ print $1 }'`
if [ "$PID" == "" ]; then
$START_CMD &
fi
}

check_connection

while [[ $connected -eq 0 ]]; do
check_connection
sleep 1
done

name=`$HCITOOL name $DEVICE`
msg "Monitoring proximity of \"$name\" [$DEVICE]";

state="near"
while /bin/true; do

check_xscreensaver
check_connection

if [[ $connected -eq 1 ]]; then
rssi=`$HCITOOL rssi $DEVICE | sed -e 's/RSSI return value: //g'`

if (( $rssi <= $FAR_THRESHOLD )); then
if [[ "$state" == "near" ]]; then
msg "*** Device \"$name\" [$DEVICE] has left proximity (signal: $rssi)"
state="far"
$FAR_CMD > /dev/null 2>&1
CHECK_INTERVAL=2
fi
elif (( $rssi >= $NEAR_THRESHOLD )); then
if [[ "$state" == "far" ]]; then
msg "*** Device \"$name\" [$DEVICE] is within proximity (signal: $rssi)"
state="near"
$NEAR_CMD > /dev/null 2>&1
$START_CMD &
CHECK_INTERVAL=5
fi
fi
# msgn "RSSI = $rssi, "
elif [[ "$state" == "near" ]]; then
msg "*** Device \"$name\" [$DEVICE] is no longer detectable"
state="far"
$FAR_CMD > /dev/null 2>&1
CHECK_INTERVAL=2
fi
# msg "state = $state, PID = $PID, sleep = $CHECK_INTERVAL"

sleep $CHECK_INTERVAL
done

1 comment:

Anonymous said...

Hi! Thanks for sharing. A quick note: you should change #!/bin/sh to bash I think - my Ubuntu (which uses dash for /bin/sh) machine complaints about the use of "function". Changing to bash solves that.