Only run if X time has passed

I'm trying to build a macro that only runs if XX amount of time or more has passed. Can someone explain why this macro doesn't work?

Even though it says the calculation is true (see image) it always runs the "otherwise execute the following actions" section. This doesn't make sense to me.

Only run if XX time has passed.kmmacros (3.3 KB)

Well, NOW is a function - see the KM wiki page: function:NOW [Keyboard Maestro Wiki]

So you need to use NOW()

Using it the way you have is referencing a global variable which will by default have a value of an empty string, unless you've assigned it a specific value somewhere else.

Also, NOW() returns the time in unixtime so your other variable needs also to be in unixtime.

Sort out these issues and you can then progress with the conditional issue.

Thx for the quick reply!

Actually, I removed the parentheses at some point to see if it would fix the error. Now I've added them back in, but it doesn't seem to change anything. Likely because I'm making some other kind of error.

Anyway, your answer led me to think it was probably better not to have to make a calculation with the NOW() function. So instead, I built a new version of the macro that stores NOW() as a number before doing the calculation. That seemed to work.

Only run if XX time has passed 2.kmmacros (3.4 KB)

In the first attempt you're always setting TimeToRun to NOW() + 10, then immediately testing if NOW() > TimeToRun. It never will be (unless you get a >10 second "beachball" at exactly the right point in your macro's execution!).

You fixed it in your second macro by storing the LastRunTime after a successful execution and comapring your "modified NOW()" against that to see if it's time to execute again -- good job!

Now you know that it wasn't the calculation that was the problem, see if you can re-write it without the first two actions...

I love looking for alternate approaches to all problems. So here's an alternate approach. At the end of your macro, insert this statement:


And then at the beginning of your macro, insert this statement:


...using these options... (the two boxes need to be unchecked)...

What this will do is make sure your macro always pauses for 5 seconds at the end of running, and the semaphore at the top will make sure it never runs at the same time as another copy of the same macro.

The other solution is fine too, but practicing this method will give you a better understanding of how semaphores work, which is educational.

This method has no variables and no functions!


Make a lot of sense! Thanks. Now I was able to simplify it even further as you suggested :slight_smile:

I like it!

But I must point out that, as written, it doesn't do quite what OP is trying to do. His macro cancels any repeat run within the stated period, while yours delays execution until the end of that period.

If OP runs his macro 3 times in quick succession there will be only 1 execution -- with your method there will be 3 executions with a 5 second interval between each. Each has merits, depending on the situation and requirements.

You are right, and I misunderstood the requirement. I think there's a way to modify my idea, and if I can adjust it, I'll post the solution here a little later.

Yes, it can be done. Here's what the macro looks like. This will cancel itself if it didn't FINISH more than 5 seconds ago. The second semaphore uses the default timeout values. The first one has to have its options set to the values of the screenshot below the actions. Perhaps this is getting too complicated for some people, but that's in the eyes of the beholder. I still prefer this over using a variable and a function.

If you don't need a fine-grained time-scale, and the nearest minute suffices,
you can create a cron string triggered macro at run time.

( executing the XML with the Keyboard Maestro Engine .doScript engine )

That second lock is unnecessary -- the first is in force throughout the macro (no explicit "Unlock" action), so any new instance created will insta-quit if the macro is already running.

But it still doesn't necessarily do what the OP wants -- for example, if this was running and you cancelled all macros you could run it again before you should be able to. Which is why a calculation involving a persistent global is proably the better bet.

Yes, I agree.

Well, maybe that's true, but he didn't actually say that, although I guess that's a better interpretation of his words than mine. But I concur your code is more error-proof.