Interested in working with us? We are hiring!

See open positions

Introducing AirControl: AirPlay Mirroring from your terminal

Timothée Boucher Written by Timothée Boucher, September 26, 2014

AirControl is a tool to control AirPlay Mirroring from the command line.

TL;DR: it’s open source and available in our GitHub repo.

If you’re interested in the details of how we got it working, read on.


Once a month, the Engineering team has a Hack Day. Anybody can work on anything they like, be it discovering a new programming language, speeding up our test suite, or, in the case at hand, building a terminal tool to control AirPlay Mirroring.

The only constraint we put on these Hack Days is to work with other people on your project.


I use vim, Vimium, have custom mappings on my keyboard, wrote a Chrome extension to log you out of websites with a keyboard shortcut: in short, I like typing more than moving my mouse and clicking around. That’s why a couple of months ago I realized that having to use the mouse to activate AirPlay Mirroring was inconvenient… nay! Barbaric! Something needed to be done.

I recruited Eric and Chris to work on this last Friday.

We worked through the problem in stages:

Activate AirPlay Mirroring programmatically

First, we got a short AppleScript to activate the AirPlay menu in the menu bar:

tell application "System Events"
    tell process "SystemUIServer"
        click (menu bar item 1 of menu bar 1 whose description contains "Displays")
        click menu item 4 of menu 1 of result
    end tell
end tell

You can open AppleScript Editor, paste this and run it, if you have an AirPlay device on your network, it should connect you.

Changing item 4 to item "Living-room" works too.

Now we needed to have this AppleScript run from the terminal and take an argument.

To the terminal!

The easiest way for that is to use osascript. osascript lets you run any Open Scripting Architecture script and in particular AppleScript.

So, instead of using AppleScript directly, we let bash invoke osascript with the above script filled in with the first argument passed in for AirPlay device name:

#!/usr/bin/env bash

tvname=\"$1\"

read -d '' APPLESCRIPT <<EOF
tell application "System Events"
    tell process "SystemUIServer"
        click (menu bar item 1 of menu bar 1 whose description contains "Displays")
        click menu item $tvname of menu 1 of result
    end tell
end tell
EOF

osascript -e "$APPLESCRIPT" > /dev/null

You can run the above as aircontrol 2nd\ Floor\ Lounge and you’ll connect to the AppleTV with that name.

NB: you’ll need to enable your terminal for Accessibility settings as shown below: accessibility settings

Picking up the tab

At this point, we have something that does what we wanted: starting AirPlay Mirroring from the terminal. But we want more! We want need to tab-complete this thing!

DNS Discovery

The first thing is to find the available device names. I had previously gathered some references that led us towards dns-sd to find out what AirPlay devices are available.

Namely dns-sd -B _aiplay._tcp will print out all the AirPlay-enabled devices on the network:

$ dns-sd -B _airplay._tcp
Browsing for _airplay._tcp
DATE: ---Fri 26 Sep 2014---
11:09:47.758  ...STARTING...
Timestamp     A/R  Flags  if Domain   Service Type    Instance Name
11:09:47.759  Add      3   4 local.   _airplay._tcp.  Honor Room (right)
11:09:47.759  Add      3   4 local.   _airplay._tcp.  Good Times Room
11:09:47.759  Add      3   4 local.   _airplay._tcp.  Dev
11:09:47.759  Add      3   4 local.   _airplay._tcp.  Unagi Room
...

Using cut would trim that down to the useful info.

(and make sure you check out this post on non-terminating bash processes)

You complete me

With that list in hand, we needed to tell bash how to tab-complete the command.

The short story is that you run complete -o nospace -F _aircontrol aircontrol where _aircontrol is a function that builds an array called COMPREPLY with the completion candidates.

Here’s our whole script to activate the tab-completion:

_aircontrol()
{
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=()

    i=0
    while read -r line; do
        i=`expr $i + 1`
        if [ $i -lt 5 ]; then continue; fi # skip the header lines
            room=$( echo $line | cut -d ' ' -f 7-100 )
            if `echo "$room" | grep ^${cur}.*$ > /dev/null`; then
                COMPREPLY+=("$(printf "%q" "$room")")
            fi

        # break if no more items will follow (e.g. Flags != 3)
        if [ $(echo $line | cut -d ' ' -f 3) -ne '3' ]; then
            break
        fi
    done < <(dns-sd -B _airplay._tcp)
}

complete -o nospace -F _aircontrol aircontrol

To wrap it up, we added an option to stop the mirroring: -k.

Here it is in action:

aircontrol in action

The final result is available here: https://github.com/AdRoll/AirControl

As you can see, not that much code at all.

Starting from this, one could build a workflow for Alfred or a plugin for Spotlight. A Spotlight extension would be an interesting hack since it’s designed for files, but that will have to wait for another Hack Day…