8.0 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
KX-Bridge is a Python 3.11+ bridge that emulates the Klipper/Moonraker API to enable OrcaSlicer to control Anycubic Kobra X printers in LAN mode — no Klipper, no Raspberry Pi required. It reverse-engineers the Anycubic MQTT protocol to relay commands from slicers to the printer.
Running
Docker (recommended):
docker compose up -d
docker compose logs -f
Python directly:
pip install -r requirements.txt
python kobrax_moonraker_bridge.py --printer-ip 192.168.x.x
Build binary:
pyinstaller kx-bridge.spec
No test suite exists in this repository.
Architecture
The system bridges three parties:
OrcaSlicer / Browser
↕ HTTP + WebSocket (port 7125+, Moonraker API)
KX-Bridge (kobrax_moonraker_bridge.py)
↕ MQTT over TLS (port 9883, Anycubic LAN protocol)
Anycubic Kobra X Printer
Core Files
- kobrax_moonraker_bridge.py — Main server (~5,000 lines). Houses
KobraXBridge(HTTP/WebSocket handlers, state management),GCodeStore(SQLite-backed file/print-history storage), andCameraCache(MJPEG stream caching). This is where all Moonraker API endpoints are implemented. - kobrax_client.py — Low-level MQTT client for the Kobra X. Handles raw MQTT 3.1.1 framing, TLS certificate auth, topic subscriptions, and message dispatch.
- config_loader.py — Loads
config/config.ini(primary); env_loader.py handles.envfallback. Also exposeslist_notification_urls(),list_printers(),list_filament_profiles(). - fetch_credentials.py — Standalone tool to pull printer credentials over HTTP from the Kobra X.
- extract_credentials.py — Reads credentials from a running AnycubicSlicerNext process (Windows/Linux).
Web UI
Single-page app served by the bridge itself:
- web/themes/default/index.html — Shell HTML
- web/themes/default/app.js — All client-side logic (~2,500 lines): WebSocket handling, state rendering, UI events
- web/themes/default/style.css — Dark/light theme via CSS variables
- web/translations/ — i18n strings (DE, EN, ES, ZH-CN)
When adding UI text, all four translation files must be updated. The hint element for notifications uses innerHTML (not textContent) to support links — follow the same pattern used for orca_profile_help_html when translation values contain HTML.
The theme system is documented in web/DOC/THEME-CSS-HOOKS.md and web/DOC/THEME-JS-ID-HOOKS.md.
Configuration
Primary config: config/config.ini (copy from config/config.ini.example). Sections:
[connection]— Printer IP, MQTT credentials, device/mode IDs[printer_N]— One section per printer for multi-printer setups (ports 7125–7130)[print]— AMS slots, auto-leveling, camera[filament_profiles]— Per-slot filament for AMS[bridge]— Poll interval (1–5 s, default 3 s), printer name[notifications]— Apprise notification URLs and interval settings (see below)
Data Storage
- SQLite at
data/kx-bridge.db— G-code metadata, print history, thumbnails (base64) data/orca_filaments.json— OrcaSlicer filament database, loaded at startup
Key Protocol Details
- MQTT auth: AES-256-CBC encrypted credentials + TLS certificates (
anycubic_slicer.crt/keybundled via PyInstaller) - Moonraker emulation: Kobra states are mapped to Klipper states via
KOBRA_TO_KLIPPER_STATEinkobrax_moonraker_bridge.py - Real-time logs: Streamed to the browser via Server-Sent Events (SSE), not WebSocket
- Multi-printer: Each printer runs its own
KobraXBridgeinstance on a separate port
PyInstaller Build
kx-bridge.spec bundles the web UI, filament database, and Anycubic TLS certificates into a single binary. The web/ tree maps to static/ inside the binary. Both pycryptodome and apprise use collect_all() to capture dynamically loaded plugins.
Notification System (Apprise)
Push notifications are sent via the apprise library (supports 60+ services via URL syntax: discord://, telegram://, pover://, gotify://, slack://, etc.).
Config format ([notifications] section):
url_1 = discord://webhook_id/webhook_token
events_1 = started,finished,failed,cancelled,paused,progress
image_1 = false
url_2 = pover://USERKEY@TOKEN
events_2 = finished,failed
image_2 = true
notify_every_minutes = 10
notify_every_layers = 0
Key implementation points:
KobraXBridge._notify(event, filename)— fires on state transitions detected in_on_print(). Splits matching URLs into plain and image groups; image group attaches a temp.jpgfromcamera_cache.latest_jpegviaapprise.AppriseAttachment.KobraXBridge._check_progress_notifications()— called from_poll_loop()every poll tick; firesprogressevent when the time or layer threshold is crossed. Both counters are reset together when either fires, and are also reset when a new print starts._prev_kobra_stateguards against duplicate notifications when the printer repeatedly reports the same state.- All notification dispatches run in
threading.Thread(daemon=True)to avoid blocking the MQTT reader thread or the event loop. - The test endpoint (
POST /api/notifications/test) runs apprise synchronously viarun_in_executor.
Supported events: started, finished, failed, cancelled, paused, progress
Settings UI: Managed through the WebUI settings modal. Each URL entry has per-event checkboxes, a 📷 Image toggle, and a Test button. Global repeat interval fields (minutes / layers) apply to all URLs subscribed to the progress event.
Android App
A companion Android app lives in android/. It connects to a running KX-Bridge server and provides a mobile printer control panel.
Building
cd android
./gradlew assembleDebug # APK at app/build/outputs/apk/debug/
./gradlew assembleRelease # Minified release APK
- Min SDK: 26 (Android 8.0), Compile/Target SDK: 34
- Language: Kotlin 2.0.0 + Jetpack Compose (Material3)
- Java toolchain: 17
- No test suite exists.
Architecture
android/app/src/main/java/com/kxbridge/
├── MainActivity.kt # Entry point; reads SharedPreferences for server URL
├── data/
│ ├── PrinterRepository.kt # OkHttp3 HTTP client; polls /api/state every 3 s
│ └── model/PrinterState.kt # kotlinx-serialization data model (snake_case JSON)
├── viewmodel/PrinterViewModel.kt # StateFlow<UiState> (Loading / Success / Error)
└── ui/
├── PrinterScreen.kt # Main control UI: temps, progress, pause/resume/cancel
└── SetupScreen.kt # First-run server URL entry form
MainActivity stores the server URL in SharedPreferences ("kxbridge" / key "server_url") and switches between SetupScreen and PrinterScreen based on whether a URL is configured.
PrinterViewModel drives all network interaction: it holds a PrinterRepository, exposes a StateFlow<UiState>, and exposes command methods (pause, resume, cancel). UiState is a sealed interface.
Communication with KX-Bridge
The app uses plain HTTP (cleartext allowed in the manifest):
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/state |
Fetch PrinterState JSON (polled every 3 s) |
| POST | /printer/print/pause |
Pause active print |
| POST | /printer/print/resume |
Resume paused print |
| POST | /printer/print/cancel |
Cancel print |
PrinterState fields use @SerialName for snake_case JSON keys. ProGuard is configured to keep com.kxbridge.data.model.** to prevent serialization breakage in release builds.