Skip to content

schartrand77/PrintLab

Repository files navigation

PrintLab

Standalone Docker app for Bambu printers that uses pybambu from greghesp/ha-bambulab.

This is not Home Assistant. It runs as a direct web/API service.

Suite demo walkthrough

PrintLab participates in the MakerWorks suite demo as the printer routing and production-handoff app. The public screenshot below uses synthetic fixture printers only, so it does not expose live printer serials, camera views, access codes, private job state, or real printer controls.

Synthetic printer fleet

PrintLab synthetic printer fleet

What it provides

  • Direct MQTT connection to your printer (LAN mode / local MQTT by default).
  • Multi-printer gallery dashboard at http://localhost:8080.
  • Printer detail dashboard at http://localhost:8080/printer/{printer_id}.
  • Required auth by default in container images, with signed cookie sessions for the web UI.
  • REST API for status and control actions.
  • Pause / resume / stop, refresh state, chamber light control, fan and temperature control.

Quick start

  1. Copy .env.example to .env and fill in either:
    • single-printer PRINTER_HOST / PRINTER_SERIAL / PRINTER_ACCESS_CODE
    • or PRINTERS_JSON with multiple printers
  2. Set admin credentials:
    • REQUIRE_AUTH=true is the image default and startup fails if credentials are missing
    • ADMIN_USERNAME (default: admin)
    • ADMIN_PASSWORD_HASH generated with python scripts/hash_password.py
    • SESSION_SECRET set to a random long secret
  3. If using single-printer mode, fill in:
    • PRINTER_HOST
    • PRINTER_SERIAL
    • PRINTER_ACCESS_CODE
  4. Build and run:
docker compose up -d --build
  1. Open:
http://localhost:8080

API endpoints

Interactive API docs are published at:

  • GET /docs

  • GET /redoc

  • GET /openapi.json

  • GET /openapi.json/export to persist the current schema to data/openapi.json

  • GET /login

  • POST /auth/login

  • POST /auth/logout

  • GET /auth/session

  • GET /health

  • GET /api/printers

  • POST /api/printers with: {"name":"X1C-002","host":"192.168.1.67","serial":"SERIAL","access_code":"CODE","id":"x1c-002"}

  • GET /api/state

  • GET /api/printers/{printer_id}/state

  • POST /api/actions/pause

  • POST /api/actions/resume

  • POST /api/actions/stop

  • POST /api/actions/refresh

  • POST /api/actions/chamber-light with {"on": true|false}

  • POST /api/actions/fan with {"fan":"part_cooling|auxiliary|chamber|heatbreak|secondary_auxiliary","percent":0-100}

  • POST /api/actions/temperature with {"target":"heatbed|nozzle","value":0-320}

  • POST /api/printers/{printer_id}/actions/... (printer-scoped equivalents for all action endpoints)

  • GET /api/works/services

  • GET /api/works/{makerworks|stockworks}/health?path=/health

  • GET /api/works/makerworks/library

  • GET /api/works/makerworks/library/{model_id}

  • POST /api/works/makerworks/preflight

  • POST /api/works/makerworks/jobs with: {"model_id":"widget-1","printer_id":"printer-1","idempotency_key":"mw-job-123","source_job_id":"makerworks-123","metadata":{"priority":"rush"}}

  • GET /api/jobs

  • GET /api/jobs/{job_id}

  • POST /api/jobs/{job_id}/sync-makerworks

  • POST /api/jobs/{job_id}/connect-current-print connects a MakerWorks queued job to the print already running on the selected printer after manual slicing/start.

  • GET /api/printers/{printer_id}/jobs

  • GET /api/printers/{printer_id}/jobs/{job_id}

  • POST /api/printers/{printer_id}/jobs/{job_id}/sync-makerworks

  • POST /api/works/{makerworks|stockworks}/request with: {"method":"GET|POST|PUT|PATCH|DELETE","path":"/v1/resource","query":{},"body":{},"headers":{},"timeout_seconds":20}

  • POST /api/print-job with: {"file_path":"/cache/model.3mf","plate_gcode":"Metadata/plate_1.gcode","use_ams":true,"ams_mapping":[0]} (optional query: ?printer_id={printer_id})

  • GET /api/printers/{printer_id}/works/... printer-scoped equivalents for health, request, and MakerWorks library browsing

  • POST /api/printers/{printer_id}/print-job printer-scoped print submission for an already sliced/staged file.

  • GET /api/successful-gcodes

  • GET /api/printers/{printer_id}/successful-gcodes

  • POST /api/successful-gcodes/{record_id}/sync-makerworks

  • POST /api/printers/{printer_id}/successful-gcodes/{record_id}/sync-makerworks

Works integration config

Minimal boot environment stays in .env: admin/session secrets, printer access, storage paths, and any values needed before the app can start. Configure MakerWorks, StockWorks, YouTube upload, callback, and allowlist settings from the authenticated Settings -> Suite Integrations screen wherever possible.

Environment variables remain supported as legacy overrides and still win over /data/config.json or /config/config.json. Remove an env value only after saving and testing the matching in-app setting.

Runtime settings that can be configured in app:

  • MAKERWORKS_BASE_URL, MAKERWORKS_API_KEY, MAKERWORKS_BEARER_TOKEN, MAKERWORKS_ADMIN_USERNAME, MAKERWORKS_ADMIN_PASSWORD, MAKERWORKS_AUTH_HEADER, MAKERWORKS_VERIFY_SSL, MAKERWORKS_ALLOWED_PATHS, MAKERWORKS_ALLOWED_METHODS
  • Optional MakerWorks job callbacks: MAKERWORKS_JOB_CALLBACK_ENABLED, MAKERWORKS_JOB_CALLBACK_METHOD, MAKERWORKS_JOB_CALLBACK_PATH_TEMPLATE
  • Optional YouTube timelapse uploads: YOUTUBE_UPLOAD_ENABLED, YOUTUBE_CLIENT_ID, YOUTUBE_CLIENT_SECRET, YOUTUBE_REFRESH_TOKEN, YOUTUBE_PRIVACY_STATUS, YOUTUBE_CATEGORY_ID, YOUTUBE_TITLE_TEMPLATE, YOUTUBE_DESCRIPTION_TEMPLATE, YOUTUBE_TAGS, YOUTUBE_NOTIFY_SUBSCRIBERS, YOUTUBE_MADE_FOR_KIDS, YOUTUBE_EMBEDDABLE, YOUTUBE_LICENSE, YOUTUBE_PUBLIC_STATS_VIEWABLE, YOUTUBE_TIMELAPSE_WAIT_SECONDS, YOUTUBE_TIMELAPSE_POLL_INTERVAL_SECONDS, YOUTUBE_UPLOAD_TIMEOUT_SECONDS
  • Conversion uploads: CONVERSION_MAX_UPLOAD_MB controls the per-file upload limit for the conversion page and API. Default: 200.
  • MakerWorks library normalization: MAKERWORKS_LIBRARY_LIST_PATH, MAKERWORKS_LIBRARY_DETAIL_PATH_TEMPLATE, MAKERWORKS_LIBRARY_SEARCH_PARAM, MAKERWORKS_LIBRARY_PAGE_PARAM, MAKERWORKS_LIBRARY_PAGE_SIZE_PARAM, MAKERWORKS_LIBRARY_PAGE_SIZE, MAKERWORKS_LIBRARY_ITEMS_PATH, MAKERWORKS_LIBRARY_TOTAL_PATH, MAKERWORKS_LIBRARY_ID_PATH, MAKERWORKS_LIBRARY_NAME_PATH, MAKERWORKS_LIBRARY_SUMMARY_PATH, MAKERWORKS_LIBRARY_DESCRIPTION_PATH, MAKERWORKS_LIBRARY_THUMBNAIL_PATH, MAKERWORKS_LIBRARY_THUMBS_PATH, MAKERWORKS_LIBRARY_MODEL_URL_PATH, MAKERWORKS_LIBRARY_DOWNLOAD_URL_PATH, MAKERWORKS_LIBRARY_AUTHOR_PATH, MAKERWORKS_LIBRARY_TAGS_PATH, MAKERWORKS_LIBRARY_FILES_PATH, MAKERWORKS_LIBRARY_CREATED_AT_PATH, MAKERWORKS_LIBRARY_UPDATED_AT_PATH
  • STOCKWORKS_BASE_URL, STOCKWORKS_API_KEY, STOCKWORKS_BEARER_TOKEN, STOCKWORKS_AUTH_HEADER, STOCKWORKS_VERIFY_SSL, STOCKWORKS_ALLOWED_PATHS, STOCKWORKS_ALLOWED_METHODS

Auth behavior:

  • Browser UI auth uses POST /auth/login, an HttpOnly signed session cookie, a non-HttpOnly CSRF cookie, and X-CSRF-Token on mutating requests.
  • Basic Auth still works for API clients.
  • If *_API_KEY is set, it is sent as *_AUTH_HEADER (default X-API-Key).
  • If *_BEARER_TOKEN is set, it is sent as Authorization: Bearer ....
  • If *_ADMIN_USERNAME and *_ADMIN_PASSWORD are set, PrintLab can log into that service to fetch protected assets such as MakerWorks preview meshes and thumbnails.
  • *_ALLOWED_PATHS is a comma-separated prefix allowlist. Requests outside the list are rejected.
  • *_ALLOWED_METHODS is optional. If set, only those methods are proxied.
  • All env vars also support Docker-style file-based secrets via *_FILE.
  • If /data/config.json or /config/config.json exists, env values can also be supplied there using either env-style keys like YOUTUBE_UPLOAD_ENABLED or section objects like "youtube": {"upload_enabled": true}. Direct env vars still win.

MakerWorks library notes:

  • The dashboard now has a MakerWorks tab inside the model library modal.
  • Responses are normalized into a stable shape (id, name, summary, thumbnail_url, model_url, download_url, author, tags, printer_handoff_ready, materials, colors, printer_profiles, estimated_print_minutes) so the UI does not need to match your upstream schema exactly.
  • The MakerWorks search page now supports one-click handoff into the PrintLab queue. PrintLab runs preflight checks before queueing and only auto-selects a printer when exactly one printer qualifies.
  • When multiple printers qualify, PrintLab stops for approval and shows ranked candidates with compatibility, filament, and wait-time checks instead of guessing.

MakerWorks job intake:

  • POST /api/works/makerworks/jobs is the new PrintLab-native submission path for MakerWorks.
  • POST /api/works/makerworks/preflight returns policy checks and ranked printer candidates before queueing.
  • PrintLab stages the asset to printer storage, creates a queue entry, and persists a submitted-job ledger in /data/submitted_jobs_{printer_id}.json.
  • Use idempotency_key when MakerWorks may retry the same submission; PrintLab will return the existing job record instead of queueing a duplicate.
  • Preflight checks cover printer compatibility, filament availability, and queue-aware time estimates. Routing prefers connected printers that pass compatibility, have matching filament, and minimize wait time.
  • Job status currently advances through queued, started, completed, failed, cancelled, and submit_failed.
  • If MAKERWORKS_JOB_CALLBACK_ENABLED=true, each new status is pushed back to MakerWorks once using MAKERWORKS_JOB_CALLBACK_PATH_TEMPLATE.

MakerWorks callback contract:

  • Recommended callback path template: /api/printlab/jobs/{job_id} or /api/printlab/jobs/{job_id}/status.
  • Default method: POST.
  • Template variables available in MAKERWORKS_JOB_CALLBACK_PATH_TEMPLATE: {job_id}, {printer_id}, {model_id}, {source_job_id}, {source_order_id}, {status}.
  • Callback payload shape: {"job_id":"...","status":"queued|started|completed|failed|cancelled|submit_failed","printer_id":"...","printer_name":"...","queue_item_id":"...","successful_gcode_id":"...","idempotency_key":"...","source":"makerworks","source_job_id":"...","source_order_id":"...","model_id":"...","model_name":"...","model_url":"...","download_url":"...","file_path":"...","file_name":"...","plate_gcode":"...","start_at":"...","started_at":"...","completed_at":"...","last_error":"...","metadata":{},"history":[],"updated_at":"...","created_at":"..."}

Successful G-code tracking:

  • Every completed print that reaches FINISH or COMPLETE is persisted to /data/successful_gcodes_{printer_id}.json.
  • SD-card model listings are enriched with successful G-code counts and latest MakerWorks sync status.
  • Optional automatic MakerWorks attachment can be enabled with:
    • MAKERWORKS_ATTACH_GCODE_ENABLED=true
    • MAKERWORKS_ATTACH_GCODE_METHOD=POST
    • MAKERWORKS_ATTACH_GCODE_PATH_TEMPLATE=/models/{model_id}/gcodes
  • The attachment payload includes printer, model, file, plate, AMS, and completion metadata. model_id is inferred from a leading numeric filename prefix such as 20906356-widget_plate_1.3mf.
  • Optional automatic YouTube upload can be enabled with:
    • TIMELAPSE_CACHE_COUNT=1 or higher so the printer timelapse is downloaded into cache
    • YOUTUBE_UPLOAD_ENABLED=true or YOUTUBE_UPLOAD_ENABLED=auto to enable once OAuth credentials are configured
    • YOUTUBE_CLIENT_ID=...
    • YOUTUBE_CLIENT_SECRET=...
    • YOUTUBE_REFRESH_TOKEN=...
    • Optional: YOUTUBE_UPLOAD_STAGING_DIR=/tmp/printlab-youtube to force uploads to use local container storage before sending to YouTube
  • To generate a YouTube refresh token locally, run:
    • python scripts/get_youtube_refresh_token.py --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET
    • Add http://127.0.0.1:8080/callback as an authorized redirect URI in your Google OAuth client before running it.
  • The YouTube uploader waits for the newest cached timelapse in {FILE_CACHE_PATH}/timelapse, stages a verified local copy in temporary storage, uploads that staged file with the configured title/description templates, and stores status back into each successful G-code record under the youtube key.

Stockworks enrichment:

  • Responses from GET /api/works/stockworks/health and POST /api/works/stockworks/request include printer_filament with:
    • loaded_filament (active AMS slot/type/color/remaining_percent)
    • remaining_filament (all visible AMS slots + remaining percentages)

Quality and SDK workflow

  • Install dev tooling with pip install -r requirements.txt -r requirements-dev.txt
  • Run ruff check app tests scripts
  • Run mypy
  • Run pytest
  • Export the schema with python scripts/export_openapi.py
  • Optionally generate a typed client with pwsh scripts/generate_client.ps1

Notes

  • Image build pulls pybambu from ha-bambulab at build time.
  • The UI is intentionally minimal; use the API for automation and advanced control.
  • Printers added from the UI/API are persisted to /data/printers_added.json.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages