bambu cloud <-> simplyprint bridge
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Kate Meerburg 8057e6e03e
Use a UUID unique_id for SimplyPrint registration; surface rejections
SimplyPrint's multi-printer endpoint rejects add_connection unless unique_id is a UUID
or <=15 chars ('bambridge-<serial>' is 25), and the ws-client only logs the rejection at
debug level, so the visible symptom was 'No connections made in 30s'. Now a UUIDv5 of
the serial (stable across reinstalls), with migration of existing state files, and an
error-level log line when the server rejects a registration. Also fix two clients racing
SsdpListener.start() into a double bind.
2026-09-09 06:20:36 +00:00
bambridge Use a UUID unique_id for SimplyPrint registration; surface rejections 2026-09-09 06:20:36 +00:00
deploy Surface SimplyPrint setup codes: loud log line, state file, bridge status 2026-09-09 05:51:54 +00:00
docs/spikes Browser-based login (Google/Apple/portal) via Studio's localhost ticket flow 2026-09-09 05:15:03 +00:00
shim Initial bridge + actuator implementation 2026-09-09 04:22:01 +00:00
tests Pin printer TLS identity to the serial; treat LAN discovery as a hint only 2026-09-09 06:07:44 +00:00
tools Pin printer TLS identity to the serial; treat LAN discovery as a hint only 2026-09-09 06:07:44 +00:00
.gitignore Initial bridge + actuator implementation 2026-09-09 04:22:01 +00:00
Makefile Add deploy (systemd, Docker), operations docs, spike checklists, plugin hang guard 2026-09-09 04:26:50 +00:00
OPERATIONS.md Auto-resolve printer IP and access code in the bridge 2026-09-09 06:01:03 +00:00
pyproject.toml Pin printer TLS identity to the serial; treat LAN discovery as a hint only 2026-09-09 06:07:44 +00:00
README.md Pin printer TLS identity to the serial; treat LAN discovery as a hint only 2026-09-09 06:07:44 +00:00

bambridge — SimplyPrint ↔ Bambu Lab, cloud mode preserved

Use SimplyPrint (queue, start, monitor, pause/resume/cancel, webcam) with Bambu printers that stay bound to Bambu Cloud, so Bambu Handy, MakerWorld one-click printing and OTA firmware keep working exactly as before. Built for an H2D + X2D on a Linux home server; works for other models.

SimplyPrint cloud ──wss──▶ bridge ──HTTP 127.0.0.1:8765──▶ actuator ──▶ libbambu_networking.so ──▶ Bambu Cloud / LAN FTPS ──▶ printer
                           ▲                                            (official plugin, unmodified)
        printer ──MQTT 8883 (subscribe-only) + camera 322/6000──┘
  • Reads come straight from the printer on the LAN (local MQTT + camera). No cloud needed.
  • Writes (start print, pause/resume/stop) go through Bambu's own network plugin — the same libbambu_networking.so Bambu Studio downloads — hosted in a small C++ shim. To Bambu's infrastructure this host is a logged-in Bambu Studio on Linux. Sends use Studio's own route ladder: LAN FTPS upload + cloud task record first, pure cloud upload second.
  • The bridge never trusts "sent"; a job counts as started only when the printer's MQTT report shows it running with the name we gave it.

Why not X? Bambu Connect has no Linux build and no automation surface; the Local Server SDK, Fleet Hub and Developer Mode all unbind the printer from Bambu Cloud; SimplyPrint's own Bambu client is monitor-only in cloud mode. See docs/spikes/S0-plugin-host.md.

Components

Path What
shim/ libbnhost.so: plain-C facade over the plugin's C++ API (mirrors Studio's NetworkAgent). Built with the distro g++ so the libstdc++ ABI matches.
bambridge/actuator/ Plugin fetch + version pin, headless Bambu login, .gcode.3mf inspection, AMS mapping, one-job-at-a-time send lane, HTTP API. Knows nothing about SimplyPrint.
bambridge/bridge/ simplyprint-ws-client integration: local MQTT ingest, state mapping (X1/P1/A1 and H2D/X2D schemas), cameras, demand handlers, interlocks.
bambridge/common/ Config (one TOML), logging (JSON lines, secrets redacted), notifier, report-document parser shared by both.
deploy/linux/ systemd units + install.sh / uninstall.sh. deploy/docker/ alternative.
docs/ Spike notes, operations.

Install (Debian/Ubuntu host on the printers' LAN)

git clone … bambridge && cd bambridge
sudo ./deploy/linux/install.sh          # builds the shim, venv in /opt/bambridge, state in /var/lib/bambridge
bambridge login --browser                # sign in with Google/Apple/email in your browser (see below); or `bambridge login` for email+password
bambridge bridge add-printer             # per printer: name + serial; IP and access code auto (LAN discovery + Bambu Cloud) or typed in
sudo systemctl enable --now bambridge-actuator bambridge-bridge
bambridge actuator selftest              # plugin ok, logged in, cloud connected, printers bound, state flowing
bambridge bridge doctor                  # local MQTT + camera per printer, actuator reachable

Why auto-discovery is safe. LAN announcements are unauthenticated, so the bridge treats them purely as a hint for where to connect. Who it is talking to is decided by TLS: the printer must present a certificate that chains to Bambu's device CA and is issued to that printer's serial number, or the handshake fails before the access code is sent (MQTT and the port-6000 camera alike). Announcements are also ignored unless they come from the address they advertise and that address is LAN-scoped, and a working link is never re-pointed by an announcement; only a down link (or an explicit ip= in the TOML) moves. If you'd rather not listen at all, set ip= for every printer and discovery has nothing to decide.

Adding the printer in SimplyPrint. There is no local web UI; pairing uses SimplyPrint's setup-code flow (same as their Duet/Bambu integrations). Once bambridge-bridge is running and online, each not-yet-added printer gets a short code from SimplyPrint:

bambridge bridge status
# lab-h2d   0948…  WAITING TO BE ADDED — setup code: K8QZ

(also logged as *** lab-h2d: not yet added in SimplyPrint. Setup code: K8QZ ***). In the SimplyPrint web panel or app: Printers → Add printer, enter that code, pick the Bambu model (H2D / X2D), and set Multi-material to Bambu AMS. bridge status then shows PAIRED. The pairing is stored under state/simplyprint/ and survives restarts and re-installs. Give the printers DHCP reservations. On X1/H2/X2 models enable LAN Mode Liveview (a.k.a. LAN Only Liveview) on the printer for the webcam; the printer stays cloud-bound.

Login options. bambridge login --browser prints a bambulab.com URL; open it on any machine, sign in however you normally do (Google works), and Bambu redirects the browser to http://localhost:13618/?ticket=…. If the browser is on the bridge host, or you ran ssh -L 13618:localhost:13618 <host> first, the ticket is picked up automatically; otherwise the page fails to load, which is expected: copy the URL from the address bar and paste it into the waiting prompt. This is the same one-time-ticket mechanism Bambu Studio uses for third-party sign-in; the plugin exchanges the ticket for its session. --provider google skips Bambu's page and goes straight to Google. Plain bambridge login does email + password (+ the emailed code or TFA if Bambu asks); --token-file accepts a token JSON if you have one.

Docker instead: cd deploy/docker && docker compose run --rm bambridge login && docker compose run --rm bambridge bridge add-printer && docker compose up -d (host networking).

Configuration

One file, $BAMBRIDGE_HOME/bambridge.toml (mode 0600; /var/lib/bambridge when installed). bambridge config prints it with secrets masked. The interesting knobs:

[bambu]
region_country_code = "NL"        # picks api.bambulab.com vs .cn
plugin_version = "02.08.02.54"    # pinned; `bambridge plugin fetch` refuses silent changes
studio_version = "02.08.02.61"    # whose headers/cert we present
[actuator]
listen_host = "127.0.0.1"         # non-loopback requires `secret`
routes = ["lan_record", "cloud"]  # Studio's order; add "lan" for no-history LAN-only as last resort
send_timeout_s = 240
[bridge]
start_timeout_s = 180             # FILE demand → printer reports PREPARE/RUNNING with our name
control_timeout_s = 60
offline_after_s = 30
[notify]
kind = "ntfy"                     # ntfy | pushover | slack | none
target = "https://ntfy.sh/your-topic"
[[printer]]
name = "lab-h2d"
serial = "0948…"
ip = ""                           # blank = follow the printer's LAN announcements (UDP 2021); set it to pin an address
access_code = ""                  # blank = use the code Bambu Cloud reports (via the actuator); set it to override
camera = "auto"                   # auto | rtsp322 | jpeg6000 | off
pushall_on_connect = true

What SimplyPrint can and can't do through this bridge

Works Notes
Status, temps (both nozzles), progress, layers, time left within a second or two of Handy
Job attribution SimplyPrint-started jobs carry their job id (task name sp-<jobid>-<file>); Handy/Studio-started jobs appear as external jobs
AMS → material data per nozzle; AMS / AMS 2 Pro / AMS HT / external spool; active slot
HMS / print_error → notifications text from Bambu's public HMS table (bambridge hms fetch)
Webcam RTSPS 322 (X1/H2/X2/P2, needs LAN liveview toggle) or JPEG 6000 (P1/A1)
Start print (.gcode.3mf) auto AMS mapping like Studio, or SimplyPrint's mms_map; bed-levelling/flow-cal/timelapse options honoured
Pause / resume / cancel signed by the plugin; confirmed via MQTT
Not available G-code terminal, manual temperature/motion, plain .gcode files (Bambu cloud printers need 3MF), host reboot from SimplyPrint

Safety interlocks (bridge, before any send)

Printer must be IDLE/FINISH/FAILED, no fatal/serious HMS active, MQTT link up and fresh, no other start in flight. The actuator repeats the state checks against the cloud view. A send that the printer never acknowledges is not retried (no double prints); it fails as start_unconfirmed and alerts. If Handy starts something while a SimplyPrint send is in flight, the SimplyPrint job fails as printer_busy_external and the external job is shown.

Development

make -C shim                       # needs g++, nlohmann-json3-dev
uv venv -p 3.12 .venv && uv pip install -p .venv/bin/python -e '.[dev]'
.venv/bin/pytest -q                # report parsing, state map, 3MF, mapping, actuator (fake plugin), bridge client
.venv/bin/python tools/sim_e2e.py  # whole pipeline against a fake SimplyPrint + simulated H2D; prints every message sent
BAMBRIDGE_HOME=$PWD/.dev bambridge init && bambridge plugin fetch && bambridge actuator serve

tests/fixtures/mqtt/*_habambulab.json are captured printer reports from ha-bambulab (MIT). Add your own captures (redact serials/access codes) as tests/fixtures/mqtt/<name>.json with a top-level pushall key.

Licence: AGPL-3.0-or-later (inherits from simplyprint-ws-client). bambridge/data/ carries Bambu's device CA bundle and slicer certificate as shipped in Bambu Studio's resources/cert.