{"id":50310770,"url":"https://github.com/george-vice/ccusage-mqtt","last_synced_at":"2026-06-14T12:00:44.290Z","repository":{"id":358473957,"uuid":"1240855330","full_name":"george-vice/ccusage-mqtt","owner":"george-vice","description":"Publish Claude Code usage telemetry to MQTT for Home Assistant (Clawdmeter-compatible mood/burn-rate sensors)","archived":false,"fork":false,"pushed_at":"2026-06-13T23:36:17.000Z","size":993,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-14T01:19:39.684Z","etag":null,"topics":["anthropic","claude","claude-code","docker","home-assistant","homeassistant-integration","mqtt","python"],"latest_commit_sha":null,"homepage":"https://george-vice.github.io/ccusage-mqtt/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/george-vice.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-16T16:49:19.000Z","updated_at":"2026-06-13T23:36:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/george-vice/ccusage-mqtt","commit_stats":null,"previous_names":["george-vice/ccusage-mqtt"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/george-vice/ccusage-mqtt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/george-vice%2Fccusage-mqtt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/george-vice%2Fccusage-mqtt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/george-vice%2Fccusage-mqtt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/george-vice%2Fccusage-mqtt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/george-vice","download_url":"https://codeload.github.com/george-vice/ccusage-mqtt/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/george-vice%2Fccusage-mqtt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34320273,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anthropic","claude","claude-code","docker","home-assistant","homeassistant-integration","mqtt","python"],"created_at":"2026-05-28T21:00:24.395Z","updated_at":"2026-06-14T12:00:44.272Z","avatar_url":"https://github.com/george-vice.png","language":"Python","funding_links":[],"categories":["Interfaces"],"sub_categories":["Monitoring"],"readme":"# ccusage-mqtt\n\nPublishes Claude Code usage telemetry to MQTT with Home Assistant\nauto-discovery. Mirrors the\n[Clawdmeter](https://github.com/HermannBjorgvin/Clawdmeter) ESP32 firmware's\ntelemetry surface so the same `mood` / burn-rate thresholds apply.\n\nOnce running, your Home Assistant gains a `Claude Code Usage` device with\nsensors for current 5h and 7d utilisation %, burn rate %/min, an enum\n`mood` sensor (idle / normal / active / heavy), time-to-limit, tokens \u0026\nspend so far, hourly token and spend rates, and reset countdowns for both\nwindows.\n\nSee [`docs/design.md`](docs/design.md) for the full design.\n\n## Prerequisites\n\n- A working Claude Code installation on the host (the publisher reads\n  `~/.claude/.credentials.json` for the OAuth token — no separate\n  Anthropic API key is required)\n- Node.js + npm if installing on-host (the publisher shells out to\n  [`ccusage`](https://github.com/ryoppippi/ccusage) for token/$ totals).\n  Not needed if you use the Docker path — the image installs it.\n- Python ≥ 3.12 if installing on-host\n- A Home Assistant instance with an MQTT broker reachable on your LAN.\n  See **Home Assistant MQTT setup** below if you don't have one yet.\n\n## Install\n\n### Option A: From source (recommended)\n\nRuns as a regular Python process — manage it under `systemd`/`launchd`/etc.\n\n```bash\n# 1. Install ccusage (Node) globally — the publisher shells out to it\nnpm install -g ccusage\n\n# 2. Install the publisher\ngit clone https://github.com/george-vice/ccusage-mqtt.git\ncd ccusage-mqtt\npipx install .\n# (or `pip install .`)\n\n# 3. Configure\n./setup.sh                  # writes ./.env in the repo root\n# (or skip setup.sh and set env vars by hand — MQTT_HOST is the only required one)\n\n# 4. Run\nccusage-mqtt                # picks up ./.env from the current directory\n# or:\nccusage-mqtt --env-file /path/to/anywhere/.env\n```\n\nDefaults resolve to `~/.claude/.credentials.json` and `~/.claude/projects/`\non disk automatically — no bind-mount to worry about. To run it as a\nservice, drop something like this into\n`~/.config/systemd/user/ccusage-mqtt.service`:\n\n```ini\n[Unit]\nDescription=ccusage-mqtt — Claude Code → MQTT publisher\nAfter=network-online.target\n\n[Service]\nExecStart=%h/.local/bin/ccusage-mqtt --env-file %h/.config/ccusage-mqtt/.env\nRestart=on-failure\nRestartSec=10\n\n[Install]\nWantedBy=default.target\n```\n\nThen `systemctl --user enable --now ccusage-mqtt`.\n\n### Option B: Docker\n\nSelf-contained — bundles Node + `ccusage` + Python in one image. Use this\nif you'd rather not install Node/Python on the host.\n\n```bash\ngit clone https://github.com/george-vice/ccusage-mqtt.git\ncd ccusage-mqtt\n./setup.sh                  # prompts for MQTT broker + per-instance identity\ndocker compose up -d --build\ndocker compose logs -f\n```\n\n## Home Assistant MQTT setup\n\n`ccusage-mqtt` is a publisher. Home Assistant needs an MQTT broker to\nreceive the messages and the MQTT integration to render them. If you don't\nalready have those, set them up first:\n\n1. **Install an MQTT broker.** The simplest path on Home Assistant OS is the\n   **Mosquitto broker** add-on:\n   - Settings → Add-ons → Add-on Store → \"Mosquitto broker\" → Install → Start.\n   - Any other MQTT broker on your LAN (a standalone mosquitto, EMQX, HiveMQ,\n     etc.) works equally well — skip this step and point at your existing\n     broker in step 3.\n\n2. **Create a Home Assistant user for MQTT** (recommended — keeps broker\n   credentials separate from your login).\n   - Settings → People → Users → Add User. Give it a username and password.\n     \"Can only log in from the local network\" is fine.\n\n3. **Add the MQTT integration to HA.**\n   - Settings → Devices \u0026 Services → Add Integration → \"MQTT\".\n   - Broker: the hostname/IP of your broker. If you're using the Mosquitto\n     add-on, this is your HA host (e.g. `homeassistant.local`, or your\n     server's LAN IP). With the add-on, `core-mosquitto` also works from\n     other add-ons but not from external hosts.\n   - Username / password: the user from step 2.\n   - **MQTT auto-discovery should be enabled by default.** This is what makes\n     the 15 sensors appear automatically when `ccusage-mqtt` starts publishing.\n\nThat's the HA side. You'll re-enter the broker hostname and the same\nusername/password into `./setup.sh` below.\n\nAfter either install path, Home Assistant auto-discovers a `Claude Code Usage`\ndevice with 15 entities within ~60s under Settings → Devices \u0026 Services → MQTT.\n\nRe-run `./setup.sh` any time to reconfigure, or hand-edit `.env` directly.\n\n## Blueprints\n\nDrop-in automations you can import into Home Assistant with one click.\n\n### Heavy-usage alert\n\nFires a notification (or any action you pick) when `mood` stays in `heavy`\nfor a configurable duration — catches runaway burn-rate sessions before\nyou hit the 5h limit.\n\n[![Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fgeorge-vice%2Fccusage-mqtt%2Fblob%2Fmaster%2Fblueprints%2Fautomation%2Fccusage-mqtt%2Fheavy-usage-alert.yaml)\n\nSource: [`blueprints/automation/ccusage-mqtt/heavy-usage-alert.yaml`](blueprints/automation/ccusage-mqtt/heavy-usage-alert.yaml).\n\n## Configuration\n\n`./setup.sh` only asks for the four values you almost always need to set\n(MQTT host / port / user / pass). Everything else takes sensible defaults\nfrom `.env.example`. Notable optional knobs:\n\n| Var | Default | Purpose |\n|---|---|---|\n| `CLAUDE_CREDENTIALS_PATH` | `~/.claude/.credentials.json` (source) / `/data/claude-projects/.credentials.json` (Docker) | Where the publisher finds Claude Code's OAuth token. Resolves automatically based on environment; only set this if your Claude Code config lives somewhere unusual. |\n| `OAUTH_REFRESH_DISABLED` | _unset_ | Set to `true` if the credentials dir is also used by Claude Code CLI on the same host. See [Sharing the credentials dir with Claude Code CLI](#sharing-the-credentials-dir-with-claude-code-cli) below. |\n| `PROBE_MODEL` | `claude-haiku-4-5-20251001` | Model used for the ratelimit-probe API call. Cheapest current Anthropic model. |\n| `HEADER_POLL_SEC` | `60` | How often to refresh the 5h / 7d utilisation from Anthropic |\n| `CCUSAGE_POLL_SEC` | `30` | How often to refresh token / cost data from `ccusage` |\n| `BURN_RATE_WINDOW_SEC` | `240` | Ring-buffer span for burn-rate smoothing. 240 matches the Clawdmeter firmware. |\n| `MOOD_*_BELOW` | `0.10 / 0.20 / 0.33` | Mood threshold %/min boundaries (Pro/Max only). Defaults match the Clawdmeter firmware. |\n| `MOOD_TOKENS_*_BELOW` | `500 / 2500 / 10000` | Mood threshold tokens/hour boundaries — used on Enterprise plans where session_pct is overage-utilization (stuck at 0 until you blow past base allocation). |\n\n## How it works\n\nTwo pollers run in a single Python process:\n\n- **Anthropic API headers** (every 60s). Sends a 1-token probe to\n  `/v1/messages` using your Claude Code OAuth token and reads the\n  `anthropic-ratelimit-unified-*` response headers — the authoritative\n  source for current 5h / 7d window % used, reset times, and allowed/limited\n  status.\n- **`ccusage` CLI** (every 30s). Reads your local Claude Code session JSONLs\n  to get current-block token count and cost in USD.\n\nA ring buffer over the 5h % samples produces a smoothed burn-rate (%/min),\nwhich classifies into one of four moods using the same thresholds as the\nfirmware. All 15 sensors publish to MQTT with the `retain` flag so values\nsurvive broker restarts.\n\nWhen an access token expires, the publisher uses the stored `refreshToken`\nto mint a fresh one at Anthropic's OAuth endpoint and writes it back to the\ncredentials file in-place — no manual `claude login` needed. If the refresh\nitself fails (only happens if the refresh token has also expired), the\ncontainer does **not** crash-loop: it marks `session_status: \"unknown\"` in\nHA and keeps publishing token/$ sensors from `ccusage`, self-healing on the\nnext poll once you re-login.\n\n### Sharing the credentials dir with Claude Code CLI\n\nIf the publisher and Claude Code CLI both write to the same\n`~/.claude/.credentials.json` — the default single-account setup — they\nwill race for the rotating refresh token. Anthropic issues refresh tokens\nsingle-use, so whichever process loses the race ends up with\n`invalid_grant: Refresh token not found or invalid` and is stuck until a\nmanual `claude` re-login.\n\nSet `OAUTH_REFRESH_DISABLED=true` in `.env` on these hosts. The publisher\nwill skip its own refresh entirely and just consume whatever `accessToken`\nthe CLI has most recently written to the file; the CLI owns rotation. On\na 401 the publisher marks `session_status: \"unknown\"` until the CLI's next\nuse refreshes the file. Token/$ sensors from `ccusage` continue regardless.\n\nLeave the flag unset for dedicated config dirs that nothing else writes to\n(e.g. a second `~/.claude-work` for a separate account) — the publisher's\nself-refresh works fine there.\n\nBoth Claude **Pro/Max** and **Enterprise** plans are supported. Enterprise\nreturns a different ratelimit header schema (no 5h or 7d window — overage\nallowance instead); the parser handles both. Enterprise users get the\n`session_*` sensors populated from overage data; `weekly_*` sensors stay\n`null` (correct, not a bug). Because overage-utilization is 0% until you\nblow past your base allocation, burn-rate is a dead signal on Enterprise —\nso `mood` instead classifies off `tokens_per_hour` (from `ccusage`) using\nthe `MOOD_TOKENS_*_BELOW` thresholds. On Pro/Max, mood continues to use\nthe %/min burn rate exactly as the Clawdmeter firmware does.\n\n## Troubleshooting\n\n- **All sensors stuck at `unknown`** — the OAuth probe is failing. Check\n  `docker compose logs \u003ccontainer\u003e` for the exact error. The publisher\n  tries to auto-refresh the access token using the stored `refreshToken`;\n  if you see `OAuth refresh failed: 400 invalid_grant`, the refresh\n  token has also expired and a manual re-login is required:\n  `rm ~/.claude/.credentials.json \u0026\u0026 claude` (i.e. re-run `claude` to log\n  in again). The publisher self-heals within the next 60s poll cycle.\n- **`invalid_grant` returns roughly daily on a single-account host** — the\n  publisher and Claude Code CLI are racing for the rotating refresh token.\n  Set `OAUTH_REFRESH_DISABLED=true` in `.env` and let the CLI own token\n  rotation. See [Sharing the credentials dir with Claude Code CLI](#sharing-the-credentials-dir-with-claude-code-cli).\n- **`mood` stuck at `idle` past 4 minutes** — your actual burn rate is below\n  0.10 %/min. This is normal — Claude Code isn't being used heavily right\n  now. Lower `MOOD_IDLE_BELOW` if you want a more sensitive scale.\n- **No `tokens_used` value** — `ccusage` couldn't find your session JSONLs.\n  Verify `~/.claude/projects/` exists on the host with at least one session\n  in it.\n- **HA never sees the device** — check that auto-discovery is enabled on the\n  MQTT integration (Settings → Devices \u0026 Services → MQTT → Configure → \"Enable\n  discovery\"). With it enabled, the device should appear within 60s of the\n  container starting.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeorge-vice%2Fccusage-mqtt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgeorge-vice%2Fccusage-mqtt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeorge-vice%2Fccusage-mqtt/lists"}