Files
clawdmeter/daemon/README-windows.md
T
wenilandClaude Opus 4.8 ec8322fa21 v2: standalone Clawdmeter.exe — runs on any Win11 machine, no Python
Package the tray+daemon as a single PyInstaller onefile exe so it runs on a
fresh Windows 11 box with no Python and no pip install. Verified on hardware:
the frozen exe connects over BLE, reads the Windows media session, and pushes
now-playing (incl. Cyrillic) at the 3s / 60s cadence.

- clawdmeter.spec: onefile, windowed (no console; logs to daemon.log). collect_all
  for winrt + bleak is the key bit — bleak loads its WinRT backend dynamically and
  the winrt.windows.* projections are split distributions that PyInstaller's static
  analysis misses, so BLE and the media session would otherwise fail at runtime.
  upx off (lowers AV false-positive rate).
- build-exe.ps1: one-command build (venv + deps + pyinstaller + spec).
- tray_windows.py: resolve the brand-logo asset from sys._MEIPASS when frozen.
- autostart_windows.py: when frozen, the HKCU\Run "Start at login" value points at
  the exe itself (sys.executable), not pythonw + script.
- .gitignore: ignore /build, /dist, /.venv — the exe ships via a Gitea release.
- README-windows.md: document the standalone-exe path (download / run / build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 09:58:26 +03:00

268 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Windows Setup and Run Guide
This guide covers running the Clawdmeter Windows daemon on native Windows hardware.
It includes the turnkey `install-windows.ps1` bootstrap (tray icon + login autostart),
the manual-run fallback, and how to manage or remove autostart.
---
## Prerequisites
| Requirement | Details |
|-------------|---------|
| **Native Windows** | Must run on real Windows — not WSL. The script prints a warning and BLE will not work under WSL. |
| **Python 3.11+** | Download from [python.org](https://www.python.org/downloads/) if not already installed. Ensure "Add python.exe to PATH" is checked during install. |
| **Claude Code installed** | Install Claude Code and complete `claude login` so credentials exist on disk. |
| **Clawdmeter powered on** | The device must be powered on and in range before the daemon starts. |
| **Paired with Windows Bluetooth** | Pair the device once via **Settings → Bluetooth & devices → Add device** (see [Pair the device](#pair-the-device-one-time)). This is required — the device is a bonded BLE HID keyboard, so pairing enables its physical buttons and keeps a persistent connection that shows your last usage even when the daemon is stopped. |
### Where are my credentials?
`claude login` writes the OAuth token to (first match wins):
1. `%USERPROFILE%\.claude\.credentials.json` — primary path (confirmed by Claude Code docs)
2. `%LOCALAPPDATA%\Claude\.credentials.json` — fallback
3. `%APPDATA%\Claude\.credentials.json` — fallback
The daemon probes these paths in order. You can also set `CLAUDE_CREDENTIALS_PATH` to an
absolute path or `CLAUDE_CONFIG_DIR` to a directory to override the search entirely.
> **Security note:** The credentials file contains your OAuth token. Never share its contents
> or embed it in scripts. The daemon reads it from disk and uses it only as the API
> `Authorization` header — the token is never written to any log, tooltip, or notification.
---
## Pair the device (one time)
The Clawdmeter is a **bonded BLE HID keyboard** as well as a usage display — its firmware
enables bonding (`NimBLEDevice::setSecurityAuth`) and advertises the HID service so its
physical buttons act as a keyboard (Space / Shift+Tab). Pair it with Windows **once**,
before running the daemon:
1. Put the device on its Bluetooth waiting screen (powered on, not yet connected).
2. Open **Settings → Bluetooth & devices → Add device → Bluetooth**.
3. Select **Claude Controller** and complete pairing.
**Why this is required:**
- **Keyboard buttons** — HID over BLE requires bonding on Windows. Without pairing, the
device's buttons won't reach the PC.
- **Persistent point-in-time view** — once paired, Windows maintains the BLE link and
auto-reconnects the device whenever it is in range. This is intentional: the device keeps
showing your **last-synced** usage even after you Quit the daemon, as a glanceable
point-in-time view. Quitting the daemon releases only its data connection — it does **not**
drop the Windows pairing, so the device stays connected to Windows.
To undo, use **Settings → Bluetooth & devices → (device) → Remove device**. Removing the
pairing disables the keyboard buttons.
---
## Standalone executable — no Python required (recommended)
To run Clawdmeter on a machine **without Python**, use the single-file
`Clawdmeter.exe`. It bundles its own Python, the WinRT BLE stack and the
media-session reader, so nothing needs to be installed.
1. Pair the device with Windows once (see [Pair the device](#pair-the-device-one-time)).
2. Get `Clawdmeter.exe` — download it from the project's Gitea release, or build it
yourself (below).
3. Double-click `Clawdmeter.exe`. The tray icon appears and the watch starts
updating within ~10 seconds.
4. To launch it automatically at every logon, right-click the tray icon →
**Start at login**. That registers the exe itself under
`HKCU\Software\Microsoft\Windows\CurrentVersion\Run` — no Python, no console window.
There is no console window; the exe logs to `%LOCALAPPDATA%\Clawdmeter\daemon.log`.
### Building the exe
On a machine that *does* have Python (e.g. your dev box), from the repo root:
```powershell
powershell -ExecutionPolicy Bypass -File build-exe.ps1
```
This creates `dist\Clawdmeter.exe` (~3060 MB). The PyInstaller config lives in
`clawdmeter.spec`. The exe is intentionally **not** committed to git — distribute
it through a Gitea release.
> **SmartScreen / antivirus:** unsigned PyInstaller executables are sometimes
> flagged by a generic heuristic (not a real detection). Until the exe is
> code-signed, you may need to allow it through SmartScreen ("More info → Run
> anyway"). Building from source sidesteps this entirely.
---
## Setup from source (one time)
> Use this if you prefer running from a Python checkout instead of the standalone
> exe above (e.g. on your dev box).
Open a PowerShell terminal and `cd` to the repository root.
**1. Create a virtual environment**
```powershell
python -m venv .venv
```
**2. Activate it**
```powershell
.venv\Scripts\Activate.ps1
```
If you see a scripts-execution-policy error, run:
```powershell
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
```
Then repeat the `Activate.ps1` step.
**3. Install dependencies**
```powershell
pip install -r daemon\requirements-windows.txt
```
This installs `bleak` (WinRT BLE) and `httpx` (async HTTP for the Anthropic API).
---
## Running the daemon
With the venv active and the Clawdmeter powered on:
```powershell
python daemon\claude_usage_daemon_windows.py
```
### Expected console output
```
[HH:MM:SS] === Claude Usage Tracker Daemon (BLE, Windows) ===
[HH:MM:SS] Poll interval: 60s
[HH:MM:SS] Scanning for 'Claude Controller' (8.0s)...
[HH:MM:SS] Found: XX:XX:XX:XX:XX:XX
[HH:MM:SS] Connecting to XX:XX:XX:XX:XX:XX...
[HH:MM:SS] Connected
[HH:MM:SS] Sending: {"s":42,"sr":180,"w":17,"wr":8820,"st":"active","ok":true}
```
- **The device must be paired with Windows first** (see [Pair the device](#pair-the-device-one-time)).
The daemon then connects over that existing link via `BleakScanner` + `BleakClient`; it does
not pop its own pairing dialog.
- After `Connected`, the daemon polls the Anthropic API immediately and sends the first
payload within a few seconds of connect (warm token path). With a valid, non-expired token
the device should leave its waiting screen and show session + weekly percentages within
about 10 seconds of launch.
- The daemon then re-polls the Anthropic API every 60 seconds while connected. If the device
fires a refresh request (e.g., after a button press), an immediate re-poll occurs without
waiting for the 60-second interval.
- The **Now Playing** screen updates on a separate, faster cadence: the daemon reads the
Windows media session every 3 seconds and pushes an update the moment the track or
play/pause state changes — so the watch reflects a song change within a few seconds, not at
the next 60-second API poll. The cached usage data is re-sent with each of these updates, so
the rate-limit / token screens never blank between polls.
- If the device disconnects or goes out of range, the daemon logs `Device disconnected` and
re-scans automatically with exponential backoff (starting at 1 second, capped at 60 seconds).
### Stopping
Press **Ctrl+C** in the terminal. The daemon logs `Daemon stopping` and exits cleanly.
---
## Troubleshooting
| Symptom | Likely cause | Fix |
|---------|-------------|-----|
| `Warning: running under Linux/WSL` | Running in WSL, not native Windows | Run from a native PowerShell or Command Prompt on Windows |
| `Scanning for 'Claude Controller'… Device not found` | Clawdmeter is off, out of range, or showing a non-Bluetooth screen | Power on the device and ensure it is on the Bluetooth waiting screen |
| `No token; skipping poll` | No credentials file found at any candidate path | Confirm `claude login` ran on this machine; check `%USERPROFILE%\.claude\.credentials.json` exists |
| `API HTTP 401` | Token expired | Re-run `claude login` in a terminal to refresh the token, then restart the daemon |
| `Connection failed` | WinRT BLE initialisation issue | Ensure Windows Bluetooth is on; try toggling Bluetooth off/on in Windows Settings |
---
## Tray icon, login autostart, and turnkey install
### One-command install (recommended)
> **Copy the repo to a native Windows path first.** Clone or copy this repository
> to a Windows location such as `%USERPROFILE%\Clawdmeter` — **not** a WSL share
> (`\\wsl$\...` or `\\wsl.localhost\...`). Installing from the WSL share would point
> the virtual environment and the login-autostart entry at a path that disappears when
> WSL shuts down, defeating the whole point of the Windows daemon. The installer
> detects a WSL path and refuses to run, telling you how to relocate.
>
> ```powershell
> Copy-Item -Recurse '\\wsl.localhost\Ubuntu\home\<you>\repos\Clawdmeter' "$env:USERPROFILE\Clawdmeter"
> cd "$env:USERPROFILE\Clawdmeter"
> ```
Run this once from the repository root in PowerShell (a native Windows path):
```powershell
powershell -ExecutionPolicy Bypass -File install-windows.ps1
```
The script does four things in order and logs progress at each step:
1. Creates a Python virtual environment at `.venv`.
2. Installs dependencies from `daemon\requirements-windows.txt` (bleak, httpx, pystray, Pillow).
3. Registers the tray app to launch automatically at login via `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` — per-user, no admin required.
4. Launches the tray app immediately (headless — no console window).
The script downloads nothing from the internet. It only installs the packages listed in
the in-repo `daemon\requirements-windows.txt`.
### Tray icon and status
After install, the Clawdmeter icon appears in the Windows notification area:
| State | Icon bubble | Tooltip |
|-------|-------------|---------|
| Connected | green | `Connected · last update HH:MM` |
| Scanning | amber | `Scanning…` |
| Error | red | `Error: token expired — run claude login` |
Hover over the icon to see the current status tooltip. A notification fires once when the
daemon first enters the Error state (e.g. after a token expiry).
### Tray menu
Right-click the tray icon for the menu:
- **Status header** (non-clickable) — live status + last data sync time.
- **Start at login** (checkable toggle) — enables or disables autostart at runtime.
Reflects the current registry state each time the menu opens.
- **Quit** — stops the daemon cleanly and exits with no lingering process. It releases the
daemon's own data connection but does **not** drop the Windows Bluetooth pairing — the
device stays connected to Windows and keeps showing your last-synced usage (point-in-time
view).
### Disabling or removing autostart
Use the tray menu toggle, or remove the registry value manually:
```powershell
reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v Clawdmeter /f
```
### WSL independence
The daemon operates fully independently of WSL. The token is read from native Windows
credential paths (`%USERPROFILE%\.claude\.credentials.json` and fallbacks); BLE uses
the WinRT stack directly. Running `wsl --shutdown` does not affect the BLE link, and
the daemon starts correctly even in a fresh Windows session where WSL has never been
launched.
---
## What is NOT covered here
- Code-signing the standalone `.exe` (to avoid SmartScreen/AV prompts) — future
- MAC-address cache / sleep-wake reconnect hardening — Phase 3