Remotely boot your iPad and launch an app

I’m one step further along on the quest for the Holy Grail of kiosk displays. We use a lot of iPads here at the museum, most of them are either in Lab Shield brackets or built into cabinets. The whole idea behind building a kiosk is to make it hard for people to break into your device, so naturally the power and home buttons are behind lock and key.

The trouble is, there are a few quirks in the iPads. One is that after running for a few days, the display seems to lose its sensitivity to touch. The only way so far to deal with that seems to be to open the bracket, hit the power button to make it sleep, then hit it again to make it wake up. Then launch the app again and lock the whole thing back up. Not something the visitor services people want to do a lot. Nor I.

Sometimes visitors also break out of the kiosk app (we’re using iCab Mobile, an absolutely fantastic app, by the way). I have no idea how, but sometimes the iPad will be doing something completely different. One of ours now has a spiffy new home screen background thanks to someone who got in.

I’m very hopeful that my newly found method of remote booting/app launching will help at least take some of the pain out of the reset process.  Here’s how:

  1. Jailbreak the iPad (only works on iPad 1′s for now)
  2. Install OpenSSH (I installed the whole BigBoss Recommended Tools suite)
  3. Install Activator
  4. Determine the IP address of the iPad
  5. ssh in as root, change the password right away!
  6. Set up Activator to launch whatever app (e.g. iCabMobile) you want to have start up at boot time. Hook the app to the “Anywhere -> Power -> Connected” event.
  7. If you’re still ssh’ed in from step 5, type ‘reboot<return>

Voilá. Your iPad will reboot, and if you’re crossing your fingers just right, the app will launch after it boots. It seems to do this pretty reliably unless you do it too many times in too short a time.

I’ve set up ssh keys for the iPad so that I can run a command like this from my desktop machine:

ssh -i .ssh/id_rsa_ipadkiosk root@192.168.1.52 reboot

Next up on my list is to build a web app that lets us reboot any iPad. Then we can carry around an iPad running the web app, and reboot troublesome exhibits with the swipe of a finger.

Update: I should note that for this to work, the iPad has to be plugged in to a power source. During the boot sequence the hardware must sense the power source and generate the same event that gets generated if you plug it in after it’s booted.

Posted in kiosk, Museum

Mac OS X user idle time in Python

I’m still on the quest for the perfect free kiosk app for Mac OS X. I’ve used Plainview to good effect in several exhibits. I’ve used Opera in one, and I’m using Firefox with R-Kiosk in another. Which program to use depends a lot on the nature of the web page(s) to be displayed in the kiosk and the way the user expects to interact with the kiosk.

For the Firefox kiosk, I needed a sure-fire way to reload the home page after a certain amount of idle time. The trouble is that Firefox doesn’t lend itself to be controlled via AppleScript. A lot of poking around led me to learn about AppleScript System Events being able to simulate a keypress, that Option-Home will make Firefox load the home page, and that there’s a really handy little application called Full Key Codes that tells you what the key code is for any key you press on a Mac.

That let me put together this handy little Python script that watches how long it’s been since a user has done something. First it waits until there’s been any user activity at all, then it waits until there’s been no activity for 90 seconds. Then it uses osascript to run a little bit of AppleScript that sends Option-Home (key code 115) to Firefox.

I suspect there’s a way to eliminate the osascript by using the Python objc module, but this is good enough for me…

#!/usr/bin/env python

#========================================================================
#
# idle.py - makes Firefox load home page after user inactivity
#
#       Set the value of 'timeout' to the number of seconds of idle
#       time you want to allow before reloading the home page
#
#       Comment out the calls to status() if you don't want to see any
#       messages
#
#========================================================================

from subprocess import Popen
from time import sleep
from Quartz.CoreGraphics import *

# From /System/Library/Frameworks/IOKit.framework/Versions/A/Headers/hidsystem/IOLLEvent.h
NX_ALLEVENTS = int(4294967295)  # 32-bits, all on.

# Set this value to the number of seconds of idle time before
# resetting Firefox
timeout = 90.0

def status(state, last, idle):
    """Print out some info about where we are """
    print "%10s %.1f %.1f" % (state, last, idle)

def getIdleTime():
    """Get number of seconds since last user input"""
    idle = CGEventSourceSecondsSinceLastEventType(1, NX_ALLEVENTS)
    return idle

def doReset():
    """Use osascript to tell Firefox to reload the home page"""
    reset = """/usr/bin/osascript \
        -e 'tell application "Firefox"' \
        -e ' activate' \
        -e 'end tell' \
        -e 'tell application "System Events"' \
        -e ' key code 115 using option down' \
        -e 'end tell' """
    Popen(reset, shell=True)
    sleep(1)                    # Prevent osascript keypress from triggering below

last = 0.0
idle = 0.0

while True:

    # These two lines can also be at the very end if you don't want to
    # load the home page once when the program starts
    status('--- RESET', last, idle)
    doReset()

    idle = getIdleTime()
    last = idle

    # Wait for user activity
    while idle >= last:
        status('wait', last, idle)
        sleep(1)
        idle = getIdleTime()

    # Wait for idle to become bigger than timeout
    while idle < timeout:
        status('triggered', last, idle)
        sleep(1)
        idle = getIdleTime()
Posted in kiosk, Mac, Museum

Spotlight for coders

Apologies to everyone who already knew this…

I just discovered something very cool. As an only occasional coder (Python, mostly, when I get the chance to write code), I don’t have a very good grasp of where all the header files for Mac OS X frameworks live.

Let’s say that you want to know where CGEventSourceSecondsSinceLastEventType is defined. Just hunt for it in Spotlight and it will turn up all the places it shows up in header files.

E.g.

/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs /iPhoneSimulator3.2.sdk/System/Library/Frameworks/ApplicationServices.framework /Versions/A/Frameworks/CoreGraphics.framework/Versions/A/Headers

Or

/Developer/SDKs/MacOSX10.6.sdk/System/Library/Frameworks /IOKit.framework/Versions/A/Headers/hidsystem

Clicking on the Spotlight results opens the file in Xcode.

Who knew?

Posted in Mac, Random

WordPress client IP address, behind a proxy

At the museum, we’re running a site where we want people to comment but where we’re also sitting ducks for spam comments. Trouble is, the site is running behind a web proxy. That means that all the comments are seemingly from the same IP address, namely that of the proxy host. That, in turn prevents any meaningful spam detection.

I poked around the web a while, looking for a way to get the real IP addresses, and finally rolled my own solution.

It boils down to this. If you’re running behind an up-to-date apache server that’s doing the proxying for you, all of the incoming HTTP requests should have the X-Fowarded-For header set to the originating client’s IP address.

Once I verified that this is the case, I put this snippet of code into my functions.php file.

function m150_ip_fixup($s) {
  $headers = apache_request_headers();

  if (!empty($headers["X-Forwarded-For"])) {
    $_SERVER["REMOTE_ADDR"] = $headers["X-Forwarded-For"];
  }
  return $s;
}

add_action( 'pre_comment_on_post', 'm150_ip_fixup');

Now, comments get tagged with their original IP address.

Posted in Museum, Wordpress

Coming up with a question is also creation

To create is not just to produce objects or phenomena.

Coming up [with] a question is also creation.

In fact, a question that has a huge receptive capacity doesn’t even need a definitive answer.

The very essence of a question is its power to elicit the possibilities or reply, to trigger a variety of thoughts.

Questioning is emptiness.

The total quantity of thoughts triggered by questioning is what matters most.

I entreat you not to produce more but to think more.

I believe that the richness of that thinking may very well be the critical resources to giving this world a future.

Kenya Hara at Google

Found via Core77

Posted in Museum, Random

Mac OS X ssh quickie

Are you getting these messages in your console log when you make a new user on Mac OS X and then try to ssh in to the computer with that new user name?

Aug  6 12:33:25 example sshd[7296]: in pam_sm_authenticate(): Failed to determine Kerberos principal name
Aug  6 12:33:29 example sshd[7294]: error: PAM: authentication error for user from example.com via 192.168.0.1
Aug  6 12:33:29 example sshd[7297]: in pam_sm_authenticate(): Failed to determine Kerberos principal name.
Aug  6 12:33:32 example sshd[7294]: error: PAM: user account has expired for user from example.com via 192.168.0.1

Then don’t do what I did. After googling to no avail, I went so far as to completely reinstall Mac OS X. No good.

[Update: Actually, what I did was clone a different system that I had recently set up and used it as the "new installation". Had I done a total, from DVD reinstallation, it would have fixed the problem but I wouldn't have discovered the cause.]

The answer? Make sure you didn’t set the Remote Login preferences in System Preferences/Sharing to “Only these users” and then forget to add the new user to the list!

D’oh.

Hopefully this will help the next person who’s looking for the answer.

Posted in Mac, Random

Review: Rubbermaid 12-Slot Organizer as a Mac mini server rack

I needed to do something about the Mac minis that were accumulating on the table in my office. Digging around, I found this Rubbermaid organizer on Amazon.

It turns out to be nearly perfect. The unit is very sturdy, was easy to put together, and the shelf height is just right. There’s enough clearance for airflow but not so much that you feel space is being wasted.

I used self-stick cable tie anchors and cable ties to mount the power bricks and used double-stick mounting tape as stops to keep things in place. The old-style minis are heavy enough and are pretty non-slip, so I just put some tape at the front of the shelf to keep them from sliding off. The one new-style mini was pretty slippery so I used the tape to actually stick the base to the shelf.

The unit came with vertical rods that go in the back of each column of shelves to keep them from sliding out the back, but I decided to leave those out. That way I can slide each shelf forward to get DVDs into the mini, or back to get at the connectors.

The weak spot of the minis is the power cord (at least on the pre-2010 models) which comes out quite easily. I tied those down as well and am pretty sure they won’t jiggle their way out. I have four minis in the rack right now along with a Drobo with 10TB of disk. I’m going to be adding a 5th mini with a stackable disk drive, that’s why there’s double-high slot  still open on the mini side of the rack.

Cable management is an issue, mostly because of the power bricks long cables. I may fiddle with how I fold the cables into the shelves a bit more.

The whole thing plus a UPS and monitor/keyboard/mouse sits nicely on some steel shelves in our A/V equipment room at the museum. I still need to time how long the UPS runs. I’m only going to have the public web site minis on it.

Posted in Mac, Museum

Old bits slipping away

I moved this blog from one of the Mac minis in my basement to the other (I’m trying to put everything on the newer one to free the other one up) yesterday. Originally I had been blogging using Plone (from about 2005-2007) and then moved to WordPress. Moving the Plone part seemed like it was more work than I wanted to put in, so it’s goodbye to those posts.

Posted in Random

OGC (re)discovers URLs, but let’s tighten up the terminology a bit

I had seen this tidbit that Sean Gillies writes about in the recent OGC newsletter. My thoughts were along the lines of Sean’s. I never understood the big deal behind URNs.

EDIT: Forget the semi-rant, see the comments, and then go read about URI…

But in re-reading Sean’s post and the OGC news coming out of the June 2010 meetings, I think the terminology is a bit imprecise. Too bad the source document, 10-124r1 isn’t available on the OGC web site (promised for mid-July, I see) to see if the issue is in the document or in the news page. Here’s the news page version:

OGC Identifiers – the case for http URIs’

The OGC Members approved release of ‘OGC Identifiers – the case for http URIs’ [OGC 10- 124r1] as an OGC Whitepaper. .According to the current OGC policy either URNs or http URIs may be used in OGC standards. However, the use of http URIs (a) resolves some deployment challenges and (b) provides an opportunity for easier engagement with broader communities. So OGC should now consider taking the next step, and mandate the use of http URIs for persistent identifiers in OGC specifications. This whitepaper canvasses a number of issues around this proposal.

http URI Policy

The OGC Members approved the following as official OGC policy to be included in the OGC Policies related to OGC standards [OGC 06- 135rN]:

  • OGC TC directs the OGC-NA that all new OGC identifiers issued for persistent public OGC resources shall be http URIs, instead of URNs
  • New standards and new major versions of existing standards shall use http URIs for persistent public OGC resources to replace OGC URN identifiers defined in previous standards and versions, unless OGC- NA approves an exception

Operational Implications: OGC should carefully manage (maintain for the long term) the http://www.opengis.net domain and identifiers in this domain

So what’s wrong? Refer to RFC3986 (or the html version). Section 1.1.3 talks about URI, URL, and URN:

A URI can be further classified as a locator, a name, or both. The term “Uniform Resource Locator” (URL) refers to the subset of URIs that, in addition to identifying a resource, provide a means of locating the resource by describing its primary access mechanism (e.g., its network “location”). The term “Uniform Resource Name” (URN) has been used historically to refer to both URIs under the “urn” scheme [RFC2141], which are required to remain globally unique and persistent even when the resource ceases to exist or becomes unavailable, and to any other URI with the properties of a name.

A URN is a kind of URI. What is called an “http URI” is really a “just” a URL in RFC3986. And, a URN need not (or I should say “need no longer”) be something with “urn:” in the scheme. A URL could be a URN based on the last part of the definition above, “any other URI with the properties of a name”

Therefore, an “http URI” (from the OGC wording) can be either a URL or a URN, based on section 1.1.3 of RFC3986. Of course, the URN is really a URL with the additional uniqueness and persistence properties. So let’s just call OGC’s newly mandated URIs URLs.

There are two primary motivations for using RFC2141 URNs. One is as a globally unique name managed by some authority. The other is as a persistent identifier, sometimes used to map onto a URL with a resolver. The trouble with the latter is that URLs really work better in the first place, and I’m guessing that’s what 10-124r1 says.

So here’s what I think they should have said in the TC:

URI Policy

The OGC Members approved the following as official OGC policy to be included in the OGC Policies related to OGC standards [OGC 06- 135rN]:

  • OGC TC directs the OGC-NA that all new OGC identifiers issued for persistent public OGC resources shall be http URLs, instead of RFC2141 URNs
  • New standards and new major versions of existing standards shall use http URLs for persistent public OGC resources to replace OGC RFC2141 URN identifiers defined in previous standards and versions, unless OGC- NA approves an exception

Sorry to be so pedantic. Back in the day, there would have been half a dozen people at any given TC who would have been able to argue the finer points of this for hours….

(And I just figured out what the OGC-NA is. I guess it’s the “Naming Authority”.)

Posted in Geo

QR Codes in exhibits

We’ve put our first QR Code up at the museum as an experiment. This seems like one way to deal with “nth level” information that might be interesting to some visitors but not to others. And it’s a way to make it easy for visitors to bookmark information for themselves.

Generating the QR Code isn’t hard. Google’s Chart api can do it. Basically, anything you put fullowing the “chl=” part of the URL here will generate a code: http://chart.apis.google.com/chart?cht=qr&chs=350×350&chl=http://think.random-stuff.org/

We’ve set aside a specific URL space for our codes to send people to. Each link will first take you to a short URL which will then redirect you to the actual URL relevant to the exhibit. The advantage of this is that we can also put up short, “human readable” links on the exhibits.  That will let us track how often the links are getting used and what mobile devices people are using. We’ve also put an explanatory page at the top level of that space.

There was a little debate about how much we should try to explain QR Codes on the exhibit itself. You can see what we wound up with in the detail photo. Basically we’ve decided that visitors will either (a) recognize the code and know what to do with it, (b) not recognize it and skip over it, or (c) ask someone. The “c” people can be given a printout of the explanatory page.

We’ve decided to introduce the codes slowly and in a way that hopefully doesn’t get in the way of people who don’t know what they are or don’t choose to use them.

Another question was whether we should format the linked pages in a phone-friendly format. The easy answer (because it requires no additional resources…) was not to do that. Phones are getting pretty good at reading full-blown web pages.

For further reading, here’s a March 2009 article titled QR codes in the museum – problems and opportunities with extended object labels

What QR Code app am I using? Right now on my iPhone 3G, I’ve got Barcodes. It’s got a huge number of one-star, negative comments but it works for me. The critical thing you need to know is that it only works on QR Codes, not regular barcodes, and at least with the 3G, you have to take the photo from about 18″ away and then use the app to zoom it to the right size.

Posted in Museum