Contributing#
Thanks for wanting to help! Bug reports, fixes, documentation and new features are all welcome. Two kinds of contribution we especially hope for:
New printer models. A Phomemo model with the same protocol needs only a model manifest; see Printer models.
New package formats. Today there are RPMs (COPR) and PyPI. Debian packages (apt), a snap or anything else that brings the driver to more systems would be great; see Add a package format.
Questions and bug reports go to GitHub issues.
Set up#
git clone https://github.com/PassionateBytes/phomemo-driver.git
cd phomemo-driver
uv sync --all-packages
That gives you one environment with the library, its test tooling (pytest, pytest-asyncio, ruff, ty) and the documentation tools.
To build the printer app you also need its C dependencies. On Fedora:
sudo dnf install gcc make pkgconf-pkg-config pappl-devel cjson-devel cups-devel
sudo dnf install cups-ipptool ghostscript # for the end-to-end tests
Repository layout#
pyproject.toml uv workspace root (members, ruff settings); no project of its own
uv.lock one lock file for the workspace
Makefile build, install, checks, release
src/phomemo/ Python library and bridge (uv project "phomemo")
src/printer-app/ PAPPL printer app in C
src/models/ model manifests, one directory per model
docs/ this site (Sphinx, uv project "phomemo-docs")
packaging/ systemd units, default configuration, spec files
docs/m08f-protocol-reference.md is the source of truth for how the printer behaves.
Run the checks#
make check
This runs what CI runs: ruff lint and format check, the Python tests and the C unit tests. The individual commands:
uv run ruff check . && uv run ruff format .
uv run pytest src/phomemo/tests
make -C src/printer-app
make -C src/printer-app test
uv run --package phomemo-docs sphinx-build -W -b html docs docs/_build/html
Tip
Build the docs with -W before you push: CI treats every Sphinx warning as an error.
The published site is versioned. .github/workflows/docs.yml builds latest/ from every push to main and a directory per release (0.3.0/, …) from every v* tag; the newest release is also copied to stable/, where the site root and the unversioned page links lead. docs/publish.py assembles the site on the gh-pages branch, and the version menu reads its versions.json. Local builds leave the menu out (the workflow sets DOCS_VERSION). To rebuild an older release, run the Docs workflow by hand with its tag.
CI (.github/workflows/ci.yml) runs on every Fedora release that still gets updates (it asks Fedora’s Bodhi which ones, from MIN_FEDORA on) and on Rawhide, each in its own container: the checks as an ordinary user, and the RPM packages as COPR builds them, python-bleak at the locked bleak version and phomemo-driver against it, both installed. Lint and the documentation build run on the newest release only. Rawhide is informational: a failure there shows in the run’s summary and as a warning, but does not fail CI.
Dependencies are kept current by Renovate (.github/renovate.json5), weekly: it moves uv.lock within the ranges in the pyproject.toml files and bumps the GitHub Actions. The runtime dependencies are capped below their next major version (bleak>=3.0.0,<4, pillow>=12.1.0,<14); a new major comes as a pull request that raises the cap and waits for review, their minor and patch updates arrive with the weekly lock refresh. Development and documentation tools are uncapped. Everything else merges on its own once CI is green; GitHub Actions majors also wait for review.
Tests#
The fake printer#
from tests.test_bridge_server import FakePrinter
FakePrinter in src/phomemo/tests/test_bridge_server.py is a scripted stand-in for phomemo.Printer. Use it to script the device side (lid, paper, a sheet running out, a dropped connection) in bridge tests, without a printer.
The printer app end to end#
make -C src/printer-app
uv run pytest src/phomemo/tests/test_printer_app.py
test_printer_app.py starts the built phomemo-printer-app as a private server, with its state, spool and control socket under the test’s temporary directory, and serves it a bridge whose printer is the FakePrinter. It needs ipptool and Ghostscript, and it is skipped when the binary has not been built.
Warning
Never run phomemo-printer-app devices or server from a test or script outside that harness. It starts a long-lived server and writes state under your config directory.
Hardware#
Tests against a real printer are not automated. If your change affects printing, check it by hand with the printer on and in range, and say in the pull request what you tried.
Conventions#
Topic |
Rule |
|---|---|
Commits |
Conventional Commits: |
Tests |
Tests first for behaviour changes |
Documentation |
Changes with the code, in the same commit: READMEs, docstrings, comments, the protocol reference and these pages stay true |
Comments |
Describe what the code does or which invariant it keeps, not how it got there |
Python |
Modern typing ( |
C |
C99, |
Terms |
As defined in the glossary: register (not pair), connection (not link), sheet and page, model manifest |
Privacy |
No device identifiers, personal paths or host names in tracked files; use |
Text |
No em-dashes |
Licences#
Every new source file starts with the copyright notice and licence statement (FSF style) used in its neighbours.
Part |
Licence |
|---|---|
Library and bridge ( |
GNU LGPL, version 3 or later |
Printer app ( |
GNU GPL, version 3 or later |
Packaging, build files and everything else |
GNU GPL, version 3 or later |
Documentation ( |
CC BY-SA 4.0 |
Packaging#
packaging/
systemd/ system units, service user (sysusers), presets
config/ bridge.toml for /etc/phomemo/, phomemo-printer-app.conf for /etc/
rpm/ phomemo-driver.spec (driver + python3-phomemo), python-bleak.spec
Files shared by every package format live in the format-neutral directories (systemd/, config/); each format gets its own directory. The Python package goes to PyPI from src/phomemo/ and needs nothing from here.
The Fedora route:
The COPR project
passionatebytes/phomemo-driverbuilds both specs from git with themake_srpmmethod, which runs.copr/Makefile.phomemo-driveris built from the newestv*tag, never from unreleased code onmain. The builds are started from GitHub through the project’s custom webhook (repository secretCOPR_WEBHOOK):release.ymlon each tag,copr-bleak.ymlwhen a push tomainchanges the locked bleak version.phomemo-driver.specbuilds from agit archiveof the repository.Fedora has no bleak, so
python-bleak.specis built at the bleak version locked inuv.lock. Renovate’s weekly lock refresh updates it within the range insrc/phomemo/pyproject.toml, or by hand:uv lock --upgrade-package bleak.
Add a package format#
Create a directory named after the format (
debian/,snap/, …) with its manifests and any build scripts it needs.Reuse
systemd/andconfig/instead of copying them. Formats that install units differently (a snap’s daemons, for example) take what they need from there or describe the same services in their own terms.Add a target to the root
Makefilethat builds the package locally, and a job to the workflows that builds it in CI and publishes it on a tag.Add the format to the table in
packaging/README.mdand its install instructions tosrc/printer-app/README.md.
Note
Not sure how a format should handle the two services or the phomemo service user? Open an issue or a draft pull request early; we are happy to help.
Releases#
make release VERSION=minor # or major, patch, or an explicit x.y.z
git push && git push origin vx.y.z
make release runs on a clean main: it sets the version in src/phomemo/pyproject.toml, the RPM spec and src/printer-app/phomemo.h, updates uv.lock, commits and creates the annotated tag vx.y.z. It refuses a tag that exists. The push is yours: pushing the tag publishes.
Pushing the tag runs .github/workflows/release.yml:
Job |
Result |
|---|---|
Build |
Wheel and sdist; fails when the tag does not match the package version |
Publish to PyPI |
Trusted publishing, environment |
Source RPMs |
|
GitHub release |
Generated notes, with the wheel, sdist, source RPM and tarball attached |
The tag also starts the COPR build of phomemo-driver (job copr).
For local artefacts: make dist (wheel and sdist), make tarball (source tarball named like the GitHub archive), make srpm (source RPM, needs rpm-build), all into dist/. make help lists every target.