A small, stateless HTTP service that reads the barcodes of images and PDFs: the file goes in, the texts of the barcodes come out. It is a Home Assistant app repository (formerly "add-on"), and the same image runs with plain Docker.
- Input: JPEG, PNG, WebP, HEIC/HEIF, PDF (recognised by content).
- Engine: zxing-cpp 3.1.1, every format it reads; PDF pages rendered by
poppler's
pdftoppm; HEIC decoded by libheif (decode only). - Stateless: no database, no users, no tokens, no outbound connection. Nothing is kept between requests and the log never holds a decoded text.
- Contained: one job at a time in a child process with memory, CPU and time limits; UID 10001; writes only to
/tmp(read-only file system with thedocker runline; not as an app); no host port as an app.
It was written for the Home Assistant integration boarding-pass-tracker (the Aztec/PDF417 code of a boarding pass), but the API is generic: any local service can send it a photo of a QR code or a PDF bill.
The user documentation (installation, the address to use, the API) is barcode_decoder/DOCS.md.
repository.yaml the Home Assistant app repository
barcode_decoder/ the app folder = the Docker build context
config.yaml the app (the only config.* file in the repository: the Supervisor reads every one)
Dockerfile python:3.13-slim-trixie pinned by digest + poppler-utils + the wheels
requirements.txt zxing-cpp, Pillow, pi-heif: wheels pinned with hashes (aarch64 and x86_64)
DOCS.md README.md CHANGELOG.md icon.png logo.png
selftest.png selftest.heic a fictional Aztec code for GET /health?selftest=1
bcdecoder/ the package
server.py HTTP API v1 (stdlib ThreadingHTTPServer)
worker.py one job in a child process with resource limits
engine.py decode + PDF render (from boarding-pass-tracker's tracker/barcodes.py)
isobmff.py the bounded HEIF header reader (ispe) for the pixel guard
settings.py the limits (not config.py, see above)
tests/ T3: unit and HTTP tests run inside the image; tests/host: the container and the layout
THIRD_PARTY.md every shipped component and its licence (GPL ones flagged)
Docker is the only requirement; the suite runs inside the image it tests (Python 3.13, the pinned wheels, poppler):
sh tests/run_t3.sh # build local/barcode-decoder:1.0.0, run tests/ in it, then tests/host
BUILD=0 sh tests/run_t3.sh # without buildingtests/test_barcodes.py: the 8 tests moved from boarding-pass-tracker'stracker/test_barcodes.py, adapted.tests/test_api.py: the HTTP API (formats and angles, EXIF, PDF pages, the pixel guard, unreadable and unsupported files, 413/400, timeouts with the job's processes killed, one 503 out of six parallel requests, slow senders that hold no job slot, the 408 of a body slower than its deadline, the bound of the files in/tmp, a child out of memory, no decoded text in the log).tests/test_heic.py: HEIC by magic bytes, the bounded header reader against hostile and damaged headers, the pixel guard before libheif decodes anything, non-HEIFftypbrands refused.tests/test_worker.py: the worker and the server's helpers in process.tests/host/: the documenteddocker run(read-only, UID 10001, no capabilities, tmpfs/tmp), the self-test, no HEIC encoder in the image, a quick stop as PID 1; the repository layout and packaging rules (needs PyYAML).
The HEIC fixtures are made by tests/fixtures/make_fixtures.py in a throwaway container that has an encoder (see
its docstring); the image never contains one.
To run the server from a checkout (Python ≥ 3.12 with the requirements and pdftoppm installed):
cd barcode_decoder && python3 -B -m bcdecoder.server # BCDECODER_PORT=0 picks a free portA release (a v<version> tag) builds ghcr.io/7hemas7er/barcode-decoder:<version> for linux/amd64 and
linux/arm64 (.github/workflows/release.yml); the app keeps building from the Dockerfile on the device until an
image: key is added to config.yaml.
MIT (LICENSE). The image's third-party components keep their licences: THIRD_PARTY.md.