Unable to detect Application Launch or Quit for Time Machine app

Hello,

I want to trigger two macros based on when /Applications/Time Machine.app launches or quits.
However, Keyboard Maestro does not seem to detect the launch or quit of Time Machine app.


Usecase

I use an application called Amethyst which automatically tiles windows. However, Time Machine becomes unsuable when Amethyst is running, so I was hoping to quit Amethyst when Time Machine app lauches and start it again when Time Machine quits. There is a related issue here where the creator of the app mentions that they are unable to detect Time Machine app window.


Things I have checked

  1. I have looked through the forum for any related advice but only found this post which led me to confirm that the macro group itself is not the problem as seen below.

  2. The Macro Group which contains these two macros has no conditional activation. Snapshot of the macro group below:

    image


  3. The Macros do fire if I use some other application (such as iTerm) as the trigger. In this test, if I activate iTerm, first one macro fires and then if I quite iTerm the other macro fires, as expected.

Macros

Launch Amethyst when TimeMachine Quits Macro (v11.0.3)

Launch Amethyst when TimeMachine Quits.kmmacros (2.4 KB)


Quit Amethyst when TimeMachine Activates Macro (v11.0.3)

Quit Amethyst when TimeMachine Activates.kmmacros (3.3 KB)

If the KM trigger doesn't trigger on it, I may know the reason, (some apps have multiple processes, and KM is probably triggering on a different one) but the solution would be to use polling on the correct process name.

Thank you very much. I did think of polling, but I can't seem to activate any other Window (like Activity Monitor) or shell (iTerm etc) to be able to see the process list when Time Machine App is running. I guess I could work around this, but SSH'ing into this machine from another and then using ps or such to see the list.

I was hoping to avoid polling as Amethyst should be quit before launching Time Machine.

The next workaround would be to create a script that does this (i.e. something like killall Amethyst && open "/Applications/Time Machine.app") however, I thought KM would be able to handle this elegantly. Have never come across an application that KM is not able to detect at all.

I'm pondering your problem. Everything takes time(machine).

1 Like

Hello @azorpheunt :wave:

TimeMachine is a Special kind of ApplicationBundle based on the Process Architecture of macOS.

There is only one option that comes into my mind on how you can make your workflow a reality.

Therefore you’ll need EventScripts by Mousedown Software (AppStore). If you want to know more about what you could also do with this Application, visit the Website.

Greetings from Germany :de:

Tobias

1 Like

Well your solution doesn't actually quit Amethyst before Time Machine launches. Your solution quits Amethyst slightly AFTER Time Machine Launches. And polling would achieve the same result. So I don't see a big problem there.

1 Like

That is very true. I agree that my desired outcome would not have been achieved by the macros posted above in any case.

Thank you very much, I'll give this a go.

1 Like

I found a way to do that, and oddly, there appears to be NO DIFFERENCE that I could see in the process list when Time Machine is running vs when it's not running. There are two processes that I could see related to Time Machine, which run all the time, and there's no difference when you launch the app itself.

So this raises a more fundamental question. If Time Machine is always running, what do you mean by: "I want to trigger two macros based on when /Applications/Time Machine.app launches or quits." That statement seems to me to have no meaning.

EDIT: I have an idea that might work. But it will take me an hour, because I need to get something to eat. My idea involves polling and might impart a 1% degradation on your CPU usage, so I'm not sure if you will accept that.

EDIT: Yes, my idea is working. Wow. But I'm still going to make some food while I ponder the reliability of my solution. (My solution might have false positive and false negatives, but I'm not sure yet.)

Hello Airy (@Airy):wave:

EventScripts by Mousedown (the Link is shared above) has a Trigger for Background Applications like TimeMachine and many many others … this way EventScripts is the best way to go.

I use it by my self to prepare my Mac for TimeMachine and many other Background Application‘s ….

Greetings from Germany :de:

Tobias

This was very interesting. Thank you!

Apparently I had already bought EventScripts (but had no memory of it). I have tried my best, but no joy.

Here is what I did.


Created an apple script and attached it to all possible 'Application Launch' events


The Args passed by all of the above events is the same, subset of the args is as follows:



The applescript

This script creates a log file on ~/Desktop/EventScripts_Log.txt and keeps appending to it for every execution.

-- EventScripts Logging Utility
-- Configuration flags
property showDialog : false -- Controls whether to show dialog (default: false)
property enableLogging : true -- Controls whether to log to file (default: true)
property logFileName : "EventScripts_Log.txt" -- Name of the log file

-- Main handler - gets called when event triggers
on run eventArgs
	-- Format message from event data
	set logMessage to formatMessage(eventArgs)
	
	-- Show dialog if enabled
	if showDialog then
		display dialog logMessage buttons {"OK"} default button "OK" with title "Event Triggered"
	end if
	
	-- Log to file if enabled
	if enableLogging then
		logToFile(eventArgs, logMessage)
	end if
end run

-- Function to format message for display and logging
on formatMessage(eventArgs)
	-- Start with basic message
	set logMessage to "EventScripts Parameters:

"
	
	-- Try to get trigger
	try
		set triggerValue to trigger of eventArgs
		set logMessage to logMessage & "trigger: " & triggerValue & "
"
	end try
	
	-- Try to get applicationName
	try
		set appName to applicationName of eventArgs
		set logMessage to logMessage & "applicationName: " & appName & "
"
	end try
	
	-- Try to get bundleCode
	try
		set bundleCode to bundleCode of eventArgs
		set logMessage to logMessage & "bundleCode: " & bundleCode & "
"
	end try
	
	-- Try to get applicationPath
	try
		set appPath to applicationPath of eventArgs
		set logMessage to logMessage & "applicationPath: " & appPath & "
"
	end try
	
	-- Try to get processIdentifier
	try
		set processID to processIdentifier of eventArgs
		set logMessage to logMessage & "processIdentifier: " & processID & "
"
	end try
	
	-- Try to get scriptPath
	try
		set scriptPath to scriptPath of eventArgs
		set logMessage to logMessage & "scriptPath: " & scriptPath & "
"
	end try
	
	-- Try to get tagName
	try
		set tagName to tagName of eventArgs
		set logMessage to logMessage & "tagName: " & tagName & "
"
	end try
	
	return logMessage
end formatMessage

-- Function to create timestamp
on getTimestamp()
	set currentDate to current date
	set dateString to (year of currentDate as string) & "-" & (month of currentDate as integer as string) & "-" & (day of currentDate as string) & " " & (time string of currentDate)
	return dateString
end getTimestamp

-- Function to log to file
on logToFile(eventArgs, logMessage)
	-- Get timestamp for log entry
	set dateString to getTimestamp()
	
	-- Get event type
	set eventType to "Unknown"
	try
		set eventType to trigger of eventArgs
	end try
	
	-- Get app name if available
	set appName to "Unknown"
	try
		set appName to applicationName of eventArgs
	on error
		-- App name not available for this event type
	end try
	
	-- Create full log entry
	set completeLogEntry to dateString & " - Event: " & eventType & " - " & appName & "
" & logMessage & "
-------------------
"
	
	-- Get desktop path for log file
	tell application "Finder"
		set desktopPath to desktop as string
	end tell
	
	set theFile to desktopPath & logFileName
	
	-- Write to file with error handling
	try
		appendToFile(theFile, completeLogEntry)
		
		-- Show notification on successful write
		display notification "Log updated for event: " & eventType with title "EventScripts Log" subtitle "Saved to desktop"
	on error errMsg
		-- Show error even if dialogs are disabled (important for debugging)
		display dialog "Error writing log: " & errMsg buttons {"OK"} default button "OK" with icon caution
	end try
end logToFile

-- Function to safely append to a file
on appendToFile(filePath, textToAppend)
	set fileExists to false
	
	-- Check if file exists
	tell application "System Events"
		if exists file filePath then
			set fileExists to true
		end if
	end tell
	
	if fileExists then
		-- File exists, read contents first
		set fileRef to open for access filePath
		set currentContent to read fileRef
		close access fileRef
		
		-- Now write everything back with the new content appended
		set fileRef to open for access filePath with write permission
		set newContent to currentContent & textToAppend
		write newContent to fileRef starting at 1
		close access fileRef
	else
		-- Create new file
		set fileRef to open for access filePath with write permission
		write textToAppend to fileRef
		close access fileRef
	end if
end appendToFile

Results

tl;dr: No joy

Sample content of this log file with my testing showing that each of the event types are detected:

-------------------
2025-3-26 11:19:16 - Event: Background application launched - ScriptMonitor
EventScripts Parameters:

trigger: Background application launched
applicationName: ScriptMonitor
bundleCode: com.apple.ScriptMonitor
applicationPath: /System/Library/CoreServices/ScriptMonitor.app
processIdentifier: 85387
scriptPath: /Users/az/Library/Application Scripts/net.mousedown.EventScripts/debug-app-events.scpt
tagName: 

-------------------
2025-3-26 11:19:18 - Event: Application activated - Finder
EventScripts Parameters:

trigger: Application activated
applicationName: Finder
bundleCode: com.apple.finder
applicationPath: /System/Library/CoreServices/Finder.app
processIdentifier: 39321
scriptPath: /Users/az/Library/Application Scripts/net.mousedown.EventScripts/debug-app-events.scpt
tagName: 

-------------------
2025-3-26 11:19:18 - Event: Background application launched - ScriptMonitor
EventScripts Parameters:

trigger: Background application launched
applicationName: ScriptMonitor
bundleCode: com.apple.ScriptMonitor
applicationPath: /System/Library/CoreServices/ScriptMonitor.app
processIdentifier: 85424
scriptPath: /Users/az/Library/Application Scripts/net.mousedown.EventScripts/debug-app-events.scpt
tagName: 

-------------------
2025-3-26 11:19:22 - Event: Application will launch - VimR
EventScripts Parameters:

trigger: Application will launch
applicationName: VimR
bundleCode: com.qvacua.VimR
applicationPath: /Applications/VimR.app
processIdentifier: 85443
scriptPath: /Users/az/Library/Application Scripts/net.mousedown.EventScripts/debug-app-events.scpt
tagName: 

Final Results

This log file is not updated and the script is not fired for any of the events (including my most hopeful candidate 'Background App Launch') when I launch Time Machine app.

This was a very interesting exploration, even if it didn't work this time around. Thank you.

That's amazing and thanks for pointing that out. I came up with a KM-only solution that involves polling. I just finished a meal and I'll try to test it before I present it here.

Thank you very much @Airy and @Nr.5-need_input , I have a solution that works fine. I had to resort to monitoring the runningboard log entries after discovering how macos Sequoia launches apps thanks to this link.

Final result

Script

#!/bin/bash

# Time Machine Launch and Quit Detector
# This script monitors for Time Machine launches and quits using macOS logs

echo "Starting Time Machine launch/quit detector..."
echo "Press Ctrl+C to exit"

# Track the last events to prevent duplicate notifications
LAST_LAUNCH_DETECTED=0
LAST_QUIT_DETECTED=0
COOLDOWN_SECONDS=10

# Set app state tracker
APP_RUNNING=false

# We'll control Amethyst directly with AppleScript
# No script paths needed

# Function to handle launch events
handle_launch() {
    CURRENT_TIME=$(date +%s)
    
    # Only trigger if we haven't recently detected a launch
    if (( CURRENT_TIME - LAST_LAUNCH_DETECTED > COOLDOWN_SECONDS )); then
        LAST_LAUNCH_DETECTED=$CURRENT_TIME
        APP_RUNNING=true
        
        # Get current date/time for logging
        TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
        
        echo "==============================================="
        echo "Time Machine launch detected at $TIMESTAMP"
        echo "==============================================="
        
        # Quit Amethyst
        osascript -e 'tell application "Amethyst" to quit'
        
        echo "Amethyst has been quit"
        
        # Display a notification
        osascript -e 'display notification "Time Machine launched and Amethyst quit" with title "Time Machine Monitor"'
        
        echo "Launch handled, waiting for events..."
    fi
}

# Function to handle quit events
handle_quit() {
    CURRENT_TIME=$(date +%s)
    
    # Only trigger if app was running and we haven't recently detected a quit
    if $APP_RUNNING && (( CURRENT_TIME - LAST_QUIT_DETECTED > COOLDOWN_SECONDS )); then
        LAST_QUIT_DETECTED=$CURRENT_TIME
        APP_RUNNING=false
        
        # Get current date/time for logging
        TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
        
        echo "==============================================="
        echo "Time Machine quit detected at $TIMESTAMP"
        echo "==============================================="
        
        # Launch Amethyst
        osascript -e 'tell application "Amethyst" to activate'
        
        echo "Amethyst has been launched"
        
        # Display a notification
        osascript -e 'display notification "Time Machine quit and Amethyst launched" with title "Time Machine Monitor"'
        
        echo "Quit handled, waiting for events..."
    fi
}

# Use the full path to log to avoid shell builtin conflicts
/usr/bin/log stream --predicate '(subsystem == "com.apple.runningboard") AND (eventMessage CONTAINS "com.apple.backup.launcher")' | while read -r line; do
    # Check for launch events
    if echo "$line" | grep -q "Launch request"; then
        handle_launch
    fi
    
    # Check for quit/termination events
    if echo "$line" | grep -E "process (exited|terminated)|assertion invalidated|no longer tracked|Process .* exited|Removing"; then
        handle_quit
    fi
done

Corresponding Launch Agent

<?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">
<dict>
    <key>Label</key>
    <string>com.user.timemachine-amethyst-monitor</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/az/Dropbx/scripts/tm-amethyst-manager.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardErrorPath</key>
    <string>/tmp/timemachine-amethyst-monitor.err</string>
    <key>StandardOutPath</key>
    <string>/tmp/timemachine-amethyst-monitor.out</string>
</dict>
</plist>
2 Likes

Hi, @azorpheunt. I don't have time at the moment to carefully follow this thread, but you can use AppleScript or shell commands to return the state of Time Machine. For more information, refer to: Time Machine Assistant

1 Like

Well, if your solution works, I won't have to test my solution. But in case anyone wants to see my ridiculous solution, here's an image of it...

Basically I'm polling with OCR to see if the bottom quarter of the screen contains the Time Machine buttons, and nothing else. Believe it or not, it seems to work.

P.S. I probably should have added semaphore to block duplicate copies from running at the same time.

1 Like

Thank you very much @_jims

I did come across this thread, however my impression was that this is more to do with Time Machine Backups being performed rather than the Time Machine application launching/quitting.

The AppleScript does determine the status. For example, I just ran the macro and the following appeared:

1 Like

Hello @azorpheunt :wave:

I’ve carefully checked after everything you’ve said that you’ve done …

Based on what I’ve seen you did a fabulous job workin out getting the desired information out of the triggers - but if you don’t use the Information to trigger your Macros then you’re right and don’t get any value out of it.

Your shell script and LaunchAgent approach might be a good idea though but it is nothing else then the same EventScripts is doing, despite the fact you’ll have to write a lot of LaunchAgents, when you‘re polling using shell scripts on every Application.

For me this would be quite, too much work.

Greetings from Germany :de:

Tobias

Just had a look at the script, and I believe that it detects whether Time Machine Backups is currently in progress.

To confirm, I executed the applescript contained in the macro. At this point the /Applications/Time Machine.app was not running. The script reported something similar to your snapshot showing that the Time Machine is not running:


Now I manually started a Time Machine backup and executed the script again, and it showed:

At this point the Time Machine Backup was running and the apple script expectedly reports this. However the Time Machine App was not running.

My quick look at the script, seems to imply that it checks the mounted volumes and metadata to try and detect if TimeMachine volumes are mounted ...

So for my use case of detecting whether the Time Machine app (not backups) is launched or quit, I don't think this script would work.

1 Like

Thank you @Nr.5-need_input

However, based on what I've seen (and posted above) EventScripts is unable (just like Keyboard Maestro) to detect the launch (even 'Background') of Time Machine app. I welcome you to use the applescript I posted above and check this yourself.

I think there is something special about the 'Time Machine.app' in that it is not a normal macOS app. I am not a macOS developer so am not certain about this, but the inability of EventScripts and KM to detect Time Machine launch/quit seems to add support to this conjecture.

I think EventScripts (and my goto application for such use case KM) does work for 'normal' apps, but not for this specific usecase (Time Machine.app) which needs special handling.