Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Pycentauri Server

БесплатноНе проверен

An MCP server that enables AI agents to monitor and control an Elegoo Centauri Carbon 3D printer via local network, supporting status checks, snapshots, and pri

GitHubEmbed

Описание

An MCP server that enables AI agents to monitor and control an Elegoo Centauri Carbon 3D printer via local network, supporting status checks, snapshots, and print control.

README

Local-network toolkit for Elegoo Centauri Carbon 3D printers — the original Centauri Carbon (CC1) and the Centauri Carbon 2 (CC2). One async client, six surfaces: Python library, CLI, MCP server for AI agents, REST/SSE HTTP server, built-in web dashboard, and an RTSP bridge for your NVR.

No cloud account, no Elegoo servers — everything talks directly to the printer on your LAN. pycentauri auto-detects which model it's talking to and speaks the right protocol:

CC1 CC2
Transport SDCP v3 over WebSocket (:3030) JSON-RPC over MQTT (:1883)
Auth none access code (printer screen)
Discovery UDP broadcast direct IP + HTTP bootstrap
Webcam MJPEG :3031 MJPEG :8080
Live head speed ✓ (gcode_move.speed ÷ 60 = the screen's mm/s readout)
Fan channels 3 5
Canvas multi-filament ✓ (status + auto-refill)
File management list, upload, delete, history list, upload, delete, disk info, history
Filament-switch detection ✓ (position-based)

Status: alpha, but used daily against real printers. Protocols were reverse-engineered from Elegoo's official elegoo-link C++ SDK, the CentauriLink project, and live wire captures. CC1 tested on firmware V1.1.46 and OpenCentauri V0.3.0-o; CC2 tested on firmware 01.03.02.51. Full wire-protocol notes live in docs/PROTOCOL.md.

Install

pip install pycentauri                    # library + CLI
pip install "pycentauri[mcp]"             # + MCP server
pip install "pycentauri[server]"          # + HTTP REST/SSE server + web UI
pip install "pycentauri[mcp,server]"      # all Python surfaces

The RTSP bridge additionally requires MediaMTX and ffmpeg on $PATH.

Python 3.10+. Core dependencies: websockets, paho-mqtt, httpx, typer, pydantic.

Connecting to your printer

CC1 needs only its IP (or nothing at all — it answers UDP discovery).

CC2 needs its IP and its access code, found on the printer's touchscreen under network/connectivity settings. Pass it as --access-code / access_code= / PYCENTAURI_ACCESS_CODE. The examples below use Ab3dEf as a stand-in — substitute your own.

Enable "LAN Only" mode on the CC2 (network settings on the touchscreen). The CC2 gates its local API behind it — with LAN Only off the printer works through Elegoo's cloud and leaves the local HTTP endpoint closed, so pycentauri can't reach it and you'll get a connection error. This is required on firmware 2.0 and recommended on all CC2 firmware.

Every CLI command accepts --host (env: PYCENTAURI_HOST). With no host given, commands try UDP discovery, which only finds CC1s.

CLI

# Discovery (CC1 only — CC2 doesn't answer broadcasts)
centauri discover

# Status, attributes, live watch, snapshot
centauri status     --host 192.168.1.209                        # CC1
centauri status     --host 192.168.1.189 --access-code Ab3dEf   # CC2
centauri status     --host 192.168.1.209 --json
centauri attributes --host 192.168.1.209
centauri watch      --host 192.168.1.209
centauri snapshot   --host 192.168.1.209 shot.jpg

# Upload a file, then print it — all writes require --enable-control
centauri upload model.gcode         --host 192.168.1.209 --enable-control
centauri upload model.gcode --start --host 192.168.1.209 --enable-control

# Print control
centauri print start model.gcode --host 192.168.1.209 --enable-control
centauri print pause             --host 192.168.1.209 --enable-control
centauri print resume            --host 192.168.1.209 --enable-control
centauri print stop              --host 192.168.1.209 --enable-control

# Live adjust while printing
centauri speed sport                            --host 192.168.1.209 --enable-control
centauri fan  --model 100 --aux 60 --chamber 30 --host 192.168.1.209 --enable-control
centauri temp --nozzle 215 --bed 60             --host 192.168.1.209 --enable-control

# File management (both models; `disk` is CC2-only)
centauri files        --host 192.168.1.209                             # CC1
centauri files --storage u-disk --host 192.168.1.189 --access-code Ab3dEf
centauri disk         --host 192.168.1.189 --access-code Ab3dEf        # CC2 only
centauri history      --host 192.168.1.209                             # CC1 or CC2
centauri delete old.gcode --host 192.168.1.209 --enable-control

# Chamber light (both models)
centauri light on   --host 192.168.1.189 --access-code Ab3dEf --enable-control
centauri light off  --host 192.168.1.189 --access-code Ab3dEf --enable-control

# Canvas multi-filament (CC2 only)
centauri canvas       --host 192.168.1.189 --access-code Ab3dEf
centauri refill --on  --host 192.168.1.189 --access-code Ab3dEf --enable-control

centauri canvas prints each tray's filament, color, temperature range, and loaded state:

auto_refill  : OFF
active_tray  : none
connected    : yes

canvas #0:
  ● tray 0: PLA Wood (PLA) #F72221 [190-230°C]
  ● tray 1: PLA Wood (PLA) #AF7832 [190-230°C]
  ● tray 2: PETG (PETG) #A03BF7 [230-260°C]
  ● tray 3: PLA Wood (PLA) #D2C5A3 [190-230°C]

Speed modes

Both printers accept exactly four speed settings — arbitrary percentages are silently ignored by the firmware:

Mode CC1 wire value (PrintSpeedPct) CC2 wire value (speed_mode)
silent 50 0
balanced 100 1
sport 130 2
ludicrous 160 3

Speed changes only take effect while a print is actively running.

CC2 speed pinning

Historically the CC2 firmware reset the speed mode back to balanced on every Canvas filament switch, losing your choice. pycentauri works around it by pinning the mode you set and re-applying it when a filament switch completes (CC2 only, requires --enable-control):

  • The mode you set via pycentauri is pinned.
  • When a Canvas filament switch finishes, the pinned mode is re-applied once (harmless if the firmware didn't reset it).
  • The pin clears when the print ends.

A note on firmware: on 02.01.00.00 the printer no longer exposes a stable "set speed mode" over the wire — gcode_move.speed_mode in the real-time stream is the current move's speed factor, which varies per feature. pycentauri therefore treats the pin as an explicit setting and never infers or enforces it from that noisy value; it only re-applies your pinned mode on switch completion. To change speed, set it through pycentauri (the dashboard, centauri speed, etc.).

The CC1 has none of this — its speed mode stays where you put it, so set_print_speed is a plain one-shot there.

Python library

import asyncio
from pycentauri import Printer, CC2Printer, connect_auto

async def main():
    # Explicit CC1
    async with await Printer.connect("192.168.1.209") as printer:
        st = await printer.status()
        print(st.print_status, st.progress, st.temp_nozzle)

    # Explicit CC2
    async with await CC2Printer.connect("192.168.1.189", access_code="Ab3dEf") as printer:
        st = await printer.status()
        print(st.temp_nozzle, st.raw["_cc2"]["gcode_move_speed"])  # mm/min; ÷60 = screen's mm/s

        canvas = await printer.canvas_status()
        for unit in canvas.canvas_list:
            for tray in unit.tray_list:
                print(tray.tray_id, tray.filament_name, tray.filament_color)

    # Auto-detect — port-probes :3030 vs :1883 and returns the right class
    async with await connect_auto("192.168.1.189", access_code="Ab3dEf") as printer:
        attrs = await printer.attributes()
        print(attrs.machine_name, attrs.firmware_version)

asyncio.run(main())

Both classes expose the same API: status(), attributes(), watch() (async iterator of live status), snapshot(), upload_file(), start_print(), pause(), resume(), stop(), set_print_speed(), set_fan_speed(), set_temperatures(), set_light(), list_files(), delete_files(), disk_info(), print_history(), canvas_status(), set_auto_refill(). Write methods require enable_control=True at connect time and raise ControlDisabledError otherwise. File management and Canvas methods raise PrinterError on CC1 (not available over SDCP).

upload_file(local_path, *, remote_name=None, progress=None) pushes a file to the printer's internal storage over chunked HTTP (independent of the control channel, so it can't disrupt a print) and returns the name start_print() expects — so the typical flow is await p.upload_file("model.gcode") then await p.start_print(...). The optional progress callback receives (bytes_sent, total_bytes).

CC2-only telemetry rides along in Status.raw["_cc2"]: the live head speed (gcode_move_speed — the commanded speed of the current move in mm/min; divide by 60 for the mm/s figure the printer's screen shows), speed_mode, filament runout sensor state, firmware-computed remaining_time_sec, machine_status/sub_status raw codes, and external_device (camera / U-disk presence).

HTTP server + web UI

# Read-only, loopback only
centauri server --host 192.168.1.209

# Read + write + RTSP, on the LAN (put an authenticating proxy in front)
centauri server --host 192.168.1.209 --bind 0.0.0.0 --port 8787 \
                --enable-control --rtsp

# CC2
centauri server --host 192.168.1.189 --access-code Ab3dEf \
                --bind 0.0.0.0 --port 8787 --enable-control

The server holds a single long-lived connection to the printer (WebSocket for CC1, MQTT for CC2) with automatic reconnect and exponential backoff — it will never exhaust CC1's 5-connection limit.

The web UI at /ui/ is a clean dark dashboard, mobile-friendly and dependency-free (no CDN assets — works on an air-gapped LAN): live webcam, job progress with layer/ETA, printer state, thermals, kinematics (with live head speed on CC2), pause/resume/stop, speed-mode selector, fan and heater sliders that hydrate from live values, a Canvas panel with per-tray color swatches and an auto-refill toggle (CC2), and RTSP bridge controls. Control panels only render when the server was started with --enable-control.

Endpoints

Method Path Notes
GET / Redirects to /ui/
GET /ui/ Web dashboard
GET /api/info Health + version + connection state
GET /status Latest status (typed summary + full raw payload)
GET /attributes Model, firmware, mainboard ID
GET /snapshot Single JPEG frame
GET /stream MJPEG proxy (drop into an <img> tag)
GET /events/status Server-Sent Events stream of status pushes
GET /discover UDP LAN scan (finds CC1s)
GET /canvas Canvas state (CC2; 501 on CC1)
GET /docs, /redoc OpenAPI documentation
POST /print/start {"filename": "cube.gcode", "storage": "local"}
POST /print/pause · /print/resume · /print/stop
POST /print/speed {"mode": "sport"} or {"mode": 130}
POST /print/fan {"model": 50, "auxiliary": 30, "chamber": 0} — any subset, 0–100 †
POST /print/temperature {"nozzle": 215, "bed": 60} — any subset, °C, 0 = off †
POST /files/upload multipart [email protected] (+ optional start=true) †
POST /files/delete {"filenames": ["a.gcode"], "storage": "local"} — refuses the printing file †
GET /files ?storage=local&limit=100 — file list
GET /disk Disk usage: total_bytes, used_bytes (CC2; 501 on CC1)
GET /history Print job history
POST /light {"on": true} — chamber light †
POST /canvas/refill {"enabled": true} (CC2) †
GET /api/rtsp RTSP bridge state (when --rtsp)
POST /api/rtsp/start · /api/rtsp/stop Toggle the bridge (when --rtsp)

† requires the server to be launched with --enable-control; otherwise the route isn't registered at all.

Temperature writes are bounds-checked server-side: nozzle 0–300 °C, bed 0–110 °C, chamber 0–60 °C.

MCP server (AI agents)

Give Claude Code, Claude Desktop, Cursor, or any MCP client eyes and hands on your printer:

# Read-only (status, snapshot, attributes, discovery, canvas)
claude mcp add pycentauri --env PYCENTAURI_HOST=192.168.1.209 \
    -- python -m pycentauri.mcp

# With control tools
claude mcp add pycentauri-cc2 \
    --env PYCENTAURI_HOST=192.168.1.189 \
    --env PYCENTAURI_ACCESS_CODE=Ab3dEf \
    -- python -m pycentauri.mcp --enable-control

The target host is pinned in the server's environment at spawn time — a prompt-injected agent cannot redirect commands to an arbitrary IP, because no tool takes a host parameter.

Tool Availability Description
get_status always State, temps, progress, layer, position, fans
get_attributes always Model, firmware, mainboard ID
get_snapshot always Webcam frame as MCP image — the model sees the print
discover_printers always UDP LAN scan
get_canvas_status always Canvas trays, colors, auto-refill (CC2)
start_print --enable-control Start a file already on the printer
pause_print / resume_print / stop_print --enable-control Job control
set_print_speed --enable-control silent/balanced/sport/ludicrous
set_fan_speed --enable-control Any subset of model/aux/chamber, 0–100%
set_temperatures --enable-control Any subset of nozzle/bed/chamber, °C
set_auto_refill --enable-control Canvas auto-refill toggle (CC2)

Control tools aren't merely gated — without the flag they are never registered, so they don't appear in the model's tool list at all.

RTSP bridge

Re-streams the printer's MJPEG webcam as H.264/RTSP for clients that don't speak MJPEG — Home Assistant, Frigate, Jellyfin, Synology Surveillance, VLC:

# Standalone (foreground, Ctrl-C to stop)
centauri rtsp --host 192.168.1.209
# → rtsp://<this-host>:8554/printer

# Integrated with the HTTP server — adds a STREAM panel to the web UI
centauri server --host 192.168.1.209 --rtsp --bind 0.0.0.0

MediaMTX only runs the ffmpeg transcode while a client is connected, so idle cost is zero. Tunables: --fps, --bitrate, --preset, --path, --port (standalone) or the --rtsp-* variants on centauri server. The bridge picks the correct camera port for CC1 vs CC2 automatically.

Print status codes

print_status in the API and library uses the CC1 firmware's code space, extended with three codes for CC2 Canvas operations:

Code Meaning Code Meaning
0 Idle 13 Printing
1 Homing 14 Error
5 Pausing 15 Leveling
6 Paused 16 Preheating
7 Stopping 27 Switching filament (CC2)
8 Stopped 28 Filament load complete (CC2)
9 Completed 29 Unloading filament (CC2)

The full table (including CC1's resin-inherited codes) is in docs/PROTOCOL.md.

On the CC2, mid-print Canvas filament switches are detected by head position: the firmware never fully leaves its "printing" state during a switch, but the head parks at the purge chute behind the bed (y ≥ 258 mm, physically outside the printable area) for the duration. pycentauri reports code 27 the entire time the head is parked there mid-print.

Safety model

  • Explicit opt-in for writes. Every surface requires enable_control=True / --enable-control before any state-changing command is possible. Read-only is the default everywhere.
  • Bounds-checked heaters. The library refuses temperature targets outside nozzle 0–300 °C / bed 0–110 °C / chamber 0–60 °C even though the firmware might accept them.
  • Unauthenticated HTTP surface. The REST server has no auth of its own. Bind it to loopback (the default) or put an authenticating reverse proxy in front before exposing it beyond localhost.
  • You own unattended printing. An agent with control tools can pause, stop, or heat your printer. Leaving one unattended is your call.

Known firmware quirks

CC1 (original Centauri Carbon)

  • 5 concurrent WebSocket slots, hard. The 6th connection gets HTTP 500 "too many client". Slots free on close. The CLI opens one per invocation; the HTTP/MCP servers hold exactly one long-lived slot.
  • Paused/errored states don't push Attributes. Every SDCP command needs the printer's MainboardID, which normally arrives in an Attributes push — but not while paused or errored. pycentauri pre-seeds it from UDP discovery on every connect. If you call Printer.connect() on a paused printer without discovery, pass mainboard_id= yourself.
  • Unknown commands crash the firmware. A few unrecognised SDCP commands in quick succession kill the printer's app daemon — and any active print with it. Don't probe undocumented command codes against a printer that's doing something you care about.
  • The push scheduler goes dormant at idle (and a reboot doesn't wake it). The firmware can enter a state where Cmd 512 subscribes are acknowledged but no status frame is ever pushed while the printer sits idle — persisting across reboots (verified 2026-07-05 on V0.3.0-o). Starting a print revives pushes at full rate. One-shot Cmd 0 requests always work, so pycentauri automatically falls back to polling when a subscribe goes quiet (~7 s updates at idle, full-rate pushes while printing). Clients that rely purely on subscribe pushes will hang forever on an idle printer in this state.

CC2 (Centauri Carbon 2)

  • No UDP discovery. The CC2 ignores broadcast probes; specify its IP explicitly. Give it a static DHCP lease — it doesn't register a hostname with most routers, so its address drifts otherwise.
  • Access code required for MQTT, passed as the password with username elegoo. The HTTP bootstrap (/system/info) wants the same code as an X-Token query parameter — it ignores the header form.
  • The webcam is unauthenticated. MJPEG on :8080 (any path) is open to anyone on your LAN, access code or not. That's the firmware's choice, not ours.
  • Rate limiting. Rapid-fire MQTT requests (3+ back-to-back) trip a cooldown of a few seconds during which the broker silently drops responses. pycentauri's polling cadence stays under it; your scripts should too.
  • The firmware resets the speed mode to balanced on every Canvas filament switch. pycentauri pins your chosen mode and re-applies it automatically, while still honoring a deliberate balanced from the touchscreen — see CC2 speed pinning above for how it tells the two apart.
  • Registrations expire without an app-level PING. The printer forgets a registered client after several quiet minutes and silently stops answering that session's requests — the MQTT connection itself stays up, so there's no error to catch. pycentauri sends the SDK's {"type": "PING"} keepalive every 30 s to hold the registration; if you write your own client, you must too.
  • File list (method 1044) and video stream (1042) don't respond on firmware 01.03.02.51, so remote print-start on CC2 requires knowing the filename in advance.

Project layout & docs

src/pycentauri/
├── client.py      # CC1: async SDCP-over-WebSocket client
├── cc2.py         # CC2: async JSON-RPC-over-MQTT client (same API)
├── connect.py     # connect_auto() — port-probe model detection
├── sdcp.py        # SDCP v3 envelope build/parse
├── discovery.py   # UDP broadcast discovery
├── camera.py      # MJPEG frame grabber
├── models.py      # Status / Attributes / CanvasStatus / PrintInfo
├── cli.py         # Typer CLI
├── server.py      # FastAPI app + connection supervisor
├── rtsp.py        # MediaMTX/ffmpeg bridge
├── mcp/           # FastMCP stdio server
└── web/           # Static dashboard (no build step, no CDN)
  • docs/PROTOCOL.md — both wire protocols in detail: envelopes, command/method tables with tested-on dates, status payloads, error codes, failure modes, and a CC1-vs-CC2 comparison.
  • docs/ARCHITECTURE.md — module map and request flow.

Development

git clone https://github.com/bjan/pycentauri && cd pycentauri
python -m venv .venv && .venv/bin/pip install -e ".[mcp,server,dev]"

.venv/bin/ruff check . && .venv/bin/ruff format --check .
.venv/bin/mypy src          # strict mode
.venv/bin/pytest -q         # no printer required — tests use in-process fakes

Tests run against an in-process fake SDCP WebSocket server plus pure translation-layer tests for CC2; nothing in CI touches real hardware. Live verification against a physical printer is manual — centauri status, centauri canvas, and a fan write are the standard smoke test after protocol-layer changes.

Credits & license

  • Protocol references: Elegoo's elegoo-link SDK (Apache-2.0) and CentauriLink.
  • Licensed under Apache-2.0 — see LICENSE.
  • Not affiliated with or endorsed by Elegoo. Reverse-engineered protocols can break with any firmware update; nothing here is warranted to keep your prints alive.

from github.com/bjan/pycentauri

Установить Pycentauri Server в Claude Desktop, Claude Code, Cursor

Рекомендуется · одна команда, все IDE
unyly install pycentauri-mcp-server

Ставит в Claude Desktop, Claude Code, Cursor и VS Code — сам разбирается с npx, uvx и сборкой из исходников.

Впервые? Поставь CLI: curl -fsSL https://unyly.org/install | sh

Или настроить вручную

Выполни в терминале:

claude mcp add pycentauri-mcp-server -- uvx pycentauri

FAQ

Pycentauri Server MCP бесплатный?

Да, Pycentauri Server MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Pycentauri Server?

Нет, Pycentauri Server работает без API-ключей и переменных окружения.

Pycentauri Server — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Pycentauri Server в Claude Desktop, Claude Code или Cursor?

Открой Pycentauri Server на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Pycentauri Server with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории ai