-
-
Notifications
You must be signed in to change notification settings - Fork 5
Development Guide
This page covers everything you need to hack on ankerctl itself: prerequisites, code generation, testing, coding conventions, and the contribution workflow.
- Python 3.10 or newer
-
git (and optionally
transwarpfor code generation) - ffmpeg (only required if you want to test the timelapse feature)
- A real or simulated AnkerMake M5 printer (for end-to-end testing)
git clone https://github.com/Django1982/ankermake-m5-protocol.git
cd ankermake-m5-protocol
# Runtime dependencies
pip install -r requirements.txt
# Development extras (testing, lint helpers)
pip install -r requirements-dev.txt
# Optional — required only if you regenerate protocol code
make install-toolsThe codebase has no enforced formatter or linter — keep diffs minimal and follow the surrounding style.
See Architecture → Repository tour for the annotated tree. Key folders:
| Folder | Contents |
|---|---|
cli/ |
CLI command implementations (Click) |
web/ |
Flask app, routes, services |
libflagship/ |
MQTT, PPPP, HTTP protocol clients |
static/ |
HTML/JS/CSS frontend |
specification/ |
.stf protocol specs (input to codegen) |
templates/ |
Jinja2 codegen templates |
tests/ |
pytest test suite |
examples/ |
Standalone protocol experiments |
ankerctl is pure Python — no compilation step. Run it directly:
./ankerctl.py webserver run
./ankerctl.py mqtt monitor
./ankerctl.py pppp lan-searchFor Docker development:
docker build \
--build-arg UID=$(id -u) \
--build-arg GID=$(id -g) \
-t django01982/ankerctl:local .
docker compose up -d --force-recreate--force-recreate is needed after a local rebuild — Compose otherwise keeps the previous container.
Files marked "DO NOT EDIT MANUALLY" are auto-generated by transwarp from .stf specs:
libflagship/mqtt.pylibflagship/pppp.pylibflagship/amtypes.pystatic/libflagship.js
To modify them, edit the source instead:
| File to edit | Affects |
|---|---|
specification/mqtt.stf |
MQTT message types |
specification/pppp.stf |
PPPP packet types |
specification/amtypes.stf |
Common AnkerMake types |
templates/python/*.j2 |
Python code generation |
templates/javascript/*.j2 |
JavaScript code generation |
Then run:
make diff # preview changes
make update # apply generated changes
make clean # remove __pycache__ etc.make install-tools clones the transwarp git submodule and pip installs it.
The test suite uses pytest and lives in tests/. Settings come from pyproject.toml.
# Run all tests
pytest
# Run a single file
pytest tests/test_protocol_pppp.py
# Run a single test by name
pytest -k test_open_lan_binds_fixed_port
# With coverage
pytest --cov=cli --cov=libflagship --cov=web
# Verbose output
pytest -vTest files cover:
-
tests/test_protocol_*.py— MQTT, PPPP, crypto -
tests/test_cli_*.py— Click CLI commands -
tests/test_*_service.py— background services -
tests/test_*_api.py— REST endpoints -
tests/test_filament_*.py,tests/test_print_history.py— SQLite stores -
tests/conftest.py— shared fixtures (e.g.FakeSocket)
Note The frontend (
static/) has no automated tests — use the web UI for manual validation.
| Topic | Convention |
|---|---|
| Indentation | 4 spaces |
| Naming |
snake_case for functions/variables, CapWords for classes |
| Imports | Stdlib first, then third-party, then local |
| Logging |
log = logging.getLogger(__name__); use log.info/warning/error/critical. Named loggers (mqtt, web, history, timelapse, homeassistant) write to separate files when ANKERCTL_LOG_DIR is set. |
| CLI | Click decorators; follow patterns in ankerctl.py
|
- Follow existing style in
static/ankersrv.js - Use Cash.js (lightweight jQuery alternative) for DOM manipulation
- Avoid introducing new frameworks
- No enforced formatter / linter — keep diffs minimal and readable
- Prefer editing existing files over creating new ones
- Match the surrounding module layout
# In cli/<group>.py
@<group>.command("subcommand")
@click.argument("arg", required=True)
@click.option("--flag", "-f", is_flag=True, help="Description")
@pass_env
def <group>_subcommand(env, arg, flag):
"""Docstring becomes help text."""
# Implementation@pass_env injects the shared env object: env.config, env.printer_index, env.insecure, env.pppp_dump.
client = cli.mqtt.mqtt_open(env.config, env.printer_index, env.insecure)
cmd = {
"commandType": MqttMsgType.ZZ_MQTT_CMD_GCODE_COMMAND.value,
"cmdData": "G28",
"cmdLen": 3,
}
client.command(cmd)
response = client.await_response(MqttMsgType.ZZ_MQTT_CMD_GCODE_COMMAND)# Collects all response packets within a time window
msgs = cli.mqtt.mqtt_gcode_dump(client, "M420 V", collect_window=4.0)
combined = "\n".join(msg.get("resData", "") for msg in msgs)api = cli.pppp.pppp_open(env.config, env.printer_index, dumpfile=env.pppp_dump)
fui = FileUploadInfo.from_data(data, filename, user_name="ankerctl", ...)
cli.pppp.pppp_send_file(api, fui, data, rate_limit_mbps=rate_limit_mbps)
api.aabb_request(b"", frametype=FileTransfer.END) # start the print
api.stop()- Add the function in
web/__init__.py - If it modifies state → use
POST/DELETE(auth enforced automatically) - If it is a GET that should require auth → add the path to
_PROTECTED_GET_PATHS - If it is a debug-only route → add inside the
ANKERCTL_DEV_MODEblock at the bottom
- Create
web/service/<name>.pyinheriting fromService(inweb/lib/service.py) - Implement
worker_init(),worker_start(),worker_run(timeout),worker_stop() - Register via
app.svc.register("<name>", MyService())inregister_services() - Access from routes with
with app.svc.borrow("<name>") as svc:
- Edit
specification/*.stf - Modify
templates/if structural changes are needed - Run
make diffto preview - Run
make updateto regenerate - Test with CLI and web UI
-
Branches: create a feature branch (
feat/...,fix/...,docs/...) frommaster -
Commits: short, descriptive, sentence case; mention the affected area (e.g.
Fix PPPP file upload reply handling) -
Issue refs: include
#NNwhen relevant - Atomic: one logical change per commit
PRs should include:
- A brief summary
- Testing notes (commands run, manual UI checks)
- Screenshots for any UI change
- A CHANGELOG entry for user-facing changes
A release tag triggers the GitHub Actions workflow that builds and publishes the multi-arch Docker image. CHANGELOG.md must be updated before tagging — the workflow exits with sys.exit(1) if the version section is missing.
-
Never log
auth_token,mqtt_key, orapi_key -
Use
--insecure/-konly for debugging. Never in production. -
Never commit
login.jsonordefault.json— they contain sensitive tokens - The
RECOVER_FACTORYMQTT command requires--forceas a safety guard - Log viewer (
/api/debug/logs/<filename>) has path-traversal protection (rejects/,\,..) - External camera URLs are validated against an allowlist (
http,https,rtsp,rtmp) - Filament text fields are HTML-escaped before storage
Quick manual validation:
./ankerctl.py mqtt monitor # MQTT cloud connectivity
./ankerctl.py pppp lan-search # local network / PPPP discovery
./ankerctl.py webserver run # web UI on http://localhost:4470Example scripts in examples/:
python examples/mqtt-connect.py # standalone MQTT test
python examples/demo-pppp.py # PPPP packet parsing
python examples/web_login_test.py # web auth flow
python examples/probe_pppp_cmds.py # probe undocumented PPPP commandsDiagnose against a running container, not by reading the source — it saves time and tokens:
docker exec -it ankerctl bash
docker exec ankerctl ./ankerctl.py mqtt gcode-dump "M114"
docker exec ankerctl tail -f /logs/timelapse.logAvailable log files in /logs/ (when ANKERCTL_LOG_DIR is set):
-
ankerctl.log(root logger + everything via stdout) -
mqtt.log,web.log,history.log,timelapse.log,homeassistant.log -
bed_leveling/YYYYMMDD_HHMMSS.bed— saved bed-grid snapshots
For a guided reading order:
-
ankerctl.py— top-level Click groups -
cli/config.py— config file format and login flow -
web/__init__.py— every REST and WebSocket route -
web/service/mqtt.py—MqttQueue(the largest service) -
libflagship/mqttapi.py— MQTT client and encryption -
libflagship/ppppapi.py— PPPP UDP framing -
documentation/MQTT_COMMANDS.md— message type reference
For the complete annotated index see CLAUDE.md and .claude/agent-memory/INDEX.md.
Contributions are welcome — open an issue or PR. For larger features, please discuss the approach in an issue first so we agree on the direction before you invest time in implementation.