How to Parse the right Macro UUID out of the KM Macros Plist File for external Script call using AppleScript?

Hello Folks

I have many external Triggers for my Macros - especially BetterTouchTool Gestures.

Editing each AppleScript that is attached to any of these Gestures - which is looking like this

-- ignoring application responses
tell application "Keyboard Maestro Engine"
	do script "FC17C8F7-01FC-4D39-A1E1-193C2623ADC4"
	-- or: do script "Yoink - Helper"
	-- or: do script "FC17C8F7-01FC-4D39-A1E1-193C2623ADC4" with parameter "Whatever"
end tell
-- end ignoring

is very time consuming because I would have to copy each AppleScript Trigger from a specific Macro for the Gesture's AppleScript to work. Especially when ReInstalling KM.

My Thoughts here are writing AppleScripts for such External Triggered Macros only once and then never have to think about them.

My Goal here is to parse the right Macro UUID for a Macro by first breaking down all Macros to only a specific Macro Group using the Macro Group's Name and then getting the Macro UUID by using the Macros Name.

something like that


use script "KurtysKMLib"

set theMacroGroup to "My Example MacroGroup"
set theMacro to "My awesome Macro"

set theUUID to KurtysKMLib's getMacroUUID(theMacroGroup, theMacro)

-- ignoring application responses
tell application "Keyboard Maestro Engine"
	do script theUUID
	-- or: do script theUUID with Parameter theParam
end tell
-- end ignoring

If anyone of the AppleScripters here could help me with this task that would be great.
please note that I want to use a Script Library which I am working on at the moment.

Greetings from Germany

Tobias

To be clear: You know the name of the macro, you know the name of the macro group, and you want to get the UUID of the macro then run it?

Does:

set theMacroGroup to "My Example MacroGroup"
set theMacro to "My awesome Macro"

tell application "Keyboard Maestro" to set theUUID to id of the first item of (every macro of (first item of (every macro group whose name is theMacroGroup)) whose name is theMacro)
tell application "Keyboard Maestro Engine" to do script theUUID

...work for you?

1 Like

Hello @Nige_S

Yes that is exactly what I am after....

Thank you for the Code I will try if it works in that way I want to use it - I didn't think that getting a Macro's UUID is so easy....

Greetings from Germany

Tobias

Control click on the macro, Copy as UUID.

Hello @peternlewis

thanks for that reminder - unfortunately I use a Macro for that when I'm in the Editor.

I can run it with and without a selection before running it - pretty nifty - but that is not my use case here....

I have Macros triggered by FolderAtionScripts, KarabinerElements, BetterTouchTool Gestures and other Scripting Utilities and I want to write the AppleScript Code only one Time and then forget about it.

The AppleScript Code should get the UUID of a Macro - regardless of how many times the UUID will change in the future.

My Intention is also to write the code wrapped in a try - on error routine and with a Prompt for opening the exact Script in Script Debugger, revealing it in the Finder and a copy option for the Error Message to the Clipboard

Greetings from Germany

Tobias

This will make it depends on Keyboard Maestro Editor (which must be running) to get the UUID. Is there a way to get UUID using the engine instead ?

1 Like

Damn .... @macdevign_mac thanks for pointing that out....

I knew that there must be something missing....

the first code from @Nige_S will be implemented anyway into my Library's Handler using a check if KM is running. should be quicker if it is running instead of parsing the KM Plist.

But there is still the missing part for using KM Engine....

By the way ..... there must be a way using ASObjC retrieving the Information without KM Engine since it is a Plist.

Greetings from Germany

On the other hand, I wonder if the macro name is unique, if so why do you need UUID to run the script if you can just use the macro name to run the script, as you are passing the name anyway ?

I know that it is sometimes hard to identify what macro group the macro belongs to. So I normally set a code for macro group name eg Experiment [EXP], then all the macro names reference the group code eg [EXP] Retrieve Data for Analysis, and this will also make the macro unique by macro group.

For example, as for my case
image

I written a post on that macro group code, probably might be useful

1 Like

Well, that is a good point... I sue the Macro Name to identify its UUID - all belonging to the Macro Group it is in - because of the fact that UUID's are unique and are also used by the url scheme.

It gives you the exact flexibility you need - especially if you have an installer Macro for a Group of Macros which are linked together by the Execute a Macro Action. It works in the same way to create them on the fly with the right Macro in use.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I have my Macro Groups for Apps named like this

Prefix) _AppName_( ) - where the Prefix is App at the Icon is the corresponding App Icon.

Greetings from Germany

Tobias

Yes, but it's hardly a heavyweight process. It also doesn't need to be the frontmost application, won't be activated, and so won't get in the way of whatever's happening. And it will be much faster than parsing KME's getmacro data object (which returns all macros -- and anyone doing this sort of thing probably has a lot!).

If you don't have KM running all the time you could always launch-then-hide as part of the AppleScript.

Otherwise you'll have to parse your /Library/Application Support/Keyboard Maestro/Keyboard Maestro Macros.plist file somehow. A quick look suggests that (non-smart) Groups all have the entries

			<key>KeyCode</key>
			<integer>32767</integer>
...
			<key>Name</key>
			<string>Your Group Name</string>

Each Group then has an array of dictionaries, one dictionary per macro, and each dictionary has a section that's

					<key>Name</key>
					<string>debugger test</string>
					<key>Triggers</key>
					<array/>
					<key>UID</key>
					<string>D7FE9F8C-E41E-4213-ADE5-66ED8111B844</string>

So if your plist processing skills are up to it you should be able to get what you need. But again -- this will be relatively slow if you have a lot of macros.

It would seem to be a lot less work to make sure that each of your macros had a unique name, and simply call it using that!

Alternatively -- how often do macro UUIDs actually change? They don't when you simply edit. I guess they must for any macro you import. If you're in control of when changes happen, why not also do a full dump of all your macros {GroupName,MacroName,UUID} in a format you can directly use in your AppleScript?

I take it back -- this was both easier and quicker than I thought. At least, for my relatively small number of macros...

You'll have to decide for yourself how to deal with duplicate Group and Macro names -- as is, this will use the first exact match for the Group name, then search that for the first exactly matching macro name.

set theGroupName to "Group Name"
set theMacroName to "Macro Name"
set theFile to "~/Library/Application Support/Keyboard Maestro/Keyboard Maestro Macros.plist"

tell application "System Events"
	tell property list file theFile
		set macroGroups to value of property list item "MacroGroups"
	end tell
end tell

set tmp to ""
repeat with eachItem in macroGroups
	if |name| of eachItem is theGroupName then
		set tmp to Macros of eachItem
		repeat with eachMacro in tmp
			try
				if |name| of eachMacro is theMacroName then return UID of eachMacro
			on error
				-- do nothing
			end try
		end repeat
	end if
end repeat

if tmp = "" then
	return "No such group as " & theGroupName
else
	return "No such macro as " & theMacroName & " in the " & theGroupName & " group"
end if

Works with limited testing -- returns the UUID if the macro is present, returns an error if either the Group or Macro name isn't an exact match.

Enjoy!

2 Likes

Hello @Nige_S

Launching and then hiding KM as part of the Script - that is something I prefer to avoid I will use your provided Code to write something that will look if KM is already running (frontmost or in the background) and search for the particular UUID and if is not running then just parse from the Macro Plist.

Using that approach - just in my opinion - is even better then to write some code that will bring me back to where I was if I would launch and hide KM during a Script.

And each line of code I don't have to run or each App is not needed to be active saves Battery life on a MacBook

I know UUID's change only if I reinstall a Macro. Maybe someday I will build something for me that sets up everything correctly but unfortunately there will be in my case some skills I don't have yet - and even I don't have the time for doing something like that in the near future.

thanks for the Code - looks quite promising to me .... now I have to deal with that putting it all together into an AppleScript Handler for my Library.

by the way I've tested the code - It works but it is really damn slow ... my 2019 iMac needs for one UUID to search 1min 52 sec

550 Groups ~ 1800 Macros

that is indeed very slow - I've read anywhere (maybe in this Forum) that sometimes it is worth trying to avoid SystemEvents and use AppleScript Text Item Delimiters instead - because it is dramatically faster or just can be...

Could tis task eventually be done using TID's ??
or maybe using ASObjC ??

Greetings from Germany

Tobias

The AppleScript above only makes one call using System Events, and that's because it is the easiest way to convert a plist into an AppleScript list.

The real problem is that plists aren't made to be searched -- open up the KME plist in a text editor to see why. You'll have to process the plist into a searchable format -- I've used lists, but the way you search those is one item at a time until you get a match. As you've seen, that doesn't scale well.

If you are talking about re-processing the plist each and every time you want to run a macro then seriously reconsider running KM, hidden, all the time. Use Activity Monitor to see how low KM's "Energy Impact" actually is -- I reckon you'll save battery, not waste it, compared to plist processing.

Even better would be to give your macros unique names and simply use those in the AppleScript. You're using the group and name in your proposed workflow anyway, simply change every macro name to <groupname-macroname> and you're done! And you should even be able to AppleScript the renaming process...

That is total clear to me - the quick and dirty method....

I know the Macros Plist is not very searchable by its Format. Plists in general not very searchable.
there must be another way getting this done because what I really need to get is the UUID ...

As you know I am working on a Script Library for KM. I am even working on some Macros based on the Spotlight Search Prompt whose need Plist Parsing as well because these Macros should generate a List based only on the Macros I have enabled in the current frontmost Application - whose could change in its Number based on Other applications I have in the Background.

The List will have this Format:

Macro Name=Macro UUID

Of course there are some to strip out - but that is not the case. The case here is that if I call a Macro from one of these Prompts the Result is the UUID.

That is like the other one an example and of course reason number 3 why I need to get the UUID for a specific Macro.

I really appreciate your knowledge - especially for AppleScript and of course all your help.

Greetings from Germany

Tobias

This is the bit that is confusing me.

Anything based on Group Name & Macro Name -> UUID -- the search you are proposing -- will only work if Group names are unique and Macro names are unique within a Group. And if that's the case then either calling KM with Macro name of Group Name or renaming all your macros in the format Group name - Macro name and calling them by name via KME will be both much faster and much less resource intensive than repeatedly parsing the plist.

What does using the UUID get you that neither of those do?

Edit to add:
Even if you parse the KM plist into AppleScript as a record using AS-ObjC's NSDictionary, MacroGroups is still a list of macro groups, each containing a list of macros -- so no different to the AppleScript previously posted. I sure you could write a utility in Swift or similar that "properly" processed the plist into an quickly searchable data structure, but that's so far beyond my ability that my brain hurts just thinking about it...

In this case I'll need to parse the Plist for all enabled Macros and put their Names and UUID's into that format. The Spotlight Search Prompt will then show me the parsed list of Macros using their Names but each result Button of that Prompt has to deal with the UUID.

That means for example I can go to the chosen Macro of the SSP for Safari in the Editor because I use KMs URL Scheme - which is build to use the UUID of a Macro.

If I parse the UUID I will always have an exact match - where it is not always needed to have Unique Names because the combination is the key. that means I can have 50 in 50 Groups with exactly the same Name using 1 Macro per Group - and I always will get this one I want to have because filter the Whole thing down to one Group - no matter if the Group contains 2 or 2000 Macros.

And since every Macro Group I have has a Unique name there will be no problems.

maybe the whole thing sounds more clear to you now ..... I tried my very best after know 32 hours without sleep....

I will go to bed now....

Greetings from Germany

Tobias

Yes, it does. I hadn't realised that "Spotlight Search Prompt" was something from the Macro Library. I still don't really understand what you're doing (or why!) but that's more my lack of experience with KM.

Given you want to use that library, it looks as though you will need to create your data in a certain format. I think you'll only get reasonable performance if you use a global (persistent) variable (or read in a file every time), updating it whenever you add/delete a macro, rather than generating it on the fly every time you need. Whether you generate the data from KM using AppleScript or by parsing the plist will be a matter of which you're most comfortable with.

Hello Nige

I've found a clipping in my Script Debugger Clippings which was originally in use of an AppleScript written by Chris Stone (@ccstone)

This little piece of Code returns a list of all my Macro Groups in 0.62 seconds.
it uses the plutil and sed command line tools - and perhaps you've got it - I am wondering if

  • this approach can be used
  • and how the heck it could be done

here is the little piece of code I am speaking of:

set kmMacrosFile to POSIX path of ((path to application support from user domain as text) & "Keyboard Maestro:Keyboard Maestro Macros.plist")

set shCMD to "
plutil -convert xml1 -o - " & quoted form of kmMacrosFile & "\\
| sed -En '/^			<key>Name</{
	n;
	s![[:blank:]]*</?string>!!g
	p
}' \\
| sort -f
"
set kmGroupList to paragraphs of (do shell script shCMD)

maybe you could help me with this ?
maybe you can help me understanding every bit of this code correctly, too - or Chris if he jumps in - because don't understand all of it at the moment...

perhaps Chris will jump in here, too - we'll see...

Greetings from Germany

Tobias

Put simply, it's using the fact that in the XML version of the plist the "Name" key for each Macro Group (and no other "Name" key) is indented by 3 tabs and the name of the Macro Group is in the next line. That's relatively easy to parse.

Unfortunately for you, the Group Name (which you want to use) is near the end of each Group's dictionary and the macros in the group are listed in an array before that. And each macro is in its own dictionary, again with the name near the end and with the UUID (your ultimate goal) as the last entry.

See the plist for yourself by typing plutil -convert xml1 -o - ~/Library/Application\ Support/Keyboard\ Maestro/Keyboard\ Maestro\ Macros.plist | less into Terminal and paging through with the space bar. You can then grok the format and work out how to parse out the bits you need.

For completeness, the AppleScript version of Chris's script would be something like this -- although it doesn't sort the names because sorting an AS list structure is a PITA!

set theFile to "~/Library/Application Support/Keyboard Maestro/Keyboard Maestro Macros.plist"

tell application "System Events"
	tell property list file theFile
		set macroGroups to value of property list item "MacroGroups"
	end tell
end tell

set nameList to {}
repeat with eachItem in macroGroups
	copy |name| of eachItem to end of nameList
end repeat

return nameList
1 Like

Okay .... given this information and the fact that every Group has its own Dictionary in the Macro Groups Array I then may need to. use. 5 tabs instead of three - is that right ?

and if that is right - how do I have to define the loop for finding the right Dictionary and then getting the UUID of the found one?

I definitely need help with this .... I have no idea how to accomplish that...

Greetings from Germany

Tobias