Files
OrcaSlicer/src/slic3r/Utils/Moonraker.hpp
T
packerlschupfer a529f8c473 Print Host: add Moonraker (Klipper) host type (#13991)
OrcaSlicer currently ships an "Octo/Klipper" host type that maps to the
OctoPrint REST endpoints (api/version, api/files/local). It works for
Klipper setups that run Moonraker with the OctoPrint-emulation plugin,
but native Moonraker — and Moonraker-compatible firmwares like the
Prusa-Firmware-Buddy buddy-klipper fork — speak a different shape:
distinct paths, JSON body for /printer/print/start, {"result":...}
envelope. There's no host type for that today.

Add a new Moonraker class deriving from PrintHost. Endpoints used,
matching the Moonraker spec:

- GET  /server/info                      — connection test, reads
                                            result.klippy_state
- GET  /server/files/roots               — storage-picker dropdown
                                            (returns roots with 'w'
                                            permission); gracefully
                                            degrades if absent
- POST /server/files/upload (multipart)  — upload (form fields:
                                            file, root)
- POST /printer/print/start (json)       — {"filename":"<path>"}; the
                                            filename is whatever the
                                            upload response returned
                                            in result.item.path, so any
                                            server-side rename
                                            (collision suffix etc.) is
                                            respected. JSON body is
                                            built via property_tree
                                            write_json so exotic
                                            characters in the path are
                                            properly escaped.

Auth: X-Api-Key header, only when printhost_apikey is non-empty
(Moonraker can be configured to require it but doesn't by default).
HTTP Basic / Digest are not part of the Moonraker spec and are not
sent.

Storage root is read from upload_data.storage with "gcodes" as the
fallback default, so the existing storage-picker plumbing in
PrintHostDialogs lights up automatically once enumerable roots are
returned.

UI: registers as the "Moonraker (Klipper)" entry under host_type;
selectable via the existing Physical Printer dialog (sidebar's
connection button on the printer card).

Verified against a Prusa-Firmware-Buddy buddy-klipper fork (firmware
identifies as moonraker_version "0.8.0-prusalink-shim"): /server/info
test, multipart upload to /server/files/upload, and JSON
/printer/print/start all work end-to-end. The existing "Octo/Klipper"
entry is left untouched so users currently relying on Moonraker's
OctoPrint-emulation plugin keep working.
2026-06-06 11:12:42 +08:00

64 lines
2.3 KiB
C++

#ifndef slic3r_Moonraker_hpp_
#define slic3r_Moonraker_hpp_
#include <string>
#include <wx/string.h>
#include <wx/arrstr.h>
#include "PrintHost.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r {
class DynamicPrintConfig;
class Http;
// Moonraker is the JSON / WebSocket gateway that ships in front of Klipper
// (and on Klipper-API-compatible firmwares like the Prusa-Firmware-Buddy
// Buddy-Klipper fork). REST shape differs from OctoPrint: distinct paths,
// JSON body for print/start, {"result":...}/{"error":...} envelope.
//
// Endpoints used:
// GET /server/info -- connection test, reads klippy_state
// POST /server/files/upload (multipart) -- upload gcode (form fields: file, root)
// POST /printer/print/start (json) -- {"filename":"<name>.gcode"} starts print
//
// Auth: X-Api-Key header if `printhost_apikey` is non-empty; Moonraker accepts
// unauthenticated LAN access by default, so the key is optional. HTTP Basic /
// Digest are not part of the Moonraker spec and are not sent.
class Moonraker : public PrintHost
{
public:
Moonraker(DynamicPrintConfig *config);
~Moonraker() override = default;
const char* get_name() const override;
bool test(wxString &curl_msg) const override;
wxString get_test_ok_msg() const override;
wxString get_test_failed_msg(wxString &msg) const override;
bool upload(PrintHostUpload upload_data, ProgressFn progress_fn, ErrorFn error_fn, InfoFn info_fn) const override;
bool has_auto_discovery() const override { return false; }
bool can_test() const override { return true; }
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
std::string get_host() const override { return m_host; }
bool get_storage(wxArrayString &storage_path, wxArrayString &storage_name) const override;
const std::string& get_apikey() const { return m_apikey; }
const std::string& get_cafile() const { return m_cafile; }
protected:
std::string m_host;
std::string m_apikey;
std::string m_cafile;
bool m_ssl_revoke_best_effort;
void set_auth(Http &http) const;
std::string make_url(const std::string &path) const;
bool start_print(wxString &error_msg, const std::string &filename) const;
};
}
#endif