Metadata-Version: 2.5
Name: codex-usage-epd
Version: 2026.10.1
Summary: Push OpenAI Codex usage quota to a 4.2" 400x300 three-colour e-paper display over BLE
Project-URL: Homepage, https://github.com/hunterMG/codex-usage-epd
Project-URL: Repository, https://github.com/hunterMG/codex-usage-epd
Project-URL: Issues, https://github.com/hunterMG/codex-usage-epd/issues
Author: hunterMG
License-Expression: AGPL-3.0-or-later
License-File: LICENSE
Keywords: ble,codex,display,e-paper,epd,usage
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Hardware
Requires-Python: >=3.10
Requires-Dist: bleak>=0.21
Requires-Dist: pillow>=10
Requires-Dist: pyyaml>=6
Requires-Dist: requests>=2.31
Provides-Extra: dev
Requires-Dist: pytest; extra == 'dev'
Description-Content-Type: text/markdown

# codex-usage-epd

[![PyPI Version](https://img.shields.io/pypi/v/codex-usage-epd?color=%2334D058)](https://pypi.org/project/codex-usage-epd)
[![PyPI Python Version](https://img.shields.io/pypi/pyversions/codex-usage-epd?color=%2334D058)](https://pypi.org/project/codex-usage-epd)

Push your OpenAI Codex usage statistics to a 4.2" 400x300 three-colour e-paper display
powered by the [EPD-nRF5](https://github.com/tsl0922/EPD-nRF5) firmware.

![Demo of the e-paper usage display](assets/demo.jpeg)

## Features

- Plan quota: 5-hour + weekly windows (used / remaining %, reset time)
- "Tokens used today": a Total across all models, plus the top three models
  with bars proportional to the most-used model
- Optional "Last 7 days" line chart for the seven completed local calendar days
  before today, with dates and compact K/M/B totals. Enable `render.show_history`
  or pass `--show-history` to place the chart beside today's usage
- Credits balance when present on the plan
- Red alert bars when remaining % drops below `render.warn_threshold`
- `--selftest` verifies RLE + bitplane round-trips without network/hardware

## Requirements

- macOS (BLE via bleak/CoreBluetooth), Python 3.10+
- ChatGPT login for Codex (`~/.codex/auth.json` with `tokens.access_token`)
- 4.2" 400x300 B/W/R panel with SSD1683/SSD1619 driver, model id `0x02`

## Install with a coding agent

Copy and paste this prompt into Codex or another coding agent:

```text
Install https://github.com/hunterMG/codex-usage-epd on this computer. Read the
README first, check that the prerequisites are available, clone the repository
into an appropriate user directory, install its dependencies with uv, generate
the configuration file, and run the self-test. Do not print, copy, or modify my
Codex authentication token. Tell me what configuration still needs my input,
and ask for confirmation before installing or starting the launchd background
agent.
```

## Setup

Clone this repo and `cd` to it in terminal.

```sh
uv sync --locked                     # creates .venv, installs deps + the package (editable)
uv run codex-usage-epd --init        # generate config/codex_usage_epd.yaml from the template
# edit config/codex_usage_epd.yaml (set ble.device etc.)
uv run codex-usage-epd --selftest    # render preview.png, no net/BLE
uv run codex-usage-epd --dry-run     # live fetch + render preview.png
```

## Usage

```sh
uv run codex-usage-epd --init          # generate a config from the template
uv run codex-usage-epd --selftest      # sample data, verify encode/decode
uv run codex-usage-epd --dry-run       # fetch + render preview.png (no BLE)
uv run codex-usage-epd --probe         # connect + INIT, print device config/mtu
uv run codex-usage-epd --test-screen   # INIT + CLEAR full refresh (firmware check)
uv run codex-usage-epd --once          # fetch + render + push to display
uv run codex-usage-epd --loop          # push every N minutes forever
```

Modifiers (combine with the actions above):

```sh
--config <path>    # use this YAML config instead of the default
--force            # with --init: overwrite an existing config
--font <path>      # override render.font
--sample           # use synthetic sample data (no network)
--debug            # also dump raw wham/usage JSON to tmp/usage_dump.json
```

Config resolution when `--config` is not given: repo `config/codex_usage_epd.yaml`,
then `~/.config/codex-usage-epd/codex_usage_epd.yaml`, then the bundled template.

`ble.device` accepts `auto` (scans for `NRF_EPD`), a MAC address, or a name
substring.

## Installing as a package

Install the latest release from PyPI:

```sh
uv tool install codex-usage-epd
# or, inside an existing environment:
uv add codex-usage-epd
```

The config template lives at `config/codex_usage_epd.yaml.example`. Run
`codex-usage-epd --init` to generate a real config (`~/.config/codex-usage-epd/`
when installed, `config/` in a checkout), or point at your own with
`--config /path/to/codex_usage_epd.yaml`.

## launchd (every 60 minutes)

```sh
./deploy/install.sh    # fills the __REPO__ placeholder with the checkout path
                       # and bootstraps the agent (works for any username)
```

This generates `~/Library/LaunchAgents/com.codex-usage-epd.plist` from
`deploy/com.codex-usage-epd.plist.in`, so no manual path editing is needed.
To remove the agent later:

```sh
launchctl bootout gui/$(id -u)/com.codex-usage-epd
rm ~/Library/LaunchAgents/com.codex-usage-epd.plist
```

## Configuration

Generate a config with `codex-usage-epd --init`, then edit
`config/codex_usage_epd.yaml` (or `~/.config/codex-usage-epd/` when installed).
Notable keys:

- `display.width` / `display.height` - panel pixels (`400`x`300`); `display.model_id` - EPD panel model id (`0x02` for SSD1619 400x300 BWR)
- `display.sleep_after_push` - send the SLEEP command after pushing (default `false`; early sleep aborts the panel refresh)
- `ble.device` - `auto` | MAC address | name substring
- `ble.scan_timeout` - seconds per scan attempt; `ble.scan_retries` - retry the scan before giving up (missed advertisements)
- `ble.interleave` - writes per response-ack, mirrors the web client (`50`)
- `ble.pacing_ms` - delay between writes (`0.0` = back-to-back)
- `ble.hold_after_refresh` - seconds to stay connected so the panel refresh completes (`20`)
- `ble.slot` - which image slot to write on the epdiy.cn slot firmware (`0..N-1`); `"auto"` picks the first free slot so an existing slot is never overwritten; `-1`/`"none"` disables the `SET_SLOT` command (plain EPD-nRF5 firmware)
- `codex.auth_file` - where the OAuth tokens live (default `~/.codex/auth.json`)
- `codex` reads `chatgpt_base_url` from `~/.codex/config.toml` if set
- `render.font` - `.ttf` path (auto-detected on macOS/Windows/Linux otherwise)
- `render.warn_threshold` - remaining % that turns a bar red
- `render.preview` - filename for the rendered preview PNG
- `render.show_history` - show the "Last 7 days" chart (default `false`); override with `--show-history` or `--no-show-history`, including in `--selftest`
- `schedule.interval_minutes` - interval used by `--loop`; launchd uses the fixed `StartInterval` in `deploy/com.codex-usage-epd.plist.in`

## Notes

- Tokens are read from `~/.codex/auth.json` and never logged or committed.
- The display must be powered (USB). With `ble.patch_wakeup_pin: true` it forces
  the device config byte to `0xFF` so the panel keeps re-advertising after the
  timeout, letting the scheduled push always connect.

## Development

```sh
uv sync --locked
uv run pre-commit install
uv run pre-commit run --all-files
```

## License

GNU AGPL v3.0 or later — see [LICENSE](LICENSE).
