Is there a way that I can find out if a macro is Enabled and Active (i.e. it can be executed), from JXA?
Better still, can I check to see if multiple macros are Enabled and Active, from JXA?
I’m doing something similar to “Execute Macro by Name”, and I only want to show macros that actually can be executed. And preferably, I’d like it to be fast.
I don’t mind having to parse plists, or anything else down-and-dirty.
Unless @peternlewis has a different solution, which I kind of doubt because I suspect this is the correct procedure, I found the answer:
I call Keyboard Maestro Engine’s “getmacros” scripting function, and parse it to check a macro’s “active” and “enabled” properties, which are up-to-date for each macro.
Dan, since the "active" status of a macro is dynamically updated by the KM Engine in real time, do you think the entire macro list would be continuously updated vs just keeping a list of active macros?
As you note, getmacros will return info about all currently active macros.
You can also use the Macro condition to test if a macro is active. The two sides of the If could set a variable, or otherwise inform whatever you are doing.
Peter, I must be misunderstanding something. The way I read the results of the getmacros command, it is returning inactive macros as well, as shown by these two lines:
<key>active</key>
<false/>
I see quite a few lines like these.
What am I missing?
Is this supposed to be only the list of active macros, or all macros?
###Script
tell application "Keyboard Maestro Engine"
set macroList to getmacros with asstring
end tell
It's all macros. You have to look at each macro's "active" and "enabled" properties.
It appears that if the group's active is false, the macro's active is also false, so you don't need to check the group's active property, unless you want to know that info.
I've got all sorts of JXA code for working with all this, if you need it. I understand the desire to write stuff myself, as well as the times I can't be bothered, so it's cool to me either way.
Dan, thanks for confirming. That is what I am seeing also, but since Peter said "all currently active macros", it made me question what I was seeing. Hopefully he will clarify.
So, according to the KM Wiki article, Macro Activation, a macro cannot be active unless both the macro and group are enabled. Is that consistent with what you are seeing?
Of course I'd love to see and use your code -- no rush though, as I don't have any immediate needs. Although, if you have any generic JXA functions for reading and converting .plist files/data to JS objects/arrays, that would be great.
I've always preferred to start as high up on the food chain as I can, building on the existing code of others rather than reinventing the wheel. While some programmers have a NIH complex, it's never been an issue for me.
Good luck with your project. I can't wait to see what you will come up with next.
I guess I lied a little. I don't think the list shows the Group's "active" state, only "enabled". However, each macro's "active" is correct, based on whether the Group should be active or not.
Here's some code. Run it through the debugger and it will stop on the "debugger" statement, and you can look at the contents of "plist".
(function() {
'use strict';
var KMEngine = (function() {
var _engineApp;
return {
convertStringToPlist: function(str) {
return ObjC.deepUnwrap(
$.NSPropertyListSerialization.propertyListWithDataOptionsFormatError(
$(str).dataUsingEncoding($.NSUTF8StringEncoding), 0, 0, null));
},
getEngineAppName: function() {
return "Keyboard Maestro Engine";
},
getEngineApp: function() {
if (!_engineApp)
_engineApp = Application(this.getEngineAppName());
return _engineApp;
},
getMacros: function(binary) {
return this.getEngineApp().getmacros({
asstring: !binary
});
},
getMacrosAsPlist: function(binary) {
return this.convertStringToPlist(this.getMacros(false));
},
getVariable: function(name, required) {
var result = this.getEngineApp().getvariable(name);
if (!result && required)
throw Error("Variable '" + name + "' is empty");
return result;
},
setVariable: function(name, value) {
this.getEngineApp().setvariable(name, {
to: value
});
},
};
})();
function getKMMacroAndGroupByMacroUUID(macroUUID, plist) {
var result;
plist.find(function(group) {
var macros = group.macros;
if (!macros) return false;
var macro = macros.find(function(m) {
return m.uid === macroUUID; });
if (macro) {
result = { macro: macro, group: group };
return true;
}
return false;
});
if (!result)
throw Error("Could not find info for Macro '" + macroUUID + "'");
return result;
}
function run() {
var macroUUID = "7E71EB71-13A6-4E96-A4A5-ACD8503D1330";
// var macroUUID = KMEngine.getVariable("kmfamMacroUUID", true);
var plist = KMEngine.getMacrosAsPlist();
debugger
// var match = getKMMacroAndGroupByMacroUUID(macroUUID, plist);
// console.log(match.group.uid)
// KMEngine.setVariable("kmfamGroupUUID", match.group.uid);
return "OK";
}
try {
return run();
} catch (e) {
return "Error: " + e.message;
}
})()
Thanks, Dan. Looks like some great stuff here, but it'll take me a while to understand it all.
One question: Is the convertStringToPlist function a generic function to convert any Plist XML to JS arrays, or is it designed specifically for the getmacros command?
I had totally forgotten that none of that code was commented - I'm re-doing how I comment things, and, well, there you go.
convertStringToPlist converts a string to a Plist object. The string can be any standard Plist XML, either a single "dict" or an array of "dict"s. You could even read a plist text file from disk and use it here, but there's other better ways of doing that.
The result isn't really a Plist object - there isn't such a thing in JXA. It's actually more like an object literal, or an array of object literals. You can think of the result as either a set of name/value pairs, possibly nested, or an array of sets of name/value pairs.
tell application "Keyboard Maestro Engine"
set activeMacros to gethotkeys asstring true ¬
with getall
end tell
####Partial Results
<?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>name</key>
<string>Custom HTML Prompt Fixed Header and Footer using Flexbox Examples (Dan)</string>
<key>sort</key>
<string>Custom HTML Prompt Fixed Header and Footer using Flexbox Examples (Dan)</string>
<key>uid</key>
<string>B040B250-40D8-4C38-B813-27E34E4FDD9B</string>
</dict>
<dict>
<key>name</key>
<string>Custom HTML Prompt with Dynamic Size and Position (Dan)</string>
<key>sort</key>
<string>Custom HTML Prompt with Dynamic Size and Position (Dan)</string>
<key>uid</key>
<string>F5D06F09-E511-4201-8DD1-4A9FDB8E3AEE</string>
</dict>
</array>
I am surprised that you use uid rather than UUID used almost everywhere else. I think this can lead to a lot of confusion and scripting errors.
May I suggest that you provide a scripting command called "getActiveMacros".
Even though the "gethotkeys" command has a parameter of "getall" that is:
retrieves all active macros, not just hot key and typed string triggered ones
It is buried in the SDEF, and not intuitive to be part of the command "gethotkeys"
Thanks for supporting scripting. I realize that we are probably penetrating the scripting interface in more detail than ever before, so there are bound to be a few glitches.
I've often wondered why "UID" and not "UUID" in the XML. I'm sure there's a logical explanation.
I should warn you that the results from getmacros are the only results (I think) that have the key names in lowercase. And when it comes to using the results, the keys are case-sensitive (at least in JXA).