×

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 fourth automation scenario listed on the main page:

The following documentation details the construction of the fourth Alfred action in the OmniFocus Collection Alfred Workflow. This action is used to generate and display in the Alfred interface a linked list of OmniFocus projects that smart match the user-provided search terms.

Scenario 4: Display the Results of a Smart Search

The following self-invoking script function performs the following procedures:

Smart Search OmniFocus Projects


(async (arg) => { try { function createUtterance(textToSpeak){ AlexID = ( (app.platformName === "macOS") ? "com.apple.speech.synthesis.voice.Alex" : "com.apple.speech.voice.Alex" ) voiceObj = Speech.Voice.withIdentifier(AlexID) voiceRate = 0.4 utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = voiceRate return utterance } var synthesizer = new Speech.Synthesizer() matchedProjects = projectsMatching(arg) if(matchedProjects.length === 0){ messageStr = `No projects were found matching: ${arg}` throw { name: "Empty Search", message: messageStr } } matchCount = matchedProjects.length itemsArray = new Array() for (i = 0; i < matchedProjects.length; i++){ aProject = matchedProjects[i] switch(aProject.status) { case Project.Status.Active: var projectStatus = "Active" break; case Project.Status.Done: var projectStatus = "Done" break; case Project.Status.Dropped: var projectStatus = "Dropped" default: var projectStatus = "OnHold" } itemObj = new Object() itemObj.title = aProject.name itemObj.subtitle = `Status: ${projectStatus}` itemObj.arg = aProject.id.primaryKey itemsArray.push(itemObj) } jsonObj = new Object() jsonObj.items = itemsArray jsonStr = JSON.stringify(jsonObj) encodedJSON = encodeURIComponent(jsonStr) alfredWorkflowID = "com.omni-automation.of.alfred-collection" alfredTriggerID = "displayResults" urlStr = `alfred://runtrigger/${alfredWorkflowID}/${alfredTriggerID}/?argument=${encodedJSON}` URL.fromString(urlStr).open() projectOrProjects = (matchCount === 1)? "project":"projects" response = `${matchCount} ${projectOrProjects} matched.` utterance = createUtterance(response) synthesizer.speakUtterance(utterance) } catch(err){ utterance = createUtterance(err.message) synthesizer.speakUtterance(utterance) //new Alert(err.name, err.message).show() } })(argument);

Using the Omni Automation Script URL Constructor encode the script and append the following argument parameter to the encoded results:

&arg=%22{query}%22

The {query} variable is used by Alfred to represent the default content passed between steps. During the action execution it will automatically replaced with the user-provided content or results.

Encoded Script URL with Argument


omnifocus://localhost/omnijs-run?script=%28async%20%28arg%29%20%3D%3E%20%7B%0A%09try%20%7B%0A%09%09function%20createUtterance%28textToSpeak%29%7B%0A%09%09%09AlexID%20%3D%20%28%0A%09%09%09%09%28app%2EplatformName%20%3D%3D%3D%20%22macOS%22%29%20%3F%20%0A%09%09%09%09%22com%2Eapple%2Espeech%2Esynthesis%2Evoice%2EAlex%22%20%3A%20%0A%09%09%09%09%22com%2Eapple%2Espeech%2Evoice%2EAlex%22%0A%09%09%09%29%0A%09%09%09voiceObj%20%3D%20Speech%2EVoice%2EwithIdentifier%28AlexID%29%0A%09%09%09voiceRate%20%3D%200%2E4%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%20voiceRate%0A%09%09%09return%20utterance%0A%09%09%7D%0A%09%09var%20synthesizer%20%3D%20new%20Speech%2ESynthesizer%28%29%0A%0A%09%09matchedProjects%20%3D%20projectsMatching%28arg%29%0A%0A%09%09if%28matchedProjects%2Elength%20%3D%3D%3D%200%29%7B%0A%09%09%09messageStr%20%3D%20%60No%20projects%20were%20found%20matching%3A%20%24%7Barg%7D%60%0A%09%09%09throw%20%7B%0A%09%09%09%09name%3A%20%22Empty%20Search%22%2C%0A%09%09%09%09message%3A%20messageStr%0A%09%09%09%7D%0A%09%09%7D%0A%09%09%0A%09%09matchCount%20%3D%20matchedProjects%2Elength%0A%09%09%0A%09%09itemsArray%20%3D%20new%20Array%28%29%0A%09%09for%20%28i%20%3D%200%3B%20i%20%3C%20matchedProjects%2Elength%3B%20i%2B%2B%29%7B%20%0A%09%09%09aProject%20%3D%20matchedProjects%5Bi%5D%0A%0A%09%09%09switch%28aProject%2Estatus%29%20%7B%0A%09%09%09%09case%20Project%2EStatus%2EActive%3A%0A%09%09%09%09%09var%20projectStatus%20%3D%20%22Active%22%0A%09%09%09%09%09break%3B%20%0A%09%09%09%09case%20Project%2EStatus%2EDone%3A%0A%09%09%09%09%09var%20projectStatus%20%3D%20%22Done%22%0A%09%09%09%09%09break%3B%0A%09%09%09%09case%20Project%2EStatus%2EDropped%3A%0A%09%09%09%09%09var%20projectStatus%20%3D%20%22Dropped%22%0A%09%09%09%09default%3A%0A%09%09%09%09%09var%20projectStatus%20%3D%20%22OnHold%22%0A%09%09%09%7D%0A%0A%09%09%09itemObj%20%3D%20new%20Object%28%29%0A%09%09%09itemObj%2Etitle%20%3D%20aProject%2Ename%0A%09%09%09itemObj%2Esubtitle%20%3D%20%60Status%3A%20%24%7BprojectStatus%7D%60%0A%09%09%09itemObj%2Earg%20%3D%20aProject%2Eid%2EprimaryKey%0A%09%09%09itemsArray%2Epush%28itemObj%29%0A%09%09%7D%0A%09%09jsonObj%20%3D%20new%20Object%28%29%0A%09%09jsonObj%2Eitems%20%3D%20itemsArray%0A%09%09jsonStr%20%3D%20JSON%2Estringify%28jsonObj%2C%20null%2C%202%29%0A%09%09%0A%09%09encodedJSON%20%3D%20encodeURIComponent%28jsonStr%29%0A%09%09alfredWorkflowID%20%3D%20%22com%2Eomni%2Dautomation%2Eof%2Ealfred%2Dcollection%22%0A%09%09alfredTriggerVar%20%3D%20%22displayResults%22%0A%09%09urlStr%20%3D%20%60alfred%3A%2F%2Fruntrigger%2F%24%7BalfredWorkflowID%7D%2F%24%7BalfredTriggerVar%7D%2F%3Fargument%3D%24%7BencodedJSON%7D%60%0A%09%09URL%2EfromString%28urlStr%29%2Eopen%28%29%0A%09%0A%09%09projectOrProjects%20%3D%20%28matchCount%20%3D%3D%3D%201%29%3F%20%22project%22%3A%22projects%22%0A%09%09response%20%3D%20%60%24%7BmatchCount%7D%20%24%7BprojectOrProjects%7D%20matched%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%28argument%29%3B&arg=%22{query}%22

Constructing the Alfred Action

The Alfred action for this example will generate and display in the Alfred interface a linked list of OmniFocus projects that smart match the user-provided search terms. It will be comprised of two steps:

Alfred action with input and response

 1  Keyword Trigger • The word, phrase, or characters defined by the user as the trigger to summon this action in Alfred.

 2  Open URL • After encoding a dn inserting the search string provided by the user, this step opens the URL causing the execution of the Omni Automation script in OmniFocus.

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

Keyword Trigger Preferences

 1  Keyword • Enter the keyword, phrase, or character combination to use as the keyword. If you’re planning on sharing this action with others, enter an Alfred variable that the user can replace during setup: {var:projectSearch}

 2  Argument Status • Since this script will require input from the user, select this checkbox and choose the “Argument Required” option from the menu.

 3  Title and Subtext • Enter the action title and subtext as you want them to appear in the Alfred interface.

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

Open URL step preferences

 1  Script URL • Copy and paste the encoded script with appended argument into this field.

 2  Argument Parameter and Variable • Note that Alfred’s default variable for passing data is included with the encoded argument parameter for the encoded script: {query}

 3  Target Application • The application to execute the passed URL.

 4  Encoding • Select the option to encode the contents of the {query} variable. When the Open URL step is executed, the variable will automatically be replaced with an encoded version of the user input.

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 “Smart Match Projects Search” action, summon Alfred and enter the keyword you entered during the workflow setup. The default is: sp Press the Return key and a list of matched projects will be displayed in OmniFocus:

The search input and results in Alfred

A list of matching projects will be presented. Navigate the list and press the return key to choose the project to have displayed in OmniFocus for you.

NOTE: as with all Omni Automation scripts, the initial execution of an external script must be approved by the user fully scrolling the script security dialog (see below) and manually selecting the “Run Script” button. Select the checkbox to enable the script to be executed again without requiring further approval.

Script Security Dialog

Next Example

In the NEXT EXAMPLE, Alfred is used to launch scripts written in Apple’s built-in JavaScript for Automation (JXA) that will display lists of projects and tags.