Use variable on set action timeout

Hi everyone,

Is there a way to use variables on a specific action timeout? I tried using it %VARIABLE%variablename% but it didn't work

Screenshot 2025-02-19 at 11.32.23

No. Its a numeric field, so it would be just variablename.

But no, it does not accept calculations.

You could have a macro you execute asynchronously as a subroutine and pass in the current instance and how long to wait, and then it would just wait and then cancel the macro. You'd need to use a global variable, or perhaps an instance variable that the executed macro reads in order to tell it that the actions have finished and not to cancel the macro, so it would be a bit tricky to get right.

1 Like

So variables would still work just not calculations?

Noted on adding a subroutine. I'm just wondering if there's a simpler solution before I start manually adding macros and get it complicated.

No, variables won't work. When he said "it's a numeric field, so it would just be variable name," what he meant was that if it did accept variables, like you were asking, it WOULD be without the token "%Variable%%" which you were offering as a solution. (Maybe he could have worded that better, to be honest.)

Like you, I have found a few places where I would like variables to work. For example, the fuzz factor in Image actions, or the progress bar value in the Display Progress Bar action, or the Display flag in image actions.

But there is a workaround, to an extent. If you really want something to accept a variable, you can just create any variable and use it in a Switch/If statement for a certain number of fixed values. Here's a simple example using the Display flag on a Find Image statement. You could do something like this: (in this example, notice that the Display option is checked differently)

image

That is how I work around the issue. You can do the same thing for the timeout value on any action. Here's how I would solve that: (in this example, the timeout values for each of the five Find Image actions would be set to the values 20, 40, 60, 80 and 100.)

image

Yes, this method limits you to a finite set of values, but sometimes a limited number of values is all you need. For example, there are only twelve common function keys, (and you can't use a variable to indicate which function key to send to an application) and if I want to store the value of a function key like this: "F1", "F2",... "F12" then I can store the keys as those strings and create a Switch action that presses each different function key based on the string variable value.

image

The above example shows how to solve the problem where KM doesn't allow you to store a keyboard key name into a variable.

In the case of a numeric value, like the example above where I use the variable "TimeoutValue" there's a small improvement you can make. You see, you may want an empty string to be treated as the value zero. The simplest way to solve that is to modify the action to do this: (notice how I'm prepending a "0" character to the beginning of the number value. This ensures that an empty variable is treated like the value 0.)

image

Let me lament about the main thing that hinders me that doesn't support a variable field. That is this: I can't find a way to use a variable to store the name of a KM named clipboard. I really really really need this feature. For example, I would frequently use an action like this: (which I just cut/pasted together.)

The hypothetical action above would copy an image from a named clipboard that is named by the contents of the variable "VarName" and place a copy of that image into an image named TempImage. This looks fairly easy to implement and would be a HUUUGE benefit to me.

2 Likes

Challenge accepted :wink:

I thought there'd be a problem, since Named Clipboards appear to be referenced by UUID, not name. XML of a "Paste from Named Clipboard" action includes:

		<key>Action</key>
		<string>Paste</string>
		<key>MacroActionType</key>
		<string>ClipboardSwitcherMacroAction</string>
		<key>Name</key>
		<string>F5D6D23F-73D9-4428-A9B8-4C7E6D2599FB</string>
		<key>RedundandDisplayName</key>
		<string>ZZ test</string>

But the Engine seems to happily accept the display name without the Name UUID, working things out for you. Obviously that would break if you had two Named Clipboard with the same name -- hopefully you don't!

Here's a POC showing how to copy to, then paste from, a variable-determined Named Clipboard. Make sure you have a TextEdit doc open with the insertion point somewhere you don't mind pasting at, switch to another app, select some text, run the macro:

Named Clipboard Using Variable.kmmacros (4.6 KB)

Image

You might be able to use a similar trick to implement variable-set time outs, although you'd have to generate the entirety of the action with AS...

1 Like

Thanks for accepting the challenge.

I'm trying to understand if there is a misunderstanding between us. Your script does something, but I'm unclear what it's doing.

Here is what I want to accomplish. This is a before and after schematic:

As the above shows, I want an image copied from an image whose name is specified in some text variable called VarName into a different named clipboard image.

Your solution isn't putting any image into the specified destination clipboard. Your solution is placing text into the destination clipboard. (I can't figure out where it's getting the text from.)

However now that I'm explaining it, I think it would be 10% better if both source and destination clipboards are specified in variable names. Is that doable?

Try it with a variable set to a name that doesn't match an existing Named Clipboard. You'll see that the macro both creates that Named Clipboard and copies the selected data to it, then switches to TextEdit and pastes from that Named Clipboard.

Bung in a long pause between the copy and paste actions and you can open KM Settings and edit the Named Clipboard to prove to yourself the paste isn't from the System Clipboard...

All it really shows is that you can combine the variable with the template XML to create the action on the fly, and the Engine can select the correct Clipboard to use based on name and without the UUID.

To move things between Named Clipboards just use the XML from the "Copy Named Clipboard to Named Clipboard" action, replacing names with variables in the same way:

Copy Between Named Clipoards.kmmacros (3.3 KB)

Image

AppleScript
set inst to system attribute "KMINSTANCE"
tell application "Keyboard Maestro Engine"
	set sourceClip to getvariable "Local_sourceClip" instance inst
	set targetClip to getvariable "Local_destClip" instance inst
	
	set theXML to "<dict>
		<key>MacroActionType</key>
		<string>CopyClipboard</string>
		<key>SourceNamedClipboardRedundantDisplayName</key>
		<string>" & sourceClip & "</string>
		<key>SourceUseNamedClipboard</key>
		<true/>
		<key>TargetNamedClipboardRedundantDisplayName</key>
		<string>" & targetClip & "</string>
		<key>TargetUseNamedClipboard</key>
		<true/>
	</dict>
"
	do script theXML
end tell

Before:

After:

Using your black magic I was able to replicate your result and get one image clipboard to be copied to another image clipboard. This is mind-blowing. :exploding_head: I think this means I can now write macros and/or subroutines that are passed a name of an image-based clipboard, which is what I've needed for many years. This is a huge deal for me. I've been needing to pass images to macros for years. This will change what KM can do for me. Sooooo.... thanks!!!! :grinning: :smile: :heart_eyes: :star_struck: :kissing_heart: :crazy_face:

I should have come to you sooner. I didn't know this might even be possible.

I will probably start a new thread once I've mastered this procedure, to document how to pass images to subroutines.

EDIT: (3 hours later) I've managed to create subroutines that receive variables that point to named clipboard images. I would still prefer local clipboards, in the same manner that local variables exist, but I think I can deal with this new way of thinking. I've had to use semaphores to ensure that asynchronous copies of macros do not conflict with each other, but that's not too hard to handle.

Neither did I, until about 3 hours ago :wink:

Note that this is well outside expected/documented use, so may get broken by a KM update...

Thanks for the help everyone. People here in the forum are really helpful, and I love how everyone is active in solving problems and trying different things to achieve something. It’s really inspiring to get creative with making macros in Keyboard Maestro. Thanks again!

3 Likes

So, having 5 minutes to spare, I can say the answer is "sort of" -- at least so far...

You can have an action with a timeout set by variable, but it doesn't cancel the original macro after the timeout. Demo using the "Copy" action -- you can run it directly with the Editor's run button:

  • First with something selected to show the "Copy" still happens
  • Then with nothing selected to see the timeout is only 1 second, you get the "Cancel" notification, but the "Display Text" happens so this macro didn't cancel
  • Then, still with nothing selected, change the value of Local_timeout to show the action's timeout does indeed change

Variable Timeout Demo.kmmacros (4.0 KB)

You'll see that, timeout or no, the AS returns missing value so we can't test that to fix the cancellation issue. But I'm guessing you could do so by adding a "Return" or variable-setting action to the XML that would only execute when the copy didn't timeout, then test the result in the "main" macro.

Whether it's worth jumping through all these hoops is another matter! Why do you need variable timeouts? There may be an easier solution if the problem is approached from a different direction...

I don't know why he wants them (it's a good question) but here's why I want them:

  • I can globally change all my timeout values in a single action, without having to manually edit 100 actions every single time that I want to change the timeouts. I would probably set all my timeout values to a handful of variable names like TimeoutBlink (0.1 sec), TimeoutBreath (2 sec), TimeoutDinner (15 min), TimeoutSleep (8 hours).
  • If a variable timeout implies that the timeout field will become visible in the action rather than hidden in the cogwheel, it would make it much easier to show those settings when taking any screenshot of an action. It takes a lot of extra time to take screenshots of the settings under the cogwheel.

And, in the spirit of The Five Why's...

Why do you want to do that? "Pauses" I can understand (and they already take a variable), but not timeouts.

Your list almost reads like these are "user response" timeouts -- if so and they're single-action then the above trick won't be too bad to do. I'd be more worried about grouped actions where you'd have to convert multiple actions to XML, with the obfuscation and maintenance burden that would cause.

In order to find out "why" I would probably need to survey all my current uses of that feature. I know I've used it hundreds of times, but is there a way to use the KM Editor to show all the macros (and hopefully highlight the actions) where timeout values are being set?

No -- I still don't understand.

You must know why you'd want to set a whole load of timeouts to 8 hours, for example. "I've an action that I'll happily wait 7 hours and 59 minutes for, but never more than 8 hours!" obviously has some thought behind it.

And I'm still not seeing an actual use -- are you saying you've a case for "Today I shall eat quickly, so I will set TimeoutDinner to only 12 minutes so that any stuck actions will abort sooner than usual"?

I don't think that's implied at all -- merely that the timeout dialog's numbers-only field could become a calculation field, capable of processing a variable.

No, I don't know why. I did know why when I wrote my code, but who has a perfect memory?

Of course! But there's no way for me to remember all my thoughts. I write about 1000 macros per year, and I can't remember all my reasoning for every action in all those macros. My memory is not as good as yours. I have said many times you are a super genius and you probably have a super memory, but I don't.

But I'm pretty sure that my most common timeout value is 0.01 seconds. So the variable I would use the most is TimeoutBlink.

You miss the point -- you said:

You obviously have a reason to want to do such a thing. And a feature request with a compelling reason is far more likely to be fulfilled than a "wouldn't it be neat if...".

And mine is 99 hours -- the default. And I suspect that the same is true for the vast majority of actions for the vast majority of people.

So I'm also trying to get behind the reasoning behind so many "custom" timeouts. Not because I'm being picky, but because they might be tricks I can "borrow" :wink:

My reasons for using different timeouts are based on real-world events that take different durations which I am choosing not to reveal. While I hold you in (almost) the same level of esteem as I hold The Architect, I won't reveal what my macros do. Let's call it a trade secret.

No worries, I don't trust me either :wink:

That still leaves the reason(s) for being able to change timeouts en masse via a variable. "Yesterday this real-world event should only have taken 1s so I gave up after 2, today it should take less than 2s so I'll give up after 3" sounds interesting, but I'm struggling to see any practical application that wouldn't be more easily solved with either state checks and flow control or simply setting it to always be 3s!

But I'm well aware of

  1. My own short-sightedness
  2. Your ability to come up with interesting solutions to interesting problems

...so really I'm just hoping to learn (steal!) a trick or two...

In an attempt to add something useful, here's an update to the previous AS-based macro that includes a return value so you can test to see if the "wrapped" action completed or timed out. Run it with and without some text selected to see the difference.

Variable Timeout Demo (with result).kmmacros (5.9 KB)

Image

You can see the changes are easy enough -- since we've gone from a single to more than one action in the XML it is now an <array> of <dict>s, so add your extra action's XML then wrap the whole in array tags. The text returned by the "Return" action is saved in theResult, which the AS then returns to KM. You don't need those two lines, you can do it one with return (do script theXML), but that ends the AS and you may want to do something between the embedded macro completing and the script finishing.