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)
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 tobeambamin v1.1.0 to reflect that the bridge supports every Bambu model, not just the X2D.
Roadmap: see ROADMAP.md for v1.3.0 candidates + the
long-tail backlog. Per-release notes in CHANGELOG.md.
What is this
Three things in one repo:
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.- 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.
- A Termux/aarch64 BambuStudio port: source-patches against
upstream
BambuStudio v02.06.00.51, anLD_PRELOADGTK/locale shim, anlibbambu_networking.soABI 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 | 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
.sois 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. Seedocs/SIGNED_VS_UNSIGNED.md. Forprint.*actions the working path iscloud-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)
beambam led on # ledctrl on / off / flashing
beambam led flashing --on-time 200 --off-time 200 --loops 5
# (alias kept: `chamber-light`)
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
# AMS slot metadata — push tray_info_idx / temps / color to one slot or
# the whole bank in one command. Profile files live in flat-profiles/.
beambam ams set 7 'flat-profiles/eSUN PLA+ @BBL X2D 0.4 nozzle.json' \
--color F98C36 # one slot, explicit color
beambam ams sync # batch from flat-profiles/ams-sync.json
beambam ams sync --dry-run # preview (no MQTT, no creds needed)
See flat-profiles/AMS_MAPPING.md for
the bundled 13-slot sync map and the schema each profile JSON must
follow.
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:
beambam cam start # bind 127.0.0.1:8766 by default (was `camera`)
beambam cam start --bind 0.0.0.0:8766 # expose on LAN (be careful!)
beambam cam start --idle-timeout 60 # stop ffmpeg after 60s of no viewers
beambam cam stop # kill running proxy (PID in ~/.x2d/cam.pid)
beambam cam # one-shot snapshot to ./cam.jpg
beambam cam watch # live in-terminal viewer
# 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 onipcam.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 inruntime/network_shim/lvl_local.py. The same printer-touchscreen "LAN-mode liveview" toggle gates this path too — without it the printer rejects with status0x0003013fand 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: trueBBL filament profiles shipped underresources/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.jsoncovering 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.jpgsnapshot 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file beambam-1.4.0.tar.gz.
File metadata
- Download URL: beambam-1.4.0.tar.gz
- Upload date:
- Size: 3.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad363698fd738c7b82ab5d99a6d3efb0f7a60bd50f3a6b7ad65f6cd7ba1bac3e
|
|
| MD5 |
f789bc0e444d83e6616454272ead39d9
|
|
| BLAKE2b-256 |
8696c6cf9689261139d4afc741c6679398caa4d93ab0d9fad657e49f58da1f23
|
Provenance
The following attestation bundles were made for beambam-1.4.0.tar.gz:
Publisher:
release.yml on tribixbite/beambam
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
beambam-1.4.0.tar.gz -
Subject digest:
ad363698fd738c7b82ab5d99a6d3efb0f7a60bd50f3a6b7ad65f6cd7ba1bac3e - Sigstore transparency entry: 1600846431
- Sigstore integration time:
-
Permalink:
tribixbite/beambam@ee271e973dcf11952143babf11d4c43511fbaf7f -
Branch / Tag:
refs/tags/v1.4.0 - Owner: https://github.com/tribixbite
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ee271e973dcf11952143babf11d4c43511fbaf7f -
Trigger Event:
release
-
Statement type:
File details
Details for the file beambam-1.4.0-py3-none-any.whl.
File metadata
- Download URL: beambam-1.4.0-py3-none-any.whl
- Upload date:
- Size: 649.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e97ddd20d0027149d795d8e70825f91e5adba02ce64df33e9c79aa52b4295cb9
|
|
| MD5 |
da2120f59df24cd29fcf68e27c2ebb2b
|
|
| BLAKE2b-256 |
aa9c3003a445cbaf914826509e0b324b9054167907a4908048f0306e361e0bff
|
Provenance
The following attestation bundles were made for beambam-1.4.0-py3-none-any.whl:
Publisher:
release.yml on tribixbite/beambam
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
beambam-1.4.0-py3-none-any.whl -
Subject digest:
e97ddd20d0027149d795d8e70825f91e5adba02ce64df33e9c79aa52b4295cb9 - Sigstore transparency entry: 1600846770
- Sigstore integration time:
-
Permalink:
tribixbite/beambam@ee271e973dcf11952143babf11d4c43511fbaf7f -
Branch / Tag:
refs/tags/v1.4.0 - Owner: https://github.com/tribixbite
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ee271e973dcf11952143babf11d4c43511fbaf7f -
Trigger Event:
release
-
Statement type: