BooxPlanner
A bidirectional handwriting loop between an e-ink tablet and Notion.
- 2025—
- in progress
- Architect & Implementer
- Python, WeasyPrint, Jinja2, n8n, Gemini Vision, Notion API, Google Fit
A bidirectional loop between a paper-feel e-ink tablet and a Notion task graph — morning generation live, evening read-back in progress. Pen for input, structured data as the source of truth, an LLM breaking goals into daily detail.
2026-05-12
The technical breakdown
The business problem
I've always written by hand — todos, plans, notes — but past a certain load that stopped scaling. Notion held the structure; a phone app held the convenience; the paper notebook held the one thing that actually mattered, the physical act of writing. Each solved part of the problem and broke another.
Pen on screen for input, a structured database as the source of truth, and something in between doing the translation. In the age of AI you don't have to pick — you build both.
The hard constraint is the hierarchy: a quarterly goal that appears identically on the monthly, weekly, and daily pages defeats the reason for printing a planner. So an LLM breaking goals into level-appropriate detail isn't decoration — it's the part that makes the printed page worth printing.
The architecture
A morning pipeline behind a FastAPI service in a Docker container on a home server. A cron POSTs to /generate; the service reads the Notion databases, asks an LLM to break quarterly goals into a monthly focus, weekly goals, and a daily focus (cached weekly so the daily run is mostly free), renders a Jinja2 template, hands the HTML to WeasyPrint, and uploads a weekly PDF to a cloud-drive folder the tablet pulls.
The evening half — planned, not yet live — reads the annotations back: a drive-change trigger, a pixel-diff to find only the pages I actually wrote on, then vision OCR into Notion. Underneath sits a reverse-engineered .note point-binary parser: 16-byte stroke records behind an 80-byte header, with an inter-stroke separator that shifts alignment and a resync on impossible coordinates.
Decisions & trade-offs
Two folders, not one. Generated pages go down one cloud folder; annotated pages come back up a different one. They physically can't feed each other, so the morning generator can never re-ingest its own output — the simplest possible defense against a sync loop.
Diff before you spend. Before sending anything to a vision model, the system pixel-diffs the clean and annotated PDFs and sends only the changed pages — typically one to three of twelve. Cost tracks edits, not document size.
Most of "AI engineering" on a budget is knowing where not to call the model.
Evening deferred until morning is solid. The /process-annotations endpoint is a 501 stub on purpose. Bringing both halves online at once doubles the surface for half-working dependencies — a flaky OCR step would mask a flaky generation step. One direction has to be solid in production before the other depends on it.
What broke
The health-data path took two attempts: a direct device login prompted for an MFA code, which works at a terminal but not under cron, so the pull moved behind a REST API. The tablet's annotation storage turned out to be hostile to overwrites — the app rewrites its caches from memory unless it's closed — so a weekly filename rotation became the cleanest path out. And the .note parser walks off-by-four whenever its inter-stroke separator happens to look like the start of a record, which is why it resyncs on coordinates that can't be on-screen.
The outcome
The morning generation half is deployed and runs (first production run 2026-05-12). The evening read-back is built in pieces but not wired live — /process-annotations is still a stub — and there's no OCR-accuracy measurement yet.
The honest open problem isn't the code — it's the habit. Daily adoption is as much behavioural as technical, and that's the part I haven't solved.