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

TDQS

A3.7/5.0

Scored across 8 tools

Disambiguation5/5

Each tool targets a clearly distinct action: printing (print_cards, print_picture), rendering (preview_cards), cancelling (cancel_print), reprinting (reprint), and read-only queries (list_printers, list_print_jobs, printer_status). print_cards vs print_picture vs reprint are naturally separable by content type and source. No overlap or confusion.

Naming Consistency4/5

Mostly follows a verb_noun snake_case pattern (print_cards, preview_cards, cancel_print, list_printers, list_print_jobs). Minor deviations exist: printer_status is noun_noun rather than a verb, and reprint is a bare verb. Still predictable and readable overall.

Tool Count5/5

Eight tools is well-scoped for a thermal printer server, covering printing, previewing, job management, and device queries. Each tool earns its place with no redundancy.

Completeness4/5

Covers the core print lifecycle: print, preview, list jobs, reprint, cancel, plus printer listing and status. Minor gaps like clearing job history or deleting individual jobs exist but are not essential to core workflows.

Maintenance

ActivityMaintained
ResponsivenessNo issues