Skip to main content
Glama
s04

phomemo

by s04
README.md
# phomemo

Print on a **Phomemo M02 Pro** pocket thermal printer from your computer, without the
phone app. Includes a command-line tool and an **MCP server**, so Claude and other
AI agents can print cards, checklists and pictures for you.

- Markdown in, neat cards out: titles, bullets, checkboxes, numbered lists.
- Pictures are scaled to the paper and dithered for thermal printing.
- Print jobs you can list, cancel (cleanly, between blocks) and reprint.
- Waits for the printer to confirm that each card actually printed.
- Reads battery, paper, lid and temperature.
- Runs locally over Bluetooth LE on macOS and Linux (and in principle Windows,
  via [bleak](https://github.com/hbldh/bleak)).

## Supported printers

| Model | Status |
|---|---|
| **M02 Pro** | Tested: printing, cancel, print-complete signal, status |
| M02S | Same protocol and resolution as the M02 Pro; untested |
| M02, T02 | Same protocol at 203 dpi; untested |

Other Phomemo families (M110, M04, D30, …) use different protocols and aren't
supported yet. See [Contributing](#contributing).

**Paper:** 53mm-wide continuous rolls (e.g. 53mm × 6.5m). The print head
covers the middle 48mm (576 dots at 300 dpi).

> The M02 Pro's USB-C port only charges. It carries no data, so Bluetooth is
> the only way to print.

## Install

```sh
uv tool install git+https://github.com/s04/phomemo
# or: pipx install git+https://github.com/s04/phomemo
```

Requires Python 3.11+.

## Use

Turn the printer on and close the Phomemo phone app, because the printer accepts
only one connection at a time.

```sh
phomemo scan                                  # find printers nearby
phomemo "# Groceries" "- [ ] oat milk" "- [ ] bread"
echo "# Idea" | phomemo                       # from stdin
phomemo -f notes.md                           # from a file
phomemo --preview -f notes.md                 # save a PNG instead of printing
phomemo image photo.jpg                       # print a picture
phomemo status                                # battery, paper, lid, temperature
```

### Card format

```markdown
# Title                 ← every "# " starts a new card
## Heading
A paragraph of text.
- bullet
1. numbered
- [ ] todo
- [x] done
> aside
---                     ← also starts a new card
```

The printable width is 48mm (about 25 characters per line), so short cards
work best.

### Jobs

```sh
phomemo jobs                     # recent jobs, status and progress
phomemo cancel                   # stop everything queued or printing
phomemo cancel 0923-1651         # stop one job (any unique id prefix)
phomemo reprint                  # print the last job again
```

- **Ctrl-C** cancels cleanly; press it twice to force quit.
- Cancelling stops between image blocks (at most about 2cm of paper) and feeds
  the paper out. The printer is left ready for the next job.
- Cancelling works across processes: `phomemo cancel` stops a print an AI agent
  started.
- Only one job talks to a printer at a time. The others wait as `queued`.
- Job statuses:
  - `done`: the printer confirmed every card printed.
  - `unconfirmed`: all data was sent but the printer never confirmed. Check the
    output; a low battery is the usual cause.
  - `failed`, `cancelled`.

Job history lives in `~/.local/state/phomemo/jobs` (override with
`PHOMEMO_STATE`).

## MCP server (Claude and other agents)

```sh
phomemo mcp                          # stdio
phomemo mcp --http 127.0.0.1:8765    # streamable HTTP at /mcp
```

Tools: `print_cards`, `preview_cards` (returns images, so the agent can check
the layout first), `print_picture`, `printer_status`, `list_print_jobs`,
`cancel_print`, `reprint`, `list_printers`. The print tools wait up to 15
seconds. A longer print keeps running in the background and the tool returns
its job id.

**Claude Code**

```sh
claude mcp add phomemo -s user -- phomemo mcp
```

**Claude Desktop** (`claude_desktop_config.json`):

```json
{ "mcpServers": { "phomemo": { "command": "phomemo", "args": ["mcp"] } } }
```

Use the full path from `which phomemo` if the app can't find the command.

## Configuration

Optional. Without a config file you get one printer, `m02pro`, found
automatically. `phomemo config` shows where the file goes, with an example:

```toml
default = "desk"

[printers.desk]
model = "m02pro"
# address = "…"    # from `phomemo scan`; skips discovery, picks one of several printers
# feed = 3         # blank lines after each card
```

Driver options are listed in `src/phomemo/drivers/m02.py`.

## Troubleshooting

- **"not found over Bluetooth"**: turn the printer on and close the Phomemo
  app. On macOS, the app running `phomemo` (Terminal, iTerm, Claude…) needs
  Bluetooth access under System Settings → Privacy & Security → Bluetooth.
- **The light blinks fast**: the paper is out, the printer is too hot, or the
  battery is low. `phomemo status` tells you which.
- **It won't charge from a USB-C charger**: use a 5V USB-A charger with an
  A-to-C cable. Some USB-C chargers never switch on power for this printer.
  Phomemo warns that chargers above 5V can damage it.
- **Photos look muddy**: pictures are contrast-stretched and brightened before
  dithering, but thermal paper suits strong contrast. Bold images print best.

## Thermal paper

Most thermal paper contains BPA or BPS, and both are absorbed through the skin.
"BPA-free" usually means BPS. If you'll handle the prints a lot, look for
**phenol-free** paper.

## How it works

The protocol was reverse-engineered by others (see Credits). What this project
verified on a real M02 Pro, including the print-complete signal and the status
replies, is in [PROTOCOL.md](PROTOCOL.md).

```
src/phomemo/
  cards.py        Markdown → cards
  render.py       cards / pictures → grayscale images at printer width
  drivers/m02.py  M02 family: raster encoding, Bluetooth transport, status
  jobs.py         job records, per-printer queue, cancel, reprint
  api.py          shared by the CLI and the MCP server
  cli.py          `phomemo`
  mcp_server.py   `phomemo mcp`
```

## Contributing

Issues and pull requests are welcome, especially reports from **M02S, M02 and
T02** owners (does printing work, and does `phomemo status` answer?).

```sh
uv sync
uv run pytest
```

⚠️ **Don't probe unknown `1F 11 NN` commands.** Sweeping opcodes above `0x14`
[bricked an M02X](https://github.com/sgrankin/phomemo/blob/HEAD/PROTOCOL.md).
Stick to documented commands.

## Credits

The protocol knowledge comes from
[vivier/phomemo-tools](https://github.com/vivier/phomemo-tools),
[ryo-endo/phomemo-printer](https://github.com/ryo-endo/phomemo-printer),
[theacodes/phomemo_m02s](https://github.com/theacodes/phomemo_m02s) and
[sgrankin/phomemo](https://github.com/sgrankin/phomemo). This project
re-implements it and contains no code from those projects.

Not affiliated with Phomemo.

## License

MIT