Porting timer widget from the Touch Bar to a floating menu

Can somebody help to adapt the Simple timer Widget for the Touch Bar mentioned in this thread to work as a floating menu, ideally putting it together in a preset?

I have extracted the scripts but don't know how to adapt them to the floating menus.

The scripts use for the original widget:

Script extracted from the Touch Bar widget json

tell application "BetterTouchTool"
	
	set remainingSeconds to get_number_variable remainingSeconds
	
	if (remainingSeconds is equal to 99999) or (remainingSeconds is equal to missing value) then --timer is not running and should be started
		
		--ask for the duration via a dialog popup
		set dialogAnswer to display dialog "Please set timer using HH:MM:SS (max. 24 hours):" default answer "00:00:05" --edit this to change the default time
		set answer to text returned of dialogAnswer
		set answerHours to text 1 thru 2 of answer as text
		set answerMinutes to text 4 thru 5 of answer as text
		set answerSeconds to text 7 thru 8 of answer as text
		
		set totalSeconds to (answerHours * 3600) + (answerMinutes * 60) + answerSeconds
		
		set_number_variable remainingSeconds to 99998
		
		repeat totalSeconds times --count
			
			if (get_number_variable remainingSeconds) ≠ 99999 then -- if the variable is 99999, that means the timer was pressed again and should be stopped
				
				set totalSeconds to (totalSeconds - 1)
				
				
				set_number_variable remainingSeconds to totalSeconds
				
				
				refresh_widget "2CA20DFA-8DAC-4E9A-A095-8BCC75ABAD10"
				
				
				
				delay 1
				
			end if
		end repeat
		
		if (get_number_variable remainingSeconds) ≠ 99999 then
			
			repeat 10 times --blink to signal that the time is up
				
				
				--update_touch_bar_widget "2CA20DFA-8DAC-4E9A-A095-8BCC75ABAD10"  background_color 0,0,0,0
				
				
				delay 0.5
				
				
				-- update_touch_bar_widget "2CA20DFA-8DAC-4E9A-A095-8BCC75ABAD10" background_color 255,38,0,255
				
				
				
				delay 0.5
				
			end repeat
		end if
		
	else --timer is running and should be stopped
		set_number_variable remainingSeconds to 99999
		refresh_widget "2CA20DFA-8DAC-4E9A-A095-8BCC75ABAD10"
		
	end if
end tell

Script when widget is pressed

--https://community.folivora.ai/t/simple-efficient-timer-widget/7927

tell application "BetterTouchTool"
	
	set remainingSeconds to get_number_variable "remainingSeconds"
	
	if (remainingSeconds is equal to 99999) or (remainingSeconds is equal to missing value) then --timer is not running and should be started
		
		--ask for the duration via a dialog popup
		set dialogAnswer to display dialog "Please set timer using HH:MM:SS (max. 24 hours):" default answer "00:00:05" --edit this to change the default time
		set answer to text returned of dialogAnswer
		set answerHours to text 1 thru 2 of answer as text
		set answerMinutes to text 4 thru 5 of answer as text
		set answerSeconds to text 7 thru 8 of answer as text
		
		set totalSeconds to (answerHours * 3600) + (answerMinutes * 60) + answerSeconds
		
		set_number_variable "remainingSeconds" to 99998
		
		repeat totalSeconds times --count
			if (get_number_variable "remainingSeconds") ≠ 99999 then -- if the variable is 99999, that means the timer was pressed again and should be stopped
				set totalSeconds to (totalSeconds - 1)
				set_number_variable "remainingSeconds" to totalSeconds
				refresh_widget "2CA20DFA-8DAC-4E9A-A095-8BCC75ABAD10"
				delay 1
			end if
		end repeat
		
		if (get_number_variable "remainingSeconds") ≠ 99999 then
			repeat 10 times --blink to signal that the time is up
				update_touch_bar_widget "2CA20DFA-8DAC-4E9A-A095-8BCC75ABAD10" background_color "0,0,0,0"
				delay 0.5
				update_touch_bar_widget "2CA20DFA-8DAC-4E9A-A095-8BCC75ABAD10" background_color "255,38,0,255"
				delay 0.5
			end repeat
		end if
		
	else --timer is running and should be stopped
		set_number_variable "remainingSeconds" to 99999
		refresh_widget "2CA20DFA-8DAC-4E9A-A095-8BCC75ABAD10"
	end if
	
end tell

Script with the return value

--https://community.folivora.ai/t/simple-efficient-timer-widget/7927

tell application "BetterTouchTool"
	set remainingSeconds to get_number_variable "remainingSeconds"
	
	if (remainingSeconds is equal to 99999) or (remainingSeconds is equal to missing value) then -- the timer is not running
		return "timer"
	else -- the timer is running or is finished
		
		--create return text
		set outputSeconds to ((remainingSeconds mod 3600) mod 60) as integer
		set outputMinutes to (((remainingSeconds - outputSeconds) mod 3600) / 60) as integer
		set outputHours to ((remainingSeconds - outputMinutes * 60 - outputSeconds) / 3600) as integer
		if length of (outputSeconds as string) is equal to 1 then
			set outputSeconds to "0" & outputSeconds as string
		end if
		if length of (outputMinutes as string) is equal to 1 then
			set outputMinutes to "0" & outputMinutes as string
		end if
		
		--return text
		if outputHours is greater than 0 then
			return (outputHours & ":" & outputMinutes & ":" & outputSeconds) as string
		else
			return (outputMinutes & ":" & outputSeconds) as string
		end if
	end if
end tell

Thanks in advance for any help.

So this should ask for the time, then just do a countdown?

Here is a simple timer example:
timer_example.bttpreset (46.2 KB)

The time is input in this format (examples):

1min

1h

1h 10min 5s

10min

30s

image

function parseDuration(duration) {
    const units = {
        h: 3600,
        min: 60,
        s: 1
    };
    
    // Regex to match the number followed by the unit (h, min, s)
    const regex = /(\d+)(h|min|s)/g;
    let totalSeconds = 0;
    let match;

    while ((match = regex.exec(duration)) !== null) {
        const value = parseInt(match[1], 10);
        const unit = match[2];
        totalSeconds += value * units[unit];
    }

    return totalSeconds;
}

function formatDuration(seconds) {
    const hours = Math.floor(seconds / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    const secs = seconds % 60;

    // Pad with zeros to ensure two digits for minutes and seconds
    const formattedHours = String(hours).padStart(2, '0');
    const formattedMinutes = String(minutes).padStart(2, '0');
    const formattedSeconds = String(secs).padStart(2, '0');

    return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
}

async function startTimer() {
	let duration = await get_string_variable("timerTime");
 	let durationInSeconds = parseDuration(duration);
	timer(durationInSeconds);
}

async function timer(durationInSeconds = 10) {

	if(durationInSeconds == 0) {
		const updateDescription = {"BTTMenuItemText": "Time is up!", "BTTMenuItemBackgroundColor": "233, 0, 0, 255"};
   await update_menu_item({"item_uuid": "1D974882-9376-4B09-81FC-E427F79CBD25", "json": JSON.stringify(updateDescription), "persist": true})
   
	} else {

	const updateDescription = {"BTTMenuItemText": formatDuration(durationInSeconds), "BTTMenuItemBackgroundColor": "0, 0, 0, 255"};
   await update_menu_item({"item_uuid": "1D974882-9376-4B09-81FC-E427F79CBD25", "json": JSON.stringify(updateDescription), "persist": true})
   

   setTimeout(() => {
   	timer(durationInSeconds-1);	
   }, 1000);

}
	return "done"
}

Thanks for that, the presets help a lot in understanding how to do things.

In case it helps anybody I have uploaded my own my own attempt below:

Stopwatch.bttpreset (40.6 KB)

it is a very simple stopwatch showing elapsed minutes and it works with a temporary variable written to BetterTouchTool and it changes color at 5, 10 and 15 minutes.

Screenshot 2024-10-12 at 16.26.41

Please leave a comment if you find it useful! :smiley:

2 Likes

Nice!! I’m currently working on documenting all the new scripting possibilities for floating menus with as many examples as possible. I’ll add this example as well

Great, and thank you for a great app and all your effort!