Skip to main content

Signed-MQTT bridge + daemon for Bambu Lab printers (X1/P1/A1/H2/X2D, Jan-2025+ firmware). Includes Home Assistant publisher, MCP server, web UI, slicing helpers, and a print-plan analyzer.

Project description

beambam — LAN-first stack for Bambu Lab printers (any model, any firmware)

ci pypi python license

beam (send) + bam (fast). Homepage: beambam.boo.

A drop-in replacement for the Bambu Network Plugin + Bambu Cloud, built for the printers Bambu's first-party stack doesn't reach: X2D, H2D, H2S, H2C, P2S, X1E, and refreshed P1+X1 firmware (Jan-2025+) that requires RSA-SHA256 signed MQTT — and for the platforms Bambu doesn't ship to (aarch64 Linux / Termux / Android phones / macOS / WSL).

The repository was originally x2d (the printer that motivated the work); the package was renamed to beambam in v1.1.0 to reflect that the bridge supports every Bambu model, not just the X2D.

What is this

Three things in one repo:

  1. x2d_bridge.py — a pure-Python LAN client that speaks Bambu's signed-MQTT control plane. Pause / resume / stop / G-code / temp / chamber light / AMS load / jog / FTPS upload + start print — same wire format the cloud plug-in uses, but readable, hackable, and running where you want.
  2. A six-surface daemon stack built on top: REST API, Server-Sent Events, Prometheus metrics, structured access log, Home Assistant MQTT auto-discovery, WebRTC chamber-camera streaming, MCP server (Claude Desktop / Cursor / Continue), and a mobile-friendly web UI with multi-printer queue, timelapse browser, AI assistant, and real-time AMS-color → filament-profile auto-resolve.
  3. A Termux/aarch64 BambuStudio port: source-patches against upstream BambuStudio v02.06.00.51, an LD_PRELOAD GTK/locale shim, an libbambu_networking.so ABI shim that lets the GUI's "Connect / AMS sync / Print" buttons drive printers via this bridge.

Who is this for

  • You own an X2D / H2D / X1E / refreshed P1+X1 and Bambu Studio refuses to connect — because the Network Plugin has no aarch64 build, OR the firmware needs RSA-SHA256 signed MQTT, OR you don't want a cloud account just to print over LAN.
  • You want to drive your printer from Home Assistant, Claude Desktop / MCP, a phone in your pocket, or a homelab dashboard — and you don't want to run a 1.5 GB Bambu Studio install just to hit "pause".
  • You want a single small Python binary you can audit, fork, and run on anything from Termux to a Raspberry Pi to a Docker container.

Feature matrix

Capability this repo Bambu Studio + Cloud ha-bambulab
LAN-only operation (no cloud login) ⚠️ partial ⚠️ requires creds
X2D / H2D RSA-SHA256 signed MQTT ✅ (cloud) ❌ blocker
aarch64 / Termux / Android ❌ (no plugin .so) ❌ (HA Core required)
One-line install ⚠️ HACS
CLI control (pause/resume/temp/...) ❌ (GUI only)
HTTP REST API + Server-Sent Events
Prometheus /metrics endpoint
Multi-printer auto-discovery (SSDP) ⚠️ manual
Home Assistant MQTT auto-discovery ✅ (32+ entities)
WebRTC chamber camera (~100 ms) ❌ (HLS only)
MCP server for Claude Desktop / IDE
Mobile-friendly web UI
Multi-printer print queue
Auto-recorded timelapses + ffmpeg stitch partial (cloud)
Natural-language assistant in UI
Real-time AMS color → profile sync
BambuStudio GUI on aarch64 Termux n/a

Full per-entity comparison vs ha-bambulab in docs/HA_VS_BAMBULAB.md.

Printer compatibility

The bridge talks to every Bambu Lab printer that exposes the standard LAN MQTT + FTPS endpoints. The signed-MQTT requirement (Jan-2025+ firmware on most models, always-on for X2D/H2D-family) is handled transparently using the publicly-leaked Bambu Connect cert — no cloud account or token needed.

Model Bambu code Signed MQTT Status Upload Print AMS Camera Notes
X1 / X1C BL-P002 / BL-P001 optional¹ RTSPS original H2 family
X1E C13 required RTSPS enterprise variant
P1P / P1S C11 / C12 required² HTTP/MJPEG P-series
P2S N7 required HTTP/MJPEG newer P-series
A1 / A1 mini N2S / N1 required² ✅ (AMS lite) HTTP/MJPEG direct drive
H2D / H2D Pro O1D / O1E required (dual nozzle) ✅ (multi-AMS) RTSPS
H2S / H2C O1S / O1C2 required RTSPS
X2D N6 required ✅ (multi-AMS, dynamic map) RTSPS primary target — beambam analyze was developed against X2D 3-color prints

¹ X1/X1C with pre-2025 firmware accept unsigned MQTT; bridge signs anyway (zero overhead, forward-compat). ² P1S/P1P/A1 enforcement varies by firmware; bridge signs regardless.

If your model isn't listed and it has the LAN MQTT switch in Settings → Network, it almost certainly works — open an issue with the X-BBL-Device-Model header from status.

Quick install + start

pip install beambam (any Linux, macOS, Windows-WSL — recommended)

pip install beambam            # core bridge + CLI
pip install beambam[all]       # + HA publisher, MCP, web UI, AI assistant

beambam --version

Configure the printer

cat >~/.x2d/credentials <<'EOF'
[printer]
ip     = 192.168.1.42
code   = 12345678
serial = 03ABC0001234567
EOF

beambam status                 # pull live state
beambam analyze model.3mf      # NEW v1.1.0 — preview the print plan
beambam print model.3mf        # FTPS upload + signed MQTT start

Web UI / daemon

beambam daemon --http 0.0.0.0:8765 \
    --queue --timelapse \
    --auth-token "$(openssl rand -hex 32)"
xdg-open http://localhost:8765/

aarch64 Termux + BambuStudio GUI (Android phones / tablets)

# One-line install. Pulls latest release tarball + builds bridge runtime;
# idempotent. Required for the BambuStudio GUI + libbambu_networking.so
# shim — the pip package alone covers everything except the GUI.
bash <(curl -fsSL https://raw.githubusercontent.com/tribixbite/beambam/main/install.sh)

Requirements

  • Python 3.10+ (3.10–3.13 tested in CI on Ubuntu + macOS)
  • paho-mqtt >= 2.0 + cryptography >= 41 (auto-installed)
  • For Termux GUI: aarch64-Termux + termux-x11 + the release tarball
  • For HA integration: any MQTT broker reachable from the daemon

Demo media

Five short H.264 MP4s in docs/demos/ — render with PYTHONPATH=. python3.12 runtime/demos/render.py to regenerate.

File Duration What
docs/demos/cli_demo.mp4 47s bridge CLI: status / chamber-light / print / daemon up
docs/demos/gui_demo.mp4 16s BambuStudio Termux port: Prepare tab, Device tab, sidebar shrink, X2D preset
docs/demos/mcp_demo.mp4 57s MCP stdio handshake: initialize → tools/list → tools/call status
docs/demos/webui_demo.mp4 15s thin web UI at S25 Ultra portrait + landscape + tablet viewport
docs/demos/ha_demo.mp4 55s Home Assistant registry: 32 entities + 1 Device with live values

Per-feature documentation

Feature Doc
Quick start docs/QUICKSTART.md
Web UI docs/WEB_UI.md
MCP server (Claude Desktop, Cursor, ...) docs/MCP.md
WebRTC streaming docs/WEBRTC.md
Home Assistant — overview docs/HA.md
Home Assistant — install + dashboard guide docs/HA_SETUP.md
HA vs ha-bambulab feature parity docs/HA_VS_BAMBULAB.md
Cloud bridge (cloud-state, cloud-print) docs/CLOUD_BRIDGE.md
Local control paths (LAN MQTT, FTPS, RTSPS, LVL_Local) docs/LOCAL_CONTROL_PATHS.md
Signed vs unsigned MQTT — leaked-key truth table docs/SIGNED_VS_UNSIGNED.md
X2D runtime pipeline (canonical reference) docs/X2D_RUNTIME_PIPELINE.md
Multi-printer setup docs/MULTI_PRINTER.md
Print queue docs/QUEUE.md
Timelapse browser docs/TIMELAPSE.md
AI assistant docs/ASSISTANT.md
AMS color sync docs/COLORSYNC.md
BambuStudio Termux port this README §"BambuStudio Termux port" below

The Bambu Network Plug-in .so is shipped only for x86_64 Linux and arm64 macOS. On aarch64 Termux it has no equivalent build, so out of the box BambuStudio's GUI cannot connect to a LAN printer or sync AMS spool data. The bridge in this repo replaces what the plug-in would have done for the LAN-only path.

BambuStudio Termux port

If you want the BambuStudio GUI itself to launch + connect on aarch64 Termux (rather than only driving the printer headlessly via the bridge), this repo also ships the source patches and LD_PRELOAD shim required. Layout:

.
├── patches/                  # 50+ unified diffs against upstream BambuStudio
│   │                         # (touchscreen-tap slop, AMSControl width,
│   │                         #  GLCanvas3D termux-x11 paint, MainFrame
│   │                         #  Ctrl+R/Ctrl+P slice/print accelerators,
│   │                         #  Plater sidebar, deps build wiring, …)
│   ├── Button.cpp.termux.patch              # touch-target 15-px slop
│   ├── AxisCtrlButton.cpp.termux.patch      # touch-target 15-px slop
│   ├── SideButton.cpp.termux.patch          # touch-target 15-px slop
│   ├── TabButton.cpp.termux.patch           # touch-target 15-px slop
│   ├── BBLTopbar.cpp.termux.patch           # vertical Print/plate stack
│   ├── BBLTopbar.hpp.termux.patch
│   ├── MainFrame.cpp.termux.patch           # Ctrl+R/Ctrl+P + sidebar tog
│   ├── GLCanvas3D.cpp.termux.patch          # termux-x11 force-paint
│   ├── AMSControl.cpp.termux.patch          # AMS strip width
│   ├── (… and ~40 more — patches/ for the full set)
├── runtime/
│   ├── preload_gtkinit.c     # LD_PRELOAD shim: GTK pre-init, locale fix,
│   │                         # wxLocale ICU bypass, wx 3.3 assert silencer,
│   │                         # hidden config_wizard_startup override
│   ├── network_shim/         # libbambu_networking.so ABI shim — lets the
│   │                         # GUI's Connect/Print/AMS sync buttons drive
│   │                         # the bridge over a Unix socket (#1)
│   ├── webrtc/               # aiortc gateway (#45)
│   ├── webui/                # web UI test harnesses (#46-48)
│   ├── ha/                   # MQTT auto-discovery publisher (#50-54)
│   ├── mcp/                  # MCP stdio server (#42-44)
│   ├── queue/                # multi-printer queue manager (#55)
│   ├── timelapse/            # auto-timelapse recorder (#56)
│   ├── assistant/            # natural-language MCP router (#57)
│   └── colorsync/            # AMS color → filament profile (#58)
├── web/                      # static HTML/CSS/JS for the thin web UI
├── docs/                     # per-feature documentation
├── run_gui_clean.sh          # canonical GUI launcher
├── x2d_bridge.py             # signed-MQTT LAN client + multi-surface daemon
├── bambu_cert.py             # publicly-leaked Bambu Connect signing key
├── lan_upload.py             # FTPS:990 implicit-TLS uploader (helper subset)
├── lan_print.py              # upload + start_print combo
├── make_frame.py             # generates a debossed picture-frame STL
├── inject_thumbnails.py      # injects the 5 PNG thumbnails firmware needs
├── resolve_profile.py        # flatten BambuStudio profile inheritance
└── test_signed_mqtt.py       # diagnostic: pushall with RSA-SHA256 signature

bs-bionic/ (the BambuStudio source tree) and dist/ (built tarball) are gitignored — the patches in patches/ reproduce the source changes against a clean clone.

Detailed install / credentials / launch walkthrough

TL;DR runtime guide: see docs/QUICKSTART.md for the install → credentials → launch → connect → print walkthrough, plus copy-pasteable shortcuts for every print-control verb, the camera proxy URLs, and a troubleshooting table.

Use the prebuilt distribution

One-line install (recommended)

bash <(curl -fsSL https://raw.githubusercontent.com/tribixbite/beambam/main/install.sh)

Idempotent — runs the platform check, pkg installs the runtime deps, fetches the latest release tarball, verifies SHA-256, drops the libbambu_networking.so + libBambuSource.so shims into ~/.config/BambuStudioInternal/plugins/, pre-seeds ~/.config/BambuStudioInternal/BambuStudio.conf with the X2D model entry, writes a chmod-600 ~/.x2d/credentials skeleton for you to fill in, and (if Termux:Boot is installed) drops a boot-time launcher for the bridge daemon. Re-run any time to upgrade — your BambuStudio.conf and credentials file are preserved.

Manual install (alternative)

A prebuilt tarball is attached to the GitHub release:

curl -L -o bs-x2d.tar.xz \
    https://github.com/tribixbite/beambam/releases/latest/download/bambustudio-x2d-termux-aarch64.tar.xz
tar -xJf bs-x2d.tar.xz
cd bambustudio-x2d-termux-aarch64
./run_gui.sh                  # needs termux-x11 running on DISPLAY=:1

Termux dependencies

pkg install x11-repo
pkg install \
    wxwidgets gtk3 webkit2gtk-4.1 \
    glew glfw mesa libllvm llvm \
    glib pango cairo gdk-pixbuf atk \
    fontconfig freetype libpng libjpeg libtiff \
    openssl curl libcurl \
    opencv libdbus libwebp \
    libavcodec libswscale libavutil ffmpeg \
    python python-cryptography xdotool \
    openbox
pip install paho-mqtt

Why openbox is in the list: termux-x11 ships without a window manager. Without one, wxFileDialogs open undecorated at (0,0) and stack under the main frame so Cancel-button taps land on the main frame instead of the dialog ("Cancel buttons don't work"); transient dialogs can't be dragged; wxFrame::Maximize is a no-op. Openbox (≈600 KB) supplies a minimal EWMH-aware WM that fixes all of those. run_gui.sh will spawn it automatically on launch if it's installed and not already running.

The most version-sensitive of these is libllvm — Mesa requires it at the same major version. If pkg upgrade ever leaves them mismatched you'll get EGL_BAD_PARAMETER / "Unable to get EGL Display"; recover with pacman -S libllvm llvm.

You also need a working X server. The reference setup is termux-x11 (install the Android app and run termux-x11 in a Termux session, reachable on :1).

Build from source

git clone --recurse-submodules https://github.com/bambulab/BambuStudio
cd BambuStudio
git checkout v02.06.00.51
for p in /path/to/x2d/patches/*.termux.patch; do git apply "$p"; done
# Use the bionic-toolchain steps from
# ~/.claude/skills/bambustudio-on-termux-aarch64.md
mkdir build && cd build && cmake -GNinja .. && ninja bambu-studio
gcc -fPIC -shared ../runtime/preload_gtkinit.c \
    $(pkg-config --cflags --libs gtk+-3.0) -ldl -o ../runtime/libpreloadgtk.so

libbambu_networking.so shim — making the GUI talk to LAN printers

The Bambu Network Plug-in .so is shipped only for x86_64 Linux + arm64 macOS, so on aarch64 Termux the GUI's "Connect / AMS / Print" buttons have nothing to dlopen. runtime/network_shim/ builds a drop-in replacement that exports the entire NetworkAgent ABI (103 bambu_network_* symbols + 21 ft_* symbols) and forwards the LAN-relevant calls over a Unix-domain socket to a long-running x2d_bridge.py serve process. Cloud-only entry points return success-with-empty so the GUI's cloud panels stay quiet but the LAN flow works end-to-end.

cd runtime/network_shim
make            # builds libbambu_networking.so + libBambuSource.so
make install    # → ~/.config/BambuStudioInternal/plugins/

A self-test harness lives at runtime/network_shim/tests/test_shim_e2e.py. It dlopens the .so, asserts every host-expected symbol is exported, then spawns a fresh x2d_bridge.py serve and round-trips through the protocol against a real X2D (handshake → connect → state event → disconnect):

python3.12 runtime/network_shim/tests/test_shim_e2e.py

Wire format and op set are spec'd in runtime/network_shim/PROTOCOL.md. The shim spawns x2d_bridge.py serve itself if no socket is found at $X2D_BRIDGE_SOCK (default ~/.x2d/bridge.sock), so the GUI launch flow is just: install the .so once, then ./run_gui.sh.

Using the LAN bridge (x2d_bridge.py)

Save credentials once. Either a single [printer] section (the default, used when no --printer NAME is passed) or as many named [printer:NAME] sections as you want:

mkdir -p ~/.x2d && chmod 700 ~/.x2d
cat > ~/.x2d/credentials <<EOF
# Single-printer setup:
[printer]
ip = 192.168.x.y
code = <8-char access code from printer screen>
serial = <printer serial from device sticker>

# OR, multiple named printers:
[printer:studio]
ip = 192.168.x.y
code = …
serial = …

[printer:basement]
ip = 10.0.0.50
code = …
serial = …
EOF
chmod 600 ~/.x2d/credentials

Pick a named printer with --printer NAME, the X2D_PRINTER env var, or — when only one named section exists and there's no plain [printer] — automatically.

Then:

# One-shot state dump
x2d_bridge.py status

# Upload + start print on AMS slot 4
x2d_bridge.py print myfile.gcode.3mf --slot 3

# Long-running monitor — polls every 5s, exposes JSON at http://127.0.0.1:8765/state
x2d_bridge.py daemon --http 127.0.0.1:8765 --quiet

# Same daemon also exposes /healthz for uptime monitoring:
#   200 + {"healthy": true,  ...} when fresh state arrived recently
#   503 + {"healthy": false, ...} when MQTT silently disconnected
# Threshold is configurable via --max-staleness (default 30s).
curl http://127.0.0.1:8765/healthz

# Prometheus scrape: gauges for nozzle/bed/chamber temps, AMS humidity,
# print progress; counters for total_messages, mqtt_disconnects, ssdp_notifies.
curl http://127.0.0.1:8765/metrics

# Every HTTP hit is appended as one JSON line to ~/.x2d/access.log
# (ts, method, path, status, duration_ms, printer, authed, client).
# Rotates to access.log.1 at 1 MiB.
tail -f ~/.x2d/access.log

Credentials can also come from --ip / --code / --serial flags or X2D_IP / X2D_CODE / X2D_SERIAL environment variables.

Slicing + printing an STL

End-to-end one-shot: slice an STL, upload to the printer, queue the print. The 3MF is the slicer's authoritative contract — cmd_print auto-derives bed_type and bed initial-layer temperature from Metadata/project_settings.config and refuses to publish if the target AMS slot is empty or holds an incompatible filament class (PLA-vs-PETG-vs-ABS). The hard guards (empty slot, class mismatch) are not bypassable; soft brand/colour mismatches warn under --force.

# Standalone slicer (STL → .gcode.3mf with proper plate + filament profile)
python3.12 x2d_slice.py model.stl --out out.gcode.3mf \
    --scale 1.0                     # uniform geometry scale
    --color "PLA Silk Gold"         # Bambu colour-name resolution against
                                     # filaments_color_codes.json (304 entries)
    --bed supertack                 # plate type — cool/engineering/high_temp/
                                     # textured/supertack OR int 1..5

# One-shot slice → upload → queue
x2d_bridge.py slice-print model.stl --slot 3 --color Gold --bed 5 --timelapse

# Send an already-sliced .gcode.3mf
x2d_bridge.py print model.gcode.3mf --slot 3 --timelapse
    # bed_type + bed_temp auto-derived from the 3MF; refuses on AMS
    # slot empty / wrong-class filament. Pass --force to bypass soft
    # brand/colour mismatches; hard guards never bypassable.

Print-control commands

Each verb is a single signed-MQTT publish — same protocol the official GUI uses, just routed through our bridge so it works on aarch64. Every command exits as soon as the printer ACKs the publish.

⚠️ LAN signing limitation. Only system.* commands (chamber light, etc.) actually verify against the leaked Bambu Connect global cert. print.* commands (project_file, ams_change_filament, etc.) are verified by the firmware against per-device factory certs and silently dropped if our cert isn't in the trust list. See docs/SIGNED_VS_UNSIGNED.md. For print.* actions the working path is cloud-print (cloud broker, JWT auth, no signing) or starting the print from the touchscreen after the bridge has uploaded the 3MF.

x2d_bridge.py pause                      # pause the current print
x2d_bridge.py resume                     # resume after pause
x2d_bridge.py stop                       # abort the current print
x2d_bridge.py home                       # G28 — home all axes
x2d_bridge.py level                      # G29 — auto bed-level
x2d_bridge.py set-temp bed     60        # set_bed_temp 60°C
x2d_bridge.py set-temp nozzle 220 --idx 0  # set_nozzle_temp on extruder 0
x2d_bridge.py set-temp chamber 35        # M141 S35 (chamber heater)
x2d_bridge.py chamber-light on           # ledctrl on / off / flashing
x2d_bridge.py chamber-light flashing --on-time 200 --off-time 200 --loops 5
x2d_bridge.py ams-load 0 3 --tar-temp 220   # AMS 0 / slot 3, preheat to 220
x2d_bridge.py ams-unload 0 --tar-temp 220   # unload from AMS 0
x2d_bridge.py jog X 10                   # relative move +10 mm on X
x2d_bridge.py jog Z -5 --feed 600        # relative -5 mm on Z @ 600 mm/min
x2d_bridge.py gcode "M115"               # send arbitrary gcode_line

Payload schemas reverse-engineered from bs-bionic/src/slic3r/GUI/DeviceManager.cpp::MachineObject::command_* so the printer behaviour matches what the official GUI sends.

Camera proxy

The printer's chamber camera streams over RTSPS, but only after you enable LAN-mode liveview on the printer's touchscreen (Settings → Network → Liveview). Otherwise the stream lives on a closed proprietary protocol on TCP/6000 that requires the x86_64-only libBambuSource.so to decode.

Once enabled, run:

x2d_bridge.py camera                     # bind 127.0.0.1:8766 by default
x2d_bridge.py camera --bind 0.0.0.0:8766 # expose on LAN (be careful!)
x2d_bridge.py camera --idle-timeout 60   # stop ffmpeg after 60s of no viewers
                                          # default 30s; set very high to keep
                                          # the pump permanently warm

The pump is on-demand since 2026-05-06: ffmpeg only starts after the first /cam.mjpeg connect or /cam.jpg//cam.m3u8 request, and the supervisor reaps it after --idle-timeout seconds of zero viewers + zero one-shot endpoint hits. Subsequent requests respawn within ~3-4 s (RTSPS reconnect + first JPEG). On phone hosts this drops idle CPU from a continuous ~66% to 0%.

Then point any browser at http://127.0.0.1:8766/cam.mjpeg for the multipart MJPEG stream, or /cam.jpg for a one-shot snapshot. Multiple viewers share one ffmpeg subprocess, so bandwidth to the printer is constant regardless of how many people are watching.

Pre-flight checks ipcam.rtsp_url via signed MQTT and bails with a clear hint if liveview is still disabled. Pass --skip-check to bypass (useful when MQTT is flaky but RTSP is open).

Two transport options:

  • --proto rtsp (default): RTSPS:322 via ffmpeg. Fast, well-tested, gated on ipcam.rtsp_url != "disable".
  • --proto local: TLS:6000 LVL_Local — Bambu's proprietary stream protocol. The TLS handshake + 80-byte auth blob + 16-byte frame framing are reverse-engineered in runtime/network_shim/lvl_local.py. The same printer-touchscreen "LAN-mode liveview" toggle gates this path too — without it the printer rejects with status 0x0003013f and a clear error message.

Three HTTP outputs:

  • /cam.mjpeg — multipart/x-mixed-replace, browser-renderable, low latency
  • /cam.jpg — single latest JPEG snapshot (one-shot)
  • /cam.m3u8 — HLS playlist with 2-second segments (12s sliding window). Plays in any mobile browser via <video src="…/cam.m3u8" controls>. Higher latency than MJPEG (~6-8s) but survives flaky connections better and supports seeking.

Exposing the daemon / camera on the LAN

/state /healthz /cam.mjpeg /cam.jpg are open on loopback by default (single-user local use). Binding non-loopback (--http 0.0.0.0:8765 or --bind 0.0.0.0:8766) requires a bearer token, otherwise every request returns 401 Unauthorized with a WWW-Authenticate: Bearer hint:

# Generate a long random token, then:
export X2D_AUTH_TOKEN=$(openssl rand -hex 32)
x2d_bridge.py daemon --http 0.0.0.0:8765
x2d_bridge.py camera --bind 0.0.0.0:8766

# From another box on the LAN:
curl -H "Authorization: Bearer $X2D_AUTH_TOKEN" http://<phone>:8765/healthz

--auth-token on the command line takes precedence over the env var. Loopback binds keep the open path even with a token configured? — no: once you set a token, loopback also enforces it, so the same curl command works against both 127.0.0.1 and a LAN-exposed bind.

Filament presets without a cloud account

When no cloud session exists, bambu_network_get_user_presets falls back to a local catalog so the GUI's AMS spool dropdown isn't empty:

  • All instantiation: true BBL filament profiles shipped under resources/profiles/BBL/filament/ (1464 entries on the current binary — every Bambu vendor variant for every supported model).
  • A small community-curated set under runtime/network_shim/data/community_filaments.json covering Generic PLA / PETG / ABS / TPU / ASA plus Polymaker / Prusament / eSun / Hatchbox flavours. Edit that file to add your own.

If you sign in via cloud-login, the cloud-synced presets take precedence — the local fallback only activates anonymously.

Bambu cloud login (optional)

The bridge can talk to the Bambu Lab cloud REST API to populate the GUI surfaces that the shim was previously stubbing — is_user_login, get_user_id, get_user_presets, get_user_tasks. Without a cloud session, those calls keep returning empty (LAN-only flow is unaffected).

x2d_bridge.py cloud-login --email me@example.com --password '…'
x2d_bridge.py cloud-status     # confirms session, expiry, region
x2d_bridge.py cloud-logout     # wipes ~/.x2d/cloud_session.json

Tokens land at ~/.x2d/cloud_session.json (chmod 600). The bridge auto-refreshes the access token when it's within 5 min of expiry. Region is auto-detected from the email TLD (.cn → CN, else US), override with --region.

This module hits the same endpoints the open-source community already documented (pybambu, bambu-farm-manager, OrcaSlicer). Bambu has no published SDK — if they rotate URLs, this module needs to rotate in lockstep with the upstream consumers.

Cloud-mediated print + remote control

Once cloud-login has succeeded, twelve cloud-* CLIs work off-LAN by routing through Bambu's cloud MQTT broker. Useful when the X2D's LAN-direct print.* is blocked by the per-installation-cert wall (items #65 / #66 / #68): the cloud broker uses standard TLS, no per- installation cert needed.

x2d_bridge.py cloud-state                          # remote monitoring
x2d_bridge.py cloud-pause                          # remote control
x2d_bridge.py cloud-print rumi_frame.gcode.3mf     # full upload + print

Same --http :8765 daemon also exposes /cloud/{status,printers,state,publish} HTTP routes for the web UI / Home Assistant. Full surface + implementation details: docs/CLOUD_BRIDGE.md.

Thin web UI (http://<phone>:8765/)

The bridge daemon serves a mobile-friendly remote-control surface at /. No build step, no framework — web/index.html + web/index.js

  • web/index.css (~17 KB total). Live state arrives over Server-Sent Events (/state.events); pause / resume / stop / lights / heat presets / AMS slot loads POST to /control/<verb> and the daemon publishes via the long-lived MQTT client. Camera tabs let you flip between /cam.jpg snapshot polling, native HLS at /cam.m3u8, and WebRTC at /cam.webrtc/offer.
python3.12 x2d_bridge.py daemon --http 0.0.0.0:8765 --auth-token "$X2D_AUTH_TOKEN"
# Then open http://<phone-ip>:8765/ in any modern browser.

Test harness: PYTHONPATH=. python3.12 runtime/webui/test_webui.py → 33/33 PASS, exercises every static / SSE / control route end-to-end.

MCP server (Claude Desktop, Cursor, Continue, …)

The bridge ships an MCP server at runtime/mcp/server.py so any MCP-aware client can drive prints conversationally. Eighteen tools (status, pause, resume, stop, gcode, set_temp, chamber_light, ams_load/unload, jog, upload, print, camera_snapshot, list_printers, healthz, metrics, home, level) plus two resources (x2d://state, x2d://camera/snapshot).

// Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json (mac)
//                 %APPDATA%\Claude\claude_desktop_config.json           (Windows)
//                 ~/.config/Claude/claude_desktop_config.json           (Linux)
{
  "mcpServers": {
    "x2d": {
      "command": "python3.12",
      "args": ["-m", "mcp_x2d"],
      "cwd": "/absolute/path/to/x2d"
    }
  }
}

Smoke-test the server before wiring it in:

python3.12 runtime/mcp/test_mcp.py     # 47/47 PASS, ALL TESTS PASSED

Full per-platform install + Termux-via-SSH setup in docs/MCP.md.

What's broken on Termux without these patches

Symptom Root cause Patch
GUI aborts at start with Gtk-ERROR: Can't create a GtkStyleContext without a display connection wxFont static init touches GTK CSS before gtk_init runtime/preload_gtkinit.c (constructor 101)
GUI shows "Switching language en_US failed" then exits wx 3.3 wxUILocale::IsAvailable is ICU-backed, Termux libicu has no en_US shim overrides the symbol
setlocale("en_US", …) returns NULL → modal exit bionic accepts en_US.UTF-8 but not bare en_US shim retries with .UTF-8 suffix
GUI runs ~20s then dies on first GL draw with zink_kopper.c:720 assert Mesa picks zink (Vulkan→GL); kopper needs DRI3/Present which termux-x11 lacks run_gui_clean.sh forces GALLIUM_DRIVER=llvmpipe
Cancel buttons / AMS spool taps / sidebar buttons silently dropped custom Button::mouseReleased strict bounds check vs. touch-drift patches/{Button,AxisCtrlButton,SideButton,TabButton}.cpp.termux.patch
Maximize button does nothing / window goes off-screen in portrait termux-x11 has no WM; BBLTopbar relies on wxFrame::Maximize(); min-size 1000×600 exceeds portrait width patches/BBLTopbar.{cpp,hpp}.termux.patch
LAN connect / AMS sync / print impossible Network Plug-in is x86_64 only runtime/network_shim/ (libbambu_networking.so stub) + x2d_bridge.py serve
mqtt message verify failed (err_code 84033543) on every command Jan-2025+ firmware requires RSA-SHA256 signature in header block bambu_cert.py (publicly-leaked Bambu Connect cert)

Provenance

Built and tested on:

  • Termux aarch64, x11-repo packages (wxwidgets 3.3, gtk3, webkit2gtk-4.1, mesa 26.0.5, libllvm 21, …)
  • termux-x11 Android app, software-rendering display :1
  • Bambu Lab X2D, dual-extruder, AMS HT 4-slot, firmware ≥ Jan 2025

GPL-3.0+ (matches upstream BambuStudio). Bambu and BambuStudio are trademarks of Shenzhen Bambu Lab Technology Co., Ltd. — this repo is not affiliated with or endorsed by them.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

beambam-1.2.0.tar.gz (3.0 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

beambam-1.2.0-py3-none-any.whl (552.2 kB view details)

Uploaded Python 3

File details

Details for the file beambam-1.2.0.tar.gz.

File metadata

  • Download URL: beambam-1.2.0.tar.gz
  • Upload date:
  • Size: 3.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for beambam-1.2.0.tar.gz
Algorithm Hash digest
SHA256 4a9ca557a0d41a1f69c3f5822350f7e62b5c9513d09f73d51c0f9004b7f73db8
MD5 4fda9cea1e17fc30072d3c9c6890b225
BLAKE2b-256 2242168c89f4cf5b0b0f4423dd2a6f3c1f46ec1597d5dbfe1a9eb880bd829f95

See more details on using hashes here.

Provenance

The following attestation bundles were made for beambam-1.2.0.tar.gz:

Publisher: release.yml on tribixbite/beambam

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file beambam-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: beambam-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 552.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for beambam-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c458b77f1e41f89f65fb2a563f0727d7caae68935b87e99027cc15861f655c19
MD5 c583f208511112fd67bb118a3a4d190e
BLAKE2b-256 4acf085ec491e704c3f3d23d18923c9f4a6b3f17bd1211c74e8e76931178ec4c

See more details on using hashes here.

Provenance

The following attestation bundles were made for beambam-1.2.0-py3-none-any.whl:

Publisher: release.yml on tribixbite/beambam

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page