How to Get the Selected Action's Information (Name, Id, Macro) in KM Editor Using AppleScript/JavaScript

I'm looking for Applescripts & JavaScripts in the forum that are able to retrieve the currently selected Action in KM Editor to do the following:

  • Get selected Action's name, ID, macro.
  • Navigate and highlight that Action into view from using Action ID (or other means).

It is probably in the forum but I'm unable to find relevant details on that. The feature "Select Last Aborted Action" shows that it can be useful to navigate to that action not just the macro.

Anyone has clue on how to do that programmatically?

Thanks.

I've done a lot of digging re getting macro/action information with AppleScript for my Checkpoint utilities. That's a KM debugging aid. The following macro just uses AppleScript to display the name of the currently-selected action in whatever macro is open in the KM editor.

Test display currently-selected action name.kmmacros (2.6 KB)

This was going to be the start of the section of my Checkpoint utility that navigates to and highlights the currently-running action, but on asking Peter Lewis I found that was not possible to do. However, if you run the AppleScript in Script Debugger, you can see all the information about the macro and its actions is available for you to extract and use as you wish.

This is a bit of a rabbit-hole I'm afraid... and my example is just the tip of the iceberg but might get you started.

1 Like

tiffle,
thank for that.

OK. I have managed to get information I needed from Dan Thomas.
thank to Dan Thomas and Tiffle.

2 Likes

Hi, Tiffle or others,

Just check with you if you know how to get information about action ?
Like running the following script in Applescript (the following didn't work)

tell application "Keyboard Maestro"
get name of action "11119" -- from actionId or actionName
end tell

I try to figure out the API but I could not understand how to retrieve action info given its ActionId or name based on the doc.

image

Hi @macdevign_mac,
the example AppleScript I gave you shows the way, but because it's AppleScript it could be somewhat confusing as to what's going on. I have to say it took a month to "discover" how to do this stuff with KM.

Anyway, I've taken that example and added comments to it so you can better follow what's going on.

I also didn't say that to run this example the following must be true:

  1. The KM Editor must be open and visible
  2. One macro must be selected.
  3. At least one action must be selected.

If these conditions are not met then the script won't actually do anything.

In pseudocode, the script does this:

Exit if the KM Editor is not visible

Exit if zero or two or more macros are selected

Loop through each selected action in the selected macro
   Get the action's properties into selAction
   Tell selAction to provide its name and id
   Display the action's name and id in a dialog
Repeat Loop

You should note that this example will not properly expose actions embedded within, for example, a KM IF action - that requires some recursion which is too complex to go through for this example.

I hope this comes closer to answering your question.

Here's the example script for you. You can unzip it and run it directly inside Script Debugger (which is infinitely better than Apple's Script Editor) or embed it in an Execute AppleScript action. It's better to run it in Script Debugger as then you can step through it and examine the values of everything as you go and that's basically how I discovered how to do this stuff.
Example.scpt.zip (491.8 KB)

(*
Example script to show how to get information about actions
in a macro.

The KM Editor must be open and one macro must be selected for
this to run; otherwise it just exits.

You must also select at least one action as this example
loops over the actions that are selected. So if no action is
selected, nothing will happen.

In this example, the name of each selected action and its ID is
extracted and displayed in a dialog.
*)

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "System Events"
	--	make sure the KM Editor is visible, if not then exit
	if visible of application process "Keyboard Maestro" then
		--display dialog "Visible"
	else
		--display dialog "Not Visible"
		return 0
	end if
end tell

tell application "Keyboard Maestro"
	try
		-- Only one macro must be selected
		-- If there's more than one then exit
		set selMacros to selected macros
		if ((count of selMacros) = 1) then
			
			-- get the list of actions in the selected macro
			set selActionList to get selection
			
			-- loop through all the actions in the selected macro
			repeat with selAction in selActionList
				-- selAction contains all the parameters for the current action
				
				-- make sure the current action is actually an action
				if the class of selAction is action then
					
					-- aID is the ID of the action
					set aID to id of selAction
					
					-- now get the name of the action and its ID and display tem
					tell selAction
						set actionName to its name
						set actionID to its id
						display dialog actionName & " " & actionID
					end tell
				end if
				
				-- keep looping over the actions
			end repeat
		end if
	end try
end tell

EDIT: I just noticed I'd left a line of code in set aID to id of selAction

That's a superfluous line and isn't required in this example. However, aID ends up containing the same value as actionID obtained a few lines further on, so by accident you can see two different but equivalent ways of getting the value of a property of an action using AppleScript.

Here's a JXA example, since I happen to be working on this right now:

(function() {
    'use strict';

    const kmEditor = Application("Keyboard Maestro");
    const selectedMacros = kmEditor.macros.whose({selected: {"=": true}});
    if (!selectedMacros || selectedMacros.length < 1) {
        console.log("No macros selected");
    } else {
        console.log("Selected Macros:");

        // NOTE: Can't use "forEach"
        for (let i = 0; i < selectedMacros.length; i++) {
            const macro = selectedMacros[i];
            console.log(`macro id: ${macro.name()}`);
        }
    }

})();

Note that there is also a "selectedmacros" method, but it only returns the UUIDs of the selected macros.

1 Like

Tho arguably simpler for the user (and certainly much faster at run-time), of course, to replace the for clause and its mutable i fiddling with:

console.log(
    selectedMacros.name().map(
        name => `macro id: ${name}`
    )
    .join("\n")
);

Incidentally, on .forEach

  • I agree with you that it's suboptimal (too many console.log events, when we only need one)
  • but I'm not sure that I agree with you that we "can't" use it:

(A property call like .name() batch-fetches a list of all of the corresponding values from selectedMacros at the cost of a single AppleEvent)


Expand disclosure triangle to view .forEach example
(() => {
    "use strict";

    const kmEditor = Application("Keyboard Maestro");
    const selectedMacros = kmEditor.macros.whose({
        selected: {
            "=": true
        }
    });

    if (!selectedMacros || selectedMacros.length < 1) {
        console.log("No macros selected");
    } else {
        console.log("Selected Macros:");

        selectedMacros.name().forEach(
            name => console.log(`macro id: ${name}`)
        );
    }
})();

It makes sense to me that for each selected macro object, if I accessed the name() property, it would result in an AppleEvent.

But the fact that accessing selectedMacros.name() would result on one AppleEvent blows my mind. How does this even work?

Well, that's always been the core architecture of the osascript interface (and thus the secret of faster code). Hamish S liked to emphasize that it's analogous to a database query. (Hence the .whose || .where)

A traditional pattern would be to make the property name part of the query.

In JS, perhaps:

(() => {
    "use strict";

    const
        kmEditor = Application("Keyboard Maestro"),
        selectedMacroNames = kmEditor.macros.whose({
            selected: {
                "=": true
            }
        })
        .name();

    return 0 < selectedMacroNames.length ? [
        "Selected Macros:",
        selectedMacroNames.map(
            name => `macro id: ${name}`
        )
        .join("\n")
    ].join("\n\n") : "No macros selected";
})();

and in AS:

tell application "Keyboard Maestro"
    name of macros where selected = true
end tell
2 Likes

i.e. the application services the query (preparing the list) and the list is returned to the user side of the osascript interface as part of a single query/response event.

The trick, of course, is to gather a whole list with one query, rather than having to run a separate query for every single individual property value.

The idea of thinking of it as a database query helps, thanks.

1 Like

Tiffle, Dan,

Thank for the sample.


Tiffle,

I have used your original script to get the actionName and action Id successfully. Just that I now need to get actionId of the actions (even if they are not selected) to verify that action exist in KM (because it may get deleted)

Dan,

that 's javascipt of gettiing action name is cool. Just that I need to find a way to verify if action is valid even if they are not selected.


What I have done so far, is that I capture selected Actions and save them into a json file.

But I need a way to verify that actions in the json file are still valid so I think that a general js/applescript code to check if the action is valid is useful.

Something along like that maybe


for Applescript

tell application "Keyboard Maestro" -- or should it be "Keyboard Maestro Engine" if it just to retrieve by actionID ?

get name of action "11119" -- from actionId or actionName

end tell

or for JS


const selectedMacros = kmEditor.macros.whose({actionId: {"=": "78823"}});

You can probably speed it up a bit, if that seems relevant, by batch-fetching both names and ids:

Expand disclosure triangle to view AppleScript
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

-- name and id of selected KM macros
on run
	tell application "Keyboard Maestro"
		tell (macros where selected is true)
			set actionIDs to id
			set macroNames to name
		end tell
	end tell
	
	set xs to {}
	repeat with i from 1 to length of actionIDs
		set end of xs to item i of macroNames & "@@##" & item i of actionIDs
	end repeat
	
	unlines(xs)
end run


-- unlines :: [String] -> String
on unlines(xs)
	-- A single string formed by the intercalation
	-- of a list of strings with the newline character.
	set {dlm, my text item delimiters} to ¬
		{my text item delimiters, linefeed}
	set s to xs as text
	set my text item delimiters to dlm
	s
end unlines





1 Like

In JS we could simplify Dan's query a little, and use a generic zipWith:

(() => {
    "use strict";

    const main = () => {
        const
            selectedMacros = Application("Keyboard Maestro")
            .macros.where({
                selected: true
            });

        return 0 < selectedMacros.length ? (
            zipWith(
                k => uuid => `${k}@@##${uuid}`
            )(
                selectedMacros.name()
            )(
                selectedMacros.id()
            ).join("\n")
        ) : "No macros selected";
    };

    // --------------------- GENERIC ---------------------

    // zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
    const zipWith = f =>
        // A list constructed by zipping with a
        // custom function, rather than with the
        // default tuple constructor.
        xs => ys => xs.map(
            (x, i) => f(x)(ys[i])
        ).slice(
            0, Math.min(xs.length, ys.length)
        );

    return main();
})();
1 Like

I summarize the steps:

let says I have this macro [ TEST Macro ] that contains some actions


Given the following input
Macro Name: TEST Macro
Macro ID: 32344-2342343-234324234432
Action Name: Execute Script
Action ID: 38847

How do I use Applescript/Javascript to validate that action with ActionID 38847 exists ?

Steps:

  1. Check that macro exists by its macroID
  2. If exists, retrieve all actions belonging to that macro. otherwise if macro is invalid, then action is confirm Invalid.
  3. Do Repeat loop to compare each macro's actionID with given Action ID
  4. If exist mean valid, otherwise the action is invalid.

The problem I have is how to translate the above into applescript/javascript.

The context here is:

  • differences between KM versions ?
  • custom actions that may not be locally installed ?

ComplexPoint,

  • to keep it simple. only KM10 is used. Because KM10 has added selectAction which accept actionID to navigate to, so i be using this in KM10.
  • I have verified that custom action has its own unique ActionID (eg I have used custom Action Activate Action By Name and it has a unique ID: 74711 ). So if it is not install, then it is treated as invalid actionID.

Thank to Tiffle, Dan, and ComplexPoint for helping

I have found the solution and modified, on the original code by Tiffle.
The main problem I have initially is getting the action by its ActionID. It seems that there is no direct way to retrieve it, but only from macro object.

So here it is the solution for those who are interested.

tell application "Keyboard Maestro"
	
	set macroId to "334C61D4-4689-4B1E-B432-6D1B88D5867B"
	set actionId to "69217"
	set actionFound to false
	
	set macroList to {}
	set macroList to every macro whose id is macroId -- Any way to retrieve macro without list ?
	
	repeat with macroItem in macroList
		set macroName to name of macroItem
		set actionList to actions of macroItem
		
		repeat with actionItem in actionList
			set curActionId to id of actionItem
			
			if curActionId is actionId then -- pass as lo
				set actionFound to true
				exit repeat
			end if
			
		end repeat
		
	end repeat
	
	display dialog actionFound -- Result
	
end tell

FYI: Don't expect the action's ID to be the same on another computer, even if you exported your macro to a .kmmacros file and loaded it into the other machine.