FastAPI service that backs the FilaMind Flow panel. It exposes health and diagnostics, the firmware build / flash API, and the input-shaping resonance-analysis API. It's also where privileged and aggregated operations land as new features arrive. The browser reads live printer data straight from Moonraker. This service handles the work that belongs on the server.
- Python 3.10+
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
pip install -r requirements-dev.txt
cp .env.example .env # adjust FILAMIND_MOONRAKER_URL if needed
python -m app # serves http://localhost:8000Interactive API docs: http://localhost:8000/docs
| Method | Path | Purpose |
|---|---|---|
| GET | /api/health |
Backend liveness probe. |
| GET | /api/moonraker/status |
Server-side Moonraker reachability check. |
| (group) | /api/firmware/* |
Firmware build / flash / devices / config-profiles / identify / backup (Firmware Manager widget). |
| POST | /api/shaper/analyze |
Analyze an uploaded resonance CSV → recommended shaper + curves. |
| GET | /api/shaper/files |
List the resonance CSVs Klipper wrote on the host. |
| POST | /api/shaper/analyze-file |
Analyze a resonance CSV already on the host (by path). |
| POST | /api/shaper/live-test |
Run TEST_RESONANCES on the printer and analyze the capture. |
| POST | /api/shaper/noise |
Run MEASURE_AXES_NOISE (motion-free) to check the sensor mount. |
| POST | /api/shaper/compare-belts |
Resonance-test each CoreXY belt diagonal and return both (moves the toolhead). |
| POST | /api/shaper/axes-map |
Jog +X/+Y/+Z to detect the accelerometer axes_map orientation (moves the toolhead). |
| POST | /api/shaper/excitate |
Hold a frequency (sustain) to find what rattles → spectrogram + energy timeline (moves the toolhead in place). |
| POST | /api/shaper/vibrations-profile |
Sweep speed × motor-angle → smoothest/worst speeds, motor symmetry, motor resonance (moves the toolhead for minutes). |
| GET | /api/shaper/archive |
List the saved runs (captures + generated configs) in the on-host archive. |
| GET | /api/shaper/archive/{id} |
One archived run + its inline config text. |
| GET | /api/shaper/archive/{id}/file/{name} |
Download a CSV / config stored in a run. |
| DELETE | /api/shaper/archive/{id} |
Delete an archived run (folder + index entry). |
| POST | /api/shaper/archive/save-config |
Save a generated [input_shaper] config to the archive. |
| POST | /api/shaper/archive/save-file |
Copy an existing host resonance CSV into the archive. |
| GET | /api/drivers/status |
Live TMC stepper-driver inventory: current / mode / microsteps / StallGuard / temperature / health + each axis's homing method (physical / sensorless / probe / …, from endstop_pin) + the effective run-current cap (current_cap = min(model code cap, motor rating)), each annotated with its model's catalog facts (Motor Drivers widget). |
| GET | /api/drivers/endstops |
Live endstop trigger state (open / TRIGGERED), actively queried on demand - for the physical-homing panel. |
| GET | /api/drivers/live/{stepper} |
Fast live telemetry for one driver (temperature / SG_RESULT / CS_ACTUAL / faults) for the live monitor. |
| GET | /api/drivers/catalog |
The curated TMC driver capability map (interface, current cap, chopper modes, StallGuard field, sensorless / temperature) keyed by model. |
| GET | /api/drivers/motors |
The stepper-motor catalog (datasheet parameters) for the motor picker - served from the unified hardware database. |
| GET | /api/drivers/mapping |
The saved stepper → motor assignments. |
| POST | /api/drivers/mapping |
Assign a catalogued motor to a stepper (empty motor_model clears it). |
| POST | /api/drivers/recommend |
Recommend a run current + StealthChop/SpreadCycle registers for a motor (compute-only; faithful motor_constants port). |
| POST | /api/drivers/config-block |
Render a printer.cfg override block to copy (no write). |
| POST | /api/drivers/apply |
Write tuning live via SET_TMC_CURRENT / SET_TMC_FIELD (refused while printing; validated). |
| POST | /api/drivers/init |
INIT_TMC - re-apply the stepper's configured registers (revert a live apply). |
| POST | /api/drivers/autotune |
Native full auto-tune - compute + apply the whole TMC register set (current + StealthChop + SpreadCycle + CoolStep + velocity thresholds) from the assigned motor's datasheet via SET_TMC_* (gated; no host extra). |
| POST | /api/drivers/stallguard |
Set a StallGuard threshold (sgthrs / sgt / sg4_thrs) - sensorless-homing sensitivity (gated + server-clamped). |
| GET | /api/drivers/field-policy/{model} |
The editable-register policy for a model - which fields the editor may expose, with control type + clamp range. |
| POST | /api/drivers/field |
Write one editable TMC register field live via SET_TMC_FIELD (gated; server-side allowlist + per-field clamp; velocity thresholds sent as mm/s). |
| POST | /api/drivers/coolstep |
Enable CoolStep with a vetted register set (semin/semax/seup/sedn/seimin) or disable it - a single gated toggle. |
| POST | /api/drivers/home |
Home one axis (G28 <axis>) as a sensorless test - moves the toolhead (gated). |
| GET | /api/drivers/motors-sync |
Whether the motors_sync add-on is installed. |
| POST | /api/drivers/motors-sync |
Run SYNC_MOTORS / SYNC_MOTORS_CALIBRATE (multi-motor phase alignment) - moves the toolhead (gated). |
| GET | /api/reference/stallguard (+ /{driver}) |
Per-driver StallGuard slip-detection tuning constants (base + overrides; {driver} = merged effective set + SG field). Static reference data. |
| GET | /api/reference/hotends |
Hotend melt-zone / expected max-flow / test presets. |
| GET | /api/reference/boards |
Board + MCU identification patterns. |
| GET | /api/reference/macros |
Built-in Klipper calibration macro definitions. |
| GET | /api/config/files |
List the editable config files (.cfg / .conf) under Moonraker's config root (Config Editor). |
| GET | /api/config/file |
Parse one config file (?filename=) into a structured view: sections → params + validation issues (read-only). |
| GET | /api/config/drift |
Compare one file on disk to the live running config (configfile): a pending-SAVE_CONFIG flag, Klipper's own parse warnings, and per-param drift (disk vs live). reachable=false when down. |
| POST | /api/config/adopt |
Set one param to a given value via the round-trip engine and return the new text - a pure, surgical transform (no write); only the target line is rewritten. |
| GET | /api/config/pin-doctor |
Whole-config pin-conflict scan (every MCU): double-assigned pins + board electronics caveats on a used pin, aggregated from each MCU's pin atlas. reachable=false when down. |
| POST | /api/config/save |
Back up then overwrite one config file (refused while printing; auto-backup to filamind-backups/). |
| POST | /api/config/restart |
FIRMWARE_RESTART to apply a saved config (refused while printing). |
| POST | /api/maxflow/plan |
Preview the max-flow test ramp (flow → feedrate per step + StallGuard field); pure compute, no actuation. |
| POST | /api/maxflow/run |
Run the live max-flow test ((optional auto-StealthChop config write) → home + center → heat → StallGuard sanity pre-check → ramp + sample); preflight (chopper-mode/StallGuard) + SG4 bias-floor + ≥180 °C; aborts before the ramp if the live SG signal is unusable; refused while printing; heater always cut; any temporary stealthchop_threshold reverted (commented out) afterward. method = auto / stallguard / accel - auto falls back to the toolhead accelerometer (vibration RMS per step) when StallGuard is unusable and an accel chip is configured. The result carries method. |
| POST | /api/maxflow/run/start |
Same run as a supervised background task (returns a task_id to poll for per-step progress + result; cancel always cuts the heater). |
| GET | /api/maxflow/extruder-driver |
The extruder's TMC model detected from the live config (preselects the driver). |
| GET | /api/camera/list |
Configured webcams (name + service) via Moonraker; empty when none - the UI then hides the live view. |
| GET | /api/camera/snapshot |
Proxy one JPEG snapshot of the named (or default) webcam, same-origin and uncached - lets the panel show the nozzle during a run regardless of how it's served. |
| GET | /api/topology |
Host → MCU topology from the live config: the host SBC (identified via /machine/system_info, linked to a catalog host; flagged integrated_into_board_id when its SoC matches the primary mainboard's declared onboard SBC) + each MCU's connection (CAN/USB/UART) + chip (linked to a catalog MCU) + a suggested board (serial-signature match, strengthened by a used-pin-set fingerprint vs each board's pin-map) + the components (steppers/drivers/heaters/fans/sensors) on it, attached by each pin's chip prefix. A saved per-MCU override (below) is re-applied as a confirmed board. |
| POST | /api/topology/override |
Confirm / override the catalog board for one MCU (keyed by its config section name). Validates the board_id against the catalog; persisted on the host and applied to every read. |
| POST | /api/topology/override/clear |
Remove an MCU's board override (revert to the auto suggestion). |
| GET | /api/topology/pin-atlas/{mcu_name} |
The used-vs-free pin map of an MCU's resolved board (honouring overrides) + a wiring-health scan: pins driven by >1 config section (double_assign) and board electronics caveats bound to a used pin (caveat). available=false when the board has no pin-map. |
| POST | /api/topology/snapshot |
Save the current topology as the hardware baseline (per-MCU board / chip / connection / component-count, keyed by section name) for later change detection. |
| GET | /api/topology/snapshot/diff |
Compare the live topology to the saved baseline → structured changes (MCU added/removed, board/connection/chip/component-count changed). has_baseline=false until a snapshot is saved. |
| POST | /api/macro/simulate |
Offline G-code simulator: macro { params.X } substitution → path / bounds / totals / time / timeline; pure, no printer. |
| GET | /api/macro/live |
The printer's own installed [gcode_macro] definitions from the live configfile (body + description + discovered { params.X } + variable_*), to load into the editor and simulate. reachable=false when Moonraker is down. |
| GET | /api/macro/limits |
The printer's real build envelope + speed cap (axis_minimum/maximum, max_velocity) from the live toolhead object, to ground the simulator's out-of-bounds / over-speed checks. reachable=false when down. |
| GET | /api/hardware |
Flat free-text search over the raw component rows (?q=/category/manufacturer, paginated). The canonical, deduped, config-carrying entities have their own typed endpoints below. |
| GET | /api/hardware/categories |
The hardware categories + per-category canonical counts (raw kept as rawCounts) + total component count. |
| GET | /api/hardware/facets |
Distinct values for the filter dropdowns - boardClass (boards), nema size (motors), kind (hosts), and catalogSubsections (per mixed catalog category → its sub-types). |
| GET | /api/hardware/manufacturers |
Canonical manufacturer entities (?q) - each with a stable manufacturer_id, auto-derived aliases and a memberCount, sorted most-connected first. |
| GET | /api/hardware/manufacturers/{manufacturer_id} |
One manufacturer's record (?expand=related adds its linked hardware, grouped by type). |
| GET | /api/hardware/mcus |
Canonical MCU entities (?q/family) parsed from board specs.MCU (normalised to a canonical part) - each with family, arch, boardCount. |
| GET | /api/hardware/mcus/{mcu_id} |
One MCU's record (?expand=related adds the boards that use it). |
| GET | /api/hardware/boards |
Canonical control-board entities (summaries; ?q/manufacturer/board_class, paginated) - each board's connectors aggregated into one ports[]. |
| GET | /api/hardware/boards/{board_id} |
One board's full record (identity + specs + aggregated ports[] + detection matchPatterns; ?expand=related adds links). |
| GET | /api/hardware/drivers |
Canonical stepper-driver entities (summaries; ?q/manufacturer/klipper_only, paginated) - one per chip, deduped. |
| GET | /api/hardware/drivers/{driver_id} |
One driver's full record (specs + Klipper support + copyable [tmcXXXX] config snippet; ?expand=related). |
| GET | /api/hardware/motors |
Canonical stepper-motor entities (summaries; ?q/manufacturer/nema, paginated) - one per model, deduped. |
| GET | /api/hardware/motors/{motor_id} |
One motor's full record (specs + recommended run_current + current presets + copyable config snippet; ?expand=related). |
| GET | /api/hardware/hosts |
Canonical host-computer entities (summaries; ?q/manufacturer/kind, paginated) - SBC / x86 / OS-image, deduped. |
| GET | /api/hardware/hosts/{host_id} |
One host's full record (specs + Klipper-open flag + copyable [mcu host] config snippet; ?expand=related). |
| GET | /api/hardware/catalog |
Canonical entities for one remaining category (?category=…, summaries; ?q/manufacturer/subsection, paginated) - sensors / hotends / extruders / fans / displays / motion / nozzles / filament / electronics, deduped. subsection filters a mixed category by sub-type. |
| GET | /api/hardware/catalog/{catalog_id} |
One catalog entity's full record (specs + copyable Klipper config snippet; ?expand=related). |
| GET | /api/hardware/{type}/{id}/related |
Cross-entity relationships for any node (boards/drivers/motors/hosts/catalog/manufacturers/mcus), grouped by relation (O(1) graph walk). |
| (group) | /api/screen/* |
Control the printer's KlipperScreen touchscreen (KlipperScreen Studio widget): read/save KlipperScreen.conf (conf, behind the gated config save - backup + busy-refusal + stale-write guard) + restart; the [main] options as a key→value map (options); the menu tree (menus) - the [menu …] button grids read as a flat list and rewritten whole (preserving non-menu sections + the auto-generated block) plus the targetable panels; and on-host themes (themes: list / preview / create / activate / delete). status reports presence + restartability. Also the FilaMind Kiosk (kiosk): report the current screen mode and reversibly swap the touchscreen between KlipperScreen and a fullscreen FilaMind browser (kiosk/switch, kiosk/restore - temporary by default, optional persist to flip the boot default) via sudo systemctl on a filamind-kiosk unit installed once by scripts/install.sh kiosk. |
The interactive /docs page is the authoritative, always-current list. The
firmware API in particular has many more routes than the summary above shows.
Curated Klipper reference datasets live under app/data/reference/ and are
served read-only by app/services/reference_data.py. These cover StallGuard
tuning, hotends, board/MCU patterns, and macros.
The large curated hardware catalog ships in the same place, but as a
compiled, read-only hardware.sqlite holding the boards, drivers, motors,
hosts, catalog, and manufacturers. The editable source is deliberately kept
outside the repo. After you edit it, regenerate the database with
python scripts/build_hardware_db.py and commit the rebuilt hardware.sqlite.
At startup reference_data reconstructs the same in-memory structure, so the
indexes, search haystacks, link graph, and ETag are all unaffected by the
storage format.
The driver write endpoints return an ApplyResponse carrying an i18n
{ code, params, message } contract. The code (plus params) is a stable key
the UI translates under motorDrivers.apply.*, and message is the English
fallback. Passthrough errors are the exception: Moonraker failures and
field_policy or value-validation text come back with code: null, and their
raw English text is shown verbatim.
POST /api/firmware/flash streams a plain-text log of the real flash sequence.
A handful of details exist specifically to make it behave the same across very
different boards. It's been validated on a USB/UART SV08 and a CAN/SPI
Voron-class machine.
- Phase markers. The stream carries machine-readable
::phase::<code>lines (start,stop,boot,write,restart,done) interleaved with the human>>> …log. That lets the UI render a progress bar instead of a raw console. The frontend strips the markers and keeps the full log for a collapsible details view. - CAN UUID resolution. A CAN flash addresses the node by its 12-hex
canbus_uuid, but a device may be registered under a friendly name.flash_service.resolve_can_uuid()resolves the real UUID from the liveconfigfile.config- either by[mcu <name>]match, or as the sole CAN node when that's unambiguous. If it can't resolve cleanly it refuses with a clear message rather than flashing the wrong node. - Honest outcomes, plus a CAN salvage.
_stream()captures the flash tool's exit code. A non-zero exit reports a real failure and skips recording a flashed-version. CAN is the tricky case: some USB-CAN adapters error in their read-back verify even though the write itself completed. When that happens the sequence confirms the true result by polling the node's reported firmware version through Klipper. - sudo capability probe. The privileged steps - stop/start Klipper and the
flash itself - need passwordless sudo. Readiness is checked with
sudo -n -l systemctl stop klipper, an authorization lookup that doesn't actually run the command, so the check is correct no matter which/etc/sudoers.d/file grants the permission.
ruff check . # lint
ruff format . # format
mypy app # type-check
pytest # testsAll settings use the FILAMIND_ env prefix (see .env.example):
| Variable | Default | Description |
|---|---|---|
FILAMIND_HOST |
0.0.0.0 |
Bind address. |
FILAMIND_PORT |
8000 |
Bind port. |
FILAMIND_LOG_LEVEL |
info |
Log level. |
FILAMIND_MOONRAKER_URL |
http://localhost:7125 |
Moonraker base URL. |
FILAMIND_KLIPPER_DIR |
~/klipper |
Klipper source tree (firmware build). |
FILAMIND_KATAPULT_DIR |
~/katapult |
Katapult source tree (firmware flash). |
FILAMIND_DATA_DIR |
~/printer_data/config/filamind |
Backend data (firmware profiles, device registry). |
FILAMIND_RESONANCE_DIRS |
/tmp,~/printer_data/config,~/printer_data/config/input_shaper |
Dirs scanned for resonance CSVs (comma-separated). |
FILAMIND_CORS_ORIGINS |
http://localhost:5173 |
Comma-separated allowed origins. |