How to create/import macros into Keyboard Maestro from code?

The KM message is saying that the xml is a valid plist, but not valid KM export structure.

Your plist is an array of Macros.

It needs to be embedded in a macro group under a Macros key. That macro group needs to. be wrapped in an array.

That will tell KM the macro's destination.

In my testing, this is the minimal macro export structure.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
	<dict>
		<key>Macros</key>
		<array>
			<dict>
				<key>Actions</key>
				<array/>
				<key>Name</key>
				<string>Minimal Macro</string>
				<key>Triggers</key>
				<array/>
			</dict>
		</array>
		<key>Name</key>
		<string>Test Group</string>
	</dict>
</array>
</plist>

(0-based numbering)
plist (version="1.0")
└─ array (root array)
└─ [0] dict "Macro Group"
├─ key "Macros" → array
│ └─ [0] dict "Macro"
│ ├─ key "Actions" → array (empty)
│ ├─ key "Name" → string "Minimal Macro"
│ └─ key "Triggers" → array (empty)
└─ key "Name" → string "Test Group"

Import it like this:

AppleScript
--How to create/import macros into Keyboard Maestro from code? - Questions & Suggestions - Keyboard Maestro Discourse
--                 https://forum.keyboardmaestro.com/t/how-to-create-import-macros-into-keyboard-maestro-from-code/50798/2

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

set minimalExportXML to ¬
	 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<array>
	<dict>
		<key>Macros</key>
		<array>
			<dict>
				<key>Actions</key>
				<array/>
				<key>Name</key>
				<string>Minimal Macro</string>
				<key>Triggers</key>
				<array/>
			</dict>
		</array>
		<key>Name</key>
		<string>Test Group</string>
	</dict>
</array>
</plist>"
tell application id "com.stairways.keyboardmaestro.editor"
	importMacros minimalExportXML
end tell

Another the example is here:

This AppleScript will properly wrap and import your xml.

AppleScript
--How to create/import macros into Keyboard Maestro from code? - Questions & Suggestions - Keyboard Maestro Discourse
--                 https://forum.keyboardmaestro.com/t/how-to-create-import-macros-into-keyboard-maestro-from-code/50798/2

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

set arrayOfMacroDicts to "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
  <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" 
  \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
  <plist version=\"1.0\">
  <array>
  <dict>
  	<key>Activate</key>
  	<string>Normal</string>
  	<key>Name</key>
  	<string>zed: LMCache</string>
  	<key>Triggers</key>
  	<array/>
  	<key>UID</key>
  	<string>2BE3EA71-D7DC-4EE5-9EB8-C8B7875E6CB2</string>
  	<key>Actions</key>
  	<array>
  		<dict>
  			<key>MacroActionType</key>
  			<string>IfThenElse</string>
  			<key>TimeOutAbortsMacro</key>
  			<true/>
  			<key>Conditions</key>
  			<dict>
  				<key>ConditionList</key>
  				<array>
  					<dict>
  						<key>ConditionType</key>
  						<string>AnyWindow</string>
  						<key>AnyWindowConditionType</key>
  						<string>EndsWith</string>
  						<key>AnyWindowTitle</key>
  						<string>LMCache</string>
  						<key>IsFrontApplication</key>
  						<false/>
  						<key>Application</key>
  						<dict>
  							<key>BundleIdentifier</key>
  							<string>dev.zed.Zed</string>
  							<key>Name</key>
  							<string>Zed</string>
  							<key>NewFile</key>
  							<string>/Applications/Zed.app</string>
  						</dict>
  					</dict>
  				</array>
  				<key>ConditionListMatch</key>
  				<string>All</string>
  			</dict>
  			<key>ThenActions</key>
  			<array>
  				<dict>
  					<key>MacroActionType</key>
  					<string>ManipulateWindow</string>
  					<key>Action</key>
  					<string>SelectWindow</string>
  					<key>Targeting</key>
  					<string>WindowNameContaining</string>
  					<key>TargetingType</key>
  					<string>Specific</string>
  					<key>WindowName</key>
  					<string>LMCache</string>
  					<key>TargetApplication</key>
  					<dict>
  						<key>BundleIdentifier</key>
  						<string>dev.zed.Zed</string>
  						<key>Name</key>
  						<string>Zed</string>
  						<key>NewFile</key>
  						<string>/Applications/Zed.app</string>
  					</dict>
  				</dict>
  			</array>
  			<key>ElseActions</key>
  			<array>
  				<dict>
  					<key>MacroActionType</key>
  					<string>ExecuteShellScript</string>
  					<key>DisplayKind</key>
  					<string>Window</string>
  					<key>HonourFailureSettings</key>
  					<true/>
  					<key>IncludeStdErr</key>
  					<false/>
  					<key>Path</key>
  					<string></string>
  					<key>Source</key>
  					<string>Nothing</string>
  					<key>Text</key>
  					<string>open -a /Applications/Zed.app ~/fork-i/LMCache/LMCache</string>
  					<key>TimeOutAbortsMacro</key>
  					<true/>
  					<key>TrimResults</key>
  					<true/>
  					<key>TrimResultsNew</key>
  					<true/>
  					<key>UseText</key>
  					<true/>
  				</dict>
  			</array>
  		</dict>
  	</array>
  </dict>
  </array>
  </plist>"
--Deserialize the array of macros 
set macroDicts to (current application's NSString's stringWithString:arrayOfMacroDicts)'s propertyList()
--embed the array of macros into the Macros key of a Macro Group dict
set groupDict to {Macros:macroDicts, |Name|:"tests"}
--wrap the Macro Group dict in an Array
set groupsArray to {groupDict}
--convert back to xml
set exportXML to serializePlistObject(groupsArray)

tell application id "com.stairways.keyboardmaestro.editor"
	importMacros exportXML
end tell

on serializePlistObject(plistObject)
	set {theData, theError} to current application's NSPropertyListSerialization's dataWithPropertyList:plistObject format:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(reference) -- don't use binary format
	if theData is missing value then error (theError's localizedDescription() as text) number -10000
	set plist to (current application's NSString's alloc()'s initWithData:theData encoding:(current application's NSUTF8StringEncoding)) as text
end serializePlistObject

(If memory serves, when KM exports macros, it wraps each macro in a macro group individually, but in practice you can put an array of Macros that share the same destination into one Macro Group.

3 Likes