Call Home Script

A year or two ago when I first set up my old laptop as a linux server I created a script that logged into my router, parsed the external ip address, checked that against the ip address it got last time, and emailed me if they were different. That worked great for the intended purpose (telling me when the address of my server changed), but I was thinking recently that I could rewrite it to tell me whenever the external ip address of the computer changes, such as if someone stole one of my linux computers and booted it far enough for my cron job to be able to fire. That depends on the computer being connected to the internet when the script runs, but updating the script was interesting enough to be worth doing even if there’s very little chance of it being overly useful.

I’m building off of code I wrote a while ago, so the parts that work won’t have much commentary on what it took to write them.

Calling Home

The first (and most important part) is the script to notify you when it decides it needs to. I chose email because I expected it to be pretty easy (it was) and because always having an email in my inbox that contained the ip address of my server seemed useful.

A quick search on “python send email” turned up a docs.python.org page with the basics, so that’s might have been where I started. I wanted a general script that I could call from other places, but I didn’t want to include the login info for the server every time so I ended up with this script.

The username, password, and email address are obviously not actually all ‘*’s, but I didn’t particularly want to post my gmail info. The smtpuser that my version of the script uses is everything before the @ in my gmail address, and the smtppass has to be a valid password for your account, though interestingly it was willing to accept my last password (my password changed after I wrote the script and it still worked). Giving it an invalid password raised an exception at session.login(smtpuser, smtppass) with a link to a gmail support page.

The documentation example I linked above used another library for setting up the body of the message, but joining the header lines I have with line breaks works too. The "", # extra line break comment is a required blank line for it to parse correctly as an email.

Checking your IP

Once your script can notify you, it has to be able to tell when it needs to. My original script logged onto my router (a linksys), opened the status page, and parsed the external ip. That obviously depends on your computer being at home where it’s able to log into the router. A more general solution is to check a website for the ip address it sees you connecting from.

Since I have web hosting and wasn’t sure that sites like whatismyip would like being called regularly from a script I looked up the php to display the external ip of the requesting computer. I don’t really want my site to be hit be any random person looking for a machine-friendly ip address site, so it’s in a passworded section of my site, but here’s the html of my getip.php file:

<html><body>
<?php echo $_SERVER['REMOTE_ADDR']; ?>
</body></html>

Note: All block code formatting and syntax highlighting is done through code2html.

Since I’m fetching the ip address from an html page behind a password I had to find a way to have python log in. I ended up using urllib2:

uri = "http://192.168.1.1/"
username = "********"
password = "********"
page = "http://192.168.1.1/Status_Router.asp"

def get_ip() :
	pass_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
	pass_mgr.add_password(None, uri, username, password)

	auth_handler = urllib2.HTTPBasicAuthHandler(pass_mgr)
	opener = urllib2.build_opener(auth_handler)
	urllib2.install_opener(opener)
	
	handle = urllib2.urlopen(page)
	site_text = handle.read()

	match = re.search("var wan_ip = \"([\d\.]+)\";", site_text)
	#print match.group(1)

	my_ip = match.group(1)

	return (time.asctime(), my_ip)

The uri and regex in that snippet are for my linksys router. For the php code earlier in this post I’m matching on "([\d\.]+)". The return statement gives the time the ip address was taken and the ip string. I keep a log file with that data pair so I can see when the ip address changed, but that’s just python file io, so I need the time at some point and making a tuple here was easiest.

The entire script (including the error handling I trimmed out of the snippet) is posted here.

Scheduling

No matter how fancy your script is, it’s useless if it doesn’t actually get run. Since I’m running it on linux cron seemed like the easiest way to go. Once the script was there to tell me if my ip address changed I didn’t want to run it too often, but I did want it to run regularly. I settled for running it once an hour, and I think I’ll stick with that:

# to deactivate (all cron jobs): crontab -r
# to activate : crontab ./cronip
MAILTO=*username*@localhost
0 * * * * /home/*username*/scripts/ipcheck.py

The *username* should be the login of the user who the script is to be run as. If I remember correctly any errors or other output from the script will be mailed to the specified local user. I think there is a way to mail to an external user, but it required more setup than just typing an email address.

For the path to the script I wrote out the full path, but a relative path from your home directory might also work. The script will be run from the home directory, so any file input and output will be done from there unless an absolute path is specified.

Rubik’s cube

Some of the people at work have started a scoreboard of rubik’s cube times, so I’ve been working on getting faster at it. I’ve been maintaining an average time of around 1:20 when I tried, but I was usually just solving it for fun, not trying to improve or even timing myself too often.

They’ve been using CubeTimer.com and solving at work. Unlike them, I don’t sit off in an isolated corner with my team, and my cube is rather noisy. Though I solve my cube at work while waiting on builds, app server restarts, etc I try to keep it quiet instead of solving it fast.

Since I just got an Android phone I checked for a cube timer for it. I found SpeedCube Timer. It’s a pretty decent timer, but it can’t be set to a 3s inspect timer and doesn’t track 3 of 5 average (drop high and low, average the remaining 3). We changed to a 5s inspect timer (since we just need a standard time, 3s was chosen because Calvin didn’t want to have any), but the only metrics on the scoreboard are fastest and 3 of 5 average.

Now I could (and did for a couple days) go through the SpeedCube Timer log and calculate my 3 of 5 average times by hand when I think they’ll turn out well, but that’s time consuming. Since I figured it would be straightforward and felt like brushing up on my Python anyway, I wrote a script to parse the SpeedCube Timer exported log file and calculate all the metrics that cubetimer tracks. That script is posted here.

Here’s the sample output using my log file up to tonight:

Average : 67.48 seconds
Best : 40.74 seconds at July 30 2010 09:18:12 PM
Top Averages:
Avg. 5 : 55.18 seconds from July 30 2010 09:16:19 PM to July 30 2010 09:27:47 PM
3 of 5 : 57.04 seconds from July 30 2010 09:16:19 PM to July 30 2010 09:27:47 PM
Avg. 10 : 61.00 seconds from July 30 2010 09:09:46 PM to July 30 2010 09:32:44 PM
10 of 12 : 61.05 seconds from July 30 2010 09:09:46 PM to July 30 2010 09:37:00 PM

Notice all of my best averages are grouped around my 40s solve (when I got crazy-lucky and all the pieces for the first few steps were on the same side but scrambled, so I didn’t have to look for anything till the last two pieces of the first two layers). My next fastest score is just over 50s currently.

I also realized that Android is a rather open platform and that I could probably find an app somewhere that would interpret Python. Sure enough, I found ASE (or SL4A). By default only the Shell interpreter is installed, but the user guide in the wiki on their site has instructions for installing the Python 2.6.2 interpreter. One of the options for adding a script is by QR Code, so I figured I’d encode my script that way rather than copying the file over the usb connection.

While the ASE wiki claims that the most data that can be put into a QR code is 4,296 characters, actually finding a free QR code generator that will go that big is apparently not easy. The recommended one (ZXing) wouldn’t take such a long query. The next several were the same, but some wanted registration or payment to generate a code. This one actually worked, but it resulted in this monstrosity. I’ve gotten it to scan twice, the first time it said it was an invalid qr code, but the second try actually worked (both times took minutes of attempting to line up the scan box on the screen perfectly with the edges of the QR code displayed on my large monitor). Come to think of it, I probably regenerated the QR code between attempts so I may have forgotten the file name or something the first time. By trimming white-space, comments, display of dates, and file-too-short/missing error handling, and generally making the code unreadable I got it down to 737 characters in this QR code.

To use it you’ll have to export the log from SpeedCube Timer, rename that file to “3 x 3 x 3 Cube.csv” (or change the script), and point the script at the file (the default path in the script is where my Droid Incredible put the file). It’s significantly easier to change/understand the script that’s linked as a .py file (or the big QR code) and that one has more features, but for convenience of transfer the compressed QR one is nice.

Now that everything is finished and working, I think I probably spent more time looking for a QR code generator and trimming my program size to make it fit than I did actually writing the program in the first place.