The future of AppleScript and Indigo September 09, 2017

Goodbye AppleScript

As we've been warning for several years now, AppleScript is a legacy scriping technology that we will be deprecating. Indigo 7.3 will be the last Indigo feature release that will allow direct AppleScript communication with the Indigo server. There are a variety of reasons for this change, including Apple's apparent deprioritization of the technology. We have been quite open about AppleScript's future in Indigo and the time has come to get serious about transitioning.

There are two main purposes of using AppleScript in Indigo today: to automate or script Indigo based home automation functions (turn on lights, execute an Indigo Action Group, etc.) and to integrate Indigo with other applications that support AppleScript (play/pause iTunes, integrate SecuritySpy, etc.). Below are suggestions on how to migrate both over to be compatible with versions of Indigo beyond 7.3. In some cases a hybrid solution is needed, where you can convert a majority of your Indigo AppleScripts to Python but still need to execute small AppleScript snippets for integration with other applications. Note that there are now hundreds of Indigo plugins that integrate directly with different services and applications. Often what used to require an AppleScript (play/pause iTunes, integrate SecuritySpy) is now more easily accomplished directly with a plugin – no scripting needed!

Converting Indigo AppleScripts to Python

Python is an easy-to-use but very powerful scripting language. Although the syntax is different than AppleScript we have found most users prefer it after spending just a bit of time getting used to the differences. Indigo's Scripting Tutorial is a great place to see examples for controlling devices, manipulating Indigo variables, executing Action Groups, and much more.

If you have an AppleScript that you are having difficulty converting, then we recommend posting on our user forum here. Our Indigo community is the best (not an exaggeration!), and if you include both your AppleScript source as well as your attempt at converting it to Python then we will be glad to help you.

Calling Indigo from AppleScripts

While we encourage users to convert their AppleScripts to python when possible, we realize there are some cases where that is difficult or impossible. An example would be a 3rd party app that allows for easy execution of AppleScript.

There are a couple of different ways to use AppleScript to control Indigo devices, variable values, and execute action groups: RESTful Web API calls and direct Indigo python scripting engine calls. Using AppleScript to make RESTful Web API calls as shown here is a good solution but requires that Indigo's Web server be enabled and that the username and password be embedded in the AppleScript. Directly calling Indigo's python scripting engine avoids those requirements and works as long as the AppleScript is being executed on the same Mac as the Indigo Server. That technique can also be used to work around the macOS bug which causes the "Application isn't running" errors discussed here.

Running AppleScripts from Python with py-applescript

We’ve recently come across a Python module that allows a much tighter integration between Python and AppleScript: py-applescript. It’s included with Indigo 7.1 and later, but you can install it yourself (use sudo pip install py-applescript) if you are using an older version of Indigo.

The basics of using py-applescript

Using the module is very simple. As with any Python module, the first thing you want to do is import it:

import applescript

The next step is to create an AppleScript object. This can be done by loading an AppleScript from a file or by using an embedded AppleScript string. Here are examples of both ways:

import applescript

# From a file
path_to_script_file = "/Users/jay/Projects/Python/AppleScriptTest/sample.scpt"
my_ascript_from_file = applescript.AppleScript(path=path_to_script_file)

# From an script in a Python string
ascript_string = '''
say "Hello Python!"
set someDict to {abool: true, afloat: 35.730}
set someList to [1, "two", 3]
return {anum:1, astring:"fun!", adict: someDict, alist: someList}
'''
my_ascript_from_string = applescript.AppleScript(source=ascript_string)

Other than where the script source comes from, these two methods result in an identical Python AppleScript object. In the embedded AppleScript string in the example above we've created a contrived record that contains several different value types which we return.

Finally, running the AppleScript object is quite simple:

reply = my_ascript_from_string.run()

This will execute the run() handler or, if there is none (as in our example), it will just execute the script. The Python reply variable will contain a Pythonic version of whatever is returned from the script. In our example it will be a Python dictionary:

{
    u'alist': [1, u'two', 3], 
    u'adict': {u'abool': True, u'afloat': 35.73}, 
    u'astring': u'fun!', 
    u'anum': 1
}

As you can see, the AppleScript record returned to the Python script has been converted to standard Python objects.

Calling handlers

If your AppleScript is complex or contains a lot of related logic, you can implement handlers (AppleScript functions) and then call those specific handlers. Let's say we have the following AppleScript:

on saySomething(speechString)
    say speechString
end saySomething

on getiTunesPlaylists()
    tell application "iTunes"
        set myPlaylists to name of every playlist
    end tell
    return myPlaylists
end getiTunesPlaylists

Note that it contains two handlers. One accepts a string and speaks it, the other doesn't have any parameters but calls out to iTunes to return a list of playlist names. Here's how you'd call those handlers from a Python script:

import applescript

# From a file
path_to_script_file = "/Users/jay/Projects/Python/AppleScriptTest/handlers.scpt"
my_ascript_from_file = applescript.AppleScript(path=path_to_script_file)

# You don't need to set the return value because the handler doesn't 
# return anything - it just speaks the string and returns.
my_ascript_from_file.call("saySomething", "Hello Python!")

# Call the method (it has no params) and assign the reply to the Python
# playlists
playlists = my_ascript_from_file.call("getiTunesPlaylists")

At the end of the above script snippit the playlists Python variable will contain a Python list of playlist names from iTunes.

As you can see, you can easily integrate data from Python, such as device state values, variable values, etc., into an AppleScript by passing that data to a handler. You can also just as easily pass back data from an AppleScript to Python and you'll get that data in native Python objects such as unicode strings, numbers, lists, dictionaries, etc. You can then use that data to insert into variables and/or device states (if running from a plugin for instance), etc.

Back to article list