ActivityWatch watcher that tracks time spent per game on PS5/PS4 by polling your own PlayStation Network presence.
The PS5 exposes no API on your LAN, so this asks PSN "what is my account playing right now?" every 30 seconds and sends heartbeats to aw-server. Consecutive heartbeats for the same game merge into a single event, giving you one event per play session per game.
The watcher doesn't run on the console — it runs on a separate always-on machine and reads your presence from Sony's servers, so it tracks whatever your PSN account is actively signed into and playing.
| Platform | Support |
|---|---|
| PS5 | ✅ Supported (tested) |
| PS4 | ✅ Supported |
| Cloud streaming (PS Plus) | ✅ Supported — streamed games report as the game on PS5/PS4 |
| PS3 | |
| PS Vita |
PSN is the data source, not a universal tracker: non-PlayStation activity (Steam, Xbox, PlayStation PC ports) does not appear in presence and is not tracked.
There is no maintained Rust PSN library, so src/psn.rs implements the slice of
the PlayStation mobile-app OAuth flow needed to read your own presence:
NPSSO cookie -> authorization code (302 Location) -> access token -> basicPresences
Access tokens last ~1 hour and are renewed with the refresh_token grant
(valid ~10 days), which avoids re-hitting the bot-protected authorize endpoint
every hour. When the refresh token lapses or a 401 arrives, the watcher falls
back to a full re-authentication from your NPSSO. NPSSO tokens themselves last
~2 months and are the hard ceiling — when yours expires, grab a fresh one.
- Adaptive polling — polls every
poll_time(default 30s) while the console is online; after a few consecutive offline polls the interval doubles per poll up tomax_poll_time(default 5 min), and snaps back topoll_timethe moment the console comes online. - Offline buffering — if aw-server is unreachable, events are buffered in memory (capped at ~3 days worth) and replayed once it is back, so play time is not lost across aw-server restarts.
- Rate-limit aware — honors
Retry-Afteron HTTP 429, and uses jittered exponential backoff (capped at 15 min) for repeated errors. - Title filtering —
exclude_titlesin the config drops unwanted titles (e.g. media apps) case-insensitively before they reach aw-server.
cargo build --release-
Get an NPSSO token: log in at https://www.playstation.com, then visit https://ca.account.sony.com/api/v1/ssocookie and copy the
npssovalue. -
Run once to generate the config, then edit it:
./target/release/aw-watcher-ps
Config path:
- macOS:
~/Library/Application Support/activitywatch/aw-watcher-ps/aw-watcher-ps.toml - Linux:
~/.config/activitywatch/aw-watcher-ps/aw-watcher-ps.toml
Paste the token into
npsso = "...". - macOS:
-
Run it (add
--testingfor an aw-server on port 5666):./target/release/aw-watcher-ps # --port N and --verbose also supported
Events land in aw-watcher-ps_<hostname> with type console.gameplay and data
{app, title, platform, np_title_id}. Per-game totals show up in the timeline
and can be summed with merge_events_by_keys(events, ["app"]).
- NPSSO tokens expire after ~2 months. When re-auth fails the watcher logs a clear error each poll — grab a fresh token and restart.
- Your console must not be in "Appear Offline" mode, and privacy settings must allow your online status to be visible.
- Presence reports the foreground game/app, so media apps (Netflix, YouTube)
show up too — add them to
exclude_titlesif you only want games. - Default poll is 30s; sessions shorter than that can be missed. The first
poll after a long offline stretch can lag by up to
max_poll_time, so the first few minutes of a session may be missed at the default cap.