LX-D02 receipt printer
Print on an LX-D02 Bluetooth thermal receipt printer (58 mm paper, sold e.g. as Dolebo, used with the Funny Print app) from a PC, without the phone app. Receipts can be any length and stack an icon, a title, Markdown text (lists, checklists, tables…), a QR code and a picture.
The printer's Bluetooth protocol, reverse engineered from the Funny Print app, is documented in PROTOCOL.md.
For Home Assistant, see the HA-LXD02 integration, which carries a copy of this package's protocol and renderer.
Requirements
- Windows, macOS or Linux with Bluetooth LE
- Python 3.12+
Install
pip install lxd02-printer
This installs the lxd02 module (import lxd02) and the lxd02-print command. To work on the code
instead, clone this repository and install it editable in a virtual
environment:
python -m venv .venv
.venv\Scripts\python -m pip install -e .
On macOS/Linux use .venv/bin/python instead of .venv\Scripts\python.
The printer switches itself off after a few minutes idle, and accepts only one connection: turn it on and close the phone app (and turn off Home Assistant's Stay connected) before printing.
lxd02-print --info
Printing
lxd02-print --icon mdi:coffee --title "Coffee break" --text "Back in 15 minutes"
lxd02-print --title Shopping --file list.md
lxd02-print --qr "WIFI:T:WPA;S:Home;P:secret;;" --title "Guest Wi-Fi"
lxd02-print --image photo.jpg --energy 5
lxd02-print --feed 20
| Option | Default | |
|---|---|---|
--title TEXT |
large centred heading | |
--text TEXT |
body text in Markdown; \n starts a new line, - reads stdin |
|
--file PATH |
body text from a UTF-8 (Markdown) file | |
--plain |
print the text exactly as written, without Markdown | |
--qr TEXT |
QR code content | |
--icon mdi:NAME |
MDI icon at the top | |
--image PATH |
picture, scaled to the paper width and dithered | |
--size |
normal |
text size: small, normal, large |
--align |
left |
body text: left, center, right |
--energy 1-6 |
printer's | darkness; the printer remembers it |
--feed MM |
0 | extra blank paper after the print. On its own (nothing else to print), just advance that much paper |
--no-dither |
hard black/white pictures | |
--copies N |
1 | |
--preview DIR |
save receipt.png instead of printing |
|
--address MAC |
scan | printer address, e.g. C0:00:00:00:00:48 |
--info |
battery, paper and darkness |
python -m lxd02 works the same as lxd02-print.
After every job the printer advances about 10 mm of paper by itself, so
the last printed line clears the tear bar. Feeds count it: --feed 12 gives
12 mm, and 10 mm is the shortest feed. The first 10–20 mm of a print can come
out faint while the print head warms up.
Markdown: lists, checklists, tables
The text is Markdown, so shopping lists, to-dos and notes print formatted:
# Weekend
## Groceries
- [ ] Milk
- [x] Bread
- [ ] Coffee, **dark roast**
## Hardware store
1. Screws *(4 mm)*
2. Wood glue
- the ~~cheap~~ good one
> Remember the bags!
| Item | Qty | Price |
|:-------|:---:|------:|
| Apples | 6 | 2.40 |
---
```
Total 6.38
```
| Markdown | Prints as |
|---|---|
# H1, ## H2, ### H3 |
headings (H1 centred, H1/H2 underlined) |
**bold**, *italic* / _italic_, ~~strike~~, `code` |
inline styles (code on a light gray background) |
[text](url) |
underlined text |
- item, * item, 1. item |
bullet and numbered lists; indent to nest |
- [ ] task, - [x] done |
checkboxes, empty or ticked |
> text |
quote with a bar on the left |
``` fenced block |
monospace box, spaces kept |
--- |
dashed tear line |
| a | b | + |---|---| |
table with a bold header; :--, :-:, --: align columns |
 |
picture, relative to the --file |
Unlike standard Markdown, every line break is kept. Text uses the bundled DejaVu fonts, so every machine prints identically.
Using it from Python
import asyncio
from lxd02 import LXD02, render_receipt
async def main():
img = render_receipt(title="Shopping", text="- [ ] Milk\n- [ ] Bread", icon="mdi:cart")
async with LXD02() as printer: # scans for the printer; or LXD02("C0:00:00:00:00:48")
status = await printer.wait_status()
print(status.battery, status.no_paper)
await printer.print_image(img, energy=3)
await printer.feed(20)
asyncio.run(main())
lxd02.protocol.LXD02 is the protocol on its own: give it any connected
bleak client (Home Assistant's, for example).
Development
src/lxd02/protocol.py, receipt.py, markdown.py and assets/ are copied
unchanged into the HA-LXD02 integration. Change them here, then copy them
there; HA-LXD02's tests check the copies match when both projects sit side by
side.
The tests cover the protocol against a simulated printer (handshake, hold/resend, out of paper, the automatic feed), the Markdown renderer and the CLI:
pip install -e . pytest pytest-asyncio
pytest
Releasing
Releases go to PyPI through the Publish workflow, with trusted publishing (no API tokens):
- Bump
versioninpyproject.tomland push. - Optional: Actions → Publish → Run workflow uploads that build to
TestPyPI; try it with
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ lxd02-printer. TestPyPI refuses a version it already has, so bump again to re-test. - Publish a GitHub release with the tag
v+ the version (e.g.v0.1.1). The workflow checks the tag matches, builds, smoke-tests the wheel and uploads it to PyPI.
Files
| Path | What it is |
|---|---|
src/lxd02/protocol.py |
the Bluetooth protocol: handshake, status, darkness, printing, feed |
src/lxd02/receipt.py, markdown.py |
the receipt and Markdown renderer |
src/lxd02/client.py |
a plain bleak connection (scan, connect, unlock) |
src/lxd02/cli.py |
the lxd02-print command |
src/lxd02/assets/ |
DejaVu Sans / Sans Mono fonts and license, MDI icon font 7.4.47 and license |
tests/ |
protocol, renderer and CLI tests |
PROTOCOL.md |
the reverse-engineered printer protocol |
.github/workflows/publish.yml |
build and publish to PyPI / TestPyPI |
LICENSE |
MIT; the bundled fonts keep their own licenses |
Metadata
Release files for lxd02-printer 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| lxd02_printer-0.1.0.tar.gz | 1.8 MB | With provenance | Details for lxd02_printer-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| lxd02_printer-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for lxd02_printer-0.1.0-py3-none-any.whl |
Total release size: 3.6 MB
Release files / lxd02_printer-0.1.0.tar.gz
| Download URL | lxd02_printer-0.1.0.tar.gz |
|---|---|
| Size | 1.8 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9a4e74d9a3b1b9e6ddf2074035df7fe7eaa93168f752a89bdd1e5c544b938cb9
|
|
BLAKE2b-256 checksum How to use checksums |
7cf03dd975e3e15873898a869086b36cb8e4d110e44344cf54fc8f1667bc93a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logRelease files / lxd02_printer-0.1.0-py3-none-any.whl
| Download URL | lxd02_printer-0.1.0-py3-none-any.whl |
|---|---|
| Size | 1.8 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
44bd6d212faeccb769a27ce6c35288277f4fccc757259d3d146759abaa9b5c36
|
|
BLAKE2b-256 checksum How to use checksums |
d6018164ff085b5e8e6a43f2408d83d9e1ecd8119c5a22c6f092881b512c9e83
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logSecurity for lxd02-printer 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for lxd02_printer-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.