Pulling the USB Bluetooth dongle made BleakScanner raise from inside the main
loop; the exception propagated out of daemon_main, the bg thread died, and the
tray froze on its last state with no recovery (reported: "daemon crashed and
doesn't come back").
Two layers of hardening:
1. main() loop now wraps scan+connect in try/except — adapter-gone errors are
logged, the tray shows Scanning, and it backs off and retries, so replugging
the adapter recovers automatically. CancelledError is re-raised.
2. The tray supervises the asyncio loop: an unexpected crash is logged and the
loop is RESTARTED after a backoff, gated by a quit_evt so Quit still stops
cleanly (and call_soon_threadsafe is guarded against a closed loop).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two bugs made the Session numbers wildly inflated (~$300/122M when the real
figures were ~$46/57M):
1. No de-duplication. Claude Code copies prior history into a new transcript
file on every compaction/resume, so the same API turn appears in several
.jsonl files. Summing all lines counted each turn multiple times — ~2.7x
inflation today (a heavily-compacted session). De-dup by (messageId,
requestId), matching ccusage.
2. Wrong Opus pricing. Used the older $15/$75 in/out rates; claude-opus-4-x is
actually $5/$25 (cache-write $10, cache-read $0.50) — 3x too high. Rates
verified against ccusage/LiteLLM, which they now reproduce to 100%.
Verified end to end against a fresh ccusage run: total tokens, output tokens and
cost all match exactly (57,256,809 tok / $45.63). Daemon-only change; no firmware
reflash needed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The total is ~97% cache reads, which reads as implausibly large on its own.
Show the cache-inclusive total as the headline token number and the output
tokens ("what Claude actually generated", ~1.1M) as a small line beneath it —
impressive total, believable sub-number. Daemon sends a new `to` field; firmware
parses output_today and renders it. Re-spaced the Session layout for the 5th row.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Daemon: compute_today_usage() sums today's Claude Code token usage across the
local project transcripts (~/.claude/projects/**/*.jsonl), reading only files
modified today so the scan stays cheap, and prices it with Anthropic list rates
to show the equivalent API cost (the ccusage-style flex for subscription users).
The compact fields tk (total tokens), tc (cost in cents) and tn (message count)
piggyback onto the existing 60s BLE payload.
Firmware: UsageData gains tokens_today (64-bit) / cost_cents_today /
messages_today; parse_json reads tk/tc/tn; a new Session screen shows the cost
as the hero with total tokens and message count below, refreshed from ui_update
so it's current whenever opened. Wire SCREEN_SESSION to the real screen (was a
stub). Verified end to end: daemon sends e.g. tk=105374109 tc=26838 tn=663.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New board firmware/src/boards/waveshare_amoled_206/ (CO5300 410x502,
FT3168 touch, AXP2101 PMU, QMI8658 IMU). PlatformIO env
waveshare_amoled_206. CO5300_COL_OFFSET=22, fixed orientation,
PWR button on GPIO10, FocalTech inline touch reader.
- Windows daemon: fix advertised device name (Claude Controller ->
Clawdmeter) to match firmware ble.cpp, and add CLAWDMETER_ADDRESS env
fallback so it connects by address when the bonded HID device is not
advertising (Windows keeps it HID-connected).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>