Shaun ONeil

Automating my Sony GPS-CS1

(or "Acting on mount events with launchd")

I recently rediscovered an old toy of mine, a Sony GPS-CS1. A nifty little no-fuss device which simply logs my position, every blah many seconds, until I turn it off again. It's a lot less stalker-y than it sounds; I later match up timestamps on photos with my position at the time.

The downside, is that importing these logs is nowhere near as fuss-free as the rest of the device. I have to pull the logs off the device (my mac just treats it like any other usb disk), and then convert the logs into a format that's more commonly handled by my various bits of software. Simply so that I can play with Google Earth, this ends up being two different formats. Fun stuff.

Starting the job; launchd

So to start automating this, I want a script to run when I plug the gizmo in. To achieve the 'when' part of this, I'm going to let launchd handle everything. So I create a plist, /Users/soneil/Library/LaunchAgents/com.shaunoneil.sonygps.plist, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" \
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<dict>
    <key>Label</key>
    <string>com.shaunoneil.sonygps</string>
    <key>Program</key>
    <string>/Users/soneil/Library/Scripts/sonygps</string>
    <key>ProgramArguments</key>
    <array>
        <string>sonygps</string>
        </array>
        <key>WatchPaths</key>
        <array>
        <string>/Volumes</string>
        </array>
</dict>
</plist>

This is hopefully pretty straight-forward. The fun part is the WatchPaths key, which means /Users/soneil/Library/Scripts/sonygps will be invoked when the contents of '/Volumes' changes.

The gotcha here is that I can't watch /Volumes/SONY_GPS, which is what the gizmo mounts as, as it doesn't exist when launchd starts. So sonygps is going to be started every single time a disk is inserted, and every single time a disk is removed; The script is going to need to be clever about this.

(Sidenote: That's the theory. In reality, it appears to be invoked twice when a disk is inserted, and once when it's removed.)

Getting some work done

There's really not much to this bit. Create & check for lock files, and don't do anything interesting unless you find the disk you're looking for. I've used growlnotify for feedback so I can end & detach the disk without waiting around for UI.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/bin/bash
DISK=/Volumes/SONY\ GPS
LOCK=/tmp/.sonygps.lck

PATH=$PATH:/usr/local/bin

# die if locked
if [ -f "$LOCK" ]; then
    exit 0
fi

if [ -d "$DISK" ]; then
    # lock
    touch "$LOCK"

    # make sure I exist
    mkdir -p $HOME/Library/GPS/Logs
    mkdir -p $HOME/Library/GPS/GPX
    mkdir -p $HOME/Library/GPS/KML

    # bring the contents off the disk
    # (using ~/Library because I can't promise ~/Documents is mounted!)
    rsync -aq "$DISK/GPS/" $HOME/Library/GPS/Logs/
    rm "$DISK"/GPS/*log

    # Convert nmea to useful formats
    cd $HOME/Library/GPS/Logs/
    for FILE in *log; do
        KML=$HOME/Library/GPS/KML/${FILE/log/kml}
        GPX=$HOME/Library/GPS/GPX/${FILE/log/gpx}

        if [ ! -f $KML ]; then
            gpsbabel -t -w -i nmea -f $FILE -o kml,units=m -F $KML
        fi

        if [ ! -f $GPX ]; then
            gpsbabel -t -w -i nmea -f $FILE -o gpx -F $GPX
        fi

    done

    # we're done here.
    hdiutil eject "$DISK"
    growlnotify -a /Applications/HoudahGPS.app -t "Sony GPS" -m "Import finished"

    # launchd freaks out if I die too quick/often, so sit around doing bugger
    # all to preserve launchd's sanity.
    sleep 30
    rm "$LOCK"
fi