Push your OpenAI Codex usage statistics to a 4.2" 400x300 three-colour e-paper display powered by the EPD-nRF5 firmware.
- 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_historyor pass--show-historyto place the chart beside today's usage - Credits balance when present on the plan
- Red alert bars when remaining % drops below
render.warn_threshold --selftestverifies RLE + bitplane round-trips without network/hardware
- macOS (BLE via bleak/CoreBluetooth), Python 3.10+
- ChatGPT login for Codex (
~/.codex/auth.jsonwithtokens.access_token) - 4.2" 400x300 B/W/R panel with SSD1683/SSD1619 driver, model id
0x02
Copy and paste this prompt into Codex or another coding agent:
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.
Clone this repo and cd to it in terminal.
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.pnguv 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 foreverModifiers (combine with the actions above):
--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.jsonConfig 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.
Not published to PyPI yet. Install from the locally built wheel:
uv build # builds sdist + wheel into dist/
uv pip install dist/*.whl # or: uv pip install --python <venv> dist/*.whlThe 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.
./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:
launchctl bootout gui/$(id -u)/com.codex-usage-epd
rm ~/Library/LaunchAgents/com.codex-usage-epd.plistGenerate 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 (400x300);display.model_id- EPD panel model id (0x02for SSD1619 400x300 BWR)display.sleep_after_push- send the SLEEP command after pushing (defaultfalse; early sleep aborts the panel refresh)ble.device-auto| MAC address | name substringble.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 theSET_SLOTcommand (plain EPD-nRF5 firmware)codex.auth_file- where the OAuth tokens live (default~/.codex/auth.json)codexreadschatgpt_base_urlfrom~/.codex/config.tomlif setrender.font-.ttfpath (auto-detected on macOS/Windows/Linux otherwise)render.warn_threshold- remaining % that turns a bar redrender.preview- filename for the rendered preview PNGrender.show_history- show the "Last 7 days" chart (defaultfalse); override with--show-historyor--no-show-history, including in--selftestschedule.interval_minutes- interval used by--loop; launchd uses the fixedStartIntervalindeploy/com.codex-usage-epd.plist.in
- Tokens are read from
~/.codex/auth.jsonand never logged or committed. - The display must be powered (USB). With
ble.patch_wakeup_pin: trueit forces the device config byte to0xFFso the panel keeps re-advertising after the timeout, letting the scheduled push always connect.
uv sync --locked
uv run pre-commit install
uv run pre-commit run --all-filesGNU AGPL v3.0 or later — see LICENSE.
