August 17, 2009

iTunes Syncing, Part 2

In my prior posting, I set up a script to handle the syncing of my iTunes library between my home system and the carputer. The script works fine, but executing it via the Terminal is a pain. With OS X, it is possible to rename a shell script with an extension of .command, which will allow it to be double clickable in the Finder, but this unfortunately will cause Terminal to launch, show the output, and then leave the Terminal window open when the script finishes. There are a few hacks to get around this, but the most common solution I've found is to instead call the shell script via AppleScript. As usual, this has caused a few additional headaches...

The AppleScript portion is extremely straightforward. Open Script Editor, and paste in the following:


-- do our sync
do shell script "~/Documents/Scripts/sync.sh &> /dev/null &"

Save this as an Application, and we're done. The line above will execute out script, and spawn this as a separate process, meaning that AppleScript won't be waiting for the script to finish, it will kick it off, and the AppleScript will then quit.

The headache I ran into is that the pmset command wasn't quitting like it should have, the issue turned out to be that the process for that was no longer able to be found by the ps command, because the script was now being launched via AppleScript and not directly via the Terminal. The solution was to use ps -e instead, so the working script is now as follows:


#!/bin/ksh

# this command will keep the system from going to sleep, spawns a new processes
`pmset noidle` &
# get pid of the pmset process so we can kill it later
pid=`ps -e | grep [p]mset | awk '{print $1}'`

# set the log file to be placed in user's home directory
theLog=~/sync.log

SERVER=10.0.0.5
USER=username
ping -c 1 $SERVER > /dev/null 2>&1
if [ $? -ne 0 ]
then
echo `date '+%m/%d/%Y %T - '` "server unreachable" >> $theLog
else
echo `date '+%m/%d/%Y %T - '` "server found" >> $theLog
rsync -avz $USER@$SERVER:Music/iTunes/ ~/Music/iTunes >> $theLog
fi

echo `date '+%m/%d/%Y %T - '` "Done" >> $theLog

# need to kill pmset we spawned earlier so system will sleep
echo `date '+%m/%d/%Y %T - '` "killing $pid" >> $theLog
kill -9 $pid >> $theLog

# Now tell system to go to sleep since we're done
pmset sleepnow >> $theLog

Posted by Jim at 12:09 PM | TrackBack

August 12, 2009

iTunes syncing

Syncing an iTunes library between two computers isn't a straightforward task, normally for this I would simply drag the necessary files between two computers, but for the carputer project, this wasn't desirable. I need some way to keep the two systems in sync with a minimum of fuss and effort. Fortunately, there is a terminal command called rsync that is ideal for handling this task. Rsync works over an ssh connection between the two systems, and since this is running via a script, the login for this connection needs to run without asking for a password, so the use of ssh keys is required. There are a number of good tutorials on setting this up on the web, so I won't document that process here.

The script I need also needs to address a few other issues, including the system sleeping, which presented a few additional wrinkles. As things stand now, the carputer will be a laptop, and will run via the power adapter most of the time. When the car shuts off, the power adapter will be off, and the laptop will be on battery power. The Energy Saver settings are set to sleep the laptop after 1 minute. Obviously, if I'm in the middle of syncing data, I don't want the system to sleep, so the script will override this.

Finally, the script needs to check if the system we're syncing data from is reachable, and abort if it isn't. So, that being said, here is the current script I'm using:


#!/bin/ksh

# this command will keep the system from going to sleep, spawns a new processes
`pmset noidle` &
# get pid of the pmset process so we can kill it later
pid=`ps | grep [p]mset | awk '{print $1}'`


# set the log file to be placed in user's home directory
theLog=~/sync.log

SERVER=10.0.0.5
USER=username
ping -c 1 $SERVER > /dev/null 2>&1
if [ $? -ne 0 ]
then
echo `date '+%m/%d/%Y %T - '` "server unreachable" >> $theLog
exit 0
else
echo `date '+%m/%d/%Y %T - '` "server found" >> $theLog
rsync -avz $USER@$SERVER:Music/iTunes/ ~/Music/iTunes >> $theLog
fi

echo `date '+%m/%d/%Y %T - '` "Done" >> $theLog

# need to kill pmset we spawned earlier so system will sleep
kill -9 $pid

# Now tell system to go to sleep since we're done
pmset sleepnow

One additional thing I added some was simply logging of the script's output, I set up a variable called theLog with the file name. Any lines that get echoed to the log will include the date, except for the output of the rsync command.

The server and user variables would be for the machine you're syncing data from, setting up this master machine with a static IP address would obviously be a good idea. Since I'll only be performing this sync while the car is parked in the driveway, I know I'll be connected to my home wireless network, but it's possible that if the script ran while connected to another network that a machine with the specified IP might be found, but the rsync would then fail. Additional programming could be put in place to check for that scenario, but for me isn't necessary at this point.

One tricky bit that this script needs to handle is to prevent the Mac from going to sleep while we're doing our sync. Our Energy Saver settings are set to sleep after 1 minute on battery power, it is possible that our script could run longer than that depending on the amount of changed data that needs to sync. There are some 3rd party tools that can prevent system sleep, but nothing that was ideal for what I wanted here. After much Googling and experimenting, a relatively elegant solution presented itself.

The pmset command, used for interfacing with the Power Manager, in addition to being able to read and set various power settings (system sleep, hd spin down, display sleep, etc), also has a relatively unknown option to prevent the system from sleeping: pmset noidle. Now, the tricky part is that if you enter this command at the terminal prompt, the command runs, preventing the system from sleeping, and just sits there until you quit the process by typing Ctrl-C. Not useful for a script.

What I've done is to tell my script to fork this process by appending an ampersand at the end, so that it will run independently. in the background, letting the rest of this script continue on. In order to kill the process later, we need the process ID of this command, which is what that grep/awk line does. There is actually an easier way to get the PID of a spawned process, but this command creates a new shell and runs the command in that shell, and simply quitting that shell wouldn't quit the pmset that is running there, so it was necessary to get the actual PID of that command in order to terminate it.

So, the system sets the log location, uses the pmset to tell the system to never sleep, , and next, we set the IP address and username for the machine we're connecting to, and do a test ping to see if that IP is reachable. As my intention is to run this script while parked in my driveway, connected to my home wireless network, it wasn't necessary to perform any additional checks, but it is possible that if the system connects to a different wireless network, another system with that IP might be found, which would mean that the rsync will fail later, but for my use this wasn't worth the effort of coding around.

So, if we find out server is down that we want to be copying iTunes data from, then we log that fact, otherwise, we log that the server was found, and use rsync to connect to the home system, and copy files from the specified user's Music folder in their home directory, the ssh connection established will default us to that user's home directory, since we aren't specifying any path, the Music/iTunes/ folder from that location is what we're copying. We specify the destination as the current user's home directory/Music/iTunes folder. The -avz parameters will cause only changed data to be synced, new files will be copied, removed files will be deleted.

When the first rsync is done, everything will need to be copied, so obviously it's preferred to do this while running on AC power when this is first setup. After that, changes should be minimal, and will only take a few moments or minutes, depending on the amount of changed data.

After the rsync completed, we kill the pmset process we forked earlier, echo to the log that we're done and about to sleep, then finally use pmset again to tell the system to sleep now, and we're done!

Posted by Jim at 3:45 PM | TrackBack

August 11, 2009

Carputer Software

There are a number of front ends used for Mac based carputers, unfortunately most of them are either abandoned projects, lacking in features or polish, or otherwise not quite exactly what I wanted. My main objective was simply to use this as a big, fancy iPod, my old click-wheel iPod just isn't quite cutting it anymore in the car. Something that used cover flow would be ideal, easy to navigate, clean interface, in short, what I was looking for was basically already part of the Mac OS, Front Row.

My main issue with Front Row, though, was that my plan was to use a touchscreen interface for the carputer, and Front Row did not allow for such use, it used keyboard input only, or the Apple IR remote, which is basically a remote keyboard, mouse or similar inputs aren't used. What I wanted, ideally, would have been some way to remap the touchscreen input, so that I could touch the top or bottom of the display for up/down arrows, left/right sides for left/right, etc. Unfortunately, I wasn't able to come up with any software that would let me capture inputs this way, so I was back to square one.

A number of folks had recommended the Griffin PowerMate for controlling Front Row, but I wasn't terribly happy with that solution either. At this point, my plan is to use a Logitech Precision Gamepad to control the action, and the USB Overdrive software to remap the controls for what I need.

After some testing, I found that I really only need four keystrokes, up, down, return, and escape to do all I need done in Front Row. My plan for phase two of this project is to disassemble the gamepad, remove the d-pad portion of the controller (lots of soldering and hacking there) and fabricate a new housing for this that would be placed at the steering wheel for easy access, that is, unless some better idea comes along.

So, with the controlling part done, the next thing I needed done in software was a way to keep my tunes in sync with my home system. Time to start coding...

Posted by Jim at 12:48 PM | TrackBack

August 10, 2009

Carputer almost ready

Back in April, I started work on a Carputer project for my new car, a 2006 Nissan Sentra SE-R Spec V. After some planning, purchasing of parts, testing of software, etc, I'm just about ready to begin a rough install this week as a proof of concept. Basically this will involve placing the key parts where they need to go, but the cables will be exposed and everything can be quickly removed. I've got a road trip coming up next week, so this is a perfect time for a good test.

The software was the trickiest part lately, I had to set up some custom scripts to do the iTunes library syncing with my home desktop, and coming up with a convenient way to control Front Row was also a challenge. I'll post a series of articles on the various pieces of the project.

Posted by Jim at 8:17 PM | TrackBack