Interoperability: Omni Automation and Alfred

DISCLAIMER: Mention of third-party websites and products is for informational purposes only and constitutes neither an endorsement nor a recommendation. OMNI-AUTOMATION.COM assumes no responsibility with regard to the selection, performance or use of information or products found at third-party websites. OMNI-AUTOMATION.COM provides this only as a convenience to our users. OMNI-AUTOMATION.COM has not tested the information found on these sites and makes no representations regarding its accuracy or reliability. There are risks inherent in the use of any information or products found on the Internet, and OMNI-AUTOMATION.COM assumes no responsibility in this regard. Please understand that a third-party site is independent from OMNI-AUTOMATION.COM and that OMNI-AUTOMATION.COM has no control over the content on that website. Please contact the vendor for additional information.

This section concerns the third automation scenario listed on the main page:

The following documentation details the construction of the third Alfred action in the OmniFocus Collection Alfred Workflow. This action is used to generate and display in the Alfred interface a linked list of the OmniFocus tasks that are due today.

Scenario 3: Displaying the Results an Omni Automation Script

This scenario begins with the execution of the self-contained Omni Automation script that requires no input from the user.

After performing a filter procedure to identify any tasks that are due today, the script generates a JSON object containing specific information for each of the matched tasks. For example, here is the JSON for a single matched tasks:

Response JSON


{ "items":[ { "title": "Sunday Dinner", "subtitle": "DUE: 17:00:00 GMT-0700 (Pacific Time)", "arg": "dvpyM40skEI" } ] }

NOTE: The highlighted object keys (line 39-53 below) items title subtitle arg are some the available JSON keys supported by Alfred’s Script Filter step which is used in the custom action to parse and display results.

The corresponding values for the keys are generated by the script dynamically for each matched task: task name (title), task due time (subtitle), and the task’s unique ID string (arg) which will be used by the Alfred response action to construct a link to the matched task in OmniFocus.

The generated JSON object is converted into an encoded string (line 56-59 below) and added to the Alfred response URL (line 69 below). When executed, the response URL will target a specific Alfred action designed to receive parse, and display the passed JSON object.

Tasks Due Today


(async () => { try { function createUtterance(textToSpeak){ langCode = Speech.Voice.currentLanguageCode voiceObj = Speech.Voice.withLanguage(langCode) utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = Speech.Utterance.defaultSpeechRate return utterance } var synthesizer = new Speech.Synthesizer() // AVAILABLE TASKS DUE TODAY fmatrStyle = Formatter.Date.Style.Short fmatr = Formatter.Date.withStyle(fmatrStyle) rangeStart = fmatr.dateFromString('today') rangeEnd = fmatr.dateFromString('tomorrow') tasksToProcess = flattenedTasks.filter(task => { return ( task.effectiveDueDate >= rangeStart && task.effectiveDueDate < rangeEnd && task.taskStatus === Task.Status.DueSoon ) }) if(tasksToProcess.length === 0){ throw { name: "No Items Matched", message: "No tasks are due today." } } matchCount = tasksToProcess.length // CONSTRUCT ARRAY OF OBJECTS FOR TASKS itemsArray = new Array() for (i = 0; i < tasksToProcess.length; i++){ aTask = tasksToProcess[i] // CONSTRUCT OBJECT FOR TASK itemObj = new Object() itemObj.title = aTask.name itemObj.subtitle = "DUE: " + aTask.dueDate.toTimeString() itemObj.arg = aTask.id.primaryKey itemsArray.push(itemObj) } // CONSTRUCT JSON OBJECT CONTAINING ITEM ARRAY jsonObj = new Object() jsonObj.items = itemsArray // CONVERT TO STRING FOR INCLUSION IN URL jsonStr = JSON.stringify(jsonObj) // ENCODE FOR URL encodedJSON = encodeURIComponent(jsonStr) // ALFRED WORKFLOW ID alfredWorkflowID = "com.omni-automation.of.alfred-collection" // ALFRED ACTION TRIGGER ID alfredTriggerVar = "displayResults" // CONSTRUCT URL STRING urlStr = `alfred://runtrigger/${alfredWorkflowID}/` urlStr += `${alfredTriggerVar}/?argument=${encodedJSON}` // CONVERT TO URL OBJECT AND EXECUTE URL.fromString(urlStr).open() // CONSTRUCT/SPEAK RESONSE WHILE ALFRED DISPLAYS RESULTS taskOrTasks = (matchCount === 1)? "task":"tasks" response = `${matchCount} ${taskOrTasks} due today.` utterance = createUtterance(response) synthesizer.speakUtterance(utterance) } catch(err){ utterance = createUtterance(err.message) synthesizer.speakUtterance(utterance) //new Alert(err.name, err.message).show() } })();

Using the Omni Automation Script URL Constructor provided on this website, convert the “Tasks Due Today” script into a script URL.

Encoded Script URL


omnifocus://localhost/omnijs-run?script=%28async%20%28%29%20%3D%3E%20%7B%0A%09try%20%7B%0A%09%09function%20createUtterance%28textToSpeak%29%7B%0A%09%09%09langCode%20%3D%20Speech%2EVoice%2EcurrentLanguageCode%0A%09%09%09voiceObj%20%3D%20Speech%2EVoice%2EwithLanguage%28langCode%29%0A%09%09%09utterance%20%3D%20new%20Speech%2EUtterance%28textToSpeak%29%0A%09%09%09utterance%2Evoice%20%3D%20voiceObj%0A%09%09%09utterance%2Erate%20%3D%20Speech%2EUtterance%2EdefaultSpeechRate%0A%09%09%09return%20utterance%0A%09%09%7D%0A%09%09var%20synthesizer%20%3D%20new%20Speech%2ESynthesizer%28%29%0A%0A%09%09%2F%2F%20AVAILABLE%20TASKS%20DUE%20TODAY%0A%09%09fmatrStyle%20%3D%20Formatter%2EDate%2EStyle%2EShort%0A%09%09fmatr%20%3D%20Formatter%2EDate%2EwithStyle%28fmatrStyle%29%0A%09%09rangeStart%20%3D%20fmatr%2EdateFromString%28%27today%27%29%0A%09%09rangeEnd%20%3D%20fmatr%2EdateFromString%28%27tomorrow%27%29%0A%09%09tasksToProcess%20%3D%20flattenedTasks%2Efilter%28task%20%3D%3E%20%7B%0A%09%09%09return%20%28%0A%09%09%09%09task%2EeffectiveDueDate%20%3E%3D%20rangeStart%20%26%26%20%0A%09%09%09%09task%2EeffectiveDueDate%20%3C%20rangeEnd%20%26%26%0A%09%09%09%09task%2EtaskStatus%20%3D%3D%3D%20Task%2EStatus%2EDueSoon%0A%09%09%09%29%0A%09%09%7D%29%0A%09%09%0A%09%09if%28tasksToProcess%2Elength%20%3D%3D%3D%200%29%7B%0A%09%09%09throw%20%7B%0A%09%09%09%09name%3A%20%22No%20Items%20Matched%22%2C%0A%09%09%09%09message%3A%20%22No%20tasks%20are%20due%20today%2E%22%0A%09%09%09%7D%0A%09%09%7D%0A%09%09matchCount%20%3D%20tasksToProcess%2Elength%0A%09%09%0A%09%09%2F%2F%20CONSTRUCT%20ARRAY%20OF%20OBJECTS%20FOR%20TASKS%0A%09%09itemsArray%20%3D%20new%20Array%28%29%0A%09%09for%20%28i%20%3D%200%3B%20i%20%3C%20tasksToProcess%2Elength%3B%20i%2B%2B%29%7B%20%0A%09%09%09aTask%20%3D%20tasksToProcess%5Bi%5D%0A%09%09%09%2F%2F%20CONSTRUCT%20OBJECT%20FOR%20TASK%0A%09%09%09itemObj%20%3D%20new%20Object%28%29%0A%09%09%09itemObj%2Etitle%20%3D%20aTask%2Ename%0A%09%09%09itemObj%2Esubtitle%20%3D%20%22DUE%3A%20%22%20%2B%20aTask%2EdueDate%2EtoTimeString%28%29%0A%09%09%09itemObj%2Earg%20%3D%20aTask%2Eid%2EprimaryKey%0A%09%09%09itemsArray%2Epush%28itemObj%29%0A%09%09%7D%0A%09%09%0A%09%09%2F%2F%20CONSTRUCT%20JSON%20OBJECT%20CONTAINING%20ITEM%20ARRAY%0A%09%09jsonObj%20%3D%20new%20Object%28%29%0A%09%09jsonObj%2Eitems%20%3D%20itemsArray%0A%09%09%0A%09%09%2F%2F%20CONVERT%20TO%20STRING%20FOR%20INCLUSION%20IN%20URL%0A%09%09jsonStr%20%3D%20JSON%2Estringify%28jsonObj%29%0A%09%09%0A%09%09%2F%2F%20ENCODE%20FOR%20URL%09%09%0A%09%09encodedJSON%20%3D%20encodeURIComponent%28jsonStr%29%0A%09%09%0A%09%09%2F%2F%20ALFRED%20WORKFLOW%20ID%0A%09%09alfredWorkflowID%20%3D%20%22com%2Eomni%2Dautomation%2Eof%2Ealfred%2Dcollection%22%0A%09%09%0A%09%09%2F%2F%20ALFRED%20ACTION%20TRIGGER%20ID%0A%09%09alfredTriggerVar%20%3D%20%22displayResults%22%0A%09%09%0A%09%09%2F%2F%20CONSTRUCT%20URL%20STRING%0A%09%09urlStr%20%3D%20%60alfred%3A%2F%2Fruntrigger%2F%24%7BalfredWorkflowID%7D%2F%60%0A%09%09urlStr%20%2B%3D%20%60%24%7BalfredTriggerVar%7D%2F%3Fargument%3D%24%7BencodedJSON%7D%60%0A%09%09%0A%09%09%2F%2F%20CONVERT%20TO%20URL%20OBJECT%20AND%20EXECUTE%0A%09%09URL%2EfromString%28urlStr%29%2Eopen%28%29%0A%09%09%0A%09%09%2F%2F%20CONSTRUCT%2FSPEAK%20RESONSE%20WHILE%20ALFRED%20DISPLAYS%20RESULTS%0A%09%09taskOrTasks%20%3D%20%28matchCount%20%3D%3D%3D%201%29%3F%20%22task%22%3A%22tasks%22%0A%09%09response%20%3D%20%60%24%7BmatchCount%7D%20%24%7BtaskOrTasks%7D%20due%20today%2E%60%0A%09%09utterance%20%3D%20createUtterance%28response%29%0A%09%09synthesizer%2EspeakUtterance%28utterance%29%0A%09%7D%0A%09catch%28err%29%7B%0A%09%09utterance%20%3D%20createUtterance%28err%2Emessage%29%0A%09%09synthesizer%2EspeakUtterance%28utterance%29%0A%09%09%2F%2Fnew%20Alert%28err%2Ename%2C%20err%2Emessage%29%2Eshow%28%29%0A%09%7D%0A%7D%29%28%29%3B

Constructing the “Tasks Due Today” Alfred Action

The “Tasks Due Today” action will display a linked list of the available OmniFocus tasks that are due today. It works by executing an Omni Automation script, not requiring user-input, that will filter all available OmniFocus tasks to determine those with a due date of today, and then call a second Alfred action to display the results as a linked list in Alfred.

Optionally, this Alfred action can be called using a Voice Control command: “What is due today?”

The Tasks Due Today Alfred action

 1  Keyword Trigger • This trigger step responds to entered words, phrases, or specified key combinations.

 2  Open URL • This step opens the indicated URL.

 3  External URL Trigger • .This trigger step responds to an externally triggered URL in this format:
alfred://runtrigger/workflow-identifier/trigger-identifier

NOTE: Both triggers  1—3  are connected to the same “Open URL” step  2  so that either may begin the action.

Keyword Trigger Preferences

Double-click the “Keyword Trigger” step to summon its preferences window:

Tasks Due Today Keyword Step

 1  Keyword • Enter the word, phrase, or key combination to use as a keyword for the action. If you’re planning on sharing the workflow with others, you can use an Alfred variable {var:variableName} for others to replace.

 2  Argument Status • Since the Omni Automation function requires no user input, leave the setting to: “No Argument”

 3  Title and Subtitle • The title for the Alfred action..

Open URL Preferences

Double-click the “Open URL” step to summon its preferences window:

The Open URL step preferences

 1  URL • The encoded Omni Automation URL is pasted into this field.

 2  Target Application • The application targeted by the URL.

 3  Encoding • Encoding (if any) to apply to variables in the URL. Since the script requires no input, leave the encoding options deselected.

Constructing the “Display Results” Alfred Action

Once a script URL has executed and has performed its processing, it will need to have Alfred display any matching items. To do so, the script will call to an action in the Alfred workflow that is designed to be triggered via an external URL that contains an encoded JSON object containing the items to display in the Alfred interface. See this page for an example of the JSON data.

As shown in lines 62, 65, 68, and 69 of the main script, the response URL is constructed like the following with the highlighted placeholders replaced with the relevant information:

alfred://runtrigger/ALFREDWORKFLOWID/ALFREDTRIGGERID/?argument=ENCODEDJSON

Here is an illustration of the Alfred action for displaying results of an OmniFocus query:

The Display Results Alfred Action

 1  External Trigger • This trigger step responds to an external AppleScript script call or a URL.

 2  Variable Constructor • Saves the passed data into a variable to be read by the following “Script Filter” step.

 3  Script Filter • Reads the passed data and displays it in Alfred as an ordered list filterable via text input.

 4  Open URL action • Receives the unique OmniFocus ID of the chosen item and constructs and executes a URL link that will cause OmniFocus to select and display the item.

Double-click the External Trigger step to summon its preferences window:

Preferences for the External Trigger step

 1  Trigger Identifier • The identifier used to call the action either via an AppleScript script or a URL.

 2  Enable External URL • Select this checkbox to enable Alfred to receive an external URL trigger.

 3  URL Example • An example URL generated using the current Alfred workflow ID and action ID. An external URL is always in this format:

alfred://runtrigger/ALFREDWORKFLOWID/ALFREDTRIGGERID/?argument=ENCODEDJSON

Double-click the “Variable Constructor” step to summon its preferences window:

The preferences window for the Variable Constructor step

 1  Variable Name • The name for the variable that will store the data passed by the external trigger.

 2  Variable Value • {query} is a special variable used by Alfred to indicate user input. During the action execution it will be replaced with the decoded JSON, generated by the other action’s script and passed as an argument in the external triggering URL.

Double-click the “Script Filter” step to summon its preferences window:

The Display Results action Script Filter step preferences

 1  Scripting Environment • Uses the default system UNIX zsh environment to execute the provided shell script.

 2  Shell Script • This simple shell script simply returns the content of the passed in variable containing the JSON passed into the action by the external URL.

 3  Alfred Filtering • Enable Alfred to filter the results content and display it within the Alfred window.

 4  Matching Preferences • Summons the “Match Preferences” window.

 5  Matching Options Menu • Set this option to the default option of: “Word Matching in Any Order”

Double-click the “Open URL” step to summon its preferences window:

The Display Results action Open URL step preferences

 1  OmniFocus Item Link • The {query} variable will contain the unique identifier for the chosen OmniFocus task. This ID is appended to the standard URL for targeting an OmniFocus task or project: omnifocus://task/ When the URL is opened by the action, OmniFocus will come forward and display the selected task.

 2  Target Application • The application targeted by the URL.

 3  Encoding • Since the URL contains no custom data beyond the already URL-compatible unique ID of the task, no further encoding is necessary.

Running the Alfred Command

To use the Tasks Due Today action, summon Alfred and enter the keyword you entered during the workflow setup. The default is: tsk

Tasks Due Today action in Alfred

A list of tasks due will be presented. Navigate the list and press the return key to choose the task to have displayed in OmniFocus for you.

The Voice Control Command

Because of Alfred’s ability to have actions triggered externally via URLs, it works well with Apple’s built-in Voice Control technology.

DOWNLOAD an example voice commands file that you can easily import and use.

Importing a voice commands file in Voice Control

In the Accessibility system preference pane  1  select the Voice Control  2  option. From the “Advanced Options” menu  3  select the option to “Import Custom Commands…” menu option and choose the provided voice commands file.

The “What is due today?” Voice Control Command

A new voice command will be added:

 1-2  Command Title • The command title is the phrase you speak to trigger the command.

 3  Scope • The availability of the command will be within any application.

 4  Command Action • This command’s action will be to open the provided URL.

 5  URL • The URL to open is the Alfred external URL targeting this Alfred action’s external trigger step:

alfred://runtrigger/com.omni-automation.of.alfred-collection/tasksDue

Next Example

In the NEXT EXAMPLE, Alfred is used to launch an Omni Automation script that requires user input within Alfred in order to perform a procedure in OmniFocus that returns a list of linked objects to Alfred for display.