Some of these questions are technically not frequently asked.

How does ActivityWatch know when I am AFK?

On Windows and macOS, we use functionality offered by those platforms that gives us the time since last input.

On Linux, we monitor all mouse and keyboard activity so that we can calculate the time since last input. We do not store what that activity was, just that it happened.

With this data (seconds since last input) we then check if there is less than 3 minutes between input activity. If there is, we consider you not-AFK. If more than 3 minutes passes without any input, we consider that as if you were AFK from the last input until the next input occurs.

Why is the active window logged as “unknown” when using Wayland?

The Wayland protocol does not have a notion of an active window, and it is unlikely to ever have. Wayland is also developed in security in mind, so access should be handed out on an app-by-app basis. This is a good idea, any application shouldn’t just give that privacy-sensitive information away freely.

Unfortunately, in Wayland compositors like Gnome’s Mutter there is no way at all to get the current window, this leaves the window watcher completely disabled in Wayland.

Solution: Switch to using X11 (the best option), and if you can’t: bother the developer of your Wayland compositor.

You can see the general status of the ability of getting the active window in Wayland on StackOverflow or follow the issue for ActivityWatch tracking the problem.

How do I programmatically use ActivityWatch?

See the documentation for Extending ActivityWatch or checkout the aw-client repository.

How do I understand the data that is stored?

All ActivityWatch data is represented using Data model.

All events from have the fields timestamp (ISO 8601 formatted), duration (in seconds), and data (a JSON object).

You can programmatically get some events yourself to inspect with the following code:

ac = aw_client.ActivityWatchClient("")

# Returns a dict with information about every bucket
buckets = ac.get_buckets()

# Get the first bucket
bucket_id = next(buckets.keys())
events = ac.get_events(bucket_id)

As an example for AFK events: The data object contains has one attribute status which can be afk or not-afk.

No two events in a bucket should cover the same moment, if that happens there is an issue with the watcher that should be resolved.

What happens if it is down or crashes?

Since ActivityWatch consists of several modules running independently, one thing crashing will have limited impact on the rest of the system.

If the server crashes, all watchers which use the heartbeat queue should simply queue heartbeats until the server becomes available again. Since heartbeats are currently sent immediately to the server for storage, all data before the crash should be untouched.

If a watcher crashes, its bucket will simply remain untouched until it is restarted.

What happens when my computer is off or asleep?

If your computer is off or asleep, watchers will usually record nothing. i.e. one events ending (timestamp + duration) will not match up with the following event’s beginning (timestamp).

Some events have 0 duration. What does this mean?

Watchers most commonly use a polling method called heartbeats in order to store information on the server. Heartbeats are received regularly with some data, and when two consecutive heartbeats have identical data they get merged and the duration of the new one becomes the time difference between the previous two. Sometimes, a single heartbeat doesn’t get a following event with identical data. It is then impossible to know the duration of that event.

The assumption could be made to consider all zero-duration events actually have a duration equal to the time of the next event, but all such assumptions are left to the analysis stage.