AppleScript
AppleScript Commands and Examples
This page shows how to do common things in Timemator with AppleScript, one task at a time. Every snippet is complete: paste it into Script Editor, change the names in quotes to your own, and click the run button.
New to AppleScript? Start with Using AppleScript with Timemator. For the exact parameters and types of every command, see the AppleScript Reference, or open the dictionary in Script Editor with File → Open Dictionary… → Timemator.
The snippets below use a folder called Acme with the tasks Design and Website. Replace them with your own names.
The basics#
Address the app#
Every script talks to Timemator inside a tell block. The app is launched when it is not running.
tell application "Timemator"
-- idle, idle with task, paused, running manually or running autotracked
get timer state
end tellThe name works for the version from timemator.com and for the Setapp version alike. If you renamed the app or keep several copies, address it by its bundle identifier instead, which does not change: com.catforce.timemator.macos for the version from timemator.com, com.catforce.timemator-setapp for the Setapp version.
tell application id "com.catforce.timemator.macos"
get timer running
end tellReference: application, Availability
Understand the object model#
The application holds folders and tasks, nested like the task list in the app. Folders contain folders and tasks; both are nodes. A task holds time entries, the tracked spans of time. The application also has a timer: current task, current time entry and timer state tell you what it is doing. Folders, tasks and time entries each have an id that stays the same across launches and across Macs that sync.
tell application "Timemator"
set theFolder to folder "Acme"
set theTask to task "Design" of theFolder
set theEntries to time entries of theTask
set theState to timer state
return {path of theTask, count of theEntries, theState}
end tellCollections come in the order the app shows them, folders and tasks as in the task list and time entries by begin date. AppleScript has no sort command; sort in your script, or ask for what you need with a whose clause.
Reference: application, node, folder, task, time entry
Get an item by name or id#
task "Design" finds the first task of that name anywhere in the list, archived tasks included. Add of folder "…" to look inside one folder, use a whose clause for a filtered list, or the id for a reference that survives renames.
tell application "Timemator"
-- by name, anywhere in the list
set theTask to task "Design"
-- by name inside a folder
set theTask to task "Design" of folder "Acme"
-- by a whose clause: a list, which may be empty
set activeDesignTasks to every task whose name is "Design" and archived is false
-- by id: the same on every Mac that syncs your library
set theId to id of theTask
set sameTask to task id theId
return path of sameTask
end tellThe same works for folders (folder "Acme", folder id "…") and, by id only, for time entries (time entry id "…"). A whose clause over every time entry walks your whole history; use find time entries for a date range.
Reference: task, folder, time entry
Loop over a collection#
A repeat with … in … loop visits each item. The loop variable is a reference, so read the properties you need inside the loop.
tell application "Timemator"
set theTasks to every task whose archived is false
set report to {}
repeat with aTask in theTasks
set trackedHours to (total duration of aTask) / 3600
set end of report to (path of aTask) & ": " & (round (trackedHours * 10)) / 10 & " h"
end repeat
return report
end tellTimers#
Start a timer#
start timer for starts tracking a task. If another task was running, its time entry is paused first. If the task is already running, nothing changes and the running entry is returned.
tell application "Timemator"
start timer for task "Design"
end tellMore examples:
tell application "Timemator"
-- a task inside a folder
start timer for task "Design" of folder "Acme"
-- with a note on the new time entry
start timer for task "Design" with notes "Homepage mockups"
-- by path: the folders and the task are created when they are missing
set theTask to create task named "Acme/Design"
start timer for theTask
-- the command returns the time entry being tracked
set theEntry to start timer for task "Design"
return id of theEntry
end tellAn archived task is refused with error 10003.
Reference: start timer, create task, time entry
Continue the current task#
start timer without a task continues the task the timer is on, like the play button in the app. With no current task it fails with error 10004, so check first.
tell application "Timemator"
if current task is missing value then
return "No current task."
end if
start timer
return path of current task
end tellReference: start timer, current task
Start a timer in the past#
at sets when the new time entry begins, for example when you notice late that you started working. The date must not be in the future.
tell application "Timemator"
-- began ten minutes ago
start timer for task "Design" at ((current date) - 10 * minutes)
end tellReference: start timer
Pause the timer#
pause timer ends the running time entry and returns whether something was running. It is safe to call when nothing runs. An entry shorter than the threshold in Preferences is dropped, as in the app.
tell application "Timemator"
set wasRunning to pause timer
end tellMore examples:
tell application "Timemator"
-- end the entry five minutes ago, for example when your screen locked
pause timer at ((current date) - 5 * minutes)
end tellReference: pause timer
Toggle the timer#
toggle timer pauses a running timer and continues the current task otherwise. It returns the new state, which makes it a good fit for one hotkey. With no current task it fails with error 10004.
tell application "Timemator"
set nowRunning to toggle timer
end tellReference: toggle timer
Read what is running#
timer running, current task and current time entry describe the timer. duration of the running entry counts up, so no arithmetic is needed for a live display.
tell application "Timemator"
if timer running then
set theTask to current task
set theEntry to current time entry
set elapsedMinutes to round ((duration of theEntry) / 60)
return (path of theTask) & ": " & elapsedMinutes & " min"
else
return "Nothing is being tracked."
end if
end tellMore examples:
tell application "Timemator"
-- the exact state: idle, idle with task, paused, running manually, running autotracked
set theState to timer state
if theState is paused then
return "Paused on " & (name of current task)
else if theState is running autotracked then
return "Auto-tracking " & (name of current task)
end if
return theState
end tellReference: application, timer state, time entry
Clear the timer#
clear timer pauses a running entry and makes the timer forget its task, so the menu bar shows nothing.
tell application "Timemator"
clear timer
end tellReference: clear timer
Time entries#
Find time entries for today#
find time entries returns the entries that overlap a date range, by begin date. Build the range from current date; the running entry is included.
set todayStart to current date
set time of todayStart to 0
set tomorrowStart to todayStart + 1 * days
tell application "Timemator"
set todaysEntries to find time entries from todayStart to tomorrowStart
set totalSeconds to 0
repeat with anEntry in todaysEntries
set totalSeconds to totalSeconds + (duration of anEntry)
end repeat
return {count of todaysEntries, totalSeconds / 3600}
end tellFor a total alone, get time summary is simpler and counts an entry that crosses midnight correctly.
Reference: find time entries, time entry
Find time entries in a date range#
Narrow the search with in (folders and tasks; a folder stands for every task in it), with billing status and including archived.
set rangeEnd to current date
set rangeStart to rangeEnd - 7 * days
tell application "Timemator"
-- everything from the last seven days
set recentEntries to find time entries from rangeStart to rangeEnd
-- one client: a folder stands for every task in it
set acmeEntries to find time entries from rangeStart to rangeEnd in {folder "Acme"}
-- two tasks
set someEntries to find time entries from rangeStart to rangeEnd in {task "Design", task "Website"}
-- unbilled entries only, archived tasks included
set openEntries to find time entries from rangeStart to rangeEnd with billing status {unbilled} including archived true
return {count of recentEntries, count of acmeEntries, count of someEntries, count of openEntries}
end tellThe result is a list of references. Read it in a repeat loop; to change the entries, set each one inside the loop.
Reference: find time entries, billing status
List a task's time entries#
time entries of task "…" holds a task's entries by begin date, the running one included. end date is missing value while an entry runs.
tell application "Timemator"
set theEntries to time entries of task "Design"
set report to {}
repeat with anEntry in theEntries
set startText to (begin date of anEntry) as text
set durationMinutes to round ((duration of anEntry) / 60)
set end of report to startText & ", " & durationMinutes & " min"
end repeat
return report
end tellMore examples:
tell application "Timemator"
-- the most recent entry
set lastEntry to last time entry of task "Design"
-- entries with notes
set notedEntries to every time entry of task "Design" whose notes is not missing value
-- billed entries
set billedEntries to every time entry of task "Design" whose billing status is billed
return {id of lastEntry, count of notedEntries, count of billedEntries}
end tellReference: time entry, task
Create a time entry#
create time entry logs a span of time on a task. The end must be after the begin.
set entryStart to current date
set time of entryStart to 9 * hours
set entryEnd to entryStart + 90 * minutes
tell application "Timemator"
create time entry from entryStart to entryEnd in task "Design" with notes "Client call"
end tellMore examples:
set entryStart to current date
set time of entryStart to 9 * hours
set entryEnd to entryStart + 90 * minutes
tell application "Timemator"
-- the standard make command does the same
make new time entry with properties {begin date:entryStart, end date:entryEnd, notes:"Client call"} at end of time entries of task "Design"
-- already billed
create time entry from entryStart to entryEnd in task "Design" billing status billed
-- replace what overlaps the range, so an import can run again
create time entry from entryStart to entryEnd in task "Design" with notes "Client call" replacing existing true
end tellreplacing existing deletes the task's entries that overlap the range before creating the new one. There is no confirmation. The whole step is one Undo in Timemator.
Reference: create time entry, make, time entry
Edit a time entry#
Set the properties you want to change: notes, begin date, end date, billing status or task. set properties of … to {…} changes several at once and saves them as one step.
tell application "Timemator"
set theEntry to last time entry of task "Design"
set notes of theEntry to "Homepage mockups, round 2"
set end date of theEntry to (begin date of theEntry) + 2 * hours
set properties of theEntry to {notes:"Review", billing status:billed}
end tellSetting end date on the running entry pauses the timer at that date. Setting it to missing value is refused; use start timer to resume. billable, hourly rate and revenue follow the task while an entry is unbilled and freeze once it is billed or paid.
Reference: set, time entry
Move a time entry to another task#
Set the task property of the entry.
tell application "Timemator"
set theEntry to last time entry of task "Design"
set task of theEntry to task "Website"
end tellReference: time entry, set
Mark time entries as billed#
Set billing status on a filtered element list to change every match in one step, for example after sending an invoice.
tell application "Timemator"
set billing status of (every time entry of task "Design" whose billing status is unbilled) to billed
end tellMore examples:
-- Last month, every task of one client, marked as paid.
set thisMonthStart to current date
set time of thisMonthStart to 0
set day of thisMonthStart to 1
set lastMonthStart to thisMonthStart - 1 * days
set day of lastMonthStart to 1
tell application "Timemator"
set openEntries to find time entries from lastMonthStart to thisMonthStart in {folder "Acme"} with billing status {unbilled, billed}
repeat with anEntry in openEntries
set billing status of anEntry to paid
end repeat
return count of openEntries
end tellexport time entries can also mark what it exports as billed; see Export time entries as CSV.
Reference: set, billing status, find time entries
Merge time entries#
merge time entries joins two or more finished entries into one, like the merge panel in the app. The merged entry starts at the earliest begin date and lasts the durations added up; with filling gaps it runs to the latest end. The originals are deleted, which one Undo in Timemator takes back.
tell application "Timemator"
set theEntries to time entries of task "Design"
if (count of theEntries) < 2 then return "Nothing to merge."
set mergedEntry to merge time entries {item 1 of theEntries, item 2 of theEntries}
return duration of mergedEntry
end tellMore examples:
tell application "Timemator"
set theEntries to time entries of task "Design"
if (count of theEntries) < 2 then return "Nothing to merge."
-- keep the time between the entries, and put the result on another task
merge time entries {item 1 of theEntries, item 2 of theEntries} into task "Website" filling gaps true
end tellThe running entry cannot be merged; pause the timer first. Entries on different tasks need into.
Reference: merge time entries, time entry
Delete a time entry#
Use the standard delete command. There is no confirmation, but the deletion is one Undo in Timemator. The running entry cannot be deleted; pause the timer first.
tell application "Timemator"
set theEntry to last time entry of task "Design"
if being tracked of theEntry then return "The entry is running; pause the timer first."
delete theEntry
end tellMore examples:
tell application "Timemator"
-- every entry of a task that is shorter than a minute
delete (every time entry of task "Design" whose duration < 60 and being tracked is false)
end tellReference: delete, time entry
Folders and tasks#
List folders and tasks#
root items holds the top level in the task list's order. every task and every folder are flat lists of everything at any depth; path tells you where an item lives.
tell application "Timemator"
set topLevel to root items
set names to {}
repeat with anItem in topLevel
if class of anItem is folder then
set end of names to (name of anItem) & "/"
else
set end of names to name of anItem
end if
end repeat
return names
end tellMore examples:
tell application "Timemator"
-- every active task, with its path
set activeTasks to every task whose archived is false
set paths to path of every task
-- the children of one folder, in display order
set acmeTasks to tasks of folder "Acme"
set acmeFolders to folders of folder "Acme"
set acmeItems to nodes of folder "Acme"
-- the tasks of one folder's subtree
set theFolder to folder "Acme"
set deeperTasks to every task whose parent folder is theFolder
return {count of activeTasks, count of paths, count of acmeTasks, count of acmeFolders, count of acmeItems, count of deeperTasks}
end tellIn a whose clause, put the folder in a variable first, as above: every task whose parent folder is folder "Acme" does not work.
Reference: application, folder, task, node
Create a task#
create task named creates a task by its path, like the Shortcuts action. Missing folders are created, existing ones reused, and an existing active task of that name is returned instead of a new one. A script that runs twice gets the same task.
tell application "Timemator"
-- the folders Clients and Acme are created when missing, then the task Design
set theTask to create task named "Clients/Acme/Design"
return path of theTask
end tellMore examples:
tell application "Timemator"
-- a path that starts in a folder
create task named "Acme/Design" in folder "Clients"
-- the standard make command, with a color
make new task with properties {name:"Website", color:teal, notes:"Relaunch"} at end of tasks of folder "Acme"
-- at the top level
make new task with properties {name:"Admin"}
end tellReference: create task, make, item color
Create a folder#
Use the standard make command.
tell application "Timemator"
set theFolder to make new folder with properties {name:"Clients"}
-- a folder inside a folder
make new folder with properties {name:"Acme"} at end of folders of theFolder
end tellRename or edit a task#
Set name, notes or color. A folder's color is given to its tasks.
tell application "Timemator"
set theTask to task "Design"
set name of theTask to "Design and Branding"
set notes of theTask to "Contact: Jane Doe"
set properties of theTask to {color:teal, notes:"Contact: Jane Doe"}
return name of theTask
end tellReference: set, task, item color
Move a task into a folder#
Set parent folder to move a task or a folder; missing value moves it to the top level. The item takes the folder's color, as when you drag it in the app. Set position to reorder it among its siblings.
tell application "Timemator"
set theFolder to folder "Acme"
set parent folder of task "Design" to theFolder
-- to the top level
set parent folder of task "Website" to missing value
-- first among its siblings
set position of task "Website" to 1
end tellSet billing and hourly rate#
billing and hourly rate setting are what you set; billable and hourly rate are what applies once inherited settings are resolved.
tell application "Timemator"
set billing of folder "Acme" to always billable
set hourly rate setting of folder "Acme" to 120.0
-- the task follows its folder
set billing of task "Design" to inherited
set hourly rate setting of task "Design" to missing value
-- what applies
return {billable of task "Design", hourly rate of task "Design"}
end tellReference: node, billing setting
Archive a task#
archive and unarchive work on a folder, a task or a list of them. A folder's contents go with it. moving to root also moves the items to the top level, where the Archive menu item puts them. The archived property cannot be set directly.
tell application "Timemator"
archive task "Design"
end tellMore examples:
tell application "Timemator"
-- a folder with everything in it, moved to the top level like the menu item
archive folder "Acme" moving to root true
-- several at once
archive {task "Design", task "Website"}
-- every task that was not tracked in the last 90 days
set cutoff to (current date) - 90 * days
set staleTasks to every task whose archived is false and last tracked < cutoff
if (count of staleTasks) > 0 then archive staleTasks
-- and back
unarchive task "Design"
end tellarchive needs at least one item. A whose clause that matches nothing is refused with error 10011, so check the count first, as above.
Reference: archive, unarchive, node
Delete a task#
Use the standard delete command. Deleting a task deletes its time entries; deleting a folder deletes everything in it. There is no confirmation. The deletion is one Undo in Timemator, but make a backup before you run a cleanup script for the first time.
tell application "Timemator"
delete task "Website"
end tellMore examples:
tell application "Timemator"
-- a folder and everything in it
delete folder "Acme"
-- every archived task without any tracked time
delete (every task whose archived is true and total duration is 0)
end tellReference: delete, task, folder
Read a task's auto-tracking rules#
rule set of task "…" holds the task's auto-tracking rules and what they do. Rules are read-only to scripts; edit them in Timemator. A property that does not apply to a rule's kind is missing value. A combination of rules, which must all match, has the kind combination and its rules as rules.
tell application "Timemator"
set theRules to rule set of task "Design"
if theRules is missing value then return "No rules."
set report to {}
repeat with aRule in rules of theRules
set end of report to {kind of aRule, text of aRule, website url of aRule, application path of aRule}
end repeat
return {active of theRules, start behavior of theRules, start delay of theRules, report}
end tellMore examples:
tell application "Timemator"
set theRules to rule set of task "Design"
if theRules is missing value then return "No rules."
-- the combinations among the rules, and what each one combines
set combinations to every rule of theRules whose kind is combination
set report to {}
repeat with aCombination in combinations
set end of report to kind of every rule of aCombination
end repeat
-- the website rules only
set websiteRules to every rule of theRules whose kind is website
return {count of combinations, report, website url of every rule of theRules}
end tellReference: rule set, rule, rule kind, match mode
Switch auto-tracking on or off#
auto tracking of a task switches its rules on or off; the task must have rules. auto tracking enabled of the application is the main switch in Preferences, for example for a focus-mode script.
tell application "Timemator"
-- one task's rules; a task without rules refuses with error 10008
if rule set of task "Design" is not missing value then
set auto tracking of task "Design" to false
end if
-- auto-tracking as a whole
set auto tracking enabled to false
end tellReference: task, auto tracking enabled
Reports and export#
Get today's total#
get time summary answers a period's totals the way the Reports window calculates them. scope daily is today; weekly, monthly and yearly use the same period boundaries as the app, including the first day of the week from Preferences.
tell application "Timemator"
set todaySummary to get time summary scope daily
set totalMinutes to round ((total duration of todaySummary) / 60)
return "Today: " & (totalMinutes div 60) & " h " & (totalMinutes mod 60) & " min"
end tellMore examples:
tell application "Timemator"
-- yesterday
set yesterdaySummary to get time summary scope daily on ((current date) - 1 * days)
-- this month, one client only, unbilled entries
set monthSummary to get time summary scope monthly in {folder "Acme"} with billing status {unbilled}
return {total duration of yesterdaySummary, total revenue of monthSummary, time entry count of monthSummary}
end tellReference: get time summary, time summary, report scope
Get a weekly summary per task#
task totals of a summary is a list of records, one per task, each with the task's reference, its duration in seconds and its revenue.
tell application "Timemator"
set weekSummary to get time summary scope weekly
set report to {}
repeat with aTotal in task totals of weekSummary
set taskMinutes to round ((duration of aTotal) / 60)
set end of report to (path of (task of aTotal)) & ": " & taskMinutes & " min"
end repeat
return report
end tellMore examples:
-- A custom period: the last 14 days.
set rangeEnd to current date
set rangeStart to rangeEnd - 14 * days
tell application "Timemator"
set customSummary to get time summary from rangeStart to rangeEnd
return {begin date of customSummary, end date of customSummary, total duration of customSummary}
end tellReference: get time summary, time summary
Export time entries as CSV#
export time entries writes the file the Export window writes. Pass the file as POSIX file with a full path; an existing file is replaced without asking.
set rangeEnd to current date
set time of rangeEnd to 0
set rangeStart to rangeEnd - 7 * days
set outputPath to (POSIX path of (path to desktop)) & "Timemator last 7 days.csv"
tell application "Timemator"
export time entries from rangeStart to rangeEnd to file (POSIX file outputPath) as CSV
end tellMore examples:
set rangeEnd to current date
set time of rangeEnd to 0
set rangeStart to rangeEnd - 7 * days
set outputPath to (POSIX path of (path to desktop)) & "Acme unbilled.csv"
tell application "Timemator"
-- one client, unbilled entries, comma-separated, without the rounding columns
export time entries from rangeStart to rangeEnd to file (POSIX file outputPath) as CSV in {folder "Acme"} with billing status {unbilled} delimiter comma including roundings false
-- the entries of archived tasks too; they are left out otherwise
export time entries from rangeStart to rangeEnd to file (POSIX file outputPath) as CSV including archived true
-- the same, and mark the exported entries as billed afterwards
export time entries from rangeStart to rangeEnd to file (POSIX file outputPath) as CSV in {folder "Acme"} with billing status {unbilled} marking as billed true
end tellmarking as billed changes your data; there is no confirmation. The change is one Undo in Timemator.
Reference: export time entries, export format, delimiter
Export a PDF report#
as PDF writes the report with totals, a task summary and the entries. group by and duration format apply to PDF only.
set monthStart to current date
set time of monthStart to 0
set day of monthStart to 1
set monthEnd to current date
set outputPath to (POSIX path of (path to desktop)) & "Timemator this month.pdf"
tell application "Timemator"
export time entries from monthStart to monthEnd to file (POSIX file outputPath) as PDF group by task duration format decimal
end tellReference: export time entries, grouping, duration format
Calculate date ranges#
AppleScript dates are easiest to build from current date. Set time to 0 for midnight, set day to 1 for the first of the month, and add or subtract days, hours and minutes. Parsing a date from text depends on the language settings of the Mac, so set the parts instead.
-- Today, from midnight to midnight
set todayStart to current date
set time of todayStart to 0
set todayEnd to todayStart + 1 * days
-- This week, starting on Monday
set daysSinceMonday to ((weekday of todayStart) as integer) - 2
if daysSinceMonday < 0 then set daysSinceMonday to 6
set weekStart to todayStart - daysSinceMonday * days
set weekEnd to weekStart + 7 * days
-- Last week
set lastWeekStart to weekStart - 7 * days
set lastWeekEnd to weekStart
-- This month
set monthStart to todayStart
set day of monthStart to 1
set nextMonthStart to monthStart + 32 * days
set day of nextMonthStart to 1
-- Last month
set lastMonthStart to monthStart - 1 * days
set day of lastMonthStart to 1
-- A fixed date, built from its parts so it works in every language
set fixedDate to current date
set time of fixedDate to 0
set day of fixedDate to 1
set year of fixedDate to 2026
set month of fixedDate to 3
set day of fixedDate to 15
return {todayStart, weekStart, lastWeekStart, monthStart, lastMonthStart, fixedDate}For reports, get time summary with scope weekly, monthly or yearly works out the period for you, with the first day of the week from Preferences.
Reference: find time entries, get time summary
Activity log#
The activity log is what activity tracking records on this Mac: which app was in front and when, with its window title, and for browsers and document apps the website or the file. The timeline shows it next to your time entries. Scripts can read it and delete from it.
Any app you allow to control Timemator can read the whole log, window titles, website addresses and file paths included, without asking again. Only allow apps you trust; see Permissions.
Find activity records#
find activity records returns the records that overlap a date range, by begin date, like find time entries. The record being recorded right now is included.
set todayStart to current date
set time of todayStart to 0
set rightNow to current date
tell application "Timemator"
set todaysRecords to find activity records from todayStart to rightNow
set report to {}
repeat with aRecord in todaysRecords
set end of report to {application name of aRecord, title of aRecord, round ((duration of aRecord) / 60)}
end repeat
return report
end tellA record of a browser has a website url, a record of a document app a file path; for every other app both are missing value. title is missing value when the window had none.
More examples:
set rangeEnd to current date
set rangeStart to rangeEnd - 7 * days
tell application "Timemator"
-- one app, by its bundle identifier
set safariRecords to find activity records from rangeStart to rangeEnd with bundle identifier "com.apple.Safari"
-- several apps
set officeRecords to find activity records from rangeStart to rangeEnd with bundle identifier {"com.apple.iWork.Pages", "com.apple.iWork.Numbers"}
-- the websites of the last seven days
set visited to {}
repeat with aRecord in safariRecords
if website url of aRecord is not missing value then set end of visited to website url of aRecord
end repeat
return {count of officeRecords, visited}
end tellwith bundle identifier compares the bundle identifier exactly, capitals included. bundle identifier of a record shows the one to use.
Reference: find activity records, activity record
Delete yesterday's activity records of an app#
Use the standard delete command on the records find activity records returns. There is no confirmation, and unlike other deletions it cannot be undone, as in the timeline. The record being recorded right now cannot be deleted, so the loop skips it.
set todayStart to current date
set time of todayStart to 0
set yesterdayStart to todayStart - 1 * days
tell application "Timemator"
set theRecords to find activity records from yesterdayStart to todayStart with bundle identifier "com.apple.Safari"
set deletedCount to 0
repeat with aRecord in theRecords
if recording of aRecord is false then
delete aRecord
set deletedCount to deletedCount + 1
end if
end repeat
return deletedCount
end tellfind activity records also returns the records that cross either midnight, such as one that began late the day before yesterday. To delete only the records that began yesterday, use a whose clause instead; it looks at every record ever recorded, so it is slower on a long log:
set todayStart to current date
set time of todayStart to 0
set yesterdayStart to todayStart - 1 * days
tell application "Timemator"
delete (every activity record whose bundle identifier is "com.apple.Safari" and begin date ≥ yesterdayStart and begin date < todayStart and recording is false)
end tellReference: delete, activity record
User interface#
Read the selected item#
selected item is the time entry selected in the main window, or else the folder or task selected there. It is missing value when nothing is selected or the window is closed. A hotkey script can act on it.
tell application "Timemator"
set theItem to selected item
if theItem is missing value then return "Nothing is selected."
if class of theItem is time entry then
return "Time entry on " & (path of task of theItem)
else
return path of theItem
end if
end tellReference: selected item, node, time entry
Show an item in the main window#
show opens the main window on the Tracking tab with a folder, a task or a time entry selected. It does not bring Timemator to the front; add activate for that.
tell application "Timemator"
show task "Design"
activate
end tellMore examples:
tell application "Timemator"
show folder "Acme"
show last time entry of task "Design"
end tellReference: show
Open the Reports window#
open reports opens the Reports window on the day, week, month or year that contains a date. Without a date it is today's period.
tell application "Timemator"
open reports scope weekly
activate
end tellMore examples:
tell application "Timemator"
-- last month
open reports scope monthly on ((current date) - 30 * days)
end tellReference: open reports, report scope
Recipes#
Import calendar events as time entries#
Logs today's events of one calendar as time entries on one task. Thanks to replacing existing, running the script again replaces what it logged before instead of adding duplicates. Change "Work" to the name of your calendar.
set dayStart to current date
set time of dayStart to 0
set dayEnd to dayStart + 1 * days
tell application "Calendar"
set theEvents to every event of calendar "Work" whose start date ≥ dayStart and start date < dayEnd
set eventList to {}
repeat with anEvent in theEvents
set end of eventList to {summary of anEvent, start date of anEvent, end date of anEvent}
end repeat
end tell
tell application "Timemator"
set theTask to create task named "Meetings"
repeat with anEvent in eventList
set {eventTitle, eventStart, eventEnd} to anEvent
create time entry from eventStart to eventEnd in theTask with notes eventTitle replacing existing true
end repeat
return count of eventList
end tellReference: create time entry, create task
Start a timer from Things or OmniFocus#
Takes the to-do selected in Things and starts a timer on a task of the same name, in a folder called Things. The task is created when it does not exist yet.
tell application "Things3"
set selectedToDos to selected to dos
if (count of selectedToDos) is 0 then return "Select a to-do in Things first."
set theTitle to name of item 1 of selectedToDos
end tell
tell application "Timemator"
set theTask to create task named ("Things/" & theTitle)
start timer for theTask
return path of theTask
end tellThe same with OmniFocus:
-- TODO: not tested; OmniFocus was not available while this page was written.
tell application "OmniFocus"
set selectedTrees to selected trees of content of first document window of front document
if (count of selectedTrees) is 0 then return "Select a task in OmniFocus first."
set theTitle to name of value of item 1 of selectedTrees
end tell
tell application "Timemator"
set theTask to create task named ("OmniFocus/" & theTitle)
start timer for theTask
return path of theTask
end tellReference: create task, start timer
Save a daily CSV report#
Exports yesterday's time entries to a dated CSV file in Documents → Timemator Reports. Save it as a script, then run it every morning with a launchd job, a Calendar alarm that opens the file, or a Shortcuts automation (see Run a script from Shortcuts).
set dayEnd to current date
set time of dayEnd to 0
set dayStart to dayEnd - 1 * days
set yearText to (year of dayStart) as text
set monthText to text -2 thru -1 of ("0" & ((month of dayStart) as integer))
set dayText to text -2 thru -1 of ("0" & (day of dayStart))
set reportFolder to (POSIX path of (path to documents folder)) & "Timemator Reports/"
do shell script "mkdir -p " & quoted form of reportFolder
set outputPath to reportFolder & yearText & "-" & monthText & "-" & dayText & ".csv"
tell application "Timemator"
export time entries from dayStart to dayEnd to file (POSIX file outputPath) as CSV delimiter comma
end tellTo run it with launchd, save the script as ~/Library/Scripts/Applications/Timemator/Daily Report.scpt and put this file at ~/Library/LaunchAgents/com.example.timemator-daily-report.plist, then run launchctl load on it once:
<?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.example.timemator-daily-report</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/osascript</string>
<string>/Users/YOUR-USER-NAME/Library/Scripts/Applications/Timemator/Daily Report.scpt</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>7</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</dict>
</plist>The first run asks for permission to control Timemator; see Permissions.
Reference: export time entries
Control Timemator from the Terminal#
osascript runs AppleScript from the shell, which makes Timemator available to shell scripts, cron jobs and tools like Keyboard Maestro, Raycast or Alfred.
# start, pause, toggle
osascript -e 'tell application "Timemator" to start timer for task "Design"'
osascript -e 'tell application "Timemator" to pause timer'
osascript -e 'tell application "Timemator" to toggle timer'
# what is running
osascript -e 'tell application "Timemator"' -e 'if timer running then return path of current task' -e 'return "idle"' -e 'end tell'
# a saved script
osascript ~/Library/Scripts/Applications/Timemator/Start\ Design.scptThe same dictionary is available to JavaScript for Automation (JXA), with camel-cased names:
# TODO: not tested; JXA is not covered by Timemator's own tests
osascript -l JavaScript -e 'Application("Timemator").timerRunning()'Reference: start timer, pause timer, toggle timer
Run a script from Shortcuts#
The Run AppleScript action in Shortcuts runs a script on the Mac, with the action's input in input. This shortcut takes a task path as text, for example from an Ask for Input action, creates the task if needed and starts the timer. Shortcuts with AppleScript actions run on the Mac only.
on run {input, parameters}
set taskPath to (item 1 of input) as text
tell application "Timemator"
set theTask to create task named taskPath
start timer for theTask
return path of theTask
end tell
end runTimemator also has Shortcuts actions of its own (Start Timer for Task, Pause Running Timer, Continue Tracking, Create Task, Create Task and Start Timer) that need no AppleScript and also run on iPhone and iPad. Use Run AppleScript for what those do not cover, such as reading totals or exporting.
Reference: create task, start timer
Show the running task in the menu bar#
A plugin for SwiftBar or xbar is a shell script whose output becomes the menu bar text. This one refreshes every minute (the 1m in the file name) and shows the running task with the elapsed minutes. It asks System Events first whether Timemator is running, so it never launches the app; see Check whether Timemator is running.
#!/bin/bash
# Save as timemator.1m.sh in your SwiftBar or xbar plugin folder and make it executable.
osascript <<'EOF'
tell application "System Events"
set timematorIsRunning to (count of (processes whose bundle identifier is "com.catforce.timemator.macos")) > 0
end tell
if not timematorIsRunning then return "Timemator off"
tell application "Timemator"
if timer running then
set elapsedMinutes to round ((duration of current time entry) / 60)
return (name of current task) & " " & elapsedMinutes & " min"
else if current task is missing value then
return "Timemator idle"
else
return (name of current task) & " paused"
end if
end tell
EOFReference: application, time entry
Error handling#
Catch errors#
Wrap commands in try … on error. Timemator's own errors have numbers from 10001 up; the message is for people, the number is for your script. Negative numbers come from AppleScript or macOS: -1728 when a name finds nothing, -1743 when Timemator may not be controlled by the app running the script. All codes are listed under Error codes.
try
tell application "Timemator"
start timer for task "Design"
end tell
on error errorMessage number errorNumber
if errorNumber is 10003 then
display alert "The task is archived." message errorMessage
else if errorNumber is -1728 then
display alert "There is no task called Design."
else if errorNumber is -1743 then
display alert "Timemator may not be controlled by this app." message "Allow it under System Settings › Privacy & Security › Automation."
else
display alert "Timemator reported error " & errorNumber message errorMessage
end if
end tryReference: Error codes
Check whether Timemator is running#
tell application "Timemator" launches the app when it is not running. A script that should stay quiet, such as a menu bar plugin, can check AppleScript's own running property of the application first; it is answered without launching the app.
if application "Timemator" is running then
tell application "Timemator"
return timer running
end tell
else
return "Timemator is not running."
end ifSystem Events answers the same question by bundle identifier, which tells the two versions apart: com.catforce.timemator.macos is the version from timemator.com, com.catforce.timemator-setapp the Setapp version.
tell application "System Events"
set timematorIsRunning to (count of (processes whose bundle identifier is "com.catforce.timemator.macos")) > 0
end tellReference: Availability
Check for read-only mode#
When a trial has expired, Timemator is in read-only mode and every change is refused with error 10001. read only lets a script find out before it tries.
tell application "Timemator"
if read only then
return "Timemator is in read-only mode; nothing can be changed."
end if
start timer for task "Design"
end tellReference: read only, Error codes
Check the dictionary version#
scripting version is the version of Timemator's AppleScript dictionary, 1 in the first release. It goes up when a command or property is added or changed, so a script that relies on a newer term can check before it uses it. The dictionary changelog says which version added what.
tell application "Timemator"
if scripting version < 1 then
return "This script needs a newer Timemator."
end if
return {scripting version, version}
end tellReference: scripting version, Dictionary changelog