Plugin idea

Posted on
Fri Feb 09, 2018 3:17 pm
jay (support) offline
Site Admin
User avatar
Posts: 18200
Joined: Mar 19, 2008
Location: Austin, Texas

Plugin idea

Hey developers - I've just posted a solution on how to create macOS Reminders from an Indigo Python script. While working on this script, it occurred to me that with a few more tweaks, you could enable multiple solution scripts like this to be used from a plugin which could theoretically allow for loadable AppleScripts, provided by others, to have UI dynamically generated for them. Here's the scenario:

The reminders script (hosted on Github) is really just a shim AppleScript that exposes handlers which accept structured data (mostly AppleScript records) and reformats that into the appropriate AppleScript to carry out the action. I use AppleScript records which are somewhat synonymous to Python dictionaries. And, in fact, if you use py-applescript (which we ship with 7.1), it will convert Python dictionaries to/from AppleScript records when you call a handler in an AppleScript (it will also convert most other base Python types). Download and take a look at the Reminders.scpt (it's a compiled script so it's easier to download it and look at it using Script Editor (or better yet Script Debugger if you own it).

The primary handler in the script is makeNewReminder(reminder_record). This AppleScript can then be loaded by py-applescript, and you can call this handler with the following Python:

Code: Select all
import applescript
from datetime import datetime, timedelta

path_to_script = "/Some/path/here/Reminders.scpt"
reminders = applescript.AppleScript(path=path_to_script)

reminder_dict = {
    "title": "Test Reminder",
    "notes": "Notes go here",
    "due_date": due_date = datetime.today() + timedelta(days=1),
    "destination_list": "Reminders",
    "destination_account": "iCloud",
    "priority_string": "high"
}
reply = reminders.call("makeNewReminder", reminder_dict)
if not reply["success"]:
    reason = reply["reason"]
    indigo.server.log("makeNewReminder failed: ({}) {}".format(reason["error_number"], reason["error_message"]))


That will create a new reminder in the "Reminders" list in the "iCloud" account in the Reminders app with a due date of 24 hours and priority set to high. Good enough, and pretty simple to use for someone who's comfortable with Python.

But, what about those who aren't? Wouldn't it be nice to build a nice Action UI so they can just configure it like any other action?

So you build a plugin. Write up the various XML files, add the code above in the right place in the plugin.py file, add to Github and to the Plugin store, write up some docs on usage, and voila, you have a Reminders plugin. Now, someone decides they want to use Messages to send a message. Or use some other AppleScriptable Mac app to do something else. They would have to go through the same process:

  1. Build the wrapper AppleScript, debug/test
  2. Build the plugin itself, debug/test
  3. Upload to Github, create docs, etc.
  4. Publish to the Plugin Store

Still not terrible, but now you're talking some real work beyond just doing the script (step #1).

What if you could do most of the rest of that stuff, and only have to do #1 (with maybe a bit of extra work rather than all the rest of it)?

We funnel basically every call that loads and parses the various XML files through methods in the plugin base class. You can override any of those methods in your own plugin. This would in effect allow you to, say, dynamically load what Actions are available, the config UI that's used to present the dialog, etc. What you would need for the Reminders.scpt (or any other shim script) would be a way to specify that stuff.

The first thought might be to package up the script and the appropriate XML files into a Python module that you could the load dynamically in your plugin and dig through to get the appropriate XML. That's not a bad idea really, but then it requires a structure for someone writing the script: a folder with an __init__.py file, the script file, and the associated XML files. That still requires a good amount of knowledge for someone to add a new script.

Maybe you can alleviate the packaging aspect by implementing a few well-defined AppleScript handlers that would just return all the necessary data to create the UI. That seems like a step in the right direction since all you'd need to distribute/load is a single AppleScript file. But it still requires the developer of the script to understand how to construct the appropriate XML. That may in fact be good enough.

And here's another thought, and is where I left off on this though experiment: what if those well-defined handlers (call them introspection handlers) didn't (necessarily) need to return XML, but rather some subset of the Config XML features. Like maybe just a property name, very basic type (string, boolean, number for instance) and whether it's required or optional. Then you could construct a very basic Config dialog for the thing (action, event, etc) and even provide basic validation. Then the script writer would only need to be able to specify that as a reply to an introspection call.

I don't honestly know if that last part would be useful really. If someone were a very good AppleScripter but didn't know or care to learn anything about Python, then I suppose that person would like that last approach. I don't know, as I said that was where this though experiment ended. There's no doubt that this thing could grow into a monster that might never really yield much in terms of usefulness. And we certainly don't have the kind of bandwidth to put more thought or development into it (the Reminder script is as far as we'll go and we only went that far to offer it up as a design pattern).

However, if you're looking to do something that's kinda cool, challenging, and could in fact provide a lot of value, then perhaps this is something that you'd like to think about.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Page 1 of 1

Who is online

Users browsing this forum: No registered users and 13 guests