forked from viewit/KX-Bridge-Release
chore: add claude.md
This commit is contained in:
161
CLAUDE.md
Normal file
161
CLAUDE.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# 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):**
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
**Python directly:**
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
python kobrax_moonraker_bridge.py --printer-ip 192.168.x.x
|
||||
```
|
||||
|
||||
**Build binary:**
|
||||
```bash
|
||||
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](kobrax_moonraker_bridge.py) — Main server (~5,000 lines). Houses `KobraXBridge` (HTTP/WebSocket handlers, state management), `GCodeStore` (SQLite-backed file/print-history storage), and `CameraCache` (MJPEG stream caching). This is where all Moonraker API endpoints are implemented.
|
||||
- [kobrax_client.py](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](config_loader.py) — Loads `config/config.ini` (primary); [env_loader.py](env_loader.py) handles `.env` fallback. Also exposes `list_notification_urls()`, `list_printers()`, `list_filament_profiles()`.
|
||||
- [fetch_credentials.py](fetch_credentials.py) — Standalone tool to pull printer credentials over HTTP from the Kobra X.
|
||||
- [extract_credentials.py](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](web/themes/default/index.html) — Shell HTML
|
||||
- [web/themes/default/app.js](web/themes/default/app.js) — All client-side logic (~2,500 lines): WebSocket handling, state rendering, UI events
|
||||
- [web/themes/default/style.css](web/themes/default/style.css) — Dark/light theme via CSS variables
|
||||
- [web/translations/](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](web/DOC/THEME-CSS-HOOKS.md) and [web/DOC/THEME-JS-ID-HOOKS.md](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/key` bundled via PyInstaller)
|
||||
- **Moonraker emulation:** Kobra states are mapped to Klipper states via `KOBRA_TO_KLIPPER_STATE` in `kobrax_moonraker_bridge.py`
|
||||
- **Real-time logs:** Streamed to the browser via Server-Sent Events (SSE), not WebSocket
|
||||
- **Multi-printer:** Each printer runs its own `KobraXBridge` instance on a separate port
|
||||
|
||||
### PyInstaller Build
|
||||
|
||||
[kx-bridge.spec](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](https://github.com/caronc/apprise) library (supports 60+ services via URL syntax: `discord://`, `telegram://`, `pover://`, `gotify://`, `slack://`, etc.).
|
||||
|
||||
**Config format** (`[notifications]` section):
|
||||
```ini
|
||||
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 `.jpg` from `camera_cache.latest_jpeg` via `apprise.AppriseAttachment`.
|
||||
- `KobraXBridge._check_progress_notifications()` — called from `_poll_loop()` every poll tick; fires `progress` event 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_state` guards 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 via `run_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/](android/). It connects to a running KX-Bridge server and provides a mobile printer control panel.
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
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.
|
||||
Reference in New Issue
Block a user