MoonrakerSharpWebApi

A simple Rest-API library for interacting with Moonraker web api (used by Fluidd and MainsailOS).


Keywords
API, Fluidd, Klipper, MainsailOS, Moonraker, Remote, Rest, Sharp, Web, xamarin
License
Apache-2.0
Install
Install-Package MoonrakerSharpWebApi -Version 1.3.1-preview

Documentation

MoonrakerRestApiSharp (former KlipperRestApiSharp)

A C# based library to communicate with the Moonraker WebApi, available with Fluidd and MainsailOS.

Important!

With the upcoming version, starting from 1.1.0, KlipperClient become MoonrakerClient. also the namespaces will changed and generalized with our other print server api nugets.

Old New
AndreasReitberger AndreasReitberger.API.Moonraker
KlipperClient MoonrakerClient
Klipper... Moonraker...

Nuget MoonrakerRestApiSharp

NuGet NuGet

Nuget KlipperRestApiSharp (deprecated)

NuGet NuGet

Platform specific setup

Android

On Android you need to allow local connections in the AndroidManifest.xml. For this, create a new xml file and link to it in your manifest at android:networkSecurityConfig

Content of the network_security_config.xml file

<?xml version="1.0" encoding="utf-8" ?>
<network-security-config>
	<base-config cleartextTrafficPermitted="true" />
</network-security-config>

The manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:versionName="1.0.0"
	android:versionCode="1"
	package="com.company.app"
	>
	<application
		android:label="App Name"
		android:allowBackup="true"
		android:icon="@mipmap/appicon" 
		android:roundIcon="@mipmap/appicon_round"
		android:supportsRtl="true"
		android:networkSecurityConfig="@xml/network_security_config"
		>
	</application>
	<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
	<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
	<uses-permission android:name="android.permission.WAKE_LOCK" />
	<uses-permission android:name="android.permission.INTERNET" />
</manifest>

Moonrakers Documentation

This C# library wraps the available Web API functions listed in the Moonraker's documentation below. https://moonraker.readthedocs.io/en/latest/web_api/

You can see the current migration progress in the following table.

Supported OS

Basically all OS using the Moonraker WebApi are theoretically supported.

Tested with our app

  • MainsailOS
  • FluiddPi

App's using this library

FeaturedImage

I also created an iOS app to interact with a moonraker based OS. The app is available for testing here:
TestFlight: https://testflight.apple.com/join/TyguKzt9

Note for Android

There is an android version in development, however due to the poor performance using Xamarin.Android, I decided to wait till .NET MAUI is available.

WebAPI migration status

Following you'll find the list of migrated functions from the WebAPI.

Printer Administration

Function Added? Tested?
Get Klippy host information βœ… βœ…
Emergency Stop βœ… βœ…
Host Restart βœ… βœ…
Firmware Restart βœ… βœ…

Printer Status

Function Added? Tested?
List available printer objects βœ… βœ…
Query printer object status βœ… βœ…
Subscribe to printer object status βœ… βœ…
Query Endstops βœ… βœ…
Query Server Info βœ… βœ…
Get Server Configuration βœ… βœ…
Request Cached Temperature Data βœ… βœ…
Request Cached GCode Responses βœ… βœ…
Restart Server βœ… βœ…
Get Websocket ID βœ… βœ…

GCode APIs

Function Added? Tested?
Run a gcode βœ… βœ…
Get GCode Help βœ… βœ…

Print Management

Function Added? Tested?
Print a file βœ… βœ…
Pause a print βœ… βœ…
Resume a print βœ… βœ…
Cancel a print βœ… βœ…

Machine Commands

Function Added? Tested?
Get System Info βœ… βœ…
Shutdown the Operating System βœ… βœ…
Reboot the Operating System βœ… βœ…
Restart a system service βœ… No
Stop a system service βœ… No
Start a system service βœ… No
Get Moonraker Process Stats βœ… βœ…

File Operations

Function Added? Tested?
List available files βœ… βœ…
Get gcode metadata βœ… βœ…
Get directory information βœ… βœ…
Create directory βœ… βœ…
Delete directory βœ… βœ…
Move a file or directory βœ… βœ…
Copy a file or directory βœ… βœ…
File download βœ… βœ…
File upload βœ… βœ…
File delete βœ… βœ…
Download klippy.log βœ… βœ…
Download moonraker.log βœ… βœ…

Authorization

Function Added? Tested?
Login User βœ… βœ…
Logout Current User βœ… βœ…
Get Current User βœ… βœ…
Create User βœ… βœ…
Delete User βœ… βœ…
List Available Users βœ… βœ…
Reset User Password βœ… βœ…
Refresh JSON Web Token βœ… βœ…
Generate a Oneshot Token βœ… βœ…
Get the Current API Key βœ… βœ…
Generate a New API Key βœ… βœ…

Database APIs

Function Added? Tested?
List namespaces βœ… βœ…
Get Database Item βœ… βœ…
Add Database Item βœ… βœ…
Delete Database Item βœ… βœ…

Job Queue APIs

Function Added? Tested?
Retrieve the job queue status βœ… βœ…
Enqueue a job βœ… βœ…
Remove a Job βœ… βœ…
Pause the job queue βœ… βœ…
Start the job queue βœ… βœ…

Update Manager APIs

Function Added? Tested?
Get update status βœ… βœ…
Perform a full update βœ… βœ…
Update Moonraker βœ… No
Update Klipper βœ… No
Update Client βœ… No
Update System Packages βœ… No
Recover a corrupt repo βœ… No

Power APIs

Function Added? Tested?
Get Device List βœ… βœ…
Get Device Status βœ… βœ…
Set Device State βœ… βœ…
Get Batch Device Status βœ… βœ…
Batch Power On Devices βœ… βœ…
Batch Power Off Devices βœ… βœ…

Octoprint API emulation

Function Added? Tested?
Version information βœ… βœ…
Server status βœ… βœ…
Login verification & User information βœ… No
Get settings βœ… βœ…
Octoprint File Upload βœ… βœ…
Get Job status βœ… βœ…
Get Printer status βœ… βœ…
Send GCode command βœ… βœ…
List Printer profiles βœ… βœ…

History APIs

Function Added? Tested?
Get job list βœ… βœ…
Get job totals βœ… βœ…
Reset totals βœ… βœ…
Get a single job βœ… βœ…
Delete job βœ… βœ…

MQTT APIs

Function Added? Tested?
Publish a topic No No
Subscribe to a topic No No

Websocket notifications

Not implemented yet.

Usage

You can check the Test project for more code examples.

Initialize the client

This initialize a new KlipperClient object. Always check if the client is reachable before using it.

private readonly string _host = "192.168.10.113";
private readonly int _port = 80;
private readonly string _api = "";
private readonly bool _ssl = false;

// Note, the api key is not mandatory
KlipperClient _server = new(_host, _port, _ssl);
await _server.CheckOnlineAsync();
if (_server.IsOnline)
{
    await _server.RefreshAllAsync();
    Assert.IsTrue(_server.InitialDataFetched);

    //var token = await _server.GetOneshotTokenAsync();
    KlipperAccessTokenResult token2 = await _server.GetApiKeyAsync();
    Assert.IsTrue(!string.IsNullOrEmpty(token2.Result));
}

WebSocket

It's recommended to StartListening() to the WebSocket of your Klipper server. An example is shown below.

Dictionary<DateTime, string> websocketMessages = new();
KlipperClient _server = new(_host, _api, _port, _ssl);

await _server.CheckOnlineAsync();
Assert.IsTrue(_server.IsOnline);

_server.StartListening();

// Once the Id has been received, subscribe to the printer status objects
_server.WebSocketConnectionIdChanged += (o, args) =>
{
    Assert.IsNotNull(args.ConnectionId);
    Assert.IsTrue(args.ConnectionId > 0);
    Task.Run(async () =>
    {
        string subResult = await _server.SubscribeAllPrinterObjectStatusAsync(args.ConnectionId);
    });
};

_server.Error += (o, args) =>
{
    Assert.Fail(args.ToString());
};
_server.ServerWentOffline += (o, args) =>
{
    Assert.Fail(args.ToString());
};

_server.WebSocketDataReceived += (o, args) =>
{
    if (!string.IsNullOrEmpty(args.Message))
    {
        websocketMessages.Add(DateTime.Now, args.Message);
        Debug.WriteLine($"WebSocket Data: {args.Message} (Total: {websocketMessages.Count})");
    }
};

_server.WebSocketError += (o, args) =>
{
    Assert.Fail($"Websocket closed due to an error: {args}");
};

// Wait 10 minutes
CancellationTokenSource cts = new(new TimeSpan(0, 10, 0));
_server.WebSocketDisconnected += (o, args) =>
{
    if (!cts.IsCancellationRequested)
        Assert.Fail($"Websocket unexpectly closed: {args}");
};

do
{
    await Task.Delay(10000);
    await _server.CheckOnlineAsync();
} while (_server.IsOnline && !cts.IsCancellationRequested);
_server.StopListening();


Assert.IsTrue(cts.IsCancellationRequested);

User auth

If your Klipper server is protected with a login, please call the LoginUserAsync method first. Alternatively you can provide the API key instead.

string username = "TestUser";
string password = "TestPassword";

KlipperUserActionResult userCreated = await _server.CreateUserAsync(username, password);
Assert.IsNotNull(userCreated);

List<KlipperUser> users = await _server.ListAvailableUsersAsync();
Assert.IsTrue(users?.Count > 0);

KlipperUserActionResult login = await _server.LoginUserAsync(username, password);
Assert.IsNotNull(login);
Assert.IsTrue(login.Username == username);

KlipperUser currentUser = await _server.GetCurrentUserAsync();
Assert.IsNotNull(currentUser);

KlipperUserActionResult newTokenResult = await _server.RefreshJSONWebTokenAsync();
Assert.IsNotNull(newTokenResult);
Assert.IsTrue(_server.UserToken == newTokenResult.Token);

string newPassword = "TestPasswordChanged";
KlipperUserActionResult refreshPassword = await _server.ResetUserPasswordAsync(password, newPassword);
Assert.IsNotNull(refreshPassword);

KlipperUserActionResult logout = await _server.LogoutCurrentUserAsync();
Assert.IsNotNull(logout);

login = await _server.LoginUserAsync(username, newPassword);
Assert.IsNotNull(login);
Assert.IsTrue(login.Username == username);

logout = await _server.LogoutCurrentUserAsync();
Assert.IsNotNull(logout);

KlipperUserActionResult userDeleted = await _server.DeleteUserAsync(username);
Assert.IsNotNull(userDeleted);