# fichero-printer Web GUI, Python CLI, and protocol documentation for the Fichero D11s thermal label printer. Blog post: [Reverse Engineering Action's Cheap Fichero Labelprinter](https://blog.hamza.homes/reverse-engineering-fichero-label-printer/) The [Fichero](https://www.action.com/nl-nl/p/3212141/fichero-labelprinter/) is a cheap Bluetooth thermal label printer sold at Action. Internally it's an AiYin D11s made by Xiamen Print Future Technology. The official app is closed-source and doesn't expose the protocol, so this project reverse-engineers it from the decompiled APK. ## The printer - 96px wide printhead, 203 DPI - Prints 1-bit raster images onto self-adhesive labels (14mm x 30mm default) - Connects via BLE or Classic Bluetooth SPP - 18500 Li-Ion battery (1200mAh), USB-C charging - Bluetooth names: `FICHERO_5836`, `D11s_` ## Why not just use the app? The Fichero app (`com.lj.fichero`) asks for 26 permissions. For a label printer. The notable ones: ``` ACCESS_FINE_LOCATION Your precise GPS location ACCESS_COARSE_LOCATION Your approximate location CAMERA Your camera READ_EXTERNAL_STORAGE Your files WRITE_EXTERNAL_STORAGE Your files (write) READ_MEDIA_IMAGES Your photos INTERNET Full internet access ACCESS_WIFI_STATE Your WiFi info CHANGE_WIFI_STATE Change your WiFi settings CHANGE_WIFI_MULTICAST_STATE Multicast on your network AD_ID Your advertising ID ACCESS_ADSERVICES_AD_ID More ad tracking ACCESS_ADSERVICES_ATTRIBUTION Ad attribution tracking BIND_GET_INSTALL_REFERRER Where you installed from ``` Some of these are reasonable. The location permissions exist because of how Android handles Bluetooth. Bluetooth signals can reveal where you physically are, think retail stores using Bluetooth beacons to track which aisle you're standing in. So Android won't let any app scan for Bluetooth devices unless it also has location permission. That's not the app being sneaky. That's Android being cautious. The camera makes sense too. The app lets you scan barcodes and photograph things to print on labels. The WiFi permissions are baggage from the underlying SDK. It powers over 159 different printer models, some of which connect over WiFi. The Fichero doesn't use WiFi at all, but the permissions are baked into the shared code. Then there are four permissions that have nothing to do with printing. Your advertising ID is a unique number assigned to your phone that follows you across every app, letting ad networks build a profile of what you do. The app also wants ad attribution tracking (which apps you installed after seeing an ad) and your install referrer (how you found the app store listing). That's a label printer quietly feeding your activity to an ad network. The package name is `com.lj.fichero` but the SDK inside is from a company called LuckPrinter (`com.luckprinter.sdk_new`). The app is what's called a white-label product: a generic app rebranded with the Fichero name and logo. The same codebase runs receipt printers, A4 thermal printers, and industrial label makers. It supports 159+ printer models across four manufacturers. Your little label printer's app is just a skin on top. One more reason to ditch the app and talk to the printer directly. ## How it works The printer uses a proprietary command set prefixed with `10 FF`. It borrows one command from ESC/POS (the `1D 76 30` raster image command) but everything else is custom. The key discovery from decompiling the Fichero APK: the D11s is an "AiYin" device class that needs specific enable/stop commands (`10 FF FE 01` / `10 FF FE 45`). Using the wrong pair means the printer accepts image data silently but never actually prints. ## Web GUI Open `web/index.html` in Chrome or Edge. No server needed, no dependencies, no build step. Click Connect, pair with the printer through the browser's Bluetooth dialog, and you're ready to print. The UI has two tabs: one for text labels (with a live 96px preview) and one for image labels (drag-and-drop). Device info, battery, and settings are all accessible from the same page. Requires Web Bluetooth, so Chrome/Edge/Opera only. Firefox and Safari don't support it. ## CLI Setup Requires Python 3.10+ and uv. Turn on the printer and run: ``` uv run printer.py info ``` This auto-discovers the printer via BLE scan. To skip scanning on subsequent runs, find your printer's address from the scan output and save it: ``` export FICHERO_ADDR=AA:BB:CC:DD:EE:FF ``` You can also pass it per-command: ``` uv run printer.py --address AA:BB:CC:DD:EE:FF info ``` ## CLI Usage ``` uv run printer.py --help ``` ### Printing ``` uv run printer.py text "Hello World" uv run printer.py text "Fragile" --density 2 --copies 3 uv run printer.py image label.png uv run printer.py image label.png --density 1 --copies 2 ``` Density: 0=light, 1=medium (default), 2=thick. ### Device info ``` uv run printer.py info uv run printer.py status ``` ### Settings ``` uv run printer.py set density 2 uv run printer.py set shutdown 30 uv run printer.py set paper gap ``` - `density` - how dark the print is. 0 is faint, 1 is normal, 2 is the darkest. Higher density uses more battery and can smudge on some label stock. - `shutdown` - how many minutes the printer waits before turning itself off when idle. Set it higher if you're tired of turning it back on between prints. - `paper` - what kind of label stock you're using. `gap` is the default, for labels with spacing between them (the printer detects the gap to know where to stop). `black` is for rolls with a black mark between labels. `continuous` is for receipt-style rolls with no markings. ## Protocol and reverse engineering See [docs/PROTOCOL.md](docs/PROTOCOL.md) for the full command reference, print sequence, and how this was reverse-engineered. ## License MIT