A standalone Claude Code usage monitor for the desk: the tide is your token budget. No host software, no companion daemon - the device connects to your WiFi and polls Anthropic directly with a dedicated long-lived token, showing your usage on whatever screen it has.
Built as an ESPHome package with per-board implementations over a shared engine: adopt it from the ESPHome Builder (inside or outside Home Assistant) with a dozen lines of YAML, update over-the-air by bumping a release tag, and pick the board entry that matches your hardware.
![]() |
![]() |
| The crab sails a sea that IS your usage | Main screen: readable from across the room |
![]() |
![]() |
| Same sea, pocket size | Minimal number-first UI |
- 5-hour window utilization as a hero number readable from desk distance
- 5h and 7-day bars with reset countdowns
- Status line: limit status, battery, and the polling cost (requests/day) so you always know what the monitor itself consumes
- On-screen touch button to force a refresh
One codebase, per-board implementations: every board gets its own entry
under boards/ combining the shared usage engine with hardware support
and a UI sized to what the device can do. All boards update the same way
(bump the release tag, flash OTA).
| Board | Import file | Status | UI |
|---|---|---|---|
| M5Stack CoreS3 / Stack-chan | boards/m5stack-cores3.yaml |
supported | rich 320x240 touch: hero number, bars, countdowns, touch refresh |
| M5Stack CoreS3 SE | boards/m5stack-cores3.yaml |
untested, should work | same as CoreS3 (no battery gauge) |
| M5StickC Plus | boards/m5stickc-plus.yaml |
supported, tested on hardware | minimal 240x135: hero number, 7d bar, countdown; front button = refresh |
| M5StickC Plus + Joystick Hat | boards/m5stickc-plus-joy.yaml |
supported | minimal + joystick: press = refresh, Y-axis = brightness |
| M5StickC Plus2 | planned | - | minimal (same UI class) |
Want another board? See CONTRIBUTING.md - the layout is designed for drive-in board additions.
Anthropic exposes the unified rate-limit state as response headers on API
calls. TokenTide sends a minimal ~1-token probe request to /v1/messages
with your Claude Code OAuth token and reads
anthropic-ratelimit-unified-{5h,7d}-{utilization,reset} from the response.
At the default 120s interval that is ~720 tiny requests/day; the footer
shows the figure for your configured interval.
The token comes from claude setup-token (valid one year, scoped to
inference). Treat it like a password: it lives in your ESPHome secrets and
never leaves the device except toward api.anthropic.com over TLS.
-
Generate a token on any machine with Claude Code:
claude setup-token -
In the Builder's Secrets editor add one entry (your WiFi secrets are usually already there from the wizard):
claude_token: "sk-ant-oat01-..."
-
Create the device with the New Device wizard as usual (pick your board or any ESP32-S3 entry - our package overrides what matters). The wizard generates a YAML with
esphome:,api:,ota:,wifi:andcaptive_portal:blocks. Keep all of them, and paste this at the bottom of the file:substitutions: claude_token: !secret claude_token # poll_interval: "120" # optional override, seconds packages: tokentide: url: https://github.com/eldios/tokentide ref: stable # latest release, auto-updating files: [boards/m5stack-cores3.yaml] # pick your board from the table refresh: 1d
To use a translated UI, add the language pack to the same list:
files: [boards/m5stack-cores3.yaml, lang/it.yaml]. -
First install: connect the device over USB and use "Install via USB" (or web.esphome.io from any browser).
-
Updates: with
ref: stablejust hit Install - thestablebranch always points at the latest release, andrefresh:controls how often the Builder re-fetches it. Prefer full control? Pinref: v0.0.3and bump it yourself per release;ref: mainrides the bleeding edge.
git clone https://github.com/eldios/tokentide
cd tokentide
cp secrets.example.yaml secrets.yaml # fill wifi + claude_token
esphome run tokentide.yaml # first time over USB, then OTA
Nix users: nix develop provides esphome and esptool.
UI strings default to English. Ready-made packs live in lang/
(it, de, fr, es): add one to the files: list after the board
entry (see Quick start), or override individual str_* substitutions
directly in your config (top-level substitutions always win).
Build fails with an error that was already fixed (even after "Clean
Build Files"): the Builder caches the git package for the refresh:
interval, and cleaning build files does not clear that cache. Set
refresh: 0s in the packages: block, hit Install once to force a
re-fetch, then restore refresh: 1d.
Compiler warnings from mipi_spi.cpp (-Wempty-body): these come
from ESPHome's own display component, not from tokentide - harmless,
safe to ignore.
Device reboots every ~15 minutes (screen suddenly back to "waiting
for data"): ESPHome's native API reboots the device when no Home
Assistant client stays connected for reboot_timeout (default 15min).
tokentide disables that failsafe since v0.0.5; on older versions add
reboot_timeout: 0s under the api: block of your device YAML. If you
DO use Home Assistant and see this, HA is not holding its connection to
the device - re-add the ESPHome integration (see below).
The device exposes its sensors natively (5h/7d utilization, limit status, Anthropic status-page indicator, battery) plus a full set of config entities: display (dim timeout/brightness, screen-off timeout, screensaver enable/timeout/metric), screensaver scenery (stars, moon, clouds, sea/sky fauna), session chime (enable, melody, volume, test button) and, on boards with one, the LED (mode select + light entity). Automations like "notify me at 80% usage" are a two-line HA automation away - no extra firmware work.
If the device does not appear automatically: mDNS discovery does not
cross VLANs, so on segmented networks (device on an IoT VLAN, HA
elsewhere) you must add it manually - Settings > Devices & services >
Add integration > ESPHome > host tokentide.local (or its IP), port
6053. OTA and logs from the ESPHome Builder work either way, since they
use direct routing rather than discovery.
For UI development only - not part of the published package. Opt in by adding both blocks to your device YAML:
external_components:
- source: github://eldios/tokentide@main
components: [tokentide_screenshot]
tokentide_screenshot:Then tools/screenshot.sh <device-ip> out.png grabs the live LVGL
screen (HTTP on port 8081, LAN-only, unauthenticated - leave it out of
day-to-day configs).
- Stack-chan body features: servo gestures, LEDs, and friends
This project stands on the shoulders of two lovely projects - thank you:
- claude-usage-stick by @oauramos - pioneered the standalone approach and the rate-limit-header probe this project uses.
- Clawdmeter by @HermannBjorgvin - the desk-distance UX this project chases, and the proof that a Claude usage meter belongs on every desk.
Hardware support comes from M5Stack's official ESPHome components.
MIT - see LICENSE.








